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,394 +0,0 @@
1
- """
2
- Context Builder — Knowledge Graph 기반 문서 생성용 컨텍스트 조합 모듈.
3
-
4
- retrieve_context_for_generation() 파이프라인:
5
- Step 1: Query → Hybrid Search (text + graph + recency)
6
- Step 2: Seed nodes → Multi-hop traversal (Document → Project → Concept)
7
- Step 3: Top-K 결과를 구조화된 Markdown Context로 변환
8
-
9
- **Shared context contract (v9.9.6).** Chat assembles context through
10
- :class:`lattice_brain.context.ContextAssembler` — budgeted, provenance-carrying,
11
- with an honest ``context_quality`` signal. Document generation used to build
12
- its own markdown with no budget and no quality signal, so the same Brain
13
- answered a chat question and wrote a document under different guarantees.
14
-
15
- The rendering stays different on purpose (a document prompt wants structured
16
- sections, a chat prompt wants terse lines), but the *contract* is now one:
17
-
18
- * the same ``approx_tokens`` accounting and an explicit budget,
19
- * the same ``context_quality_signal`` shape chat reports,
20
- * a ``trace`` in the assembler's shape (budget / used / sections).
21
-
22
- Review 2026-07-27 P1 #5: "같은 Brain이면 같은 품질 계약".
23
- """
24
-
25
- import re
26
- from typing import Any, Dict, List
27
-
28
- from lattice_brain.context import approx_tokens
29
- from lattice_brain.graph.retrieval import context_quality_signal, multimodal_signal
30
- from lattice_brain.self_model import DEFAULT_SUMMARY_TOKENS, summary_for_prompt
31
-
32
- _CLEAN_RE = re.compile(r"\s+")
33
-
34
- # Approximate-token ceiling for the document-generation context block. Mirrors
35
- # the chat assembler's default budget so neither path can silently outgrow the
36
- # other on the same Brain.
37
- DEFAULT_DOCUMENT_CONTEXT_BUDGET = 2000
38
-
39
- # ── Self-Model injection (v11.1.0) ──────────────────────────────────────────
40
- # What the Brain knows about its owner rides along with the knowledge, so a
41
- # generated document sounds like the person who asked for it. Three rules keep
42
- # it from becoming another thing that can go wrong:
43
- # 1. an empty Self-Model injects *nothing* — no header, no blank section;
44
- # 2. the block never takes more than half the context budget, so the profile
45
- # can never crowd out the knowledge the request is actually about;
46
- # 3. the returned contract is unchanged — same keys, same
47
- # ``context_quality`` shape, one extra trace section only when a block
48
- # was really injected.
49
- SELF_MODEL_SECTION_TITLE = "사용자 프로필"
50
- SELF_MODEL_TRACE_SOURCE = "self_model"
51
- SELF_MODEL_SECTION_HEADER = f"### 🙋 {SELF_MODEL_SECTION_TITLE}\n\n"
52
-
53
-
54
- def _clean(text: str, max_len: int = 700) -> str:
55
- return _CLEAN_RE.sub(" ", str(text or "")).strip()[:max_len]
56
-
57
-
58
- def _empty_result(query: str, reason: str) -> Dict[str, Any]:
59
- quality = context_quality_signal("none", 0, reason=reason)
60
- return {
61
- "query": query,
62
- "context_markdown": "",
63
- "sources": [],
64
- "stats": {"method": "none", "matches": 0},
65
- "context_quality": quality,
66
- "trace": {
67
- "budget_approx_tokens": DEFAULT_DOCUMENT_CONTEXT_BUDGET,
68
- "used_approx_tokens": 0,
69
- "sections": [],
70
- },
71
- }
72
-
73
-
74
- def _fit_to_budget(context_md: str, budget: int) -> tuple:
75
- """Trim rendered markdown to ``budget`` approximate tokens.
76
-
77
- Cuts at a section boundary when possible so the prompt never ends inside
78
- half an entry, and reports the trim so the trace stays honest.
79
- """
80
- if budget <= 0 or approx_tokens(context_md) <= budget:
81
- return context_md, False
82
- limit = budget * 4
83
- head = context_md[:limit]
84
- boundary = head.rfind("\n### ")
85
- if boundary > limit // 3:
86
- head = head[:boundary]
87
- return head.rstrip(), True
88
-
89
-
90
- def _self_model_budget(budget: int, limit_tokens: int) -> int:
91
- """Tokens the profile block may use — never more than half the budget."""
92
- if budget <= 0:
93
- return limit_tokens
94
- return min(limit_tokens, budget // 2)
95
-
96
-
97
- def _self_model_block(
98
- kg_store,
99
- *,
100
- enabled: bool,
101
- budget: int,
102
- limit_tokens: int,
103
- allowed_workspaces,
104
- ) -> str:
105
- """Rendered profile section, or ``""`` when there is nothing to inject.
106
-
107
- The section header is charged to the same allowance as the summary, so the
108
- *rendered block* — not just its text — honours the ceiling.
109
- """
110
- allowance = _self_model_budget(budget, limit_tokens) - approx_tokens(
111
- SELF_MODEL_SECTION_HEADER
112
- )
113
- if not enabled or allowance <= 0:
114
- return ""
115
- summary = summary_for_prompt(
116
- kg_store, limit_tokens=allowance, allowed_workspaces=allowed_workspaces
117
- )
118
- if not summary:
119
- return ""
120
- return f"{SELF_MODEL_SECTION_HEADER}{summary}"
121
-
122
-
123
- def _with_self_model(block: str, context_md: str) -> str:
124
- """Prepend the profile block to rendered knowledge (either may be empty)."""
125
- return "\n\n".join(part for part in (block, context_md) if part)
126
-
127
-
128
- def _self_model_trace(block: str) -> List[Dict[str, Any]]:
129
- if not block:
130
- return []
131
- return [{
132
- "name": SELF_MODEL_SECTION_TITLE,
133
- "source": SELF_MODEL_TRACE_SOURCE,
134
- "approx_tokens": approx_tokens(block),
135
- "provenance": [],
136
- }]
137
-
138
-
139
- def retrieve_context_for_generation(
140
- kg_store,
141
- query: str,
142
- *,
143
- max_results: int = 10,
144
- max_hops: int = 2,
145
- allowed_workspaces=None,
146
- include_legacy_global: bool = False,
147
- budget: int = DEFAULT_DOCUMENT_CONTEXT_BUDGET,
148
- include_self_model: bool = True,
149
- self_model_tokens: int = DEFAULT_SUMMARY_TOKENS,
150
- ) -> Dict[str, Any]:
151
- """Knowledge Graph에서 문서 생성에 필요한 컨텍스트를 검색·조합한다.
152
-
153
- Returns:
154
- {
155
- "query": str,
156
- "context_markdown": str, # LLM 프롬프트에 직접 주입할 Markdown
157
- "sources": [...], # 참조된 소스 목록
158
- "stats": {...}, # 검색 통계
159
- "context_quality": {...}, # chat과 동일한 정직 신호 (v9.9.6)
160
- "trace": {...}, # assembler와 동일한 예산/섹션 추적
161
- }
162
- """
163
- query = str(query or "").strip()
164
- if not query or not kg_store:
165
- return _empty_result(query, "문서 생성 컨텍스트를 조회할 수 없습니다")
166
-
167
- scope_kwargs = (
168
- {
169
- "allowed_workspaces": allowed_workspaces,
170
- "include_legacy_global": include_legacy_global,
171
- }
172
- if allowed_workspaces is not None
173
- else {}
174
- )
175
- self_model_md = _self_model_block(
176
- kg_store,
177
- enabled=include_self_model,
178
- budget=budget,
179
- limit_tokens=self_model_tokens,
180
- allowed_workspaces=allowed_workspaces,
181
- )
182
- # The profile's tokens come out of the same budget, so injecting it can
183
- # never push the assembled context over the ceiling the caller asked for.
184
- # The blank line that joins the two blocks is counted too — otherwise the
185
- # assembled total lands one token over a tight budget.
186
- knowledge_budget = budget
187
- if self_model_md:
188
- knowledge_budget -= approx_tokens(f"{self_model_md}\n\n")
189
-
190
- results = kg_store.search_for_document_generation(query, limit=max_results, **scope_kwargs)
191
- if not results:
192
- fallback_ctx = kg_store.context_for_query(
193
- query,
194
- limit=max_results,
195
- **scope_kwargs,
196
- )
197
- fallback_ctx, trimmed = _fit_to_budget(fallback_ctx or "", knowledge_budget)
198
- # Lexical fallback: the hybrid document search found nothing, so the
199
- # signal says lexical_only — exactly what chat reports in the same
200
- # situation, never a quiet downgrade.
201
- quality = context_quality_signal(
202
- "lexical_only" if fallback_ctx else "none",
203
- 1 if fallback_ctx else 0,
204
- )
205
- assembled = _with_self_model(self_model_md, fallback_ctx)
206
- return {
207
- "query": query,
208
- "context_markdown": assembled,
209
- "sources": [],
210
- "stats": {"method": "fallback", "matches": 0, "budget_trimmed": trimmed},
211
- "context_quality": quality,
212
- "trace": {
213
- "budget_approx_tokens": budget,
214
- "used_approx_tokens": approx_tokens(assembled),
215
- "sections": _self_model_trace(self_model_md) + [{
216
- "name": "Knowledge (fallback)",
217
- "source": "knowledge",
218
- "approx_tokens": approx_tokens(fallback_ctx),
219
- "provenance": [],
220
- }],
221
- },
222
- }
223
-
224
- seed_ids = [r["id"] for r in results]
225
- hop_data = kg_store.multi_hop_context(
226
- seed_ids,
227
- max_hops=max_hops,
228
- **scope_kwargs,
229
- )
230
-
231
- extra_nodes_by_id = {}
232
- for node in hop_data.get("nodes", []):
233
- if node["id"] not in {r["id"] for r in results}:
234
- extra_nodes_by_id[node["id"]] = node
235
-
236
- sections = _build_context_sections(results, extra_nodes_by_id, hop_data.get("edges", []))
237
- context_md = _render_markdown(query, sections)
238
- sources = _extract_sources(results)
239
- context_md, trimmed = _fit_to_budget(context_md, knowledge_budget)
240
- assembled = _with_self_model(self_model_md, context_md)
241
-
242
- return {
243
- "query": query,
244
- "context_markdown": assembled,
245
- "sources": sources,
246
- "stats": {
247
- "method": "hybrid",
248
- "primary_matches": len(results),
249
- "graph_nodes": len(hop_data.get("nodes", [])),
250
- "graph_edges": len(hop_data.get("edges", [])),
251
- "budget_trimmed": trimmed,
252
- },
253
- # Same honest signal chat reports for the same Brain — including the
254
- # multimodal key when the document's context really rests on pictures.
255
- "context_quality": context_quality_signal(
256
- "hybrid", len(results), multimodal=multimodal_signal(results)
257
- ),
258
- "trace": {
259
- "budget_approx_tokens": budget,
260
- "used_approx_tokens": approx_tokens(assembled),
261
- "sections": _self_model_trace(self_model_md) + [
262
- {
263
- "name": section["title"],
264
- "source": "knowledge",
265
- "approx_tokens": approx_tokens(
266
- " ".join(str(item.get("summary") or "") for item in section["items"])
267
- ),
268
- "provenance": [
269
- {"id": item.get("id"), "type": item.get("type")}
270
- for item in section["items"][:8]
271
- ],
272
- }
273
- for section in sections
274
- ],
275
- },
276
- }
277
-
278
-
279
- def _build_context_sections(
280
- primary_results: List[Dict[str, Any]],
281
- extra_nodes: Dict[str, Dict[str, Any]],
282
- edges: List[Dict[str, Any]],
283
- ) -> List[Dict[str, Any]]:
284
- sections = []
285
-
286
- docs = [r for r in primary_results if r["type"] in (
287
- "Document", "File", "SlideDeck", "Spreadsheet", "CodeFile", "Image", "ImageText",
288
- "Audio",
289
- )]
290
- if docs:
291
- sections.append({
292
- "title": "관련 문서/파일",
293
- "items": docs,
294
- "icon": "📄",
295
- })
296
-
297
- decisions = [r for r in primary_results if r["type"] in ("Decision", "Task")]
298
- if decisions:
299
- sections.append({
300
- "title": "관련 결정사항/작업",
301
- "items": decisions,
302
- "icon": "✅",
303
- })
304
-
305
- conversations = [r for r in primary_results if r["type"] == "Chat"]
306
- if conversations:
307
- sections.append({
308
- "title": "관련 대화",
309
- "items": conversations,
310
- "icon": "💬",
311
- })
312
-
313
- concepts = [r for r in primary_results if r["type"] in ("Concept", "Feature")]
314
- extra_concepts = [n for n in extra_nodes.values() if n["type"] in ("Concept", "Feature")]
315
- all_concepts = concepts + extra_concepts[:8]
316
- if all_concepts:
317
- sections.append({
318
- "title": "관련 개념/기술",
319
- "items": all_concepts,
320
- "icon": "🔗",
321
- })
322
-
323
- return sections
324
-
325
-
326
- def _render_markdown(query: str, sections: List[Dict[str, Any]]) -> str:
327
- lines = []
328
- for section in sections:
329
- if not section["items"]:
330
- continue
331
- lines.append(f"### {section['icon']} {section['title']}")
332
- lines.append("")
333
- for item in section["items"][:8]:
334
- title = item.get("title", "")
335
- summary = _clean(item.get("summary", ""))
336
- item_type = item.get("type", "")
337
- score_info = ""
338
- if "hybrid_score" in item:
339
- score_info = f" (relevance: {item['hybrid_score']:.2f})"
340
-
341
- meta = item.get("metadata") or {}
342
- source = (
343
- meta.get("relative_path")
344
- or meta.get("filename")
345
- or meta.get("conversation_id")
346
- or meta.get("source")
347
- or item.get("id", "")
348
- )
349
-
350
- lines.append(f"- **[{item_type}] {title}**{score_info}")
351
- if source and source != item.get("id", ""):
352
- lines.append(f" - 출처: {source}")
353
- if summary:
354
- lines.append(f" - {summary}")
355
-
356
- related = item.get("related_concepts", [])
357
- if related:
358
- tags = ", ".join(c["title"] for c in related[:5])
359
- lines.append(f" - 관련: {tags}")
360
-
361
- lines.append("")
362
-
363
- return "\n".join(lines).strip()
364
-
365
-
366
- def _extract_sources(results: List[Dict[str, Any]]) -> List[Dict[str, str]]:
367
- sources = []
368
- seen = set()
369
- for r in results:
370
- meta = r.get("metadata") or {}
371
- source_key = (
372
- meta.get("relative_path")
373
- or meta.get("filename")
374
- or meta.get("conversation_id")
375
- or r.get("id", "")
376
- )
377
- if source_key and source_key not in seen:
378
- seen.add(source_key)
379
- sources.append({
380
- "id": r.get("id", ""),
381
- "type": r.get("type", ""),
382
- "title": r.get("title", ""),
383
- "source": source_key,
384
- })
385
- return sources
386
-
387
-
388
- def format_sources_footnote(sources: List[Dict[str, str]]) -> str:
389
- if not sources:
390
- return ""
391
- lines = ["\n---\n**참조된 지식 그래프 노드:**"]
392
- for i, src in enumerate(sources[:10], 1):
393
- lines.append(f"{i}. [{src['type']}] {src['title']} ({src['source']})")
394
- return "\n".join(lines)
@@ -1,103 +0,0 @@
1
- """
2
- Document Generator — 지식 그래프 기반 고품질 문서 자동 생성 모듈.
3
-
4
- 사용자가 "Q3 마케팅 전략 보고서 작성해줘" 같은 일반 채팅을 하면,
5
- Knowledge Graph에서 관련 과거 문서/개념/관계를 자동으로 찾아
6
- LLM이 자연스럽고 일관성 있게 새로운 문서를 생성한다.
7
- """
8
-
9
- import re
10
- from typing import Optional
11
-
12
- _DOCUMENT_INTENT_PATTERNS = [
13
- re.compile(r"(보고서|계획서|기획서|제안서|문서|리포트|요약서|분석서|전략서|매뉴얼|가이드)", re.IGNORECASE),
14
- re.compile(r"(작성|만들어|생성|써|줘|write|create|generate|draft|compose|prepare)", re.IGNORECASE),
15
- re.compile(r"(report|proposal|plan|document|summary|analysis|strategy|guide|manual|brief)", re.IGNORECASE),
16
- ]
17
-
18
- _STRONG_INTENT_PATTERNS = [
19
- re.compile(r"(작성해|만들어\s*줘|써\s*줘|생성해|write\s+(?:a|me|the)|create\s+(?:a|me|the)|draft\s+(?:a|me|the))", re.IGNORECASE),
20
- re.compile(r"(보고서|계획서|기획서|제안서|전략서|매뉴얼).*(작성|만들|생성|써)", re.IGNORECASE),
21
- re.compile(r"(작성|만들|생성|써).*(보고서|계획서|기획서|제안서|전략서|매뉴얼)", re.IGNORECASE),
22
- ]
23
-
24
-
25
- def detect_document_intent(message: str) -> bool:
26
- """Detect whether the user's message is requesting document generation."""
27
- if not message or len(message) < 5:
28
- return False
29
- for pattern in _STRONG_INTENT_PATTERNS:
30
- if pattern.search(message):
31
- return True
32
- hit_count = sum(1 for p in _DOCUMENT_INTENT_PATTERNS if p.search(message))
33
- return hit_count >= 2
34
-
35
-
36
- DOCUMENT_GENERATION_SYSTEM_PROMPT = """당신은 사용자의 개인 AI 지식 어시스턴트 Lattice AI입니다.
37
- 사용자의 기존 지식 기반을 활용하여 고품질 문서를 생성합니다.
38
-
39
- ## 지침
40
- 1. 아래 제공된 지식 그래프 컨텍스트를 최대한 활용하세요.
41
- 2. 이전 문서의 스타일과 톤을 유지하면서 최신적이고 전문적인 문서를 작성하세요.
42
- 3. 출처는 자연스럽게 본문이나 각주에 포함하세요.
43
- 4. 사용자의 언어(한국어/영어)에 맞춰 작성하세요.
44
- 5. 구조화된 포맷(제목, 소제목, 목록 등)을 사용하세요.
45
-
46
- ## 사용자의 지식 기반
47
-
48
- {graph_context}"""
49
-
50
- DOCUMENT_GENERATION_FOLLOWUP_PROMPT = """당신은 사용자의 개인 AI 지식 어시스턴트 Lattice AI입니다.
51
- 이전에 생성한 문서를 사용자의 요청에 따라 수정/보완합니다.
52
-
53
- ## 이전 생성 컨텍스트
54
-
55
- {graph_context}
56
-
57
- ## 이전 문서
58
- {previous_document}
59
-
60
- 위 문서를 사용자의 요청에 따라 수정하세요. 기존 스타일과 톤을 유지하세요."""
61
-
62
-
63
- def build_document_system_prompt(graph_context: str) -> str:
64
- if not graph_context:
65
- return DOCUMENT_GENERATION_SYSTEM_PROMPT.replace("{graph_context}", "(사용 가능한 지식 기반이 없습니다. 일반 지식을 활용하여 작성합니다.)")
66
- return DOCUMENT_GENERATION_SYSTEM_PROMPT.replace("{graph_context}", graph_context)
67
-
68
-
69
- def build_followup_system_prompt(graph_context: str, previous_document: str) -> str:
70
- prompt = DOCUMENT_GENERATION_FOLLOWUP_PROMPT.replace("{graph_context}", graph_context or "(없음)")
71
- return prompt.replace("{previous_document}", previous_document or "(없음)")
72
-
73
-
74
- class DocumentGenerationSession:
75
- """Maintains state across iterative document generation requests."""
76
-
77
- def __init__(self):
78
- self._last_context: Optional[str] = None
79
- self._last_document: Optional[str] = None
80
- self._conversation_id: Optional[str] = None
81
-
82
- @property
83
- def has_previous(self) -> bool:
84
- return self._last_document is not None
85
-
86
- def update(self, context: str, document: str, conversation_id: Optional[str] = None) -> None:
87
- self._last_context = context
88
- self._last_document = document
89
- if conversation_id:
90
- self._conversation_id = conversation_id
91
-
92
- def get_system_prompt(self, graph_context: str) -> str:
93
- if self.has_previous:
94
- return build_followup_system_prompt(
95
- graph_context or self._last_context or "",
96
- self._last_document or "",
97
- )
98
- return build_document_system_prompt(graph_context)
99
-
100
- def clear(self) -> None:
101
- self._last_context = None
102
- self._last_document = None
103
- self._conversation_id = None
@@ -1,154 +0,0 @@
1
- """Enterprise extension seam (open-core boundary).
2
-
3
- LatticeAI Community is fully functional on its own. This module defines the
4
- *seam* where a future, separately-distributed Enterprise plugin can attach
5
- advanced capabilities (SSO provisioning, SCIM, tenant isolation, compliance
6
- retention, SIEM export, and so on) **without** any of that code living in the
7
- public Community repository.
8
-
9
- Design rules enforced here:
10
-
11
- * Community is the default :class:`Edition`. Every :class:`EnterpriseCapability`
12
- is **disabled** unless an Enterprise provider is explicitly registered.
13
- * Nothing in this module restricts or gates a Community feature. It only answers
14
- "is this *Enterprise* capability available?" — which is ``False`` in the open
15
- build.
16
- * Enterprise behaviour is supplied at runtime through
17
- :class:`CapabilityProvider` implementations registered on the shared
18
- :data:`capability_registry`. The public code never imports Enterprise code.
19
- """
20
-
21
- from __future__ import annotations
22
-
23
- import os
24
- from enum import Enum
25
- from typing import Dict, Protocol, runtime_checkable
26
-
27
-
28
- class Edition(str, Enum):
29
- """Distribution edition. Community is the open-source default."""
30
-
31
- COMMUNITY = "community"
32
- ENTERPRISE = "enterprise"
33
-
34
-
35
- class EnterpriseCapability(str, Enum):
36
- """Capabilities reserved for the Enterprise edition.
37
-
38
- These are documented in ``docs/ENTERPRISE.md``. They are intentionally
39
- declared (so the seam is stable and discoverable) but never implemented in
40
- the Community build.
41
- """
42
-
43
- SSO_ADVANCED = "sso_advanced"
44
- IDP_PROVISIONING = "idp_provisioning" # Entra ID / Okta provisioning
45
- SCIM = "scim"
46
- RBAC_ABAC_ADVANCED = "rbac_abac_advanced"
47
- TENANT_ISOLATION = "tenant_isolation"
48
- COMPLIANCE_RETENTION = "compliance_retention"
49
- SIEM_EXPORT = "siem_export"
50
- PRIVATE_VPC = "private_vpc"
51
- AIR_GAPPED_DEPLOYMENT = "air_gapped_deployment"
52
- DLP_POLICY = "dlp_policy"
53
- EDISCOVERY = "ediscovery"
54
- ADMIN_POLICY_PACKS = "admin_policy_packs"
55
- # Human review-before-promote governance for knowledge-graph concept
56
- # promotions (review 2026-07-25 Wave 4). Community users can opt in per
57
- # deployment via LATTICEAI_GRAPH_PROMOTION_REVIEW; this capability is the
58
- # seam for Enterprise policy packs that mandate reviewed promotions.
59
- GRAPH_PROMOTION_REVIEW = "graph_promotion_review"
60
-
61
-
62
- @runtime_checkable
63
- class CapabilityProvider(Protocol):
64
- """Contract an Enterprise plugin implements to light up capabilities.
65
-
66
- A provider is registered at runtime via
67
- :meth:`CapabilityRegistry.register_provider`. The Community build ships no
68
- provider, so :meth:`is_enabled` is effectively ``False`` everywhere.
69
- """
70
-
71
- def edition(self) -> Edition:
72
- ...
73
-
74
- def is_enabled(self, capability: EnterpriseCapability) -> bool:
75
- ...
76
-
77
-
78
- class CommunityCapabilityProvider:
79
- """Default provider: Community edition, no Enterprise capabilities."""
80
-
81
- def edition(self) -> Edition:
82
- return Edition.COMMUNITY
83
-
84
- def is_enabled(self, capability: EnterpriseCapability) -> bool: # noqa: ARG002
85
- return False
86
-
87
-
88
- class CapabilityRegistry:
89
- """Runtime seam that an Enterprise plugin can attach a provider to.
90
-
91
- The registry holds at most one active provider. Until an Enterprise build
92
- registers one, the :class:`CommunityCapabilityProvider` answers every query
93
- with "Community / disabled".
94
- """
95
-
96
- def __init__(self) -> None:
97
- self._provider: CapabilityProvider = CommunityCapabilityProvider()
98
-
99
- def register_provider(self, provider: CapabilityProvider) -> None:
100
- if not isinstance(provider, CapabilityProvider):
101
- raise TypeError("provider must implement the CapabilityProvider protocol")
102
- self._provider = provider
103
-
104
- def reset(self) -> None:
105
- """Restore the Community default provider (used by tests)."""
106
- self._provider = CommunityCapabilityProvider()
107
-
108
- def edition(self) -> Edition:
109
- return self._provider.edition()
110
-
111
- def is_capability_enabled(self, capability: EnterpriseCapability) -> bool:
112
- try:
113
- return bool(self._provider.is_enabled(capability))
114
- except Exception:
115
- # A misbehaving plugin must never break a Community request.
116
- return False
117
-
118
- def describe(self) -> Dict[str, object]:
119
- """Edition + capability matrix for ``/workspace/editions`` and admin UI."""
120
- return {
121
- "edition": self.edition().value,
122
- "is_enterprise": self.edition() is Edition.ENTERPRISE,
123
- "capabilities": {
124
- cap.value: self.is_capability_enabled(cap) for cap in EnterpriseCapability
125
- },
126
- "community_notice": (
127
- "All listed capabilities are Enterprise-only extension points. "
128
- "The open-source Community edition ships none of them enabled; "
129
- "see docs/ENTERPRISE.md and docs/EDITION_STRATEGY.md."
130
- ),
131
- }
132
-
133
-
134
- # Shared singleton seam. Enterprise plugins import this and call
135
- # ``capability_registry.register_provider(...)`` during their own startup.
136
- capability_registry = CapabilityRegistry()
137
-
138
-
139
- def detect_edition() -> Edition:
140
- """Best-effort edition detection.
141
-
142
- Honours an explicit ``LATTICE_EDITION=enterprise`` opt-in for environments
143
- that have separately installed an Enterprise provider, but otherwise the
144
- registry's active provider is the source of truth.
145
- """
146
- env = os.environ.get("LATTICE_EDITION", "").strip().lower()
147
- if env == Edition.ENTERPRISE.value and capability_registry.edition() is Edition.ENTERPRISE:
148
- return Edition.ENTERPRISE
149
- return capability_registry.edition()
150
-
151
-
152
- def is_capability_enabled(capability: EnterpriseCapability) -> bool:
153
- """Module-level convenience used by Community code at extension points."""
154
- return capability_registry.is_capability_enabled(capability)