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.
Files changed (601) hide show
  1. aria_bots/__init__.py +7 -0
  2. aria_bots/channels/README.md +97 -0
  3. aria_bots/channels/__init__.py +1 -0
  4. aria_bots/channels/approvals.py +263 -0
  5. aria_bots/channels/conversation.py +280 -0
  6. aria_bots/channels/digest.py +186 -0
  7. aria_bots/channels/registry.py +95 -0
  8. aria_bots/engine.py +144 -0
  9. aria_bots/feishu_bot.py +1385 -0
  10. aria_bots/logistics/__init__.py +1 -0
  11. aria_bots/logistics/carriers.py +253 -0
  12. aria_bots/logistics/freight.py +154 -0
  13. aria_bots/logistics/inventory.py +242 -0
  14. aria_bots/logistics/mcp_server.py +156 -0
  15. aria_bots/logistics/records.py +119 -0
  16. aria_bots/logistics/tenancy.py +152 -0
  17. aria_bots/market.py +91 -0
  18. aria_bots/relay_client.py +326 -0
  19. aria_bots/telegram_bot.py +299 -0
  20. aria_code/__init__.py +111 -0
  21. aria_code/_bin/aria-native.exe +0 -0
  22. aria_code/_bin/aria-runtime.exe +0 -0
  23. aria_code/_version.py +20 -0
  24. aria_code/account_memory.py +296 -0
  25. aria_code/account_session.py +166 -0
  26. aria_code/adk_apps/__init__.py +1 -0
  27. aria_code/adk_apps/aria_code_review/__init__.py +1 -0
  28. aria_code/adk_apps/aria_code_review/agent.py +32 -0
  29. aria_code/adk_apps/aria_research/__init__.py +1 -0
  30. aria_code/adk_apps/aria_research/agent.py +31 -0
  31. aria_code/agents/__init__.py +49 -0
  32. aria_code/agents/base.py +329 -0
  33. aria_code/agents/code_review.py +200 -0
  34. aria_code/agents/deep/__init__.py +37 -0
  35. aria_code/agents/deep/calibration_loop.py +144 -0
  36. aria_code/agents/deep/critic.py +125 -0
  37. aria_code/agents/deep/deepen.py +193 -0
  38. aria_code/agents/deep/models.py +149 -0
  39. aria_code/agents/deep/pipeline.py +164 -0
  40. aria_code/agents/deep/quant_fusion.py +192 -0
  41. aria_code/agents/deep/themes.py +95 -0
  42. aria_code/agents/deep/tiers.py +106 -0
  43. aria_code/agents/engineering/__init__.py +14 -0
  44. aria_code/agents/engineering/coder.py +222 -0
  45. aria_code/agents/engineering/tester.py +145 -0
  46. aria_code/agents/financial/__init__.py +21 -0
  47. aria_code/agents/financial/cashflow_burn.py +119 -0
  48. aria_code/agents/financial/catalyst.py +279 -0
  49. aria_code/agents/financial/corporate_finance.py +175 -0
  50. aria_code/agents/financial/debate.py +224 -0
  51. aria_code/agents/financial/earnings.py +315 -0
  52. aria_code/agents/financial/fundamental.py +178 -0
  53. aria_code/agents/financial/macro.py +111 -0
  54. aria_code/agents/financial/news.py +231 -0
  55. aria_code/agents/financial/risk.py +146 -0
  56. aria_code/agents/financial/sector.py +279 -0
  57. aria_code/agents/financial/strategist.py +121 -0
  58. aria_code/agents/financial/stripe_revenue.py +127 -0
  59. aria_code/agents/financial/synthesis.py +274 -0
  60. aria_code/agents/financial/technical.py +277 -0
  61. aria_code/agents/orchestrator.py +44 -0
  62. aria_code/agents/portfolio_agent.py +392 -0
  63. aria_code/agents/realty/__init__.py +62 -0
  64. aria_code/agents/realty/asset_diagnosis.py +152 -0
  65. aria_code/agents/realty/business_match.py +166 -0
  66. aria_code/agents/realty/cashflow_verify.py +209 -0
  67. aria_code/agents/realty/contract_rules.py +210 -0
  68. aria_code/agents/realty/energy_anomaly.py +189 -0
  69. aria_code/agents/realty/exit_settlement.py +208 -0
  70. aria_code/agents/realty/fulfillment_risk.py +206 -0
  71. aria_code/agents/realty/ops_optimize.py +160 -0
  72. aria_code/agents/realty/revenue_share.py +215 -0
  73. aria_code/agents/registry.py +168 -0
  74. aria_code/agents/signal_scheme.py +111 -0
  75. aria_code/agents/sports/__init__.py +0 -0
  76. aria_code/agents/sports/football_agent.py +197 -0
  77. aria_code/agents/supervisor.py +113 -0
  78. aria_code/agents/team.py +500 -0
  79. aria_code/agents/video/__init__.py +16 -0
  80. aria_code/agents/video/director.py +376 -0
  81. aria_code/agents/video/qa_agent.py +143 -0
  82. aria_code/agents/video/script_doctor.py +112 -0
  83. aria_code/agents/warehouse/__init__.py +25 -0
  84. aria_code/agents/warehouse/contracts.py +26 -0
  85. aria_code/agents/warehouse/dashboard.py +155 -0
  86. aria_code/agents/warehouse/fulfillment_leadtime.py +95 -0
  87. aria_code/agents/warehouse/inbound_exceptions.py +25 -0
  88. aria_code/agents/warehouse/inventory_health.py +24 -0
  89. aria_code/agents/warehouse/local_run.py +71 -0
  90. aria_code/agents/warehouse/local_snapshot.py +132 -0
  91. aria_code/agents/warehouse/logistics_cost.py +84 -0
  92. aria_code/agents/warehouse/logistics_sync.py +21 -0
  93. aria_code/agents/warehouse/navigation.schema.json +110 -0
  94. aria_code/agents/warehouse/task_agent.py +172 -0
  95. aria_code/agents/warehouse/workflow.py +31 -0
  96. aria_code/aliyun_data_client.py +4 -0
  97. aria_code/apps/README.md +12 -0
  98. aria_code/apps/__init__.py +2 -0
  99. aria_code/apps/channels/README.md +9 -0
  100. aria_code/apps/channels/__init__.py +17 -0
  101. aria_code/apps/channels/approvals.py +11 -0
  102. aria_code/apps/channels/conversation.py +11 -0
  103. aria_code/apps/channels/digest.py +11 -0
  104. aria_code/apps/channels/intake.py +92 -0
  105. aria_code/apps/channels/registry.py +11 -0
  106. aria_code/apps/channels/tradingview.py +94 -0
  107. aria_code/apps/cli/README.md +13 -0
  108. aria_code/apps/cli/__init__.py +2 -0
  109. aria_code/apps/cli/_optional.py +120 -0
  110. aria_code/apps/cli/app_server.py +656 -0
  111. aria_code/apps/cli/auto_update.py +125 -0
  112. aria_code/apps/cli/bootstrap.py +206 -0
  113. aria_code/apps/cli/broker_render.py +206 -0
  114. aria_code/apps/cli/chat_turn.py +1327 -0
  115. aria_code/apps/cli/cloud_models.py +231 -0
  116. aria_code/apps/cli/codegen_paths.py +29 -0
  117. aria_code/apps/cli/commands/__init__.py +34 -0
  118. aria_code/apps/cli/commands/_business_render.py +78 -0
  119. aria_code/apps/cli/commands/_ui.py +71 -0
  120. aria_code/apps/cli/commands/analysis_cmds.py +378 -0
  121. aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
  122. aria_code/apps/cli/commands/auth_cmds.py +215 -0
  123. aria_code/apps/cli/commands/backtest_cmds.py +2518 -0
  124. aria_code/apps/cli/commands/broker_cmds.py +1236 -0
  125. aria_code/apps/cli/commands/business_workflow_cmds.py +379 -0
  126. aria_code/apps/cli/commands/canvas_cmds.py +77 -0
  127. aria_code/apps/cli/commands/catalog.py +189 -0
  128. aria_code/apps/cli/commands/core_cmds.py +2748 -0
  129. aria_code/apps/cli/commands/data_cmds.py +521 -0
  130. aria_code/apps/cli/commands/diagnostic_cmds.py +285 -0
  131. aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
  132. aria_code/apps/cli/commands/engineering_cmds.py +399 -0
  133. aria_code/apps/cli/commands/file_cmds.py +312 -0
  134. aria_code/apps/cli/commands/finance_service_cmds.py +60 -0
  135. aria_code/apps/cli/commands/fx_commodity_cmds.py +123 -0
  136. aria_code/apps/cli/commands/market.py +544 -0
  137. aria_code/apps/cli/commands/market_cmds.py +1419 -0
  138. aria_code/apps/cli/commands/market_context.py +432 -0
  139. aria_code/apps/cli/commands/market_render.py +7 -0
  140. aria_code/apps/cli/commands/model_cmds.py +2374 -0
  141. aria_code/apps/cli/commands/ops_cmds.py +721 -0
  142. aria_code/apps/cli/commands/orchestrator_cmds.py +181 -0
  143. aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
  144. aria_code/apps/cli/commands/portfolio_cmds.py +1316 -0
  145. aria_code/apps/cli/commands/report.py +476 -0
  146. aria_code/apps/cli/commands/scaffold_templates.py +617 -0
  147. aria_code/apps/cli/commands/session_cmds.py +236 -0
  148. aria_code/apps/cli/commands/session_ux_cmds.py +359 -0
  149. aria_code/apps/cli/commands/team.py +723 -0
  150. aria_code/apps/cli/commands/team_render.py +8 -0
  151. aria_code/apps/cli/commands/ui_cmds.py +401 -0
  152. aria_code/apps/cli/commands/warehouse_cmds.py +297 -0
  153. aria_code/apps/cli/commands/workflow_cmds.py +629 -0
  154. aria_code/apps/cli/commands/workspace_cmds.py +1543 -0
  155. aria_code/apps/cli/config_paths.py +73 -0
  156. aria_code/apps/cli/config_store.py +158 -0
  157. aria_code/apps/cli/constants.py +88 -0
  158. aria_code/apps/cli/context.py +34 -0
  159. aria_code/apps/cli/conversation_policy.py +53 -0
  160. aria_code/apps/cli/deterministic.py +200 -0
  161. aria_code/apps/cli/direct.py +48 -0
  162. aria_code/apps/cli/earnings_view.py +172 -0
  163. aria_code/apps/cli/endpoint_migration.py +64 -0
  164. aria_code/apps/cli/exec_events.py +255 -0
  165. aria_code/apps/cli/file_references.py +155 -0
  166. aria_code/apps/cli/finance_service_catalog.py +111 -0
  167. aria_code/apps/cli/football_reports.py +231 -0
  168. aria_code/apps/cli/github_app_auth.py +135 -0
  169. aria_code/apps/cli/google_login.py +239 -0
  170. aria_code/apps/cli/handlers/__init__.py +11 -0
  171. aria_code/apps/cli/handlers/broker_handlers.py +122 -0
  172. aria_code/apps/cli/handlers/chart_handlers.py +1314 -0
  173. aria_code/apps/cli/handlers/market_handlers.py +2570 -0
  174. aria_code/apps/cli/handlers/realty_handlers.py +114 -0
  175. aria_code/apps/cli/handlers/sports_handlers.py +22 -0
  176. aria_code/apps/cli/handlers/strategy_advice.py +82 -0
  177. aria_code/apps/cli/headless.py +352 -0
  178. aria_code/apps/cli/helpers.py +326 -0
  179. aria_code/apps/cli/hooks.py +180 -0
  180. aria_code/apps/cli/i18n.py +310 -0
  181. aria_code/apps/cli/intent.py +136 -0
  182. aria_code/apps/cli/intent_router.py +305 -0
  183. aria_code/apps/cli/intent_shadow.py +72 -0
  184. aria_code/apps/cli/intent_signals.py +347 -0
  185. aria_code/apps/cli/lifecycle_hooks.py +48 -0
  186. aria_code/apps/cli/local_tool_schemas.py +550 -0
  187. aria_code/apps/cli/main.py +39 -0
  188. aria_code/apps/cli/market_hours.py +100 -0
  189. aria_code/apps/cli/market_metadata.py +135 -0
  190. aria_code/apps/cli/market_universe.py +487 -0
  191. aria_code/apps/cli/message_processing.py +310 -0
  192. aria_code/apps/cli/mixin_binding.py +32 -0
  193. aria_code/apps/cli/model_catalog.py +652 -0
  194. aria_code/apps/cli/model_probe.py +169 -0
  195. aria_code/apps/cli/model_retirement.py +65 -0
  196. aria_code/apps/cli/native_branding.py +12 -0
  197. aria_code/apps/cli/native_bridge.py +118 -0
  198. aria_code/apps/cli/native_entry.py +58 -0
  199. aria_code/apps/cli/pdf_report.py +977 -0
  200. aria_code/apps/cli/plan_mode.py +139 -0
  201. aria_code/apps/cli/plotly_html.py +15 -0
  202. aria_code/apps/cli/prediction_feedback.py +202 -0
  203. aria_code/apps/cli/preflight.py +502 -0
  204. aria_code/apps/cli/project_aria.py +60 -0
  205. aria_code/apps/cli/prompt_assembly.py +78 -0
  206. aria_code/apps/cli/prompts/__init__.py +0 -0
  207. aria_code/apps/cli/prompts/coding.py +71 -0
  208. aria_code/apps/cli/prompts/select.py +193 -0
  209. aria_code/apps/cli/prompts/system_prompts.py +562 -0
  210. aria_code/apps/cli/prompts/ui.py +436 -0
  211. aria_code/apps/cli/provider_endpoints.py +237 -0
  212. aria_code/apps/cli/provider_keys.py +73 -0
  213. aria_code/apps/cli/providers/__init__.py +1 -0
  214. aria_code/apps/cli/providers/base.py +775 -0
  215. aria_code/apps/cli/providers/chat_routing.py +139 -0
  216. aria_code/apps/cli/providers/collaboration.py +98 -0
  217. aria_code/apps/cli/providers/llm/__init__.py +1 -0
  218. aria_code/apps/cli/providers/llm/offline_responses.py +68 -0
  219. aria_code/apps/cli/providers/llm/ollama_stream.py +1574 -0
  220. aria_code/apps/cli/providers/llm/response_cache.py +60 -0
  221. aria_code/apps/cli/providers/llm/sse_stream.py +298 -0
  222. aria_code/apps/cli/providers/runtime_bridge.py +586 -0
  223. aria_code/apps/cli/providers/vertexai_stream.py +592 -0
  224. aria_code/apps/cli/release_download.py +159 -0
  225. aria_code/apps/cli/review_runner.py +111 -0
  226. aria_code/apps/cli/runtime_consumer.py +769 -0
  227. aria_code/apps/cli/sandbox.py +32 -0
  228. aria_code/apps/cli/session_export.py +120 -0
  229. aria_code/apps/cli/session_jsonl.py +208 -0
  230. aria_code/apps/cli/session_refresh.py +123 -0
  231. aria_code/apps/cli/session_store.py +125 -0
  232. aria_code/apps/cli/skills_catalog.py +273 -0
  233. aria_code/apps/cli/task_isolation.py +211 -0
  234. aria_code/apps/cli/terminal_chrome.py +412 -0
  235. aria_code/apps/cli/todo_tracker.py +190 -0
  236. aria_code/apps/cli/tool_executor.py +932 -0
  237. aria_code/apps/cli/tool_registry.py +34 -0
  238. aria_code/apps/cli/tool_scope.py +113 -0
  239. aria_code/apps/cli/tools/__init__.py +40 -0
  240. aria_code/apps/cli/tools/context.py +46 -0
  241. aria_code/apps/cli/tools/file_tools.py +181 -0
  242. aria_code/apps/cli/tools/market_tools.py +626 -0
  243. aria_code/apps/cli/tools/notebook_tools.py +111 -0
  244. aria_code/apps/cli/tools/system_tools.py +656 -0
  245. aria_code/apps/cli/tools/write_tools.py +961 -0
  246. aria_code/apps/cli/tradingview_bridge.py +529 -0
  247. aria_code/apps/cli/transactions.py +269 -0
  248. aria_code/apps/cli/turn_planning.py +137 -0
  249. aria_code/apps/cli/update_check.py +257 -0
  250. aria_code/apps/cli/updater.py +373 -0
  251. aria_code/apps/cli/utils/__init__.py +0 -0
  252. aria_code/apps/cli/utils/market_detect.py +1663 -0
  253. aria_code/apps/cli/web_login.py +136 -0
  254. aria_code/apps/cli/workflow_runner.py +127 -0
  255. aria_code/apps/cli/workspace_route.py +121 -0
  256. aria_code/apps/daemon/README.md +14 -0
  257. aria_code/apps/vscode/README.md +115 -0
  258. aria_code/apps/vscode/package.json +70 -0
  259. aria_code/aria_cli.py +5244 -0
  260. aria_code/aria_daemon.py +1372 -0
  261. aria_code/aria_feishu_bot.py +16 -0
  262. aria_code/aria_mcp_server.py +61 -0
  263. aria_code/aria_relay_client.py +4 -0
  264. aria_code/aria_telegram_bot.py +11 -0
  265. aria_code/ariarc.py +385 -0
  266. aria_code/artifacts.py +581 -0
  267. aria_code/backtest_engine.py +4 -0
  268. aria_code/backtest_report.py +4 -0
  269. aria_code/brokers/__init__.py +78 -0
  270. aria_code/brokers/automation.py +193 -0
  271. aria_code/brokers/base.py +231 -0
  272. aria_code/brokers/capabilities.py +264 -0
  273. aria_code/brokers/cn/__init__.py +10 -0
  274. aria_code/brokers/cn/easytrader_broker.py +193 -0
  275. aria_code/brokers/cn/futu_broker.py +194 -0
  276. aria_code/brokers/cn/longbridge_broker.py +190 -0
  277. aria_code/brokers/cn/tiger_broker.py +196 -0
  278. aria_code/brokers/cn/xtquant_broker.py +225 -0
  279. aria_code/brokers/config.py +414 -0
  280. aria_code/brokers/intl/__init__.py +5 -0
  281. aria_code/brokers/intl/alpaca_broker.py +183 -0
  282. aria_code/brokers/intl/ibkr_broker.py +215 -0
  283. aria_code/brokers/intl/trading212_broker.py +183 -0
  284. aria_code/brokers/intl/webull_broker.py +156 -0
  285. aria_code/brokers/paper_broker.py +266 -0
  286. aria_code/brokers/planning.py +296 -0
  287. aria_code/brokers/registry.py +213 -0
  288. aria_code/brokers/trading.py +267 -0
  289. aria_code/canva_client.py +4 -0
  290. aria_code/change_store.py +127 -0
  291. aria_code/clients/__init__.py +0 -0
  292. aria_code/clients/aliyun_data_client.py +660 -0
  293. aria_code/clients/aria_relay_client.py +16 -0
  294. aria_code/clients/canva_client.py +284 -0
  295. aria_code/clients/figma_client.py +100 -0
  296. aria_code/clients/football_data_client.py +1671 -0
  297. aria_code/clients/kling_video_client.py +182 -0
  298. aria_code/clients/market_data_client.py +1985 -0
  299. aria_code/clients/openai_image_client.py +242 -0
  300. aria_code/clients/runway_video_client.py +157 -0
  301. aria_code/clients/warehouse_erp_client.py +105 -0
  302. aria_code/cloud_memory.py +190 -0
  303. aria_code/command_safety.py +19 -0
  304. aria_code/computer_use_server.py +276 -0
  305. aria_code/computer_use_tools.py +4 -0
  306. aria_code/dashboard_generator.py +579 -0
  307. aria_code/data_analysis_tools.py +4 -0
  308. aria_code/data_cleaner.py +483 -0
  309. aria_code/data_service.py +513 -0
  310. aria_code/datasources/__init__.py +23 -0
  311. aria_code/datasources/base.py +166 -0
  312. aria_code/datasources/router.py +253 -0
  313. aria_code/datasources/sec_statements.py +217 -0
  314. aria_code/datasources/sources/__init__.py +15 -0
  315. aria_code/datasources/sources/akshare_source.py +269 -0
  316. aria_code/datasources/sources/alpha_vantage_source.py +203 -0
  317. aria_code/datasources/sources/edgar_source.py +219 -0
  318. aria_code/datasources/sources/finnhub_source.py +198 -0
  319. aria_code/datasources/sources/fred_source.py +223 -0
  320. aria_code/datasources/sources/tushare_source.py +143 -0
  321. aria_code/datasources/sources/web_scraper_source.py +278 -0
  322. aria_code/datasources/sources/world_bank_source.py +206 -0
  323. aria_code/datasources/sources/yfinance_source.py +152 -0
  324. aria_code/doctor.py +860 -0
  325. aria_code/domain/__init__.py +0 -0
  326. aria_code/domain/_finance_core/__init__.py +5 -0
  327. aria_code/domain/_finance_core/backtest_engine.py +325 -0
  328. aria_code/domain/_finance_core/performance_stats.py +101 -0
  329. aria_code/domain/backtest_engine.py +75 -0
  330. aria_code/domain/backtest_report.py +488 -0
  331. aria_code/domain/finance_formulas.py +389 -0
  332. aria_code/domain/performance_stats.py +6 -0
  333. aria_code/domain/portfolio_ledger.py +307 -0
  334. aria_code/domain/strategy_vault.py +421 -0
  335. aria_code/evals/__init__.py +37 -0
  336. aria_code/evals/behavior.py +113 -0
  337. aria_code/evals/gap.py +93 -0
  338. aria_code/evals/harness.py +949 -0
  339. aria_code/evals/review_probe.py +132 -0
  340. aria_code/evals/runner.py +425 -0
  341. aria_code/evals/scorecard.py +322 -0
  342. aria_code/evals/summary.py +125 -0
  343. aria_code/external_agent_runner.py +115 -0
  344. aria_code/figma_client.py +4 -0
  345. aria_code/file_analysis_tools.py +4 -0
  346. aria_code/finance_formulas.py +4 -0
  347. aria_code/football_data_client.py +4 -0
  348. aria_code/image_gen_tools.py +4 -0
  349. aria_code/intent_classifier.py +459 -0
  350. aria_code/kling_video_client.py +4 -0
  351. aria_code/licensing.py +151 -0
  352. aria_code/local_finance_tools.py +4 -0
  353. aria_code/local_image_provider.py +4 -0
  354. aria_code/local_llm_provider.py +4 -0
  355. aria_code/macro_tools.py +4 -0
  356. aria_code/markdown_pdf.py +289 -0
  357. aria_code/market_data_client.py +4 -0
  358. aria_code/mcp_client.py +744 -0
  359. aria_code/memory_manager.py +273 -0
  360. aria_code/model_capability.py +512 -0
  361. aria_code/notification_tools.py +4 -0
  362. aria_code/openai_image_client.py +4 -0
  363. aria_code/packages/__init__.py +26 -0
  364. aria_code/packages/adk_bridge/__init__.py +11 -0
  365. aria_code/packages/adk_bridge/code_review_tools.py +49 -0
  366. aria_code/packages/adk_bridge/market_tools.py +125 -0
  367. aria_code/packages/aria_agents/__init__.py +5 -0
  368. aria_code/packages/aria_agents/manifest.py +78 -0
  369. aria_code/packages/aria_core/__init__.py +34 -0
  370. aria_code/packages/aria_core/architecture.py +269 -0
  371. aria_code/packages/aria_core/export.py +147 -0
  372. aria_code/packages/aria_core/manifest.py +65 -0
  373. aria_code/packages/aria_core/paths.py +57 -0
  374. aria_code/packages/aria_core/secure_file.py +68 -0
  375. aria_code/packages/aria_infra/__init__.py +15 -0
  376. aria_code/packages/aria_infra/arthera.py +58 -0
  377. aria_code/packages/aria_infra/doctor.py +246 -0
  378. aria_code/packages/aria_infra/product.py +40 -0
  379. aria_code/packages/aria_mcp/__init__.py +25 -0
  380. aria_code/packages/aria_mcp/bridge.py +232 -0
  381. aria_code/packages/aria_mcp/circuit.py +82 -0
  382. aria_code/packages/aria_mcp/config.py +162 -0
  383. aria_code/packages/aria_mcp/installer.py +95 -0
  384. aria_code/packages/aria_mcp/server.py +1705 -0
  385. aria_code/packages/aria_mcp/tools.py +81 -0
  386. aria_code/packages/aria_sdk/__init__.py +19 -0
  387. aria_code/packages/aria_sdk/client.py +393 -0
  388. aria_code/packages/aria_sdk/providers.py +70 -0
  389. aria_code/packages/aria_sdk/streaming.py +75 -0
  390. aria_code/packages/aria_sdk/types.py +86 -0
  391. aria_code/packages/aria_services/__init__.py +92 -0
  392. aria_code/packages/aria_services/cache.py +60 -0
  393. aria_code/packages/aria_services/context.py +356 -0
  394. aria_code/packages/aria_services/context_checkpoints.py +157 -0
  395. aria_code/packages/aria_services/data.py +11 -0
  396. aria_code/packages/aria_services/provider_health.py +304 -0
  397. aria_code/packages/aria_services/references.py +247 -0
  398. aria_code/packages/aria_services/registry.py +249 -0
  399. aria_code/packages/aria_services/research_protocol.py +137 -0
  400. aria_code/packages/aria_services/research_quality.py +186 -0
  401. aria_code/packages/aria_services/settings.py +160 -0
  402. aria_code/packages/aria_services/usage.py +151 -0
  403. aria_code/packages/aria_skills/__init__.py +46 -0
  404. aria_code/packages/aria_skills/catalog.py +149 -0
  405. aria_code/packages/aria_skills/loader.py +455 -0
  406. aria_code/packages/aria_skills/registry.py +84 -0
  407. aria_code/packages/aria_tools/__init__.py +5 -0
  408. aria_code/packages/aria_tools/financial/__init__.py +5 -0
  409. aria_code/packages/aria_tools/financial/_paths.py +92 -0
  410. aria_code/packages/aria_tools/registry.py +129 -0
  411. aria_code/packages/quant_engine/__init__.py +33 -0
  412. aria_code/packages/quant_engine/_namespace.py +24 -0
  413. aria_code/packages/quant_engine/backtest/__init__.py +10 -0
  414. aria_code/packages/quant_engine/backtest/core.py +53 -0
  415. aria_code/packages/quant_engine/backtest/engine.py +102 -0
  416. aria_code/packages/quant_engine/portfolio/__init__.py +9 -0
  417. aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
  418. aria_code/packages/quant_engine/sports/__init__.py +76 -0
  419. aria_code/packages/quant_engine/sports/calibrator.py +354 -0
  420. aria_code/packages/quant_engine/sports/dixon_coles.py +234 -0
  421. aria_code/packages/quant_engine/sports/elo.py +300 -0
  422. aria_code/packages/quant_engine/sports/form.py +188 -0
  423. aria_code/packages/quant_engine/sports/h2h.py +195 -0
  424. aria_code/packages/quant_engine/sports/predictor.py +312 -0
  425. aria_code/packages/quant_engine/sports/tracker.py +665 -0
  426. aria_code/packages/quant_engine/stochastic/__init__.py +31 -0
  427. aria_code/packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
  428. aria_code/packages/quant_engine/stochastic/ito_calculus.py +477 -0
  429. aria_code/packages/quant_engine/stochastic/kelly_criterion.py +181 -0
  430. aria_code/packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
  431. aria_code/packages/quant_engine/stochastic/options_pricing.py +576 -0
  432. aria_code/packages/quant_engine/stochastic/stochastic_processes.py +90 -0
  433. aria_code/packs/__init__.py +85 -0
  434. aria_code/packs/base.py +168 -0
  435. aria_code/packs/finance/__init__.py +250 -0
  436. aria_code/packs/healthcare/__init__.py +40 -0
  437. aria_code/packs/logistics/__init__.py +171 -0
  438. aria_code/packs/payments/__init__.py +139 -0
  439. aria_code/packs/realty/__init__.py +190 -0
  440. aria_code/packs/registry.py +202 -0
  441. aria_code/packs/rules.py +90 -0
  442. aria_code/plan_utils.py +194 -0
  443. aria_code/plugin_loader.py +328 -0
  444. aria_code/portfolio_ledger.py +4 -0
  445. aria_code/preview_server.py +449 -0
  446. aria_code/privacy/__init__.py +5 -0
  447. aria_code/privacy/feedback.py +123 -0
  448. aria_code/project_review.py +278 -0
  449. aria_code/project_review_api.py +235 -0
  450. aria_code/project_review_server.py +18 -0
  451. aria_code/project_tools.py +4 -0
  452. aria_code/providers/__init__.py +30 -0
  453. aria_code/providers/google_video_provider.py +256 -0
  454. aria_code/providers/llm/__init__.py +19 -0
  455. aria_code/providers/llm/anthropic.py +184 -0
  456. aria_code/providers/llm/arthera_gateway.py +90 -0
  457. aria_code/providers/llm/autoconfig.py +213 -0
  458. aria_code/providers/llm/base.py +139 -0
  459. aria_code/providers/llm/custom.py +158 -0
  460. aria_code/providers/llm/ollama.py +137 -0
  461. aria_code/providers/llm/openai_compat.py +350 -0
  462. aria_code/providers/llm/registry.py +457 -0
  463. aria_code/providers/local_image_provider.py +202 -0
  464. aria_code/providers/local_llm_provider.py +549 -0
  465. aria_code/providers/ollama_models.py +79 -0
  466. aria_code/providers/openai_params.py +32 -0
  467. aria_code/providers/tool_messages.py +100 -0
  468. aria_code/realty_data_tools.py +4 -0
  469. aria_code/report_exporters.py +150 -0
  470. aria_code/report_generator.py +1764 -0
  471. aria_code/review_service.py +497 -0
  472. aria_code/runtime/__init__.py +176 -0
  473. aria_code/runtime/acceptance.py +632 -0
  474. aria_code/runtime/agent_loop.py +1851 -0
  475. aria_code/runtime/agent_profiles.py +75 -0
  476. aria_code/runtime/approval.py +71 -0
  477. aria_code/runtime/baseline.py +219 -0
  478. aria_code/runtime/budget.py +232 -0
  479. aria_code/runtime/checkpoints.py +481 -0
  480. aria_code/runtime/consult.py +65 -0
  481. aria_code/runtime/contract.py +282 -0
  482. aria_code/runtime/delivery.py +432 -0
  483. aria_code/runtime/events.py +113 -0
  484. aria_code/runtime/gateway.py +167 -0
  485. aria_code/runtime/lsp.py +484 -0
  486. aria_code/runtime/native_index.py +153 -0
  487. aria_code/runtime/orchestrator.py +55 -0
  488. aria_code/runtime/processes.py +343 -0
  489. aria_code/runtime/project_graph.py +614 -0
  490. aria_code/runtime/repo_map.py +712 -0
  491. aria_code/runtime/research_bundle.py +105 -0
  492. aria_code/runtime/review.py +269 -0
  493. aria_code/runtime/run_state.py +127 -0
  494. aria_code/runtime/run_store.py +522 -0
  495. aria_code/runtime/scoped_runs.py +113 -0
  496. aria_code/runtime/self_healing.py +422 -0
  497. aria_code/runtime/semantic_diff.py +171 -0
  498. aria_code/runtime/subagent.py +721 -0
  499. aria_code/runtime/symbol_edit.py +117 -0
  500. aria_code/runtime/task_brief.py +46 -0
  501. aria_code/runtime/task_context.py +145 -0
  502. aria_code/runtime/task_ledger.py +108 -0
  503. aria_code/runtime/task_orchestrator.py +90 -0
  504. aria_code/runtime/task_outcome.py +33 -0
  505. aria_code/runtime/task_verification.py +266 -0
  506. aria_code/runtime/task_worktree.py +448 -0
  507. aria_code/runtime/tool_executor.py +329 -0
  508. aria_code/runtime/tool_policy.py +107 -0
  509. aria_code/runtime/transactions.py +216 -0
  510. aria_code/runtime/windows_job.py +115 -0
  511. aria_code/runtime/workflows.py +222 -0
  512. aria_code/runtime/worktrees.py +215 -0
  513. aria_code/runway_video_client.py +4 -0
  514. aria_code/safety/__init__.py +27 -0
  515. aria_code/safety/client_isolation.py +178 -0
  516. aria_code/safety/permissions.py +339 -0
  517. aria_code/safety/risk.py +699 -0
  518. aria_code/safety/sandbox.py +148 -0
  519. aria_code/safety/service.py +102 -0
  520. aria_code/service_endpoints.py +14 -0
  521. aria_code/setup_wizard.py +684 -0
  522. aria_code/spreadsheet_tools.py +4 -0
  523. aria_code/strategy_vault.py +4 -0
  524. aria_code/tools/__init__.py +0 -0
  525. aria_code/tools/artifact_tools.py +252 -0
  526. aria_code/tools/broker_tools.py +92 -0
  527. aria_code/tools/build_quant_engine.py +97 -0
  528. aria_code/tools/code_audit_tools.py +192 -0
  529. aria_code/tools/computer_use_tools.py +509 -0
  530. aria_code/tools/data_analysis_tools.py +831 -0
  531. aria_code/tools/enterprise_finance_tools.py +202 -0
  532. aria_code/tools/extended_tools.py +240 -0
  533. aria_code/tools/file_analysis_tools.py +880 -0
  534. aria_code/tools/image_gen_tools.py +114 -0
  535. aria_code/tools/local_finance_tools.py +3488 -0
  536. aria_code/tools/logistics_carriers.py +11 -0
  537. aria_code/tools/logistics_inventory.py +11 -0
  538. aria_code/tools/logistics_io.py +11 -0
  539. aria_code/tools/logistics_tenancy.py +11 -0
  540. aria_code/tools/logistics_tools.py +11 -0
  541. aria_code/tools/macro_tools.py +412 -0
  542. aria_code/tools/notification_tools.py +249 -0
  543. aria_code/tools/project_tools.py +525 -0
  544. aria_code/tools/realty_data_tools.py +662 -0
  545. aria_code/tools/spreadsheet_tools.py +399 -0
  546. aria_code/tools/stripe_tools.py +115 -0
  547. aria_code/tools/video_tools.py +500 -0
  548. aria_code/training/__init__.py +1 -0
  549. aria_code/training/compare.py +117 -0
  550. aria_code/training/prelude_commands.py +111 -0
  551. aria_code/training/prelude_eval.py +390 -0
  552. aria_code/training/prelude_real.py +183 -0
  553. aria_code/training/prelude_train.py +248 -0
  554. aria_code/training/sft_dataset.py +230 -0
  555. aria_code/training/trajectory_dataset.py +263 -0
  556. aria_code/ui/__init__.py +105 -0
  557. aria_code/ui/banner.py +404 -0
  558. aria_code/ui/completer.py +571 -0
  559. aria_code/ui/console.py +285 -0
  560. aria_code/ui/input_box.py +505 -0
  561. aria_code/ui/mentions.py +206 -0
  562. aria_code/ui/picker.py +248 -0
  563. aria_code/ui/render/__init__.py +11 -0
  564. aria_code/ui/render/actions.py +297 -0
  565. aria_code/ui/render/finance.py +1732 -0
  566. aria_code/ui/render/market.py +253 -0
  567. aria_code/ui/render/output.py +954 -0
  568. aria_code/ui/render/responsive.py +75 -0
  569. aria_code/ui/render/team.py +367 -0
  570. aria_code/ui/robot.py +239 -0
  571. aria_code/ui/startup_dashboard.py +110 -0
  572. aria_code/video_analysis.py +147 -0
  573. aria_code/video_editor.py +518 -0
  574. aria_code/video_workflow/__init__.py +8 -0
  575. aria_code/video_workflow/__main__.py +61 -0
  576. aria_code/video_workflow/engines/__init__.py +18 -0
  577. aria_code/video_workflow/engines/base.py +69 -0
  578. aria_code/video_workflow/engines/custom_model.py +204 -0
  579. aria_code/video_workflow/engines/google_veo.py +94 -0
  580. aria_code/video_workflow/engines/local_engine.py +71 -0
  581. aria_code/video_workflow/engines/registry.py +98 -0
  582. aria_code/video_workflow/google_models.py +278 -0
  583. aria_code/video_workflow/music/__init__.py +5 -0
  584. aria_code/video_workflow/music/foley_service.py +132 -0
  585. aria_code/video_workflow/music/service.py +212 -0
  586. aria_code/video_workflow/pipeline.py +620 -0
  587. aria_code/video_workflow/plan.py +202 -0
  588. aria_code/video_workflow/session.py +297 -0
  589. aria_code/workspace/__init__.py +6 -0
  590. aria_code/workspace/files.py +219 -0
  591. aria_code/workspace/verify.py +167 -0
  592. aria_code/workspace_changes.py +199 -0
  593. aria_code/workspace_service.py +196 -0
  594. aria_code/workspace_tools.py +150 -0
  595. aria_code-0.167.0.dist-info/METADATA +330 -0
  596. aria_code-0.167.0.dist-info/RECORD +601 -0
  597. aria_code-0.167.0.dist-info/WHEEL +5 -0
  598. aria_code-0.167.0.dist-info/entry_points.txt +5 -0
  599. aria_code-0.167.0.dist-info/licenses/LICENSE +201 -0
  600. aria_code-0.167.0.dist-info/licenses/NOTICE +49 -0
  601. aria_code-0.167.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1 @@
1
+ """Logistics tools: freight, carrier scoring and inventory policy, scoped per shipper."""
@@ -0,0 +1,253 @@
1
+ """Carrier scorecards, freight anomalies and savings for a 3PL, by lane.
2
+
3
+ A raw on-time percentage ranks a carrier with 1 of 1 deliveries on time above
4
+ one with 98 of 100, which is backwards. Carriers here are ranked by the lower
5
+ bound of the Wilson score interval for their on-time rate, which is what the
6
+ data actually supports: a carrier earns a high rank by being on time *often*,
7
+ not by being on time once.
8
+
9
+ Wilson lower bound = (p̂ + z²/2n − z·√(p̂(1−p̂)/n + z²/4n²)) / (1 + z²/n)
10
+
11
+ Freight anomalies use the modified z-score of cost per kg within a lane —
12
+ median and median absolute deviation, so one outlier cannot hide itself by
13
+ inflating the mean it is compared against (Iglewicz & Hoaglin, |Mᵢ| > 3.5):
14
+
15
+ Mᵢ = 0.6745 · (xᵢ − median) / MAD
16
+ Mᵢ = (xᵢ − median) / (1.253314 · MeanAD) when MAD = 0
17
+
18
+ The second form matters: on a contracted lane most shipments share one rate,
19
+ which makes MAD zero, and those are the lanes where an overcharge is plainest.
20
+
21
+ A savings opportunity is reported only when, on the same lane, an alternative
22
+ carrier is both cheaper per kg *and* at least as reliable by the same lower
23
+ bound, with enough shipments on both sides to say so. The estimate is volume
24
+ × rate difference and nothing more; contract terms, surcharges, minimums and
25
+ capacity are not modelled and the result says so.
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import math
31
+ import statistics
32
+ from collections import defaultdict
33
+ from statistics import NormalDist
34
+ from typing import Any, Dict, List
35
+
36
+ from .records import flag, load_records, number, text
37
+ from .tenancy import TenancyError, owner_params_schema, scope_records
38
+
39
+ DEFAULTS = {"confidence": 0.95, "min_shipments": 20}
40
+ ANOMALY_THRESHOLD = 3.5
41
+ OVERBILLED_WEIGHT_RATIO = 1.2
42
+ MIN_LANE_ROWS_FOR_OUTLIERS = 5
43
+
44
+
45
+ def wilson_lower_bound(successes: int, trials: int, confidence: float = 0.95) -> float | None:
46
+ if trials <= 0:
47
+ return None
48
+ z = NormalDist().inv_cdf(1 - (1 - confidence) / 2)
49
+ p = successes / trials
50
+ denominator = 1 + z * z / trials
51
+ centre = p + z * z / (2 * trials)
52
+ margin = z * math.sqrt(p * (1 - p) / trials + z * z / (4 * trials * trials))
53
+ return (centre - margin) / denominator
54
+
55
+
56
+ def _count(n: int, one: str, many: str) -> str:
57
+ return f"{n} {one if n == 1 else many}"
58
+
59
+
60
+ def _lane(record: Dict[str, Any], row: int) -> str:
61
+ origin = text(record, "origin", row)
62
+ destination = text(record, "destination", row)
63
+ return f"{origin}→{destination}" if origin and destination else "(all lanes)"
64
+
65
+
66
+ def tool_score_carriers(params: Dict[str, Any]) -> Dict[str, Any]:
67
+ try:
68
+ records, source = load_records(params, "waybills")
69
+ records, scope = scope_records(records, params.get("owner_id"),
70
+ all_owners=bool(params.get("all_owners")))
71
+ confidence = float(params.get("confidence", DEFAULTS["confidence"]))
72
+ if not 0.5 <= confidence < 1:
73
+ raise ValueError("confidence must be at least 0.5 and below 1")
74
+ min_shipments = int(params.get("min_shipments", DEFAULTS["min_shipments"]))
75
+ if min_shipments < 1:
76
+ raise ValueError("min_shipments must be at least 1")
77
+
78
+ cells: Dict[tuple[str, str], Dict[str, Any]] = defaultdict(lambda: {
79
+ "shipments": 0, "total_cost": 0.0, "total_kg": 0.0, "cost_per_kg": [],
80
+ "on_time_known": 0, "on_time": 0, "exception_known": 0, "exceptions": 0,
81
+ })
82
+ lane_rates: Dict[str, List[tuple[float, Dict[str, Any], str]]] = defaultdict(list)
83
+ anomalies: List[Dict[str, Any]] = []
84
+
85
+ for row, record in enumerate(records, 1):
86
+ carrier = text(record, "carrier", row, required=True)
87
+ cost = number(record, "total_cost", row, required=True)
88
+ billed = number(record, "billed_weight_kg", row)
89
+ actual = number(record, "actual_weight_kg", row)
90
+ on_time = flag(record, "is_on_time", row)
91
+ exception = flag(record, "has_exception", row)
92
+ lane = _lane(record, row)
93
+ waybill = text(record, "waybill_no", row) or f"row {row}"
94
+
95
+ cell = cells[(lane, carrier)]
96
+ cell["shipments"] += 1
97
+ cell["total_cost"] += cost
98
+ weight = billed if billed else actual
99
+ if weight:
100
+ cell["total_kg"] += weight
101
+ rate = cost / weight
102
+ cell["cost_per_kg"].append(rate)
103
+ lane_rates[lane].append((rate, {"waybill_no": waybill, "carrier": carrier}, carrier))
104
+ if on_time is not None:
105
+ cell["on_time_known"] += 1
106
+ cell["on_time"] += int(on_time)
107
+ if exception is not None:
108
+ cell["exception_known"] += 1
109
+ cell["exceptions"] += int(exception)
110
+ if actual and billed and billed > actual * OVERBILLED_WEIGHT_RATIO:
111
+ anomalies.append({
112
+ "waybill_no": waybill, "carrier": carrier, "lane": lane,
113
+ "kind": "billed_weight",
114
+ "detail": f"billed {billed:g} kg vs actual {actual:g} kg "
115
+ f"(> {OVERBILLED_WEIGHT_RATIO - 1:.0%} over); check dimensional weight before disputing",
116
+ })
117
+
118
+ # Cost-per-kg outliers within each lane, expensive side only.
119
+ for lane, rows in lane_rates.items():
120
+ if len(rows) < MIN_LANE_ROWS_FOR_OUTLIERS:
121
+ continue
122
+ rates = [r for r, _, _ in rows]
123
+ median = statistics.median(rates)
124
+ deviations = [abs(r - median) for r in rates]
125
+ mad = statistics.median(deviations)
126
+ # When more than half the shipments share one rate — the normal
127
+ # case on a contracted lane — MAD is 0 and the modified z-score
128
+ # is undefined. Skipping the lane there would let a 6x overcharge
129
+ # through on exactly the lanes where one is easiest to see. Fall
130
+ # back to the mean absolute deviation, scaled so the threshold
131
+ # means the same thing (1.253314 · MeanAD estimates σ).
132
+ if mad > 0:
133
+ scale = mad / 0.6745
134
+ else:
135
+ mean_ad = statistics.fmean(deviations)
136
+ if mean_ad == 0:
137
+ continue # every shipment on the lane has the same rate
138
+ scale = 1.253314 * mean_ad
139
+ for rate, meta, carrier in rows:
140
+ score = (rate - median) / scale
141
+ if score > ANOMALY_THRESHOLD:
142
+ anomalies.append({
143
+ **meta, "lane": lane, "kind": "cost_per_kg",
144
+ "detail": f"{rate:.2f}/kg vs lane median {median:.2f}/kg (modified z {score:.1f})",
145
+ })
146
+
147
+ scorecard: List[Dict[str, Any]] = []
148
+ for (lane, carrier), cell in cells.items():
149
+ lower = wilson_lower_bound(cell["on_time"], cell["on_time_known"], confidence)
150
+ scorecard.append({
151
+ "lane": lane,
152
+ "carrier": carrier,
153
+ "shipments": cell["shipments"],
154
+ "on_time_rate": round(cell["on_time"] / cell["on_time_known"], 4) if cell["on_time_known"] else None,
155
+ "on_time_lower_bound": round(lower, 4) if lower is not None else None,
156
+ "on_time_sample": cell["on_time_known"],
157
+ "median_cost_per_kg": round(statistics.median(cell["cost_per_kg"]), 4) if cell["cost_per_kg"] else None,
158
+ "total_cost": round(cell["total_cost"], 2),
159
+ "total_kg": round(cell["total_kg"], 2),
160
+ "exception_rate": round(cell["exceptions"] / cell["exception_known"], 4) if cell["exception_known"] else None,
161
+ "rankable": cell["shipments"] >= min_shipments and lower is not None,
162
+ })
163
+
164
+ # Rank within each lane: reliability first, then price.
165
+ for lane in {c["lane"] for c in scorecard}:
166
+ ranked = sorted(
167
+ (c for c in scorecard if c["lane"] == lane and c["rankable"]),
168
+ key=lambda c: (-c["on_time_lower_bound"],
169
+ c["median_cost_per_kg"] if c["median_cost_per_kg"] is not None else math.inf,
170
+ c["carrier"]),
171
+ )
172
+ for position, entry in enumerate(ranked, 1):
173
+ entry["rank_in_lane"] = position
174
+ for entry in scorecard:
175
+ entry.setdefault("rank_in_lane", None)
176
+
177
+ savings: List[Dict[str, Any]] = []
178
+ for lane in {c["lane"] for c in scorecard}:
179
+ eligible = [c for c in scorecard if c["lane"] == lane and c["rankable"]
180
+ and c["median_cost_per_kg"] is not None and c["total_kg"] > 0]
181
+ for current in eligible:
182
+ for alternative in eligible:
183
+ if alternative is current:
184
+ continue
185
+ if (alternative["median_cost_per_kg"] < current["median_cost_per_kg"]
186
+ and alternative["on_time_lower_bound"] >= current["on_time_lower_bound"]):
187
+ estimate = current["total_kg"] * (current["median_cost_per_kg"] - alternative["median_cost_per_kg"])
188
+ savings.append({
189
+ "lane": lane,
190
+ "from_carrier": current["carrier"],
191
+ "to_carrier": alternative["carrier"],
192
+ "volume_kg": current["total_kg"],
193
+ "rate_difference_per_kg": round(current["median_cost_per_kg"] - alternative["median_cost_per_kg"], 4),
194
+ "estimated_saving": round(estimate, 2),
195
+ })
196
+ # Keep the best alternative per (lane, from_carrier).
197
+ best: Dict[tuple[str, str], Dict[str, Any]] = {}
198
+ for item in savings:
199
+ key = (item["lane"], item["from_carrier"])
200
+ if key not in best or item["estimated_saving"] > best[key]["estimated_saving"]:
201
+ best[key] = item
202
+ savings = sorted(best.values(), key=lambda s: -s["estimated_saving"])
203
+
204
+ scorecard.sort(key=lambda c: (c["lane"], c["rank_in_lane"] or math.inf, c["carrier"]))
205
+ assumptions = [
206
+ f"carriers ranked within each lane by the {confidence:.0%} Wilson lower bound of on-time rate, then median cost/kg",
207
+ f"only carrier-lane pairs with ≥ {min_shipments} shipments are ranked or used for savings",
208
+ "cost per kg uses billed weight where present, otherwise actual weight",
209
+ f"cost/kg anomaly: modified z-score > {ANOMALY_THRESHOLD} within the lane (lanes with ≥ {MIN_LANE_ROWS_FOR_OUTLIERS} weighed shipments); "
210
+ "MAD-based, falling back to 1.253314 × mean absolute deviation when MAD is 0",
211
+ f"weight anomaly: billed weight > {OVERBILLED_WEIGHT_RATIO:g} × actual",
212
+ "savings require the alternative to be cheaper per kg and at least as reliable; "
213
+ "estimate = volume × rate difference, excluding contract terms, surcharges, minimums and capacity",
214
+ ]
215
+ if scope["mode"] == "all_owners":
216
+ assumptions.insert(0, scope["marker"])
217
+
218
+ total_saving = round(sum(s["estimated_saving"] for s in savings), 2)
219
+ summary = (
220
+ f"{len(records)} waybills across {len({c['lane'] for c in scorecard})} lanes and "
221
+ f"{len({c['carrier'] for c in scorecard})} carriers ({source}); {_count(len(anomalies), 'anomaly', 'anomalies')} "
222
+ f"to check; {_count(len(savings), 'savings opportunity', 'savings opportunities')}, "
223
+ f"estimated {total_saving:,.2f} in total."
224
+ )
225
+ return {
226
+ "success": True,
227
+ "data": {"scope": scope, "scorecard": scorecard, "anomalies": anomalies,
228
+ "savings": savings, "estimated_total_saving": total_saving,
229
+ "assumptions": assumptions},
230
+ "source": source,
231
+ "summary": summary,
232
+ }
233
+ except (OSError, ValueError, TypeError, TenancyError) as exc:
234
+ return {"success": False, "error": str(exc)}
235
+
236
+
237
+ SCHEMA = {
238
+ "name": "score_carriers",
239
+ "description": (
240
+ "Carrier scorecard by lane (on-time rate with a confidence lower bound, median cost/kg, "
241
+ "exception rate), freight cost anomalies and like-for-like savings, from supplied "
242
+ "waybills. Refuses to mix shippers' data unless told which one."
243
+ ),
244
+ "parameters": {
245
+ "type": "object",
246
+ "properties": {
247
+ "file_path": {"type": "string", "description": "CSV or JSON file of waybills"},
248
+ "waybills": {"type": "array", "description": "Waybills: carrier, total_cost, origin, destination, billed_weight_kg, is_on_time"},
249
+ "min_shipments": {"type": "integer", "description": "Minimum shipments for a carrier-lane to be ranked, default 20"},
250
+ **owner_params_schema(),
251
+ },
252
+ },
253
+ }
@@ -0,0 +1,154 @@
1
+ """Auditable freight analysis from supplied waybills or an existing local database."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import csv
6
+ import json
7
+ import math
8
+ import pathlib
9
+ import sqlite3
10
+ from collections import defaultdict
11
+ from contextlib import closing
12
+ from typing import Any, Dict, List
13
+
14
+
15
+ def _number(record: dict[str, Any], field: str, row: int, *, required: bool = False) -> float | None:
16
+ value = record.get(field)
17
+ if value is None or value == "":
18
+ if required:
19
+ raise ValueError(f"Row {row}: {field} is required")
20
+ return None
21
+ if isinstance(value, bool):
22
+ raise ValueError(f"Row {row}: {field} must be a number")
23
+ try:
24
+ number = float(value)
25
+ except (TypeError, ValueError) as exc:
26
+ raise ValueError(f"Row {row}: {field} must be a number") from exc
27
+ if not math.isfinite(number) or number < 0:
28
+ raise ValueError(f"Row {row}: {field} must be finite and non-negative")
29
+ return number
30
+
31
+
32
+ def _on_time(value: Any, row: int) -> bool | None:
33
+ if value is None or value == "":
34
+ return None
35
+ if isinstance(value, bool):
36
+ return value
37
+ if value in (0, 1):
38
+ return bool(value)
39
+ if isinstance(value, str):
40
+ normalized = value.strip().lower()
41
+ if normalized in {"true", "yes", "1"}:
42
+ return True
43
+ if normalized in {"false", "no", "0"}:
44
+ return False
45
+ raise ValueError(f"Row {row}: is_on_time must be a boolean")
46
+
47
+
48
+ def tool_analyze_logistics_data(params: Dict[str, Any]) -> Dict[str, Any]:
49
+ """Analyze actual records. Empty input never produces representative data."""
50
+ try:
51
+ file_path = params.get("file_path")
52
+ records = params.get("waybills")
53
+ if file_path and records is not None:
54
+ raise ValueError("Provide either file_path or waybills, not both")
55
+ if file_path:
56
+ path = pathlib.Path(file_path).expanduser().resolve()
57
+ if path.suffix.lower() == ".json":
58
+ content = json.loads(path.read_text(encoding="utf-8"))
59
+ records = content if isinstance(content, list) else content.get("waybills") if isinstance(content, dict) else None
60
+ elif path.suffix.lower() == ".csv":
61
+ with path.open(encoding="utf-8-sig", newline="") as handle:
62
+ records = list(csv.DictReader(handle))
63
+ else:
64
+ raise ValueError("Only CSV and JSON waybill files are supported")
65
+ source = str(path)
66
+ data_source = f"file:{path.name}"
67
+ elif records is None:
68
+ path = pathlib.Path.home() / ".aria" / "erp_warehouse.db"
69
+ if not path.is_file():
70
+ raise ValueError("No waybills supplied and no local logistics database found")
71
+ with closing(sqlite3.connect(f"{path.as_uri()}?mode=ro", uri=True)) as connection:
72
+ connection.row_factory = sqlite3.Row
73
+ records = [dict(row) for row in connection.execute("SELECT * FROM logistics_waybills")]
74
+ source = str(path)
75
+ data_source = "local logistics database"
76
+ else:
77
+ source = "provided_waybills"
78
+ data_source = "caller-supplied records"
79
+
80
+ if not isinstance(records, list) or not records:
81
+ raise ValueError("No waybill records available for analysis")
82
+
83
+ # Before this, every shipper's waybills were summed into one spend
84
+ # figure and one carrier table — a 3PL report built from all its
85
+ # clients' commercial data. See logistics_tenancy for the rules.
86
+ from .tenancy import scope_records
87
+ records, scope = scope_records(records, params.get("owner_id"),
88
+ all_owners=bool(params.get("all_owners")))
89
+
90
+ spend = 0.0
91
+ known_delivery = 0
92
+ on_time = 0
93
+ carriers: dict[str, dict[str, Any]] = defaultdict(lambda: {"count": 0, "total_spend": 0.0, "known_delivery_count": 0, "on_time_count": 0})
94
+ anomalies: list[dict[str, Any]] = []
95
+ for row, record in enumerate(records, 1):
96
+ if not isinstance(record, dict):
97
+ raise ValueError(f"Row {row}: waybill must be an object")
98
+ cost = _number(record, "total_cost", row, required=True)
99
+ assert cost is not None
100
+ actual = _number(record, "actual_weight_kg", row)
101
+ billed = _number(record, "billed_weight_kg", row)
102
+ timely = _on_time(record.get("is_on_time"), row)
103
+ carrier = str(record.get("carrier") or "Unknown")
104
+ spend += cost
105
+ carriers[carrier]["count"] += 1
106
+ carriers[carrier]["total_spend"] += cost
107
+ if timely is not None:
108
+ known_delivery += 1
109
+ on_time += int(timely)
110
+ carriers[carrier]["known_delivery_count"] += 1
111
+ carriers[carrier]["on_time_count"] += int(timely)
112
+ if actual is not None and billed is not None and actual > 0 and billed > actual * 1.2:
113
+ anomalies.append({"waybill_no": record.get("waybill_no"), "carrier": carrier, "actual_weight_kg": actual, "billed_weight_kg": billed, "reason": "Billed weight exceeds actual weight by more than 20%; verify dimensional weight before disputing"})
114
+
115
+ metrics = []
116
+ for carrier, item in sorted(carriers.items()):
117
+ item["total_spend"] = round(item["total_spend"], 2)
118
+ item["on_time_rate"] = round(item["on_time_count"] / item["known_delivery_count"] * 100, 2) if item["known_delivery_count"] else None
119
+ metrics.append({"carrier": carrier, **item})
120
+ rate = round(on_time / known_delivery * 100, 2) if known_delivery else None
121
+ result = {"scope": scope, "total_waybills": len(records), "total_freight_spend": round(spend, 2), "overall_on_time_rate": rate, "known_delivery_count": known_delivery, "carrier_metrics": metrics, "billing_anomalies": anomalies, "data_source": data_source}
122
+ rate_label = f"{rate}%" if rate is not None else "不可得(无准时状态记录)"
123
+ return {"success": True, "data": result, "source": source, "summary": f"已审计 {len(records)} 单运单(来源:{data_source}),运费总计 ¥{spend:,.2f},已知准时交付率 {rate_label},发现 {len(anomalies)} 笔需核实的计费重量异常。"}
124
+ except (OSError, sqlite3.Error, ValueError, TypeError, json.JSONDecodeError) as exc:
125
+ # TenancyError is a ValueError, so a refusal to mix shippers lands here.
126
+ return {"success": False, "error": str(exc)}
127
+
128
+
129
+ def register_logistics_tools(tools_dict: Dict[str, Any], schemas_list: List[Dict[str, Any]]) -> int:
130
+ """Register the logistics analyses as local tools."""
131
+ from .tenancy import owner_params_schema
132
+
133
+ tools_dict["analyze_logistics_data"] = (tool_analyze_logistics_data, "Analyze supplied freight waybills and billing anomalies")
134
+ schemas_list.append({
135
+ "name": "analyze_logistics_data",
136
+ "description": "Analyze actual waybills from supplied records, a CSV/JSON file, or an existing local database",
137
+ "parameters": {
138
+ "type": "object",
139
+ "properties": {
140
+ "file_path": {"type": "string", "description": "CSV or JSON waybill file path"},
141
+ "waybills": {"type": "array", "description": "Waybill records with total_cost"},
142
+ **owner_params_schema(),
143
+ },
144
+ },
145
+ })
146
+
147
+ from .carriers import SCHEMA as _carrier_schema, tool_score_carriers
148
+ from .inventory import SCHEMA as _inventory_schema, tool_plan_inventory_policy
149
+
150
+ tools_dict["plan_inventory_policy"] = (tool_plan_inventory_policy, _inventory_schema["description"])
151
+ schemas_list.append(_inventory_schema)
152
+ tools_dict["score_carriers"] = (tool_score_carriers, _carrier_schema["description"])
153
+ schemas_list.append(_carrier_schema)
154
+ return 3
@@ -0,0 +1,242 @@
1
+ """Inventory policy for a 3PL: when to reorder, how much, and what is stuck.
2
+
3
+ Everything here is a standard, published formula, applied to the records the
4
+ caller supplies, with the formula and every threshold written into the result
5
+ so a client can recompute any number by hand:
6
+
7
+ z = Φ⁻¹(service level)
8
+ safety stock = z · √(L·σ_d² + d̄²·σ_L²) demand and lead-time variability
9
+ reorder point= d̄·L + safety stock
10
+ order-up-to = reorder point + d̄·R R = review period
11
+ order qty = order-up-to − inventory position, when position ≤ reorder point
12
+ position = on hand + on order − backordered
13
+
14
+ ABC ranks a shipper's SKUs by annual consumption value (Pareto 80/95), XYZ by
15
+ the coefficient of variation of daily demand (0.5 / 1.0). Movement status
16
+ comes from days since last movement (30 slow, 90 dead).
17
+
18
+ What it will not do is guess. A SKU with less demand history than
19
+ `min_history_days` gets no safety stock and no reorder point — it is reported
20
+ as insufficient history, because a policy computed from a handful of days
21
+ would be confidently wrong, and a reorder quantity is money.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import math
27
+ import statistics
28
+ from collections import defaultdict
29
+ from statistics import NormalDist
30
+ from typing import Any, Dict, List
31
+
32
+ from .records import load_records, number, series, text
33
+ from .tenancy import OWNER_FIELD, TenancyError, owner_params_schema, scope_records
34
+
35
+ DEFAULTS = {
36
+ "service_level": 0.95,
37
+ "review_period_days": 7.0,
38
+ "min_history_days": 14,
39
+ "slow_days": 30.0,
40
+ "dead_days": 90.0,
41
+ "excess_cover_days": 180.0,
42
+ }
43
+ ABC_BOUNDS = (0.80, 0.95)
44
+ XYZ_BOUNDS = (0.5, 1.0)
45
+
46
+
47
+ def _setting(params: Dict[str, Any], key: str) -> float:
48
+ value = params.get(key, DEFAULTS[key])
49
+ try:
50
+ value = float(value)
51
+ except (TypeError, ValueError) as exc:
52
+ raise ValueError(f"{key} must be a number") from exc
53
+ if not math.isfinite(value) or value < 0:
54
+ raise ValueError(f"{key} must be finite and non-negative")
55
+ return value
56
+
57
+
58
+ def _demand(record: Dict[str, Any], row: int) -> tuple[float, float, int] | None:
59
+ """(mean, sample std, days of history), from a daily series or from aggregates."""
60
+ history = series(record, "daily_demand", row)
61
+ if history is not None:
62
+ if len(history) < 2:
63
+ return (history[0] if history else 0.0), 0.0, len(history)
64
+ return statistics.fmean(history), statistics.stdev(history), len(history)
65
+ mean = number(record, "demand_mean", row)
66
+ if mean is None:
67
+ return None
68
+ std = number(record, "demand_std", row) or 0.0
69
+ days = number(record, "demand_days", row)
70
+ return mean, std, int(days) if days is not None else 0
71
+
72
+
73
+ def _abc(items: List[Dict[str, Any]]) -> None:
74
+ """Assign ABC within each shipper's portfolio, by annual consumption value."""
75
+ by_owner: Dict[str, List[Dict[str, Any]]] = defaultdict(list)
76
+ for item in items:
77
+ if item["annual_value"] is not None:
78
+ by_owner[item.get(OWNER_FIELD) or ""].append(item)
79
+ for group in by_owner.values():
80
+ total = sum(i["annual_value"] for i in group)
81
+ if total <= 0:
82
+ continue
83
+ cumulative = 0.0
84
+ for item in sorted(group, key=lambda i: (-i["annual_value"], i["sku"])):
85
+ share_before = cumulative / total
86
+ item["abc"] = "A" if share_before < ABC_BOUNDS[0] else "B" if share_before < ABC_BOUNDS[1] else "C"
87
+ cumulative += item["annual_value"]
88
+
89
+
90
+ def tool_plan_inventory_policy(params: Dict[str, Any]) -> Dict[str, Any]:
91
+ try:
92
+ records, source = load_records(params, "skus")
93
+ records, scope = scope_records(records, params.get("owner_id"),
94
+ all_owners=bool(params.get("all_owners")))
95
+
96
+ service_level = _setting(params, "service_level")
97
+ if not 0.5 <= service_level < 1:
98
+ raise ValueError("service_level must be at least 0.5 and below 1")
99
+ review = _setting(params, "review_period_days")
100
+ min_history = int(_setting(params, "min_history_days"))
101
+ slow_days = _setting(params, "slow_days")
102
+ dead_days = _setting(params, "dead_days")
103
+ excess_days = _setting(params, "excess_cover_days")
104
+ z = NormalDist().inv_cdf(service_level)
105
+
106
+ items: List[Dict[str, Any]] = []
107
+ for row, record in enumerate(records, 1):
108
+ sku = text(record, "sku", row, required=True)
109
+ on_hand = number(record, "on_hand", row, required=True)
110
+ on_order = number(record, "on_order", row) or 0.0
111
+ backorder = number(record, "backorder", row) or 0.0
112
+ lead = number(record, "lead_time_days", row, required=True, positive=True)
113
+ lead_std = number(record, "lead_time_std_days", row) or 0.0
114
+ unit_cost = number(record, "unit_cost", row)
115
+ idle_days = number(record, "days_since_last_movement", row)
116
+ demand = _demand(record, row)
117
+ if demand is None:
118
+ raise ValueError(f"Row {row}: daily_demand (or demand_mean) is required")
119
+ mean, std, history_days = demand
120
+ position = on_hand + on_order - backorder
121
+
122
+ item: Dict[str, Any] = {
123
+ OWNER_FIELD: text(record, OWNER_FIELD, row) or None,
124
+ "warehouse_id": text(record, "warehouse_id", row) or None,
125
+ "sku": sku,
126
+ "on_hand": on_hand,
127
+ "inventory_position": round(position, 2),
128
+ "avg_daily_demand": round(mean, 4),
129
+ "demand_std": round(std, 4),
130
+ "history_days": history_days,
131
+ "lead_time_days": lead,
132
+ "safety_stock": None,
133
+ "reorder_point": None,
134
+ "order_up_to": None,
135
+ "suggested_order_qty": 0,
136
+ "days_of_cover": round(on_hand / mean, 1) if mean > 0 else None,
137
+ "abc": None,
138
+ "xyz": None,
139
+ "movement": None,
140
+ "flags": [],
141
+ "annual_value": round(mean * 365 * unit_cost, 2) if unit_cost is not None else None,
142
+ }
143
+
144
+ if mean > 0:
145
+ cv = std / mean
146
+ item["xyz"] = "X" if cv <= XYZ_BOUNDS[0] else "Y" if cv <= XYZ_BOUNDS[1] else "Z"
147
+
148
+ if idle_days is not None:
149
+ item["movement"] = ("dead" if idle_days >= dead_days
150
+ else "slow" if idle_days >= slow_days else "active")
151
+
152
+ if mean == 0:
153
+ item["action"] = "no_demand"
154
+ if on_hand > 0:
155
+ item["flags"].append("stock held with no demand in the history")
156
+ elif history_days < min_history:
157
+ item["action"] = "insufficient_history"
158
+ item["flags"].append(
159
+ f"{history_days} days of demand history; at least {min_history} are needed "
160
+ f"for a safety stock that means anything"
161
+ )
162
+ else:
163
+ safety = z * math.sqrt(lead * std ** 2 + mean ** 2 * lead_std ** 2)
164
+ reorder_point = mean * lead + safety
165
+ order_up_to = reorder_point + mean * review
166
+ # Rounded up: a fractional unit cannot be held, and rounding the
167
+ # buffer down would quietly lower the service level.
168
+ item["safety_stock"] = math.ceil(safety)
169
+ item["reorder_point"] = math.ceil(reorder_point)
170
+ item["order_up_to"] = math.ceil(order_up_to)
171
+ if position <= reorder_point:
172
+ item["action"] = "reorder"
173
+ item["suggested_order_qty"] = max(0, math.ceil(order_up_to - position))
174
+ else:
175
+ item["action"] = "ok"
176
+
177
+ if item["days_of_cover"] is not None and item["days_of_cover"] > excess_days:
178
+ item["flags"].append(f"{item['days_of_cover']:g} days of cover exceeds {excess_days:g}")
179
+ items.append(item)
180
+
181
+ _abc(items)
182
+
183
+ order = {"reorder": 0, "insufficient_history": 1, "no_demand": 2, "ok": 3}
184
+ items.sort(key=lambda i: (order[i["action"]],
185
+ i["days_of_cover"] if i["days_of_cover"] is not None else math.inf,
186
+ i["sku"]))
187
+
188
+ counts = defaultdict(int)
189
+ for item in items:
190
+ counts[item["action"]] += 1
191
+ dead = sum(1 for i in items if i["movement"] == "dead")
192
+ slow = sum(1 for i in items if i["movement"] == "slow")
193
+
194
+ assumptions = [
195
+ f"service level {service_level:.1%} → z = {z:.3f}",
196
+ "safety stock = z·√(L·σ_d² + d̄²·σ_L²); reorder point = d̄·L + safety stock",
197
+ f"order-up-to = reorder point + d̄·{review:g} (review period, days)",
198
+ "inventory position = on hand + on order − backordered",
199
+ "safety stock, reorder point and order quantity rounded up to whole units",
200
+ f"no policy below {min_history} days of demand history",
201
+ f"ABC by annual consumption value within each shipper (A < {ABC_BOUNDS[0]:.0%}, B < {ABC_BOUNDS[1]:.0%} cumulative)",
202
+ f"XYZ by demand CV (X ≤ {XYZ_BOUNDS[0]}, Y ≤ {XYZ_BOUNDS[1]}, Z above)",
203
+ f"movement: slow ≥ {slow_days:g} days idle, dead ≥ {dead_days:g}",
204
+ "capacity, MOQs, case packs and supplier terms are not modelled",
205
+ ]
206
+ if scope["mode"] == "all_owners":
207
+ assumptions.insert(0, scope["marker"])
208
+
209
+ summary = (
210
+ f"{len(items)} {'SKU' if len(items) == 1 else 'SKUs'} analysed ({source}): {counts['reorder']} to reorder, "
211
+ f"{counts['insufficient_history']} with too little history, {counts['no_demand']} "
212
+ f"with no demand; {dead} dead and {slow} slow-moving."
213
+ )
214
+ return {
215
+ "success": True,
216
+ "data": {"scope": scope, "items": items, "counts": dict(counts),
217
+ "dead_stock": dead, "slow_stock": slow, "assumptions": assumptions},
218
+ "source": source,
219
+ "summary": summary,
220
+ }
221
+ except (OSError, ValueError, TypeError, TenancyError) as exc:
222
+ return {"success": False, "error": str(exc)}
223
+
224
+
225
+ SCHEMA = {
226
+ "name": "plan_inventory_policy",
227
+ "description": (
228
+ "Reorder points, safety stock, order quantities, ABC/XYZ classes and dead/slow stock "
229
+ "for a shipper's SKUs, from supplied demand history and lead times. Formulas are "
230
+ "stated in the result. Refuses to mix shippers' data unless told which one."
231
+ ),
232
+ "parameters": {
233
+ "type": "object",
234
+ "properties": {
235
+ "file_path": {"type": "string", "description": "CSV or JSON file of SKU records"},
236
+ "skus": {"type": "array", "description": "SKU records: sku, on_hand, lead_time_days, daily_demand"},
237
+ "service_level": {"type": "number", "description": "Target cycle service level, default 0.95"},
238
+ "review_period_days": {"type": "number", "description": "Days between reviews, default 7"},
239
+ **owner_params_schema(),
240
+ },
241
+ },
242
+ }