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,95 +0,0 @@
1
- """Honest context-quality and multimodal signals for a result set.
2
-
3
- Plain functions over an already-computed match list — no store, no I/O — so
4
- every mixin in the package (and the chat surfaces outside it) can build the
5
- same signal the same way.
6
- """
7
-
8
- from __future__ import annotations
9
-
10
- # ruff: noqa: F403,F405
11
- from .._kg_common import * # noqa: F403,F401
12
-
13
- #: Node types that are a *thing you can look at or listen to*, not prose. A
14
- #: match of one of these means the answer rests on more than text.
15
- MULTIMODAL_NODE_TYPES = ("Image", "ImageText")
16
-
17
-
18
- def multimodal_signal(matches: Iterable[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
19
- """``{"images": n, "types": [...]}`` when a result set includes pictures.
20
-
21
- ``None`` when it does not: the context-quality contract stays four keys
22
- wide for the ordinary all-text case, and a caller that sees the key knows
23
- it means something rather than having to compare a zero.
24
- """
25
- images = 0
26
- seen: List[str] = []
27
- for match in matches:
28
- node_type = str(match.get("type") or "")
29
- if node_type in MULTIMODAL_NODE_TYPES:
30
- images += 1
31
- if node_type not in seen:
32
- seen.append(node_type)
33
- if not images:
34
- return None
35
- return {"images": images, "types": seen}
36
-
37
-
38
- def context_quality_signal(
39
- mode: str,
40
- nodes: int,
41
- *,
42
- reason: Optional[str] = None,
43
- vector: Optional[Dict[str, Any]] = None,
44
- multimodal: Optional[Dict[str, Any]] = None,
45
- ) -> Dict[str, Any]:
46
- """Honest RAG context-quality signal (v9.8.0, additive contract).
47
-
48
- Shape consumed by the chat metadata channel:
49
- ``{"mode": "hybrid"|"lexical_only"|"none", "nodes": int, "limited": bool,
50
- "reason": str|None}``. ``nodes == 0`` always collapses ``mode`` to
51
- ``"none"``; ``limited`` is true whenever the context is thin (0–1 nodes)
52
- or the vector side fell back to lexical-only retrieval. ``reason`` is a
53
- short human-readable Korean phrase, only present when limited.
54
-
55
- ``vector`` (v11.1.0) carries the vector channel's own honesty block —
56
- which backend scored, whether it was approximate, whether the candidate
57
- scan was truncated. "hybrid, 6 nodes" describes two different answers
58
- depending on those bits, and the caller that has to say "I did not find
59
- it" deserves to know which one it got. The key is present **only when
60
- there is a caveat to report**: an exact, complete vector scan is the
61
- contract's baseline assumption, so annotating it would be noise, and the
62
- four-key shape stays exactly what existing consumers pin.
63
-
64
- ``multimodal`` (v11.1.0) follows the same present-only-when-true rule and
65
- says that part of this context is a picture. "6 nodes" reads differently
66
- when two of them are screenshots whose text came out of OCR, and the
67
- surface that has to explain the answer deserves to know.
68
- """
69
- nodes = max(0, int(nodes or 0))
70
- mode = str(mode or "none")
71
- if nodes == 0:
72
- mode = "none"
73
- if mode not in ("hybrid", "lexical_only", "none"):
74
- mode = "lexical_only"
75
- limited = nodes <= 1 or mode != "hybrid"
76
- if reason is None and limited:
77
- if nodes == 0:
78
- reason = "그래프에서 관련 지식을 찾지 못했습니다"
79
- elif mode == "lexical_only":
80
- reason = "벡터 검색을 사용할 수 없어 키워드 검색 결과만 사용했습니다"
81
- else:
82
- reason = "그래프 기반 컨텍스트가 제한적입니다"
83
- if not limited:
84
- reason = None
85
- signal: Dict[str, Any] = {
86
- "mode": mode,
87
- "nodes": nodes,
88
- "limited": limited,
89
- "reason": reason,
90
- }
91
- if vector is not None:
92
- signal["vector"] = dict(vector)
93
- if multimodal is not None:
94
- signal["multimodal"] = dict(multimodal)
95
- return signal
@@ -1,253 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import TYPE_CHECKING
4
-
5
- # ruff: noqa: F403,F405
6
- from ._kg_common import * # noqa: F403,F401
7
-
8
- # The cross-mixin surface (`_connect`, `_upsert_node`, …) is declared in
9
- # `_kg_contract.KnowledgeGraphCore`. It is a typing-only base: at runtime this
10
- # is `object`, so the MRO of `KnowledgeGraphStore` is unchanged.
11
- if TYPE_CHECKING:
12
- from ._kg_contract import KnowledgeGraphCore as _Core
13
- else:
14
- _Core = object
15
-
16
-
17
-
18
- class KnowledgeGraphDocGenMixin(_Core):
19
- """Multi-hop retrieval specialised for document generation, split out
20
- of retrieval. Composed into KnowledgeGraphStore alongside the other
21
- retrieval mixins; shared instance means sibling helpers resolve via MRO.
22
- """
23
-
24
- def search_for_document_generation(
25
- self,
26
- query: str,
27
- limit: int = 10,
28
- *,
29
- allowed_workspaces=None,
30
- include_legacy_global: bool = False,
31
- ) -> List[Dict[str, Any]]:
32
- """Hybrid retrieval optimized for document generation.
33
-
34
- Scoring: 0.5*text_relevance + 0.3*graph_relationship + 0.2*recency
35
- Returns nodes with rich context for document generation prompts.
36
- """
37
- query = str(query or "").strip()
38
- if not query:
39
- return []
40
- limit = max(1, min(int(limit or 10), 50))
41
- terms = _topic_candidates(query, limit=12)
42
- now = datetime.now()
43
- nt, et = self._read_tables()
44
-
45
- with self._connect() as conn:
46
- candidate_rows = []
47
- seen_ids = set()
48
-
49
- # `query` is non-empty here — the early return above took the blank case.
50
- q = f"%{query}%"
51
- rows = conn.execute(
52
- f"""
53
- SELECT id, type, title, summary, metadata_json, updated_at
54
- FROM {nt}
55
- WHERE (title LIKE ? OR summary LIKE ? OR metadata_json LIKE ?)
56
- AND type IN ('Document', 'File', 'CodeFile', 'SlideDeck',
57
- 'Spreadsheet', 'Image', 'ImageText', 'Audio',
58
- 'Chat', 'Decision', 'Task', 'Concept',
59
- 'Feature', 'Page', 'Slide')
60
- ORDER BY updated_at DESC, id ASC
61
- LIMIT ?
62
- """,
63
- (q, q, q, limit * 5),
64
- ).fetchall()
65
- for row in rows:
66
- if row["id"] not in seen_ids:
67
- seen_ids.add(row["id"])
68
- candidate_rows.append(row)
69
-
70
- for term in terms:
71
- t = f"%{term}%"
72
- rows = conn.execute(
73
- f"""
74
- SELECT id, type, title, summary, metadata_json, updated_at
75
- FROM {nt}
76
- WHERE (title LIKE ? OR summary LIKE ? OR metadata_json LIKE ?)
77
- AND type IN ('Document', 'File', 'CodeFile', 'SlideDeck',
78
- 'Spreadsheet', 'Image', 'ImageText', 'Audio',
79
- 'Chat', 'Decision', 'Task', 'Concept',
80
- 'Feature', 'Page', 'Slide')
81
- ORDER BY updated_at DESC, id ASC
82
- LIMIT ?
83
- """,
84
- (t, t, t, limit * 3),
85
- ).fetchall()
86
- for row in rows:
87
- if row["id"] not in seen_ids:
88
- seen_ids.add(row["id"])
89
- candidate_rows.append(row)
90
-
91
- scored_results = []
92
- for row in candidate_rows:
93
- haystack = (
94
- f"{row['title']} {row['summary']} {row['metadata_json']}".lower()
95
- )
96
-
97
- text_hits = sum(1 for term in terms if term.lower() in haystack)
98
- text_score = min(1.0, text_hits / max(len(terms), 1))
99
-
100
- edge_count = conn.execute(
101
- f"SELECT COUNT(*) AS c FROM {et} WHERE from_node=? OR to_node=?",
102
- (row["id"], row["id"]),
103
- ).fetchone()["c"]
104
- graph_score = min(1.0, math.log1p(edge_count) / 4.0)
105
-
106
- recency = _recency_score(
107
- row["updated_at"], now=now, half_life_days=14.0
108
- )
109
-
110
- doc_type_boost = (
111
- 1.2
112
- if row["type"]
113
- in (
114
- "Document",
115
- "File",
116
- "SlideDeck",
117
- "Decision",
118
- )
119
- else 1.0
120
- )
121
-
122
- hybrid_score = (
123
- 0.5 * text_score + 0.3 * graph_score + 0.2 * recency
124
- ) * doc_type_boost
125
-
126
- meta = _safe_loads(row["metadata_json"])
127
- neighbor_concepts = []
128
- neighbor_rows = conn.execute(
129
- f"""
130
- SELECT n.id, n.title, n.type FROM {et} e
131
- JOIN {nt} n ON n.id = CASE WHEN e.from_node = ? THEN e.to_node ELSE e.from_node END
132
- WHERE (e.from_node = ? OR e.to_node = ?)
133
- AND n.type IN ('Concept', 'Feature', 'Decision', 'Task')
134
- LIMIT 8
135
- """,
136
- (row["id"], row["id"], row["id"]),
137
- ).fetchall()
138
- for nr in neighbor_rows:
139
- neighbor_concepts.append({"id": nr["id"], "title": nr["title"], "type": nr["type"]})
140
-
141
- scored_results.append(
142
- {
143
- "id": row["id"],
144
- "type": row["type"],
145
- "title": row["title"],
146
- "summary": row["summary"],
147
- "metadata": meta,
148
- "updated_at": row["updated_at"],
149
- "hybrid_score": round(hybrid_score, 4),
150
- "scores": {
151
- "text": round(text_score, 4),
152
- "graph": round(graph_score, 4),
153
- "recency": round(recency, 4),
154
- },
155
- "related_concepts": neighbor_concepts,
156
- }
157
- )
158
-
159
- if allowed_workspaces is not None:
160
- scored_results = self.filter_scoped_nodes(
161
- scored_results,
162
- allowed_workspaces,
163
- include_legacy_global=include_legacy_global,
164
- )
165
- for item in scored_results:
166
- item["related_concepts"] = self.filter_scoped_nodes(
167
- item.get("related_concepts", []),
168
- allowed_workspaces,
169
- include_legacy_global=include_legacy_global,
170
- )
171
- scored_results.sort(key=lambda x: x["hybrid_score"], reverse=True)
172
- return scored_results[:limit]
173
-
174
- def multi_hop_context(
175
- self,
176
- node_ids: List[str],
177
- max_hops: int = 2,
178
- *,
179
- allowed_workspaces=None,
180
- include_legacy_global: bool = False,
181
- ) -> Dict[str, Any]:
182
- """Multi-hop graph traversal from seed nodes for richer context."""
183
- visited_nodes = set()
184
- visited_edges = set()
185
- all_nodes = []
186
- all_edges = []
187
- frontier = set(node_ids)
188
- nt, et = self._read_tables()
189
-
190
- with self._connect() as conn:
191
- for hop in range(max_hops):
192
- if not frontier:
193
- break
194
- next_frontier = set()
195
- for nid in frontier:
196
- if nid in visited_nodes:
197
- continue
198
- visited_nodes.add(nid)
199
- row = conn.execute(
200
- f"SELECT id, type, title, summary, metadata_json, updated_at FROM {nt} WHERE id=?",
201
- (nid,),
202
- ).fetchone()
203
- if row:
204
- all_nodes.append(
205
- {
206
- "id": row["id"],
207
- "type": row["type"],
208
- "title": row["title"],
209
- "summary": row["summary"],
210
- "metadata": _safe_loads(row["metadata_json"]),
211
- "hop": hop,
212
- }
213
- )
214
- edge_rows = conn.execute(
215
- f"""
216
- SELECT id, from_node, to_node, type, weight
217
- FROM {et} WHERE from_node=? OR to_node=?
218
- ORDER BY id ASC
219
- """,
220
- (nid, nid),
221
- ).fetchall()
222
- for er in edge_rows:
223
- if er["id"] not in visited_edges:
224
- visited_edges.add(er["id"])
225
- all_edges.append(
226
- {
227
- "from": er["from_node"],
228
- "to": er["to_node"],
229
- "type": er["type"],
230
- "weight": er["weight"],
231
- }
232
- )
233
- other = (
234
- er["to_node"]
235
- if er["from_node"] == nid
236
- else er["from_node"]
237
- )
238
- if other not in visited_nodes:
239
- next_frontier.add(other)
240
- frontier = next_frontier
241
-
242
- if allowed_workspaces is not None:
243
- all_nodes = self.filter_scoped_nodes(
244
- all_nodes,
245
- allowed_workspaces,
246
- include_legacy_global=include_legacy_global,
247
- )
248
- visible_ids = {node.get("id") for node in all_nodes}
249
- all_edges = [
250
- edge for edge in all_edges
251
- if edge.get("from") in visible_ids and edge.get("to") in visible_ids
252
- ]
253
- return {"nodes": all_nodes, "edges": all_edges}
@@ -1,180 +0,0 @@
1
- """Single retrieval policy consulted by both hybrid fusion layers.
2
-
3
- Review 2026-07-25 (UX/harness/loop/KG-RAG), Wave 0.2 — "RetrievalPolicy
4
- 단일화" + "query rewrite" + "recency age decay". Before this module the two
5
- hybrid entry points (``KnowledgeGraphStore.hybrid_search`` two-channel
6
- alpha fusion and ``SearchService.hybrid_search`` three-channel weight
7
- fusion) each resolved :func:`lattice_brain.graph.fusion.fusion_profile`
8
- independently and had no shared place to hang retrieval-wide behavior.
9
- This module is that single place:
10
-
11
- * :func:`rewrite_query` — deterministic, rule-based ko/en normalization
12
- (NO LLM). Strips a conservative fixed list of leading/trailing politeness
13
- fillers ("~좀 알려줘", "궁금해", "뭐였지", "what is", "please", …) and
14
- collapses repeated whitespace. Code-class queries pass through untouched
15
- (except whitespace collapse) because exact identifiers/filenames ARE the
16
- retrieval signal. ``LATTICEAI_QUERY_REWRITE=0`` disables rewriting.
17
- * :func:`resolve_policy` — composes the rewrite with
18
- :func:`~lattice_brain.graph.fusion.fusion_profile` (the weight table is
19
- NOT duplicated here) and adds ``recency_half_life_days``, which is
20
- non-``None`` ONLY for the ``recency`` query class — the honest signal
21
- that age decay applies exactly where the fusion layers wire it.
22
-
23
- Both functions are pure, deterministic, and never raise; the ``fact``
24
- query class resolves to the historical default weights, so fact-class
25
- behavior stays byte-compatible. Scores are never combined with ``or``
26
- (score ``0.0`` is falsy but valid).
27
- """
28
-
29
- from __future__ import annotations
30
-
31
- import os
32
- import re
33
- from typing import Any, Dict, List, Mapping, Optional
34
-
35
- from .fusion import classify_query, fusion_profile
36
-
37
- QUERY_REWRITE_ENV = "LATTICEAI_QUERY_REWRITE"
38
-
39
- # Half-life (days) for the recency-class age decay. Matches the default of
40
- # lattice_brain.graph._kg_fsutil._recency_score, which the fusion layers use
41
- # to compute the actual multiplier — this module only declares the policy.
42
- RECENCY_HALF_LIFE_DAYS = 14.0
43
-
44
- # A filler strip is applied only when the remaining query keeps at least this
45
- # many characters — never rewrite a query down to (near-)nothing.
46
- _MIN_REMAINDER_CHARS = 4
47
-
48
- _WS_RE = re.compile(r"\s+")
49
-
50
- # Conservative trailing Korean politeness/filler phrases ("… 좀 알려줘",
51
- # "… 궁금해", "… 뭐였지"). Anchored at end-of-string, must start the query or
52
- # follow whitespace so word interiors are never clipped.
53
- _KO_FILLER_TAIL_RE = re.compile(
54
- r"(?:^|(?<=\s))(?:좀\s+)?"
55
- r"(?:알려\s*줘요?|알려\s*주세요|알려\s*줄래요?"
56
- r"|말해\s*줘요?|말해\s*주세요"
57
- r"|설명해\s*줘요?|설명해\s*주세요"
58
- r"|궁금해요?|궁금합니다"
59
- r"|뭐였지|뭐였더라|뭐더라|뭐지|뭐야"
60
- r")\s*[?!.…~]*\s*$"
61
- )
62
-
63
- # Conservative leading English filler ("what is …", "tell me about …",
64
- # optionally prefixed with "please").
65
- _EN_FILLER_LEAD_RE = re.compile(
66
- r"^(?:please\s+)?(?:tell\s+me\s+about|what\s+is|what\s+was|what\s+are|what's)\s+",
67
- re.IGNORECASE,
68
- )
69
-
70
- # Conservative trailing English politeness ("…, please?").
71
- _EN_FILLER_TAIL_RE = re.compile(r"[,\s]*\bplease\b\s*[?!.]*\s*$", re.IGNORECASE)
72
-
73
- _FILLER_RULES = (
74
- ("strip_filler_ko", _KO_FILLER_TAIL_RE),
75
- ("strip_filler_en_leading", _EN_FILLER_LEAD_RE),
76
- ("strip_filler_en_trailing", _EN_FILLER_TAIL_RE),
77
- )
78
-
79
-
80
- def _rewrite_enabled() -> bool:
81
- raw = os.getenv(QUERY_REWRITE_ENV, "").strip().lower()
82
- return raw not in {"0", "false", "no", "off"}
83
-
84
-
85
- def rewrite_query(query: Any) -> Dict[str, Any]:
86
- """Deterministic rule-based ko/en query normalization (never raises).
87
-
88
- Returns ``{"original": str, "rewritten": str, "rules": [rule names]}``.
89
- Only three conservative rules exist: whitespace collapse, trailing
90
- Korean filler strip, and leading/trailing English filler strip — each
91
- filler strip applies only when the remainder keeps >= 4 characters.
92
- Code-class queries (exact identifiers/filenames are the signal) are
93
- returned unchanged apart from whitespace collapse. The
94
- ``LATTICEAI_QUERY_REWRITE=0`` kill-switch disables rewriting entirely
95
- (``rewritten == original``). Empty input yields empty output.
96
- """
97
- try:
98
- text = str(query or "").strip()
99
- except Exception: # noqa: BLE001 — rewrite must never raise
100
- return {"original": "", "rewritten": "", "rules": []}
101
- if not text:
102
- return {"original": "", "rewritten": "", "rules": []}
103
- original = text
104
- if not _rewrite_enabled():
105
- return {"original": original, "rewritten": original, "rules": []}
106
-
107
- rules: List[str] = []
108
- rewritten = _WS_RE.sub(" ", text).strip()
109
- if rewritten != original:
110
- rules.append("collapse_whitespace")
111
-
112
- if classify_query(original) == "code":
113
- # Exact identifiers/filenames are the retrieval signal — never touch
114
- # them (whitespace collapse is the only permitted normalization).
115
- return {"original": original, "rewritten": rewritten, "rules": rules}
116
-
117
- for name, pattern in _FILLER_RULES:
118
- candidate = pattern.sub("", rewritten).strip()
119
- if candidate != rewritten and len(candidate) >= _MIN_REMAINDER_CHARS:
120
- rewritten = candidate
121
- rules.append(name)
122
- return {"original": original, "rewritten": rewritten, "rules": rules}
123
-
124
-
125
- def resolve_policy(
126
- query: Any,
127
- *,
128
- overrides: Optional[Mapping[str, Mapping[str, float]]] = None,
129
- ) -> Dict[str, Any]:
130
- """Resolve the full retrieval policy for ``query`` (never raises).
131
-
132
- Composes :func:`rewrite_query` with
133
- :func:`~lattice_brain.graph.fusion.fusion_profile` (classification runs
134
- on the ORIGINAL query, so ``query_class`` / ``weights`` / ``alpha`` are
135
- byte-identical to calling ``fusion_profile`` directly, as both hybrid
136
- layers did before this module). Returns::
137
-
138
- {
139
- "query_class": "fact" | "code" | "person" | "recency",
140
- "weights": {"keyword", "vector", "graph"}, # service fusion
141
- "alpha": float, # graph-layer fusion
142
- "fusion_strategy": "alpha" | "rrf", # how they combine
143
- "original_query": str,
144
- "search_query": str, # the rewritten form to search with
145
- "rewrite_rules": [str],
146
- "recency_half_life_days": float | None, # 14.0 only for recency
147
- }
148
-
149
- ``fusion_strategy`` is ``"alpha"`` for every class unless
150
- ``LATTICEAI_FUSION_STRATEGY`` says otherwise, so the default policy is
151
- byte-identical to the pre-11.1.0 one.
152
-
153
- ``recency_half_life_days`` is non-``None`` only for the ``recency``
154
- class — the honest contract that age decay applies exactly where the
155
- fusion layers wire it, and nowhere else.
156
- """
157
- rewrite = rewrite_query(query)
158
- profile = fusion_profile(rewrite["original"], overrides=overrides)
159
- query_class = profile["query_class"]
160
- search_query = rewrite["rewritten"] if rewrite["rewritten"] else rewrite["original"]
161
- return {
162
- "query_class": query_class,
163
- "weights": dict(profile["weights"]),
164
- "alpha": float(profile["alpha"]),
165
- "fusion_strategy": str(profile["strategy"]),
166
- "original_query": rewrite["original"],
167
- "search_query": search_query,
168
- "rewrite_rules": list(rewrite["rules"]),
169
- "recency_half_life_days": (
170
- RECENCY_HALF_LIFE_DAYS if query_class == "recency" else None
171
- ),
172
- }
173
-
174
-
175
- __all__ = [
176
- "QUERY_REWRITE_ENV",
177
- "RECENCY_HALF_LIFE_DAYS",
178
- "resolve_policy",
179
- "rewrite_query",
180
- ]