ltcai 11.5.2 → 11.7.0

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 (443) hide show
  1. package/README.md +93 -148
  2. package/bin/ltcai.js +234 -24
  3. package/docs/CHANGELOG.md +119 -0
  4. package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
  5. package/docs/DEVELOPMENT.md +8 -6
  6. package/docs/ENTERPRISE.md +2 -1
  7. package/docs/MULTI_AGENT_RUNTIME.md +12 -5
  8. package/docs/ONBOARDING.md +1 -1
  9. package/docs/OPERATIONS.md +6 -3
  10. package/docs/REALTIME_COLLABORATION.md +1 -1
  11. package/docs/TRUST_MODEL.md +1 -1
  12. package/docs/WHY_LATTICE.md +1 -1
  13. package/docs/WORKFLOW_DESIGNER.md +3 -2
  14. package/docs/kg-schema.md +1 -1
  15. package/docs/v11.6.0_ONE_DOOR_PLAN.md +170 -0
  16. package/lattice_brain/__init__.py +42 -79
  17. package/lattice_brain/graph/__init__.py +9 -24
  18. package/lattice_brain/graph/_kg_common/__init__.py +13 -24
  19. package/lattice_brain/ingestion/__init__.py +19 -58
  20. package/lattice_brain/ingestion/pipeline.py +20 -398
  21. package/lattice_brain/multimodal/__init__.py +7 -18
  22. package/lattice_brain/multimodal/images.py +6 -264
  23. package/lattice_brain/multimodal/video.py +8 -247
  24. package/lattice_brain/runtime/__init__.py +8 -79
  25. package/lattice_brain/runtime/hooks.py +22 -584
  26. package/latticeai/__init__.py +1 -1
  27. package/latticeai/api/agent_worker_seam.py +7 -80
  28. package/latticeai/api/health.py +6 -20
  29. package/latticeai/api/local_files.py +16 -613
  30. package/latticeai/api/models.py +12 -87
  31. package/latticeai/api/search.py +17 -255
  32. package/latticeai/api/tools.py +69 -750
  33. package/latticeai/api/voice_capture.py +8 -70
  34. package/latticeai/api/worker_compute.py +842 -0
  35. package/latticeai/api/worker_seams.py +216 -0
  36. package/latticeai/app_factory.py +23 -232
  37. package/latticeai/cli/entrypoint.py +36 -249
  38. package/latticeai/core/agent_permission.py +16 -91
  39. package/latticeai/core/messages.py +89 -531
  40. package/latticeai/runtime/access_runtime.py +0 -14
  41. package/latticeai/runtime/bootstrap.py +10 -21
  42. package/latticeai/runtime/brain_runtime.py +35 -43
  43. package/latticeai/runtime/build_phases/__init__.py +21 -37
  44. package/latticeai/runtime/build_phases/features.py +99 -389
  45. package/latticeai/runtime/build_phases/foundation.py +82 -392
  46. package/latticeai/runtime/build_phases/web.py +91 -408
  47. package/latticeai/runtime/build_phases/worker_profile.py +244 -0
  48. package/latticeai/runtime/lifespan_runtime.py +7 -16
  49. package/latticeai/runtime/platform_services_runtime.py +1 -15
  50. package/latticeai/runtime/runtime_context.py +13 -130
  51. package/latticeai/runtime/security_runtime.py +10 -103
  52. package/latticeai/services/architecture_readiness.py +82 -43
  53. package/latticeai/services/model_runtime/__init__.py +2 -11
  54. package/latticeai/services/model_runtime/service.py +4 -28
  55. package/latticeai/services/p_reinforce.py +10 -261
  56. package/latticeai/services/product_readiness.py +31 -38
  57. package/latticeai/services/search_service.py +37 -795
  58. package/latticeai/services/tool_dispatch.py +30 -386
  59. package/latticeai/services/voice_capture.py +13 -107
  60. package/latticeai/tools/__init__.py +22 -49
  61. package/latticeai/tools/commands.py +0 -163
  62. package/latticeai/tools/computer.py +0 -39
  63. package/latticeai/tools/documents.py +1 -134
  64. package/latticeai/tools/filesystem.py +1 -247
  65. package/latticeai/tools/knowledge.py +1 -52
  66. package/latticeai/tools/local_files.py +0 -20
  67. package/latticeai/worker_app.py +75 -0
  68. package/package.json +2 -3
  69. package/requirements.txt +0 -5
  70. package/scripts/agent_eval.py +16 -28
  71. package/scripts/brain_quality_eval.py +20 -183
  72. package/scripts/bump_version.py +5 -5
  73. package/scripts/check_current_release_docs.mjs +7 -4
  74. package/scripts/check_openapi_drift.mjs +10 -0
  75. package/scripts/check_server_i18n.mjs +2 -18
  76. package/scripts/compose_openapi.py +377 -0
  77. package/scripts/export_openapi.py +32 -4
  78. package/scripts/gen_messages_catalog_fixture.py +302 -0
  79. package/scripts/gen_openapi_fragments.py +365 -0
  80. package/scripts/gen_redact_fixture.py +196 -0
  81. package/scripts/gen_worker_allowlist_fixture.py +115 -0
  82. package/scripts/generate_agent_parity_fixtures.py +33 -14
  83. package/scripts/openapi_route_families.json +2161 -0
  84. package/scripts/release_screen_claims.json +54 -0
  85. package/scripts/run_integration_tests.mjs +134 -29
  86. package/scripts/run_sidecar_e2e.mjs +104 -11
  87. package/scripts/wheel_smoke.py +32 -22
  88. package/src-tauri/Cargo.lock +375 -9
  89. package/src-tauri/Cargo.toml +1 -1
  90. package/src-tauri/src/backend.rs +151 -45
  91. package/src-tauri/src/main.rs +18 -18
  92. package/src-tauri/src/topology.rs +58 -88
  93. package/src-tauri/tauri.conf.json +1 -2
  94. package/static/app/asset-manifest.json +41 -41
  95. package/static/app/assets/{Act-DcQizkl1.js → Act-BPcVAbOL.js} +1 -1
  96. package/static/app/assets/AdminConsole-Bw1ATQL0.js +1 -0
  97. package/static/app/assets/{Brain-3VCSHFcn.js → Brain-CT92Kos0.js} +2 -2
  98. package/static/app/assets/{BrainHome-Qm8eaztx.js → BrainHome-CFBkt1K_.js} +1 -1
  99. package/static/app/assets/{BrainSignals-DS9BtKOW.js → BrainSignals-ReLWF2H8.js} +1 -1
  100. package/static/app/assets/Capture-BsTokYkk.js +1 -0
  101. package/static/app/assets/{Chronicle-BGvuAchH.js → Chronicle-B6f0T9id.js} +1 -1
  102. package/static/app/assets/{CommandPalette-Bqhm0Urn.js → CommandPalette-CuvjTv1u.js} +1 -1
  103. package/static/app/assets/{Library-BV6NnF0a.js → Library-BGJbG9Hd.js} +1 -1
  104. package/static/app/assets/{LivingBrain-GzenJchP.js → LivingBrain-DGYK_Jsa.js} +1 -1
  105. package/static/app/assets/{ProductFlow-DEP6-vML.js → ProductFlow-DXBC6brE.js} +1 -1
  106. package/static/app/assets/{ReviewCard-CNZ7XjWG.js → ReviewCard-HXRle3qq.js} +2 -2
  107. package/static/app/assets/System-CMHSO9qM.js +1 -0
  108. package/static/app/assets/arrow-left-BfmkskWx.js +1 -0
  109. package/static/app/assets/{bot-B_K1Tdmw.js → bot-Cn8bWRuq.js} +1 -1
  110. package/static/app/assets/{brain-DWyaV1L1.js → brain-CQJberbE.js} +1 -1
  111. package/static/app/assets/{button-aTn4s84A.js → button-Ct9f2_oT.js} +1 -1
  112. package/static/app/assets/circle-check-DruOxB-4.js +1 -0
  113. package/static/app/assets/{circle-pause-xKgeGXkT.js → circle-pause-CmzC_apg.js} +1 -1
  114. package/static/app/assets/{circle-play-DkT6tYPX.js → circle-play-D8mW2aQ7.js} +1 -1
  115. package/static/app/assets/{cpu-85xYObUC.js → cpu-DZcdd0PZ.js} +1 -1
  116. package/static/app/assets/{download-B5Fm7YXo.js → download-bv1KEPGQ.js} +1 -1
  117. package/static/app/assets/{folder-open-kk2Xa52u.js → folder-open-d-Pip5gr.js} +1 -1
  118. package/static/app/assets/{hard-drive-DkA3zBW_.js → hard-drive-D20iavUb.js} +1 -1
  119. package/static/app/assets/index-D9x-kSNy.css +2 -0
  120. package/static/app/assets/{index-BMPdTmlY.js → index-Do83hDzJ.js} +3 -3
  121. package/static/app/assets/{input-B0nRf2jO.js → input-BLXVNmj1.js} +1 -1
  122. package/static/app/assets/{link-2-Dwb4gnTc.js → link-2-BPJOFlAy.js} +1 -1
  123. package/static/app/assets/{permissionCopy-CQDUBrOZ.js → permissionCopy-ChdJd493.js} +1 -1
  124. package/static/app/assets/primitives-Cv5tbZBY.js +1 -0
  125. package/static/app/assets/search-CT9aho2j.js +1 -0
  126. package/static/app/assets/{share-2-BsrxFglO.js → share-2-YNX_NtMU.js} +1 -1
  127. package/static/app/assets/{shield-alert-5BStfp2_.js → shield-alert-DuQ3zrVL.js} +1 -1
  128. package/static/app/assets/{textarea-Cg8IUA-k.js → textarea-DqwLnli4.js} +1 -1
  129. package/static/app/assets/{useFocusTrap-CYKvE46M.js → useFocusTrap-ZVI98jaW.js} +1 -1
  130. package/static/app/assets/{useMutation-CSn9t1op.js → useMutation-CVC4qv_D.js} +1 -1
  131. package/static/app/assets/{useQuery-CY2OI2uy.js → useQuery-C7BeG4HU.js} +1 -1
  132. package/static/app/assets/{utils-Ddol2RWD.js → utils-CiFtIdZq.js} +1 -1
  133. package/static/app/assets/{workspace-BqDwOz_p.js → workspace-DQz9vIId.js} +1 -1
  134. package/static/app/index.html +4 -4
  135. package/static/sw.js +1 -1
  136. package/lattice_brain/archive.py +0 -522
  137. package/lattice_brain/context.py +0 -325
  138. package/lattice_brain/conversations.py +0 -382
  139. package/lattice_brain/core.py +0 -82
  140. package/lattice_brain/graph/_kg_contract.py +0 -249
  141. package/lattice_brain/graph/curator.py +0 -676
  142. package/lattice_brain/graph/discovery.py +0 -595
  143. package/lattice_brain/graph/discovery_index/__init__.py +0 -35
  144. package/lattice_brain/graph/discovery_index/cleanup.py +0 -182
  145. package/lattice_brain/graph/discovery_index/extract.py +0 -137
  146. package/lattice_brain/graph/discovery_index/scan.py +0 -411
  147. package/lattice_brain/graph/discovery_index/upsert.py +0 -495
  148. package/lattice_brain/graph/documents.py +0 -380
  149. package/lattice_brain/graph/fusion.py +0 -395
  150. package/lattice_brain/graph/identity.py +0 -175
  151. package/lattice_brain/graph/image_vectors.py +0 -230
  152. package/lattice_brain/graph/ingest.py +0 -829
  153. package/lattice_brain/graph/network.py +0 -205
  154. package/lattice_brain/graph/proactive.py +0 -724
  155. package/lattice_brain/graph/projection/__init__.py +0 -42
  156. package/lattice_brain/graph/projection/curation.py +0 -500
  157. package/lattice_brain/graph/projection/v2_schema.py +0 -518
  158. package/lattice_brain/graph/provenance.py +0 -524
  159. package/lattice_brain/graph/rerank.py +0 -163
  160. package/lattice_brain/graph/retrieval/__init__.py +0 -54
  161. package/lattice_brain/graph/retrieval/context.py +0 -197
  162. package/lattice_brain/graph/retrieval/graph_view.py +0 -319
  163. package/lattice_brain/graph/retrieval/hybrid.py +0 -488
  164. package/lattice_brain/graph/retrieval/maintenance.py +0 -121
  165. package/lattice_brain/graph/retrieval/signals.py +0 -95
  166. package/lattice_brain/graph/retrieval_docgen.py +0 -253
  167. package/lattice_brain/graph/retrieval_policy.py +0 -180
  168. package/lattice_brain/graph/retrieval_reads.py +0 -769
  169. package/lattice_brain/graph/retrieval_vector/__init__.py +0 -42
  170. package/lattice_brain/graph/retrieval_vector/fingerprint.py +0 -97
  171. package/lattice_brain/graph/retrieval_vector/indexing.py +0 -347
  172. package/lattice_brain/graph/retrieval_vector/search.py +0 -560
  173. package/lattice_brain/graph/retrieval_vector/status.py +0 -374
  174. package/lattice_brain/graph/schema.py +0 -792
  175. package/lattice_brain/graph/store.py +0 -268
  176. package/lattice_brain/graph/vector_index/__init__.py +0 -85
  177. package/lattice_brain/graph/vector_index/base.py +0 -167
  178. package/lattice_brain/graph/vector_index/brute_force.py +0 -110
  179. package/lattice_brain/graph/vector_index/hnsw.py +0 -290
  180. package/lattice_brain/graph/vector_index/jobs.py +0 -287
  181. package/lattice_brain/graph/vector_index/quantized.py +0 -148
  182. package/lattice_brain/graph/vector_index/selector.py +0 -161
  183. package/lattice_brain/graph/write_master.py +0 -308
  184. package/lattice_brain/ingestion/_contract.py +0 -90
  185. package/lattice_brain/ingestion/folder_scan.py +0 -57
  186. package/lattice_brain/ingestion/folders.py +0 -258
  187. package/lattice_brain/ingestion/jobs_api.py +0 -107
  188. package/lattice_brain/ingestion/routing.py +0 -295
  189. package/lattice_brain/ingestion_jobs.py +0 -380
  190. package/lattice_brain/memory.py +0 -75
  191. package/lattice_brain/portability/__init__.py +0 -90
  192. package/lattice_brain/portability/_contract.py +0 -42
  193. package/lattice_brain/portability/backups.py +0 -338
  194. package/lattice_brain/portability/bundles.py +0 -136
  195. package/lattice_brain/portability/constants.py +0 -93
  196. package/lattice_brain/portability/fsops.py +0 -138
  197. package/lattice_brain/portability/service.py +0 -41
  198. package/lattice_brain/portability/sharing.py +0 -710
  199. package/lattice_brain/quality.py +0 -543
  200. package/lattice_brain/retrieval_benchmark_fixtures.py +0 -92
  201. package/lattice_brain/runtime/agent_runtime.py +0 -859
  202. package/lattice_brain/runtime/contracts.py +0 -460
  203. package/lattice_brain/runtime/multi_agent.py +0 -942
  204. package/lattice_brain/runtime/statuses.py +0 -10
  205. package/lattice_brain/sealed_box.py +0 -240
  206. package/lattice_brain/self_model.py +0 -675
  207. package/lattice_brain/sensitivity.py +0 -94
  208. package/lattice_brain/storage/__init__.py +0 -22
  209. package/lattice_brain/storage/base.py +0 -100
  210. package/lattice_brain/storage/docker.py +0 -105
  211. package/lattice_brain/storage/factory.py +0 -31
  212. package/lattice_brain/storage/migration.py +0 -191
  213. package/lattice_brain/storage/postgres.py +0 -123
  214. package/lattice_brain/storage/sqlite.py +0 -143
  215. package/lattice_brain/synthesis.py +0 -824
  216. package/lattice_brain/workflow.py +0 -497
  217. package/latticeai/api/admin.py +0 -471
  218. package/latticeai/api/agent_registry.py +0 -105
  219. package/latticeai/api/agents.py +0 -228
  220. package/latticeai/api/auth.py +0 -383
  221. package/latticeai/api/automation_intelligence.py +0 -401
  222. package/latticeai/api/brain_intelligence.py +0 -199
  223. package/latticeai/api/browser.py +0 -493
  224. package/latticeai/api/change_proposals.py +0 -89
  225. package/latticeai/api/chat.py +0 -572
  226. package/latticeai/api/chat_agent_http.py +0 -892
  227. package/latticeai/api/chat_contracts.py +0 -73
  228. package/latticeai/api/chat_documents.py +0 -276
  229. package/latticeai/api/chat_helpers.py +0 -460
  230. package/latticeai/api/chat_history.py +0 -101
  231. package/latticeai/api/chat_hybrid.py +0 -113
  232. package/latticeai/api/chat_intents.py +0 -646
  233. package/latticeai/api/chat_stream.py +0 -216
  234. package/latticeai/api/chronicle.py +0 -63
  235. package/latticeai/api/command_center.py +0 -51
  236. package/latticeai/api/computer_use.py +0 -474
  237. package/latticeai/api/evidence_actions.py +0 -48
  238. package/latticeai/api/features.py +0 -70
  239. package/latticeai/api/funnel_metrics.py +0 -31
  240. package/latticeai/api/garden.py +0 -34
  241. package/latticeai/api/hooks.py +0 -165
  242. package/latticeai/api/index_jobs.py +0 -145
  243. package/latticeai/api/invitations.py +0 -100
  244. package/latticeai/api/knowledge_graph.py +0 -536
  245. package/latticeai/api/marketplace.py +0 -105
  246. package/latticeai/api/mcp.py +0 -482
  247. package/latticeai/api/memory.py +0 -270
  248. package/latticeai/api/network.py +0 -81
  249. package/latticeai/api/network_boundary.py +0 -225
  250. package/latticeai/api/permission_mode.py +0 -61
  251. package/latticeai/api/permissions.py +0 -436
  252. package/latticeai/api/plugins.py +0 -126
  253. package/latticeai/api/portability.py +0 -391
  254. package/latticeai/api/project_sessions.py +0 -114
  255. package/latticeai/api/realtime.py +0 -118
  256. package/latticeai/api/review_queue.py +0 -364
  257. package/latticeai/api/security_dashboard.py +0 -604
  258. package/latticeai/api/setup.py +0 -319
  259. package/latticeai/api/static_routes.py +0 -354
  260. package/latticeai/api/ui_redirects.py +0 -26
  261. package/latticeai/api/workflow_designer.py +0 -394
  262. package/latticeai/api/workspace.py +0 -856
  263. package/latticeai/api/workspace_scope.py +0 -125
  264. package/latticeai/core/agent/__init__.py +0 -93
  265. package/latticeai/core/agent/_contract.py +0 -79
  266. package/latticeai/core/agent/context.py +0 -57
  267. package/latticeai/core/agent/deps.py +0 -125
  268. package/latticeai/core/agent/execution.py +0 -622
  269. package/latticeai/core/agent/planning.py +0 -145
  270. package/latticeai/core/agent/recovery.py +0 -157
  271. package/latticeai/core/agent/runtime.py +0 -210
  272. package/latticeai/core/agent/verification.py +0 -231
  273. package/latticeai/core/agent_eval.py +0 -739
  274. package/latticeai/core/agent_helpers.py +0 -493
  275. package/latticeai/core/agent_profiles.py +0 -110
  276. package/latticeai/core/agent_prompts.py +0 -171
  277. package/latticeai/core/agent_registry.py +0 -232
  278. package/latticeai/core/agent_state.py +0 -41
  279. package/latticeai/core/agent_trace.py +0 -104
  280. package/latticeai/core/artifact_ledger.py +0 -109
  281. package/latticeai/core/audit.py +0 -260
  282. package/latticeai/core/builtin_hooks.py +0 -105
  283. package/latticeai/core/context_builder.py +0 -394
  284. package/latticeai/core/document_generator.py +0 -103
  285. package/latticeai/core/enterprise.py +0 -154
  286. package/latticeai/core/enterprise_admin.py +0 -158
  287. package/latticeai/core/file_generation/__init__.py +0 -115
  288. package/latticeai/core/file_generation/bundles.py +0 -76
  289. package/latticeai/core/file_generation/extraction.py +0 -154
  290. package/latticeai/core/file_generation/inference.py +0 -235
  291. package/latticeai/core/file_generation/orchestration.py +0 -152
  292. package/latticeai/core/file_generation/prompting.py +0 -117
  293. package/latticeai/core/file_generation/repair.py +0 -114
  294. package/latticeai/core/file_generation/sanitize.py +0 -61
  295. package/latticeai/core/file_generation/validation.py +0 -201
  296. package/latticeai/core/invitations.py +0 -132
  297. package/latticeai/core/legacy_compatibility.py +0 -243
  298. package/latticeai/core/logging_safety.py +0 -46
  299. package/latticeai/core/marketplace.py +0 -293
  300. package/latticeai/core/mcp_catalog.py +0 -452
  301. package/latticeai/core/mcp_registry.py +0 -506
  302. package/latticeai/core/network_boundary.py +0 -168
  303. package/latticeai/core/oidc.py +0 -208
  304. package/latticeai/core/plugins.py +0 -432
  305. package/latticeai/core/product_hardening.py +0 -218
  306. package/latticeai/core/project_sessions.py +0 -337
  307. package/latticeai/core/realtime.py +0 -238
  308. package/latticeai/core/run_explain.py +0 -426
  309. package/latticeai/core/run_store.py +0 -252
  310. package/latticeai/core/timezones.py +0 -80
  311. package/latticeai/core/workspace_computer_memory.py +0 -84
  312. package/latticeai/core/workspace_graph_trace.py +0 -155
  313. package/latticeai/core/workspace_indexing.py +0 -102
  314. package/latticeai/core/workspace_memory.py +0 -77
  315. package/latticeai/core/workspace_onboarding.py +0 -104
  316. package/latticeai/core/workspace_os.py +0 -978
  317. package/latticeai/core/workspace_os_constants.py +0 -126
  318. package/latticeai/core/workspace_os_state.py +0 -180
  319. package/latticeai/core/workspace_os_utils.py +0 -103
  320. package/latticeai/core/workspace_permissions.py +0 -101
  321. package/latticeai/core/workspace_plugins.py +0 -97
  322. package/latticeai/core/workspace_relationships.py +0 -99
  323. package/latticeai/core/workspace_reorganization.py +0 -335
  324. package/latticeai/core/workspace_review_items.py +0 -112
  325. package/latticeai/core/workspace_runs.py +0 -726
  326. package/latticeai/core/workspace_skills.py +0 -109
  327. package/latticeai/core/workspace_snapshots.py +0 -198
  328. package/latticeai/core/workspace_timeline.py +0 -110
  329. package/latticeai/integrations/__init__.py +0 -0
  330. package/latticeai/integrations/telegram_bot/__init__.py +0 -123
  331. package/latticeai/integrations/telegram_bot/__main__.py +0 -17
  332. package/latticeai/integrations/telegram_bot/config.py +0 -86
  333. package/latticeai/integrations/telegram_bot/dispatch.py +0 -311
  334. package/latticeai/integrations/telegram_bot/flows.py +0 -478
  335. package/latticeai/integrations/telegram_bot/helpers.py +0 -322
  336. package/latticeai/integrations/telegram_bot/screens.py +0 -394
  337. package/latticeai/runtime/audit_runtime.py +0 -76
  338. package/latticeai/runtime/automation_runtime.py +0 -81
  339. package/latticeai/runtime/chat_wiring.py +0 -141
  340. package/latticeai/runtime/context_runtime.py +0 -66
  341. package/latticeai/runtime/feature_toggle_wiring.py +0 -157
  342. package/latticeai/runtime/history_runtime.py +0 -163
  343. package/latticeai/runtime/history_writer.py +0 -138
  344. package/latticeai/runtime/hooks_runtime.py +0 -77
  345. package/latticeai/runtime/model_wiring.py +0 -68
  346. package/latticeai/runtime/namespace_runtime.py +0 -163
  347. package/latticeai/runtime/network_boundary_wiring.py +0 -117
  348. package/latticeai/runtime/network_config_runtime.py +0 -56
  349. package/latticeai/runtime/permission_mode_wiring.py +0 -112
  350. package/latticeai/runtime/persistence_runtime.py +0 -159
  351. package/latticeai/runtime/platform_runtime_wiring.py +0 -89
  352. package/latticeai/runtime/review_wiring.py +0 -42
  353. package/latticeai/runtime/router_registration.py +0 -693
  354. package/latticeai/runtime/service_singletons.py +0 -55
  355. package/latticeai/runtime/sso_config_runtime.py +0 -128
  356. package/latticeai/runtime/user_key_runtime.py +0 -106
  357. package/latticeai/runtime/web_runtime.py +0 -92
  358. package/latticeai/server_app.py +0 -51
  359. package/latticeai/services/app_context.py +0 -130
  360. package/latticeai/services/automation_execution.py +0 -266
  361. package/latticeai/services/automation_intelligence.py +0 -614
  362. package/latticeai/services/brain_automation.py +0 -191
  363. package/latticeai/services/brain_intelligence/__init__.py +0 -58
  364. package/latticeai/services/brain_intelligence/_contract.py +0 -71
  365. package/latticeai/services/brain_intelligence/consistency.py +0 -193
  366. package/latticeai/services/brain_intelligence/constants.py +0 -47
  367. package/latticeai/services/brain_intelligence/digest.py +0 -258
  368. package/latticeai/services/brain_intelligence/health.py +0 -331
  369. package/latticeai/services/brain_intelligence/proposals.py +0 -259
  370. package/latticeai/services/brain_intelligence/sampling.py +0 -84
  371. package/latticeai/services/brain_intelligence/service.py +0 -48
  372. package/latticeai/services/change_proposals.py +0 -471
  373. package/latticeai/services/chat_service.py +0 -243
  374. package/latticeai/services/chronicle.py +0 -555
  375. package/latticeai/services/cloud_egress_audit.py +0 -85
  376. package/latticeai/services/cloud_extraction.py +0 -129
  377. package/latticeai/services/cloud_streaming.py +0 -268
  378. package/latticeai/services/cloud_token_guard.py +0 -84
  379. package/latticeai/services/command_center.py +0 -548
  380. package/latticeai/services/evidence_actions.py +0 -258
  381. package/latticeai/services/feature_toggles.py +0 -502
  382. package/latticeai/services/folder_watch.py +0 -520
  383. package/latticeai/services/funnel_metrics.py +0 -307
  384. package/latticeai/services/hybrid_chat.py +0 -316
  385. package/latticeai/services/hybrid_context.py +0 -228
  386. package/latticeai/services/hybrid_policy.py +0 -129
  387. package/latticeai/services/interop_bridges.py +0 -978
  388. package/latticeai/services/local_knowledge.py +0 -465
  389. package/latticeai/services/memory_service/__init__.py +0 -52
  390. package/latticeai/services/memory_service/_contract.py +0 -100
  391. package/latticeai/services/memory_service/brief.py +0 -431
  392. package/latticeai/services/memory_service/constants.py +0 -57
  393. package/latticeai/services/memory_service/maintenance.py +0 -138
  394. package/latticeai/services/memory_service/manager.py +0 -186
  395. package/latticeai/services/memory_service/proof.py +0 -136
  396. package/latticeai/services/memory_service/recall.py +0 -225
  397. package/latticeai/services/memory_service/service.py +0 -48
  398. package/latticeai/services/memory_service/stores.py +0 -110
  399. package/latticeai/services/mode_store.py +0 -132
  400. package/latticeai/services/model_recommendation.py +0 -224
  401. package/latticeai/services/model_runtime/cloud.py +0 -87
  402. package/latticeai/services/network_boundary_service.py +0 -117
  403. package/latticeai/services/obsidian_bridge.py +0 -609
  404. package/latticeai/services/openai_compatible_adapter.py +0 -101
  405. package/latticeai/services/permission_mode_service.py +0 -122
  406. package/latticeai/services/platform_runtime.py +0 -366
  407. package/latticeai/services/review_queue.py +0 -380
  408. package/latticeai/services/router_context.py +0 -59
  409. package/latticeai/services/run_executor.py +0 -387
  410. package/latticeai/services/self_model_service.py +0 -171
  411. package/latticeai/services/setup_detection.py +0 -147
  412. package/latticeai/services/triggers.py +0 -378
  413. package/latticeai/services/upload_service.py +0 -172
  414. package/latticeai/services/workspace_service.py +0 -165
  415. package/latticeai/setup/__init__.py +0 -25
  416. package/latticeai/setup/auto_setup.py +0 -846
  417. package/latticeai/setup/demo_corpus.py +0 -98
  418. package/latticeai/setup/wizard/__init__.py +0 -126
  419. package/latticeai/setup/wizard/catalog.py +0 -175
  420. package/latticeai/setup/wizard/detect.py +0 -302
  421. package/latticeai/setup/wizard/install.py +0 -348
  422. package/latticeai/setup/wizard/paths.py +0 -165
  423. package/latticeai/setup/wizard/plans.py +0 -74
  424. package/latticeai/setup/wizard/recommend.py +0 -320
  425. package/scripts/bench_agent_smoke.py +0 -409
  426. package/scripts/bench_models.py +0 -540
  427. package/scripts/bench_vector_index.py +0 -295
  428. package/scripts/funnel_soft_gate.py +0 -192
  429. package/scripts/generate_agent_loop_fixtures.py +0 -994
  430. package/scripts/generate_rust_parity_fixtures.py +0 -908
  431. package/scripts/migrate_brain_storage.py +0 -57
  432. package/scripts/parity_fixture_corpus_context.py +0 -162
  433. package/scripts/parity_fixture_corpus_docgen.py +0 -341
  434. package/scripts/profile_kg.py +0 -355
  435. package/server.py +0 -30
  436. package/static/app/assets/AdminConsole-cf4npybT.js +0 -1
  437. package/static/app/assets/Capture-DiQ219jW.js +0 -1
  438. package/static/app/assets/System-CieofHQa.js +0 -1
  439. package/static/app/assets/arrow-left-kfsrk0mv.js +0 -1
  440. package/static/app/assets/circle-check-qqLug9nU.js +0 -1
  441. package/static/app/assets/index-DxmOfNRi.css +0 -2
  442. package/static/app/assets/primitives-SNp0LRJz.js +0 -1
  443. package/static/app/assets/search-BcHqkjoy.js +0 -1
@@ -1,474 +0,0 @@
1
- """Computer-use routes and the desktop-control agent loop."""
2
-
3
- from __future__ import annotations
4
-
5
- import json
6
- import re
7
- from typing import Any, Dict, List, Optional
8
-
9
- from fastapi import APIRouter, HTTPException, Request
10
- from fastapi.responses import StreamingResponse
11
- from pydantic import BaseModel
12
-
13
- from lattice_brain.runtime.hooks import dispatch_tool
14
- from latticeai.api.workspace_scope import resolve_workspace_scope
15
- from latticeai.core.agent import extract_action as _extract_agent_action
16
- from latticeai.core.sse import sse_frame
17
- from latticeai.services.tool_dispatch import enforce_tool_policy
18
- from latticeai.tools import (
19
- AGENT_ROOT,
20
- ToolError,
21
- computer_click,
22
- computer_drag,
23
- computer_key,
24
- computer_move,
25
- computer_open_app,
26
- computer_open_url,
27
- computer_screenshot,
28
- computer_scroll,
29
- computer_status,
30
- computer_type,
31
- desktop_bridge_status,
32
- execute_tool,
33
- )
34
-
35
- CU_SYSTEM_PROMPT = """You are Lattice AI desktop-control agent. You control the Mac desktop using tools.
36
- Prefer non-visual direct actions when possible. Use screenshots only when you must inspect visible UI state or choose screen coordinates.
37
-
38
- Available actions:
39
- - computer_screenshot: {"action":"computer_screenshot","args":{}} — capture screen, returns screenshot_b64
40
- - computer_open_app: {"action":"computer_open_app","args":{"app":"Google Chrome"}} — open or focus a Mac app
41
- - computer_open_url: {"action":"computer_open_url","args":{"url":"https://example.com","app":"Google Chrome"}} — open URL in app
42
- - computer_click: {"action":"computer_click","args":{"x":500,"y":300,"button":"left","double":false}}
43
- - computer_type: {"action":"computer_type","args":{"text":"hello world","interval":0.04}}
44
- - computer_key: {"action":"computer_key","args":{"key":"return"}} — keys: return, escape, tab, space, command+c, etc.
45
- - computer_scroll: {"action":"computer_scroll","args":{"x":500,"y":300,"direction":"down","clicks":3}}
46
- - computer_move: {"action":"computer_move","args":{"x":500,"y":300}}
47
- - computer_drag: {"action":"computer_drag","args":{"x1":100,"y1":100,"x2":500,"y2":500}}
48
- - vision_analyze: {"action":"vision_analyze","args":{"image_b64": "...", "prompt": "What do you see on screen?"}} — use after screenshot to let VLM describe/answer about the image (only when multimodal model loaded)
49
- - final: {"action":"final","message":"Korean summary of what was accomplished"}
50
-
51
- Rules:
52
- - Respond with exactly ONE JSON object. No markdown, no extra text.
53
- - Do not take screenshots for simple app launch, URL opening, keyboard shortcuts, or non-visual tasks.
54
- - Take a screenshot before coordinate-based clicks/drags or when the task explicitly asks you to inspect the screen.
55
- - After screenshot, prefer vision_analyze (with good prompt) over raw b64 in next step when you need to understand what is on screen (especially with multimodal/VLM loaded).
56
- - After coordinate-based clicking or typing into an unknown focused field, take a screenshot only if verification is necessary.
57
- - Use coordinates relative to the screen (0,0 is top-left).
58
- - If a UI element is not visible, scroll or search for it first.
59
- - macOS Accessibility permission required for mouse/keyboard control.
60
- """
61
-
62
-
63
- class CuAgentRequest(BaseModel):
64
- task: str
65
- conversation_id: Optional[str] = None
66
- max_steps: int = 15
67
- temperature: float = 0.1
68
-
69
-
70
- class CuClickRequest(BaseModel):
71
- x: int
72
- y: int
73
- button: str = "left"
74
- double: bool = False
75
-
76
-
77
- class CuOpenAppRequest(BaseModel):
78
- app: str = "Google Chrome"
79
-
80
-
81
- class CuOpenUrlRequest(BaseModel):
82
- url: str
83
- app: str = "Google Chrome"
84
-
85
-
86
- class CuTypeRequest(BaseModel):
87
- text: str
88
- interval: float = 0.04
89
-
90
-
91
- class CuKeyRequest(BaseModel):
92
- key: str
93
-
94
-
95
- class CuScrollRequest(BaseModel):
96
- x: int
97
- y: int
98
- direction: str = "down"
99
- clicks: int = 3
100
-
101
-
102
- class CuMoveRequest(BaseModel):
103
- x: int
104
- y: int
105
-
106
-
107
- class CuDragRequest(BaseModel):
108
- x1: int
109
- y1: int
110
- x2: int
111
- y2: int
112
-
113
-
114
- def create_computer_use_router(
115
- *,
116
- model_router,
117
- require_user,
118
- tool_response,
119
- save_to_history,
120
- hooks=None,
121
- append_audit_event=None,
122
- workspace_service=None,
123
- ) -> APIRouter:
124
- router = APIRouter()
125
-
126
- def _write_workspace(request: Request, current_user: str) -> Optional[str]:
127
- """Resolve only the CU agent's durable write scope.
128
-
129
- The other ``/cu`` actions remain host operations and do not acquire a
130
- workspace dependency. A no-auth local request without an explicit
131
- scope preserves its legacy unscoped history behavior — that is exactly
132
- what ``allow_unscoped_anonymous`` names in the shared resolver;
133
- explicit scopes and every authenticated request go through the normal
134
- write gate.
135
- """
136
-
137
- return resolve_workspace_scope(
138
- request,
139
- user=current_user,
140
- workspace_service=workspace_service,
141
- write=True,
142
- allow_unscoped_anonymous=True,
143
- )
144
-
145
- def _audit_args(name: str, args: dict) -> dict:
146
- """Return audit-safe argument metadata without storing typed text."""
147
- args = dict(args or {})
148
- if name == "computer_type":
149
- return {"text_length": len(str(args.get("text") or "")), "interval": args.get("interval")}
150
- if name == "vision_analyze":
151
- return {
152
- "image_b64_length": len(str(args.get("image_b64") or "")),
153
- "prompt_length": len(str(args.get("prompt") or "")),
154
- }
155
- return args
156
-
157
- def _audit(name: str, args: dict, *, current_user: str, status: str, error: Optional[str] = None) -> None:
158
- if append_audit_event is None:
159
- return
160
- payload = {
161
- "user_email": current_user,
162
- "source": "computer_use",
163
- "tool": name,
164
- "status": status,
165
- "args": _audit_args(name, args),
166
- }
167
- if error:
168
- payload["error"] = error[:500]
169
- append_audit_event("computer_use_tool", **payload)
170
-
171
- def _dispatch(name, args, fn, *, current_user: str):
172
- # Run a computer-use action through the unified pre_tool/post_tool
173
- # lifecycle after the same ToolRegistry policy gate used by direct
174
- # /tools execution. This keeps desktop control behind one governance
175
- # boundary instead of relying on route-specific checks.
176
- args = dict(args or {})
177
- try:
178
- enforce_tool_policy(name, args, current_user=current_user, source="computer_use")
179
- except HTTPException as exc:
180
- _audit(name, args, current_user=current_user, status="blocked", error=str(exc.detail))
181
- raise
182
- try:
183
- result = dispatch_tool(hooks, name, args, fn, source="computer_use")
184
- except (PermissionError, ToolError, KeyError, TypeError) as exc:
185
- _audit(name, args, current_user=current_user, status="error", error=str(exc))
186
- raise
187
- _audit(name, args, current_user=current_user, status="ok")
188
- return result
189
-
190
- def _response(result):
191
- return {"status": "ok", "workspace": str(AGENT_ROOT), "result": result}
192
-
193
- @router.get("/tools/chrome_status")
194
- async def tools_chrome_status(request: Request):
195
- current_user = require_user(request)
196
- result = _dispatch(
197
- "chrome_status",
198
- {},
199
- desktop_bridge_status,
200
- current_user=current_user,
201
- )
202
- return _response(result)
203
-
204
- @router.get("/tools/computer_use_status")
205
- async def tools_computer_use_status(request: Request):
206
- current_user = require_user(request)
207
- result = _dispatch(
208
- "computer_use_status",
209
- {},
210
- computer_status,
211
- current_user=current_user,
212
- )
213
- return _response(result)
214
-
215
- @router.get("/cu/status")
216
- async def cu_status(request: Request):
217
- current_user = require_user(request)
218
- try:
219
- return _dispatch("computer_status", {}, computer_status, current_user=current_user)
220
- except HTTPException:
221
- raise
222
- except PermissionError as exc:
223
- raise HTTPException(status_code=403, detail=str(exc))
224
- except ToolError as exc:
225
- raise HTTPException(status_code=400, detail=str(exc))
226
-
227
- @router.get("/cu/screenshot")
228
- async def cu_screenshot(request: Request):
229
- current_user = require_user(request)
230
- try:
231
- return _dispatch("computer_screenshot", {}, computer_screenshot, current_user=current_user)
232
- except HTTPException:
233
- raise
234
- except PermissionError as exc:
235
- raise HTTPException(status_code=403, detail=str(exc))
236
- except ToolError as exc:
237
- raise HTTPException(status_code=400, detail=str(exc))
238
-
239
- @router.post("/cu/open_app")
240
- async def cu_open_app(req: CuOpenAppRequest, request: Request):
241
- current_user = require_user(request)
242
- result = _dispatch(
243
- "computer_open_app",
244
- {"app": req.app},
245
- lambda: computer_open_app(req.app),
246
- current_user=current_user,
247
- )
248
- return _response(result)
249
-
250
- @router.post("/cu/open_url")
251
- async def cu_open_url(req: CuOpenUrlRequest, request: Request):
252
- current_user = require_user(request)
253
- result = _dispatch(
254
- "computer_open_url",
255
- {"url": req.url, "app": req.app},
256
- lambda: computer_open_url(req.url, req.app),
257
- current_user=current_user,
258
- )
259
- return _response(result)
260
-
261
- @router.post("/cu/click")
262
- async def cu_click(req: CuClickRequest, request: Request):
263
- current_user = require_user(request)
264
- result = _dispatch(
265
- "computer_click",
266
- {"x": req.x, "y": req.y, "button": req.button, "double": req.double},
267
- lambda: computer_click(req.x, req.y, req.button, req.double),
268
- current_user=current_user,
269
- )
270
- return _response(result)
271
-
272
- @router.post("/cu/type")
273
- async def cu_type(req: CuTypeRequest, request: Request):
274
- current_user = require_user(request)
275
- result = _dispatch(
276
- "computer_type",
277
- {"text": req.text, "interval": req.interval},
278
- lambda: computer_type(req.text, req.interval),
279
- current_user=current_user,
280
- )
281
- return _response(result)
282
-
283
- @router.post("/cu/key")
284
- async def cu_key(req: CuKeyRequest, request: Request):
285
- current_user = require_user(request)
286
- result = _dispatch(
287
- "computer_key",
288
- {"key": req.key},
289
- lambda: computer_key(req.key),
290
- current_user=current_user,
291
- )
292
- return _response(result)
293
-
294
- @router.post("/cu/scroll")
295
- async def cu_scroll(req: CuScrollRequest, request: Request):
296
- current_user = require_user(request)
297
- result = _dispatch(
298
- "computer_scroll",
299
- {"x": req.x, "y": req.y, "direction": req.direction, "clicks": req.clicks},
300
- lambda: computer_scroll(req.x, req.y, req.direction, req.clicks),
301
- current_user=current_user,
302
- )
303
- return _response(result)
304
-
305
- @router.post("/cu/move")
306
- async def cu_move(req: CuMoveRequest, request: Request):
307
- current_user = require_user(request)
308
- result = _dispatch(
309
- "computer_move",
310
- {"x": req.x, "y": req.y},
311
- lambda: computer_move(req.x, req.y),
312
- current_user=current_user,
313
- )
314
- return _response(result)
315
-
316
- @router.post("/cu/drag")
317
- async def cu_drag(req: CuDragRequest, request: Request):
318
- current_user = require_user(request)
319
- result = _dispatch(
320
- "computer_drag",
321
- {"x1": req.x1, "y1": req.y1, "x2": req.x2, "y2": req.y2},
322
- lambda: computer_drag(req.x1, req.y1, req.x2, req.y2),
323
- current_user=current_user,
324
- )
325
- return _response(result)
326
-
327
- @router.post("/cu/agent")
328
- async def cu_agent(req: CuAgentRequest, request: Request):
329
- current_user = require_user(request)
330
- workspace_id = _write_workspace(request, current_user)
331
-
332
- def _save_completed(role: str, message: str) -> None:
333
- save_to_history(
334
- role,
335
- message,
336
- source="web",
337
- conversation_id=req.conversation_id,
338
- user_email=current_user,
339
- workspace_id=workspace_id,
340
- )
341
-
342
- async def _stream():
343
- task_lower = (req.task or "").lower()
344
- url_match = re.search(r"(https?://[^\s]+|localhost:\d+[^\s]*|127\.0\.0\.1:\d+[^\s]*)", req.task or "")
345
-
346
- if ("chrome" in task_lower or "크롬" in task_lower) and any(
347
- word in task_lower for word in ["open", "열", "켜", "실행", "띄"]
348
- ):
349
- yield sse_frame("start", {"task": req.task, "max_steps": 1})
350
- try:
351
- if url_match:
352
- url = url_match.group(1)
353
- yield sse_frame(
354
- "action",
355
- {"step": 1, "action": "computer_open_url", "args": {"url": url, "app": "Google Chrome"}},
356
- )
357
- result = _dispatch(
358
- "computer_open_url",
359
- {"url": url, "app": "Google Chrome"},
360
- lambda: computer_open_url(url, "Google Chrome"),
361
- current_user=current_user,
362
- )
363
- yield sse_frame("result", {"step": 1, "action": "computer_open_url", "result": result})
364
- message = f"Google Chrome에서 {url}을 열었습니다."
365
- action_name = "computer_open_url"
366
- else:
367
- yield sse_frame(
368
- "action",
369
- {"step": 1, "action": "computer_open_app", "args": {"app": "Google Chrome"}},
370
- )
371
- result = _dispatch(
372
- "computer_open_app",
373
- {"app": "Google Chrome"},
374
- lambda: computer_open_app("Google Chrome"),
375
- current_user=current_user,
376
- )
377
- yield sse_frame("result", {"step": 1, "action": "computer_open_app", "result": result})
378
- message = "Google Chrome을 열었습니다."
379
- action_name = "computer_open_app"
380
- _save_completed("user", req.task)
381
- _save_completed("assistant", message)
382
- yield sse_frame("final", {"message": message, "steps": [{"step": 1, "action": action_name, "result": result}]})
383
- except HTTPException as exc:
384
- yield sse_frame("tool_error", {"step": 1, "action": "computer_open_app", "error": str(exc.detail)})
385
- except (ToolError, PermissionError) as exc:
386
- yield sse_frame("tool_error", {"step": 1, "action": "computer_open_app", "error": str(exc)})
387
- return
388
-
389
- if not model_router.current_model_id:
390
- yield sse_frame("error", {"error": "No model loaded."})
391
- return
392
-
393
- transcript: List[Dict[str, Any]] = []
394
- last_screenshot_b64: Optional[str] = None
395
- max_steps = max(1, min(req.max_steps, 20))
396
- yield sse_frame("start", {"task": req.task, "max_steps": max_steps})
397
-
398
- for step in range(max_steps):
399
- context = (
400
- f"{CU_SYSTEM_PROMPT}\n\n"
401
- f"Task: {req.task}\n\n"
402
- f"Steps completed so far:\n{json.dumps(transcript, ensure_ascii=False, indent=2)}"
403
- )
404
- raw = await model_router.generate(
405
- message="Choose the next computer use action.",
406
- context=context,
407
- image_data=last_screenshot_b64,
408
- max_tokens=1024,
409
- temperature=req.temperature,
410
- )
411
-
412
- try:
413
- action = _extract_agent_action(str(raw))
414
- except ValueError as exc:
415
- yield sse_frame("error", {"step": step + 1, "error": str(exc), "raw": str(raw)})
416
- break
417
-
418
- name = action.get("action")
419
- args = action.get("args") or {}
420
- if name == "final":
421
- message = action.get("message", "작업을 완료했습니다.")
422
- _save_completed("user", req.task)
423
- _save_completed("assistant", message)
424
- yield sse_frame("final", {"message": message, "steps": transcript})
425
- return
426
-
427
- yield sse_frame("action", {"step": step + 1, "action": name, "args": args})
428
- try:
429
- result = _dispatch(
430
- name,
431
- args,
432
- # Bound as defaults; see chat_agent_http for why.
433
- lambda name=name, args=args: execute_tool(name, args),
434
- current_user=current_user,
435
- )
436
- if name == "computer_screenshot" and "screenshot_b64" in result:
437
- last_screenshot_b64 = result["screenshot_b64"]
438
- result_summary = {k: v for k, v in result.items() if k != "screenshot_b64"}
439
- result_summary["screenshot_captured"] = True
440
- transcript.append({"step": step + 1, "action": name, "args": args, "result": result_summary})
441
- yield sse_frame(
442
- "screenshot",
443
- {
444
- "step": step + 1,
445
- "screenshot_b64": last_screenshot_b64,
446
- "width": result.get("screen_width"),
447
- "height": result.get("screen_height"),
448
- },
449
- )
450
- else:
451
- last_screenshot_b64 = None
452
- transcript.append({"step": step + 1, "action": name, "args": args, "result": result})
453
- yield sse_frame("result", {"step": step + 1, "action": name, "result": result})
454
- except HTTPException as exc:
455
- error_str = str(exc.detail)
456
- transcript.append({"step": step + 1, "action": name, "args": args, "error": error_str})
457
- yield sse_frame("tool_error", {"step": step + 1, "action": name, "error": error_str})
458
- except (ToolError, PermissionError, KeyError, TypeError) as exc:
459
- error_str = str(exc)
460
- transcript.append({"step": step + 1, "action": name, "args": args, "error": error_str})
461
- yield sse_frame("tool_error", {"step": step + 1, "action": name, "error": error_str})
462
-
463
- yield sse_frame("done", {"steps": len(transcript), "transcript": transcript})
464
-
465
- return StreamingResponse(
466
- _stream(),
467
- media_type="text/event-stream",
468
- headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
469
- )
470
-
471
- return router
472
-
473
-
474
- __all__ = ["create_computer_use_router"]
@@ -1,48 +0,0 @@
1
- """Evidence → action router (v9.9.6).
2
-
3
- ``POST /api/evidence/actions`` turns an answer's citations into ready-to-send,
4
- evidence-scoped follow-up prompts. Read-only and deterministic: it resolves
5
- graph nodes and composes text — no model call, no writes. Execution stays on
6
- the existing chat / file-generation path so there is exactly one road from a
7
- prompt to an artifact.
8
- """
9
-
10
- from __future__ import annotations
11
-
12
- from typing import Any, Callable, List, Optional
13
-
14
- from fastapi import APIRouter, Request
15
- from pydantic import BaseModel, Field
16
-
17
-
18
- class EvidenceActionsRequest(BaseModel):
19
- """Citations from one answer (the ids the grounding badge reported)."""
20
-
21
- question: str = ""
22
- source_ids: List[str] = Field(default_factory=list)
23
- language: str = "ko"
24
-
25
-
26
- def create_evidence_actions_router(
27
- *,
28
- service: Any,
29
- require_user: Callable[[Request], Any],
30
- allowed_workspaces_for: Optional[Callable[[Any], Any]] = None,
31
- ) -> APIRouter:
32
- router = APIRouter()
33
-
34
- @router.post("/api/evidence/actions")
35
- async def evidence_actions(req: EvidenceActionsRequest, request: Request):
36
- user = require_user(request)
37
- scope = allowed_workspaces_for(user) if allowed_workspaces_for is not None else None
38
- return service.actions_for(
39
- question=req.question,
40
- source_ids=req.source_ids,
41
- language=req.language,
42
- allowed_workspaces=scope,
43
- )
44
-
45
- return router
46
-
47
-
48
- __all__ = ["create_evidence_actions_router", "EvidenceActionsRequest"]
@@ -1,70 +0,0 @@
1
- """Feature switchboard API — read the catalog, move one switch (v11.2.0).
2
-
3
- Two routes and no more. The catalog is rendered by the service (labels,
4
- explanations, defaults, which options are even installable), so this router
5
- carries no product knowledge at all: adding a feature is a line in
6
- ``latticeai/services/feature_toggles.CATALOG``, not a change here or in the
7
- client.
8
- """
9
-
10
- from __future__ import annotations
11
-
12
- from typing import Any, Callable, Union
13
-
14
- from fastapi import APIRouter, HTTPException, Request
15
- from pydantic import BaseModel, Field
16
-
17
- from latticeai.core.messages import http_error, resolve_language
18
- from latticeai.services.feature_toggles import (
19
- FeatureToggleService,
20
- InvalidFeatureValue,
21
- UnknownFeature,
22
- )
23
-
24
-
25
- class SetFeatureRequest(BaseModel):
26
- """One switch move. ``value`` is a bool for a toggle, a string for a choice."""
27
-
28
- value: Union[bool, str] = Field(
29
- ..., description="true/false for a toggle, or the option id for a choice"
30
- )
31
-
32
-
33
- def create_features_router(
34
- *,
35
- service: FeatureToggleService,
36
- require_user: Callable[..., str],
37
- ) -> APIRouter:
38
- router = APIRouter(tags=["features"])
39
-
40
- @router.get("/api/features")
41
- async def list_features(request: Request) -> Any:
42
- """Every opt-in feature, its live value, and where that value came from."""
43
- require_user(request)
44
- return service.catalog(resolve_language(request))
45
-
46
- @router.post("/api/features/{feature_id}")
47
- async def set_feature(
48
- feature_id: str, body: SetFeatureRequest, request: Request
49
- ) -> Any:
50
- """Persist one person's choice; the answer is the feature as it now reads."""
51
- user = require_user(request)
52
- language = resolve_language(request)
53
- try:
54
- return service.set(
55
- feature_id, body.value, language=language, user_email=user
56
- )
57
- except UnknownFeature as exc:
58
- raise http_error(
59
- 400, "features.unknown", language, feature=feature_id
60
- ) from exc
61
- except InvalidFeatureValue as exc:
62
- # Pass-through: the service already localized the reason through the
63
- # same catalog (wrong type, or an option whose optional dependency
64
- # is not installed), and re-wording it here would only lose it.
65
- raise HTTPException(status_code=400, detail=str(exc)) from exc
66
-
67
- return router
68
-
69
-
70
- __all__ = ["SetFeatureRequest", "create_features_router"]
@@ -1,31 +0,0 @@
1
- """Admin-only UX funnel metrics endpoint (backlog #16).
2
-
3
- Exposes :class:`~latticeai.services.funnel_metrics.FunnelMetricsService`
4
- snapshots at ``GET /api/admin/funnel-metrics``. Follows the admin router
5
- convention: gate through the injected ``require_admin`` callable, read-only,
6
- no side effects.
7
- """
8
-
9
- from __future__ import annotations
10
-
11
- from typing import Any, Callable
12
-
13
- from fastapi import APIRouter, Request
14
-
15
-
16
- def create_funnel_metrics_router(
17
- *,
18
- service: Any,
19
- require_admin: Callable[[Request], Any],
20
- ) -> APIRouter:
21
- router = APIRouter()
22
-
23
- @router.get("/api/admin/funnel-metrics")
24
- async def funnel_metrics(request: Request):
25
- require_admin(request)
26
- return service.snapshot()
27
-
28
- return router
29
-
30
-
31
- __all__ = ["create_funnel_metrics_router"]
@@ -1,34 +0,0 @@
1
- """P-Reinforce garden routes."""
2
-
3
- from __future__ import annotations
4
-
5
- from typing import Optional
6
-
7
- from fastapi import APIRouter, Request
8
- from pydantic import BaseModel
9
-
10
-
11
- class GardenRequest(BaseModel):
12
- raw_data: str
13
- category: Optional[str] = None
14
-
15
-
16
- def create_garden_router(*, gardener, require_user) -> APIRouter:
17
- api_router = APIRouter()
18
-
19
- # ── P-Reinforce Knowledge Gardener ────────────────────────────────────────────
20
-
21
- @api_router.post("/garden")
22
- async def garden(req: GardenRequest, request: Request):
23
- """Raw 데이터를 P-Reinforce 구조로 자동 분류·저장"""
24
- require_user(request)
25
- result = await gardener.process(req.raw_data, req.category)
26
- return result
27
-
28
-
29
- @api_router.get("/garden/tree")
30
- async def garden_tree(request: Request):
31
- """지식 정원 파일트리 반환"""
32
- require_user(request)
33
- return gardener.get_tree()
34
- return api_router