PlanOpticon

1
2
<!doctype html>
3
<html lang="en" class="no-js">
4
<head>
5
6
<meta charset="utf-8">
7
<meta name="viewport" content="width=device-width,initial-scale=1">
8
9
<meta name="description" content="AI-powered video analysis and knowledge extraction">
10
11
12
<meta name="author" content="CONFLICT LLC">
13
14
15
<link rel="canonical" href="https://planopticon.dev/contributing/">
16
17
18
<link rel="prev" href="../faq/">
19
20
21
22
23
24
25
<link rel="icon" href="../assets/images/favicon.png">
26
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.4">
27
28
29
30
<title>Contributing - PlanOpticon</title>
31
32
33
34
<link rel="stylesheet" href="../assets/stylesheets/main.484c7ddc.min.css">
35
36
37
<link rel="stylesheet" href="../assets/stylesheets/palette.ab4e12ef.min.css">
38
39
40
41
42
43
44
45
46
47
48
49
50
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
51
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
52
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
53
54
55
56
<link rel="stylesheet" href="../assets/_mkdocstrings.css">
57
58
<link rel="stylesheet" href="../assets/css/custom.css">
59
60
<script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
61
62
63
64
65
66
</head>
67
68
69
70
71
72
73
74
75
76
<body dir="ltr" data-md-color-scheme="slate" data-md-color-primary="custom" data-md-color-accent="custom">
77
78
79
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
80
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
81
<label class="md-overlay" for="__drawer"></label>
82
<div data-md-component="skip">
83
84
85
<a href="#contributing" class="md-skip">
86
Skip to content
87
</a>
88
89
</div>
90
<div data-md-component="announce">
91
92
</div>
93
94
95
96
97
<header class="md-header" data-md-component="header">
98
<nav class="md-header__inner md-grid" aria-label="Header">
99
<a href=".." title="PlanOpticon" class="md-header__button md-logo" aria-label="PlanOpticon" data-md-component="logo">
100
101
<img src="../assets/images/conflict-logo.svg" alt="logo">
102
103
</a>
104
<label class="md-header__button md-icon" for="__drawer">
105
106
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
107
</label>
108
<div class="md-header__title" data-md-component="header-title">
109
<div class="md-header__ellipsis">
110
<div class="md-header__topic">
111
<span class="md-ellipsis">
112
PlanOpticon
113
</span>
114
</div>
115
<div class="md-header__topic" data-md-component="header-topic">
116
<span class="md-ellipsis">
117
118
Contributing
119
120
</span>
121
</div>
122
</div>
123
</div>
124
125
126
<form class="md-header__option" data-md-component="palette">
127
128
129
130
131
<input class="md-option" data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme="slate" data-md-color-primary="custom" data-md-color-accent="custom" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_0">
132
133
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
134
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg>
135
</label>
136
137
138
139
140
141
<input class="md-option" data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme="default" data-md-color-primary="custom" data-md-color-accent="custom" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
142
143
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_0" hidden>
144
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg>
145
</label>
146
147
148
</form>
149
150
151
152
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
153
154
155
156
157
158
<label class="md-header__button md-icon" for="__search">
159
160
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
161
</label>
162
<div class="md-search" data-md-component="search" role="dialog">
163
<label class="md-search__overlay" for="__search"></label>
164
<div class="md-search__inner" role="search">
165
<form class="md-search__form" name="search">
166
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
167
<label class="md-search__icon md-icon" for="__search">
168
169
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
170
171
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
172
</label>
173
<nav class="md-search__options" aria-label="Search">
174
175
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
176
177
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
178
</button>
179
</nav>
180
181
<div class="md-search__suggest" data-md-component="search-suggest"></div>
182
183
</form>
184
<div class="md-search__output">
185
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
186
<div class="md-search-result" data-md-component="search-result">
187
<div class="md-search-result__meta">
188
Initializing search
189
</div>
190
<ol class="md-search-result__list" role="presentation"></ol>
191
</div>
192
</div>
193
</div>
194
</div>
195
</div>
196
197
198
199
<div class="md-header__source">
200
<a href="https://github.com/ConflictHQ/PlanOpticon" title="Go to repository" class="md-source" data-md-component="source">
201
<div class="md-source__icon md-icon">
202
203
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
204
</div>
205
<div class="md-source__repository">
206
ConflictHQ/PlanOpticon
207
</div>
208
</a>
209
</div>
210
211
</nav>
212
213
</header>
214
215
<div class="md-container" data-md-component="container">
216
217
218
219
220
221
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
222
<div class="md-grid">
223
<ul class="md-tabs__list">
224
225
226
227
228
229
230
<li class="md-tabs__item">
231
<a href=".." class="md-tabs__link">
232
233
234
235
236
237
Home
238
239
</a>
240
</li>
241
242
243
244
245
246
247
248
249
250
251
<li class="md-tabs__item">
252
<a href="../getting-started/installation/" class="md-tabs__link">
253
254
255
256
Getting Started
257
258
</a>
259
</li>
260
261
262
263
264
265
266
267
268
269
270
271
<li class="md-tabs__item">
272
<a href="../guide/single-video/" class="md-tabs__link">
273
274
275
276
User Guide
277
278
</a>
279
</li>
280
281
282
283
284
285
286
287
288
289
<li class="md-tabs__item">
290
<a href="../use-cases/" class="md-tabs__link">
291
292
293
294
295
296
Use Cases
297
298
</a>
299
</li>
300
301
302
303
304
305
306
307
308
<li class="md-tabs__item">
309
<a href="../cli-reference/" class="md-tabs__link">
310
311
312
313
314
315
CLI Reference
316
317
</a>
318
</li>
319
320
321
322
323
324
325
326
327
328
329
<li class="md-tabs__item">
330
<a href="../architecture/overview/" class="md-tabs__link">
331
332
333
334
Architecture
335
336
</a>
337
</li>
338
339
340
341
342
343
344
345
346
347
348
349
<li class="md-tabs__item">
350
<a href="../api/models/" class="md-tabs__link">
351
352
353
354
API Reference
355
356
</a>
357
</li>
358
359
360
361
362
363
364
365
366
367
<li class="md-tabs__item">
368
<a href="../faq/" class="md-tabs__link">
369
370
371
372
373
374
FAQ & Troubleshooting
375
376
</a>
377
</li>
378
379
380
381
382
383
384
385
386
387
388
<li class="md-tabs__item md-tabs__item--active">
389
<a href="./" class="md-tabs__link">
390
391
392
393
394
395
Contributing
396
397
</a>
398
</li>
399
400
401
402
</ul>
403
</div>
404
</nav>
405
406
407
408
<main class="md-main" data-md-component="main">
409
<div class="md-main__inner md-grid">
410
411
412
413
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
414
<div class="md-sidebar__scrollwrap">
415
<div class="md-sidebar__inner">
416
417
418
419
420
421
422
<nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0">
423
<label class="md-nav__title" for="__drawer">
424
<a href=".." title="PlanOpticon" class="md-nav__button md-logo" aria-label="PlanOpticon" data-md-component="logo">
425
426
<img src="../assets/images/conflict-logo.svg" alt="logo">
427
428
</a>
429
PlanOpticon
430
</label>
431
432
<div class="md-nav__source">
433
<a href="https://github.com/ConflictHQ/PlanOpticon" title="Go to repository" class="md-source" data-md-component="source">
434
<div class="md-source__icon md-icon">
435
436
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
437
</div>
438
<div class="md-source__repository">
439
ConflictHQ/PlanOpticon
440
</div>
441
</a>
442
</div>
443
444
<ul class="md-nav__list" data-md-scrollfix>
445
446
447
448
449
450
451
452
<li class="md-nav__item">
453
<a href=".." class="md-nav__link">
454
455
456
457
<span class="md-ellipsis">
458
459
460
Home
461
462
463
464
</span>
465
466
467
468
</a>
469
</li>
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
<li class="md-nav__item md-nav__item--nested">
490
491
492
493
494
495
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2" >
496
497
498
<label class="md-nav__link" for="__nav_2" id="__nav_2_label" tabindex="0">
499
500
501
502
<span class="md-ellipsis">
503
504
505
Getting Started
506
507
508
509
</span>
510
511
512
513
<span class="md-nav__icon md-icon"></span>
514
</label>
515
516
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_2_label" aria-expanded="false">
517
<label class="md-nav__title" for="__nav_2">
518
<span class="md-nav__icon md-icon"></span>
519
520
521
Getting Started
522
523
524
</label>
525
<ul class="md-nav__list" data-md-scrollfix>
526
527
528
529
530
531
532
533
<li class="md-nav__item">
534
<a href="../getting-started/installation/" class="md-nav__link">
535
536
537
538
<span class="md-ellipsis">
539
540
541
Installation
542
543
544
545
</span>
546
547
548
549
</a>
550
</li>
551
552
553
554
555
556
557
558
559
560
561
<li class="md-nav__item">
562
<a href="../getting-started/quickstart/" class="md-nav__link">
563
564
565
566
<span class="md-ellipsis">
567
568
569
Quick Start
570
571
572
573
</span>
574
575
576
577
</a>
578
</li>
579
580
581
582
583
584
585
586
587
588
589
<li class="md-nav__item">
590
<a href="../getting-started/configuration/" class="md-nav__link">
591
592
593
594
<span class="md-ellipsis">
595
596
597
Configuration
598
599
600
601
</span>
602
603
604
605
</a>
606
</li>
607
608
609
610
611
</ul>
612
</nav>
613
614
</li>
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
<li class="md-nav__item md-nav__item--nested">
635
636
637
638
639
640
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_3" >
641
642
643
<label class="md-nav__link" for="__nav_3" id="__nav_3_label" tabindex="0">
644
645
646
647
<span class="md-ellipsis">
648
649
650
User Guide
651
652
653
654
</span>
655
656
657
658
<span class="md-nav__icon md-icon"></span>
659
</label>
660
661
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_3_label" aria-expanded="false">
662
<label class="md-nav__title" for="__nav_3">
663
<span class="md-nav__icon md-icon"></span>
664
665
666
User Guide
667
668
669
</label>
670
<ul class="md-nav__list" data-md-scrollfix>
671
672
673
674
675
676
677
678
<li class="md-nav__item">
679
<a href="../guide/single-video/" class="md-nav__link">
680
681
682
683
<span class="md-ellipsis">
684
685
686
Single Video Analysis
687
688
689
690
</span>
691
692
693
694
</a>
695
</li>
696
697
698
699
700
701
702
703
704
705
706
<li class="md-nav__item">
707
<a href="../guide/batch/" class="md-nav__link">
708
709
710
711
<span class="md-ellipsis">
712
713
714
Batch Processing
715
716
717
718
</span>
719
720
721
722
</a>
723
</li>
724
725
726
727
728
729
730
731
732
733
734
<li class="md-nav__item">
735
<a href="../guide/document-ingestion/" class="md-nav__link">
736
737
738
739
<span class="md-ellipsis">
740
741
742
Document Ingestion
743
744
745
746
</span>
747
748
749
750
</a>
751
</li>
752
753
754
755
756
757
758
759
760
761
762
<li class="md-nav__item">
763
<a href="../guide/cloud-sources/" class="md-nav__link">
764
765
766
767
<span class="md-ellipsis">
768
769
770
Cloud Sources
771
772
773
774
</span>
775
776
777
778
</a>
779
</li>
780
781
782
783
784
785
786
787
788
789
790
<li class="md-nav__item">
791
<a href="../guide/knowledge-graphs/" class="md-nav__link">
792
793
794
795
<span class="md-ellipsis">
796
797
798
Knowledge Graphs
799
800
801
802
</span>
803
804
805
806
</a>
807
</li>
808
809
810
811
812
813
814
815
816
817
818
<li class="md-nav__item">
819
<a href="../guide/companion/" class="md-nav__link">
820
821
822
823
<span class="md-ellipsis">
824
825
826
Interactive Companion
827
828
829
830
</span>
831
832
833
834
</a>
835
</li>
836
837
838
839
840
841
842
843
844
845
846
<li class="md-nav__item">
847
<a href="../guide/planning-agent/" class="md-nav__link">
848
849
850
851
<span class="md-ellipsis">
852
853
854
Planning Agent
855
856
857
858
</span>
859
860
861
862
</a>
863
</li>
864
865
866
867
868
869
870
871
872
873
874
<li class="md-nav__item">
875
<a href="../guide/authentication/" class="md-nav__link">
876
877
878
879
<span class="md-ellipsis">
880
881
882
Authentication
883
884
885
886
</span>
887
888
889
890
</a>
891
</li>
892
893
894
895
896
897
898
899
900
901
902
<li class="md-nav__item">
903
<a href="../guide/export/" class="md-nav__link">
904
905
906
907
<span class="md-ellipsis">
908
909
910
Export & Documents
911
912
913
914
</span>
915
916
917
918
</a>
919
</li>
920
921
922
923
924
925
926
927
928
929
930
<li class="md-nav__item">
931
<a href="../guide/output-formats/" class="md-nav__link">
932
933
934
935
<span class="md-ellipsis">
936
937
938
Output Formats
939
940
941
942
</span>
943
944
945
946
</a>
947
</li>
948
949
950
951
952
</ul>
953
</nav>
954
955
</li>
956
957
958
959
960
961
962
963
964
965
<li class="md-nav__item">
966
<a href="../use-cases/" class="md-nav__link">
967
968
969
970
<span class="md-ellipsis">
971
972
973
Use Cases
974
975
976
977
</span>
978
979
980
981
</a>
982
</li>
983
984
985
986
987
988
989
990
991
992
<li class="md-nav__item">
993
<a href="../cli-reference/" class="md-nav__link">
994
995
996
997
<span class="md-ellipsis">
998
999
1000
CLI Reference
1001
1002
1003
1004
</span>
1005
1006
1007
1008
</a>
1009
</li>
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
<li class="md-nav__item md-nav__item--nested">
1030
1031
1032
1033
1034
1035
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_6" >
1036
1037
1038
<label class="md-nav__link" for="__nav_6" id="__nav_6_label" tabindex="0">
1039
1040
1041
1042
<span class="md-ellipsis">
1043
1044
1045
Architecture
1046
1047
1048
1049
</span>
1050
1051
1052
1053
<span class="md-nav__icon md-icon"></span>
1054
</label>
1055
1056
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_6_label" aria-expanded="false">
1057
<label class="md-nav__title" for="__nav_6">
1058
<span class="md-nav__icon md-icon"></span>
1059
1060
1061
Architecture
1062
1063
1064
</label>
1065
<ul class="md-nav__list" data-md-scrollfix>
1066
1067
1068
1069
1070
1071
1072
1073
<li class="md-nav__item">
1074
<a href="../architecture/overview/" class="md-nav__link">
1075
1076
1077
1078
<span class="md-ellipsis">
1079
1080
1081
Overview
1082
1083
1084
1085
</span>
1086
1087
1088
1089
</a>
1090
</li>
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
<li class="md-nav__item">
1102
<a href="../architecture/providers/" class="md-nav__link">
1103
1104
1105
1106
<span class="md-ellipsis">
1107
1108
1109
Provider System
1110
1111
1112
1113
</span>
1114
1115
1116
1117
</a>
1118
</li>
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
<li class="md-nav__item">
1130
<a href="../architecture/pipeline/" class="md-nav__link">
1131
1132
1133
1134
<span class="md-ellipsis">
1135
1136
1137
Processing Pipeline
1138
1139
1140
1141
</span>
1142
1143
1144
1145
</a>
1146
</li>
1147
1148
1149
1150
1151
</ul>
1152
</nav>
1153
1154
</li>
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
<li class="md-nav__item md-nav__item--nested">
1175
1176
1177
1178
1179
1180
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_7" >
1181
1182
1183
<label class="md-nav__link" for="__nav_7" id="__nav_7_label" tabindex="0">
1184
1185
1186
1187
<span class="md-ellipsis">
1188
1189
1190
API Reference
1191
1192
1193
1194
</span>
1195
1196
1197
1198
<span class="md-nav__icon md-icon"></span>
1199
</label>
1200
1201
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_7_label" aria-expanded="false">
1202
<label class="md-nav__title" for="__nav_7">
1203
<span class="md-nav__icon md-icon"></span>
1204
1205
1206
API Reference
1207
1208
1209
</label>
1210
<ul class="md-nav__list" data-md-scrollfix>
1211
1212
1213
1214
1215
1216
1217
1218
<li class="md-nav__item">
1219
<a href="../api/models/" class="md-nav__link">
1220
1221
1222
1223
<span class="md-ellipsis">
1224
1225
1226
Models
1227
1228
1229
1230
</span>
1231
1232
1233
1234
</a>
1235
</li>
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
<li class="md-nav__item">
1247
<a href="../api/providers/" class="md-nav__link">
1248
1249
1250
1251
<span class="md-ellipsis">
1252
1253
1254
Providers
1255
1256
1257
1258
</span>
1259
1260
1261
1262
</a>
1263
</li>
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
<li class="md-nav__item">
1275
<a href="../api/analyzers/" class="md-nav__link">
1276
1277
1278
1279
<span class="md-ellipsis">
1280
1281
1282
Analyzers
1283
1284
1285
1286
</span>
1287
1288
1289
1290
</a>
1291
</li>
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
<li class="md-nav__item">
1303
<a href="../api/agent/" class="md-nav__link">
1304
1305
1306
1307
<span class="md-ellipsis">
1308
1309
1310
Agent & Skills
1311
1312
1313
1314
</span>
1315
1316
1317
1318
</a>
1319
</li>
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
<li class="md-nav__item">
1331
<a href="../api/sources/" class="md-nav__link">
1332
1333
1334
1335
<span class="md-ellipsis">
1336
1337
1338
Sources
1339
1340
1341
1342
</span>
1343
1344
1345
1346
</a>
1347
</li>
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
<li class="md-nav__item">
1359
<a href="../api/auth/" class="md-nav__link">
1360
1361
1362
1363
<span class="md-ellipsis">
1364
1365
1366
Authentication
1367
1368
1369
1370
</span>
1371
1372
1373
1374
</a>
1375
</li>
1376
1377
1378
1379
1380
</ul>
1381
</nav>
1382
1383
</li>
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
<li class="md-nav__item">
1394
<a href="../faq/" class="md-nav__link">
1395
1396
1397
1398
<span class="md-ellipsis">
1399
1400
1401
FAQ & Troubleshooting
1402
1403
1404
1405
</span>
1406
1407
1408
1409
</a>
1410
</li>
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
<li class="md-nav__item md-nav__item--active">
1423
1424
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
1425
1426
1427
1428
1429
1430
<label class="md-nav__link md-nav__link--active" for="__toc">
1431
1432
1433
1434
<span class="md-ellipsis">
1435
1436
1437
Contributing
1438
1439
1440
1441
</span>
1442
1443
1444
1445
<span class="md-nav__icon md-icon"></span>
1446
</label>
1447
1448
<a href="./" class="md-nav__link md-nav__link--active">
1449
1450
1451
1452
<span class="md-ellipsis">
1453
1454
1455
Contributing
1456
1457
1458
1459
</span>
1460
1461
1462
1463
</a>
1464
1465
1466
1467
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
1468
1469
1470
1471
1472
1473
1474
<label class="md-nav__title" for="__toc">
1475
<span class="md-nav__icon md-icon"></span>
1476
Table of contents
1477
</label>
1478
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
1479
1480
<li class="md-nav__item">
1481
<a href="#development-setup" class="md-nav__link">
1482
<span class="md-ellipsis">
1483
1484
Development setup
1485
1486
</span>
1487
</a>
1488
1489
</li>
1490
1491
<li class="md-nav__item">
1492
<a href="#running-tests" class="md-nav__link">
1493
<span class="md-ellipsis">
1494
1495
Running tests
1496
1497
</span>
1498
</a>
1499
1500
<nav class="md-nav" aria-label="Running tests">
1501
<ul class="md-nav__list">
1502
1503
<li class="md-nav__item">
1504
<a href="#test-conventions" class="md-nav__link">
1505
<span class="md-ellipsis">
1506
1507
Test conventions
1508
1509
</span>
1510
</a>
1511
1512
</li>
1513
1514
<li class="md-nav__item">
1515
<a href="#mocking-patterns" class="md-nav__link">
1516
<span class="md-ellipsis">
1517
1518
Mocking patterns
1519
1520
</span>
1521
</a>
1522
1523
</li>
1524
1525
</ul>
1526
</nav>
1527
1528
</li>
1529
1530
<li class="md-nav__item">
1531
<a href="#code-style" class="md-nav__link">
1532
<span class="md-ellipsis">
1533
1534
Code style
1535
1536
</span>
1537
</a>
1538
1539
<nav class="md-nav" aria-label="Code style">
1540
<ul class="md-nav__list">
1541
1542
<li class="md-nav__item">
1543
<a href="#ruff-configuration" class="md-nav__link">
1544
<span class="md-ellipsis">
1545
1546
Ruff configuration
1547
1548
</span>
1549
</a>
1550
1551
</li>
1552
1553
</ul>
1554
</nav>
1555
1556
</li>
1557
1558
<li class="md-nav__item">
1559
<a href="#project-structure" class="md-nav__link">
1560
<span class="md-ellipsis">
1561
1562
Project structure
1563
1564
</span>
1565
</a>
1566
1567
</li>
1568
1569
<li class="md-nav__item">
1570
<a href="#adding-a-new-provider" class="md-nav__link">
1571
<span class="md-ellipsis">
1572
1573
Adding a new provider
1574
1575
</span>
1576
</a>
1577
1578
<nav class="md-nav" aria-label="Adding a new provider">
1579
<ul class="md-nav__list">
1580
1581
<li class="md-nav__item">
1582
<a href="#example-provider-skeleton" class="md-nav__link">
1583
<span class="md-ellipsis">
1584
1585
Example provider skeleton
1586
1587
</span>
1588
</a>
1589
1590
</li>
1591
1592
<li class="md-nav__item">
1593
<a href="#openai-compatible-providers" class="md-nav__link">
1594
<span class="md-ellipsis">
1595
1596
OpenAI-compatible providers
1597
1598
</span>
1599
</a>
1600
1601
</li>
1602
1603
</ul>
1604
</nav>
1605
1606
</li>
1607
1608
<li class="md-nav__item">
1609
<a href="#adding-a-new-cloud-source" class="md-nav__link">
1610
<span class="md-ellipsis">
1611
1612
Adding a new cloud source
1613
1614
</span>
1615
</a>
1616
1617
<nav class="md-nav" aria-label="Adding a new cloud source">
1618
<ul class="md-nav__list">
1619
1620
<li class="md-nav__item">
1621
<a href="#example-source-skeleton" class="md-nav__link">
1622
<span class="md-ellipsis">
1623
1624
Example source skeleton
1625
1626
</span>
1627
</a>
1628
1629
</li>
1630
1631
<li class="md-nav__item">
1632
<a href="#registering-in-__init__py" class="md-nav__link">
1633
<span class="md-ellipsis">
1634
1635
Registering in __init__.py
1636
1637
</span>
1638
</a>
1639
1640
</li>
1641
1642
</ul>
1643
</nav>
1644
1645
</li>
1646
1647
<li class="md-nav__item">
1648
<a href="#adding-a-new-skill" class="md-nav__link">
1649
<span class="md-ellipsis">
1650
1651
Adding a new skill
1652
1653
</span>
1654
</a>
1655
1656
<nav class="md-nav" aria-label="Adding a new skill">
1657
<ul class="md-nav__list">
1658
1659
<li class="md-nav__item">
1660
<a href="#example-skill-skeleton" class="md-nav__link">
1661
<span class="md-ellipsis">
1662
1663
Example skill skeleton
1664
1665
</span>
1666
</a>
1667
1668
</li>
1669
1670
<li class="md-nav__item">
1671
<a href="#registering-in-__init__py_1" class="md-nav__link">
1672
<span class="md-ellipsis">
1673
1674
Registering in __init__.py
1675
1676
</span>
1677
</a>
1678
1679
</li>
1680
1681
</ul>
1682
</nav>
1683
1684
</li>
1685
1686
<li class="md-nav__item">
1687
<a href="#adding-a-new-document-processor" class="md-nav__link">
1688
<span class="md-ellipsis">
1689
1690
Adding a new document processor
1691
1692
</span>
1693
</a>
1694
1695
<nav class="md-nav" aria-label="Adding a new document processor">
1696
<ul class="md-nav__list">
1697
1698
<li class="md-nav__item">
1699
<a href="#example-processor-skeleton" class="md-nav__link">
1700
<span class="md-ellipsis">
1701
1702
Example processor skeleton
1703
1704
</span>
1705
</a>
1706
1707
</li>
1708
1709
<li class="md-nav__item">
1710
<a href="#registering-in-__init__py_2" class="md-nav__link">
1711
<span class="md-ellipsis">
1712
1713
Registering in __init__.py
1714
1715
</span>
1716
</a>
1717
1718
</li>
1719
1720
</ul>
1721
</nav>
1722
1723
</li>
1724
1725
<li class="md-nav__item">
1726
<a href="#adding-a-new-exporter" class="md-nav__link">
1727
<span class="md-ellipsis">
1728
1729
Adding a new exporter
1730
1731
</span>
1732
</a>
1733
1734
<nav class="md-nav" aria-label="Adding a new exporter">
1735
<ul class="md-nav__list">
1736
1737
<li class="md-nav__item">
1738
<a href="#example-exporter-skeleton" class="md-nav__link">
1739
<span class="md-ellipsis">
1740
1741
Example exporter skeleton
1742
1743
</span>
1744
</a>
1745
1746
</li>
1747
1748
<li class="md-nav__item">
1749
<a href="#adding-the-cli-command" class="md-nav__link">
1750
<span class="md-ellipsis">
1751
1752
Adding the CLI command
1753
1754
</span>
1755
</a>
1756
1757
</li>
1758
1759
</ul>
1760
</nav>
1761
1762
</li>
1763
1764
<li class="md-nav__item">
1765
<a href="#license" class="md-nav__link">
1766
<span class="md-ellipsis">
1767
1768
License
1769
1770
</span>
1771
</a>
1772
1773
</li>
1774
1775
</ul>
1776
1777
</nav>
1778
1779
</li>
1780
1781
1782
1783
</ul>
1784
</nav>
1785
</div>
1786
</div>
1787
</div>
1788
1789
1790
1791
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
1792
<div class="md-sidebar__scrollwrap">
1793
<div class="md-sidebar__inner">
1794
1795
1796
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
1797
1798
1799
1800
1801
1802
1803
<label class="md-nav__title" for="__toc">
1804
<span class="md-nav__icon md-icon"></span>
1805
Table of contents
1806
</label>
1807
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
1808
1809
<li class="md-nav__item">
1810
<a href="#development-setup" class="md-nav__link">
1811
<span class="md-ellipsis">
1812
1813
Development setup
1814
1815
</span>
1816
</a>
1817
1818
</li>
1819
1820
<li class="md-nav__item">
1821
<a href="#running-tests" class="md-nav__link">
1822
<span class="md-ellipsis">
1823
1824
Running tests
1825
1826
</span>
1827
</a>
1828
1829
<nav class="md-nav" aria-label="Running tests">
1830
<ul class="md-nav__list">
1831
1832
<li class="md-nav__item">
1833
<a href="#test-conventions" class="md-nav__link">
1834
<span class="md-ellipsis">
1835
1836
Test conventions
1837
1838
</span>
1839
</a>
1840
1841
</li>
1842
1843
<li class="md-nav__item">
1844
<a href="#mocking-patterns" class="md-nav__link">
1845
<span class="md-ellipsis">
1846
1847
Mocking patterns
1848
1849
</span>
1850
</a>
1851
1852
</li>
1853
1854
</ul>
1855
</nav>
1856
1857
</li>
1858
1859
<li class="md-nav__item">
1860
<a href="#code-style" class="md-nav__link">
1861
<span class="md-ellipsis">
1862
1863
Code style
1864
1865
</span>
1866
</a>
1867
1868
<nav class="md-nav" aria-label="Code style">
1869
<ul class="md-nav__list">
1870
1871
<li class="md-nav__item">
1872
<a href="#ruff-configuration" class="md-nav__link">
1873
<span class="md-ellipsis">
1874
1875
Ruff configuration
1876
1877
</span>
1878
</a>
1879
1880
</li>
1881
1882
</ul>
1883
</nav>
1884
1885
</li>
1886
1887
<li class="md-nav__item">
1888
<a href="#project-structure" class="md-nav__link">
1889
<span class="md-ellipsis">
1890
1891
Project structure
1892
1893
</span>
1894
</a>
1895
1896
</li>
1897
1898
<li class="md-nav__item">
1899
<a href="#adding-a-new-provider" class="md-nav__link">
1900
<span class="md-ellipsis">
1901
1902
Adding a new provider
1903
1904
</span>
1905
</a>
1906
1907
<nav class="md-nav" aria-label="Adding a new provider">
1908
<ul class="md-nav__list">
1909
1910
<li class="md-nav__item">
1911
<a href="#example-provider-skeleton" class="md-nav__link">
1912
<span class="md-ellipsis">
1913
1914
Example provider skeleton
1915
1916
</span>
1917
</a>
1918
1919
</li>
1920
1921
<li class="md-nav__item">
1922
<a href="#openai-compatible-providers" class="md-nav__link">
1923
<span class="md-ellipsis">
1924
1925
OpenAI-compatible providers
1926
1927
</span>
1928
</a>
1929
1930
</li>
1931
1932
</ul>
1933
</nav>
1934
1935
</li>
1936
1937
<li class="md-nav__item">
1938
<a href="#adding-a-new-cloud-source" class="md-nav__link">
1939
<span class="md-ellipsis">
1940
1941
Adding a new cloud source
1942
1943
</span>
1944
</a>
1945
1946
<nav class="md-nav" aria-label="Adding a new cloud source">
1947
<ul class="md-nav__list">
1948
1949
<li class="md-nav__item">
1950
<a href="#example-source-skeleton" class="md-nav__link">
1951
<span class="md-ellipsis">
1952
1953
Example source skeleton
1954
1955
</span>
1956
</a>
1957
1958
</li>
1959
1960
<li class="md-nav__item">
1961
<a href="#registering-in-__init__py" class="md-nav__link">
1962
<span class="md-ellipsis">
1963
1964
Registering in __init__.py
1965
1966
</span>
1967
</a>
1968
1969
</li>
1970
1971
</ul>
1972
</nav>
1973
1974
</li>
1975
1976
<li class="md-nav__item">
1977
<a href="#adding-a-new-skill" class="md-nav__link">
1978
<span class="md-ellipsis">
1979
1980
Adding a new skill
1981
1982
</span>
1983
</a>
1984
1985
<nav class="md-nav" aria-label="Adding a new skill">
1986
<ul class="md-nav__list">
1987
1988
<li class="md-nav__item">
1989
<a href="#example-skill-skeleton" class="md-nav__link">
1990
<span class="md-ellipsis">
1991
1992
Example skill skeleton
1993
1994
</span>
1995
</a>
1996
1997
</li>
1998
1999
<li class="md-nav__item">
2000
<a href="#registering-in-__init__py_1" class="md-nav__link">
2001
<span class="md-ellipsis">
2002
2003
Registering in __init__.py
2004
2005
</span>
2006
</a>
2007
2008
</li>
2009
2010
</ul>
2011
</nav>
2012
2013
</li>
2014
2015
<li class="md-nav__item">
2016
<a href="#adding-a-new-document-processor" class="md-nav__link">
2017
<span class="md-ellipsis">
2018
2019
Adding a new document processor
2020
2021
</span>
2022
</a>
2023
2024
<nav class="md-nav" aria-label="Adding a new document processor">
2025
<ul class="md-nav__list">
2026
2027
<li class="md-nav__item">
2028
<a href="#example-processor-skeleton" class="md-nav__link">
2029
<span class="md-ellipsis">
2030
2031
Example processor skeleton
2032
2033
</span>
2034
</a>
2035
2036
</li>
2037
2038
<li class="md-nav__item">
2039
<a href="#registering-in-__init__py_2" class="md-nav__link">
2040
<span class="md-ellipsis">
2041
2042
Registering in __init__.py
2043
2044
</span>
2045
</a>
2046
2047
</li>
2048
2049
</ul>
2050
</nav>
2051
2052
</li>
2053
2054
<li class="md-nav__item">
2055
<a href="#adding-a-new-exporter" class="md-nav__link">
2056
<span class="md-ellipsis">
2057
2058
Adding a new exporter
2059
2060
</span>
2061
</a>
2062
2063
<nav class="md-nav" aria-label="Adding a new exporter">
2064
<ul class="md-nav__list">
2065
2066
<li class="md-nav__item">
2067
<a href="#example-exporter-skeleton" class="md-nav__link">
2068
<span class="md-ellipsis">
2069
2070
Example exporter skeleton
2071
2072
</span>
2073
</a>
2074
2075
</li>
2076
2077
<li class="md-nav__item">
2078
<a href="#adding-the-cli-command" class="md-nav__link">
2079
<span class="md-ellipsis">
2080
2081
Adding the CLI command
2082
2083
</span>
2084
</a>
2085
2086
</li>
2087
2088
</ul>
2089
</nav>
2090
2091
</li>
2092
2093
<li class="md-nav__item">
2094
<a href="#license" class="md-nav__link">
2095
<span class="md-ellipsis">
2096
2097
License
2098
2099
</span>
2100
</a>
2101
2102
</li>
2103
2104
</ul>
2105
2106
</nav>
2107
</div>
2108
</div>
2109
</div>
2110
2111
2112
2113
<div class="md-content" data-md-component="content">
2114
2115
<article class="md-content__inner md-typeset">
2116
2117
2118
2119
2120
2121
2122
2123
2124
<h1 id="contributing">Contributing<a class="headerlink" href="#contributing" title="Permanent link">&para;</a></h1>
2125
<h2 id="development-setup">Development setup<a class="headerlink" href="#development-setup" title="Permanent link">&para;</a></h2>
2126
<div class="highlight"><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a>git<span class="w"> </span>clone<span class="w"> </span>https://github.com/ConflictHQ/PlanOpticon.git
2127
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="nb">cd</span><span class="w"> </span>PlanOpticon
2128
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a>python<span class="w"> </span>-m<span class="w"> </span>venv<span class="w"> </span>.venv
2129
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="nb">source</span><span class="w"> </span>.venv/bin/activate
2130
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a>pip<span class="w"> </span>install<span class="w"> </span>-e<span class="w"> </span><span class="s2">&quot;.[dev]&quot;</span>
2131
</code></pre></div>
2132
<h2 id="running-tests">Running tests<a class="headerlink" href="#running-tests" title="Permanent link">&para;</a></h2>
2133
<p>PlanOpticon has 822+ tests covering providers, pipeline stages, document processors, knowledge graph operations, exporters, skills, and CLI commands.</p>
2134
<div class="highlight"><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a><span class="c1"># Run all tests</span>
2135
<a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a>pytest<span class="w"> </span>tests/<span class="w"> </span>-v
2136
<a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a>
2137
<a id="__codelineno-1-4" name="__codelineno-1-4" href="#__codelineno-1-4"></a><span class="c1"># Run with coverage</span>
2138
<a id="__codelineno-1-5" name="__codelineno-1-5" href="#__codelineno-1-5"></a>pytest<span class="w"> </span>tests/<span class="w"> </span>--cov<span class="o">=</span>video_processor<span class="w"> </span>--cov-report<span class="o">=</span>html
2139
<a id="__codelineno-1-6" name="__codelineno-1-6" href="#__codelineno-1-6"></a>
2140
<a id="__codelineno-1-7" name="__codelineno-1-7" href="#__codelineno-1-7"></a><span class="c1"># Run a specific test file</span>
2141
<a id="__codelineno-1-8" name="__codelineno-1-8" href="#__codelineno-1-8"></a>pytest<span class="w"> </span>tests/test_models.py<span class="w"> </span>-v
2142
<a id="__codelineno-1-9" name="__codelineno-1-9" href="#__codelineno-1-9"></a>
2143
<a id="__codelineno-1-10" name="__codelineno-1-10" href="#__codelineno-1-10"></a><span class="c1"># Run tests matching a keyword</span>
2144
<a id="__codelineno-1-11" name="__codelineno-1-11" href="#__codelineno-1-11"></a>pytest<span class="w"> </span>tests/<span class="w"> </span>-k<span class="w"> </span><span class="s2">&quot;test_knowledge_graph&quot;</span><span class="w"> </span>-v
2145
<a id="__codelineno-1-12" name="__codelineno-1-12" href="#__codelineno-1-12"></a>
2146
<a id="__codelineno-1-13" name="__codelineno-1-13" href="#__codelineno-1-13"></a><span class="c1"># Run only fast tests (skip slow integration tests)</span>
2147
<a id="__codelineno-1-14" name="__codelineno-1-14" href="#__codelineno-1-14"></a>pytest<span class="w"> </span>tests/<span class="w"> </span>-m<span class="w"> </span><span class="s2">&quot;not slow&quot;</span><span class="w"> </span>-v
2148
</code></pre></div>
2149
<h3 id="test-conventions">Test conventions<a class="headerlink" href="#test-conventions" title="Permanent link">&para;</a></h3>
2150
<ul>
2151
<li>All tests live in the <code>tests/</code> directory, mirroring the <code>video_processor/</code> package structure</li>
2152
<li>Test files are named <code>test_&lt;module&gt;.py</code></li>
2153
<li>Use <code>pytest</code> as the test runner -- do not use <code>unittest.TestCase</code> unless necessary for specific setup/teardown patterns</li>
2154
<li>Mock external API calls. Never make real API calls in tests. Use <code>unittest.mock.patch</code> or <code>pytest-mock</code> fixtures to mock provider responses.</li>
2155
<li>Use <code>tmp_path</code> (pytest fixture) for any tests that write files to disk</li>
2156
<li>Fixtures shared across test files go in <code>conftest.py</code></li>
2157
<li>For testing CLI commands, use <code>click.testing.CliRunner</code></li>
2158
<li>For testing provider implementations, mock at the HTTP client level (e.g., patch <code>requests.post</code> or the provider's SDK client)</li>
2159
</ul>
2160
<h3 id="mocking-patterns">Mocking patterns<a class="headerlink" href="#mocking-patterns" title="Permanent link">&para;</a></h3>
2161
<div class="highlight"><pre><span></span><code><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a><span class="c1"># Mocking a provider&#39;s chat method</span>
2162
<a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a><span class="kn">from</span><span class="w"> </span><span class="nn">unittest.mock</span><span class="w"> </span><span class="kn">import</span> <span class="n">MagicMock</span><span class="p">,</span> <span class="n">patch</span>
2163
<a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a>
2164
<a id="__codelineno-2-4" name="__codelineno-2-4" href="#__codelineno-2-4"></a><span class="k">def</span><span class="w"> </span><span class="nf">test_key_point_extraction</span><span class="p">():</span>
2165
<a id="__codelineno-2-5" name="__codelineno-2-5" href="#__codelineno-2-5"></a> <span class="n">pm</span> <span class="o">=</span> <span class="n">MagicMock</span><span class="p">()</span>
2166
<a id="__codelineno-2-6" name="__codelineno-2-6" href="#__codelineno-2-6"></a> <span class="n">pm</span><span class="o">.</span><span class="n">chat</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="s1">&#39;[&quot;Point 1&quot;, &quot;Point 2&quot;]&#39;</span>
2167
<a id="__codelineno-2-7" name="__codelineno-2-7" href="#__codelineno-2-7"></a> <span class="n">result</span> <span class="o">=</span> <span class="n">extract_key_points</span><span class="p">(</span><span class="n">pm</span><span class="p">,</span> <span class="s2">&quot;transcript text&quot;</span><span class="p">)</span>
2168
<a id="__codelineno-2-8" name="__codelineno-2-8" href="#__codelineno-2-8"></a> <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="o">==</span> <span class="mi">2</span>
2169
<a id="__codelineno-2-9" name="__codelineno-2-9" href="#__codelineno-2-9"></a>
2170
<a id="__codelineno-2-10" name="__codelineno-2-10" href="#__codelineno-2-10"></a><span class="c1"># Mocking an external API at the HTTP level</span>
2171
<a id="__codelineno-2-11" name="__codelineno-2-11" href="#__codelineno-2-11"></a><span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;requests.post&quot;</span><span class="p">)</span>
2172
<a id="__codelineno-2-12" name="__codelineno-2-12" href="#__codelineno-2-12"></a><span class="k">def</span><span class="w"> </span><span class="nf">test_provider_chat</span><span class="p">(</span><span class="n">mock_post</span><span class="p">):</span>
2173
<a id="__codelineno-2-13" name="__codelineno-2-13" href="#__codelineno-2-13"></a> <span class="n">mock_post</span><span class="o">.</span><span class="n">return_value</span><span class="o">.</span><span class="n">json</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="p">{</span>
2174
<a id="__codelineno-2-14" name="__codelineno-2-14" href="#__codelineno-2-14"></a> <span class="s2">&quot;choices&quot;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&quot;content&quot;</span><span class="p">:</span> <span class="s2">&quot;response&quot;</span><span class="p">}}]</span>
2175
<a id="__codelineno-2-15" name="__codelineno-2-15" href="#__codelineno-2-15"></a> <span class="p">}</span>
2176
<a id="__codelineno-2-16" name="__codelineno-2-16" href="#__codelineno-2-16"></a> <span class="n">provider</span> <span class="o">=</span> <span class="n">OpenAIProvider</span><span class="p">(</span><span class="n">api_key</span><span class="o">=</span><span class="s2">&quot;test&quot;</span><span class="p">)</span>
2177
<a id="__codelineno-2-17" name="__codelineno-2-17" href="#__codelineno-2-17"></a> <span class="n">result</span> <span class="o">=</span> <span class="n">provider</span><span class="o">.</span><span class="n">chat</span><span class="p">([{</span><span class="s2">&quot;role&quot;</span><span class="p">:</span> <span class="s2">&quot;user&quot;</span><span class="p">,</span> <span class="s2">&quot;content&quot;</span><span class="p">:</span> <span class="s2">&quot;hello&quot;</span><span class="p">}])</span>
2178
<a id="__codelineno-2-18" name="__codelineno-2-18" href="#__codelineno-2-18"></a> <span class="k">assert</span> <span class="n">result</span> <span class="o">==</span> <span class="s2">&quot;response&quot;</span>
2179
</code></pre></div>
2180
<h2 id="code-style">Code style<a class="headerlink" href="#code-style" title="Permanent link">&para;</a></h2>
2181
<p>We use:</p>
2182
<ul>
2183
<li><strong>Ruff</strong> for both linting and formatting (100 char line length)</li>
2184
<li><strong>mypy</strong> for type checking</li>
2185
</ul>
2186
<p>Ruff handles all linting (error, warning, pyflakes, and import sorting rules) and formatting in a single tool. There is no need to run Black or isort separately.</p>
2187
<div class="highlight"><pre><span></span><code><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a><span class="c1"># Lint</span>
2188
<a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a>ruff<span class="w"> </span>check<span class="w"> </span>video_processor/
2189
<a id="__codelineno-3-3" name="__codelineno-3-3" href="#__codelineno-3-3"></a>
2190
<a id="__codelineno-3-4" name="__codelineno-3-4" href="#__codelineno-3-4"></a><span class="c1"># Format</span>
2191
<a id="__codelineno-3-5" name="__codelineno-3-5" href="#__codelineno-3-5"></a>ruff<span class="w"> </span>format<span class="w"> </span>video_processor/
2192
<a id="__codelineno-3-6" name="__codelineno-3-6" href="#__codelineno-3-6"></a>
2193
<a id="__codelineno-3-7" name="__codelineno-3-7" href="#__codelineno-3-7"></a><span class="c1"># Auto-fix lint issues</span>
2194
<a id="__codelineno-3-8" name="__codelineno-3-8" href="#__codelineno-3-8"></a>ruff<span class="w"> </span>check<span class="w"> </span>video_processor/<span class="w"> </span>--fix
2195
<a id="__codelineno-3-9" name="__codelineno-3-9" href="#__codelineno-3-9"></a>
2196
<a id="__codelineno-3-10" name="__codelineno-3-10" href="#__codelineno-3-10"></a><span class="c1"># Type check</span>
2197
<a id="__codelineno-3-11" name="__codelineno-3-11" href="#__codelineno-3-11"></a>mypy<span class="w"> </span>video_processor/<span class="w"> </span>--ignore-missing-imports
2198
</code></pre></div>
2199
<h3 id="ruff-configuration">Ruff configuration<a class="headerlink" href="#ruff-configuration" title="Permanent link">&para;</a></h3>
2200
<p>The project's <code>pyproject.toml</code> configures ruff as follows:</p>
2201
<div class="highlight"><pre><span></span><code><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a><span class="k">[tool.ruff]</span>
2202
<a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a><span class="n">line-length</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">100</span>
2203
<a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a><span class="n">target-version</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">&quot;py310&quot;</span>
2204
<a id="__codelineno-4-4" name="__codelineno-4-4" href="#__codelineno-4-4"></a>
2205
<a id="__codelineno-4-5" name="__codelineno-4-5" href="#__codelineno-4-5"></a><span class="k">[tool.ruff.lint]</span>
2206
<a id="__codelineno-4-6" name="__codelineno-4-6" href="#__codelineno-4-6"></a><span class="n">select</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;E&quot;</span><span class="p">,</span><span class="w"> </span><span class="s2">&quot;F&quot;</span><span class="p">,</span><span class="w"> </span><span class="s2">&quot;W&quot;</span><span class="p">,</span><span class="w"> </span><span class="s2">&quot;I&quot;</span><span class="p">]</span>
2207
</code></pre></div>
2208
<p>The <code>I</code> rule set covers import sorting (equivalent to isort), so imports are automatically organized by ruff.</p>
2209
<h2 id="project-structure">Project structure<a class="headerlink" href="#project-structure" title="Permanent link">&para;</a></h2>
2210
<div class="highlight"><pre><span></span><code><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a>PlanOpticon/
2211
<a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a>├── video_processor/
2212
<a id="__codelineno-5-3" name="__codelineno-5-3" href="#__codelineno-5-3"></a>│ ├── cli/ # Click CLI commands
2213
<a id="__codelineno-5-4" name="__codelineno-5-4" href="#__codelineno-5-4"></a>│ │ └── commands.py
2214
<a id="__codelineno-5-5" name="__codelineno-5-5" href="#__codelineno-5-5"></a>│ ├── providers/ # LLM/API provider implementations
2215
<a id="__codelineno-5-6" name="__codelineno-5-6" href="#__codelineno-5-6"></a>│ │ ├── base.py # BaseProvider, ProviderRegistry
2216
<a id="__codelineno-5-7" name="__codelineno-5-7" href="#__codelineno-5-7"></a>│ │ ├── manager.py # ProviderManager
2217
<a id="__codelineno-5-8" name="__codelineno-5-8" href="#__codelineno-5-8"></a>│ │ ├── discovery.py # Auto-discovery of available providers
2218
<a id="__codelineno-5-9" name="__codelineno-5-9" href="#__codelineno-5-9"></a>│ │ ├── openai_provider.py
2219
<a id="__codelineno-5-10" name="__codelineno-5-10" href="#__codelineno-5-10"></a>│ │ ├── anthropic_provider.py
2220
<a id="__codelineno-5-11" name="__codelineno-5-11" href="#__codelineno-5-11"></a>│ │ ├── gemini_provider.py
2221
<a id="__codelineno-5-12" name="__codelineno-5-12" href="#__codelineno-5-12"></a>│ │ └── ... # 15+ provider implementations
2222
<a id="__codelineno-5-13" name="__codelineno-5-13" href="#__codelineno-5-13"></a>│ ├── sources/ # Cloud and web source connectors
2223
<a id="__codelineno-5-14" name="__codelineno-5-14" href="#__codelineno-5-14"></a>│ │ ├── base.py # BaseSource, SourceFile
2224
<a id="__codelineno-5-15" name="__codelineno-5-15" href="#__codelineno-5-15"></a>│ │ ├── google_drive.py
2225
<a id="__codelineno-5-16" name="__codelineno-5-16" href="#__codelineno-5-16"></a>│ │ ├── zoom_source.py
2226
<a id="__codelineno-5-17" name="__codelineno-5-17" href="#__codelineno-5-17"></a>│ │ └── ... # 20+ source implementations
2227
<a id="__codelineno-5-18" name="__codelineno-5-18" href="#__codelineno-5-18"></a>│ ├── processors/ # Document processors
2228
<a id="__codelineno-5-19" name="__codelineno-5-19" href="#__codelineno-5-19"></a>│ │ ├── base.py # DocumentProcessor, registry
2229
<a id="__codelineno-5-20" name="__codelineno-5-20" href="#__codelineno-5-20"></a>│ │ ├── ingest.py # File/directory ingestion
2230
<a id="__codelineno-5-21" name="__codelineno-5-21" href="#__codelineno-5-21"></a>│ │ ├── markdown_processor.py
2231
<a id="__codelineno-5-22" name="__codelineno-5-22" href="#__codelineno-5-22"></a>│ │ ├── pdf_processor.py
2232
<a id="__codelineno-5-23" name="__codelineno-5-23" href="#__codelineno-5-23"></a>│ │ └── __init__.py # Auto-registration of built-in processors
2233
<a id="__codelineno-5-24" name="__codelineno-5-24" href="#__codelineno-5-24"></a>│ ├── integrators/ # Knowledge graph and analysis
2234
<a id="__codelineno-5-25" name="__codelineno-5-25" href="#__codelineno-5-25"></a>│ │ ├── knowledge_graph.py # KnowledgeGraph class
2235
<a id="__codelineno-5-26" name="__codelineno-5-26" href="#__codelineno-5-26"></a>│ │ ├── graph_store.py # SQLite graph storage
2236
<a id="__codelineno-5-27" name="__codelineno-5-27" href="#__codelineno-5-27"></a>│ │ ├── graph_query.py # GraphQueryEngine
2237
<a id="__codelineno-5-28" name="__codelineno-5-28" href="#__codelineno-5-28"></a>│ │ ├── graph_discovery.py # Auto-find knowledge_graph.db
2238
<a id="__codelineno-5-29" name="__codelineno-5-29" href="#__codelineno-5-29"></a>│ │ └── taxonomy.py # Planning taxonomy classifier
2239
<a id="__codelineno-5-30" name="__codelineno-5-30" href="#__codelineno-5-30"></a>│ ├── agent/ # Planning agent
2240
<a id="__codelineno-5-31" name="__codelineno-5-31" href="#__codelineno-5-31"></a>│ │ ├── orchestrator.py # Agent orchestration
2241
<a id="__codelineno-5-32" name="__codelineno-5-32" href="#__codelineno-5-32"></a>│ │ └── skills/ # Skill implementations
2242
<a id="__codelineno-5-33" name="__codelineno-5-33" href="#__codelineno-5-33"></a>│ │ ├── base.py # Skill ABC, registry, Artifact
2243
<a id="__codelineno-5-34" name="__codelineno-5-34" href="#__codelineno-5-34"></a>│ │ ├── project_plan.py
2244
<a id="__codelineno-5-35" name="__codelineno-5-35" href="#__codelineno-5-35"></a>│ │ ├── prd.py
2245
<a id="__codelineno-5-36" name="__codelineno-5-36" href="#__codelineno-5-36"></a>│ │ ├── roadmap.py
2246
<a id="__codelineno-5-37" name="__codelineno-5-37" href="#__codelineno-5-37"></a>│ │ ├── task_breakdown.py
2247
<a id="__codelineno-5-38" name="__codelineno-5-38" href="#__codelineno-5-38"></a>│ │ ├── doc_generator.py
2248
<a id="__codelineno-5-39" name="__codelineno-5-39" href="#__codelineno-5-39"></a>│ │ ├── wiki_generator.py
2249
<a id="__codelineno-5-40" name="__codelineno-5-40" href="#__codelineno-5-40"></a>│ │ ├── notes_export.py
2250
<a id="__codelineno-5-41" name="__codelineno-5-41" href="#__codelineno-5-41"></a>│ │ ├── artifact_export.py
2251
<a id="__codelineno-5-42" name="__codelineno-5-42" href="#__codelineno-5-42"></a>│ │ ├── github_integration.py
2252
<a id="__codelineno-5-43" name="__codelineno-5-43" href="#__codelineno-5-43"></a>│ │ ├── requirements_chat.py
2253
<a id="__codelineno-5-44" name="__codelineno-5-44" href="#__codelineno-5-44"></a>│ │ ├── cli_adapter.py
2254
<a id="__codelineno-5-45" name="__codelineno-5-45" href="#__codelineno-5-45"></a>│ │ └── __init__.py # Auto-registration of skills
2255
<a id="__codelineno-5-46" name="__codelineno-5-46" href="#__codelineno-5-46"></a>│ ├── exporters/ # Output format exporters
2256
<a id="__codelineno-5-47" name="__codelineno-5-47" href="#__codelineno-5-47"></a>│ │ ├── __init__.py
2257
<a id="__codelineno-5-48" name="__codelineno-5-48" href="#__codelineno-5-48"></a>│ │ └── markdown.py # Template-based markdown generation
2258
<a id="__codelineno-5-49" name="__codelineno-5-49" href="#__codelineno-5-49"></a>│ ├── utils/ # Shared utilities
2259
<a id="__codelineno-5-50" name="__codelineno-5-50" href="#__codelineno-5-50"></a>│ │ ├── export.py # Multi-format export orchestration
2260
<a id="__codelineno-5-51" name="__codelineno-5-51" href="#__codelineno-5-51"></a>│ │ ├── rendering.py # Mermaid/chart rendering
2261
<a id="__codelineno-5-52" name="__codelineno-5-52" href="#__codelineno-5-52"></a>│ │ ├── prompt_templates.py
2262
<a id="__codelineno-5-53" name="__codelineno-5-53" href="#__codelineno-5-53"></a>│ │ ├── callbacks.py # Progress callback helpers
2263
<a id="__codelineno-5-54" name="__codelineno-5-54" href="#__codelineno-5-54"></a>│ │ └── ...
2264
<a id="__codelineno-5-55" name="__codelineno-5-55" href="#__codelineno-5-55"></a>│ ├── exchange.py # PlanOpticonExchange format
2265
<a id="__codelineno-5-56" name="__codelineno-5-56" href="#__codelineno-5-56"></a>│ ├── pipeline.py # Main video processing pipeline
2266
<a id="__codelineno-5-57" name="__codelineno-5-57" href="#__codelineno-5-57"></a>│ ├── models.py # Pydantic data models
2267
<a id="__codelineno-5-58" name="__codelineno-5-58" href="#__codelineno-5-58"></a>│ └── output_structure.py # Output directory helpers
2268
<a id="__codelineno-5-59" name="__codelineno-5-59" href="#__codelineno-5-59"></a>├── tests/ # 822+ tests
2269
<a id="__codelineno-5-60" name="__codelineno-5-60" href="#__codelineno-5-60"></a>├── knowledge-base/ # Local-first graph tools
2270
<a id="__codelineno-5-61" name="__codelineno-5-61" href="#__codelineno-5-61"></a>│ ├── viewer.html # Self-contained D3.js graph viewer
2271
<a id="__codelineno-5-62" name="__codelineno-5-62" href="#__codelineno-5-62"></a>│ └── query.py # Python query script (NetworkX)
2272
<a id="__codelineno-5-63" name="__codelineno-5-63" href="#__codelineno-5-63"></a>├── docs/ # MkDocs documentation
2273
<a id="__codelineno-5-64" name="__codelineno-5-64" href="#__codelineno-5-64"></a>└── pyproject.toml # Project configuration
2274
</code></pre></div>
2275
<p>See <a href="../architecture/overview/">Architecture Overview</a> for a more detailed breakdown of module responsibilities.</p>
2276
<h2 id="adding-a-new-provider">Adding a new provider<a class="headerlink" href="#adding-a-new-provider" title="Permanent link">&para;</a></h2>
2277
<p>Providers self-register via <code>ProviderRegistry.register()</code> at module level. When the provider module is imported, it registers itself automatically.</p>
2278
<ol>
2279
<li>Create <code>video_processor/providers/your_provider.py</code></li>
2280
<li>Extend <code>BaseProvider</code> from <code>video_processor/providers/base.py</code></li>
2281
<li>Implement the four required methods: <code>chat()</code>, <code>analyze_image()</code>, <code>transcribe_audio()</code>, <code>list_models()</code></li>
2282
<li>Call <code>ProviderRegistry.register()</code> at module level</li>
2283
<li>Add the import to <code>video_processor/providers/manager.py</code> in the lazy-import block</li>
2284
<li>Add tests in <code>tests/test_providers.py</code></li>
2285
</ol>
2286
<h3 id="example-provider-skeleton">Example provider skeleton<a class="headerlink" href="#example-provider-skeleton" title="Permanent link">&para;</a></h3>
2287
<div class="highlight"><pre><span></span><code><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a><span class="sd">&quot;&quot;&quot;Your provider implementation.&quot;&quot;&quot;</span>
2288
<a id="__codelineno-6-2" name="__codelineno-6-2" href="#__codelineno-6-2"></a>
2289
<a id="__codelineno-6-3" name="__codelineno-6-3" href="#__codelineno-6-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">video_processor.providers.base</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseProvider</span><span class="p">,</span> <span class="n">ModelInfo</span><span class="p">,</span> <span class="n">ProviderRegistry</span>
2290
<a id="__codelineno-6-4" name="__codelineno-6-4" href="#__codelineno-6-4"></a>
2291
<a id="__codelineno-6-5" name="__codelineno-6-5" href="#__codelineno-6-5"></a>
2292
<a id="__codelineno-6-6" name="__codelineno-6-6" href="#__codelineno-6-6"></a><span class="k">class</span><span class="w"> </span><span class="nc">YourProvider</span><span class="p">(</span><span class="n">BaseProvider</span><span class="p">):</span>
2293
<a id="__codelineno-6-7" name="__codelineno-6-7" href="#__codelineno-6-7"></a> <span class="n">provider_name</span> <span class="o">=</span> <span class="s2">&quot;yourprovider&quot;</span>
2294
<a id="__codelineno-6-8" name="__codelineno-6-8" href="#__codelineno-6-8"></a>
2295
<a id="__codelineno-6-9" name="__codelineno-6-9" href="#__codelineno-6-9"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">api_key</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">):</span>
2296
<a id="__codelineno-6-10" name="__codelineno-6-10" href="#__codelineno-6-10"></a> <span class="kn">import</span><span class="w"> </span><span class="nn">os</span>
2297
<a id="__codelineno-6-11" name="__codelineno-6-11" href="#__codelineno-6-11"></a> <span class="bp">self</span><span class="o">.</span><span class="n">api_key</span> <span class="o">=</span> <span class="n">api_key</span> <span class="ow">or</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;YOUR_API_KEY&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span>
2298
<a id="__codelineno-6-12" name="__codelineno-6-12" href="#__codelineno-6-12"></a>
2299
<a id="__codelineno-6-13" name="__codelineno-6-13" href="#__codelineno-6-13"></a> <span class="k">def</span><span class="w"> </span><span class="nf">chat</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">messages</span><span class="p">,</span> <span class="n">max_tokens</span><span class="o">=</span><span class="mi">4096</span><span class="p">,</span> <span class="n">temperature</span><span class="o">=</span><span class="mf">0.7</span><span class="p">,</span> <span class="n">model</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
2300
<a id="__codelineno-6-14" name="__codelineno-6-14" href="#__codelineno-6-14"></a> <span class="c1"># Implement chat completion</span>
2301
<a id="__codelineno-6-15" name="__codelineno-6-15" href="#__codelineno-6-15"></a> <span class="o">...</span>
2302
<a id="__codelineno-6-16" name="__codelineno-6-16" href="#__codelineno-6-16"></a>
2303
<a id="__codelineno-6-17" name="__codelineno-6-17" href="#__codelineno-6-17"></a> <span class="k">def</span><span class="w"> </span><span class="nf">analyze_image</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">image_bytes</span><span class="p">,</span> <span class="n">prompt</span><span class="p">,</span> <span class="n">max_tokens</span><span class="o">=</span><span class="mi">4096</span><span class="p">,</span> <span class="n">model</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
2304
<a id="__codelineno-6-18" name="__codelineno-6-18" href="#__codelineno-6-18"></a> <span class="c1"># Implement image analysis</span>
2305
<a id="__codelineno-6-19" name="__codelineno-6-19" href="#__codelineno-6-19"></a> <span class="o">...</span>
2306
<a id="__codelineno-6-20" name="__codelineno-6-20" href="#__codelineno-6-20"></a>
2307
<a id="__codelineno-6-21" name="__codelineno-6-21" href="#__codelineno-6-21"></a> <span class="k">def</span><span class="w"> </span><span class="nf">transcribe_audio</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">audio_path</span><span class="p">,</span> <span class="n">language</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">model</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
2308
<a id="__codelineno-6-22" name="__codelineno-6-22" href="#__codelineno-6-22"></a> <span class="c1"># Implement audio transcription (or raise NotImplementedError)</span>
2309
<a id="__codelineno-6-23" name="__codelineno-6-23" href="#__codelineno-6-23"></a> <span class="o">...</span>
2310
<a id="__codelineno-6-24" name="__codelineno-6-24" href="#__codelineno-6-24"></a>
2311
<a id="__codelineno-6-25" name="__codelineno-6-25" href="#__codelineno-6-25"></a> <span class="k">def</span><span class="w"> </span><span class="nf">list_models</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
2312
<a id="__codelineno-6-26" name="__codelineno-6-26" href="#__codelineno-6-26"></a> <span class="k">return</span> <span class="p">[</span><span class="n">ModelInfo</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="s2">&quot;your-model&quot;</span><span class="p">,</span> <span class="n">provider</span><span class="o">=</span><span class="s2">&quot;yourprovider&quot;</span><span class="p">,</span> <span class="n">capabilities</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;chat&quot;</span><span class="p">])]</span>
2313
<a id="__codelineno-6-27" name="__codelineno-6-27" href="#__codelineno-6-27"></a>
2314
<a id="__codelineno-6-28" name="__codelineno-6-28" href="#__codelineno-6-28"></a>
2315
<a id="__codelineno-6-29" name="__codelineno-6-29" href="#__codelineno-6-29"></a><span class="c1"># Self-registration at import time</span>
2316
<a id="__codelineno-6-30" name="__codelineno-6-30" href="#__codelineno-6-30"></a><span class="n">ProviderRegistry</span><span class="o">.</span><span class="n">register</span><span class="p">(</span>
2317
<a id="__codelineno-6-31" name="__codelineno-6-31" href="#__codelineno-6-31"></a> <span class="s2">&quot;yourprovider&quot;</span><span class="p">,</span>
2318
<a id="__codelineno-6-32" name="__codelineno-6-32" href="#__codelineno-6-32"></a> <span class="n">YourProvider</span><span class="p">,</span>
2319
<a id="__codelineno-6-33" name="__codelineno-6-33" href="#__codelineno-6-33"></a> <span class="n">env_var</span><span class="o">=</span><span class="s2">&quot;YOUR_API_KEY&quot;</span><span class="p">,</span>
2320
<a id="__codelineno-6-34" name="__codelineno-6-34" href="#__codelineno-6-34"></a> <span class="n">model_prefixes</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;your-&quot;</span><span class="p">],</span>
2321
<a id="__codelineno-6-35" name="__codelineno-6-35" href="#__codelineno-6-35"></a> <span class="n">default_models</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;chat&quot;</span><span class="p">:</span> <span class="s2">&quot;your-model&quot;</span><span class="p">},</span>
2322
<a id="__codelineno-6-36" name="__codelineno-6-36" href="#__codelineno-6-36"></a><span class="p">)</span>
2323
</code></pre></div>
2324
<h3 id="openai-compatible-providers">OpenAI-compatible providers<a class="headerlink" href="#openai-compatible-providers" title="Permanent link">&para;</a></h3>
2325
<p>For providers that use the OpenAI API format, extend <code>OpenAICompatibleProvider</code> instead of <code>BaseProvider</code>. This provides default implementations of <code>chat()</code>, <code>analyze_image()</code>, and <code>list_models()</code> -- you only need to configure the base URL and model mappings.</p>
2326
<div class="highlight"><pre><span></span><code><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">video_processor.providers.base</span><span class="w"> </span><span class="kn">import</span> <span class="n">OpenAICompatibleProvider</span><span class="p">,</span> <span class="n">ProviderRegistry</span>
2327
<a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a>
2328
<a id="__codelineno-7-3" name="__codelineno-7-3" href="#__codelineno-7-3"></a><span class="k">class</span><span class="w"> </span><span class="nc">YourProvider</span><span class="p">(</span><span class="n">OpenAICompatibleProvider</span><span class="p">):</span>
2329
<a id="__codelineno-7-4" name="__codelineno-7-4" href="#__codelineno-7-4"></a> <span class="n">provider_name</span> <span class="o">=</span> <span class="s2">&quot;yourprovider&quot;</span>
2330
<a id="__codelineno-7-5" name="__codelineno-7-5" href="#__codelineno-7-5"></a> <span class="n">base_url</span> <span class="o">=</span> <span class="s2">&quot;https://api.yourprovider.com/v1&quot;</span>
2331
<a id="__codelineno-7-6" name="__codelineno-7-6" href="#__codelineno-7-6"></a> <span class="n">env_var</span> <span class="o">=</span> <span class="s2">&quot;YOUR_API_KEY&quot;</span>
2332
<a id="__codelineno-7-7" name="__codelineno-7-7" href="#__codelineno-7-7"></a>
2333
<a id="__codelineno-7-8" name="__codelineno-7-8" href="#__codelineno-7-8"></a><span class="n">ProviderRegistry</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="s2">&quot;yourprovider&quot;</span><span class="p">,</span> <span class="n">YourProvider</span><span class="p">,</span> <span class="n">env_var</span><span class="o">=</span><span class="s2">&quot;YOUR_API_KEY&quot;</span><span class="p">)</span>
2334
</code></pre></div>
2335
<h2 id="adding-a-new-cloud-source">Adding a new cloud source<a class="headerlink" href="#adding-a-new-cloud-source" title="Permanent link">&para;</a></h2>
2336
<p>Source connectors implement the <code>BaseSource</code> ABC from <code>video_processor/sources/base.py</code>. Authentication is handled per-source, typically via environment variables.</p>
2337
<ol>
2338
<li>Create <code>video_processor/sources/your_source.py</code></li>
2339
<li>Extend <code>BaseSource</code></li>
2340
<li>Implement <code>authenticate()</code>, <code>list_videos()</code>, and <code>download()</code></li>
2341
<li>Add the class to the lazy-import map in <code>video_processor/sources/__init__.py</code></li>
2342
<li>Add CLI commands in <code>video_processor/cli/commands.py</code> if needed</li>
2343
<li>Add tests and documentation</li>
2344
</ol>
2345
<h3 id="example-source-skeleton">Example source skeleton<a class="headerlink" href="#example-source-skeleton" title="Permanent link">&para;</a></h3>
2346
<div class="highlight"><pre><span></span><code><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a><span class="sd">&quot;&quot;&quot;Your source integration.&quot;&quot;&quot;</span>
2347
<a id="__codelineno-8-2" name="__codelineno-8-2" href="#__codelineno-8-2"></a>
2348
<a id="__codelineno-8-3" name="__codelineno-8-3" href="#__codelineno-8-3"></a><span class="kn">import</span><span class="w"> </span><span class="nn">os</span>
2349
<a id="__codelineno-8-4" name="__codelineno-8-4" href="#__codelineno-8-4"></a><span class="kn">import</span><span class="w"> </span><span class="nn">logging</span>
2350
<a id="__codelineno-8-5" name="__codelineno-8-5" href="#__codelineno-8-5"></a><span class="kn">from</span><span class="w"> </span><span class="nn">pathlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">Path</span>
2351
<a id="__codelineno-8-6" name="__codelineno-8-6" href="#__codelineno-8-6"></a><span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Optional</span>
2352
<a id="__codelineno-8-7" name="__codelineno-8-7" href="#__codelineno-8-7"></a>
2353
<a id="__codelineno-8-8" name="__codelineno-8-8" href="#__codelineno-8-8"></a><span class="kn">from</span><span class="w"> </span><span class="nn">video_processor.sources.base</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseSource</span><span class="p">,</span> <span class="n">SourceFile</span>
2354
<a id="__codelineno-8-9" name="__codelineno-8-9" href="#__codelineno-8-9"></a>
2355
<a id="__codelineno-8-10" name="__codelineno-8-10" href="#__codelineno-8-10"></a><span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
2356
<a id="__codelineno-8-11" name="__codelineno-8-11" href="#__codelineno-8-11"></a>
2357
<a id="__codelineno-8-12" name="__codelineno-8-12" href="#__codelineno-8-12"></a>
2358
<a id="__codelineno-8-13" name="__codelineno-8-13" href="#__codelineno-8-13"></a><span class="k">class</span><span class="w"> </span><span class="nc">YourSource</span><span class="p">(</span><span class="n">BaseSource</span><span class="p">):</span>
2359
<a id="__codelineno-8-14" name="__codelineno-8-14" href="#__codelineno-8-14"></a> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">api_key</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">):</span>
2360
<a id="__codelineno-8-15" name="__codelineno-8-15" href="#__codelineno-8-15"></a> <span class="bp">self</span><span class="o">.</span><span class="n">api_key</span> <span class="o">=</span> <span class="n">api_key</span> <span class="ow">or</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;YOUR_SOURCE_KEY&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span>
2361
<a id="__codelineno-8-16" name="__codelineno-8-16" href="#__codelineno-8-16"></a>
2362
<a id="__codelineno-8-17" name="__codelineno-8-17" href="#__codelineno-8-17"></a> <span class="k">def</span><span class="w"> </span><span class="nf">authenticate</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
2363
<a id="__codelineno-8-18" name="__codelineno-8-18" href="#__codelineno-8-18"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;Validate credentials. Return True on success.&quot;&quot;&quot;</span>
2364
<a id="__codelineno-8-19" name="__codelineno-8-19" href="#__codelineno-8-19"></a> <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">api_key</span><span class="p">:</span>
2365
<a id="__codelineno-8-20" name="__codelineno-8-20" href="#__codelineno-8-20"></a> <span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="s2">&quot;API key not set. Set YOUR_SOURCE_KEY env var.&quot;</span><span class="p">)</span>
2366
<a id="__codelineno-8-21" name="__codelineno-8-21" href="#__codelineno-8-21"></a> <span class="k">return</span> <span class="kc">False</span>
2367
<a id="__codelineno-8-22" name="__codelineno-8-22" href="#__codelineno-8-22"></a> <span class="c1"># Make a test API call to verify credentials</span>
2368
<a id="__codelineno-8-23" name="__codelineno-8-23" href="#__codelineno-8-23"></a> <span class="o">...</span>
2369
<a id="__codelineno-8-24" name="__codelineno-8-24" href="#__codelineno-8-24"></a> <span class="k">return</span> <span class="kc">True</span>
2370
<a id="__codelineno-8-25" name="__codelineno-8-25" href="#__codelineno-8-25"></a>
2371
<a id="__codelineno-8-26" name="__codelineno-8-26" href="#__codelineno-8-26"></a> <span class="k">def</span><span class="w"> </span><span class="nf">list_videos</span><span class="p">(</span>
2372
<a id="__codelineno-8-27" name="__codelineno-8-27" href="#__codelineno-8-27"></a> <span class="bp">self</span><span class="p">,</span>
2373
<a id="__codelineno-8-28" name="__codelineno-8-28" href="#__codelineno-8-28"></a> <span class="n">folder_id</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
2374
<a id="__codelineno-8-29" name="__codelineno-8-29" href="#__codelineno-8-29"></a> <span class="n">folder_path</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
2375
<a id="__codelineno-8-30" name="__codelineno-8-30" href="#__codelineno-8-30"></a> <span class="n">patterns</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
2376
<a id="__codelineno-8-31" name="__codelineno-8-31" href="#__codelineno-8-31"></a> <span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">SourceFile</span><span class="p">]:</span>
2377
<a id="__codelineno-8-32" name="__codelineno-8-32" href="#__codelineno-8-32"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;List available video files.&quot;&quot;&quot;</span>
2378
<a id="__codelineno-8-33" name="__codelineno-8-33" href="#__codelineno-8-33"></a> <span class="o">...</span>
2379
<a id="__codelineno-8-34" name="__codelineno-8-34" href="#__codelineno-8-34"></a>
2380
<a id="__codelineno-8-35" name="__codelineno-8-35" href="#__codelineno-8-35"></a> <span class="k">def</span><span class="w"> </span><span class="nf">download</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">file</span><span class="p">:</span> <span class="n">SourceFile</span><span class="p">,</span> <span class="n">destination</span><span class="p">:</span> <span class="n">Path</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Path</span><span class="p">:</span>
2381
<a id="__codelineno-8-36" name="__codelineno-8-36" href="#__codelineno-8-36"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;Download a single file. Return the local path.&quot;&quot;&quot;</span>
2382
<a id="__codelineno-8-37" name="__codelineno-8-37" href="#__codelineno-8-37"></a> <span class="n">destination</span><span class="o">.</span><span class="n">parent</span><span class="o">.</span><span class="n">mkdir</span><span class="p">(</span><span class="n">parents</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
2383
<a id="__codelineno-8-38" name="__codelineno-8-38" href="#__codelineno-8-38"></a> <span class="c1"># Download file content to destination</span>
2384
<a id="__codelineno-8-39" name="__codelineno-8-39" href="#__codelineno-8-39"></a> <span class="o">...</span>
2385
<a id="__codelineno-8-40" name="__codelineno-8-40" href="#__codelineno-8-40"></a> <span class="k">return</span> <span class="n">destination</span>
2386
</code></pre></div>
2387
<h3 id="registering-in-__init__py">Registering in <code>__init__.py</code><a class="headerlink" href="#registering-in-__init__py" title="Permanent link">&para;</a></h3>
2388
<p>Add your source to the <code>__all__</code> list and the <code>_lazy_map</code> dictionary in <code>video_processor/sources/__init__.py</code>:</p>
2389
<div class="highlight"><pre><span></span><code><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a><span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span>
2390
<a id="__codelineno-9-2" name="__codelineno-9-2" href="#__codelineno-9-2"></a> <span class="o">...</span>
2391
<a id="__codelineno-9-3" name="__codelineno-9-3" href="#__codelineno-9-3"></a> <span class="s2">&quot;YourSource&quot;</span><span class="p">,</span>
2392
<a id="__codelineno-9-4" name="__codelineno-9-4" href="#__codelineno-9-4"></a><span class="p">]</span>
2393
<a id="__codelineno-9-5" name="__codelineno-9-5" href="#__codelineno-9-5"></a>
2394
<a id="__codelineno-9-6" name="__codelineno-9-6" href="#__codelineno-9-6"></a><span class="n">_lazy_map</span> <span class="o">=</span> <span class="p">{</span>
2395
<a id="__codelineno-9-7" name="__codelineno-9-7" href="#__codelineno-9-7"></a> <span class="o">...</span>
2396
<a id="__codelineno-9-8" name="__codelineno-9-8" href="#__codelineno-9-8"></a> <span class="s2">&quot;YourSource&quot;</span><span class="p">:</span> <span class="s2">&quot;video_processor.sources.your_source&quot;</span><span class="p">,</span>
2397
<a id="__codelineno-9-9" name="__codelineno-9-9" href="#__codelineno-9-9"></a><span class="p">}</span>
2398
</code></pre></div>
2399
<h2 id="adding-a-new-skill">Adding a new skill<a class="headerlink" href="#adding-a-new-skill" title="Permanent link">&para;</a></h2>
2400
<p>Agent skills extend the <code>Skill</code> ABC from <code>video_processor/agent/skills/base.py</code> and self-register via <code>register_skill()</code>.</p>
2401
<ol>
2402
<li>Create <code>video_processor/agent/skills/your_skill.py</code></li>
2403
<li>Extend <code>Skill</code> and set <code>name</code> and <code>description</code> class attributes</li>
2404
<li>Implement <code>execute()</code> to return an <code>Artifact</code></li>
2405
<li>Optionally override <code>can_execute()</code> for custom precondition checks</li>
2406
<li>Call <code>register_skill()</code> at module level</li>
2407
<li>Add the import to <code>video_processor/agent/skills/__init__.py</code></li>
2408
<li>Add tests</li>
2409
</ol>
2410
<h3 id="example-skill-skeleton">Example skill skeleton<a class="headerlink" href="#example-skill-skeleton" title="Permanent link">&para;</a></h3>
2411
<div class="highlight"><pre><span></span><code><a id="__codelineno-10-1" name="__codelineno-10-1" href="#__codelineno-10-1"></a><span class="sd">&quot;&quot;&quot;Your custom skill.&quot;&quot;&quot;</span>
2412
<a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a>
2413
<a id="__codelineno-10-3" name="__codelineno-10-3" href="#__codelineno-10-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">video_processor.agent.skills.base</span><span class="w"> </span><span class="kn">import</span> <span class="n">AgentContext</span><span class="p">,</span> <span class="n">Artifact</span><span class="p">,</span> <span class="n">Skill</span><span class="p">,</span> <span class="n">register_skill</span>
2414
<a id="__codelineno-10-4" name="__codelineno-10-4" href="#__codelineno-10-4"></a>
2415
<a id="__codelineno-10-5" name="__codelineno-10-5" href="#__codelineno-10-5"></a>
2416
<a id="__codelineno-10-6" name="__codelineno-10-6" href="#__codelineno-10-6"></a><span class="k">class</span><span class="w"> </span><span class="nc">YourSkill</span><span class="p">(</span><span class="n">Skill</span><span class="p">):</span>
2417
<a id="__codelineno-10-7" name="__codelineno-10-7" href="#__codelineno-10-7"></a> <span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;your_skill&quot;</span>
2418
<a id="__codelineno-10-8" name="__codelineno-10-8" href="#__codelineno-10-8"></a> <span class="n">description</span> <span class="o">=</span> <span class="s2">&quot;Generates a custom artifact from the knowledge graph.&quot;</span>
2419
<a id="__codelineno-10-9" name="__codelineno-10-9" href="#__codelineno-10-9"></a>
2420
<a id="__codelineno-10-10" name="__codelineno-10-10" href="#__codelineno-10-10"></a> <span class="k">def</span><span class="w"> </span><span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">context</span><span class="p">:</span> <span class="n">AgentContext</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Artifact</span><span class="p">:</span>
2421
<a id="__codelineno-10-11" name="__codelineno-10-11" href="#__codelineno-10-11"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;Generate the artifact.&quot;&quot;&quot;</span>
2422
<a id="__codelineno-10-12" name="__codelineno-10-12" href="#__codelineno-10-12"></a> <span class="n">kg_data</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="n">knowledge_graph</span><span class="o">.</span><span class="n">to_dict</span><span class="p">()</span>
2423
<a id="__codelineno-10-13" name="__codelineno-10-13" href="#__codelineno-10-13"></a> <span class="c1"># Build content from knowledge graph data</span>
2424
<a id="__codelineno-10-14" name="__codelineno-10-14" href="#__codelineno-10-14"></a> <span class="n">content</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;# Your Artifact</span><span class="se">\n\n</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">kg_data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;entities&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">[]))</span><span class="si">}</span><span class="s2"> entities found.&quot;</span>
2425
<a id="__codelineno-10-15" name="__codelineno-10-15" href="#__codelineno-10-15"></a> <span class="k">return</span> <span class="n">Artifact</span><span class="p">(</span>
2426
<a id="__codelineno-10-16" name="__codelineno-10-16" href="#__codelineno-10-16"></a> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;your_artifact&quot;</span><span class="p">,</span>
2427
<a id="__codelineno-10-17" name="__codelineno-10-17" href="#__codelineno-10-17"></a> <span class="n">content</span><span class="o">=</span><span class="n">content</span><span class="p">,</span>
2428
<a id="__codelineno-10-18" name="__codelineno-10-18" href="#__codelineno-10-18"></a> <span class="n">artifact_type</span><span class="o">=</span><span class="s2">&quot;document&quot;</span><span class="p">,</span>
2429
<a id="__codelineno-10-19" name="__codelineno-10-19" href="#__codelineno-10-19"></a> <span class="nb">format</span><span class="o">=</span><span class="s2">&quot;markdown&quot;</span><span class="p">,</span>
2430
<a id="__codelineno-10-20" name="__codelineno-10-20" href="#__codelineno-10-20"></a> <span class="p">)</span>
2431
<a id="__codelineno-10-21" name="__codelineno-10-21" href="#__codelineno-10-21"></a>
2432
<a id="__codelineno-10-22" name="__codelineno-10-22" href="#__codelineno-10-22"></a> <span class="k">def</span><span class="w"> </span><span class="nf">can_execute</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">context</span><span class="p">:</span> <span class="n">AgentContext</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
2433
<a id="__codelineno-10-23" name="__codelineno-10-23" href="#__codelineno-10-23"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;Check prerequisites (default requires KG + provider).&quot;&quot;&quot;</span>
2434
<a id="__codelineno-10-24" name="__codelineno-10-24" href="#__codelineno-10-24"></a> <span class="k">return</span> <span class="n">context</span><span class="o">.</span><span class="n">knowledge_graph</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
2435
<a id="__codelineno-10-25" name="__codelineno-10-25" href="#__codelineno-10-25"></a>
2436
<a id="__codelineno-10-26" name="__codelineno-10-26" href="#__codelineno-10-26"></a>
2437
<a id="__codelineno-10-27" name="__codelineno-10-27" href="#__codelineno-10-27"></a><span class="c1"># Self-registration at import time</span>
2438
<a id="__codelineno-10-28" name="__codelineno-10-28" href="#__codelineno-10-28"></a><span class="n">register_skill</span><span class="p">(</span><span class="n">YourSkill</span><span class="p">())</span>
2439
</code></pre></div>
2440
<h3 id="registering-in-__init__py_1">Registering in <code>__init__.py</code><a class="headerlink" href="#registering-in-__init__py_1" title="Permanent link">&para;</a></h3>
2441
<p>Add the import to <code>video_processor/agent/skills/__init__.py</code> so the skill is loaded (and self-registered) when the skills package is imported:</p>
2442
<div class="highlight"><pre><span></span><code><a id="__codelineno-11-1" name="__codelineno-11-1" href="#__codelineno-11-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">video_processor.agent.skills</span><span class="w"> </span><span class="kn">import</span> <span class="p">(</span>
2443
<a id="__codelineno-11-2" name="__codelineno-11-2" href="#__codelineno-11-2"></a> <span class="o">...</span>
2444
<a id="__codelineno-11-3" name="__codelineno-11-3" href="#__codelineno-11-3"></a> <span class="n">your_skill</span><span class="p">,</span> <span class="c1"># noqa: F401</span>
2445
<a id="__codelineno-11-4" name="__codelineno-11-4" href="#__codelineno-11-4"></a><span class="p">)</span>
2446
</code></pre></div>
2447
<h2 id="adding-a-new-document-processor">Adding a new document processor<a class="headerlink" href="#adding-a-new-document-processor" title="Permanent link">&para;</a></h2>
2448
<p>Document processors extend the <code>DocumentProcessor</code> ABC from <code>video_processor/processors/base.py</code> and are registered via <code>register_processor()</code>.</p>
2449
<ol>
2450
<li>Create <code>video_processor/processors/your_processor.py</code></li>
2451
<li>Extend <code>DocumentProcessor</code></li>
2452
<li>Set <code>supported_extensions</code> class attribute</li>
2453
<li>Implement <code>process()</code> (returns <code>List[DocumentChunk]</code>) and <code>can_process()</code></li>
2454
<li>Call <code>register_processor()</code> at module level</li>
2455
<li>Add the import to <code>video_processor/processors/__init__.py</code></li>
2456
<li>Add tests</li>
2457
</ol>
2458
<h3 id="example-processor-skeleton">Example processor skeleton<a class="headerlink" href="#example-processor-skeleton" title="Permanent link">&para;</a></h3>
2459
<div class="highlight"><pre><span></span><code><a id="__codelineno-12-1" name="__codelineno-12-1" href="#__codelineno-12-1"></a><span class="sd">&quot;&quot;&quot;Your document processor.&quot;&quot;&quot;</span>
2460
<a id="__codelineno-12-2" name="__codelineno-12-2" href="#__codelineno-12-2"></a>
2461
<a id="__codelineno-12-3" name="__codelineno-12-3" href="#__codelineno-12-3"></a><span class="kn">from</span><span class="w"> </span><span class="nn">pathlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">Path</span>
2462
<a id="__codelineno-12-4" name="__codelineno-12-4" href="#__codelineno-12-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">List</span>
2463
<a id="__codelineno-12-5" name="__codelineno-12-5" href="#__codelineno-12-5"></a>
2464
<a id="__codelineno-12-6" name="__codelineno-12-6" href="#__codelineno-12-6"></a><span class="kn">from</span><span class="w"> </span><span class="nn">video_processor.processors.base</span><span class="w"> </span><span class="kn">import</span> <span class="p">(</span>
2465
<a id="__codelineno-12-7" name="__codelineno-12-7" href="#__codelineno-12-7"></a> <span class="n">DocumentChunk</span><span class="p">,</span>
2466
<a id="__codelineno-12-8" name="__codelineno-12-8" href="#__codelineno-12-8"></a> <span class="n">DocumentProcessor</span><span class="p">,</span>
2467
<a id="__codelineno-12-9" name="__codelineno-12-9" href="#__codelineno-12-9"></a> <span class="n">register_processor</span><span class="p">,</span>
2468
<a id="__codelineno-12-10" name="__codelineno-12-10" href="#__codelineno-12-10"></a><span class="p">)</span>
2469
<a id="__codelineno-12-11" name="__codelineno-12-11" href="#__codelineno-12-11"></a>
2470
<a id="__codelineno-12-12" name="__codelineno-12-12" href="#__codelineno-12-12"></a>
2471
<a id="__codelineno-12-13" name="__codelineno-12-13" href="#__codelineno-12-13"></a><span class="k">class</span><span class="w"> </span><span class="nc">YourProcessor</span><span class="p">(</span><span class="n">DocumentProcessor</span><span class="p">):</span>
2472
<a id="__codelineno-12-14" name="__codelineno-12-14" href="#__codelineno-12-14"></a> <span class="n">supported_extensions</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;.xyz&quot;</span><span class="p">,</span> <span class="s2">&quot;.abc&quot;</span><span class="p">]</span>
2473
<a id="__codelineno-12-15" name="__codelineno-12-15" href="#__codelineno-12-15"></a>
2474
<a id="__codelineno-12-16" name="__codelineno-12-16" href="#__codelineno-12-16"></a> <span class="k">def</span><span class="w"> </span><span class="nf">can_process</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">path</span><span class="p">:</span> <span class="n">Path</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
2475
<a id="__codelineno-12-17" name="__codelineno-12-17" href="#__codelineno-12-17"></a> <span class="k">return</span> <span class="n">path</span><span class="o">.</span><span class="n">suffix</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">supported_extensions</span>
2476
<a id="__codelineno-12-18" name="__codelineno-12-18" href="#__codelineno-12-18"></a>
2477
<a id="__codelineno-12-19" name="__codelineno-12-19" href="#__codelineno-12-19"></a> <span class="k">def</span><span class="w"> </span><span class="nf">process</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">path</span><span class="p">:</span> <span class="n">Path</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">DocumentChunk</span><span class="p">]:</span>
2478
<a id="__codelineno-12-20" name="__codelineno-12-20" href="#__codelineno-12-20"></a> <span class="n">text</span> <span class="o">=</span> <span class="n">path</span><span class="o">.</span><span class="n">read_text</span><span class="p">()</span>
2479
<a id="__codelineno-12-21" name="__codelineno-12-21" href="#__codelineno-12-21"></a> <span class="c1"># Split into chunks as appropriate for your format</span>
2480
<a id="__codelineno-12-22" name="__codelineno-12-22" href="#__codelineno-12-22"></a> <span class="k">return</span> <span class="p">[</span>
2481
<a id="__codelineno-12-23" name="__codelineno-12-23" href="#__codelineno-12-23"></a> <span class="n">DocumentChunk</span><span class="p">(</span>
2482
<a id="__codelineno-12-24" name="__codelineno-12-24" href="#__codelineno-12-24"></a> <span class="n">text</span><span class="o">=</span><span class="n">text</span><span class="p">,</span>
2483
<a id="__codelineno-12-25" name="__codelineno-12-25" href="#__codelineno-12-25"></a> <span class="n">source_file</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">path</span><span class="p">),</span>
2484
<a id="__codelineno-12-26" name="__codelineno-12-26" href="#__codelineno-12-26"></a> <span class="n">chunk_index</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span>
2485
<a id="__codelineno-12-27" name="__codelineno-12-27" href="#__codelineno-12-27"></a> <span class="n">metadata</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;format&quot;</span><span class="p">:</span> <span class="s2">&quot;xyz&quot;</span><span class="p">},</span>
2486
<a id="__codelineno-12-28" name="__codelineno-12-28" href="#__codelineno-12-28"></a> <span class="p">)</span>
2487
<a id="__codelineno-12-29" name="__codelineno-12-29" href="#__codelineno-12-29"></a> <span class="p">]</span>
2488
<a id="__codelineno-12-30" name="__codelineno-12-30" href="#__codelineno-12-30"></a>
2489
<a id="__codelineno-12-31" name="__codelineno-12-31" href="#__codelineno-12-31"></a>
2490
<a id="__codelineno-12-32" name="__codelineno-12-32" href="#__codelineno-12-32"></a><span class="c1"># Self-registration at import time</span>
2491
<a id="__codelineno-12-33" name="__codelineno-12-33" href="#__codelineno-12-33"></a><span class="n">register_processor</span><span class="p">([</span><span class="s2">&quot;.xyz&quot;</span><span class="p">,</span> <span class="s2">&quot;.abc&quot;</span><span class="p">],</span> <span class="n">YourProcessor</span><span class="p">)</span>
2492
</code></pre></div>
2493
<h3 id="registering-in-__init__py_2">Registering in <code>__init__.py</code><a class="headerlink" href="#registering-in-__init__py_2" title="Permanent link">&para;</a></h3>
2494
<p>Add the import to <code>video_processor/processors/__init__.py</code>:</p>
2495
<div class="highlight"><pre><span></span><code><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">video_processor.processors</span><span class="w"> </span><span class="kn">import</span> <span class="p">(</span>
2496
<a id="__codelineno-13-2" name="__codelineno-13-2" href="#__codelineno-13-2"></a> <span class="n">markdown_processor</span><span class="p">,</span> <span class="c1"># noqa: F401, E402</span>
2497
<a id="__codelineno-13-3" name="__codelineno-13-3" href="#__codelineno-13-3"></a> <span class="n">pdf_processor</span><span class="p">,</span> <span class="c1"># noqa: F401, E402</span>
2498
<a id="__codelineno-13-4" name="__codelineno-13-4" href="#__codelineno-13-4"></a> <span class="n">your_processor</span><span class="p">,</span> <span class="c1"># noqa: F401, E402</span>
2499
<a id="__codelineno-13-5" name="__codelineno-13-5" href="#__codelineno-13-5"></a><span class="p">)</span>
2500
</code></pre></div>
2501
<h2 id="adding-a-new-exporter">Adding a new exporter<a class="headerlink" href="#adding-a-new-exporter" title="Permanent link">&para;</a></h2>
2502
<p>Exporters live in <code>video_processor/exporters/</code> and are typically called from CLI commands. There is no strict ABC for exporters -- they are plain functions that accept knowledge graph data and an output directory.</p>
2503
<ol>
2504
<li>Create <code>video_processor/exporters/your_exporter.py</code></li>
2505
<li>Implement one or more export functions that accept KG data (as a dict) and an output path</li>
2506
<li>Add CLI integration in <code>video_processor/cli/commands.py</code> under the <code>export</code> group</li>
2507
<li>Add tests</li>
2508
</ol>
2509
<h3 id="example-exporter-skeleton">Example exporter skeleton<a class="headerlink" href="#example-exporter-skeleton" title="Permanent link">&para;</a></h3>
2510
<div class="highlight"><pre><span></span><code><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a><span class="sd">&quot;&quot;&quot;Your exporter.&quot;&quot;&quot;</span>
2511
<a id="__codelineno-14-2" name="__codelineno-14-2" href="#__codelineno-14-2"></a>
2512
<a id="__codelineno-14-3" name="__codelineno-14-3" href="#__codelineno-14-3"></a><span class="kn">import</span><span class="w"> </span><span class="nn">json</span>
2513
<a id="__codelineno-14-4" name="__codelineno-14-4" href="#__codelineno-14-4"></a><span class="kn">from</span><span class="w"> </span><span class="nn">pathlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">Path</span>
2514
<a id="__codelineno-14-5" name="__codelineno-14-5" href="#__codelineno-14-5"></a><span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">List</span>
2515
<a id="__codelineno-14-6" name="__codelineno-14-6" href="#__codelineno-14-6"></a>
2516
<a id="__codelineno-14-7" name="__codelineno-14-7" href="#__codelineno-14-7"></a>
2517
<a id="__codelineno-14-8" name="__codelineno-14-8" href="#__codelineno-14-8"></a><span class="k">def</span><span class="w"> </span><span class="nf">export_your_format</span><span class="p">(</span><span class="n">kg_data</span><span class="p">:</span> <span class="nb">dict</span><span class="p">,</span> <span class="n">output_dir</span><span class="p">:</span> <span class="n">Path</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">Path</span><span class="p">]:</span>
2518
<a id="__codelineno-14-9" name="__codelineno-14-9" href="#__codelineno-14-9"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;Export knowledge graph data in your format.</span>
2519
<a id="__codelineno-14-10" name="__codelineno-14-10" href="#__codelineno-14-10"></a>
2520
<a id="__codelineno-14-11" name="__codelineno-14-11" href="#__codelineno-14-11"></a><span class="sd"> Args:</span>
2521
<a id="__codelineno-14-12" name="__codelineno-14-12" href="#__codelineno-14-12"></a><span class="sd"> kg_data: Knowledge graph as a dict (from KnowledgeGraph.to_dict()).</span>
2522
<a id="__codelineno-14-13" name="__codelineno-14-13" href="#__codelineno-14-13"></a><span class="sd"> output_dir: Directory to write output files.</span>
2523
<a id="__codelineno-14-14" name="__codelineno-14-14" href="#__codelineno-14-14"></a>
2524
<a id="__codelineno-14-15" name="__codelineno-14-15" href="#__codelineno-14-15"></a><span class="sd"> Returns:</span>
2525
<a id="__codelineno-14-16" name="__codelineno-14-16" href="#__codelineno-14-16"></a><span class="sd"> List of created file paths.</span>
2526
<a id="__codelineno-14-17" name="__codelineno-14-17" href="#__codelineno-14-17"></a><span class="sd"> &quot;&quot;&quot;</span>
2527
<a id="__codelineno-14-18" name="__codelineno-14-18" href="#__codelineno-14-18"></a> <span class="n">output_dir</span><span class="o">.</span><span class="n">mkdir</span><span class="p">(</span><span class="n">parents</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
2528
<a id="__codelineno-14-19" name="__codelineno-14-19" href="#__codelineno-14-19"></a> <span class="n">created</span> <span class="o">=</span> <span class="p">[]</span>
2529
<a id="__codelineno-14-20" name="__codelineno-14-20" href="#__codelineno-14-20"></a>
2530
<a id="__codelineno-14-21" name="__codelineno-14-21" href="#__codelineno-14-21"></a> <span class="n">output_file</span> <span class="o">=</span> <span class="n">output_dir</span> <span class="o">/</span> <span class="s2">&quot;export.xyz&quot;</span>
2531
<a id="__codelineno-14-22" name="__codelineno-14-22" href="#__codelineno-14-22"></a> <span class="n">output_file</span><span class="o">.</span><span class="n">write_text</span><span class="p">(</span><span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">kg_data</span><span class="p">,</span> <span class="n">indent</span><span class="o">=</span><span class="mi">2</span><span class="p">))</span>
2532
<a id="__codelineno-14-23" name="__codelineno-14-23" href="#__codelineno-14-23"></a> <span class="n">created</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">output_file</span><span class="p">)</span>
2533
<a id="__codelineno-14-24" name="__codelineno-14-24" href="#__codelineno-14-24"></a>
2534
<a id="__codelineno-14-25" name="__codelineno-14-25" href="#__codelineno-14-25"></a> <span class="k">return</span> <span class="n">created</span>
2535
</code></pre></div>
2536
<h3 id="adding-the-cli-command">Adding the CLI command<a class="headerlink" href="#adding-the-cli-command" title="Permanent link">&para;</a></h3>
2537
<p>Add a subcommand under the <code>export</code> group in <code>video_processor/cli/commands.py</code>:</p>
2538
<div class="highlight"><pre><span></span><code><a id="__codelineno-15-1" name="__codelineno-15-1" href="#__codelineno-15-1"></a><span class="nd">@export</span><span class="o">.</span><span class="n">command</span><span class="p">(</span><span class="s2">&quot;your-format&quot;</span><span class="p">)</span>
2539
<a id="__codelineno-15-2" name="__codelineno-15-2" href="#__codelineno-15-2"></a><span class="nd">@click</span><span class="o">.</span><span class="n">argument</span><span class="p">(</span><span class="s2">&quot;db_path&quot;</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="n">click</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="n">exists</span><span class="o">=</span><span class="kc">True</span><span class="p">))</span>
2540
<a id="__codelineno-15-3" name="__codelineno-15-3" href="#__codelineno-15-3"></a><span class="nd">@click</span><span class="o">.</span><span class="n">option</span><span class="p">(</span><span class="s2">&quot;-o&quot;</span><span class="p">,</span> <span class="s2">&quot;--output&quot;</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="n">click</span><span class="o">.</span><span class="n">Path</span><span class="p">(),</span> <span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span>
2541
<a id="__codelineno-15-4" name="__codelineno-15-4" href="#__codelineno-15-4"></a><span class="k">def</span><span class="w"> </span><span class="nf">export_your_format_cmd</span><span class="p">(</span><span class="n">db_path</span><span class="p">,</span> <span class="n">output</span><span class="p">):</span>
2542
<a id="__codelineno-15-5" name="__codelineno-15-5" href="#__codelineno-15-5"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;Export knowledge graph in your format.&quot;&quot;&quot;</span>
2543
<a id="__codelineno-15-6" name="__codelineno-15-6" href="#__codelineno-15-6"></a> <span class="kn">from</span><span class="w"> </span><span class="nn">video_processor.exporters.your_exporter</span><span class="w"> </span><span class="kn">import</span> <span class="n">export_your_format</span>
2544
<a id="__codelineno-15-7" name="__codelineno-15-7" href="#__codelineno-15-7"></a> <span class="kn">from</span><span class="w"> </span><span class="nn">video_processor.integrators.knowledge_graph</span><span class="w"> </span><span class="kn">import</span> <span class="n">KnowledgeGraph</span>
2545
<a id="__codelineno-15-8" name="__codelineno-15-8" href="#__codelineno-15-8"></a>
2546
<a id="__codelineno-15-9" name="__codelineno-15-9" href="#__codelineno-15-9"></a> <span class="n">kg</span> <span class="o">=</span> <span class="n">KnowledgeGraph</span><span class="p">(</span><span class="n">db_path</span><span class="o">=</span><span class="n">Path</span><span class="p">(</span><span class="n">db_path</span><span class="p">))</span>
2547
<a id="__codelineno-15-10" name="__codelineno-15-10" href="#__codelineno-15-10"></a> <span class="n">out_dir</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">output</span><span class="p">)</span> <span class="k">if</span> <span class="n">output</span> <span class="k">else</span> <span class="n">Path</span><span class="o">.</span><span class="n">cwd</span><span class="p">()</span> <span class="o">/</span> <span class="s2">&quot;your-export&quot;</span>
2548
<a id="__codelineno-15-11" name="__codelineno-15-11" href="#__codelineno-15-11"></a> <span class="n">created</span> <span class="o">=</span> <span class="n">export_your_format</span><span class="p">(</span><span class="n">kg</span><span class="o">.</span><span class="n">to_dict</span><span class="p">(),</span> <span class="n">out_dir</span><span class="p">)</span>
2549
<a id="__codelineno-15-12" name="__codelineno-15-12" href="#__codelineno-15-12"></a> <span class="n">click</span><span class="o">.</span><span class="n">echo</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Exported </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">created</span><span class="p">)</span><span class="si">}</span><span class="s2"> files to </span><span class="si">{</span><span class="n">out_dir</span><span class="si">}</span><span class="s2">/&quot;</span><span class="p">)</span>
2550
</code></pre></div>
2551
<h2 id="license">License<a class="headerlink" href="#license" title="Permanent link">&para;</a></h2>
2552
<p>MIT License -- Copyright (c) 2026 CONFLICT LLC. All rights reserved.</p>
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
</article>
2567
</div>
2568
2569
2570
<script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script>
2571
2572
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
2573
</div>
2574
2575
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
2576
2577
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg>
2578
Back to top
2579
</button>
2580
2581
</main>
2582
2583
<footer class="md-footer">
2584
2585
<div class="md-footer-meta md-typeset">
2586
<div class="md-footer-meta__inner md-grid">
2587
<div class="md-copyright">
2588
2589
<div class="md-copyright__highlight">
2590
Copyright &copy; 2026 CONFLICT LLC
2591
</div>
2592
2593
2594
Made with
2595
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
2596
Material for MkDocs
2597
</a>
2598
2599
</div>
2600
2601
2602
<div class="md-social">
2603
2604
2605
2606
2607
2608
2609
2610
2611
<a href="https://github.com/ConflictHQ/PlanOpticon" target="_blank" rel="noopener" title="github.com" class="md-social__link">
2612
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
2613
</a>
2614
2615
</div>
2616
2617
</div>
2618
</div>
2619
</footer>
2620
2621
</div>
2622
<div class="md-dialog" data-md-component="dialog">
2623
<div class="md-dialog__inner md-typeset"></div>
2624
</div>
2625
2626
2627
2628
2629
2630
<script id="__config" type="application/json">{"annotate": null, "base": "..", "features": ["navigation.instant", "navigation.tabs", "navigation.sections", "navigation.expand", "navigation.top", "search.suggest", "search.highlight", "content.code.copy", "content.tabs.link", "header.autohide"], "search": "../assets/javascripts/workers/search.2c215733.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
2631
2632
2633
<script src="../assets/javascripts/bundle.79ae519e.min.js"></script>
2634
2635
2636
</body>
2637
</html>

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button