aria-code 0.167.0__py3-none-win_amd64.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.
- aria_bots/__init__.py +7 -0
- aria_bots/channels/README.md +97 -0
- aria_bots/channels/__init__.py +1 -0
- aria_bots/channels/approvals.py +263 -0
- aria_bots/channels/conversation.py +280 -0
- aria_bots/channels/digest.py +186 -0
- aria_bots/channels/registry.py +95 -0
- aria_bots/engine.py +144 -0
- aria_bots/feishu_bot.py +1385 -0
- aria_bots/logistics/__init__.py +1 -0
- aria_bots/logistics/carriers.py +253 -0
- aria_bots/logistics/freight.py +154 -0
- aria_bots/logistics/inventory.py +242 -0
- aria_bots/logistics/mcp_server.py +156 -0
- aria_bots/logistics/records.py +119 -0
- aria_bots/logistics/tenancy.py +152 -0
- aria_bots/market.py +91 -0
- aria_bots/relay_client.py +326 -0
- aria_bots/telegram_bot.py +299 -0
- aria_code/__init__.py +111 -0
- aria_code/_bin/aria-native.exe +0 -0
- aria_code/_bin/aria-runtime.exe +0 -0
- aria_code/_version.py +20 -0
- aria_code/account_memory.py +296 -0
- aria_code/account_session.py +166 -0
- aria_code/adk_apps/__init__.py +1 -0
- aria_code/adk_apps/aria_code_review/__init__.py +1 -0
- aria_code/adk_apps/aria_code_review/agent.py +32 -0
- aria_code/adk_apps/aria_research/__init__.py +1 -0
- aria_code/adk_apps/aria_research/agent.py +31 -0
- aria_code/agents/__init__.py +49 -0
- aria_code/agents/base.py +329 -0
- aria_code/agents/code_review.py +200 -0
- aria_code/agents/deep/__init__.py +37 -0
- aria_code/agents/deep/calibration_loop.py +144 -0
- aria_code/agents/deep/critic.py +125 -0
- aria_code/agents/deep/deepen.py +193 -0
- aria_code/agents/deep/models.py +149 -0
- aria_code/agents/deep/pipeline.py +164 -0
- aria_code/agents/deep/quant_fusion.py +192 -0
- aria_code/agents/deep/themes.py +95 -0
- aria_code/agents/deep/tiers.py +106 -0
- aria_code/agents/engineering/__init__.py +14 -0
- aria_code/agents/engineering/coder.py +222 -0
- aria_code/agents/engineering/tester.py +145 -0
- aria_code/agents/financial/__init__.py +21 -0
- aria_code/agents/financial/cashflow_burn.py +119 -0
- aria_code/agents/financial/catalyst.py +279 -0
- aria_code/agents/financial/corporate_finance.py +175 -0
- aria_code/agents/financial/debate.py +224 -0
- aria_code/agents/financial/earnings.py +315 -0
- aria_code/agents/financial/fundamental.py +178 -0
- aria_code/agents/financial/macro.py +111 -0
- aria_code/agents/financial/news.py +231 -0
- aria_code/agents/financial/risk.py +146 -0
- aria_code/agents/financial/sector.py +279 -0
- aria_code/agents/financial/strategist.py +121 -0
- aria_code/agents/financial/stripe_revenue.py +127 -0
- aria_code/agents/financial/synthesis.py +274 -0
- aria_code/agents/financial/technical.py +277 -0
- aria_code/agents/orchestrator.py +44 -0
- aria_code/agents/portfolio_agent.py +392 -0
- aria_code/agents/realty/__init__.py +62 -0
- aria_code/agents/realty/asset_diagnosis.py +152 -0
- aria_code/agents/realty/business_match.py +166 -0
- aria_code/agents/realty/cashflow_verify.py +209 -0
- aria_code/agents/realty/contract_rules.py +210 -0
- aria_code/agents/realty/energy_anomaly.py +189 -0
- aria_code/agents/realty/exit_settlement.py +208 -0
- aria_code/agents/realty/fulfillment_risk.py +206 -0
- aria_code/agents/realty/ops_optimize.py +160 -0
- aria_code/agents/realty/revenue_share.py +215 -0
- aria_code/agents/registry.py +168 -0
- aria_code/agents/signal_scheme.py +111 -0
- aria_code/agents/sports/__init__.py +0 -0
- aria_code/agents/sports/football_agent.py +197 -0
- aria_code/agents/supervisor.py +113 -0
- aria_code/agents/team.py +500 -0
- aria_code/agents/video/__init__.py +16 -0
- aria_code/agents/video/director.py +376 -0
- aria_code/agents/video/qa_agent.py +143 -0
- aria_code/agents/video/script_doctor.py +112 -0
- aria_code/agents/warehouse/__init__.py +25 -0
- aria_code/agents/warehouse/contracts.py +26 -0
- aria_code/agents/warehouse/dashboard.py +155 -0
- aria_code/agents/warehouse/fulfillment_leadtime.py +95 -0
- aria_code/agents/warehouse/inbound_exceptions.py +25 -0
- aria_code/agents/warehouse/inventory_health.py +24 -0
- aria_code/agents/warehouse/local_run.py +71 -0
- aria_code/agents/warehouse/local_snapshot.py +132 -0
- aria_code/agents/warehouse/logistics_cost.py +84 -0
- aria_code/agents/warehouse/logistics_sync.py +21 -0
- aria_code/agents/warehouse/navigation.schema.json +110 -0
- aria_code/agents/warehouse/task_agent.py +172 -0
- aria_code/agents/warehouse/workflow.py +31 -0
- aria_code/aliyun_data_client.py +4 -0
- aria_code/apps/README.md +12 -0
- aria_code/apps/__init__.py +2 -0
- aria_code/apps/channels/README.md +9 -0
- aria_code/apps/channels/__init__.py +17 -0
- aria_code/apps/channels/approvals.py +11 -0
- aria_code/apps/channels/conversation.py +11 -0
- aria_code/apps/channels/digest.py +11 -0
- aria_code/apps/channels/intake.py +92 -0
- aria_code/apps/channels/registry.py +11 -0
- aria_code/apps/channels/tradingview.py +94 -0
- aria_code/apps/cli/README.md +13 -0
- aria_code/apps/cli/__init__.py +2 -0
- aria_code/apps/cli/_optional.py +120 -0
- aria_code/apps/cli/app_server.py +656 -0
- aria_code/apps/cli/auto_update.py +125 -0
- aria_code/apps/cli/bootstrap.py +206 -0
- aria_code/apps/cli/broker_render.py +206 -0
- aria_code/apps/cli/chat_turn.py +1327 -0
- aria_code/apps/cli/cloud_models.py +231 -0
- aria_code/apps/cli/codegen_paths.py +29 -0
- aria_code/apps/cli/commands/__init__.py +34 -0
- aria_code/apps/cli/commands/_business_render.py +78 -0
- aria_code/apps/cli/commands/_ui.py +71 -0
- aria_code/apps/cli/commands/analysis_cmds.py +378 -0
- aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
- aria_code/apps/cli/commands/auth_cmds.py +215 -0
- aria_code/apps/cli/commands/backtest_cmds.py +2518 -0
- aria_code/apps/cli/commands/broker_cmds.py +1236 -0
- aria_code/apps/cli/commands/business_workflow_cmds.py +379 -0
- aria_code/apps/cli/commands/canvas_cmds.py +77 -0
- aria_code/apps/cli/commands/catalog.py +189 -0
- aria_code/apps/cli/commands/core_cmds.py +2748 -0
- aria_code/apps/cli/commands/data_cmds.py +521 -0
- aria_code/apps/cli/commands/diagnostic_cmds.py +285 -0
- aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
- aria_code/apps/cli/commands/engineering_cmds.py +399 -0
- aria_code/apps/cli/commands/file_cmds.py +312 -0
- aria_code/apps/cli/commands/finance_service_cmds.py +60 -0
- aria_code/apps/cli/commands/fx_commodity_cmds.py +123 -0
- aria_code/apps/cli/commands/market.py +544 -0
- aria_code/apps/cli/commands/market_cmds.py +1419 -0
- aria_code/apps/cli/commands/market_context.py +432 -0
- aria_code/apps/cli/commands/market_render.py +7 -0
- aria_code/apps/cli/commands/model_cmds.py +2374 -0
- aria_code/apps/cli/commands/ops_cmds.py +721 -0
- aria_code/apps/cli/commands/orchestrator_cmds.py +181 -0
- aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
- aria_code/apps/cli/commands/portfolio_cmds.py +1316 -0
- aria_code/apps/cli/commands/report.py +476 -0
- aria_code/apps/cli/commands/scaffold_templates.py +617 -0
- aria_code/apps/cli/commands/session_cmds.py +236 -0
- aria_code/apps/cli/commands/session_ux_cmds.py +359 -0
- aria_code/apps/cli/commands/team.py +723 -0
- aria_code/apps/cli/commands/team_render.py +8 -0
- aria_code/apps/cli/commands/ui_cmds.py +401 -0
- aria_code/apps/cli/commands/warehouse_cmds.py +297 -0
- aria_code/apps/cli/commands/workflow_cmds.py +629 -0
- aria_code/apps/cli/commands/workspace_cmds.py +1543 -0
- aria_code/apps/cli/config_paths.py +73 -0
- aria_code/apps/cli/config_store.py +158 -0
- aria_code/apps/cli/constants.py +88 -0
- aria_code/apps/cli/context.py +34 -0
- aria_code/apps/cli/conversation_policy.py +53 -0
- aria_code/apps/cli/deterministic.py +200 -0
- aria_code/apps/cli/direct.py +48 -0
- aria_code/apps/cli/earnings_view.py +172 -0
- aria_code/apps/cli/endpoint_migration.py +64 -0
- aria_code/apps/cli/exec_events.py +255 -0
- aria_code/apps/cli/file_references.py +155 -0
- aria_code/apps/cli/finance_service_catalog.py +111 -0
- aria_code/apps/cli/football_reports.py +231 -0
- aria_code/apps/cli/github_app_auth.py +135 -0
- aria_code/apps/cli/google_login.py +239 -0
- aria_code/apps/cli/handlers/__init__.py +11 -0
- aria_code/apps/cli/handlers/broker_handlers.py +122 -0
- aria_code/apps/cli/handlers/chart_handlers.py +1314 -0
- aria_code/apps/cli/handlers/market_handlers.py +2570 -0
- aria_code/apps/cli/handlers/realty_handlers.py +114 -0
- aria_code/apps/cli/handlers/sports_handlers.py +22 -0
- aria_code/apps/cli/handlers/strategy_advice.py +82 -0
- aria_code/apps/cli/headless.py +352 -0
- aria_code/apps/cli/helpers.py +326 -0
- aria_code/apps/cli/hooks.py +180 -0
- aria_code/apps/cli/i18n.py +310 -0
- aria_code/apps/cli/intent.py +136 -0
- aria_code/apps/cli/intent_router.py +305 -0
- aria_code/apps/cli/intent_shadow.py +72 -0
- aria_code/apps/cli/intent_signals.py +347 -0
- aria_code/apps/cli/lifecycle_hooks.py +48 -0
- aria_code/apps/cli/local_tool_schemas.py +550 -0
- aria_code/apps/cli/main.py +39 -0
- aria_code/apps/cli/market_hours.py +100 -0
- aria_code/apps/cli/market_metadata.py +135 -0
- aria_code/apps/cli/market_universe.py +487 -0
- aria_code/apps/cli/message_processing.py +310 -0
- aria_code/apps/cli/mixin_binding.py +32 -0
- aria_code/apps/cli/model_catalog.py +652 -0
- aria_code/apps/cli/model_probe.py +169 -0
- aria_code/apps/cli/model_retirement.py +65 -0
- aria_code/apps/cli/native_branding.py +12 -0
- aria_code/apps/cli/native_bridge.py +118 -0
- aria_code/apps/cli/native_entry.py +58 -0
- aria_code/apps/cli/pdf_report.py +977 -0
- aria_code/apps/cli/plan_mode.py +139 -0
- aria_code/apps/cli/plotly_html.py +15 -0
- aria_code/apps/cli/prediction_feedback.py +202 -0
- aria_code/apps/cli/preflight.py +502 -0
- aria_code/apps/cli/project_aria.py +60 -0
- aria_code/apps/cli/prompt_assembly.py +78 -0
- aria_code/apps/cli/prompts/__init__.py +0 -0
- aria_code/apps/cli/prompts/coding.py +71 -0
- aria_code/apps/cli/prompts/select.py +193 -0
- aria_code/apps/cli/prompts/system_prompts.py +562 -0
- aria_code/apps/cli/prompts/ui.py +436 -0
- aria_code/apps/cli/provider_endpoints.py +237 -0
- aria_code/apps/cli/provider_keys.py +73 -0
- aria_code/apps/cli/providers/__init__.py +1 -0
- aria_code/apps/cli/providers/base.py +775 -0
- aria_code/apps/cli/providers/chat_routing.py +139 -0
- aria_code/apps/cli/providers/collaboration.py +98 -0
- aria_code/apps/cli/providers/llm/__init__.py +1 -0
- aria_code/apps/cli/providers/llm/offline_responses.py +68 -0
- aria_code/apps/cli/providers/llm/ollama_stream.py +1574 -0
- aria_code/apps/cli/providers/llm/response_cache.py +60 -0
- aria_code/apps/cli/providers/llm/sse_stream.py +298 -0
- aria_code/apps/cli/providers/runtime_bridge.py +586 -0
- aria_code/apps/cli/providers/vertexai_stream.py +592 -0
- aria_code/apps/cli/release_download.py +159 -0
- aria_code/apps/cli/review_runner.py +111 -0
- aria_code/apps/cli/runtime_consumer.py +769 -0
- aria_code/apps/cli/sandbox.py +32 -0
- aria_code/apps/cli/session_export.py +120 -0
- aria_code/apps/cli/session_jsonl.py +208 -0
- aria_code/apps/cli/session_refresh.py +123 -0
- aria_code/apps/cli/session_store.py +125 -0
- aria_code/apps/cli/skills_catalog.py +273 -0
- aria_code/apps/cli/task_isolation.py +211 -0
- aria_code/apps/cli/terminal_chrome.py +412 -0
- aria_code/apps/cli/todo_tracker.py +190 -0
- aria_code/apps/cli/tool_executor.py +932 -0
- aria_code/apps/cli/tool_registry.py +34 -0
- aria_code/apps/cli/tool_scope.py +113 -0
- aria_code/apps/cli/tools/__init__.py +40 -0
- aria_code/apps/cli/tools/context.py +46 -0
- aria_code/apps/cli/tools/file_tools.py +181 -0
- aria_code/apps/cli/tools/market_tools.py +626 -0
- aria_code/apps/cli/tools/notebook_tools.py +111 -0
- aria_code/apps/cli/tools/system_tools.py +656 -0
- aria_code/apps/cli/tools/write_tools.py +961 -0
- aria_code/apps/cli/tradingview_bridge.py +529 -0
- aria_code/apps/cli/transactions.py +269 -0
- aria_code/apps/cli/turn_planning.py +137 -0
- aria_code/apps/cli/update_check.py +257 -0
- aria_code/apps/cli/updater.py +373 -0
- aria_code/apps/cli/utils/__init__.py +0 -0
- aria_code/apps/cli/utils/market_detect.py +1663 -0
- aria_code/apps/cli/web_login.py +136 -0
- aria_code/apps/cli/workflow_runner.py +127 -0
- aria_code/apps/cli/workspace_route.py +121 -0
- aria_code/apps/daemon/README.md +14 -0
- aria_code/apps/vscode/README.md +115 -0
- aria_code/apps/vscode/package.json +70 -0
- aria_code/aria_cli.py +5244 -0
- aria_code/aria_daemon.py +1372 -0
- aria_code/aria_feishu_bot.py +16 -0
- aria_code/aria_mcp_server.py +61 -0
- aria_code/aria_relay_client.py +4 -0
- aria_code/aria_telegram_bot.py +11 -0
- aria_code/ariarc.py +385 -0
- aria_code/artifacts.py +581 -0
- aria_code/backtest_engine.py +4 -0
- aria_code/backtest_report.py +4 -0
- aria_code/brokers/__init__.py +78 -0
- aria_code/brokers/automation.py +193 -0
- aria_code/brokers/base.py +231 -0
- aria_code/brokers/capabilities.py +264 -0
- aria_code/brokers/cn/__init__.py +10 -0
- aria_code/brokers/cn/easytrader_broker.py +193 -0
- aria_code/brokers/cn/futu_broker.py +194 -0
- aria_code/brokers/cn/longbridge_broker.py +190 -0
- aria_code/brokers/cn/tiger_broker.py +196 -0
- aria_code/brokers/cn/xtquant_broker.py +225 -0
- aria_code/brokers/config.py +414 -0
- aria_code/brokers/intl/__init__.py +5 -0
- aria_code/brokers/intl/alpaca_broker.py +183 -0
- aria_code/brokers/intl/ibkr_broker.py +215 -0
- aria_code/brokers/intl/trading212_broker.py +183 -0
- aria_code/brokers/intl/webull_broker.py +156 -0
- aria_code/brokers/paper_broker.py +266 -0
- aria_code/brokers/planning.py +296 -0
- aria_code/brokers/registry.py +213 -0
- aria_code/brokers/trading.py +267 -0
- aria_code/canva_client.py +4 -0
- aria_code/change_store.py +127 -0
- aria_code/clients/__init__.py +0 -0
- aria_code/clients/aliyun_data_client.py +660 -0
- aria_code/clients/aria_relay_client.py +16 -0
- aria_code/clients/canva_client.py +284 -0
- aria_code/clients/figma_client.py +100 -0
- aria_code/clients/football_data_client.py +1671 -0
- aria_code/clients/kling_video_client.py +182 -0
- aria_code/clients/market_data_client.py +1985 -0
- aria_code/clients/openai_image_client.py +242 -0
- aria_code/clients/runway_video_client.py +157 -0
- aria_code/clients/warehouse_erp_client.py +105 -0
- aria_code/cloud_memory.py +190 -0
- aria_code/command_safety.py +19 -0
- aria_code/computer_use_server.py +276 -0
- aria_code/computer_use_tools.py +4 -0
- aria_code/dashboard_generator.py +579 -0
- aria_code/data_analysis_tools.py +4 -0
- aria_code/data_cleaner.py +483 -0
- aria_code/data_service.py +513 -0
- aria_code/datasources/__init__.py +23 -0
- aria_code/datasources/base.py +166 -0
- aria_code/datasources/router.py +253 -0
- aria_code/datasources/sec_statements.py +217 -0
- aria_code/datasources/sources/__init__.py +15 -0
- aria_code/datasources/sources/akshare_source.py +269 -0
- aria_code/datasources/sources/alpha_vantage_source.py +203 -0
- aria_code/datasources/sources/edgar_source.py +219 -0
- aria_code/datasources/sources/finnhub_source.py +198 -0
- aria_code/datasources/sources/fred_source.py +223 -0
- aria_code/datasources/sources/tushare_source.py +143 -0
- aria_code/datasources/sources/web_scraper_source.py +278 -0
- aria_code/datasources/sources/world_bank_source.py +206 -0
- aria_code/datasources/sources/yfinance_source.py +152 -0
- aria_code/doctor.py +860 -0
- aria_code/domain/__init__.py +0 -0
- aria_code/domain/_finance_core/__init__.py +5 -0
- aria_code/domain/_finance_core/backtest_engine.py +325 -0
- aria_code/domain/_finance_core/performance_stats.py +101 -0
- aria_code/domain/backtest_engine.py +75 -0
- aria_code/domain/backtest_report.py +488 -0
- aria_code/domain/finance_formulas.py +389 -0
- aria_code/domain/performance_stats.py +6 -0
- aria_code/domain/portfolio_ledger.py +307 -0
- aria_code/domain/strategy_vault.py +421 -0
- aria_code/evals/__init__.py +37 -0
- aria_code/evals/behavior.py +113 -0
- aria_code/evals/gap.py +93 -0
- aria_code/evals/harness.py +949 -0
- aria_code/evals/review_probe.py +132 -0
- aria_code/evals/runner.py +425 -0
- aria_code/evals/scorecard.py +322 -0
- aria_code/evals/summary.py +125 -0
- aria_code/external_agent_runner.py +115 -0
- aria_code/figma_client.py +4 -0
- aria_code/file_analysis_tools.py +4 -0
- aria_code/finance_formulas.py +4 -0
- aria_code/football_data_client.py +4 -0
- aria_code/image_gen_tools.py +4 -0
- aria_code/intent_classifier.py +459 -0
- aria_code/kling_video_client.py +4 -0
- aria_code/licensing.py +151 -0
- aria_code/local_finance_tools.py +4 -0
- aria_code/local_image_provider.py +4 -0
- aria_code/local_llm_provider.py +4 -0
- aria_code/macro_tools.py +4 -0
- aria_code/markdown_pdf.py +289 -0
- aria_code/market_data_client.py +4 -0
- aria_code/mcp_client.py +744 -0
- aria_code/memory_manager.py +273 -0
- aria_code/model_capability.py +512 -0
- aria_code/notification_tools.py +4 -0
- aria_code/openai_image_client.py +4 -0
- aria_code/packages/__init__.py +26 -0
- aria_code/packages/adk_bridge/__init__.py +11 -0
- aria_code/packages/adk_bridge/code_review_tools.py +49 -0
- aria_code/packages/adk_bridge/market_tools.py +125 -0
- aria_code/packages/aria_agents/__init__.py +5 -0
- aria_code/packages/aria_agents/manifest.py +78 -0
- aria_code/packages/aria_core/__init__.py +34 -0
- aria_code/packages/aria_core/architecture.py +269 -0
- aria_code/packages/aria_core/export.py +147 -0
- aria_code/packages/aria_core/manifest.py +65 -0
- aria_code/packages/aria_core/paths.py +57 -0
- aria_code/packages/aria_core/secure_file.py +68 -0
- aria_code/packages/aria_infra/__init__.py +15 -0
- aria_code/packages/aria_infra/arthera.py +58 -0
- aria_code/packages/aria_infra/doctor.py +246 -0
- aria_code/packages/aria_infra/product.py +40 -0
- aria_code/packages/aria_mcp/__init__.py +25 -0
- aria_code/packages/aria_mcp/bridge.py +232 -0
- aria_code/packages/aria_mcp/circuit.py +82 -0
- aria_code/packages/aria_mcp/config.py +162 -0
- aria_code/packages/aria_mcp/installer.py +95 -0
- aria_code/packages/aria_mcp/server.py +1705 -0
- aria_code/packages/aria_mcp/tools.py +81 -0
- aria_code/packages/aria_sdk/__init__.py +19 -0
- aria_code/packages/aria_sdk/client.py +393 -0
- aria_code/packages/aria_sdk/providers.py +70 -0
- aria_code/packages/aria_sdk/streaming.py +75 -0
- aria_code/packages/aria_sdk/types.py +86 -0
- aria_code/packages/aria_services/__init__.py +92 -0
- aria_code/packages/aria_services/cache.py +60 -0
- aria_code/packages/aria_services/context.py +356 -0
- aria_code/packages/aria_services/context_checkpoints.py +157 -0
- aria_code/packages/aria_services/data.py +11 -0
- aria_code/packages/aria_services/provider_health.py +304 -0
- aria_code/packages/aria_services/references.py +247 -0
- aria_code/packages/aria_services/registry.py +249 -0
- aria_code/packages/aria_services/research_protocol.py +137 -0
- aria_code/packages/aria_services/research_quality.py +186 -0
- aria_code/packages/aria_services/settings.py +160 -0
- aria_code/packages/aria_services/usage.py +151 -0
- aria_code/packages/aria_skills/__init__.py +46 -0
- aria_code/packages/aria_skills/catalog.py +149 -0
- aria_code/packages/aria_skills/loader.py +455 -0
- aria_code/packages/aria_skills/registry.py +84 -0
- aria_code/packages/aria_tools/__init__.py +5 -0
- aria_code/packages/aria_tools/financial/__init__.py +5 -0
- aria_code/packages/aria_tools/financial/_paths.py +92 -0
- aria_code/packages/aria_tools/registry.py +129 -0
- aria_code/packages/quant_engine/__init__.py +33 -0
- aria_code/packages/quant_engine/_namespace.py +24 -0
- aria_code/packages/quant_engine/backtest/__init__.py +10 -0
- aria_code/packages/quant_engine/backtest/core.py +53 -0
- aria_code/packages/quant_engine/backtest/engine.py +102 -0
- aria_code/packages/quant_engine/portfolio/__init__.py +9 -0
- aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
- aria_code/packages/quant_engine/sports/__init__.py +76 -0
- aria_code/packages/quant_engine/sports/calibrator.py +354 -0
- aria_code/packages/quant_engine/sports/dixon_coles.py +234 -0
- aria_code/packages/quant_engine/sports/elo.py +300 -0
- aria_code/packages/quant_engine/sports/form.py +188 -0
- aria_code/packages/quant_engine/sports/h2h.py +195 -0
- aria_code/packages/quant_engine/sports/predictor.py +312 -0
- aria_code/packages/quant_engine/sports/tracker.py +665 -0
- aria_code/packages/quant_engine/stochastic/__init__.py +31 -0
- aria_code/packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
- aria_code/packages/quant_engine/stochastic/ito_calculus.py +477 -0
- aria_code/packages/quant_engine/stochastic/kelly_criterion.py +181 -0
- aria_code/packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
- aria_code/packages/quant_engine/stochastic/options_pricing.py +576 -0
- aria_code/packages/quant_engine/stochastic/stochastic_processes.py +90 -0
- aria_code/packs/__init__.py +85 -0
- aria_code/packs/base.py +168 -0
- aria_code/packs/finance/__init__.py +250 -0
- aria_code/packs/healthcare/__init__.py +40 -0
- aria_code/packs/logistics/__init__.py +171 -0
- aria_code/packs/payments/__init__.py +139 -0
- aria_code/packs/realty/__init__.py +190 -0
- aria_code/packs/registry.py +202 -0
- aria_code/packs/rules.py +90 -0
- aria_code/plan_utils.py +194 -0
- aria_code/plugin_loader.py +328 -0
- aria_code/portfolio_ledger.py +4 -0
- aria_code/preview_server.py +449 -0
- aria_code/privacy/__init__.py +5 -0
- aria_code/privacy/feedback.py +123 -0
- aria_code/project_review.py +278 -0
- aria_code/project_review_api.py +235 -0
- aria_code/project_review_server.py +18 -0
- aria_code/project_tools.py +4 -0
- aria_code/providers/__init__.py +30 -0
- aria_code/providers/google_video_provider.py +256 -0
- aria_code/providers/llm/__init__.py +19 -0
- aria_code/providers/llm/anthropic.py +184 -0
- aria_code/providers/llm/arthera_gateway.py +90 -0
- aria_code/providers/llm/autoconfig.py +213 -0
- aria_code/providers/llm/base.py +139 -0
- aria_code/providers/llm/custom.py +158 -0
- aria_code/providers/llm/ollama.py +137 -0
- aria_code/providers/llm/openai_compat.py +350 -0
- aria_code/providers/llm/registry.py +457 -0
- aria_code/providers/local_image_provider.py +202 -0
- aria_code/providers/local_llm_provider.py +549 -0
- aria_code/providers/ollama_models.py +79 -0
- aria_code/providers/openai_params.py +32 -0
- aria_code/providers/tool_messages.py +100 -0
- aria_code/realty_data_tools.py +4 -0
- aria_code/report_exporters.py +150 -0
- aria_code/report_generator.py +1764 -0
- aria_code/review_service.py +497 -0
- aria_code/runtime/__init__.py +176 -0
- aria_code/runtime/acceptance.py +632 -0
- aria_code/runtime/agent_loop.py +1851 -0
- aria_code/runtime/agent_profiles.py +75 -0
- aria_code/runtime/approval.py +71 -0
- aria_code/runtime/baseline.py +219 -0
- aria_code/runtime/budget.py +232 -0
- aria_code/runtime/checkpoints.py +481 -0
- aria_code/runtime/consult.py +65 -0
- aria_code/runtime/contract.py +282 -0
- aria_code/runtime/delivery.py +432 -0
- aria_code/runtime/events.py +113 -0
- aria_code/runtime/gateway.py +167 -0
- aria_code/runtime/lsp.py +484 -0
- aria_code/runtime/native_index.py +153 -0
- aria_code/runtime/orchestrator.py +55 -0
- aria_code/runtime/processes.py +343 -0
- aria_code/runtime/project_graph.py +614 -0
- aria_code/runtime/repo_map.py +712 -0
- aria_code/runtime/research_bundle.py +105 -0
- aria_code/runtime/review.py +269 -0
- aria_code/runtime/run_state.py +127 -0
- aria_code/runtime/run_store.py +522 -0
- aria_code/runtime/scoped_runs.py +113 -0
- aria_code/runtime/self_healing.py +422 -0
- aria_code/runtime/semantic_diff.py +171 -0
- aria_code/runtime/subagent.py +721 -0
- aria_code/runtime/symbol_edit.py +117 -0
- aria_code/runtime/task_brief.py +46 -0
- aria_code/runtime/task_context.py +145 -0
- aria_code/runtime/task_ledger.py +108 -0
- aria_code/runtime/task_orchestrator.py +90 -0
- aria_code/runtime/task_outcome.py +33 -0
- aria_code/runtime/task_verification.py +266 -0
- aria_code/runtime/task_worktree.py +448 -0
- aria_code/runtime/tool_executor.py +329 -0
- aria_code/runtime/tool_policy.py +107 -0
- aria_code/runtime/transactions.py +216 -0
- aria_code/runtime/windows_job.py +115 -0
- aria_code/runtime/workflows.py +222 -0
- aria_code/runtime/worktrees.py +215 -0
- aria_code/runway_video_client.py +4 -0
- aria_code/safety/__init__.py +27 -0
- aria_code/safety/client_isolation.py +178 -0
- aria_code/safety/permissions.py +339 -0
- aria_code/safety/risk.py +699 -0
- aria_code/safety/sandbox.py +148 -0
- aria_code/safety/service.py +102 -0
- aria_code/service_endpoints.py +14 -0
- aria_code/setup_wizard.py +684 -0
- aria_code/spreadsheet_tools.py +4 -0
- aria_code/strategy_vault.py +4 -0
- aria_code/tools/__init__.py +0 -0
- aria_code/tools/artifact_tools.py +252 -0
- aria_code/tools/broker_tools.py +92 -0
- aria_code/tools/build_quant_engine.py +97 -0
- aria_code/tools/code_audit_tools.py +192 -0
- aria_code/tools/computer_use_tools.py +509 -0
- aria_code/tools/data_analysis_tools.py +831 -0
- aria_code/tools/enterprise_finance_tools.py +202 -0
- aria_code/tools/extended_tools.py +240 -0
- aria_code/tools/file_analysis_tools.py +880 -0
- aria_code/tools/image_gen_tools.py +114 -0
- aria_code/tools/local_finance_tools.py +3488 -0
- aria_code/tools/logistics_carriers.py +11 -0
- aria_code/tools/logistics_inventory.py +11 -0
- aria_code/tools/logistics_io.py +11 -0
- aria_code/tools/logistics_tenancy.py +11 -0
- aria_code/tools/logistics_tools.py +11 -0
- aria_code/tools/macro_tools.py +412 -0
- aria_code/tools/notification_tools.py +249 -0
- aria_code/tools/project_tools.py +525 -0
- aria_code/tools/realty_data_tools.py +662 -0
- aria_code/tools/spreadsheet_tools.py +399 -0
- aria_code/tools/stripe_tools.py +115 -0
- aria_code/tools/video_tools.py +500 -0
- aria_code/training/__init__.py +1 -0
- aria_code/training/compare.py +117 -0
- aria_code/training/prelude_commands.py +111 -0
- aria_code/training/prelude_eval.py +390 -0
- aria_code/training/prelude_real.py +183 -0
- aria_code/training/prelude_train.py +248 -0
- aria_code/training/sft_dataset.py +230 -0
- aria_code/training/trajectory_dataset.py +263 -0
- aria_code/ui/__init__.py +105 -0
- aria_code/ui/banner.py +404 -0
- aria_code/ui/completer.py +571 -0
- aria_code/ui/console.py +285 -0
- aria_code/ui/input_box.py +505 -0
- aria_code/ui/mentions.py +206 -0
- aria_code/ui/picker.py +248 -0
- aria_code/ui/render/__init__.py +11 -0
- aria_code/ui/render/actions.py +297 -0
- aria_code/ui/render/finance.py +1732 -0
- aria_code/ui/render/market.py +253 -0
- aria_code/ui/render/output.py +954 -0
- aria_code/ui/render/responsive.py +75 -0
- aria_code/ui/render/team.py +367 -0
- aria_code/ui/robot.py +239 -0
- aria_code/ui/startup_dashboard.py +110 -0
- aria_code/video_analysis.py +147 -0
- aria_code/video_editor.py +518 -0
- aria_code/video_workflow/__init__.py +8 -0
- aria_code/video_workflow/__main__.py +61 -0
- aria_code/video_workflow/engines/__init__.py +18 -0
- aria_code/video_workflow/engines/base.py +69 -0
- aria_code/video_workflow/engines/custom_model.py +204 -0
- aria_code/video_workflow/engines/google_veo.py +94 -0
- aria_code/video_workflow/engines/local_engine.py +71 -0
- aria_code/video_workflow/engines/registry.py +98 -0
- aria_code/video_workflow/google_models.py +278 -0
- aria_code/video_workflow/music/__init__.py +5 -0
- aria_code/video_workflow/music/foley_service.py +132 -0
- aria_code/video_workflow/music/service.py +212 -0
- aria_code/video_workflow/pipeline.py +620 -0
- aria_code/video_workflow/plan.py +202 -0
- aria_code/video_workflow/session.py +297 -0
- aria_code/workspace/__init__.py +6 -0
- aria_code/workspace/files.py +219 -0
- aria_code/workspace/verify.py +167 -0
- aria_code/workspace_changes.py +199 -0
- aria_code/workspace_service.py +196 -0
- aria_code/workspace_tools.py +150 -0
- aria_code-0.167.0.dist-info/METADATA +330 -0
- aria_code-0.167.0.dist-info/RECORD +601 -0
- aria_code-0.167.0.dist-info/WHEEL +5 -0
- aria_code-0.167.0.dist-info/entry_points.txt +5 -0
- aria_code-0.167.0.dist-info/licenses/LICENSE +201 -0
- aria_code-0.167.0.dist-info/licenses/NOTICE +49 -0
- aria_code-0.167.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
"""Layer 1 of the video architecture: deterministic ffmpeg-based editing —
|
|
2
|
+
trim, concat, overlay text, overlay audio, convert format/aspect, change
|
|
3
|
+
speed. No AI, no model weights, no API key — just a subprocess wrapper
|
|
4
|
+
around the `ffmpeg`/`ffprobe` CLIs, same pattern as project_tools.py's
|
|
5
|
+
`_git()` helper (timeout-bounded, capture output, never raise — return an
|
|
6
|
+
error dict instead).
|
|
7
|
+
|
|
8
|
+
Layer 2 (video_analysis.py, local Whisper transcription + opencv scene
|
|
9
|
+
detection) and Layer 3 (cloud AI generation, not built yet — needs a
|
|
10
|
+
provider/budget decision first) are separate modules.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import shutil
|
|
16
|
+
import subprocess
|
|
17
|
+
import tempfile
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Any, Dict, List, Optional
|
|
20
|
+
|
|
21
|
+
DEFAULT_TIMEOUT = 300.0
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _require_ffmpeg() -> Optional[str]:
|
|
25
|
+
if shutil.which("ffmpeg") is None:
|
|
26
|
+
return "ffmpeg not found on PATH. Install it: brew install ffmpeg"
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _run_ffmpeg(args: List[str], *, timeout: float = DEFAULT_TIMEOUT) -> Dict[str, Any]:
|
|
31
|
+
"""Run an ffmpeg command. Returns {"success": bool, "error"?: str}."""
|
|
32
|
+
missing = _require_ffmpeg()
|
|
33
|
+
if missing:
|
|
34
|
+
return {"success": False, "error": missing}
|
|
35
|
+
try:
|
|
36
|
+
result = subprocess.run(
|
|
37
|
+
["ffmpeg", "-y", *args],
|
|
38
|
+
capture_output=True, text=True, timeout=timeout,
|
|
39
|
+
)
|
|
40
|
+
except subprocess.TimeoutExpired:
|
|
41
|
+
return {"success": False, "error": f"ffmpeg timed out after {timeout:.0f}s"}
|
|
42
|
+
except Exception as exc:
|
|
43
|
+
return {"success": False, "error": str(exc)}
|
|
44
|
+
if result.returncode != 0:
|
|
45
|
+
return {"success": False, "error": result.stderr[-800:]}
|
|
46
|
+
return {"success": True}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _default_output(input_path: str, suffix: str, ext: Optional[str] = None) -> Path:
|
|
50
|
+
src = Path(input_path)
|
|
51
|
+
from artifacts import create_user_artifact
|
|
52
|
+
|
|
53
|
+
artifact = create_user_artifact("video", src.stem, f"{src.stem}_{suffix}", ext or src.suffix)
|
|
54
|
+
return artifact.path
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def probe_video(input_path: str) -> Dict[str, Any]:
|
|
58
|
+
"""Return duration/resolution/codec info via ffprobe."""
|
|
59
|
+
if shutil.which("ffprobe") is None:
|
|
60
|
+
return {"success": False, "error": "ffprobe not found on PATH. Install it: brew install ffmpeg"}
|
|
61
|
+
if not Path(input_path).exists():
|
|
62
|
+
return {"success": False, "error": f"File not found: {input_path}"}
|
|
63
|
+
try:
|
|
64
|
+
result = subprocess.run(
|
|
65
|
+
["ffprobe", "-v", "error", "-show_entries",
|
|
66
|
+
"stream=width,height,codec_type,codec_name,r_frame_rate:format=duration,size",
|
|
67
|
+
"-of", "json", input_path],
|
|
68
|
+
capture_output=True, text=True, timeout=30,
|
|
69
|
+
)
|
|
70
|
+
except Exception as exc:
|
|
71
|
+
return {"success": False, "error": str(exc)}
|
|
72
|
+
if result.returncode != 0:
|
|
73
|
+
return {"success": False, "error": result.stderr[-500:]}
|
|
74
|
+
import json
|
|
75
|
+
return {"success": True, **json.loads(result.stdout)}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def trim_video(input_path: str, start: float, end: float, *, output_path: Optional[str] = None) -> Dict[str, Any]:
|
|
79
|
+
"""Cut [start, end] seconds out of a video (re-encodes for frame-accurate cuts)."""
|
|
80
|
+
if not Path(input_path).exists():
|
|
81
|
+
return {"success": False, "error": f"File not found: {input_path}"}
|
|
82
|
+
if end <= start:
|
|
83
|
+
return {"success": False, "error": "end must be greater than start"}
|
|
84
|
+
out = Path(output_path) if output_path else _default_output(input_path, "trimmed")
|
|
85
|
+
result = _run_ffmpeg([
|
|
86
|
+
"-ss", str(start), "-to", str(end), "-i", input_path,
|
|
87
|
+
"-c:v", "libx264", "-c:a", "aac", str(out),
|
|
88
|
+
])
|
|
89
|
+
if not result["success"]:
|
|
90
|
+
return result
|
|
91
|
+
return {"success": True, "path": str(out)}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def concat_videos(input_paths: List[str], *, output_path: Optional[str] = None) -> Dict[str, Any]:
|
|
95
|
+
"""Concatenate multiple videos in order. Re-encodes each to a common
|
|
96
|
+
codec first so mismatched source formats (different codec/resolution)
|
|
97
|
+
don't silently produce a broken concat — the naive ffmpeg concat demuxer
|
|
98
|
+
only works when all inputs already share a codec."""
|
|
99
|
+
for p in input_paths:
|
|
100
|
+
if not Path(p).exists():
|
|
101
|
+
return {"success": False, "error": f"File not found: {p}"}
|
|
102
|
+
if len(input_paths) < 2:
|
|
103
|
+
return {"success": False, "error": "Need at least 2 videos to concatenate"}
|
|
104
|
+
|
|
105
|
+
out = Path(output_path) if output_path else _default_output(input_paths[0], "concat")
|
|
106
|
+
filter_inputs = "".join(f"[{i}:v:0][{i}:a:0]" for i in range(len(input_paths)))
|
|
107
|
+
args = []
|
|
108
|
+
for p in input_paths:
|
|
109
|
+
args += ["-i", p]
|
|
110
|
+
args += [
|
|
111
|
+
"-filter_complex", f"{filter_inputs}concat=n={len(input_paths)}:v=1:a=1[outv][outa]",
|
|
112
|
+
"-map", "[outv]", "-map", "[outa]",
|
|
113
|
+
"-c:v", "libx264", "-c:a", "aac", str(out),
|
|
114
|
+
]
|
|
115
|
+
result = _run_ffmpeg(args)
|
|
116
|
+
if not result["success"]:
|
|
117
|
+
return result
|
|
118
|
+
return {"success": True, "path": str(out)}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def overlay_text(
|
|
122
|
+
input_path: str,
|
|
123
|
+
text: str,
|
|
124
|
+
*,
|
|
125
|
+
position: str = "bottom",
|
|
126
|
+
font_size: int = 36,
|
|
127
|
+
font_color: str = "white",
|
|
128
|
+
output_path: Optional[str] = None,
|
|
129
|
+
) -> Dict[str, Any]:
|
|
130
|
+
"""Burn a text overlay onto the video.
|
|
131
|
+
|
|
132
|
+
Renders the text as a transparent PNG via Pillow and composites it with
|
|
133
|
+
ffmpeg's `overlay` filter, rather than ffmpeg's own `drawtext` filter —
|
|
134
|
+
`drawtext` needs ffmpeg built with libfreetype, which isn't guaranteed
|
|
135
|
+
(confirmed absent on a stock Homebrew ffmpeg install: `No such filter:
|
|
136
|
+
'drawtext'`). Rendering the text ourselves works on any ffmpeg build.
|
|
137
|
+
"""
|
|
138
|
+
if not Path(input_path).exists():
|
|
139
|
+
return {"success": False, "error": f"File not found: {input_path}"}
|
|
140
|
+
|
|
141
|
+
probe = probe_video(input_path)
|
|
142
|
+
if not probe["success"]:
|
|
143
|
+
return probe
|
|
144
|
+
video_stream = next((s for s in probe.get("streams", []) if s.get("codec_type") == "video"), None)
|
|
145
|
+
if not video_stream:
|
|
146
|
+
return {"success": False, "error": "Could not determine video dimensions"}
|
|
147
|
+
width, height = int(video_stream["width"]), int(video_stream["height"])
|
|
148
|
+
|
|
149
|
+
from PIL import Image, ImageDraw, ImageFont
|
|
150
|
+
|
|
151
|
+
overlay_img = Image.new("RGBA", (width, height), (0, 0, 0, 0))
|
|
152
|
+
draw = ImageDraw.Draw(overlay_img)
|
|
153
|
+
try:
|
|
154
|
+
font = ImageFont.truetype("/System/Library/Fonts/Helvetica.ttc", font_size)
|
|
155
|
+
except Exception:
|
|
156
|
+
font = ImageFont.load_default()
|
|
157
|
+
|
|
158
|
+
bbox = draw.textbbox((0, 0), text, font=font)
|
|
159
|
+
text_w, text_h = bbox[2] - bbox[0], bbox[3] - bbox[1]
|
|
160
|
+
margin = 40
|
|
161
|
+
positions = {
|
|
162
|
+
"top": ((width - text_w) // 2, margin),
|
|
163
|
+
"bottom": ((width - text_w) // 2, height - text_h - margin),
|
|
164
|
+
"center": ((width - text_w) // 2, (height - text_h) // 2),
|
|
165
|
+
}
|
|
166
|
+
x, y = positions.get(position, positions["bottom"])
|
|
167
|
+
pad = 10
|
|
168
|
+
draw.rectangle([x - pad, y - pad, x + text_w + pad, y + text_h + pad], fill=(0, 0, 0, 128))
|
|
169
|
+
draw.text((x, y), text, font=font, fill=font_color)
|
|
170
|
+
|
|
171
|
+
out = Path(output_path) if output_path else _default_output(input_path, "text")
|
|
172
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
173
|
+
overlay_path = str(Path(tmpdir) / "overlay.png")
|
|
174
|
+
overlay_img.save(overlay_path)
|
|
175
|
+
result = _run_ffmpeg([
|
|
176
|
+
"-i", input_path, "-i", overlay_path,
|
|
177
|
+
"-filter_complex", "[0:v][1:v]overlay=0:0",
|
|
178
|
+
"-c:a", "copy", str(out),
|
|
179
|
+
])
|
|
180
|
+
if not result["success"]:
|
|
181
|
+
return result
|
|
182
|
+
return {"success": True, "path": str(out)}
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def overlay_audio(input_path: str, audio_path: str, *, replace: bool = False, output_path: Optional[str] = None) -> Dict[str, Any]:
|
|
186
|
+
"""Add or replace the audio track. replace=True drops the original audio
|
|
187
|
+
entirely; replace=False mixes the new track under the original."""
|
|
188
|
+
if not Path(input_path).exists():
|
|
189
|
+
return {"success": False, "error": f"File not found: {input_path}"}
|
|
190
|
+
if not Path(audio_path).exists():
|
|
191
|
+
return {"success": False, "error": f"File not found: {audio_path}"}
|
|
192
|
+
|
|
193
|
+
out = Path(output_path) if output_path else _default_output(input_path, "audio")
|
|
194
|
+
if replace:
|
|
195
|
+
args = ["-i", input_path, "-i", audio_path, "-map", "0:v:0", "-map", "1:a:0",
|
|
196
|
+
"-c:v", "copy", "-shortest", str(out)]
|
|
197
|
+
else:
|
|
198
|
+
args = ["-i", input_path, "-i", audio_path,
|
|
199
|
+
"-filter_complex", "[0:a][1:a]amix=inputs=2:duration=first[a]",
|
|
200
|
+
"-map", "0:v:0", "-map", "[a]", "-c:v", "copy", "-shortest", str(out)]
|
|
201
|
+
result = _run_ffmpeg(args)
|
|
202
|
+
if not result["success"]:
|
|
203
|
+
return result
|
|
204
|
+
return {"success": True, "path": str(out)}
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def convert_video(
|
|
208
|
+
input_path: str,
|
|
209
|
+
*,
|
|
210
|
+
output_format: Optional[str] = None,
|
|
211
|
+
aspect: Optional[str] = None,
|
|
212
|
+
output_path: Optional[str] = None,
|
|
213
|
+
) -> Dict[str, Any]:
|
|
214
|
+
"""Convert container/codec format and/or reframe to a target aspect
|
|
215
|
+
ratio (e.g. "9:16" for vertical social, "1:1" for square) via
|
|
216
|
+
crop-to-fill — center-crops rather than letterboxing, since a cropped
|
|
217
|
+
fill is what "reframe for a platform" almost always means in practice."""
|
|
218
|
+
if not Path(input_path).exists():
|
|
219
|
+
return {"success": False, "error": f"File not found: {input_path}"}
|
|
220
|
+
|
|
221
|
+
ext = f".{output_format}" if output_format else None
|
|
222
|
+
out = Path(output_path) if output_path else _default_output(input_path, "converted", ext)
|
|
223
|
+
|
|
224
|
+
vf = None
|
|
225
|
+
if aspect:
|
|
226
|
+
try:
|
|
227
|
+
aw, ah = (int(x) for x in aspect.split(":"))
|
|
228
|
+
except ValueError:
|
|
229
|
+
return {"success": False, "error": "aspect must be like '9:16'"}
|
|
230
|
+
vf = f"crop='min(iw,ih*{aw}/{ah})':'min(ih,iw*{ah}/{aw})',scale=trunc(iw/2)*2:trunc(ih/2)*2"
|
|
231
|
+
|
|
232
|
+
args = ["-i", input_path]
|
|
233
|
+
if vf:
|
|
234
|
+
args += ["-vf", vf]
|
|
235
|
+
args += ["-c:v", "libx264", "-c:a", "aac", str(out)]
|
|
236
|
+
result = _run_ffmpeg(args)
|
|
237
|
+
if not result["success"]:
|
|
238
|
+
return result
|
|
239
|
+
return {"success": True, "path": str(out)}
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def change_speed(input_path: str, factor: float, *, output_path: Optional[str] = None) -> Dict[str, Any]:
|
|
243
|
+
"""Speed up (factor > 1) or slow down (factor < 1) a video, audio pitch-
|
|
244
|
+
corrected. atempo only accepts 0.5-2.0 per filter pass, so factors
|
|
245
|
+
outside that range are chained across multiple atempo stages."""
|
|
246
|
+
if not Path(input_path).exists():
|
|
247
|
+
return {"success": False, "error": f"File not found: {input_path}"}
|
|
248
|
+
if factor <= 0:
|
|
249
|
+
return {"success": False, "error": "factor must be positive"}
|
|
250
|
+
|
|
251
|
+
out = Path(output_path) if output_path else _default_output(input_path, "speed")
|
|
252
|
+
atempo_stages = []
|
|
253
|
+
remaining = factor
|
|
254
|
+
while remaining > 2.0:
|
|
255
|
+
atempo_stages.append(2.0)
|
|
256
|
+
remaining /= 2.0
|
|
257
|
+
while remaining < 0.5:
|
|
258
|
+
atempo_stages.append(0.5)
|
|
259
|
+
remaining /= 0.5
|
|
260
|
+
atempo_stages.append(remaining)
|
|
261
|
+
atempo_filter = ",".join(f"atempo={s:.4f}" for s in atempo_stages)
|
|
262
|
+
|
|
263
|
+
result = _run_ffmpeg([
|
|
264
|
+
"-i", input_path,
|
|
265
|
+
"-filter_complex", f"[0:v]setpts={1/factor:.6f}*PTS[v];[0:a]{atempo_filter}[a]",
|
|
266
|
+
"-map", "[v]", "-map", "[a]",
|
|
267
|
+
"-c:v", "libx264", "-c:a", "aac", str(out),
|
|
268
|
+
])
|
|
269
|
+
if not result["success"]:
|
|
270
|
+
return result
|
|
271
|
+
return {"success": True, "path": str(out)}
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def create_ken_burns_clip(
|
|
275
|
+
image_path: str,
|
|
276
|
+
*,
|
|
277
|
+
duration: float = 4.0,
|
|
278
|
+
effect: str = "zoom_in",
|
|
279
|
+
aspect_ratio: str = "16:9",
|
|
280
|
+
output_path: Optional[str] = None,
|
|
281
|
+
) -> Dict[str, Any]:
|
|
282
|
+
"""Animate a static image with a smooth Ken Burns pan/zoom camera motion into an MP4 clip.
|
|
283
|
+
|
|
284
|
+
Guarantees 100% local, free, zero-API-cost video creation for storyboards and social cuts.
|
|
285
|
+
Includes a silent stereo audio track so concat with speech-backed clips works seamlessly.
|
|
286
|
+
"""
|
|
287
|
+
if not Path(image_path).exists():
|
|
288
|
+
return {"success": False, "error": f"Image file not found: {image_path}"}
|
|
289
|
+
|
|
290
|
+
duration = max(1.0, float(duration or 4.0))
|
|
291
|
+
fps = 25
|
|
292
|
+
frames = int(duration * fps)
|
|
293
|
+
|
|
294
|
+
if aspect_ratio == "9:16":
|
|
295
|
+
width, height = 1080, 1920
|
|
296
|
+
elif aspect_ratio == "1:1":
|
|
297
|
+
width, height = 1080, 1080
|
|
298
|
+
else:
|
|
299
|
+
width, height = 1920, 1080
|
|
300
|
+
|
|
301
|
+
if effect == "zoom_out":
|
|
302
|
+
zoom_expr = "if(lte(zoom,1.0),1.2,max(1.001,zoom-0.0015))"
|
|
303
|
+
x_expr = "iw/2-(iw/zoom/2)"
|
|
304
|
+
y_expr = "ih/2-(ih/zoom/2)"
|
|
305
|
+
elif effect == "pan_right":
|
|
306
|
+
zoom_expr = "1.15"
|
|
307
|
+
x_expr = "if(lte(on,1),(iw-iw/zoom)*0.2,x+0.5)"
|
|
308
|
+
y_expr = "ih/2-(ih/zoom/2)"
|
|
309
|
+
elif effect == "pan_left":
|
|
310
|
+
zoom_expr = "1.15"
|
|
311
|
+
x_expr = "if(lte(on,1),(iw-iw/zoom)*0.8,x-0.5)"
|
|
312
|
+
y_expr = "ih/2-(ih/zoom/2)"
|
|
313
|
+
else: # zoom_in
|
|
314
|
+
zoom_expr = "min(zoom+0.0015,1.25)"
|
|
315
|
+
x_expr = "iw/2-(iw/zoom/2)"
|
|
316
|
+
y_expr = "ih/2-(ih/zoom/2)"
|
|
317
|
+
|
|
318
|
+
out = Path(output_path) if output_path else _default_output(image_path, f"kenburns_{effect}", ".mp4")
|
|
319
|
+
|
|
320
|
+
filter_str = (
|
|
321
|
+
f"scale=8000:-1,"
|
|
322
|
+
f"zoompan=z='{zoom_expr}':x='{x_expr}':y='{y_expr}':d={frames}:s={width}x{height}:fps={fps},"
|
|
323
|
+
f"format=yuv420p"
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
args = [
|
|
327
|
+
"-loop", "1", "-i", str(image_path),
|
|
328
|
+
"-f", "lavfi", "-i", "anullsrc=channel_layout=stereo:sample_rate=44100",
|
|
329
|
+
"-vf", filter_str,
|
|
330
|
+
"-c:v", "libx264", "-tune", "stillimage",
|
|
331
|
+
"-c:a", "aac", "-b:a", "128k",
|
|
332
|
+
"-t", f"{duration:.2f}",
|
|
333
|
+
"-pix_fmt", "yuv420p",
|
|
334
|
+
str(out),
|
|
335
|
+
]
|
|
336
|
+
|
|
337
|
+
result = _run_ffmpeg(args, timeout=180.0)
|
|
338
|
+
if not result["success"]:
|
|
339
|
+
return result
|
|
340
|
+
return {
|
|
341
|
+
"success": True,
|
|
342
|
+
"path": str(out),
|
|
343
|
+
"duration": duration,
|
|
344
|
+
"aspect_ratio": aspect_ratio,
|
|
345
|
+
"effect": effect,
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def burn_subtitles_and_audio(
|
|
350
|
+
video_path: str,
|
|
351
|
+
*,
|
|
352
|
+
audio_path: Optional[str] = None,
|
|
353
|
+
srt_path: Optional[str] = None,
|
|
354
|
+
bgm_path: Optional[str] = None,
|
|
355
|
+
bgm_volume: float = 0.25,
|
|
356
|
+
output_path: Optional[str] = None,
|
|
357
|
+
) -> Dict[str, Any]:
|
|
358
|
+
"""Overlay narration voice, background music (BGM), and/or subtitles onto a video."""
|
|
359
|
+
if not Path(video_path).exists():
|
|
360
|
+
return {"success": False, "error": f"Video file not found: {video_path}"}
|
|
361
|
+
|
|
362
|
+
out = Path(output_path) if output_path else _default_output(video_path, "mastered", ".mp4")
|
|
363
|
+
|
|
364
|
+
inputs = ["-i", video_path]
|
|
365
|
+
filter_complex = []
|
|
366
|
+
has_narration = audio_path and Path(audio_path).exists()
|
|
367
|
+
has_bgm = bgm_path and Path(bgm_path).exists()
|
|
368
|
+
|
|
369
|
+
audio_inputs_idx = 1
|
|
370
|
+
if has_narration:
|
|
371
|
+
inputs += ["-i", audio_path]
|
|
372
|
+
narration_idx = audio_inputs_idx
|
|
373
|
+
audio_inputs_idx += 1
|
|
374
|
+
|
|
375
|
+
if has_bgm:
|
|
376
|
+
inputs += ["-stream_loop", "-1", "-i", bgm_path]
|
|
377
|
+
bgm_idx = audio_inputs_idx
|
|
378
|
+
audio_inputs_idx += 1
|
|
379
|
+
|
|
380
|
+
# Audio mixing
|
|
381
|
+
if has_narration and has_bgm:
|
|
382
|
+
filter_complex.append(
|
|
383
|
+
f"[{bgm_idx}:a]volume={bgm_volume:.2f}[bgm];"
|
|
384
|
+
f"[{narration_idx}:a][bgm]amix=inputs=2:duration=first:dropout_transition=2[aout]"
|
|
385
|
+
)
|
|
386
|
+
audio_map = ["[aout]"]
|
|
387
|
+
elif has_narration:
|
|
388
|
+
audio_map = [f"{narration_idx}:a"]
|
|
389
|
+
elif has_bgm:
|
|
390
|
+
filter_complex.append(f"[{bgm_idx}:a]volume={bgm_volume:.2f}[bgm]")
|
|
391
|
+
audio_map = ["[bgm]"]
|
|
392
|
+
else:
|
|
393
|
+
audio_map = ["0:a?"]
|
|
394
|
+
|
|
395
|
+
# Subtitles video filter
|
|
396
|
+
vf_list = []
|
|
397
|
+
if srt_path and Path(srt_path).exists():
|
|
398
|
+
# Escape path for ffmpeg subtitles filter
|
|
399
|
+
escaped_srt = str(Path(srt_path).resolve()).replace(":", "\\:").replace("'", "\\'")
|
|
400
|
+
vf_list.append(f"subtitles='{escaped_srt}':force_style='FontSize=18,PrimaryColour=&HFFFFFF,BackColour=&H80000000,BorderStyle=4'")
|
|
401
|
+
|
|
402
|
+
args = list(inputs)
|
|
403
|
+
if filter_complex:
|
|
404
|
+
args += ["-filter_complex", "".join(filter_complex)]
|
|
405
|
+
if vf_list:
|
|
406
|
+
args += ["-vf", ",".join(vf_list)]
|
|
407
|
+
|
|
408
|
+
args += ["-map", "0:v:0"]
|
|
409
|
+
for am in audio_map:
|
|
410
|
+
args += ["-map", am]
|
|
411
|
+
|
|
412
|
+
args += ["-c:v", "libx264", "-c:a", "aac", "-b:a", "192k", "-shortest", str(out)]
|
|
413
|
+
result = _run_ffmpeg(args, timeout=300.0)
|
|
414
|
+
if not result["success"]:
|
|
415
|
+
return result
|
|
416
|
+
return {"success": True, "path": str(out)}
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def assemble_storyboard(
|
|
420
|
+
storyboard: Dict[str, Any],
|
|
421
|
+
*,
|
|
422
|
+
output_path: Optional[str] = None,
|
|
423
|
+
aspect_ratio: str = "16:9",
|
|
424
|
+
) -> Dict[str, Any]:
|
|
425
|
+
"""Assemble a full multi-shot storyboard into a finished video package.
|
|
426
|
+
|
|
427
|
+
storyboard shape:
|
|
428
|
+
{
|
|
429
|
+
"title": "...",
|
|
430
|
+
"aspect_ratio": "16:9" | "9:16",
|
|
431
|
+
"shots": [
|
|
432
|
+
{
|
|
433
|
+
"shot_index": 1,
|
|
434
|
+
"duration": 4.0,
|
|
435
|
+
"clip_path": "/path/to/clip.mp4" (optional if source_image is set),
|
|
436
|
+
"source_image": "/path/to/image.jpg" (used for Ken Burns if clip_path missing),
|
|
437
|
+
"effect": "zoom_in" | "pan_right" | "pan_left" | "zoom_out",
|
|
438
|
+
"caption": "...",
|
|
439
|
+
},
|
|
440
|
+
...
|
|
441
|
+
],
|
|
442
|
+
"audio_path": optional narration wav/mp3,
|
|
443
|
+
"bgm_path": optional background music wav/mp3,
|
|
444
|
+
"subtitles_srt": optional SRT file path or text
|
|
445
|
+
}
|
|
446
|
+
"""
|
|
447
|
+
shots = storyboard.get("shots") or []
|
|
448
|
+
if not shots:
|
|
449
|
+
return {"success": False, "error": "Storyboard has no shots defined"}
|
|
450
|
+
|
|
451
|
+
aspect = storyboard.get("aspect_ratio") or aspect_ratio
|
|
452
|
+
temp_clips: List[str] = []
|
|
453
|
+
effects_cycle = ["zoom_in", "pan_right", "zoom_out", "pan_left"]
|
|
454
|
+
|
|
455
|
+
try:
|
|
456
|
+
for idx, shot in enumerate(shots):
|
|
457
|
+
clip = shot.get("clip_path")
|
|
458
|
+
if clip and Path(clip).exists():
|
|
459
|
+
temp_clips.append(str(clip))
|
|
460
|
+
elif shot.get("source_image") and Path(shot["source_image"]).exists():
|
|
461
|
+
effect = shot.get("effect") or effects_cycle[idx % len(effects_cycle)]
|
|
462
|
+
dur = float(shot.get("duration") or 4.0)
|
|
463
|
+
ken_res = create_ken_burns_clip(
|
|
464
|
+
shot["source_image"],
|
|
465
|
+
duration=dur,
|
|
466
|
+
effect=effect,
|
|
467
|
+
aspect_ratio=aspect,
|
|
468
|
+
)
|
|
469
|
+
if not ken_res.get("success"):
|
|
470
|
+
return {"success": False, "error": f"Shot {idx + 1} Ken Burns failed: {ken_res.get('error')}"}
|
|
471
|
+
temp_clips.append(ken_res["path"])
|
|
472
|
+
else:
|
|
473
|
+
return {
|
|
474
|
+
"success": False,
|
|
475
|
+
"error": f"Shot {idx + 1} has neither valid 'clip_path' nor 'source_image'",
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
# 1. Concatenate shot clips
|
|
479
|
+
if len(temp_clips) == 1:
|
|
480
|
+
concat_path = temp_clips[0]
|
|
481
|
+
else:
|
|
482
|
+
concat_res = concat_videos(temp_clips)
|
|
483
|
+
if not concat_res.get("success"):
|
|
484
|
+
return concat_res
|
|
485
|
+
concat_path = concat_res["path"]
|
|
486
|
+
|
|
487
|
+
# 2. Master audio & subtitles if present
|
|
488
|
+
audio_p = storyboard.get("audio_path")
|
|
489
|
+
bgm_p = storyboard.get("bgm_path")
|
|
490
|
+
srt_p = storyboard.get("subtitles_srt")
|
|
491
|
+
|
|
492
|
+
# If srt content is raw text instead of a file path, write to temporary file
|
|
493
|
+
srt_file: Optional[str] = None
|
|
494
|
+
if srt_p and not Path(srt_p).exists() and ("-->" in srt_p):
|
|
495
|
+
from artifacts import create_user_artifact
|
|
496
|
+
srt_art = create_user_artifact("text", "subtitles", "captions", ".srt")
|
|
497
|
+
srt_art.path.write_text(srt_p, encoding="utf-8")
|
|
498
|
+
srt_file = str(srt_art.path)
|
|
499
|
+
elif srt_p and Path(srt_p).exists():
|
|
500
|
+
srt_file = str(srt_p)
|
|
501
|
+
|
|
502
|
+
if audio_p or bgm_p or srt_file:
|
|
503
|
+
final_res = burn_subtitles_and_audio(
|
|
504
|
+
concat_path,
|
|
505
|
+
audio_path=audio_p,
|
|
506
|
+
bgm_path=bgm_p,
|
|
507
|
+
srt_path=srt_file,
|
|
508
|
+
output_path=output_path,
|
|
509
|
+
)
|
|
510
|
+
return final_res
|
|
511
|
+
|
|
512
|
+
if output_path and output_path != concat_path:
|
|
513
|
+
shutil.copy2(concat_path, output_path)
|
|
514
|
+
return {"success": True, "path": output_path, "shots_count": len(shots)}
|
|
515
|
+
|
|
516
|
+
return {"success": True, "path": concat_path, "shots_count": len(shots)}
|
|
517
|
+
except Exception as exc:
|
|
518
|
+
return {"success": False, "error": f"Failed to assemble storyboard: {exc}"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Script + images → short video, using Google Cloud models (see pipeline.py)."""
|
|
2
|
+
from aria_code.video_workflow.pipeline import VideoWorkflowError, plan_video, render_video, verify_video
|
|
3
|
+
from aria_code.video_workflow.plan import Asset, Beat, EditPlan, to_srt, validate_plan
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"Asset", "Beat", "EditPlan", "VideoWorkflowError",
|
|
7
|
+
"plan_video", "render_video", "to_srt", "validate_plan", "verify_video",
|
|
8
|
+
]
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Command-line entry for desktop clients: one JSON request in, JSON lines out.
|
|
2
|
+
|
|
3
|
+
python -m aria_code.video_workflow plan '{"script": ..., "asset_paths": [...], "project_dir": ...}'
|
|
4
|
+
python -m aria_code.video_workflow render '{"project_dir": ..., "confirm_generation": false}'
|
|
5
|
+
|
|
6
|
+
stdout carries one JSON object per line: {"type": "progress", "stage", "message"}
|
|
7
|
+
while working, then exactly one {"type": "result", ...} or {"type": "error", "error"}.
|
|
8
|
+
Nothing else is written to stdout, so a client can parse every line.
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _emit(payload: dict) -> None:
|
|
17
|
+
sys.stdout.write(json.dumps(payload, ensure_ascii=False) + "\n")
|
|
18
|
+
sys.stdout.flush()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def main(argv: list[str]) -> int:
|
|
22
|
+
from aria_code.video_workflow import VideoWorkflowError, plan_video, render_video
|
|
23
|
+
from aria_code.video_workflow.google_models import GoogleModelError
|
|
24
|
+
|
|
25
|
+
if len(argv) != 2 or argv[0] not in {"plan", "render"}:
|
|
26
|
+
_emit({"type": "error", "error": "usage: python -m aria_code.video_workflow plan|render '<json>'"})
|
|
27
|
+
return 2
|
|
28
|
+
try:
|
|
29
|
+
request = json.loads(argv[1])
|
|
30
|
+
except json.JSONDecodeError as exc:
|
|
31
|
+
_emit({"type": "error", "error": f"request is not JSON: {exc}"})
|
|
32
|
+
return 2
|
|
33
|
+
progress = lambda stage, message: _emit({"type": "progress", "stage": stage, "message": message}) # noqa: E731
|
|
34
|
+
try:
|
|
35
|
+
if argv[0] == "plan":
|
|
36
|
+
result = plan_video(
|
|
37
|
+
str(request.get("script") or ""),
|
|
38
|
+
[str(p) for p in request.get("asset_paths") or []],
|
|
39
|
+
str(request.get("project_dir") or ""),
|
|
40
|
+
platform=str(request.get("platform") or "douyin"),
|
|
41
|
+
aspect_ratio=request.get("aspect_ratio"),
|
|
42
|
+
language=str(request.get("language") or "zh"),
|
|
43
|
+
target_seconds=int(request.get("target_seconds") or 30),
|
|
44
|
+
allow_generation=bool(request.get("allow_generation")),
|
|
45
|
+
progress=progress,
|
|
46
|
+
)
|
|
47
|
+
else:
|
|
48
|
+
result = render_video(
|
|
49
|
+
str(request.get("project_dir") or ""),
|
|
50
|
+
confirm_generation=request.get("confirm_generation") is True,
|
|
51
|
+
progress=progress,
|
|
52
|
+
)
|
|
53
|
+
except (VideoWorkflowError, GoogleModelError, OSError, ValueError) as exc:
|
|
54
|
+
_emit({"type": "error", "error": str(exc)})
|
|
55
|
+
return 1
|
|
56
|
+
_emit({"type": "result", **result})
|
|
57
|
+
return 0
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
if __name__ == "__main__":
|
|
61
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""engines package — 视频生成引擎抽象与多模型适配层。"""
|
|
2
|
+
|
|
3
|
+
from .base import BaseVideoEngine, VideoShotRequest, VideoShotResult
|
|
4
|
+
from .google_veo import GoogleCloudVeoEngine
|
|
5
|
+
from .custom_model import CustomModelEngine
|
|
6
|
+
from .local_engine import LocalFallbackEngine
|
|
7
|
+
from .registry import VideoEngineRegistry, get_video_engine
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"BaseVideoEngine",
|
|
11
|
+
"VideoShotRequest",
|
|
12
|
+
"VideoShotResult",
|
|
13
|
+
"GoogleCloudVeoEngine",
|
|
14
|
+
"CustomModelEngine",
|
|
15
|
+
"LocalFallbackEngine",
|
|
16
|
+
"VideoEngineRegistry",
|
|
17
|
+
"get_video_engine",
|
|
18
|
+
]
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""base.py — 自研视频生成引擎抽象基类与数据规范。
|
|
2
|
+
|
|
3
|
+
定义统一的视频镜头请求 (VideoShotRequest)、生成结果 (VideoShotResult) 以及
|
|
4
|
+
引擎抽象接口 (BaseVideoEngine)。所有底层模型(Google Cloud Veo、自研私有模型、
|
|
5
|
+
本地引擎等)均需实现该接口。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from abc import ABC, abstractmethod
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from typing import Any, Dict, List, Optional
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class VideoShotRequest:
|
|
17
|
+
"""单个视频镜头的生成请求参数。"""
|
|
18
|
+
prompt: str
|
|
19
|
+
image_path: Optional[str] = None
|
|
20
|
+
duration_seconds: int = 5
|
|
21
|
+
aspect_ratio: str = "16:9" # "16:9" | "9:16" | "1:1"
|
|
22
|
+
camera_motion: Optional[Dict[str, float]] = None # e.g. {"zoom": 0.2, "pan": 0.0, "tilt": 0.1}
|
|
23
|
+
negative_prompt: Optional[str] = None
|
|
24
|
+
seed: Optional[int] = None
|
|
25
|
+
cfg_scale: float = 0.5
|
|
26
|
+
# 面向自研模型的专属扩展参数
|
|
27
|
+
custom_parameters: Dict[str, Any] = field(default_factory=dict)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class VideoShotResult:
|
|
32
|
+
"""单个视频镜头的生成结果。"""
|
|
33
|
+
success: bool
|
|
34
|
+
path: Optional[str] = None
|
|
35
|
+
video_url: Optional[str] = None
|
|
36
|
+
engine_name: str = ""
|
|
37
|
+
model_version: str = ""
|
|
38
|
+
duration: float = 0.0
|
|
39
|
+
aspect_ratio: str = "16:9"
|
|
40
|
+
cost_estimate_usd: Optional[float] = None
|
|
41
|
+
error: Optional[str] = None
|
|
42
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class BaseVideoEngine(ABC):
|
|
46
|
+
"""视频生成引擎抽象基类。"""
|
|
47
|
+
|
|
48
|
+
name: str = "base"
|
|
49
|
+
display_name: str = "Base Video Engine"
|
|
50
|
+
|
|
51
|
+
@abstractmethod
|
|
52
|
+
def generate_shot(self, request: VideoShotRequest) -> VideoShotResult:
|
|
53
|
+
"""同步/阻塞轮询生成单个视频镜头。"""
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
@abstractmethod
|
|
57
|
+
def health_check(self) -> Dict[str, Any]:
|
|
58
|
+
"""检查当前引擎的配置可用性、鉴权及服务状态。"""
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
def get_capabilities(self) -> Dict[str, Any]:
|
|
62
|
+
"""返回引擎支持的功能特性。"""
|
|
63
|
+
return {
|
|
64
|
+
"text_to_video": True,
|
|
65
|
+
"image_to_video": True,
|
|
66
|
+
"camera_control": False,
|
|
67
|
+
"supported_aspect_ratios": ["16:9", "9:16", "1:1"],
|
|
68
|
+
"max_duration_seconds": 10,
|
|
69
|
+
}
|