pulse-engine 0.2.14__tar.gz → 0.2.15__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.
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/PKG-INFO +1 -1
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/pyproject.toml +2 -2
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/youtube_downloader.py +42 -21
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/api/v1/auth.py +1 -2
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/config.py +4 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/main.py +1 -1
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/pipeline/translators/prefect_translator.py +29 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/runners/prefect_pipeline_flow.py +10 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/services/bootstrap.py +6 -1
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/README.md +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/audio_transcription.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/batcher.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/digital_news.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/digital_news_metadata.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/exceptions.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/models.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/opensearch_storage.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/speech_content.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/speech_metadata.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/transcription/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/transcription/base.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/transcription/config.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/transcription/factory.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/transcription/providers/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/transcription/providers/deepgram.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/transcription/providers/openai_whisper.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/transcription/providers/selfhosted_job.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/transcription/providers/youtube_captions.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/transcription/service.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/transcription/source.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/twitter.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/youtube_metadata.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/api/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/api/v1/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/api/v1/health.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/api/v1/router.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cli/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cli/main.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cli/templates/cookiecutter.json +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/.gitignore +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/Dockerfile +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/pipeline.yaml +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/pyproject.toml +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/src/pulse_{{cookiecutter.product_slug}}/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/tests/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/tests/unit/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cli/templates/pulse-{{cookiecutter.product_name}}/tests/unit/test_manifest.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/client.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/core/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/core/error_handlers.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/core/exceptions.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/core/job_token.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/core/logging.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/core/scope.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/core/security.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cron/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cron/models.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cron/pipeline_sync.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cron/repository.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cron/router.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cron/runner.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cron/schemas.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cron/service.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cron/tasks.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cron/translator.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/database.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/dependencies.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/deployment/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/deployment/infra_provisioner.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/evolution/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/evolution/adapter.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/evolution/client.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/evolution/models.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/evolution/router.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/evolution/service.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/extractor/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/extractor/adapters/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/extractor/base.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/mcp/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/mcp/__main__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/mcp/server.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/mcp/tools_kb.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/mcp/tools_modules.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/mcp/tools_pipelines.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/mcp/tools_processor.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/middleware/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/middleware/rate_limit.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/middleware/request_id.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/middleware/security_headers.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/middleware/tenant.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/notifications/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/notifications/apprise_notifier.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/pipeline/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/pipeline/config_parser.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/pipeline/expression.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/pipeline/models.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/pipeline/repositories.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/pipeline/router_modules.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/pipeline/router_pipelines.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/pipeline/schemas.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/pipeline/service.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/pipeline/translators/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/pipeline/translators/airflow_status.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/pipeline/translators/airflow_translator.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/pipeline/translators/base.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/pipeline/translators/prefect_status.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/base.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/core/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/core/analysis.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/core/chunking.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/core/prompts.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/core/topic_splitter.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/defaults/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/defaults/core_processor.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/defaults/postprocessor.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/defaults/preprocessor.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/llm/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/llm/provider.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/ocr/gemini.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/pipeline.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/postprocessor/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/postprocessor/embeddings.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/postprocessor/tasks.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/preprocessor/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/preprocessor/tasks.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/router.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/schemas.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/registry.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/runners/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/runners/lambda_runner.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/runners/pipeline_runner.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/runners/prefect_runner.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/s3.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/secrets.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/services/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/services/opensearch.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/storage/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/storage/connectors/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/storage/connectors/athena.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/storage/connectors/base.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/storage/connectors/opensearch.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/storage/knowledge_base.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/storage/router.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/storage/schemas.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/testing/__init__.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/testing/fixtures.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/testing/mocks.py +0 -0
- {pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/worker.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pulse-engine"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.15"
|
|
4
4
|
description = "Pulse Engine — Hybrid framework for building Pulse products"
|
|
5
5
|
authors = ["Pulse Team"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -107,7 +107,7 @@ module = ["langdetect.*", "tiktoken.*", "bs4.*", "jose", "jose.*", "boto3", "bot
|
|
|
107
107
|
ignore_missing_imports = true
|
|
108
108
|
|
|
109
109
|
[[tool.mypy.overrides]]
|
|
110
|
-
module = ["prefect", "prefect.*", "entrypoint", "fitz", "twikit", "twikit.*", "yt_dlp", "google", "google.*", "apprise", "apprise.*"]
|
|
110
|
+
module = ["prefect", "prefect.*", "entrypoint", "fitz", "twikit", "twikit.*", "yt_dlp", "yt_dlp.*", "google", "google.*", "apprise", "apprise.*"]
|
|
111
111
|
ignore_missing_imports = true
|
|
112
112
|
|
|
113
113
|
[[tool.mypy.overrides]]
|
|
@@ -21,6 +21,8 @@ _DEFAULT_PLAYER_CLIENTS = ["tv_embedded", "web"]
|
|
|
21
21
|
def _build_yt_dlp_opts(
|
|
22
22
|
output_template: str,
|
|
23
23
|
extra: dict[str, Any] | None = None,
|
|
24
|
+
clip_start: float | None = None,
|
|
25
|
+
clip_end: float | None = None,
|
|
24
26
|
) -> dict[str, Any]:
|
|
25
27
|
"""Build yt-dlp option dict, merging env defaults with caller overrides."""
|
|
26
28
|
raw_clients = os.environ.get("YT_DLP_PLAYER_CLIENTS", "").strip()
|
|
@@ -53,23 +55,28 @@ def _build_yt_dlp_opts(
|
|
|
53
55
|
},
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
if
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
58
|
+
if extra:
|
|
59
|
+
override = dict(extra)
|
|
60
|
+
# cookies_content is handled separately — materialised to a temp file.
|
|
61
|
+
override.pop("cookies_content", None)
|
|
62
|
+
ea = override.pop("extractor_args", None)
|
|
63
|
+
opts.update(override)
|
|
64
|
+
if isinstance(ea, dict):
|
|
65
|
+
existing = opts.get("extractor_args") or {}
|
|
66
|
+
merged: dict[str, Any] = {**existing}
|
|
67
|
+
for k, v in ea.items():
|
|
68
|
+
if isinstance(v, dict) and isinstance(merged.get(k), dict):
|
|
69
|
+
merged[k] = {**merged[k], **v}
|
|
70
|
+
else:
|
|
71
|
+
merged[k] = v
|
|
72
|
+
opts["extractor_args"] = merged
|
|
73
|
+
|
|
74
|
+
if clip_start is not None or clip_end is not None:
|
|
75
|
+
from yt_dlp.utils import download_range_func
|
|
76
|
+
|
|
77
|
+
start = clip_start if clip_start is not None else 0.0
|
|
78
|
+
end = clip_end if clip_end is not None else float("inf")
|
|
79
|
+
opts["download_ranges"] = download_range_func(None, [(start, end)])
|
|
73
80
|
|
|
74
81
|
return opts
|
|
75
82
|
|
|
@@ -91,7 +98,13 @@ class YouTubeDownloader:
|
|
|
91
98
|
self._aws_region: str = os.environ.get("AWS_REGION", "us-east-1").strip()
|
|
92
99
|
self._cookies_cache: str | None = None # lazily populated
|
|
93
100
|
|
|
94
|
-
async def download(
|
|
101
|
+
async def download(
|
|
102
|
+
self,
|
|
103
|
+
video_id: str,
|
|
104
|
+
output_dir: str,
|
|
105
|
+
start: float | None = None,
|
|
106
|
+
end: float | None = None,
|
|
107
|
+
) -> Path | None:
|
|
95
108
|
"""Download audio for *video_id* into *output_dir*, returning path to mp3.
|
|
96
109
|
|
|
97
110
|
Returns ``None`` if the download fails for any reason.
|
|
@@ -99,7 +112,7 @@ class YouTubeDownloader:
|
|
|
99
112
|
url = f"https://www.youtube.com/watch?v={video_id}"
|
|
100
113
|
logger.info("youtube_download_started", video_id=video_id)
|
|
101
114
|
|
|
102
|
-
result = await self._download_audio(url, output_dir)
|
|
115
|
+
result = await self._download_audio(url, output_dir, start=start, end=end)
|
|
103
116
|
if result is None:
|
|
104
117
|
logger.warning("youtube_download_failed", video_id=video_id)
|
|
105
118
|
else:
|
|
@@ -152,7 +165,13 @@ class YouTubeDownloader:
|
|
|
152
165
|
)
|
|
153
166
|
return cookies
|
|
154
167
|
|
|
155
|
-
async def _download_audio(
|
|
168
|
+
async def _download_audio(
|
|
169
|
+
self,
|
|
170
|
+
url: str,
|
|
171
|
+
output_dir: str,
|
|
172
|
+
start: float | None = None,
|
|
173
|
+
end: float | None = None,
|
|
174
|
+
) -> Path | None:
|
|
156
175
|
"""Download audio from a YouTube URL to *output_dir* via yt-dlp."""
|
|
157
176
|
try:
|
|
158
177
|
import yt_dlp
|
|
@@ -161,7 +180,9 @@ class YouTubeDownloader:
|
|
|
161
180
|
return None
|
|
162
181
|
|
|
163
182
|
output_template = str(Path(output_dir) / "%(id)s.%(ext)s")
|
|
164
|
-
ydl_opts = _build_yt_dlp_opts(
|
|
183
|
+
ydl_opts = _build_yt_dlp_opts(
|
|
184
|
+
output_template, extra=self._yt_dlp_opts, clip_start=start, clip_end=end
|
|
185
|
+
)
|
|
165
186
|
|
|
166
187
|
# Resolve cookies (priority: Secrets Manager > env var).
|
|
167
188
|
cookies_content = await self._resolve_cookies()
|
|
@@ -47,8 +47,7 @@ def _compute_secret_hash(email: str, client_id: str, client_secret: str) -> str:
|
|
|
47
47
|
@router.post("/login", response_model=LoginResponse)
|
|
48
48
|
async def login(request: Request, body: LoginRequest) -> LoginResponse:
|
|
49
49
|
"""Authenticate with email and password, returns JWT tokens."""
|
|
50
|
-
|
|
51
|
-
check_rate_limit(request, scope="login", max_requests=5, window_seconds=60)
|
|
50
|
+
check_rate_limit(request, scope="login", max_requests=500, window_seconds=60)
|
|
52
51
|
settings = get_settings()
|
|
53
52
|
|
|
54
53
|
client_id = settings.cognito_app_client_id
|
|
@@ -51,6 +51,10 @@ class Settings(BaseSettings):
|
|
|
51
51
|
|
|
52
52
|
# Prefect ECS backend
|
|
53
53
|
prefect_ecs_work_pool_name: str = "pipeline-ecs"
|
|
54
|
+
# Fleet-wide cap on concurrently-running flow runs for the ECS work pool.
|
|
55
|
+
# Acts as the queue throttle: scheduled runs beyond this wait in Prefect's
|
|
56
|
+
# queue instead of all launching at once (and OOM-ing the workers).
|
|
57
|
+
prefect_work_pool_concurrency_limit: int = 100
|
|
54
58
|
# ECR image used to run pipeline_flow in ECS work pool
|
|
55
59
|
prefect_engine_image: str = ""
|
|
56
60
|
# Override compute backend for all pipeline steps ("ecs", "local", etc.)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import base64
|
|
4
|
+
import logging
|
|
4
5
|
from typing import Any
|
|
5
6
|
|
|
6
7
|
import httpx
|
|
@@ -8,6 +9,8 @@ import httpx
|
|
|
8
9
|
from pulse_engine.pipeline.schemas import PipelineConfig, SecretRef
|
|
9
10
|
from pulse_engine.pipeline.translators.base import BaseTranslator
|
|
10
11
|
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
11
14
|
|
|
12
15
|
class PrefectTranslator(BaseTranslator):
|
|
13
16
|
"""Translates a PipelineConfig into a Prefect flow run submission."""
|
|
@@ -128,6 +131,32 @@ class PrefectTranslator(BaseTranslator):
|
|
|
128
131
|
deploy_resp.raise_for_status()
|
|
129
132
|
return str(deploy_resp.json()["id"])
|
|
130
133
|
|
|
134
|
+
async def ensure_work_pool_concurrency_limit(self, limit: int) -> None:
|
|
135
|
+
"""Set the work pool's concurrency limit (idempotent).
|
|
136
|
+
|
|
137
|
+
This is the fleet-wide queue throttle: Prefect holds scheduled runs
|
|
138
|
+
beyond ``limit`` in its queue rather than dispatching them all at once,
|
|
139
|
+
which is what prevents workers from over-grabbing and OOM-ing. Failures
|
|
140
|
+
are swallowed (logged) so a Prefect hiccup can't block app startup.
|
|
141
|
+
"""
|
|
142
|
+
client = self._get_client()
|
|
143
|
+
try:
|
|
144
|
+
resp = await client.patch(
|
|
145
|
+
f"{self._api_url}/work_pools/{self._work_pool_name}",
|
|
146
|
+
json={"concurrency_limit": limit},
|
|
147
|
+
)
|
|
148
|
+
resp.raise_for_status()
|
|
149
|
+
except Exception as exc: # noqa: BLE001 - best-effort, non-fatal at startup
|
|
150
|
+
logger.warning(
|
|
151
|
+
"Failed to set concurrency limit %s on work pool %s: %s",
|
|
152
|
+
limit,
|
|
153
|
+
self._work_pool_name,
|
|
154
|
+
exc,
|
|
155
|
+
)
|
|
156
|
+
finally:
|
|
157
|
+
if self._client is None:
|
|
158
|
+
await client.aclose()
|
|
159
|
+
|
|
131
160
|
async def submit(
|
|
132
161
|
self,
|
|
133
162
|
pipeline_run_id: str,
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/runners/prefect_pipeline_flow.py
RENAMED
|
@@ -25,6 +25,12 @@ from pulse_engine.secrets import fetch_secret
|
|
|
25
25
|
|
|
26
26
|
_STDERR_LIMIT = int(os.getenv("PULSE_VM_STDERR_LIMIT", "4000"))
|
|
27
27
|
_VM_DEBUG = os.getenv("PULSE_VM_DEBUG", "0") == "1"
|
|
28
|
+
# Safety cap applied to fan-out steps that omit `max_concurrency`, so a single
|
|
29
|
+
# step can't launch unbounded step tasks and overwhelm the pipeline cluster.
|
|
30
|
+
# Set to 0 to restore fully-unbounded default behaviour.
|
|
31
|
+
_DEFAULT_FANOUT_MAX_CONCURRENCY = int(
|
|
32
|
+
os.getenv("PULSE_DEFAULT_FANOUT_MAX_CONCURRENCY", "20")
|
|
33
|
+
)
|
|
28
34
|
|
|
29
35
|
_F = TypeVar("_F", bound=Callable[..., Any])
|
|
30
36
|
|
|
@@ -1293,6 +1299,10 @@ def _execute_layer(
|
|
|
1293
1299
|
# below is a no-op (it's already done). Net effect: sync mode
|
|
1294
1300
|
# is unaffected by max_concurrency.
|
|
1295
1301
|
max_c = step_config.get("max_concurrency")
|
|
1302
|
+
# When a step omits an explicit cap, fall back to the global safety
|
|
1303
|
+
# default so unbounded fan-out can't flood the pipeline cluster.
|
|
1304
|
+
if (not max_c or max_c <= 0) and _DEFAULT_FANOUT_MAX_CONCURRENCY > 0:
|
|
1305
|
+
max_c = _DEFAULT_FANOUT_MAX_CONCURRENCY
|
|
1296
1306
|
n_items = len(items)
|
|
1297
1307
|
if not max_c or max_c <= 0 or max_c >= n_items:
|
|
1298
1308
|
futures = [
|
|
@@ -154,12 +154,17 @@ async def bootstrap_services(
|
|
|
154
154
|
PrefectTranslator,
|
|
155
155
|
)
|
|
156
156
|
|
|
157
|
-
|
|
157
|
+
prefect_translator = PrefectTranslator(
|
|
158
158
|
prefect_api_url=settings.prefect_api_url,
|
|
159
159
|
prefect_api_key=settings.prefect_api_key,
|
|
160
160
|
work_pool_name=settings.prefect_ecs_work_pool_name,
|
|
161
161
|
engine_image=settings.prefect_engine_image,
|
|
162
162
|
)
|
|
163
|
+
# Apply the fleet-wide queue throttle (idempotent, best-effort).
|
|
164
|
+
await prefect_translator.ensure_work_pool_concurrency_limit(
|
|
165
|
+
settings.prefect_work_pool_concurrency_limit
|
|
166
|
+
)
|
|
167
|
+
pipeline_translators["prefect"] = prefect_translator
|
|
163
168
|
pipeline_status_providers["prefect"] = PrefectStatusProvider(
|
|
164
169
|
prefect_api_url=settings.prefect_api_url,
|
|
165
170
|
prefect_api_key=settings.prefect_api_key,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/audio_transcription.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/digital_news_metadata.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/transcription/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/transcription/config.py
RENAMED
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/transcription/factory.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/transcription/service.py
RENAMED
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/adapters/transcription/source.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/cli/templates/cookiecutter.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/deployment/infra_provisioner.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/notifications/apprise_notifier.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/pipeline/translators/__init__.py
RENAMED
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/pipeline/translators/airflow_status.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/pipeline/translators/prefect_status.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/core/topic_splitter.py
RENAMED
|
File without changes
|
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/defaults/core_processor.py
RENAMED
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/defaults/postprocessor.py
RENAMED
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/defaults/preprocessor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/postprocessor/__init__.py
RENAMED
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/postprocessor/embeddings.py
RENAMED
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/postprocessor/tasks.py
RENAMED
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/preprocessor/__init__.py
RENAMED
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/processor/preprocessor/tasks.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulse_engine-0.2.14 → pulse_engine-0.2.15}/src/pulse_engine/storage/connectors/opensearch.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|