pulse-engine 0.2.43__tar.gz → 0.2.44__tar.gz

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 (183) hide show
  1. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/PKG-INFO +1 -1
  2. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/pyproject.toml +1 -1
  3. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/youtube_downloader.py +104 -0
  4. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/README.md +0 -0
  5. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/__init__.py +0 -0
  6. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/__init__.py +0 -0
  7. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/audio_transcription.py +0 -0
  8. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/batcher.py +0 -0
  9. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/digital_news.py +0 -0
  10. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/digital_news_metadata.py +0 -0
  11. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/document_event_campaign_mapper.py +0 -0
  12. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/exceptions.py +0 -0
  13. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/google_sheets.py +0 -0
  14. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/mart_export.py +0 -0
  15. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/models.py +0 -0
  16. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/opensearch_sql.py +0 -0
  17. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/opensearch_storage.py +0 -0
  18. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/prompts.py +0 -0
  19. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/report_generator.py +0 -0
  20. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/speech_content.py +0 -0
  21. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/speech_metadata.py +0 -0
  22. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/transcription/__init__.py +0 -0
  23. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/transcription/base.py +0 -0
  24. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/transcription/config.py +0 -0
  25. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/transcription/factory.py +0 -0
  26. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/transcription/providers/__init__.py +0 -0
  27. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/transcription/providers/deepgram.py +0 -0
  28. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/transcription/providers/openai_whisper.py +0 -0
  29. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/transcription/providers/selfhosted_job.py +0 -0
  30. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/transcription/providers/youtube_captions.py +0 -0
  31. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/transcription/service.py +0 -0
  32. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/transcription/source.py +0 -0
  33. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/twitter.py +0 -0
  34. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/adapters/youtube_metadata.py +0 -0
  35. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/api/__init__.py +0 -0
  36. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/api/v1/__init__.py +0 -0
  37. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/api/v1/auth.py +0 -0
  38. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/api/v1/health.py +0 -0
  39. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/api/v1/router.py +0 -0
  40. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cli/__init__.py +0 -0
  41. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cli/main.py +0 -0
  42. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cli/templates/cookiecutter.json +0 -0
  43. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/.gitignore +0 -0
  44. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/Dockerfile +0 -0
  45. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/pipeline.yaml +0 -0
  46. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/pyproject.toml +0 -0
  47. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/src/pulse_{{cookiecutter.product_slug}}/__init__.py +0 -0
  48. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/tests/__init__.py +0 -0
  49. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/tests/unit/__init__.py +0 -0
  50. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/tests/unit/test_manifest.py +0 -0
  51. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/client.py +0 -0
  52. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/config.py +0 -0
  53. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/core/__init__.py +0 -0
  54. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/core/error_handlers.py +0 -0
  55. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/core/exceptions.py +0 -0
  56. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/core/job_token.py +0 -0
  57. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/core/logging.py +0 -0
  58. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/core/scope.py +0 -0
  59. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/core/security.py +0 -0
  60. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cron/__init__.py +0 -0
  61. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cron/models.py +0 -0
  62. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cron/pipeline_sync.py +0 -0
  63. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cron/repository.py +0 -0
  64. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cron/router.py +0 -0
  65. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cron/runner.py +0 -0
  66. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cron/schemas.py +0 -0
  67. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cron/service.py +0 -0
  68. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cron/tasks.py +0 -0
  69. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/cron/translator.py +0 -0
  70. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/database.py +0 -0
  71. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/dependencies.py +0 -0
  72. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/deployment/__init__.py +0 -0
  73. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/deployment/infra_provisioner.py +0 -0
  74. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/evolution/__init__.py +0 -0
  75. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/evolution/adapter.py +0 -0
  76. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/evolution/client.py +0 -0
  77. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/evolution/models.py +0 -0
  78. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/evolution/router.py +0 -0
  79. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/evolution/service.py +0 -0
  80. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/extractor/__init__.py +0 -0
  81. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/extractor/adapters/__init__.py +0 -0
  82. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/extractor/base.py +0 -0
  83. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/main.py +0 -0
  84. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/mcp/__init__.py +0 -0
  85. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/mcp/__main__.py +0 -0
  86. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/mcp/server.py +0 -0
  87. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/mcp/tools_kb.py +0 -0
  88. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/mcp/tools_modules.py +0 -0
  89. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/mcp/tools_pipelines.py +0 -0
  90. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/mcp/tools_processor.py +0 -0
  91. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/middleware/__init__.py +0 -0
  92. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/middleware/rate_limit.py +0 -0
  93. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/middleware/request_id.py +0 -0
  94. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/middleware/security_headers.py +0 -0
  95. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/middleware/tenant.py +0 -0
  96. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/notifications/__init__.py +0 -0
  97. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/notifications/apprise_notifier.py +0 -0
  98. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/pipeline/__init__.py +0 -0
  99. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/pipeline/config_parser.py +0 -0
  100. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/pipeline/expression.py +0 -0
  101. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/pipeline/models.py +0 -0
  102. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/pipeline/repositories.py +0 -0
  103. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/pipeline/router_modules.py +0 -0
  104. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/pipeline/router_pipelines.py +0 -0
  105. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/pipeline/schemas.py +0 -0
  106. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/pipeline/service.py +0 -0
  107. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/pipeline/translators/__init__.py +0 -0
  108. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/pipeline/translators/airflow_status.py +0 -0
  109. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/pipeline/translators/airflow_translator.py +0 -0
  110. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/pipeline/translators/base.py +0 -0
  111. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/pipeline/translators/prefect_status.py +0 -0
  112. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/pipeline/translators/prefect_translator.py +0 -0
  113. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/__init__.py +0 -0
  114. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/base.py +0 -0
  115. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/core/__init__.py +0 -0
  116. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/core/analysis.py +0 -0
  117. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/core/chunking.py +0 -0
  118. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/core/prompts.py +0 -0
  119. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/core/topic_splitter.py +0 -0
  120. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/defaults/__init__.py +0 -0
  121. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/defaults/core_processor.py +0 -0
  122. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/defaults/postprocessor.py +0 -0
  123. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/defaults/preprocessor.py +0 -0
  124. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/llm/__init__.py +0 -0
  125. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/llm/provider.py +0 -0
  126. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/ocr/gemini.py +0 -0
  127. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/pipeline.py +0 -0
  128. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/postprocessor/__init__.py +0 -0
  129. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/postprocessor/embeddings.py +0 -0
  130. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/postprocessor/tasks.py +0 -0
  131. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/preprocessor/__init__.py +0 -0
  132. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/preprocessor/tasks.py +0 -0
  133. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/router.py +0 -0
  134. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/processor/schemas.py +0 -0
  135. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/registry.py +0 -0
  136. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/reporting/__init__.py +0 -0
  137. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/reporting/agent.py +0 -0
  138. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/reporting/fixtures.py +0 -0
  139. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/reporting/models.py +0 -0
  140. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/reporting/prompts.py +0 -0
  141. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/reporting/service.py +0 -0
  142. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/reporting/staging.py +0 -0
  143. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/reporting/tools.py +0 -0
  144. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/reporting/validation.py +0 -0
  145. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/reporting/wiring.py +0 -0
  146. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/runners/__init__.py +0 -0
  147. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/runners/lambda_runner.py +0 -0
  148. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/runners/pipeline_runner.py +0 -0
  149. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/runners/prefect_pipeline_flow.py +0 -0
  150. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/runners/prefect_runner.py +0 -0
  151. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/s3.py +0 -0
  152. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/sandbox/__init__.py +0 -0
  153. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/sandbox/backend.py +0 -0
  154. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/sandbox/lambda_microvm.py +0 -0
  155. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/sandbox/microvm_agent/Dockerfile +0 -0
  156. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/sandbox/microvm_agent/__init__.py +0 -0
  157. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/sandbox/microvm_agent/server.py +0 -0
  158. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/sandbox/session.py +0 -0
  159. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/secrets.py +0 -0
  160. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/services/__init__.py +0 -0
  161. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/services/bootstrap.py +0 -0
  162. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/services/opensearch.py +0 -0
  163. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/skills/__init__.py +0 -0
  164. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/skills/catalog.py +0 -0
  165. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/skills/models.py +0 -0
  166. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/skills/repo.py +0 -0
  167. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/skills/seed.py +0 -0
  168. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/skills/seed_data/skill_files.json +0 -0
  169. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/skills/seed_data/skills.json +0 -0
  170. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/skills/sync.py +0 -0
  171. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/skills/tools.py +0 -0
  172. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/storage/__init__.py +0 -0
  173. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/storage/connectors/__init__.py +0 -0
  174. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/storage/connectors/athena.py +0 -0
  175. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/storage/connectors/base.py +0 -0
  176. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/storage/connectors/opensearch.py +0 -0
  177. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/storage/knowledge_base.py +0 -0
  178. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/storage/router.py +0 -0
  179. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/storage/schemas.py +0 -0
  180. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/testing/__init__.py +0 -0
  181. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/testing/fixtures.py +0 -0
  182. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/testing/mocks.py +0 -0
  183. {pulse_engine-0.2.43 → pulse_engine-0.2.44}/src/pulse_engine/worker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pulse-engine
3
- Version: 0.2.43
3
+ Version: 0.2.44
4
4
  Summary: Pulse Engine — Hybrid framework for building Pulse products
5
5
  Author: Pulse Team
6
6
  Requires-Python: >=3.11,<3.13
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pulse-engine"
3
- version = "0.2.43"
3
+ version = "0.2.44"
4
4
  description = "Pulse Engine — Hybrid framework for building Pulse products"
5
5
  authors = ["Pulse Team"]
6
6
  readme = "README.md"
@@ -207,10 +207,114 @@ class YouTubeDownloader:
207
207
  path=path, used_proxy=used_proxy, used_cookies=used_cookies, error=error
208
208
  )
209
209
 
210
+ async def has_captions(
211
+ self, video_id: str, *, languages: list[str] | None = None
212
+ ) -> bool:
213
+ """Whether YouTube holds captions for *video_id*, without downloading anything.
214
+
215
+ For a caller that stages audio only so transcription has something to fall back
216
+ on, this is the question worth asking first: a video with captions does not need
217
+ its audio pulled at all, and the pull is the expensive part.
218
+
219
+ Cheap by construction — `process=False` skips format resolution, so this is
220
+ one metadata call. The same direct → proxy → cookies escalation as `download`
221
+ applies: a bot-gated probe reports "no captions" rather than failing, and a
222
+ false negative costs a download rather than losing data.
223
+
224
+ `languages` narrows the question: only those codes count. A bare prefix match,
225
+ so "en" accepts "en-GB". Manual and automatic captions both count — the caller
226
+ decides what is good enough by whether it allows auto captions downstream.
227
+
228
+ Returns False when every attempt fails. That is the safe direction: the
229
+ caller downloads audio it might not have needed, rather than skipping audio
230
+ it did.
231
+ """
232
+ manual, auto = await self._list_captions(
233
+ video_id, use_proxy=False, use_cookies=False
234
+ )
235
+
236
+ if manual is None and self._proxy_configured:
237
+ logger.info("youtube_captions_probe_retry_with_proxy", video_id=video_id)
238
+ manual, auto = await self._list_captions(
239
+ video_id, use_proxy=True, use_cookies=False
240
+ )
241
+
242
+ if manual is None and self._proxy_configured and self._cookies_secret_id:
243
+ logger.info("youtube_captions_probe_retry_with_cookies", video_id=video_id)
244
+ manual, auto = await self._list_captions(
245
+ video_id, use_proxy=True, use_cookies=True
246
+ )
247
+
248
+ if manual is None:
249
+ logger.warning("youtube_captions_probe_failed", video_id=video_id)
250
+ return False
251
+
252
+ found = sorted(set(manual) | set(auto or []))
253
+ if languages:
254
+ wanted = [code.strip().lower() for code in languages if code.strip()]
255
+ found = [
256
+ code
257
+ for code in found
258
+ if any(code.lower().startswith(want) for want in wanted)
259
+ ]
260
+
261
+ logger.info("youtube_captions_probe", video_id=video_id, available=len(found))
262
+ return bool(found)
263
+
210
264
  # ------------------------------------------------------------------
211
265
  # Private helpers
212
266
  # ------------------------------------------------------------------
213
267
 
268
+ async def _list_captions(
269
+ self, video_id: str, *, use_proxy: bool, use_cookies: bool
270
+ ) -> tuple[list[str] | None, list[str] | None]:
271
+ """(manual, automatic) caption languages, or (None, None) if the probe failed.
272
+
273
+ None is distinct from an empty list: one means the probe could not run, the
274
+ other means it ran and the video has no captions.
275
+ """
276
+ import tempfile
277
+
278
+ with tempfile.TemporaryDirectory() as tmp_dir:
279
+ opts = _build_yt_dlp_opts(
280
+ output_template=str(Path(tmp_dir) / "%(id)s.%(ext)s"),
281
+ extra={"skip_download": True},
282
+ use_proxy=use_proxy,
283
+ )
284
+ if use_cookies:
285
+ cookies = await self._resolve_cookies()
286
+ if cookies:
287
+ cookies_path = Path(tmp_dir) / "cookies.txt"
288
+ cookies_path.write_text(cookies, encoding="utf-8")
289
+ cookies_path.chmod(0o600)
290
+ opts["cookiefile"] = str(cookies_path)
291
+
292
+ def _probe() -> tuple[list[str] | None, list[str] | None]:
293
+ import yt_dlp as _yt
294
+
295
+ url = f"https://www.youtube.com/watch?v={video_id}"
296
+ try:
297
+ with _yt.YoutubeDL(opts) as ydl:
298
+ # process=False keeps this to one metadata call: subtitle keys
299
+ # are populated before format processing, and format resolution
300
+ # is what fails on restricted format sets from datacentre IPs.
301
+ info = (
302
+ ydl.extract_info(url, download=False, process=False) or {}
303
+ )
304
+ except Exception as exc: # noqa: BLE001 — reported, not raised
305
+ logger.info(
306
+ "youtube_captions_probe_attempt_failed",
307
+ video_id=video_id,
308
+ error=str(exc)[:200],
309
+ )
310
+ return None, None
311
+ return (
312
+ list((info.get("subtitles") or {}).keys()),
313
+ list((info.get("automatic_captions") or {}).keys()),
314
+ )
315
+
316
+ return await asyncio.to_thread(_probe)
317
+
214
318
  async def _resolve_cookies(self) -> str:
215
319
  """Return cookies.txt content, fetching from Secrets Manager if configured.
216
320
 
File without changes