ltcai 11.5.1 → 11.6.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 (473) hide show
  1. package/README.md +92 -117
  2. package/bin/ltcai.js +234 -24
  3. package/docs/CHANGELOG.md +129 -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/HYBRID_CLOUD_KG_STREAMING.md +8 -4
  8. package/docs/MULTI_AGENT_RUNTIME.md +12 -5
  9. package/docs/ONBOARDING.md +1 -1
  10. package/docs/OPERATIONS.md +6 -3
  11. package/docs/REALTIME_COLLABORATION.md +1 -1
  12. package/docs/TRUST_MODEL.md +1 -1
  13. package/docs/WHY_LATTICE.md +1 -1
  14. package/docs/WORKFLOW_DESIGNER.md +3 -2
  15. package/docs/architecture.md +4 -4
  16. package/docs/kg-schema.md +1 -1
  17. package/docs/v11.5.2_TIGHT_SHIP_PLAN.md +144 -0
  18. package/docs/v11.6.0_ONE_DOOR_PLAN.md +170 -0
  19. package/lattice_brain/__init__.py +42 -79
  20. package/lattice_brain/graph/__init__.py +9 -24
  21. package/lattice_brain/graph/_kg_common/__init__.py +13 -24
  22. package/lattice_brain/ingestion/__init__.py +19 -58
  23. package/lattice_brain/ingestion/pipeline.py +20 -398
  24. package/lattice_brain/multimodal/__init__.py +7 -18
  25. package/lattice_brain/multimodal/images.py +6 -264
  26. package/lattice_brain/multimodal/video.py +8 -247
  27. package/lattice_brain/runtime/__init__.py +8 -79
  28. package/lattice_brain/runtime/hooks.py +22 -597
  29. package/latticeai/__init__.py +1 -1
  30. package/latticeai/api/agent_worker_seam.py +7 -80
  31. package/latticeai/api/health.py +26 -20
  32. package/latticeai/api/local_files.py +25 -608
  33. package/latticeai/api/models.py +12 -87
  34. package/latticeai/api/search.py +17 -231
  35. package/latticeai/api/tools.py +69 -750
  36. package/latticeai/api/voice_capture.py +8 -70
  37. package/latticeai/api/worker_compute.py +842 -0
  38. package/latticeai/api/worker_seams.py +216 -0
  39. package/latticeai/app_factory.py +23 -232
  40. package/latticeai/cli/entrypoint.py +36 -249
  41. package/latticeai/cli/runtime.py +7 -3
  42. package/latticeai/core/agent_permission.py +16 -91
  43. package/latticeai/core/config.py +47 -3
  44. package/latticeai/core/csrf.py +28 -2
  45. package/latticeai/core/embedding_providers/__init__.py +3 -5
  46. package/latticeai/core/embedding_providers/base.py +1 -1
  47. package/latticeai/core/embedding_providers/text.py +1 -1
  48. package/latticeai/core/http_origin.py +146 -0
  49. package/latticeai/core/io_utils.py +2 -11
  50. package/latticeai/core/messages.py +94 -531
  51. package/latticeai/core/model_compat.py +2 -8
  52. package/latticeai/core/module_probe.py +37 -0
  53. package/latticeai/core/security.py +13 -0
  54. package/latticeai/core/sessions.py +3 -2
  55. package/latticeai/core/sse.py +25 -0
  56. package/latticeai/core/users.py +0 -9
  57. package/latticeai/models/router/documents.py +0 -35
  58. package/latticeai/models/router/registry.py +0 -4
  59. package/latticeai/runtime/access_runtime.py +16 -18
  60. package/latticeai/runtime/bootstrap.py +10 -21
  61. package/latticeai/runtime/brain_runtime.py +35 -43
  62. package/latticeai/runtime/build_phases/__init__.py +21 -37
  63. package/latticeai/runtime/build_phases/features.py +101 -389
  64. package/latticeai/runtime/build_phases/foundation.py +82 -392
  65. package/latticeai/runtime/build_phases/web.py +91 -406
  66. package/latticeai/runtime/build_phases/worker_profile.py +244 -0
  67. package/latticeai/runtime/lifespan_runtime.py +7 -16
  68. package/latticeai/runtime/platform_services_runtime.py +1 -15
  69. package/latticeai/runtime/runtime_context.py +14 -143
  70. package/latticeai/runtime/security_runtime.py +10 -103
  71. package/latticeai/services/architecture_readiness.py +82 -43
  72. package/latticeai/services/model_capability_registry.py +0 -15
  73. package/latticeai/services/model_catalog.py +0 -7
  74. package/latticeai/services/model_loading.py +3 -2
  75. package/latticeai/services/model_runtime/__init__.py +2 -68
  76. package/latticeai/services/model_runtime/engines.py +27 -128
  77. package/latticeai/services/model_runtime/loading.py +2 -2
  78. package/latticeai/services/model_runtime/service.py +4 -28
  79. package/latticeai/services/p_reinforce.py +10 -261
  80. package/latticeai/services/product_readiness.py +31 -38
  81. package/latticeai/services/search_service.py +37 -795
  82. package/latticeai/services/tool_dispatch.py +30 -390
  83. package/latticeai/services/voice_capture.py +13 -107
  84. package/latticeai/tools/__init__.py +22 -49
  85. package/latticeai/tools/commands.py +0 -163
  86. package/latticeai/tools/computer.py +0 -39
  87. package/latticeai/tools/documents.py +1 -134
  88. package/latticeai/tools/filesystem.py +1 -247
  89. package/latticeai/tools/knowledge.py +1 -52
  90. package/latticeai/tools/local_files.py +0 -20
  91. package/latticeai/worker_app.py +75 -0
  92. package/package.json +3 -6
  93. package/requirements.txt +0 -5
  94. package/scripts/agent_eval.py +16 -28
  95. package/scripts/brain_quality_eval.py +20 -183
  96. package/scripts/bump_version.py +19 -6
  97. package/scripts/check_current_release_docs.mjs +7 -4
  98. package/scripts/check_legacy_debt.mjs +1 -1
  99. package/scripts/check_openapi_drift.mjs +10 -0
  100. package/scripts/check_server_i18n.mjs +2 -18
  101. package/scripts/compose_openapi.py +377 -0
  102. package/scripts/export_openapi.py +32 -4
  103. package/scripts/gen_messages_catalog_fixture.py +302 -0
  104. package/scripts/gen_openapi_fragments.py +365 -0
  105. package/scripts/gen_redact_fixture.py +196 -0
  106. package/scripts/gen_worker_allowlist_fixture.py +115 -0
  107. package/scripts/generate_agent_parity_fixtures.py +33 -14
  108. package/scripts/openapi_route_families.json +2161 -0
  109. package/scripts/release_screen_claims.json +31 -0
  110. package/scripts/run_integration_tests.mjs +134 -29
  111. package/scripts/run_sidecar_e2e.mjs +104 -11
  112. package/scripts/wheel_smoke.py +32 -22
  113. package/src-tauri/Cargo.lock +375 -9
  114. package/src-tauri/Cargo.toml +1 -1
  115. package/src-tauri/src/backend.rs +223 -43
  116. package/src-tauri/src/main.rs +27 -17
  117. package/src-tauri/src/topology.rs +58 -88
  118. package/src-tauri/tauri.conf.json +2 -3
  119. package/static/app/asset-manifest.json +41 -41
  120. package/static/app/assets/{Act-Drs_jd-O.js → Act-CToZnOHz.js} +1 -1
  121. package/static/app/assets/{AdminConsole-BqNx5oF6.js → AdminConsole-D5kbrBu8.js} +1 -1
  122. package/static/app/assets/{Brain-C7bdNmz-.js → Brain-C6zCdv4S.js} +1 -1
  123. package/static/app/assets/{BrainHome-DcTnINcI.js → BrainHome-D4n3EDUt.js} +1 -1
  124. package/static/app/assets/{BrainSignals-Df1MRIPD.js → BrainSignals-CzeZ-vQ9.js} +1 -1
  125. package/static/app/assets/{Capture-Dgsxvnbx.js → Capture-DPcyTczq.js} +1 -1
  126. package/static/app/assets/{Chronicle-BrkTxJK1.js → Chronicle-D3Nx-oWS.js} +1 -1
  127. package/static/app/assets/{CommandPalette-DDziw7Oj.js → CommandPalette-DPPvpp28.js} +1 -1
  128. package/static/app/assets/{Library-CYxeuraA.js → Library-Co2qTu3I.js} +1 -1
  129. package/static/app/assets/{LivingBrain-CNp6mvCm.js → LivingBrain-Bsmzwy8_.js} +1 -1
  130. package/static/app/assets/{ProductFlow-BVsowu3Z.js → ProductFlow-CBLdmesT.js} +1 -1
  131. package/static/app/assets/{ReviewCard-D1LbOfJS.js → ReviewCard-CfR6DLs7.js} +1 -1
  132. package/static/app/assets/{System-DcRAq7wj.js → System-Ce-P2YL6.js} +1 -1
  133. package/static/app/assets/arrow-left-DD5jFGHV.js +1 -0
  134. package/static/app/assets/{bot-BK2xQ9mN.js → bot-C5-XIww2.js} +1 -1
  135. package/static/app/assets/{brain-BZNztFBb.js → brain-CFRJUvdX.js} +1 -1
  136. package/static/app/assets/{button-CjueubVZ.js → button-DI1KcQP-.js} +1 -1
  137. package/static/app/assets/circle-check-CY20XMCh.js +1 -0
  138. package/static/app/assets/{circle-pause-HRjSmpyY.js → circle-pause-C8HHyood.js} +1 -1
  139. package/static/app/assets/{circle-play-CMcKDVwu.js → circle-play-Cufc235E.js} +1 -1
  140. package/static/app/assets/{cpu-B_MyTfYr.js → cpu-DW2_JVsv.js} +1 -1
  141. package/static/app/assets/{download-CTy0EByV.js → download-gbdKTBb8.js} +1 -1
  142. package/static/app/assets/{folder-open-DF6bLhTg.js → folder-open-C9ydyX33.js} +1 -1
  143. package/static/app/assets/{hard-drive-CpWHG77C.js → hard-drive-BxNMzBM3.js} +1 -1
  144. package/static/app/assets/index-BovSWRmQ.css +2 -0
  145. package/static/app/assets/{index-Dd6abJHX.js → index-q9XyDY7A.js} +3 -3
  146. package/static/app/assets/{input-bVgIRN3s.js → input-GLD94Y_K.js} +1 -1
  147. package/static/app/assets/{link-2-bg6CG5Vk.js → link-2-B4Y0gz1S.js} +1 -1
  148. package/static/app/assets/{permissionCopy-DFTf1HDZ.js → permissionCopy-CxrV0fvU.js} +1 -1
  149. package/static/app/assets/{primitives-D7D-sQ3T.js → primitives-2ANdAsu2.js} +1 -1
  150. package/static/app/assets/search-DTxtjQ07.js +1 -0
  151. package/static/app/assets/{share-2-BLzo7U4L.js → share-2-DxArfiuI.js} +1 -1
  152. package/static/app/assets/{shield-alert-BmngqnsJ.js → shield-alert-CgftuFFs.js} +1 -1
  153. package/static/app/assets/{textarea-DOSlAQQ3.js → textarea-BHVBAoCr.js} +1 -1
  154. package/static/app/assets/{useFocusTrap-pZhgeee4.js → useFocusTrap-Dzq2j_OG.js} +1 -1
  155. package/static/app/assets/{useMutation-BMDwNk4I.js → useMutation-BcqfMLNq.js} +1 -1
  156. package/static/app/assets/{useQuery-CSjKttRo.js → useQuery-DBzLKCOv.js} +1 -1
  157. package/static/app/assets/{utils-D3u_yv7B.js → utils-B-Aah1bO.js} +1 -1
  158. package/static/app/assets/{workspace-H3bMjYXC.js → workspace-B6fFhoko.js} +1 -1
  159. package/static/app/index.html +4 -4
  160. package/static/sw.js +1 -1
  161. package/desktop/electron/README.md +0 -9
  162. package/desktop/electron/main.cjs +0 -58
  163. package/desktop/electron/preload.cjs +0 -5
  164. package/lattice_brain/archive.py +0 -522
  165. package/lattice_brain/context.py +0 -325
  166. package/lattice_brain/conversations.py +0 -382
  167. package/lattice_brain/core.py +0 -82
  168. package/lattice_brain/graph/_kg_contract.py +0 -249
  169. package/lattice_brain/graph/curator.py +0 -676
  170. package/lattice_brain/graph/discovery.py +0 -595
  171. package/lattice_brain/graph/discovery_index/__init__.py +0 -35
  172. package/lattice_brain/graph/discovery_index/cleanup.py +0 -182
  173. package/lattice_brain/graph/discovery_index/extract.py +0 -137
  174. package/lattice_brain/graph/discovery_index/scan.py +0 -411
  175. package/lattice_brain/graph/discovery_index/upsert.py +0 -495
  176. package/lattice_brain/graph/documents.py +0 -380
  177. package/lattice_brain/graph/fusion.py +0 -395
  178. package/lattice_brain/graph/identity.py +0 -175
  179. package/lattice_brain/graph/image_vectors.py +0 -230
  180. package/lattice_brain/graph/ingest.py +0 -829
  181. package/lattice_brain/graph/network.py +0 -205
  182. package/lattice_brain/graph/proactive.py +0 -724
  183. package/lattice_brain/graph/projection/__init__.py +0 -42
  184. package/lattice_brain/graph/projection/curation.py +0 -500
  185. package/lattice_brain/graph/projection/v2_schema.py +0 -546
  186. package/lattice_brain/graph/provenance.py +0 -524
  187. package/lattice_brain/graph/rerank.py +0 -163
  188. package/lattice_brain/graph/retrieval/__init__.py +0 -54
  189. package/lattice_brain/graph/retrieval/context.py +0 -197
  190. package/lattice_brain/graph/retrieval/graph_view.py +0 -319
  191. package/lattice_brain/graph/retrieval/hybrid.py +0 -488
  192. package/lattice_brain/graph/retrieval/maintenance.py +0 -121
  193. package/lattice_brain/graph/retrieval/signals.py +0 -95
  194. package/lattice_brain/graph/retrieval_docgen.py +0 -253
  195. package/lattice_brain/graph/retrieval_policy.py +0 -180
  196. package/lattice_brain/graph/retrieval_reads.py +0 -769
  197. package/lattice_brain/graph/retrieval_vector/__init__.py +0 -42
  198. package/lattice_brain/graph/retrieval_vector/fingerprint.py +0 -97
  199. package/lattice_brain/graph/retrieval_vector/indexing.py +0 -347
  200. package/lattice_brain/graph/retrieval_vector/search.py +0 -560
  201. package/lattice_brain/graph/retrieval_vector/status.py +0 -374
  202. package/lattice_brain/graph/schema.py +0 -792
  203. package/lattice_brain/graph/store.py +0 -268
  204. package/lattice_brain/graph/vector_index/__init__.py +0 -85
  205. package/lattice_brain/graph/vector_index/base.py +0 -170
  206. package/lattice_brain/graph/vector_index/brute_force.py +0 -114
  207. package/lattice_brain/graph/vector_index/hnsw.py +0 -293
  208. package/lattice_brain/graph/vector_index/jobs.py +0 -287
  209. package/lattice_brain/graph/vector_index/quantized.py +0 -151
  210. package/lattice_brain/graph/vector_index/selector.py +0 -161
  211. package/lattice_brain/graph/write_master.py +0 -308
  212. package/lattice_brain/ingestion/_contract.py +0 -90
  213. package/lattice_brain/ingestion/folder_scan.py +0 -57
  214. package/lattice_brain/ingestion/folders.py +0 -258
  215. package/lattice_brain/ingestion/jobs_api.py +0 -107
  216. package/lattice_brain/ingestion/routing.py +0 -295
  217. package/lattice_brain/ingestion_jobs.py +0 -383
  218. package/lattice_brain/memory.py +0 -102
  219. package/lattice_brain/portability/__init__.py +0 -90
  220. package/lattice_brain/portability/_contract.py +0 -42
  221. package/lattice_brain/portability/backups.py +0 -338
  222. package/lattice_brain/portability/bundles.py +0 -136
  223. package/lattice_brain/portability/constants.py +0 -93
  224. package/lattice_brain/portability/fsops.py +0 -138
  225. package/lattice_brain/portability/service.py +0 -41
  226. package/lattice_brain/portability/sharing.py +0 -714
  227. package/lattice_brain/quality.py +0 -592
  228. package/lattice_brain/retrieval_benchmark_fixtures.py +0 -92
  229. package/lattice_brain/runtime/agent_runtime.py +0 -859
  230. package/lattice_brain/runtime/contracts.py +0 -460
  231. package/lattice_brain/runtime/multi_agent.py +0 -942
  232. package/lattice_brain/runtime/statuses.py +0 -10
  233. package/lattice_brain/sealed_box.py +0 -244
  234. package/lattice_brain/self_model.py +0 -675
  235. package/lattice_brain/sensitivity.py +0 -94
  236. package/lattice_brain/storage/__init__.py +0 -22
  237. package/lattice_brain/storage/base.py +0 -100
  238. package/lattice_brain/storage/docker.py +0 -105
  239. package/lattice_brain/storage/factory.py +0 -31
  240. package/lattice_brain/storage/migration.py +0 -191
  241. package/lattice_brain/storage/postgres.py +0 -123
  242. package/lattice_brain/storage/sqlite.py +0 -143
  243. package/lattice_brain/synthesis.py +0 -824
  244. package/lattice_brain/workflow.py +0 -497
  245. package/latticeai/api/admin.py +0 -471
  246. package/latticeai/api/agent_registry.py +0 -105
  247. package/latticeai/api/agents.py +0 -228
  248. package/latticeai/api/auth.py +0 -360
  249. package/latticeai/api/automation_intelligence.py +0 -401
  250. package/latticeai/api/brain_intelligence.py +0 -199
  251. package/latticeai/api/browser.py +0 -493
  252. package/latticeai/api/change_proposals.py +0 -89
  253. package/latticeai/api/chat.py +0 -572
  254. package/latticeai/api/chat_agent_http.py +0 -901
  255. package/latticeai/api/chat_contracts.py +0 -73
  256. package/latticeai/api/chat_documents.py +0 -276
  257. package/latticeai/api/chat_helpers.py +0 -463
  258. package/latticeai/api/chat_history.py +0 -101
  259. package/latticeai/api/chat_hybrid.py +0 -113
  260. package/latticeai/api/chat_intents.py +0 -646
  261. package/latticeai/api/chat_stream.py +0 -216
  262. package/latticeai/api/chronicle.py +0 -63
  263. package/latticeai/api/command_center.py +0 -51
  264. package/latticeai/api/computer_use.py +0 -485
  265. package/latticeai/api/evidence_actions.py +0 -48
  266. package/latticeai/api/features.py +0 -70
  267. package/latticeai/api/funnel_metrics.py +0 -31
  268. package/latticeai/api/garden.py +0 -34
  269. package/latticeai/api/hooks.py +0 -165
  270. package/latticeai/api/index_jobs.py +0 -145
  271. package/latticeai/api/invitations.py +0 -100
  272. package/latticeai/api/knowledge_graph.py +0 -536
  273. package/latticeai/api/marketplace.py +0 -105
  274. package/latticeai/api/mcp.py +0 -482
  275. package/latticeai/api/memory.py +0 -270
  276. package/latticeai/api/network.py +0 -81
  277. package/latticeai/api/network_boundary.py +0 -225
  278. package/latticeai/api/permission_mode.py +0 -61
  279. package/latticeai/api/permissions.py +0 -410
  280. package/latticeai/api/plugins.py +0 -126
  281. package/latticeai/api/portability.py +0 -391
  282. package/latticeai/api/project_sessions.py +0 -114
  283. package/latticeai/api/realtime.py +0 -118
  284. package/latticeai/api/review_queue.py +0 -364
  285. package/latticeai/api/security_dashboard.py +0 -604
  286. package/latticeai/api/setup.py +0 -319
  287. package/latticeai/api/static_routes.py +0 -351
  288. package/latticeai/api/ui_redirects.py +0 -26
  289. package/latticeai/api/workflow_designer.py +0 -394
  290. package/latticeai/api/workspace.py +0 -856
  291. package/latticeai/api/workspace_scope.py +0 -125
  292. package/latticeai/core/agent/__init__.py +0 -93
  293. package/latticeai/core/agent/_contract.py +0 -79
  294. package/latticeai/core/agent/context.py +0 -57
  295. package/latticeai/core/agent/deps.py +0 -125
  296. package/latticeai/core/agent/execution.py +0 -622
  297. package/latticeai/core/agent/planning.py +0 -145
  298. package/latticeai/core/agent/recovery.py +0 -157
  299. package/latticeai/core/agent/runtime.py +0 -210
  300. package/latticeai/core/agent/verification.py +0 -231
  301. package/latticeai/core/agent_eval.py +0 -739
  302. package/latticeai/core/agent_helpers.py +0 -493
  303. package/latticeai/core/agent_profiles.py +0 -110
  304. package/latticeai/core/agent_prompts.py +0 -171
  305. package/latticeai/core/agent_registry.py +0 -232
  306. package/latticeai/core/agent_state.py +0 -41
  307. package/latticeai/core/agent_trace.py +0 -104
  308. package/latticeai/core/artifact_ledger.py +0 -109
  309. package/latticeai/core/audit.py +0 -260
  310. package/latticeai/core/builtin_hooks.py +0 -105
  311. package/latticeai/core/context_builder.py +0 -394
  312. package/latticeai/core/document_generator.py +0 -103
  313. package/latticeai/core/enterprise.py +0 -157
  314. package/latticeai/core/enterprise_admin.py +0 -158
  315. package/latticeai/core/file_generation/__init__.py +0 -115
  316. package/latticeai/core/file_generation/bundles.py +0 -76
  317. package/latticeai/core/file_generation/extraction.py +0 -154
  318. package/latticeai/core/file_generation/inference.py +0 -235
  319. package/latticeai/core/file_generation/orchestration.py +0 -152
  320. package/latticeai/core/file_generation/prompting.py +0 -117
  321. package/latticeai/core/file_generation/repair.py +0 -114
  322. package/latticeai/core/file_generation/sanitize.py +0 -61
  323. package/latticeai/core/file_generation/validation.py +0 -201
  324. package/latticeai/core/graph_curator.py +0 -11
  325. package/latticeai/core/hooks.py +0 -11
  326. package/latticeai/core/invitations.py +0 -131
  327. package/latticeai/core/legacy_compatibility.py +0 -243
  328. package/latticeai/core/local_embeddings.py +0 -104
  329. package/latticeai/core/logging_safety.py +0 -46
  330. package/latticeai/core/marketplace.py +0 -293
  331. package/latticeai/core/mcp_catalog.py +0 -452
  332. package/latticeai/core/mcp_registry.py +0 -506
  333. package/latticeai/core/multi_agent.py +0 -11
  334. package/latticeai/core/network_boundary.py +0 -168
  335. package/latticeai/core/oidc.py +0 -208
  336. package/latticeai/core/plugins.py +0 -432
  337. package/latticeai/core/product_hardening.py +0 -218
  338. package/latticeai/core/project_sessions.py +0 -337
  339. package/latticeai/core/realtime.py +0 -238
  340. package/latticeai/core/run_explain.py +0 -423
  341. package/latticeai/core/run_store.py +0 -252
  342. package/latticeai/core/timezones.py +0 -80
  343. package/latticeai/core/workflow_engine.py +0 -11
  344. package/latticeai/core/workspace_computer_memory.py +0 -84
  345. package/latticeai/core/workspace_graph_trace.py +0 -161
  346. package/latticeai/core/workspace_indexing.py +0 -102
  347. package/latticeai/core/workspace_memory.py +0 -77
  348. package/latticeai/core/workspace_onboarding.py +0 -104
  349. package/latticeai/core/workspace_os.py +0 -988
  350. package/latticeai/core/workspace_os_constants.py +0 -126
  351. package/latticeai/core/workspace_os_state.py +0 -180
  352. package/latticeai/core/workspace_os_utils.py +0 -69
  353. package/latticeai/core/workspace_permissions.py +0 -101
  354. package/latticeai/core/workspace_plugins.py +0 -97
  355. package/latticeai/core/workspace_relationships.py +0 -99
  356. package/latticeai/core/workspace_reorganization.py +0 -335
  357. package/latticeai/core/workspace_review_items.py +0 -118
  358. package/latticeai/core/workspace_runs.py +0 -732
  359. package/latticeai/core/workspace_skills.py +0 -114
  360. package/latticeai/core/workspace_snapshots.py +0 -198
  361. package/latticeai/core/workspace_timeline.py +0 -110
  362. package/latticeai/integrations/__init__.py +0 -0
  363. package/latticeai/integrations/telegram_bot/__init__.py +0 -123
  364. package/latticeai/integrations/telegram_bot/__main__.py +0 -17
  365. package/latticeai/integrations/telegram_bot/config.py +0 -86
  366. package/latticeai/integrations/telegram_bot/dispatch.py +0 -311
  367. package/latticeai/integrations/telegram_bot/flows.py +0 -478
  368. package/latticeai/integrations/telegram_bot/helpers.py +0 -322
  369. package/latticeai/integrations/telegram_bot/screens.py +0 -394
  370. package/latticeai/runtime/audit_runtime.py +0 -76
  371. package/latticeai/runtime/automation_runtime.py +0 -81
  372. package/latticeai/runtime/chat_wiring.py +0 -141
  373. package/latticeai/runtime/context_runtime.py +0 -66
  374. package/latticeai/runtime/feature_toggle_wiring.py +0 -163
  375. package/latticeai/runtime/history_runtime.py +0 -163
  376. package/latticeai/runtime/history_writer.py +0 -138
  377. package/latticeai/runtime/hooks_runtime.py +0 -77
  378. package/latticeai/runtime/model_wiring.py +0 -68
  379. package/latticeai/runtime/namespace_runtime.py +0 -163
  380. package/latticeai/runtime/network_boundary_wiring.py +0 -128
  381. package/latticeai/runtime/network_config_runtime.py +0 -56
  382. package/latticeai/runtime/permission_mode_wiring.py +0 -119
  383. package/latticeai/runtime/persistence_runtime.py +0 -159
  384. package/latticeai/runtime/platform_runtime_wiring.py +0 -89
  385. package/latticeai/runtime/review_wiring.py +0 -42
  386. package/latticeai/runtime/router_registration.py +0 -689
  387. package/latticeai/runtime/sso_config_runtime.py +0 -128
  388. package/latticeai/runtime/user_key_runtime.py +0 -106
  389. package/latticeai/runtime/web_runtime.py +0 -92
  390. package/latticeai/server_app.py +0 -51
  391. package/latticeai/services/app_context.py +0 -130
  392. package/latticeai/services/automation_execution.py +0 -266
  393. package/latticeai/services/automation_intelligence.py +0 -614
  394. package/latticeai/services/brain_automation.py +0 -191
  395. package/latticeai/services/brain_intelligence/__init__.py +0 -58
  396. package/latticeai/services/brain_intelligence/_contract.py +0 -71
  397. package/latticeai/services/brain_intelligence/consistency.py +0 -193
  398. package/latticeai/services/brain_intelligence/constants.py +0 -47
  399. package/latticeai/services/brain_intelligence/digest.py +0 -258
  400. package/latticeai/services/brain_intelligence/health.py +0 -331
  401. package/latticeai/services/brain_intelligence/proposals.py +0 -264
  402. package/latticeai/services/brain_intelligence/sampling.py +0 -84
  403. package/latticeai/services/brain_intelligence/service.py +0 -48
  404. package/latticeai/services/change_proposals.py +0 -505
  405. package/latticeai/services/chat_service.py +0 -243
  406. package/latticeai/services/chronicle.py +0 -557
  407. package/latticeai/services/cloud_egress_audit.py +0 -85
  408. package/latticeai/services/cloud_extraction.py +0 -129
  409. package/latticeai/services/cloud_streaming.py +0 -268
  410. package/latticeai/services/cloud_token_guard.py +0 -84
  411. package/latticeai/services/command_center.py +0 -548
  412. package/latticeai/services/evidence_actions.py +0 -255
  413. package/latticeai/services/feature_toggles.py +0 -502
  414. package/latticeai/services/folder_watch.py +0 -520
  415. package/latticeai/services/funnel_metrics.py +0 -307
  416. package/latticeai/services/hybrid_chat.py +0 -316
  417. package/latticeai/services/hybrid_context.py +0 -228
  418. package/latticeai/services/hybrid_policy.py +0 -178
  419. package/latticeai/services/ingestion.py +0 -11
  420. package/latticeai/services/interop_bridges.py +0 -978
  421. package/latticeai/services/kg_portability.py +0 -11
  422. package/latticeai/services/local_knowledge.py +0 -465
  423. package/latticeai/services/memory_service/__init__.py +0 -52
  424. package/latticeai/services/memory_service/_contract.py +0 -100
  425. package/latticeai/services/memory_service/brief.py +0 -431
  426. package/latticeai/services/memory_service/constants.py +0 -57
  427. package/latticeai/services/memory_service/maintenance.py +0 -138
  428. package/latticeai/services/memory_service/manager.py +0 -186
  429. package/latticeai/services/memory_service/proof.py +0 -136
  430. package/latticeai/services/memory_service/recall.py +0 -225
  431. package/latticeai/services/memory_service/service.py +0 -48
  432. package/latticeai/services/memory_service/stores.py +0 -110
  433. package/latticeai/services/model_recommendation.py +0 -224
  434. package/latticeai/services/model_runtime/cloud.py +0 -87
  435. package/latticeai/services/multimodal_streaming.py +0 -129
  436. package/latticeai/services/network_boundary_service.py +0 -154
  437. package/latticeai/services/obsidian_bridge.py +0 -609
  438. package/latticeai/services/openai_compatible_adapter.py +0 -101
  439. package/latticeai/services/permission_mode_service.py +0 -170
  440. package/latticeai/services/platform_runtime.py +0 -366
  441. package/latticeai/services/review_queue.py +0 -380
  442. package/latticeai/services/router_context.py +0 -59
  443. package/latticeai/services/run_executor.py +0 -387
  444. package/latticeai/services/self_model_service.py +0 -171
  445. package/latticeai/services/setup_detection.py +0 -81
  446. package/latticeai/services/triggers.py +0 -378
  447. package/latticeai/services/upload_service.py +0 -185
  448. package/latticeai/services/workspace_service.py +0 -171
  449. package/latticeai/setup/__init__.py +0 -25
  450. package/latticeai/setup/auto_setup.py +0 -857
  451. package/latticeai/setup/demo_corpus.py +0 -98
  452. package/latticeai/setup/wizard/__init__.py +0 -126
  453. package/latticeai/setup/wizard/catalog.py +0 -172
  454. package/latticeai/setup/wizard/detect.py +0 -323
  455. package/latticeai/setup/wizard/install.py +0 -348
  456. package/latticeai/setup/wizard/paths.py +0 -168
  457. package/latticeai/setup/wizard/plans.py +0 -74
  458. package/latticeai/setup/wizard/recommend.py +0 -320
  459. package/scripts/bench_agent_smoke.py +0 -409
  460. package/scripts/bench_models.py +0 -540
  461. package/scripts/bench_vector_index.py +0 -295
  462. package/scripts/funnel_soft_gate.py +0 -192
  463. package/scripts/generate_agent_loop_fixtures.py +0 -907
  464. package/scripts/generate_rust_parity_fixtures.py +0 -1000
  465. package/scripts/measure_brain_home_fill.mjs +0 -141
  466. package/scripts/migrate_brain_storage.py +0 -57
  467. package/scripts/parity_fixture_corpus_docgen.py +0 -341
  468. package/scripts/profile_kg.py +0 -355
  469. package/server.py +0 -30
  470. package/static/app/assets/arrow-left-BHYmOTWU.js +0 -1
  471. package/static/app/assets/circle-check-CLeWJr67.js +0 -1
  472. package/static/app/assets/index-DxmOfNRi.css +0 -2
  473. package/static/app/assets/search-BgSVX6NG.js +0 -1
@@ -1,105 +1,58 @@
1
- """The single normalized entrypoint every source is fed through.
1
+ """What this machine will do with a picture, a recording, or a video.
2
2
 
3
3
  Source → normalize → content hash → (file | text) ingest → provenance
4
4
 
5
- :meth:`IngestionPipeline.ingest` owns normalization, the gate reads, idempotency
6
- reporting, provenance capture, the advisory quality annotation, and routing
7
- every ingest through the shared ``dispatch_tool`` lifecycle so ``pre_tool`` /
8
- ``post_tool`` hooks fire on data ingestion exactly as they do on tool calls.
9
- The per-source doors live in ``routing.py``, the folder walk in ``folders.py``
10
- and background scheduling in ``jobs_api.py``; this module is what they compose
11
- into, and it owns the constructor state all three read.
5
+ That was this module: :meth:`IngestionPipeline.ingest` owned normalization, the
6
+ gate reads, idempotency reporting, provenance capture, the advisory quality
7
+ annotation, and routing every ingest through the shared ``dispatch_tool``
8
+ lifecycle. The per-source doors lived in ``routing.py``, the folder walk in
9
+ ``folders.py`` and background scheduling in ``jobs_api.py``.
10
+
11
+ v11.6.0 §Wave 2.5 made Rust the single writer of the Brain, so the whole write
12
+ side moved to ``lattice-core``'s graph write engine and ``lattice-jobs``. What
13
+ a stateless worker still owns is the *capability question* behind
14
+ ``GET /api/ingestion/multimodal``: which gates are on, which model-backed ports
15
+ actually resolved, and — if a video would be refused — which of the three
16
+ reasons applies. The answer is derived from this process's environment and its
17
+ injected ports, which is why it is still answered here and not natively.
12
18
  """
13
19
 
14
20
  from __future__ import annotations
15
21
 
16
- from typing import Any, Dict, List, Optional, Tuple
22
+ from typing import Any, Dict, Optional
17
23
 
18
- from ..graph.vector_index import DEFAULT_TICK_LIMIT as VECTOR_TICK_LIMIT
19
- from ..ingestion_jobs import BackgroundIngestionQueue
20
24
  from ..multimodal import (
21
25
  DEFAULT_KEYFRAMES,
22
- MODALITY_AUDIO,
23
- MODALITY_IMAGE,
24
- MODALITY_VIDEO,
25
26
  VIDEO_UNAVAILABLE_DETAIL,
26
27
  MultimodalPorts,
27
28
  ffmpeg_available,
28
29
  )
29
- from ..quiet import quiet
30
- from ..runtime.hooks import dispatch_tool
31
- from ..utils import utc_now_iso
32
30
  from .constants import (
33
31
  ALLOW_MULTIMODAL_ENV,
34
32
  ALLOW_VIDEO_ENV,
35
- AUTO_VECTOR_INDEX_GATE,
36
- CHAT_SOURCE_TYPES,
37
- DEFAULT_MAX_TEXT_BYTES,
38
- FILE_SOURCE_TYPES,
39
- MEMORY_SOURCE_TYPES,
40
33
  MULTIMODAL_GATE,
41
34
  VIDEO_GATE,
42
35
  )
43
- from .folders import IngestionFolderMixin
44
- from .jobs_api import IngestionJobsMixin
45
- from .models import IngestionItem, IngestionResult
46
- from .quality import QUALITY_LOW_WARNING, assess_extraction_quality
47
- from .routing import IngestionRoutingMixin
48
36
 
49
37
 
50
- class IngestionPipeline(
51
- IngestionRoutingMixin, IngestionFolderMixin, IngestionJobsMixin
52
- ):
53
- """Single normalized entrypoint that feeds every source into the graph."""
38
+ class IngestionPipeline:
39
+ """The multi-modal capability probe. Writes nothing, holds no store."""
54
40
 
55
41
  def __init__(
56
42
  self,
57
- knowledge_graph: Any,
58
43
  *,
59
- hooks: Any = None,
60
- enable_graph: bool = True,
61
- audit: Optional[Any] = None,
62
- max_text_bytes: int = DEFAULT_MAX_TEXT_BYTES,
63
- pipeline_name: str = "unified-ingestion",
64
- bg_queue: Optional[BackgroundIngestionQueue] = None,
65
- auto_vector_index: bool = True,
66
44
  allow_multimodal: bool = False,
67
45
  multimodal: Optional[MultimodalPorts] = None,
68
46
  ) -> None:
69
- self._kg = knowledge_graph
70
- self._hooks = hooks
71
- self._enable = bool(enable_graph)
72
- self._audit = audit
73
- self._max_text_bytes = int(max_text_bytes)
74
- self._pipeline_name = pipeline_name
75
- # Background job state lives in the graph database by default, so a
76
- # restart resumes from the last completed item instead of replaying the
77
- # whole corpus. A store without a usable ``db_path`` (mocks, disabled
78
- # graph) degrades to the historical in-memory queue, which reports
79
- # itself as non-durable through ``BackgroundIngestionQueue.describe()``.
80
- self._bg_queue = bg_queue or BackgroundIngestionQueue(
81
- db_path=getattr(knowledge_graph, "db_path", None)
82
- )
83
- # Incremental vector sync after each successful non-duplicate ingest.
84
- # Constructor opt-out AND gate opt-out (LATTICEAI_AUTO_VECTOR_INDEX=0,
85
- # or the settings toggle bound to it) both disable it; a vector failure
86
- # never fails the ingest. The gate half is asked per ingest rather than
87
- # frozen here, so turning it off takes effect on the next item.
88
- self._auto_vector_index_opt_in = bool(auto_vector_index)
89
47
  # Multi-modal routing. Off unless the caller asks for it *or* the gate
90
48
  # says yes — the env behind that gate is the escape hatch for an
91
49
  # install with no code path to the constructor (CLI, background
92
- # worker), and the gate is now asked per call so a runtime toggle can
93
- # reach it. A constructor ``True`` is still a permanent yes.
50
+ # worker), and the gate is asked per call so a runtime toggle can reach
51
+ # it. A constructor ``True`` is still a permanent yes.
94
52
  self._multimodal_opt_in = bool(allow_multimodal)
95
53
  self._multimodal = multimodal or MultimodalPorts()
96
54
  self._keyframes = DEFAULT_KEYFRAMES
97
55
 
98
- @property
99
- def _auto_vector_index(self) -> bool:
100
- """Whether a landed ingest also syncs its vector, asked *now*."""
101
- return self._auto_vector_index_opt_in and AUTO_VECTOR_INDEX_GATE.enabled()
102
-
103
56
  @property
104
57
  def _allow_multimodal(self) -> bool:
105
58
  """Whether pictures and recordings route by modality, asked *now*."""
@@ -114,9 +67,6 @@ class IngestionPipeline(
114
67
  """An injected keyframe port counts as a decoder; otherwise, ffmpeg."""
115
68
  return self._multimodal.keyframe_extractor is not None or ffmpeg_available()
116
69
 
117
- def available(self) -> bool:
118
- return self._enable and self._kg is not None
119
-
120
70
  def multimodal_status(self) -> Dict[str, Any]:
121
71
  """What this pipeline will do with a picture or a recording, honestly.
122
72
 
@@ -154,333 +104,5 @@ class IngestionPipeline(
154
104
  )
155
105
  return VIDEO_UNAVAILABLE_DETAIL
156
106
 
157
- # ── public API ───────────────────────────────────────────────────────────
158
- def ingest(self, item: IngestionItem, *, user_email: Optional[str] = None) -> IngestionResult:
159
- """Normalize, hash, route through dispatch_tool, and record provenance."""
160
- source_type = str(item.source_type or "text").strip().lower()
161
- if not self.available():
162
- return IngestionResult(
163
- status="unavailable", source_type=source_type,
164
- indexing_status="skipped",
165
- detail="Knowledge Graph is disabled (LATTICEAI_ENABLE_GRAPH).",
166
- )
167
-
168
- # Modality routing is a no-op while the flag is off: ``modality`` stays
169
- # "text" and every branch below behaves exactly as it did before.
170
- modality = self._modality_for(item, source_type)
171
- if modality == MODALITY_VIDEO and not self._allow_video:
172
- # Recognized and refused, with the reason that actually applies
173
- # right now — a missing decoder is not the same answer as a
174
- # switched-off feature, and the caller can act on the difference.
175
- return IngestionResult(
176
- status="unavailable", source_type=source_type,
177
- indexing_status="skipped", detail=self._video_refusal(),
178
- )
179
-
180
- captured_at = item.captured_at or utc_now_iso()
181
- owner = item.owner or user_email
182
- tool_name = f"kg_ingest.{source_type}"
183
- # Only the keys are read by the hook payload, so this dict is safe/cheap.
184
- args = {
185
- "source_type": source_type,
186
- "source_uri": item.source_uri,
187
- "owner": owner,
188
- "workspace_id": item.workspace_id,
189
- }
190
-
191
- def _run() -> Dict[str, Any]:
192
- if source_type in CHAT_SOURCE_TYPES:
193
- return self._ingest_chat(item, source_type=source_type, owner=owner)
194
- if source_type in MEMORY_SOURCE_TYPES:
195
- return self._ingest_memory_record(item, source_type=source_type, owner=owner)
196
- if modality == MODALITY_IMAGE:
197
- return self._ingest_image(item, source_type=source_type, owner=owner, captured_at=captured_at)
198
- if modality == MODALITY_AUDIO:
199
- return self._ingest_audio(item, source_type=source_type, owner=owner, captured_at=captured_at)
200
- if modality == MODALITY_VIDEO:
201
- return self._ingest_video(item, source_type=source_type, owner=owner, captured_at=captured_at)
202
- if source_type in FILE_SOURCE_TYPES or (item.path and not item.text):
203
- return self._ingest_file(item, source_type=source_type, owner=owner, captured_at=captured_at)
204
- return self._ingest_text(item, source_type=source_type, owner=owner, captured_at=captured_at)
205
-
206
- # v9.8.0 observation-only quality gate: computed *before* the write so
207
- # the search never matches the node we are about to create. It is
208
- # recorded on the result and never skips an ingest (behavior unchanged).
209
- quality_text = self._extractable_text(item)
210
- quality_gate = self._observe_quality_gate(
211
- item, source_type=source_type, text=quality_text,
212
- )
213
-
214
- try:
215
- raw = dispatch_tool(
216
- self._hooks, tool_name, args, _run,
217
- user_email=user_email, workspace_id=item.workspace_id, source="ingestion",
218
- )
219
- except PermissionError as exc:
220
- return IngestionResult(
221
- status="blocked", source_type=source_type,
222
- indexing_status="skipped", detail=str(exc),
223
- )
224
- except FileNotFoundError as exc:
225
- return IngestionResult(
226
- status="failed", source_type=source_type,
227
- indexing_status="failed", detail=str(exc),
228
- )
229
- except Exception as exc: # noqa: BLE001 — surface as a failed result, never crash the caller
230
- return IngestionResult(
231
- status="failed", source_type=source_type,
232
- indexing_status="failed", detail=str(exc),
233
- )
234
-
235
- node_id = raw.get("node_id")
236
- content_hash = raw.get("content_hash") or raw.get("sha256")
237
- chunk_ids = list(raw.get("chunk_ids") or [])
238
- title = raw.get("title") or item.title
239
-
240
- # Incremental vector-index sync (opt-in via auto_vector_index +
241
- # LATTICEAI_AUTO_VECTOR_INDEX). Exception-safe by contract: the graph
242
- # write above already landed, so a vector failure only downgrades
243
- # indexing_status to "pending" — index_status()/rebuild_vector_index()
244
- # discover the same node as backlog and pick it up later.
245
- indexing_status = "indexed"
246
- vector_detail: Optional[str] = None
247
- if node_id and self._auto_vector_index and not bool(raw.get("duplicate")):
248
- indexing_status, vector_detail = self._sync_vector_index(node_id)
249
- embedded = bool(self._kg.node_is_embedded(node_id)) if node_id else False
250
-
251
- # Provenance capture must never turn an already-persisted ingest into a
252
- # caller-visible failure: the graph write above succeeded, so a broken
253
- # provenance table degrades the result instead of raising.
254
- provenance_detail: Optional[str] = None
255
- try:
256
- prov = self._kg.record_provenance(
257
- node_id=node_id,
258
- source_type=source_type,
259
- pipeline=self._pipeline_name,
260
- source_uri=item.source_uri,
261
- content_hash=content_hash,
262
- title=title,
263
- owner=owner,
264
- workspace_id=item.workspace_id,
265
- captured_at=captured_at,
266
- modified_at=item.modified_at,
267
- embedded=embedded,
268
- linked=bool(raw.get("source_node_id")),
269
- duplicate=bool(raw.get("duplicate")),
270
- agent_used=item.agent_used,
271
- chunk_count=len(chunk_ids),
272
- permissions=item.permissions,
273
- metadata=item.metadata,
274
- )
275
- except Exception as exc: # noqa: BLE001 — the ingest itself already landed
276
- prov = {}
277
- provenance_detail = f"provenance capture failed: {exc}"
278
- if self._audit is not None:
279
- try:
280
- self._audit(
281
- "kg_ingest",
282
- {
283
- "source_type": source_type, "node_id": node_id,
284
- "content_hash": content_hash, "duplicate": bool(raw.get("duplicate")),
285
- },
286
- user_email,
287
- )
288
- except Exception: # noqa: BLE001 — audit must never break ingestion
289
- quiet()
290
-
291
- # A modality-aware door scores its own extraction (a picture's quality
292
- # is "how much of it can be retrieved", not "does the text read well"),
293
- # so its verdict wins. Text/file doors never set the key and keep the
294
- # historical scoring untouched.
295
- extraction_quality = raw.get("extraction_quality") or self._assess_item_quality(
296
- item, source_type=source_type, text=quality_text, chunk_ids=chunk_ids,
297
- )
298
- warnings: List[str] = []
299
- if extraction_quality is not None and extraction_quality.get("level") == "low":
300
- warnings.append(QUALITY_LOW_WARNING)
301
-
302
- details = [d for d in (provenance_detail, vector_detail) if d]
303
- return IngestionResult(
304
- status="ok",
305
- source_type=source_type,
306
- node_id=node_id,
307
- source_node_id=raw.get("source_node_id"),
308
- content_hash=content_hash,
309
- title=title,
310
- chunk_ids=chunk_ids,
311
- chunk_count=len(chunk_ids),
312
- duplicate=bool(raw.get("duplicate")),
313
- embedded=embedded,
314
- indexing_status=indexing_status,
315
- provenance_id=prov.get("id"),
316
- detail="; ".join(details) if details else None,
317
- extraction_quality=extraction_quality,
318
- warnings=warnings,
319
- quality_gate=quality_gate,
320
- )
321
-
322
- # ── extraction quality (v9.8.0 A1 — advisory, never gates) ───────────────
323
- @staticmethod
324
- def _extractable_text(item: IngestionItem) -> Optional[str]:
325
- """Best available extracted text for quality scoring/gating."""
326
- if item.text is not None:
327
- return item.text
328
- extracted = (item.metadata or {}).get("extracted")
329
- if isinstance(extracted, dict):
330
- content = extracted.get("content") or extracted.get("text")
331
- if content is not None:
332
- return str(content)
333
- return None
334
-
335
- @staticmethod
336
- def _upstream_confidence(item: IngestionItem) -> Optional[Any]:
337
- """Upstream extractor confidence, if the capture surface supplied one."""
338
- meta = item.metadata or {}
339
- extracted = meta.get("extracted")
340
- if isinstance(extracted, dict) and extracted.get("confidence") is not None:
341
- return extracted.get("confidence")
342
- if meta.get("extraction_confidence") is not None:
343
- return meta.get("extraction_confidence")
344
- return None
345
107
 
346
- def _assess_item_quality(
347
- self,
348
- item: IngestionItem,
349
- *,
350
- source_type: str,
351
- text: Optional[str],
352
- chunk_ids: List[str],
353
- ) -> Optional[Dict[str, Any]]:
354
- """Quality annotation for document-like sources (not chat/memory)."""
355
- if source_type in CHAT_SOURCE_TYPES or source_type in MEMORY_SOURCE_TYPES:
356
- return None
357
- confidence = self._upstream_confidence(item)
358
- if text is not None or confidence is not None:
359
- return assess_extraction_quality(
360
- text, source_type=source_type, upstream_confidence=confidence,
361
- )
362
- # File door without inline extraction (e.g. PDF): the pipeline never saw
363
- # the text, so score honestly from the chunk output instead of guessing.
364
- if chunk_ids:
365
- return {
366
- "score": 0.5,
367
- "level": "medium",
368
- "reasons": ["content_extracted_upstream_not_scored"],
369
- }
370
- return {"score": 0.0, "level": "low", "reasons": ["no_extracted_text"]}
371
-
372
- def _observe_quality_gate(
373
- self,
374
- item: IngestionItem,
375
- *,
376
- source_type: str,
377
- text: Optional[str],
378
- ) -> Optional[Dict[str, Any]]:
379
- """Observation-mode ``gate_ingest_candidate`` wiring.
380
-
381
- Records what the proactive gate *would* decide (ingest /
382
- skip_duplicate / review) without ever acting on it. Any failure —
383
- import, search, gate — yields ``None``; the ingest proceeds untouched.
384
- """
385
- if source_type in CHAT_SOURCE_TYPES or source_type in MEMORY_SOURCE_TYPES:
386
- return None
387
- body = str(text or "").strip()
388
- if not body:
389
- return None
390
- try:
391
- from ..graph.proactive import gate_ingest_candidate
392
- except Exception: # noqa: BLE001 — optional observation, never required
393
- return None
394
-
395
- def _search(query: str) -> Any:
396
- snippet = str(query or "")[:400]
397
- try:
398
- if item.workspace_id:
399
- return self._kg.search(
400
- snippet, 20, allowed_workspaces={item.workspace_id},
401
- )
402
- return self._kg.search(snippet, 20)
403
- except TypeError:
404
- # Older store without workspace-scoped search.
405
- return self._kg.search(snippet, 20)
406
-
407
- try:
408
- gate = gate_ingest_candidate(body, _search)
409
- except Exception: # noqa: BLE001 — observation must never fail the ingest
410
- return None
411
- parts = [str(gate.get("reason") or "")]
412
- if gate.get("similarity") is not None:
413
- parts.append(f"similarity={gate.get('similarity')}")
414
- if gate.get("match_id"):
415
- parts.append(f"match={gate.get('match_id')}")
416
- return {
417
- "action": str(gate.get("action") or "review"),
418
- "detail": "; ".join(p for p in parts if p),
419
- }
420
-
421
- def _queue_pending_embed(self, node_id: str, detail: str) -> bool:
422
- """Hand a node the inline sync could not embed to the background queue.
423
-
424
- Before v11.1.0 ``indexing_status="pending"`` was the end of the story:
425
- honest, but nobody was coming back for it, so the node stayed
426
- unsearchable until a human ran a rebuild. The durable queue is who
427
- comes back. A store without one (older stores, mocks) just keeps the
428
- old behaviour — the node is still visible as ``index_status`` backlog.
429
- """
430
- queue = getattr(self._kg, "vector_queue", None)
431
- if queue is None:
432
- return False
433
- try:
434
- return bool(queue.schedule(node_id, detail=detail))
435
- except Exception: # noqa: BLE001 — queueing must never fail an ingest
436
- quiet()
437
- return False
438
-
439
- def _sync_vector_index(self, node_id: str) -> Tuple[str, Optional[str]]:
440
- """Best-effort incremental vector sync → (indexing_status, detail).
441
-
442
- Any failure — missing method on older stores, embedding provider down,
443
- storage error — yields ``("pending", detail)`` so a later
444
- ``rebuild_vector_index`` run picks the node up from the backlog, and
445
- the node is queued for background embedding so that pickup happens on
446
- its own.
447
- """
448
- sync = getattr(self._kg, "index_node_incremental", None)
449
- if not callable(sync):
450
- # Older store without the incremental path: the write-side already
451
- # embeds inline, so nothing extra to do.
452
- return "indexed", None
453
- try:
454
- outcome = sync(node_id) or {}
455
- except Exception as exc: # noqa: BLE001 — vector sync must never fail the ingest
456
- return "pending", self._pending_detail(node_id, f"vector index sync failed: {exc}")
457
- if str(outcome.get("status") or "") == "failed":
458
- reason = outcome.get("detail") or "unknown error"
459
- return "pending", self._pending_detail(
460
- node_id, f"vector index sync failed: {reason}"
461
- )
462
- return "indexed", None
463
-
464
- def _pending_detail(self, node_id: str, reason: str) -> str:
465
- """``reason``, plus whether a background retry was actually scheduled."""
466
- if self._queue_pending_embed(node_id, reason):
467
- return f"{reason}; queued for background embedding"
468
- return reason
469
-
470
- def drain_vector_queue(self, limit: int = VECTOR_TICK_LIMIT) -> Dict[str, Any]:
471
- """Run one background-embedding tick over the store's pending backlog.
472
-
473
- Deliberately caller-driven (a scheduler, a CLI, a test) rather than a
474
- thread this pipeline owns: the queue is durable, so "who runs it" is a
475
- deployment decision, not a property of having ingested something.
476
- """
477
- queue = getattr(self._kg, "vector_queue", None)
478
- if queue is None:
479
- return {
480
- "claimed": 0,
481
- "indexed": 0,
482
- "retried": 0,
483
- "failed": 0,
484
- "detail": "this store has no background vector queue",
485
- }
486
- return dict(queue.tick(limit))
108
+ __all__ = ["IngestionPipeline"]
@@ -34,6 +34,12 @@ Brain Core owns none of those models. Every heavy dependency arrives as an
34
34
  injected callable (:class:`MultimodalPorts`), which is also why this module
35
35
  imports nothing from ``latticeai``.
36
36
 
37
+ v11.6.0 removed the *writing* half — ``write_image_memory``,
38
+ ``write_video_memory``, the keyframe writer and the node-id helpers. Extraction
39
+ returns facts; ``lattice-core``'s graph write engine turns them into nodes. What
40
+ is left here is exactly what ``POST /worker/multimodal/describe`` and
41
+ ``POST /worker/asr`` answer with.
42
+
37
43
  Split into cohesive submodules in v11.3.0 (no behaviour change): ``common``
38
44
  (taxonomy + shared helpers), ``ports`` (injected capabilities + the ffmpeg
39
45
  fallback), ``images``, ``audio``, ``video``. This module re-exports every name
@@ -74,16 +80,8 @@ from .common import (
74
80
  from .common import _sha256_file as _sha256_file
75
81
  from .common import _sha256_text as _sha256_text
76
82
  from .common import _split_index_text as _split_index_text
77
- from .images import (
78
- ImageFacts,
79
- extract_image_facts,
80
- image_node_id,
81
- image_quality_score,
82
- write_image_memory,
83
- )
83
+ from .images import ImageFacts, extract_image_facts, image_quality_score
84
84
  from .images import _apply_vision_embedding as _apply_vision_embedding
85
- from .images import _attach_concepts as _attach_concepts
86
- from .images import _attach_source as _attach_source
87
85
  from .images import _open_image as _open_image
88
86
  from .images import _run_ocr as _run_ocr
89
87
  from .images import _safe_caption as _safe_caption
@@ -112,12 +110,8 @@ from .video import (
112
110
  find_subtitle,
113
111
  parse_subtitles,
114
112
  read_video_facts,
115
- video_frame_dir,
116
- video_node_id,
117
113
  video_quality_score,
118
- write_video_memory,
119
114
  )
120
- from .video import _write_keyframes as _write_keyframes
121
115
 
122
116
  __all__ = [
123
117
  "AUDIO_EXTENSIONS",
@@ -151,14 +145,9 @@ __all__ = [
151
145
  "extract_keyframes",
152
146
  "ffmpeg_available",
153
147
  "find_subtitle",
154
- "image_node_id",
155
148
  "image_quality_score",
156
149
  "parse_subtitles",
157
150
  "read_video_facts",
158
151
  "transcribe_audio",
159
- "video_frame_dir",
160
- "video_node_id",
161
152
  "video_quality_score",
162
- "write_image_memory",
163
- "write_video_memory",
164
153
  ]