agent-recipes 0.0.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. agent_recipes/__init__.py +27 -0
  2. agent_recipes/recipe_runtime/__init__.py +28 -0
  3. agent_recipes/recipe_runtime/core.py +385 -0
  4. agent_recipes/templates/ai-ab-hook-tester/recipe.yaml +45 -0
  5. agent_recipes/templates/ai-ab-hook-tester/tools.py +169 -0
  6. agent_recipes/templates/ai-angle-generator/recipe.yaml +49 -0
  7. agent_recipes/templates/ai-angle-generator/tools.py +182 -0
  8. agent_recipes/templates/ai-api-doc-generator/README.md +59 -0
  9. agent_recipes/templates/ai-api-doc-generator/TEMPLATE.yaml +29 -0
  10. agent_recipes/templates/ai-api-tester/README.md +60 -0
  11. agent_recipes/templates/ai-api-tester/TEMPLATE.yaml +29 -0
  12. agent_recipes/templates/ai-audio-enhancer/README.md +59 -0
  13. agent_recipes/templates/ai-audio-enhancer/TEMPLATE.yaml +28 -0
  14. agent_recipes/templates/ai-audio-normalizer/README.md +13 -0
  15. agent_recipes/templates/ai-audio-normalizer/TEMPLATE.yaml +44 -0
  16. agent_recipes/templates/ai-audio-splitter/README.md +14 -0
  17. agent_recipes/templates/ai-audio-splitter/TEMPLATE.yaml +47 -0
  18. agent_recipes/templates/ai-background-music-generator/README.md +59 -0
  19. agent_recipes/templates/ai-background-music-generator/TEMPLATE.yaml +28 -0
  20. agent_recipes/templates/ai-background-remover/README.md +60 -0
  21. agent_recipes/templates/ai-background-remover/TEMPLATE.yaml +27 -0
  22. agent_recipes/templates/ai-barcode-scanner/README.md +60 -0
  23. agent_recipes/templates/ai-barcode-scanner/TEMPLATE.yaml +26 -0
  24. agent_recipes/templates/ai-blog-generator/README.md +59 -0
  25. agent_recipes/templates/ai-blog-generator/TEMPLATE.yaml +28 -0
  26. agent_recipes/templates/ai-brief-generator/recipe.yaml +52 -0
  27. agent_recipes/templates/ai-brief-generator/tools.py +231 -0
  28. agent_recipes/templates/ai-broll-builder/recipe.yaml +47 -0
  29. agent_recipes/templates/ai-broll-builder/tools.py +204 -0
  30. agent_recipes/templates/ai-calendar-scheduler/README.md +60 -0
  31. agent_recipes/templates/ai-calendar-scheduler/TEMPLATE.yaml +29 -0
  32. agent_recipes/templates/ai-changelog-generator/README.md +14 -0
  33. agent_recipes/templates/ai-changelog-generator/TEMPLATE.yaml +46 -0
  34. agent_recipes/templates/ai-chart-generator/README.md +61 -0
  35. agent_recipes/templates/ai-chart-generator/TEMPLATE.yaml +32 -0
  36. agent_recipes/templates/ai-code-documenter/README.md +12 -0
  37. agent_recipes/templates/ai-code-documenter/TEMPLATE.yaml +37 -0
  38. agent_recipes/templates/ai-code-refactorer/README.md +59 -0
  39. agent_recipes/templates/ai-code-refactorer/TEMPLATE.yaml +28 -0
  40. agent_recipes/templates/ai-code-reviewer/README.md +59 -0
  41. agent_recipes/templates/ai-code-reviewer/TEMPLATE.yaml +31 -0
  42. agent_recipes/templates/ai-color-palette-extractor/README.md +60 -0
  43. agent_recipes/templates/ai-color-palette-extractor/TEMPLATE.yaml +27 -0
  44. agent_recipes/templates/ai-comment-miner/recipe.yaml +40 -0
  45. agent_recipes/templates/ai-comment-miner/tools.py +141 -0
  46. agent_recipes/templates/ai-commit-message-generator/README.md +59 -0
  47. agent_recipes/templates/ai-commit-message-generator/TEMPLATE.yaml +31 -0
  48. agent_recipes/templates/ai-content-calendar/recipe.yaml +43 -0
  49. agent_recipes/templates/ai-content-calendar/tools.py +170 -0
  50. agent_recipes/templates/ai-context-enricher/recipe.yaml +48 -0
  51. agent_recipes/templates/ai-context-enricher/tools.py +258 -0
  52. agent_recipes/templates/ai-contract-analyzer/README.md +60 -0
  53. agent_recipes/templates/ai-contract-analyzer/TEMPLATE.yaml +34 -0
  54. agent_recipes/templates/ai-csv-cleaner/README.md +13 -0
  55. agent_recipes/templates/ai-csv-cleaner/TEMPLATE.yaml +45 -0
  56. agent_recipes/templates/ai-cta-generator/recipe.yaml +54 -0
  57. agent_recipes/templates/ai-cta-generator/tools.py +174 -0
  58. agent_recipes/templates/ai-daily-news-show/recipe.yaml +103 -0
  59. agent_recipes/templates/ai-daily-news-show/tools.py +308 -0
  60. agent_recipes/templates/ai-data-anonymizer/README.md +60 -0
  61. agent_recipes/templates/ai-data-anonymizer/TEMPLATE.yaml +31 -0
  62. agent_recipes/templates/ai-data-profiler/README.md +14 -0
  63. agent_recipes/templates/ai-data-profiler/TEMPLATE.yaml +42 -0
  64. agent_recipes/templates/ai-dependency-auditor/README.md +12 -0
  65. agent_recipes/templates/ai-dependency-auditor/TEMPLATE.yaml +37 -0
  66. agent_recipes/templates/ai-doc-translator/README.md +12 -0
  67. agent_recipes/templates/ai-doc-translator/TEMPLATE.yaml +41 -0
  68. agent_recipes/templates/ai-duplicate-finder/README.md +59 -0
  69. agent_recipes/templates/ai-duplicate-finder/TEMPLATE.yaml +28 -0
  70. agent_recipes/templates/ai-ebook-converter/README.md +60 -0
  71. agent_recipes/templates/ai-ebook-converter/TEMPLATE.yaml +27 -0
  72. agent_recipes/templates/ai-email-parser/README.md +59 -0
  73. agent_recipes/templates/ai-email-parser/TEMPLATE.yaml +29 -0
  74. agent_recipes/templates/ai-etl-pipeline/README.md +60 -0
  75. agent_recipes/templates/ai-etl-pipeline/TEMPLATE.yaml +30 -0
  76. agent_recipes/templates/ai-excel-formula-generator/README.md +59 -0
  77. agent_recipes/templates/ai-excel-formula-generator/TEMPLATE.yaml +28 -0
  78. agent_recipes/templates/ai-face-blur/README.md +60 -0
  79. agent_recipes/templates/ai-face-blur/TEMPLATE.yaml +28 -0
  80. agent_recipes/templates/ai-fact-checker/recipe.yaml +52 -0
  81. agent_recipes/templates/ai-fact-checker/tools.py +279 -0
  82. agent_recipes/templates/ai-faq-generator/README.md +59 -0
  83. agent_recipes/templates/ai-faq-generator/TEMPLATE.yaml +28 -0
  84. agent_recipes/templates/ai-file-organizer/README.md +59 -0
  85. agent_recipes/templates/ai-file-organizer/TEMPLATE.yaml +29 -0
  86. agent_recipes/templates/ai-folder-packager/README.md +15 -0
  87. agent_recipes/templates/ai-folder-packager/TEMPLATE.yaml +48 -0
  88. agent_recipes/templates/ai-form-filler/README.md +60 -0
  89. agent_recipes/templates/ai-form-filler/TEMPLATE.yaml +30 -0
  90. agent_recipes/templates/ai-hashtag-optimizer/recipe.yaml +45 -0
  91. agent_recipes/templates/ai-hashtag-optimizer/tools.py +134 -0
  92. agent_recipes/templates/ai-hook-generator/recipe.yaml +50 -0
  93. agent_recipes/templates/ai-hook-generator/tools.py +177 -0
  94. agent_recipes/templates/ai-image-captioner/README.md +59 -0
  95. agent_recipes/templates/ai-image-captioner/TEMPLATE.yaml +28 -0
  96. agent_recipes/templates/ai-image-cataloger/README.md +13 -0
  97. agent_recipes/templates/ai-image-cataloger/TEMPLATE.yaml +39 -0
  98. agent_recipes/templates/ai-image-optimizer/README.md +13 -0
  99. agent_recipes/templates/ai-image-optimizer/TEMPLATE.yaml +43 -0
  100. agent_recipes/templates/ai-image-resizer/README.md +12 -0
  101. agent_recipes/templates/ai-image-resizer/TEMPLATE.yaml +39 -0
  102. agent_recipes/templates/ai-image-tagger/README.md +59 -0
  103. agent_recipes/templates/ai-image-tagger/TEMPLATE.yaml +28 -0
  104. agent_recipes/templates/ai-image-upscaler/README.md +60 -0
  105. agent_recipes/templates/ai-image-upscaler/TEMPLATE.yaml +27 -0
  106. agent_recipes/templates/ai-invoice-processor/README.md +60 -0
  107. agent_recipes/templates/ai-invoice-processor/TEMPLATE.yaml +34 -0
  108. agent_recipes/templates/ai-json-to-csv/README.md +12 -0
  109. agent_recipes/templates/ai-json-to-csv/TEMPLATE.yaml +36 -0
  110. agent_recipes/templates/ai-log-analyzer/README.md +59 -0
  111. agent_recipes/templates/ai-log-analyzer/TEMPLATE.yaml +28 -0
  112. agent_recipes/templates/ai-markdown-to-pdf/README.md +12 -0
  113. agent_recipes/templates/ai-markdown-to-pdf/TEMPLATE.yaml +40 -0
  114. agent_recipes/templates/ai-meeting-summarizer/README.md +59 -0
  115. agent_recipes/templates/ai-meeting-summarizer/TEMPLATE.yaml +32 -0
  116. agent_recipes/templates/ai-meta-tag-generator/README.md +59 -0
  117. agent_recipes/templates/ai-meta-tag-generator/TEMPLATE.yaml +28 -0
  118. agent_recipes/templates/ai-news-capture-pack/recipe.yaml +42 -0
  119. agent_recipes/templates/ai-news-capture-pack/tools.py +150 -0
  120. agent_recipes/templates/ai-news-crawler/recipe.yaml +99 -0
  121. agent_recipes/templates/ai-news-crawler/tools.py +417 -0
  122. agent_recipes/templates/ai-news-deduper/recipe.yaml +47 -0
  123. agent_recipes/templates/ai-news-deduper/tools.py +235 -0
  124. agent_recipes/templates/ai-newsletter-generator/README.md +59 -0
  125. agent_recipes/templates/ai-newsletter-generator/TEMPLATE.yaml +28 -0
  126. agent_recipes/templates/ai-note-summarizer/README.md +59 -0
  127. agent_recipes/templates/ai-note-summarizer/TEMPLATE.yaml +28 -0
  128. agent_recipes/templates/ai-pdf-summarizer/README.md +12 -0
  129. agent_recipes/templates/ai-pdf-summarizer/TEMPLATE.yaml +40 -0
  130. agent_recipes/templates/ai-pdf-to-markdown/README.md +19 -0
  131. agent_recipes/templates/ai-pdf-to-markdown/TEMPLATE.yaml +63 -0
  132. agent_recipes/templates/ai-performance-analyzer/recipe.yaml +45 -0
  133. agent_recipes/templates/ai-performance-analyzer/tools.py +159 -0
  134. agent_recipes/templates/ai-podcast-cleaner/README.md +117 -0
  135. agent_recipes/templates/ai-podcast-cleaner/TEMPLATE.yaml +117 -0
  136. agent_recipes/templates/ai-podcast-cleaner/agents.yaml +59 -0
  137. agent_recipes/templates/ai-podcast-cleaner/workflow.yaml +77 -0
  138. agent_recipes/templates/ai-podcast-transcriber/README.md +59 -0
  139. agent_recipes/templates/ai-podcast-transcriber/TEMPLATE.yaml +32 -0
  140. agent_recipes/templates/ai-post-copy-generator/recipe.yaml +41 -0
  141. agent_recipes/templates/ai-post-copy-generator/tools.py +105 -0
  142. agent_recipes/templates/ai-product-description-generator/README.md +59 -0
  143. agent_recipes/templates/ai-product-description-generator/TEMPLATE.yaml +28 -0
  144. agent_recipes/templates/ai-publisher-pack/recipe.yaml +44 -0
  145. agent_recipes/templates/ai-publisher-pack/tools.py +252 -0
  146. agent_recipes/templates/ai-qr-code-generator/README.md +60 -0
  147. agent_recipes/templates/ai-qr-code-generator/TEMPLATE.yaml +26 -0
  148. agent_recipes/templates/ai-regex-generator/README.md +59 -0
  149. agent_recipes/templates/ai-regex-generator/TEMPLATE.yaml +28 -0
  150. agent_recipes/templates/ai-repo-readme/README.md +13 -0
  151. agent_recipes/templates/ai-repo-readme/TEMPLATE.yaml +42 -0
  152. agent_recipes/templates/ai-report-generator/README.md +61 -0
  153. agent_recipes/templates/ai-report-generator/TEMPLATE.yaml +32 -0
  154. agent_recipes/templates/ai-resume-parser/README.md +60 -0
  155. agent_recipes/templates/ai-resume-parser/TEMPLATE.yaml +33 -0
  156. agent_recipes/templates/ai-rss-aggregator/README.md +60 -0
  157. agent_recipes/templates/ai-rss-aggregator/TEMPLATE.yaml +30 -0
  158. agent_recipes/templates/ai-schema-generator/README.md +12 -0
  159. agent_recipes/templates/ai-schema-generator/TEMPLATE.yaml +34 -0
  160. agent_recipes/templates/ai-screen-recorder/recipe.yaml +43 -0
  161. agent_recipes/templates/ai-screen-recorder/tools.py +184 -0
  162. agent_recipes/templates/ai-screenshot-capture/recipe.yaml +45 -0
  163. agent_recipes/templates/ai-screenshot-capture/tools.py +231 -0
  164. agent_recipes/templates/ai-screenshot-ocr/README.md +12 -0
  165. agent_recipes/templates/ai-screenshot-ocr/TEMPLATE.yaml +37 -0
  166. agent_recipes/templates/ai-script-writer/recipe.yaml +58 -0
  167. agent_recipes/templates/ai-script-writer/tools.py +297 -0
  168. agent_recipes/templates/ai-sentiment-analyzer/README.md +59 -0
  169. agent_recipes/templates/ai-sentiment-analyzer/TEMPLATE.yaml +28 -0
  170. agent_recipes/templates/ai-seo-optimizer/README.md +59 -0
  171. agent_recipes/templates/ai-seo-optimizer/TEMPLATE.yaml +28 -0
  172. agent_recipes/templates/ai-signal-ranker/recipe.yaml +54 -0
  173. agent_recipes/templates/ai-signal-ranker/tools.py +256 -0
  174. agent_recipes/templates/ai-sitemap-generator/README.md +59 -0
  175. agent_recipes/templates/ai-sitemap-generator/TEMPLATE.yaml +26 -0
  176. agent_recipes/templates/ai-sitemap-scraper/README.md +13 -0
  177. agent_recipes/templates/ai-sitemap-scraper/TEMPLATE.yaml +41 -0
  178. agent_recipes/templates/ai-slide-generator/README.md +60 -0
  179. agent_recipes/templates/ai-slide-generator/TEMPLATE.yaml +29 -0
  180. agent_recipes/templates/ai-slide-to-notes/README.md +12 -0
  181. agent_recipes/templates/ai-slide-to-notes/TEMPLATE.yaml +37 -0
  182. agent_recipes/templates/ai-social-media-generator/README.md +59 -0
  183. agent_recipes/templates/ai-social-media-generator/TEMPLATE.yaml +28 -0
  184. agent_recipes/templates/ai-sql-generator/README.md +59 -0
  185. agent_recipes/templates/ai-sql-generator/TEMPLATE.yaml +28 -0
  186. agent_recipes/templates/ai-subtitle-generator/README.md +59 -0
  187. agent_recipes/templates/ai-subtitle-generator/TEMPLATE.yaml +31 -0
  188. agent_recipes/templates/ai-test-generator/README.md +59 -0
  189. agent_recipes/templates/ai-test-generator/TEMPLATE.yaml +28 -0
  190. agent_recipes/templates/ai-translation-batch/README.md +59 -0
  191. agent_recipes/templates/ai-translation-batch/TEMPLATE.yaml +28 -0
  192. agent_recipes/templates/ai-url-to-markdown/README.md +14 -0
  193. agent_recipes/templates/ai-url-to-markdown/TEMPLATE.yaml +44 -0
  194. agent_recipes/templates/ai-video-chapter-generator/README.md +59 -0
  195. agent_recipes/templates/ai-video-chapter-generator/TEMPLATE.yaml +32 -0
  196. agent_recipes/templates/ai-video-compressor/README.md +59 -0
  197. agent_recipes/templates/ai-video-compressor/TEMPLATE.yaml +28 -0
  198. agent_recipes/templates/ai-video-editor/README.md +254 -0
  199. agent_recipes/templates/ai-video-editor/TEMPLATE.yaml +139 -0
  200. agent_recipes/templates/ai-video-editor/agents.yaml +36 -0
  201. agent_recipes/templates/ai-video-editor/requirements.txt +8 -0
  202. agent_recipes/templates/ai-video-editor/scripts/run.sh +10 -0
  203. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__init__.py +45 -0
  204. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__main__.py +8 -0
  205. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/__init__.cpython-312.pyc +0 -0
  206. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/cli.cpython-312.pyc +0 -0
  207. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/config.cpython-312.pyc +0 -0
  208. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/ffmpeg_probe.cpython-312.pyc +0 -0
  209. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/heuristics.cpython-312.pyc +0 -0
  210. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/llm_plan.cpython-312.pyc +0 -0
  211. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/models.cpython-312.pyc +0 -0
  212. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/pipeline.cpython-312.pyc +0 -0
  213. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/render.cpython-312.pyc +0 -0
  214. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/timeline.cpython-312.pyc +0 -0
  215. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/transcribe.cpython-312.pyc +0 -0
  216. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/utils.cpython-312.pyc +0 -0
  217. agent_recipes/templates/ai-video-editor/src/ai_video_editor/cli.py +343 -0
  218. agent_recipes/templates/ai-video-editor/src/ai_video_editor/config.py +102 -0
  219. agent_recipes/templates/ai-video-editor/src/ai_video_editor/ffmpeg_probe.py +92 -0
  220. agent_recipes/templates/ai-video-editor/src/ai_video_editor/heuristics.py +119 -0
  221. agent_recipes/templates/ai-video-editor/src/ai_video_editor/llm_plan.py +277 -0
  222. agent_recipes/templates/ai-video-editor/src/ai_video_editor/models.py +343 -0
  223. agent_recipes/templates/ai-video-editor/src/ai_video_editor/pipeline.py +287 -0
  224. agent_recipes/templates/ai-video-editor/src/ai_video_editor/render.py +274 -0
  225. agent_recipes/templates/ai-video-editor/src/ai_video_editor/timeline.py +278 -0
  226. agent_recipes/templates/ai-video-editor/src/ai_video_editor/transcribe.py +233 -0
  227. agent_recipes/templates/ai-video-editor/src/ai_video_editor/utils.py +222 -0
  228. agent_recipes/templates/ai-video-editor/src/input.mov +0 -0
  229. agent_recipes/templates/ai-video-editor/src/out.mp4 +0 -0
  230. agent_recipes/templates/ai-video-editor/tests/test_heuristics.py +130 -0
  231. agent_recipes/templates/ai-video-editor/tests/test_models.py +152 -0
  232. agent_recipes/templates/ai-video-editor/tests/test_timeline.py +105 -0
  233. agent_recipes/templates/ai-video-editor/workflow.yaml +51 -0
  234. agent_recipes/templates/ai-video-highlight-extractor/README.md +60 -0
  235. agent_recipes/templates/ai-video-highlight-extractor/TEMPLATE.yaml +33 -0
  236. agent_recipes/templates/ai-video-merger/recipe.yaml +40 -0
  237. agent_recipes/templates/ai-video-merger/tools.py +172 -0
  238. agent_recipes/templates/ai-video-thumbnails/README.md +16 -0
  239. agent_recipes/templates/ai-video-thumbnails/TEMPLATE.yaml +53 -0
  240. agent_recipes/templates/ai-video-to-gif/README.md +14 -0
  241. agent_recipes/templates/ai-video-to-gif/TEMPLATE.yaml +64 -0
  242. agent_recipes/templates/ai-voice-cloner/README.md +59 -0
  243. agent_recipes/templates/ai-voice-cloner/TEMPLATE.yaml +31 -0
  244. agent_recipes/templates/ai-voiceover-generator/recipe.yaml +41 -0
  245. agent_recipes/templates/ai-voiceover-generator/tools.py +194 -0
  246. agent_recipes/templates/ai-watermark-adder/README.md +59 -0
  247. agent_recipes/templates/ai-watermark-adder/TEMPLATE.yaml +26 -0
  248. agent_recipes/templates/ai-watermark-remover/README.md +60 -0
  249. agent_recipes/templates/ai-watermark-remover/TEMPLATE.yaml +32 -0
  250. agent_recipes/templates/data-transformer/README.md +75 -0
  251. agent_recipes/templates/data-transformer/TEMPLATE.yaml +63 -0
  252. agent_recipes/templates/data-transformer/agents.yaml +70 -0
  253. agent_recipes/templates/data-transformer/workflow.yaml +92 -0
  254. agent_recipes/templates/shorts-generator/README.md +61 -0
  255. agent_recipes/templates/shorts-generator/TEMPLATE.yaml +65 -0
  256. agent_recipes/templates/shorts-generator/agents.yaml +66 -0
  257. agent_recipes/templates/shorts-generator/workflow.yaml +86 -0
  258. agent_recipes/templates/transcript-generator/README.md +103 -0
  259. agent_recipes/templates/transcript-generator/TEMPLATE.yaml +57 -0
  260. agent_recipes/templates/transcript-generator/agents.yaml +62 -0
  261. agent_recipes/templates/transcript-generator/workflow.yaml +82 -0
  262. agent_recipes/templates/video-editor/README.md +70 -0
  263. agent_recipes/templates/video-editor/TEMPLATE.yaml +55 -0
  264. agent_recipes/templates/video-editor/agents.yaml +68 -0
  265. agent_recipes/templates/video-editor/workflow.yaml +92 -0
  266. agent_recipes-0.0.5.dist-info/METADATA +145 -0
  267. agent_recipes-0.0.5.dist-info/RECORD +269 -0
  268. agent_recipes-0.0.5.dist-info/WHEEL +5 -0
  269. agent_recipes-0.0.5.dist-info/top_level.txt +1 -0
  270. /236/326/177nE/243/231/214/232/265/322m/201/253/353/022C/372/321/266/b/225^=/272/017t/262/3337/310@/315wb/341pB/277z/216/330/314/004/265B/213/375/236/203/026/373/307/354z41/347#/374q/262/22589/032/276 /277/244Vh/322/017/004/224/215/004/367/377/375/335/n +0 -0
@@ -0,0 +1,277 @@
1
+ """
2
+ LLM-based content analysis for video editing.
3
+
4
+ Uses LLM to:
5
+ - Understand video content
6
+ - Identify tangent segments
7
+ - Generate chapter markers
8
+ - Create edit plans
9
+ """
10
+
11
+ import json
12
+ import os
13
+ from typing import Any, Dict, List
14
+
15
+ from .models import (
16
+ Chapter,
17
+ EditPlan,
18
+ Segment,
19
+ SegmentCategory,
20
+ TranscriptResult,
21
+ )
22
+ from .heuristics import detect_fillers, detect_repetitions, detect_silence
23
+
24
+
25
+ ANALYSIS_SYSTEM_PROMPT = """You are an expert video editor AI assistant. Your task is to analyze video transcripts and create precise edit plans.
26
+
27
+ You will receive:
28
+ 1. A transcript with word-level timestamps
29
+ 2. Configuration for what to remove (fillers, tangents, repetitions)
30
+ 3. Optional target duration
31
+
32
+ Your job is to identify segments to KEEP and segments to REMOVE, with precise timestamps.
33
+
34
+ Rules:
35
+ - Never cut in the middle of a word - use word boundaries
36
+ - Minimum segment length should be respected
37
+ - Add padding around cuts for smooth transitions
38
+ - Be conservative with tangent detection - only remove clearly off-topic content
39
+ - For fillers, only remove standalone filler words, not fillers that are part of natural speech flow
40
+ - For repetitions, identify when speaker restarts a sentence or repeats themselves
41
+
42
+ Output must be valid JSON matching the specified schema."""
43
+
44
+
45
+ ANALYSIS_USER_PROMPT = """Analyze this transcript and create an edit plan.
46
+
47
+ TRANSCRIPT:
48
+ {transcript}
49
+
50
+ WORD TIMESTAMPS:
51
+ {word_timestamps}
52
+
53
+ CONFIGURATION:
54
+ - Remove fillers: {remove_fillers}
55
+ - Filler words to detect: {filler_words}
56
+ - Remove repetitions: {remove_repetitions}
57
+ - Remove tangents: {remove_tangents}
58
+ - Remove long silences: {remove_silence}
59
+ - Silence threshold: {silence_threshold_ms}ms
60
+ - Minimum segment length: {min_segment_length}s
61
+ - Padding around cuts: {padding_ms}ms
62
+ {target_length_instruction}
63
+
64
+ Analyze the content and return a JSON object with this exact structure:
65
+ {{
66
+ "summary": "Brief summary of what the video is about",
67
+ "topics": ["main topic 1", "main topic 2"],
68
+ "segments_to_keep": [
69
+ {{"start": 0.0, "end": 10.5, "reason": "Introduction", "text": "transcript text..."}}
70
+ ],
71
+ "segments_to_remove": [
72
+ {{"start": 10.5, "end": 11.2, "category": "filler", "reason": "um", "text": "um"}}
73
+ ],
74
+ "chapters": [
75
+ {{"start": 0.0, "title": "Introduction", "description": "..."}}
76
+ ]
77
+ }}
78
+
79
+ Categories for segments_to_remove: "filler", "tangent", "repeat", "silence"
80
+
81
+ Be precise with timestamps. Ensure segments don't overlap and cover the entire duration."""
82
+
83
+
84
+ def analyze_content(
85
+ transcript: TranscriptResult,
86
+ config: Dict[str, Any],
87
+ model: str = None
88
+ ) -> EditPlan:
89
+ """
90
+ Analyze transcript content using LLM.
91
+
92
+ Args:
93
+ transcript: Transcript with word timestamps
94
+ config: Edit configuration
95
+ model: LLM model to use (default: gpt-4o-mini)
96
+
97
+ Returns:
98
+ EditPlan with segments to keep/remove
99
+ """
100
+ try:
101
+ from openai import OpenAI
102
+ except ImportError:
103
+ raise ImportError(
104
+ "OpenAI package required for content analysis. "
105
+ "Install with: pip install openai"
106
+ )
107
+
108
+ api_key = os.environ.get("OPENAI_API_KEY")
109
+ if not api_key:
110
+ raise ValueError("OPENAI_API_KEY environment variable required")
111
+
112
+ client = OpenAI(api_key=api_key)
113
+
114
+ if model is None:
115
+ model = os.environ.get("OPENAI_MODEL", "gpt-4o-mini")
116
+
117
+ word_ts_lines = []
118
+ for w in transcript.words[:500]:
119
+ word_ts_lines.append(f"[{w.start:.2f}-{w.end:.2f}] {w.text}")
120
+
121
+ target_instruction = ""
122
+ if config.get("target_length"):
123
+ target_instruction = f"- Target output length: {config['target_length']}s (prioritize removing lower-value content to meet this)"
124
+
125
+ user_prompt = ANALYSIS_USER_PROMPT.format(
126
+ transcript=transcript.text[:8000],
127
+ word_timestamps="\n".join(word_ts_lines),
128
+ remove_fillers=config.get("remove_fillers", True),
129
+ filler_words=", ".join(config.get("filler_words", ["um", "uh"])),
130
+ remove_repetitions=config.get("remove_repetitions", True),
131
+ remove_tangents=config.get("remove_tangents", False),
132
+ remove_silence=config.get("remove_silence", True),
133
+ silence_threshold_ms=config.get("silence_threshold_ms", 700),
134
+ min_segment_length=config.get("min_segment_length", 1.2),
135
+ padding_ms=config.get("padding_ms", 120),
136
+ target_length_instruction=target_instruction
137
+ )
138
+
139
+ response = client.chat.completions.create(
140
+ model=model,
141
+ messages=[
142
+ {"role": "system", "content": ANALYSIS_SYSTEM_PROMPT},
143
+ {"role": "user", "content": user_prompt}
144
+ ],
145
+ temperature=0.1,
146
+ response_format={"type": "json_object"}
147
+ )
148
+
149
+ content = response.choices[0].message.content
150
+ try:
151
+ data = json.loads(content)
152
+ except json.JSONDecodeError as e:
153
+ raise RuntimeError(f"Failed to parse LLM response as JSON: {e}\nResponse: {content[:500]}")
154
+
155
+ return _parse_edit_plan(data)
156
+
157
+
158
+ def _parse_edit_plan(data: Dict[str, Any]) -> EditPlan:
159
+ """Parse LLM response into EditPlan."""
160
+ segments_to_keep = []
161
+ segments_to_remove = []
162
+ chapters = []
163
+
164
+ for seg_data in data.get("segments_to_keep", []):
165
+ segments_to_keep.append(Segment(
166
+ start=float(seg_data.get("start", 0)),
167
+ end=float(seg_data.get("end", 0)),
168
+ category=SegmentCategory.KEEP,
169
+ reason=seg_data.get("reason", ""),
170
+ text=seg_data.get("text", "")
171
+ ))
172
+
173
+ for seg_data in data.get("segments_to_remove", []):
174
+ category_str = seg_data.get("category", "filler").lower()
175
+ try:
176
+ category = SegmentCategory(category_str)
177
+ except ValueError:
178
+ category = SegmentCategory.FILLER
179
+
180
+ segments_to_remove.append(Segment(
181
+ start=float(seg_data.get("start", 0)),
182
+ end=float(seg_data.get("end", 0)),
183
+ category=category,
184
+ reason=seg_data.get("reason", ""),
185
+ text=seg_data.get("text", "")
186
+ ))
187
+
188
+ for ch_data in data.get("chapters", []):
189
+ chapters.append(Chapter(
190
+ start=float(ch_data.get("start", 0)),
191
+ title=ch_data.get("title", ""),
192
+ description=ch_data.get("description", "")
193
+ ))
194
+
195
+ return EditPlan(
196
+ segments_to_keep=segments_to_keep,
197
+ segments_to_remove=segments_to_remove,
198
+ chapters=chapters,
199
+ summary=data.get("summary", ""),
200
+ topics=data.get("topics", [])
201
+ )
202
+
203
+
204
+ def create_simple_edit_plan(
205
+ transcript: TranscriptResult,
206
+ config: Dict[str, Any]
207
+ ) -> EditPlan:
208
+ """
209
+ Create edit plan using simple pattern matching (no LLM).
210
+
211
+ This is faster but less accurate than LLM-based analysis.
212
+
213
+ Args:
214
+ transcript: Transcript with word timestamps
215
+ config: Edit configuration
216
+
217
+ Returns:
218
+ EditPlan with segments to keep/remove
219
+ """
220
+ segments_to_remove = []
221
+
222
+ if config.get("remove_fillers", True):
223
+ filler_words = config.get("filler_words", ["um", "uh"])
224
+ segments_to_remove.extend(detect_fillers(transcript, filler_words))
225
+
226
+ if config.get("remove_repetitions", True):
227
+ segments_to_remove.extend(detect_repetitions(transcript))
228
+
229
+ if config.get("remove_silence", True):
230
+ threshold = config.get("silence_threshold_ms", 700)
231
+ segments_to_remove.extend(detect_silence(transcript, threshold))
232
+
233
+ segments_to_remove.sort(key=lambda s: s.start)
234
+
235
+ segments_to_keep = _invert_segments(
236
+ segments_to_remove,
237
+ 0.0,
238
+ transcript.duration
239
+ )
240
+
241
+ return EditPlan(
242
+ segments_to_keep=segments_to_keep,
243
+ segments_to_remove=segments_to_remove,
244
+ summary="",
245
+ topics=[]
246
+ )
247
+
248
+
249
+ def _invert_segments(
250
+ remove_segments: List[Segment],
251
+ start: float,
252
+ end: float
253
+ ) -> List[Segment]:
254
+ """Convert remove segments to keep segments."""
255
+ if not remove_segments:
256
+ return [Segment(start=start, end=end, category=SegmentCategory.KEEP)]
257
+
258
+ keep_segments = []
259
+ current = start
260
+
261
+ for seg in sorted(remove_segments, key=lambda s: s.start):
262
+ if seg.start > current:
263
+ keep_segments.append(Segment(
264
+ start=current,
265
+ end=seg.start,
266
+ category=SegmentCategory.KEEP
267
+ ))
268
+ current = max(current, seg.end)
269
+
270
+ if current < end:
271
+ keep_segments.append(Segment(
272
+ start=current,
273
+ end=end,
274
+ category=SegmentCategory.KEEP
275
+ ))
276
+
277
+ return keep_segments
@@ -0,0 +1,343 @@
1
+ """
2
+ Data models for AI Video Editor.
3
+
4
+ Defines data structures for:
5
+ - Video metadata and probe results
6
+ - Transcripts with word-level timestamps
7
+ - Edit plans and segments
8
+ - Final edit results
9
+ """
10
+
11
+ from dataclasses import dataclass, field
12
+ from enum import Enum
13
+ from pathlib import Path
14
+ from typing import Any, Dict, List, Optional, Union
15
+ import json
16
+
17
+
18
+ class SegmentCategory(str, Enum):
19
+ """Category of segment to remove."""
20
+ FILLER = "filler"
21
+ TANGENT = "tangent"
22
+ REPEAT = "repeat"
23
+ NOISE = "noise"
24
+ SILENCE = "silence"
25
+ KEEP = "keep"
26
+
27
+
28
+ @dataclass
29
+ class Word:
30
+ """A single word with timing information."""
31
+ text: str
32
+ start: float # seconds
33
+ end: float # seconds
34
+ confidence: float = 1.0
35
+
36
+ def to_dict(self) -> Dict[str, Any]:
37
+ return {
38
+ "text": self.text,
39
+ "start": self.start,
40
+ "end": self.end,
41
+ "confidence": self.confidence
42
+ }
43
+
44
+ @classmethod
45
+ def from_dict(cls, data: Dict[str, Any]) -> "Word":
46
+ return cls(
47
+ text=data["text"],
48
+ start=data["start"],
49
+ end=data["end"],
50
+ confidence=data.get("confidence", 1.0)
51
+ )
52
+
53
+
54
+ @dataclass
55
+ class Segment:
56
+ """A segment of video with timing and metadata."""
57
+ start: float # seconds
58
+ end: float # seconds
59
+ category: SegmentCategory = SegmentCategory.KEEP
60
+ reason: str = ""
61
+ confidence: float = 1.0
62
+ text: str = ""
63
+ words: List[Word] = field(default_factory=list)
64
+
65
+ @property
66
+ def duration(self) -> float:
67
+ return self.end - self.start
68
+
69
+ def to_dict(self) -> Dict[str, Any]:
70
+ return {
71
+ "start": self.start,
72
+ "end": self.end,
73
+ "category": self.category.value,
74
+ "reason": self.reason,
75
+ "confidence": self.confidence,
76
+ "text": self.text,
77
+ "words": [w.to_dict() for w in self.words]
78
+ }
79
+
80
+ @classmethod
81
+ def from_dict(cls, data: Dict[str, Any]) -> "Segment":
82
+ return cls(
83
+ start=data["start"],
84
+ end=data["end"],
85
+ category=SegmentCategory(data.get("category", "keep")),
86
+ reason=data.get("reason", ""),
87
+ confidence=data.get("confidence", 1.0),
88
+ text=data.get("text", ""),
89
+ words=[Word.from_dict(w) for w in data.get("words", [])]
90
+ )
91
+
92
+
93
+ @dataclass
94
+ class Chapter:
95
+ """A chapter marker for the video."""
96
+ start: float # seconds
97
+ title: str
98
+ description: str = ""
99
+
100
+ def to_dict(self) -> Dict[str, Any]:
101
+ return {
102
+ "start": self.start,
103
+ "title": self.title,
104
+ "description": self.description
105
+ }
106
+
107
+ @classmethod
108
+ def from_dict(cls, data: Dict[str, Any]) -> "Chapter":
109
+ return cls(
110
+ start=data["start"],
111
+ title=data["title"],
112
+ description=data.get("description", "")
113
+ )
114
+
115
+
116
+ @dataclass
117
+ class EditPlan:
118
+ """Complete edit plan for a video."""
119
+ segments_to_keep: List[Segment] = field(default_factory=list)
120
+ segments_to_remove: List[Segment] = field(default_factory=list)
121
+ chapters: List[Chapter] = field(default_factory=list)
122
+ summary: str = ""
123
+ topics: List[str] = field(default_factory=list)
124
+
125
+ @property
126
+ def total_keep_duration(self) -> float:
127
+ return sum(s.duration for s in self.segments_to_keep)
128
+
129
+ @property
130
+ def total_remove_duration(self) -> float:
131
+ return sum(s.duration for s in self.segments_to_remove)
132
+
133
+ @property
134
+ def removal_stats(self) -> Dict[str, float]:
135
+ """Get duration removed by category."""
136
+ stats: Dict[str, float] = {}
137
+ for seg in self.segments_to_remove:
138
+ cat = seg.category.value
139
+ stats[cat] = stats.get(cat, 0) + seg.duration
140
+ return stats
141
+
142
+ def to_dict(self) -> Dict[str, Any]:
143
+ return {
144
+ "segments_to_keep": [s.to_dict() for s in self.segments_to_keep],
145
+ "segments_to_remove": [s.to_dict() for s in self.segments_to_remove],
146
+ "chapters": [c.to_dict() for c in self.chapters],
147
+ "summary": self.summary,
148
+ "topics": self.topics,
149
+ "stats": {
150
+ "total_keep_duration": self.total_keep_duration,
151
+ "total_remove_duration": self.total_remove_duration,
152
+ "removal_by_category": self.removal_stats
153
+ }
154
+ }
155
+
156
+ @classmethod
157
+ def from_dict(cls, data: Dict[str, Any]) -> "EditPlan":
158
+ return cls(
159
+ segments_to_keep=[Segment.from_dict(s) for s in data.get("segments_to_keep", [])],
160
+ segments_to_remove=[Segment.from_dict(s) for s in data.get("segments_to_remove", [])],
161
+ chapters=[Chapter.from_dict(c) for c in data.get("chapters", [])],
162
+ summary=data.get("summary", ""),
163
+ topics=data.get("topics", [])
164
+ )
165
+
166
+ def to_json(self, path: Optional[Union[str, Path]] = None) -> str:
167
+ """Export plan to JSON."""
168
+ json_str = json.dumps(self.to_dict(), indent=2)
169
+ if path:
170
+ Path(path).write_text(json_str)
171
+ return json_str
172
+
173
+ @classmethod
174
+ def from_json(cls, json_str_or_path: Union[str, Path]) -> "EditPlan":
175
+ """Load plan from JSON string or file."""
176
+ path = Path(json_str_or_path)
177
+ if path.exists():
178
+ data = json.loads(path.read_text())
179
+ else:
180
+ data = json.loads(str(json_str_or_path))
181
+ return cls.from_dict(data)
182
+
183
+
184
+ @dataclass
185
+ class VideoProbeResult:
186
+ """Result of probing a video file."""
187
+ path: str
188
+ duration: float # seconds
189
+ width: int
190
+ height: int
191
+ fps: float
192
+ codec: str
193
+ audio_codec: str = ""
194
+ audio_channels: int = 0
195
+ audio_sample_rate: int = 0
196
+ bitrate: int = 0
197
+ file_size: int = 0
198
+ format_name: str = ""
199
+
200
+ def to_dict(self) -> Dict[str, Any]:
201
+ return {
202
+ "path": self.path,
203
+ "duration": self.duration,
204
+ "width": self.width,
205
+ "height": self.height,
206
+ "fps": self.fps,
207
+ "codec": self.codec,
208
+ "audio_codec": self.audio_codec,
209
+ "audio_channels": self.audio_channels,
210
+ "audio_sample_rate": self.audio_sample_rate,
211
+ "bitrate": self.bitrate,
212
+ "file_size": self.file_size,
213
+ "format_name": self.format_name
214
+ }
215
+
216
+ @classmethod
217
+ def from_dict(cls, data: Dict[str, Any]) -> "VideoProbeResult":
218
+ return cls(**data)
219
+
220
+
221
+ @dataclass
222
+ class TranscriptResult:
223
+ """Result of transcribing audio."""
224
+ text: str
225
+ words: List[Word] = field(default_factory=list)
226
+ language: str = "en"
227
+ duration: float = 0.0
228
+ provider: str = "openai"
229
+
230
+ def to_srt(self, path: Optional[Union[str, Path]] = None) -> str:
231
+ """Export transcript to SRT format."""
232
+ lines = []
233
+ segments = []
234
+ current_segment: List[Word] = []
235
+ segment_start = 0.0
236
+
237
+ for word in self.words:
238
+ if not current_segment:
239
+ segment_start = word.start
240
+ current_segment.append(word)
241
+ elif len(current_segment) >= 10 or (word.end - segment_start) > 5.0:
242
+ segments.append((segment_start, current_segment[-1].end, current_segment))
243
+ current_segment = [word]
244
+ segment_start = word.start
245
+ else:
246
+ current_segment.append(word)
247
+
248
+ if current_segment:
249
+ segments.append((segment_start, current_segment[-1].end, current_segment))
250
+
251
+ for i, (start, end, words) in enumerate(segments, 1):
252
+ text = " ".join(w.text for w in words)
253
+ start_ts = _format_srt_time(start)
254
+ end_ts = _format_srt_time(end)
255
+ lines.append(f"{i}")
256
+ lines.append(f"{start_ts} --> {end_ts}")
257
+ lines.append(text)
258
+ lines.append("")
259
+
260
+ srt_content = "\n".join(lines)
261
+ if path:
262
+ Path(path).write_text(srt_content)
263
+ return srt_content
264
+
265
+ def to_dict(self) -> Dict[str, Any]:
266
+ return {
267
+ "text": self.text,
268
+ "words": [w.to_dict() for w in self.words],
269
+ "language": self.language,
270
+ "duration": self.duration,
271
+ "provider": self.provider
272
+ }
273
+
274
+ @classmethod
275
+ def from_dict(cls, data: Dict[str, Any]) -> "TranscriptResult":
276
+ return cls(
277
+ text=data["text"],
278
+ words=[Word.from_dict(w) for w in data.get("words", [])],
279
+ language=data.get("language", "en"),
280
+ duration=data.get("duration", 0.0),
281
+ provider=data.get("provider", "openai")
282
+ )
283
+
284
+
285
+ @dataclass
286
+ class VideoEditResult:
287
+ """Result of editing a video."""
288
+ output_path: str
289
+ report_path: str
290
+ transcript_path: str
291
+ srt_path: str
292
+ edl_path: str
293
+
294
+ original_duration: float
295
+ final_duration: float
296
+ time_saved: float
297
+
298
+ edit_plan: EditPlan
299
+ probe: VideoProbeResult
300
+ transcript: TranscriptResult
301
+
302
+ workdir: str = ""
303
+ config_snapshot: Dict[str, Any] = field(default_factory=dict)
304
+
305
+ @property
306
+ def compression_ratio(self) -> float:
307
+ if self.original_duration == 0:
308
+ return 0
309
+ return self.final_duration / self.original_duration
310
+
311
+ def to_dict(self) -> Dict[str, Any]:
312
+ return {
313
+ "output_path": self.output_path,
314
+ "report_path": self.report_path,
315
+ "transcript_path": self.transcript_path,
316
+ "srt_path": self.srt_path,
317
+ "edl_path": self.edl_path,
318
+ "original_duration": self.original_duration,
319
+ "final_duration": self.final_duration,
320
+ "time_saved": self.time_saved,
321
+ "compression_ratio": self.compression_ratio,
322
+ "edit_plan": self.edit_plan.to_dict(),
323
+ "probe": self.probe.to_dict(),
324
+ "transcript": self.transcript.to_dict(),
325
+ "workdir": self.workdir,
326
+ "config_snapshot": self.config_snapshot
327
+ }
328
+
329
+ def to_json(self, path: Optional[Union[str, Path]] = None) -> str:
330
+ """Export result to JSON."""
331
+ json_str = json.dumps(self.to_dict(), indent=2, default=str)
332
+ if path:
333
+ Path(path).write_text(json_str)
334
+ return json_str
335
+
336
+
337
+ def _format_srt_time(seconds: float) -> str:
338
+ """Format seconds as SRT timestamp (HH:MM:SS,mmm)."""
339
+ hours = int(seconds // 3600)
340
+ minutes = int((seconds % 3600) // 60)
341
+ secs = int(seconds % 60)
342
+ millis = int((seconds % 1) * 1000)
343
+ return f"{hours:02d}:{minutes:02d}:{secs:02d},{millis:03d}"