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,183 @@
|
|
|
1
|
+
"""A real-message test for Prelude's intent routing, kept on this computer.
|
|
2
|
+
|
|
3
|
+
Hand-written test messages only show that a model learned its author's style. Real
|
|
4
|
+
messages are the check that matters, and they are private, so this tool never writes them
|
|
5
|
+
into a repository and the person who owns them does the reading and the labelling:
|
|
6
|
+
|
|
7
|
+
python -m aria_code.training.prelude_real extract --source DIR_OR_FILE
|
|
8
|
+
python -m aria_code.training.prelude_real label
|
|
9
|
+
python -m aria_code.training.prelude_real import --source FILE # label<TAB>message
|
|
10
|
+
python -m aria_code.training.prelude_real score --adapter ADAPTER
|
|
11
|
+
|
|
12
|
+
``extract`` collects candidate messages from a directory of ``*.json`` interaction logs
|
|
13
|
+
(the ``instruction`` field) or a text file with one message per line, drops duplicates,
|
|
14
|
+
very short messages and anything that looks like a secret, an email address or a phone
|
|
15
|
+
number, and writes them to ``~/.aria/prelude/`` with owner-only permissions. ``label``
|
|
16
|
+
shows each message and asks for its label. ``score`` runs a model over the labelled
|
|
17
|
+
messages and prints counts only: accuracy and an interval, never a message.
|
|
18
|
+
|
|
19
|
+
Nothing here is ever committed; ``~/.aria/prelude`` is outside every repository.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import argparse
|
|
25
|
+
import json
|
|
26
|
+
import os
|
|
27
|
+
import re
|
|
28
|
+
import sys
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
from typing import Iterable, List, Optional
|
|
31
|
+
|
|
32
|
+
from aria_code.evals.scorecard import wilson
|
|
33
|
+
from aria_code.training import prelude_eval as P
|
|
34
|
+
|
|
35
|
+
HOME = Path(os.environ.get("ARIA_PRELUDE_HOME") or Path.home() / ".aria" / "prelude")
|
|
36
|
+
CANDIDATES = HOME / "real_candidates.jsonl"
|
|
37
|
+
LABELLED = HOME / "real_test.jsonl"
|
|
38
|
+
KEYS = {"c": "coding", "a": "analysis", "r": "realtime", "g": "general", "f": "finance"}
|
|
39
|
+
MIN_CHARS = 6
|
|
40
|
+
|
|
41
|
+
_PRIVATE = [
|
|
42
|
+
re.compile(r"[\w.+-]+@[\w-]+\.[\w.-]+"), # email address
|
|
43
|
+
re.compile(r"(?<!\d)(?:\+?\d[\d\s-]{8,}\d)(?!\d)"), # phone or long account number
|
|
44
|
+
re.compile(r"\b(?:sk|pk|ghp|gho|AIza|AKIA|xox[bap])[-_A-Za-z0-9]{12,}"), # API keys and tokens
|
|
45
|
+
re.compile(r"\b[A-Za-z0-9_-]{32,}\b"), # any long opaque string
|
|
46
|
+
re.compile(r"(?i)\b(?:password|passwd|secret|token|api[_-]?key)\b\s*[:=]"),
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def looks_private(text: str) -> bool:
|
|
51
|
+
return any(pattern.search(text) for pattern in _PRIVATE)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def read_source(source: Path) -> Iterable[str]:
|
|
55
|
+
if source.is_dir():
|
|
56
|
+
for path in sorted(source.rglob("*.json")):
|
|
57
|
+
try:
|
|
58
|
+
record = json.loads(path.read_text(encoding="utf-8"))
|
|
59
|
+
except (OSError, ValueError):
|
|
60
|
+
continue
|
|
61
|
+
text = record.get("instruction") if isinstance(record, dict) else None
|
|
62
|
+
if isinstance(text, str):
|
|
63
|
+
yield text
|
|
64
|
+
else:
|
|
65
|
+
for line in source.read_text(encoding="utf-8").splitlines():
|
|
66
|
+
yield line
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def candidates(texts: Iterable[str]) -> List[str]:
|
|
70
|
+
seen, out = set(), []
|
|
71
|
+
for text in texts:
|
|
72
|
+
text = " ".join(text.split())
|
|
73
|
+
if len(text) < MIN_CHARS or text in seen or looks_private(text):
|
|
74
|
+
continue
|
|
75
|
+
seen.add(text)
|
|
76
|
+
out.append(text)
|
|
77
|
+
return out
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _write_private(path: Path, rows: List[dict]) -> None:
|
|
81
|
+
HOME.mkdir(parents=True, exist_ok=True)
|
|
82
|
+
os.chmod(HOME, 0o700)
|
|
83
|
+
path.write_text("".join(json.dumps(r, ensure_ascii=False) + "\n" for r in rows), encoding="utf-8")
|
|
84
|
+
os.chmod(path, 0o600)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def extract(source: Path) -> dict:
|
|
88
|
+
kept = candidates(read_source(source))
|
|
89
|
+
_write_private(CANDIDATES, [{"text": t} for t in kept])
|
|
90
|
+
return {"candidates": len(kept), "written_to": str(CANDIDATES)}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _load(path: Path) -> List[dict]:
|
|
94
|
+
if not path.is_file():
|
|
95
|
+
return []
|
|
96
|
+
return [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines() if line.strip()]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def label(ask=input, show=print) -> dict:
|
|
100
|
+
"""Walk through unlabelled candidates; resumable, and a skipped message stays out."""
|
|
101
|
+
done = {r["text"] for r in _load(LABELLED)}
|
|
102
|
+
rows = _load(LABELLED)
|
|
103
|
+
todo = [r["text"] for r in _load(CANDIDATES) if r["text"] not in done]
|
|
104
|
+
show("c=coding a=analysis r=realtime g=general f=finance s=skip q=quit")
|
|
105
|
+
for text in todo:
|
|
106
|
+
show(f"\n{text}")
|
|
107
|
+
while True:
|
|
108
|
+
answer = ask("> ").strip().lower()
|
|
109
|
+
if answer == "q":
|
|
110
|
+
_write_private(LABELLED, rows)
|
|
111
|
+
return {"labelled": len(rows)}
|
|
112
|
+
if answer == "s" or answer in KEYS:
|
|
113
|
+
break
|
|
114
|
+
if answer in KEYS:
|
|
115
|
+
rows.append({"text": text, "label": KEYS[answer]})
|
|
116
|
+
_write_private(LABELLED, rows)
|
|
117
|
+
return {"labelled": len(rows)}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def import_labelled(source: Path) -> dict:
|
|
121
|
+
"""Load ``label<TAB>message`` lines written by the owner, replacing any earlier labels."""
|
|
122
|
+
valid = set(P.INTENT_LABELS)
|
|
123
|
+
rows, skipped = [], 0
|
|
124
|
+
for line in source.read_text(encoding="utf-8").splitlines():
|
|
125
|
+
label_name, _, text = line.partition("\t")
|
|
126
|
+
label_name, text = label_name.strip().lower(), " ".join(text.split())
|
|
127
|
+
if label_name in valid and len(text) >= 2:
|
|
128
|
+
rows.append({"text": text, "label": label_name})
|
|
129
|
+
elif line.strip():
|
|
130
|
+
skipped += 1
|
|
131
|
+
_write_private(LABELLED, rows)
|
|
132
|
+
per_label = {name: sum(1 for r in rows if r["label"] == name) for name in P.INTENT_LABELS}
|
|
133
|
+
return {"labelled": len(rows), "skipped_lines": skipped, "per_label": per_label}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def score(adapter: Optional[Path], model: Optional[str]) -> dict:
|
|
137
|
+
"""Counts only. A message never leaves this function."""
|
|
138
|
+
from aria_code.training import prelude_train as T
|
|
139
|
+
from mlx_lm import generate, load
|
|
140
|
+
|
|
141
|
+
labelled = _load(LABELLED)
|
|
142
|
+
if not labelled:
|
|
143
|
+
raise SystemExit(f"Nothing labelled yet in {LABELLED}. Run `label` first.")
|
|
144
|
+
network, tokenizer = load(T.base_model(model), adapter_path=str(adapter) if adapter else None)
|
|
145
|
+
right = unparsable = 0
|
|
146
|
+
for row in labelled:
|
|
147
|
+
prompt = tokenizer.apply_chat_template(
|
|
148
|
+
[{"role": "system", "content": P.INTENT_SYSTEM}, {"role": "user", "content": row["text"]}],
|
|
149
|
+
add_generation_prompt=True, tokenize=False)
|
|
150
|
+
prediction = P.parse_prediction("intent", generate(network, tokenizer, prompt=prompt, max_tokens=6))
|
|
151
|
+
unparsable += prediction is None
|
|
152
|
+
right += prediction == row["label"]
|
|
153
|
+
low, high = wilson(right, len(labelled))
|
|
154
|
+
return {"adapter": str(adapter) if adapter else None, "n": len(labelled), "correct": right,
|
|
155
|
+
"accuracy": round(right / len(labelled), 4), "ci95": [round(low, 4), round(high, 4)],
|
|
156
|
+
"unparsable": unparsable}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def main(argv: Optional[List[str]] = None) -> int:
|
|
160
|
+
parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
|
|
161
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
162
|
+
e = sub.add_parser("extract")
|
|
163
|
+
e.add_argument("--source", required=True, help="a directory of *.json interaction logs, or a text file")
|
|
164
|
+
sub.add_parser("label")
|
|
165
|
+
i = sub.add_parser("import", help="load label<TAB>message lines you wrote yourself")
|
|
166
|
+
i.add_argument("--source", required=True)
|
|
167
|
+
s = sub.add_parser("score")
|
|
168
|
+
s.add_argument("--adapter")
|
|
169
|
+
s.add_argument("--model")
|
|
170
|
+
args = parser.parse_args(argv)
|
|
171
|
+
if args.command == "extract":
|
|
172
|
+
print(json.dumps(extract(Path(args.source).expanduser())))
|
|
173
|
+
elif args.command == "label":
|
|
174
|
+
print(json.dumps(label()))
|
|
175
|
+
elif args.command == "import":
|
|
176
|
+
print(json.dumps(import_labelled(Path(args.source).expanduser())))
|
|
177
|
+
else:
|
|
178
|
+
print(json.dumps(score(Path(args.adapter) if args.adapter else None, args.model), indent=2))
|
|
179
|
+
return 0
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
if __name__ == "__main__":
|
|
183
|
+
sys.exit(main())
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"""Train and score Prelude's risk gate with MLX LoRA on a Mac.
|
|
2
|
+
|
|
3
|
+
Reproduces the experiment end to end from files in this repository:
|
|
4
|
+
|
|
5
|
+
python -m aria_code.training.prelude_train data --out DIR [--task intent]
|
|
6
|
+
python -m aria_code.training.prelude_train train --data DIR --adapter DIR/adapter
|
|
7
|
+
python -m aria_code.training.prelude_train pick --log DIR/train.log --adapter DIR/adapter
|
|
8
|
+
python -m aria_code.training.prelude_train eval --adapter DIR/adapter
|
|
9
|
+
|
|
10
|
+
``data`` uses only train-side splits (``dev`` and ``extra``). Variants of those commands
|
|
11
|
+
are generated by swapping arguments and wrapping them (``cd x &&``, ``time``, a pipe to
|
|
12
|
+
``tail``), labelled by ``safety.risk``, and a command that also appears in a test split
|
|
13
|
+
is dropped. Levels 3 and 4 are repeated so the loss weighs the dangerous cases. Risk
|
|
14
|
+
``data`` needs the locked sets from aria-evals-holdout (``ARIA_PRELUDE_HOLDOUT``) so it can
|
|
15
|
+
drop their families too. ``pick`` chooses a checkpoint by validation loss only. ``eval``
|
|
16
|
+
reports every split it can load; read ``test2`` as a score and never as a list of
|
|
17
|
+
mistakes to fix.
|
|
18
|
+
|
|
19
|
+
Needs ``mlx-lm`` (Apple Silicon); it is imported only by ``train`` and ``eval``.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import argparse
|
|
25
|
+
import hashlib
|
|
26
|
+
import json
|
|
27
|
+
import random
|
|
28
|
+
import re
|
|
29
|
+
import shutil
|
|
30
|
+
import subprocess
|
|
31
|
+
import sys
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
from typing import Dict, List, Optional, Tuple
|
|
34
|
+
|
|
35
|
+
from aria_code.training import prelude_eval as P
|
|
36
|
+
|
|
37
|
+
FILES = ["app.py", "src/main.rs", "README.md", "config.yaml", "docs/guide.md", "build/out.js", "tests/test_x.py",
|
|
38
|
+
"data/prices.csv", "notes.txt", "src/index.ts", "lib/util.go", "scripts/run.sh", "output/report.pdf",
|
|
39
|
+
"tmp/cache.bin", "web/app.css"]
|
|
40
|
+
PACKAGES = ["left-pad", "requests", "numpy", "zod", "serde", "rich", "pandas", "express", "tokio", "axios"]
|
|
41
|
+
BRANCHES = ["main", "dev", "feature/login", "fix/crash", "release/1.2"]
|
|
42
|
+
HOSTS = ["https://api.example.com/v1", "https://example.org/data.json", "https://files.test.io/pkg.tgz",
|
|
43
|
+
"http://localhost:8080/health"]
|
|
44
|
+
WRAPPERS = [
|
|
45
|
+
lambda c: c, lambda c: f"cd project && {c}", lambda c: f"time {c}", lambda c: f"{c} 2>&1",
|
|
46
|
+
lambda c: f"{c} | tail -20", lambda c: f"cd src && {c}", lambda c: f"FOO=1 {c}", lambda c: f"{c} | head -5",
|
|
47
|
+
]
|
|
48
|
+
WEIGHT = {3: 3, 4: 2} # how many times a level appears in the training file
|
|
49
|
+
VARIANTS_PER_FAMILY = 14
|
|
50
|
+
SEED = 7
|
|
51
|
+
MODEL_ENV = "ARIA_PRELUDE_BASE"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def vary(command: str, rng: random.Random) -> str:
|
|
55
|
+
tokens = command.split(" ")
|
|
56
|
+
for i, token in enumerate(tokens):
|
|
57
|
+
if not i:
|
|
58
|
+
continue
|
|
59
|
+
if re.search(r"\.\w{1,4}$|/", token) and not token.startswith(("http", "-", "/dev", "~/.ssh", ".env")) \
|
|
60
|
+
and ".pem" not in token and "credentials" not in token:
|
|
61
|
+
tokens[i] = rng.choice(FILES)
|
|
62
|
+
elif token in PACKAGES:
|
|
63
|
+
tokens[i] = rng.choice(PACKAGES)
|
|
64
|
+
elif token in BRANCHES:
|
|
65
|
+
tokens[i] = rng.choice(BRANCHES)
|
|
66
|
+
elif token.startswith("http") and "|" not in command:
|
|
67
|
+
tokens[i] = rng.choice(HOSTS)
|
|
68
|
+
return " ".join(tokens)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def record(command: str, level, system: str = P.RISK_SYSTEM) -> dict:
|
|
72
|
+
return {"messages": [{"role": "system", "content": system}, {"role": "user", "content": command},
|
|
73
|
+
{"role": "assistant", "content": str(level)}]}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def is_validation(family: str) -> bool:
|
|
77
|
+
return int(hashlib.sha256(family.encode()).hexdigest(), 16) % 10 == 3
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def build_training_data(require_holdout: bool = True) -> Tuple[List[dict], List[dict], dict]:
|
|
81
|
+
"""(train, validation, facts) from train-side splits only.
|
|
82
|
+
|
|
83
|
+
Generated variants can land in a locked family, and only the locked sets say which
|
|
84
|
+
families those are, so without them this refuses rather than risk training on one.
|
|
85
|
+
"""
|
|
86
|
+
from aria_code.safety.risk import assess_command
|
|
87
|
+
|
|
88
|
+
if require_holdout and not P.holdout_available("risk"):
|
|
89
|
+
raise SystemExit(f"The locked risk sets are not at {P.HOLDOUT}. Clone aria-evals-holdout "
|
|
90
|
+
"next to this checkout or set ARIA_PRELUDE_HOLDOUT to its prelude/ directory.")
|
|
91
|
+
rng = random.Random(SEED)
|
|
92
|
+
rows = P.load("risk")
|
|
93
|
+
train_rows = [r for r in rows if r["split"] in ("dev", "extra", "extra2")]
|
|
94
|
+
held_out = {r["input"] for r in rows if r["split"] in ("test", "test2", "test3")}
|
|
95
|
+
held_families = {r["family"] for r in rows if r["split"] in ("test", "test2", "test3")}
|
|
96
|
+
bases: Dict[str, str] = {}
|
|
97
|
+
for r in train_rows:
|
|
98
|
+
bases.setdefault(re.sub(r"^(cd project && |time )", "", r["input"]), r["family"])
|
|
99
|
+
variants: Dict[str, str] = {}
|
|
100
|
+
counts: Dict[str, int] = {}
|
|
101
|
+
for base, fam in bases.items():
|
|
102
|
+
for _ in range(40):
|
|
103
|
+
command = rng.choice(WRAPPERS)(vary(base, rng))
|
|
104
|
+
if "\n" in command or command in variants:
|
|
105
|
+
continue
|
|
106
|
+
variants[command] = fam
|
|
107
|
+
counts[fam] = counts.get(fam, 0) + 1
|
|
108
|
+
if counts[fam] >= VARIANTS_PER_FAMILY:
|
|
109
|
+
break
|
|
110
|
+
pool = [(c, assess_command(c).level, f) for c, f in variants.items()]
|
|
111
|
+
pool += [(r["input"], r["label"], r["family"]) for r in train_rows]
|
|
112
|
+
seen, distinct = set(), []
|
|
113
|
+
for command, level, fam in pool:
|
|
114
|
+
if command in seen or command in held_out or fam in held_families or P.family(command) in held_families:
|
|
115
|
+
continue
|
|
116
|
+
seen.add(command)
|
|
117
|
+
distinct.append((command, level, fam))
|
|
118
|
+
train, valid = [], []
|
|
119
|
+
for command, level, fam in distinct:
|
|
120
|
+
if is_validation(fam):
|
|
121
|
+
valid.append(record(command, level))
|
|
122
|
+
else:
|
|
123
|
+
train.extend(record(command, level) for _ in range(WEIGHT.get(level, 1)))
|
|
124
|
+
rng.shuffle(train)
|
|
125
|
+
return train, valid, {"distinct": len(distinct), "train_lines": len(train), "validation_lines": len(valid)}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def build_intent_data() -> Tuple[List[dict], List[dict], dict]:
|
|
129
|
+
"""Train and validation records for intent routing, from the ``dev`` split only.
|
|
130
|
+
|
|
131
|
+
Messages are hand-written and independent, so each is its own family. A fixed share of
|
|
132
|
+
``dev`` is held back for choosing a checkpoint; ``test`` is never read here.
|
|
133
|
+
"""
|
|
134
|
+
rng = random.Random(SEED)
|
|
135
|
+
rows = [r for r in P.load("intent") if r["split"] == "dev"]
|
|
136
|
+
train, valid = [], []
|
|
137
|
+
for r in rows:
|
|
138
|
+
(valid if is_validation(r["input"]) else train).append(record(r["input"], r["label"], P.INTENT_SYSTEM))
|
|
139
|
+
# A short run over ~150 messages underfits; repeat so the step count means something.
|
|
140
|
+
train = train * 4
|
|
141
|
+
rng.shuffle(train)
|
|
142
|
+
return train, valid, {"distinct": len(rows), "train_lines": len(train), "validation_lines": len(valid)}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def write_data(out: Path, task: str = "risk") -> dict:
|
|
146
|
+
train, valid, facts = build_intent_data() if task == "intent" else build_training_data()
|
|
147
|
+
out.mkdir(parents=True, exist_ok=True)
|
|
148
|
+
for name, data in (("train", train), ("valid", valid)):
|
|
149
|
+
(out / f"{name}.jsonl").write_text("".join(json.dumps(x, ensure_ascii=False) + "\n" for x in data),
|
|
150
|
+
encoding="utf-8")
|
|
151
|
+
return facts
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def base_model(explicit: Optional[str]) -> str:
|
|
155
|
+
import os
|
|
156
|
+
|
|
157
|
+
value = explicit or os.environ.get(MODEL_ENV)
|
|
158
|
+
if value:
|
|
159
|
+
return value
|
|
160
|
+
cache = Path.home() / ".cache" / "huggingface" / "hub" / "models--Qwen--Qwen2.5-1.5B-Instruct" / "snapshots"
|
|
161
|
+
found = sorted(cache.glob("*")) if cache.is_dir() else []
|
|
162
|
+
if not found:
|
|
163
|
+
raise SystemExit(f"Pass --model, or set {MODEL_ENV}, to a Qwen2.5-1.5B-Instruct directory or Hugging Face id.")
|
|
164
|
+
return str(found[-1])
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def train(data: Path, adapter: Path, model: Optional[str], iters: int, rate: float, layers: int) -> int:
|
|
168
|
+
command = [sys.executable, "-m", "mlx_lm", "lora", "--model", base_model(model), "--train", "--data", str(data),
|
|
169
|
+
"--adapter-path", str(adapter), "--iters", str(iters), "--batch-size", "4", "--num-layers", str(layers),
|
|
170
|
+
"--learning-rate", str(rate), "--steps-per-eval", "100", "--val-batches", "42", "--save-every", "100",
|
|
171
|
+
"--mask-prompt"]
|
|
172
|
+
return subprocess.call(command)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def validation_losses(log: str) -> Dict[int, float]:
|
|
176
|
+
text = re.sub(r"\x1b\[[0-9;]*m", "", log).replace("\r", "\n")
|
|
177
|
+
return {int(m.group(1)): float(m.group(2)) for m in re.finditer(r"^\s*(\d+)\s+val\s+([\d.]+)", text, re.M)
|
|
178
|
+
if int(m.group(1)) > 1}
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def pick(log: Path, adapter: Path) -> int:
|
|
182
|
+
losses = validation_losses(log.read_text(encoding="utf-8", errors="replace"))
|
|
183
|
+
if not losses:
|
|
184
|
+
raise SystemExit("No validation losses in the log.")
|
|
185
|
+
step = min(losses, key=losses.get)
|
|
186
|
+
checkpoint = adapter / f"{step:07d}_adapters.safetensors"
|
|
187
|
+
if not checkpoint.is_file():
|
|
188
|
+
raise SystemExit(f"Checkpoint for step {step} is missing: {checkpoint}")
|
|
189
|
+
chosen = adapter.with_name(adapter.name + f"-best-{step}")
|
|
190
|
+
chosen.mkdir(exist_ok=True)
|
|
191
|
+
shutil.copy(adapter / "adapter_config.json", chosen / "adapter_config.json")
|
|
192
|
+
shutil.copy(checkpoint, chosen / "adapters.safetensors")
|
|
193
|
+
print(json.dumps({"step": step, "validation_loss": losses[step], "adapter": str(chosen)}))
|
|
194
|
+
return 0
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def evaluate(adapter: Optional[Path], model: Optional[str], task: str = "risk") -> dict:
|
|
198
|
+
from mlx_lm import generate, load
|
|
199
|
+
|
|
200
|
+
network, tokenizer = load(base_model(model), adapter_path=str(adapter) if adapter else None)
|
|
201
|
+
rows = P.load(task)
|
|
202
|
+
system = P.RISK_SYSTEM if task == "risk" else P.INTENT_SYSTEM
|
|
203
|
+
predictions = {}
|
|
204
|
+
for row in rows:
|
|
205
|
+
prompt = tokenizer.apply_chat_template(
|
|
206
|
+
[{"role": "system", "content": system}, {"role": "user", "content": row["input"]}],
|
|
207
|
+
add_generation_prompt=True, tokenize=False)
|
|
208
|
+
predictions[row["id"]] = P.parse_prediction(task, generate(network, tokenizer, prompt=prompt, max_tokens=6))
|
|
209
|
+
report = P.score(task, rows, predictions)
|
|
210
|
+
report.pop("confusion", None)
|
|
211
|
+
return {"adapter": str(adapter) if adapter else None, **report}
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def main(argv: Optional[List[str]] = None) -> int:
|
|
215
|
+
parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
|
|
216
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
217
|
+
d = sub.add_parser("data")
|
|
218
|
+
d.add_argument("--out", required=True)
|
|
219
|
+
d.add_argument("--task", choices=("risk", "intent"), default="risk")
|
|
220
|
+
t = sub.add_parser("train")
|
|
221
|
+
t.add_argument("--data", required=True)
|
|
222
|
+
t.add_argument("--adapter", required=True)
|
|
223
|
+
t.add_argument("--model")
|
|
224
|
+
t.add_argument("--iters", type=int, default=1000)
|
|
225
|
+
t.add_argument("--rate", type=float, default=5e-5)
|
|
226
|
+
t.add_argument("--layers", type=int, default=8)
|
|
227
|
+
k = sub.add_parser("pick")
|
|
228
|
+
k.add_argument("--log", required=True)
|
|
229
|
+
k.add_argument("--adapter", required=True)
|
|
230
|
+
e = sub.add_parser("eval")
|
|
231
|
+
e.add_argument("--task", choices=("risk", "intent"), default="risk")
|
|
232
|
+
e.add_argument("--adapter")
|
|
233
|
+
e.add_argument("--model")
|
|
234
|
+
args = parser.parse_args(argv)
|
|
235
|
+
if args.command == "data":
|
|
236
|
+
print(json.dumps(write_data(Path(args.out), args.task)))
|
|
237
|
+
return 0
|
|
238
|
+
if args.command == "train":
|
|
239
|
+
return train(Path(args.data), Path(args.adapter), args.model, args.iters, args.rate, args.layers)
|
|
240
|
+
if args.command == "pick":
|
|
241
|
+
return pick(Path(args.log), Path(args.adapter))
|
|
242
|
+
print(json.dumps(evaluate(Path(args.adapter) if args.adapter else None, args.model, args.task),
|
|
243
|
+
ensure_ascii=False, indent=2))
|
|
244
|
+
return 0
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
if __name__ == "__main__":
|
|
248
|
+
sys.exit(main())
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"""Eval trajectories -> a supervised fine-tuning set, built the way a quant builds a backtest.
|
|
2
|
+
|
|
3
|
+
The rules (docs/training-protocol.md) are enforced here, in code, not by care:
|
|
4
|
+
|
|
5
|
+
- **No look-ahead.** Tasks in the locked holdout suites (``hard`` by default)
|
|
6
|
+
never produce an example, and a trajectory that touched a grader file is
|
|
7
|
+
dropped: a model trained on the answer key measures nothing.
|
|
8
|
+
- **Split by task, never by attempt.** Two attempts at one task are near
|
|
9
|
+
copies; one in train and one in validation is leakage. The split is a
|
|
10
|
+
stable hash of the task id, so it does not move when data is added.
|
|
11
|
+
- **No survivorship of lucky runs.** Only attempts the grader passed *and*
|
|
12
|
+
whose own acceptance check did not fail become examples.
|
|
13
|
+
- **Exposure is reported.** Examples per domain and split, so a set that is
|
|
14
|
+
90% one domain is visible before it trains a lopsided model.
|
|
15
|
+
- **Provenance.** The manifest records each input file's sha256, the rules
|
|
16
|
+
and the counts, so a tuned model can be traced to exactly what it saw.
|
|
17
|
+
|
|
18
|
+
Output is the Vertex AI Gemini supervised-tuning format: one JSON object per
|
|
19
|
+
line with ``systemInstruction`` and ``contents``, tool use as
|
|
20
|
+
``functionCall`` / ``functionResponse`` parts.
|
|
21
|
+
|
|
22
|
+
python -m aria_code.training.sft_dataset trajectories/ --suites evals/suites --out sft/
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import argparse
|
|
28
|
+
import fnmatch
|
|
29
|
+
import hashlib
|
|
30
|
+
import json
|
|
31
|
+
from collections import Counter, defaultdict
|
|
32
|
+
from dataclasses import dataclass, field
|
|
33
|
+
from pathlib import Path
|
|
34
|
+
from typing import Iterable
|
|
35
|
+
|
|
36
|
+
DEFAULT_HOLDOUT = ("hard",)
|
|
37
|
+
DOMAINS = ("software", "finance", "payments", "logistics")
|
|
38
|
+
RESULT_CHARS = 6_000 # one tool result in an example; long outputs are cut, not dropped
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class Trajectory:
|
|
43
|
+
path: Path
|
|
44
|
+
suite: str
|
|
45
|
+
task_id: str
|
|
46
|
+
attempt: int
|
|
47
|
+
model: str
|
|
48
|
+
prompt: str
|
|
49
|
+
events: list[dict] = field(default_factory=list)
|
|
50
|
+
outcome: dict = field(default_factory=dict)
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def final(self) -> dict:
|
|
54
|
+
return next((e for e in reversed(self.events) if e.get("type") == "turn.completed"), {})
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class TaskInfo:
|
|
59
|
+
suite: str
|
|
60
|
+
tags: tuple[str, ...]
|
|
61
|
+
hidden: tuple[str, ...]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def load_trajectories(root: Path) -> list[Trajectory]:
|
|
65
|
+
out = []
|
|
66
|
+
for path in sorted(Path(root).rglob("*.jsonl")):
|
|
67
|
+
lines = [json.loads(l) for l in path.read_text(encoding="utf-8").splitlines() if l.strip().startswith("{")]
|
|
68
|
+
if not lines or lines[0].get("type") != "eval.task":
|
|
69
|
+
continue
|
|
70
|
+
head = lines[0]
|
|
71
|
+
outcome: dict = next((l for l in reversed(lines) if l.get("type") == "eval.outcome"), {})
|
|
72
|
+
events = [l for l in lines[1:] if l.get("type") not in ("eval.task", "eval.outcome")]
|
|
73
|
+
out.append(Trajectory(path=path, suite=path.parent.name, task_id=str(head.get("task_id")),
|
|
74
|
+
attempt=int(head.get("attempt") or 1), model=str(head.get("model") or ""),
|
|
75
|
+
prompt=str(head.get("prompt") or ""), events=events, outcome=outcome))
|
|
76
|
+
return out
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def load_tasks(suites_dir: Path) -> dict[str, TaskInfo]:
|
|
80
|
+
import yaml
|
|
81
|
+
|
|
82
|
+
tasks = {}
|
|
83
|
+
for suite_file in sorted(Path(suites_dir).glob("*.yaml")):
|
|
84
|
+
doc = yaml.safe_load(suite_file.read_text(encoding="utf-8")) or {}
|
|
85
|
+
for t in doc.get("tasks") or []:
|
|
86
|
+
tasks[str(t["id"])] = TaskInfo(suite=str(doc.get("suite") or suite_file.stem),
|
|
87
|
+
tags=tuple(t.get("tags") or ()),
|
|
88
|
+
hidden=tuple(t.get("hidden") or ()))
|
|
89
|
+
return tasks
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def split_of(task_id: str, validation_share: float = 0.2) -> str:
|
|
93
|
+
""""train" or "validation", fixed by the task id alone."""
|
|
94
|
+
bucket = int(hashlib.sha256(task_id.encode()).hexdigest()[:8], 16) / 0xFFFFFFFF
|
|
95
|
+
return "validation" if bucket < validation_share else "train"
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def domain_of(tags: Iterable[str]) -> str:
|
|
99
|
+
tags = set(tags)
|
|
100
|
+
for d in DOMAINS:
|
|
101
|
+
if d in tags:
|
|
102
|
+
return "finance" if d == "payments" else d
|
|
103
|
+
return "other"
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def why_excluded(t: Trajectory, info: TaskInfo | None, holdout: tuple[str, ...]) -> str | None:
|
|
107
|
+
if info is None:
|
|
108
|
+
return "task not in any suite"
|
|
109
|
+
if info.suite in holdout or t.suite in holdout:
|
|
110
|
+
return "locked holdout"
|
|
111
|
+
if t.outcome.get("outcome") != "pass":
|
|
112
|
+
return f"outcome {t.outcome.get('outcome') or 'missing'}"
|
|
113
|
+
final = t.final
|
|
114
|
+
if not final:
|
|
115
|
+
return "no turn.completed"
|
|
116
|
+
acceptance = final.get("acceptance") or {}
|
|
117
|
+
if isinstance(acceptance, dict) and acceptance.get("verified") is False:
|
|
118
|
+
return "its own checks failed"
|
|
119
|
+
text = json.dumps(t.events, ensure_ascii=False)
|
|
120
|
+
graders = set(info.hidden) | {"test_*_hidden.py"}
|
|
121
|
+
for event in t.events:
|
|
122
|
+
for value in (event.get("params") or {}).values():
|
|
123
|
+
name = Path(str(value)).name
|
|
124
|
+
if any(fnmatch.fnmatch(name, g) for g in graders):
|
|
125
|
+
return "touched a grader"
|
|
126
|
+
if "eval.outcome" in text or "reward.json" in text:
|
|
127
|
+
return "grader output in the trajectory"
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def to_example(t: Trajectory, system: str) -> dict:
|
|
132
|
+
"""One Vertex Gemini SFT record from one trajectory."""
|
|
133
|
+
contents: list[dict] = [{"role": "user", "parts": [{"text": t.prompt}]}]
|
|
134
|
+
pending: list[dict] = []
|
|
135
|
+
for event in t.events:
|
|
136
|
+
kind = event.get("type")
|
|
137
|
+
if kind == "tool.started":
|
|
138
|
+
call = {"functionCall": {"name": event.get("tool"), "args": event.get("params") or {}}}
|
|
139
|
+
if contents[-1]["role"] == "model" and not pending:
|
|
140
|
+
contents[-1]["parts"].append(call)
|
|
141
|
+
else:
|
|
142
|
+
contents.append({"role": "model", "parts": [call]})
|
|
143
|
+
pending.append(event)
|
|
144
|
+
elif kind == "tool.completed" and pending:
|
|
145
|
+
pending.pop(0)
|
|
146
|
+
result = event.get("result")
|
|
147
|
+
if result is None:
|
|
148
|
+
result = {"success": event.get("success"), "error": event.get("error", "")}
|
|
149
|
+
body = json.dumps(result, ensure_ascii=False)
|
|
150
|
+
if len(body) > RESULT_CHARS:
|
|
151
|
+
result = {"truncated": True, "head": body[:RESULT_CHARS]}
|
|
152
|
+
part = {"functionResponse": {"name": event.get("tool"), "response": result}}
|
|
153
|
+
if contents[-1]["role"] == "user" and "functionResponse" in contents[-1]["parts"][0]:
|
|
154
|
+
contents[-1]["parts"].append(part)
|
|
155
|
+
else:
|
|
156
|
+
contents.append({"role": "user", "parts": [part]})
|
|
157
|
+
final = str(t.final.get("response") or "").strip()
|
|
158
|
+
if final:
|
|
159
|
+
contents.append({"role": "model", "parts": [{"text": final}]})
|
|
160
|
+
return {"systemInstruction": {"role": "system", "parts": [{"text": system}]}, "contents": contents}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def build(trajectories: Path, suites: Path, out: Path, *, holdout=DEFAULT_HOLDOUT,
|
|
164
|
+
validation_share: float = 0.2, system: str = "") -> dict:
|
|
165
|
+
tasks = load_tasks(suites)
|
|
166
|
+
trajs = load_trajectories(trajectories)
|
|
167
|
+
out.mkdir(parents=True, exist_ok=True)
|
|
168
|
+
system = system or ("You are Aria, an AI agent for coding, financial analysis and logistics "
|
|
169
|
+
"operations. Read the workspace first, compute every number with code from "
|
|
170
|
+
"named data, keep each client's data apart, and check your work before you finish.")
|
|
171
|
+
split_files = {s: (out / f"{s}.jsonl").open("w", encoding="utf-8") for s in ("train", "validation")}
|
|
172
|
+
excluded: Counter = Counter()
|
|
173
|
+
exposure: dict[str, Counter] = defaultdict(Counter)
|
|
174
|
+
seen_tasks: dict[str, set[str]] = defaultdict(set)
|
|
175
|
+
fingerprints: set[str] = set()
|
|
176
|
+
try:
|
|
177
|
+
for t in trajs:
|
|
178
|
+
info = tasks.get(t.task_id)
|
|
179
|
+
reason = why_excluded(t, info, tuple(holdout))
|
|
180
|
+
if reason:
|
|
181
|
+
excluded[reason] += 1
|
|
182
|
+
continue
|
|
183
|
+
example = to_example(t, system)
|
|
184
|
+
fp = hashlib.sha256(json.dumps(example["contents"], sort_keys=True).encode()).hexdigest()
|
|
185
|
+
if fp in fingerprints:
|
|
186
|
+
excluded["duplicate"] += 1
|
|
187
|
+
continue
|
|
188
|
+
fingerprints.add(fp)
|
|
189
|
+
split = split_of(t.task_id, validation_share)
|
|
190
|
+
seen_tasks[split].add(t.task_id)
|
|
191
|
+
exposure[split][domain_of(info.tags)] += 1
|
|
192
|
+
split_files[split].write(json.dumps(example, ensure_ascii=False) + "\n")
|
|
193
|
+
finally:
|
|
194
|
+
for fh in split_files.values():
|
|
195
|
+
fh.close()
|
|
196
|
+
|
|
197
|
+
overlap = seen_tasks["train"] & seen_tasks["validation"]
|
|
198
|
+
assert not overlap, f"task in two splits: {sorted(overlap)}"
|
|
199
|
+
holdout_tasks = sorted(tid for tid, info in tasks.items() if info.suite in holdout)
|
|
200
|
+
manifest = {
|
|
201
|
+
"rules": {"holdout_suites": list(holdout), "split": "sha256(task_id)", "validation_share": validation_share,
|
|
202
|
+
"eligible": "grader PASS and acceptance not failed; no grader file touched"},
|
|
203
|
+
"inputs": {str(t.path): hashlib.sha256(t.path.read_bytes()).hexdigest() for t in trajs},
|
|
204
|
+
"models": sorted({t.model for t in trajs}),
|
|
205
|
+
"examples": {s: sum(c.values()) for s, c in exposure.items()},
|
|
206
|
+
"tasks": {s: sorted(v) for s, v in seen_tasks.items()},
|
|
207
|
+
"exposure_by_domain": {s: dict(c) for s, c in exposure.items()},
|
|
208
|
+
"excluded": dict(excluded),
|
|
209
|
+
"holdout_tasks": holdout_tasks,
|
|
210
|
+
}
|
|
211
|
+
(out / "manifest.json").write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
212
|
+
return manifest
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def main(argv: list[str] | None = None) -> int:
|
|
216
|
+
ap = argparse.ArgumentParser(description="Build a Vertex Gemini SFT set from eval trajectories.")
|
|
217
|
+
ap.add_argument("trajectories", type=Path)
|
|
218
|
+
ap.add_argument("--suites", type=Path, default=Path("evals/suites"))
|
|
219
|
+
ap.add_argument("--out", type=Path, required=True)
|
|
220
|
+
ap.add_argument("--holdout", nargs="*", default=list(DEFAULT_HOLDOUT))
|
|
221
|
+
ap.add_argument("--validation-share", type=float, default=0.2)
|
|
222
|
+
args = ap.parse_args(argv)
|
|
223
|
+
m = build(args.trajectories, args.suites, args.out, holdout=tuple(args.holdout),
|
|
224
|
+
validation_share=args.validation_share)
|
|
225
|
+
print(json.dumps({k: m[k] for k in ("examples", "exposure_by_domain", "excluded")}, ensure_ascii=False, indent=2))
|
|
226
|
+
return 0
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
if __name__ == "__main__":
|
|
230
|
+
raise SystemExit(main())
|