ltcai 11.5.2 → 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.
- package/README.md +91 -148
- package/bin/ltcai.js +234 -24
- package/docs/CHANGELOG.md +75 -0
- package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
- package/docs/DEVELOPMENT.md +8 -6
- package/docs/ENTERPRISE.md +2 -1
- package/docs/MULTI_AGENT_RUNTIME.md +12 -5
- package/docs/ONBOARDING.md +1 -1
- package/docs/OPERATIONS.md +6 -3
- package/docs/REALTIME_COLLABORATION.md +1 -1
- package/docs/TRUST_MODEL.md +1 -1
- package/docs/WHY_LATTICE.md +1 -1
- package/docs/WORKFLOW_DESIGNER.md +3 -2
- package/docs/kg-schema.md +1 -1
- package/docs/v11.6.0_ONE_DOOR_PLAN.md +170 -0
- package/lattice_brain/__init__.py +42 -79
- package/lattice_brain/graph/__init__.py +9 -24
- package/lattice_brain/graph/_kg_common/__init__.py +13 -24
- package/lattice_brain/ingestion/__init__.py +19 -58
- package/lattice_brain/ingestion/pipeline.py +20 -398
- package/lattice_brain/multimodal/__init__.py +7 -18
- package/lattice_brain/multimodal/images.py +6 -264
- package/lattice_brain/multimodal/video.py +8 -247
- package/lattice_brain/runtime/__init__.py +8 -79
- package/lattice_brain/runtime/hooks.py +22 -584
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/agent_worker_seam.py +7 -80
- package/latticeai/api/health.py +6 -20
- package/latticeai/api/local_files.py +16 -613
- package/latticeai/api/models.py +12 -87
- package/latticeai/api/search.py +17 -255
- package/latticeai/api/tools.py +69 -750
- package/latticeai/api/voice_capture.py +8 -70
- package/latticeai/api/worker_compute.py +842 -0
- package/latticeai/api/worker_seams.py +216 -0
- package/latticeai/app_factory.py +23 -232
- package/latticeai/cli/entrypoint.py +36 -249
- package/latticeai/core/agent_permission.py +16 -91
- package/latticeai/core/messages.py +89 -531
- package/latticeai/runtime/access_runtime.py +0 -14
- package/latticeai/runtime/bootstrap.py +10 -21
- package/latticeai/runtime/brain_runtime.py +35 -43
- package/latticeai/runtime/build_phases/__init__.py +21 -37
- package/latticeai/runtime/build_phases/features.py +99 -389
- package/latticeai/runtime/build_phases/foundation.py +82 -392
- package/latticeai/runtime/build_phases/web.py +91 -408
- package/latticeai/runtime/build_phases/worker_profile.py +244 -0
- package/latticeai/runtime/lifespan_runtime.py +7 -16
- package/latticeai/runtime/platform_services_runtime.py +1 -15
- package/latticeai/runtime/runtime_context.py +13 -130
- package/latticeai/runtime/security_runtime.py +10 -103
- package/latticeai/services/architecture_readiness.py +82 -43
- package/latticeai/services/model_runtime/__init__.py +2 -11
- package/latticeai/services/model_runtime/service.py +4 -28
- package/latticeai/services/p_reinforce.py +10 -261
- package/latticeai/services/product_readiness.py +31 -38
- package/latticeai/services/search_service.py +37 -795
- package/latticeai/services/tool_dispatch.py +30 -386
- package/latticeai/services/voice_capture.py +13 -107
- package/latticeai/tools/__init__.py +22 -49
- package/latticeai/tools/commands.py +0 -163
- package/latticeai/tools/computer.py +0 -39
- package/latticeai/tools/documents.py +1 -134
- package/latticeai/tools/filesystem.py +1 -247
- package/latticeai/tools/knowledge.py +1 -52
- package/latticeai/tools/local_files.py +0 -20
- package/latticeai/worker_app.py +75 -0
- package/package.json +2 -3
- package/requirements.txt +0 -5
- package/scripts/agent_eval.py +16 -28
- package/scripts/brain_quality_eval.py +20 -183
- package/scripts/bump_version.py +5 -5
- package/scripts/check_current_release_docs.mjs +7 -4
- package/scripts/check_openapi_drift.mjs +10 -0
- package/scripts/check_server_i18n.mjs +2 -18
- package/scripts/compose_openapi.py +377 -0
- package/scripts/export_openapi.py +32 -4
- package/scripts/gen_messages_catalog_fixture.py +302 -0
- package/scripts/gen_openapi_fragments.py +365 -0
- package/scripts/gen_redact_fixture.py +196 -0
- package/scripts/gen_worker_allowlist_fixture.py +115 -0
- package/scripts/generate_agent_parity_fixtures.py +33 -14
- package/scripts/openapi_route_families.json +2161 -0
- package/scripts/release_screen_claims.json +21 -0
- package/scripts/run_integration_tests.mjs +134 -29
- package/scripts/run_sidecar_e2e.mjs +104 -11
- package/scripts/wheel_smoke.py +32 -22
- package/src-tauri/Cargo.lock +375 -9
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/src/backend.rs +151 -45
- package/src-tauri/src/main.rs +18 -18
- package/src-tauri/src/topology.rs +58 -88
- package/src-tauri/tauri.conf.json +1 -2
- package/static/app/asset-manifest.json +41 -41
- package/static/app/assets/{Act-DcQizkl1.js → Act-CToZnOHz.js} +1 -1
- package/static/app/assets/{AdminConsole-cf4npybT.js → AdminConsole-D5kbrBu8.js} +1 -1
- package/static/app/assets/{Brain-3VCSHFcn.js → Brain-C6zCdv4S.js} +1 -1
- package/static/app/assets/{BrainHome-Qm8eaztx.js → BrainHome-D4n3EDUt.js} +1 -1
- package/static/app/assets/{BrainSignals-DS9BtKOW.js → BrainSignals-CzeZ-vQ9.js} +1 -1
- package/static/app/assets/{Capture-DiQ219jW.js → Capture-DPcyTczq.js} +1 -1
- package/static/app/assets/{Chronicle-BGvuAchH.js → Chronicle-D3Nx-oWS.js} +1 -1
- package/static/app/assets/{CommandPalette-Bqhm0Urn.js → CommandPalette-DPPvpp28.js} +1 -1
- package/static/app/assets/{Library-BV6NnF0a.js → Library-Co2qTu3I.js} +1 -1
- package/static/app/assets/{LivingBrain-GzenJchP.js → LivingBrain-Bsmzwy8_.js} +1 -1
- package/static/app/assets/{ProductFlow-DEP6-vML.js → ProductFlow-CBLdmesT.js} +1 -1
- package/static/app/assets/{ReviewCard-CNZ7XjWG.js → ReviewCard-CfR6DLs7.js} +1 -1
- package/static/app/assets/{System-CieofHQa.js → System-Ce-P2YL6.js} +1 -1
- package/static/app/assets/arrow-left-DD5jFGHV.js +1 -0
- package/static/app/assets/{bot-B_K1Tdmw.js → bot-C5-XIww2.js} +1 -1
- package/static/app/assets/{brain-DWyaV1L1.js → brain-CFRJUvdX.js} +1 -1
- package/static/app/assets/{button-aTn4s84A.js → button-DI1KcQP-.js} +1 -1
- package/static/app/assets/circle-check-CY20XMCh.js +1 -0
- package/static/app/assets/{circle-pause-xKgeGXkT.js → circle-pause-C8HHyood.js} +1 -1
- package/static/app/assets/{circle-play-DkT6tYPX.js → circle-play-Cufc235E.js} +1 -1
- package/static/app/assets/{cpu-85xYObUC.js → cpu-DW2_JVsv.js} +1 -1
- package/static/app/assets/{download-B5Fm7YXo.js → download-gbdKTBb8.js} +1 -1
- package/static/app/assets/{folder-open-kk2Xa52u.js → folder-open-C9ydyX33.js} +1 -1
- package/static/app/assets/{hard-drive-DkA3zBW_.js → hard-drive-BxNMzBM3.js} +1 -1
- package/static/app/assets/index-BovSWRmQ.css +2 -0
- package/static/app/assets/{index-BMPdTmlY.js → index-q9XyDY7A.js} +3 -3
- package/static/app/assets/{input-B0nRf2jO.js → input-GLD94Y_K.js} +1 -1
- package/static/app/assets/{link-2-Dwb4gnTc.js → link-2-B4Y0gz1S.js} +1 -1
- package/static/app/assets/{permissionCopy-CQDUBrOZ.js → permissionCopy-CxrV0fvU.js} +1 -1
- package/static/app/assets/{primitives-SNp0LRJz.js → primitives-2ANdAsu2.js} +1 -1
- package/static/app/assets/search-DTxtjQ07.js +1 -0
- package/static/app/assets/{share-2-BsrxFglO.js → share-2-DxArfiuI.js} +1 -1
- package/static/app/assets/{shield-alert-5BStfp2_.js → shield-alert-CgftuFFs.js} +1 -1
- package/static/app/assets/{textarea-Cg8IUA-k.js → textarea-BHVBAoCr.js} +1 -1
- package/static/app/assets/{useFocusTrap-CYKvE46M.js → useFocusTrap-Dzq2j_OG.js} +1 -1
- package/static/app/assets/{useMutation-CSn9t1op.js → useMutation-BcqfMLNq.js} +1 -1
- package/static/app/assets/{useQuery-CY2OI2uy.js → useQuery-DBzLKCOv.js} +1 -1
- package/static/app/assets/{utils-Ddol2RWD.js → utils-B-Aah1bO.js} +1 -1
- package/static/app/assets/{workspace-BqDwOz_p.js → workspace-B6fFhoko.js} +1 -1
- package/static/app/index.html +4 -4
- package/static/sw.js +1 -1
- package/lattice_brain/archive.py +0 -522
- package/lattice_brain/context.py +0 -325
- package/lattice_brain/conversations.py +0 -382
- package/lattice_brain/core.py +0 -82
- package/lattice_brain/graph/_kg_contract.py +0 -249
- package/lattice_brain/graph/curator.py +0 -676
- package/lattice_brain/graph/discovery.py +0 -595
- package/lattice_brain/graph/discovery_index/__init__.py +0 -35
- package/lattice_brain/graph/discovery_index/cleanup.py +0 -182
- package/lattice_brain/graph/discovery_index/extract.py +0 -137
- package/lattice_brain/graph/discovery_index/scan.py +0 -411
- package/lattice_brain/graph/discovery_index/upsert.py +0 -495
- package/lattice_brain/graph/documents.py +0 -380
- package/lattice_brain/graph/fusion.py +0 -395
- package/lattice_brain/graph/identity.py +0 -175
- package/lattice_brain/graph/image_vectors.py +0 -230
- package/lattice_brain/graph/ingest.py +0 -829
- package/lattice_brain/graph/network.py +0 -205
- package/lattice_brain/graph/proactive.py +0 -724
- package/lattice_brain/graph/projection/__init__.py +0 -42
- package/lattice_brain/graph/projection/curation.py +0 -500
- package/lattice_brain/graph/projection/v2_schema.py +0 -518
- package/lattice_brain/graph/provenance.py +0 -524
- package/lattice_brain/graph/rerank.py +0 -163
- package/lattice_brain/graph/retrieval/__init__.py +0 -54
- package/lattice_brain/graph/retrieval/context.py +0 -197
- package/lattice_brain/graph/retrieval/graph_view.py +0 -319
- package/lattice_brain/graph/retrieval/hybrid.py +0 -488
- package/lattice_brain/graph/retrieval/maintenance.py +0 -121
- package/lattice_brain/graph/retrieval/signals.py +0 -95
- package/lattice_brain/graph/retrieval_docgen.py +0 -253
- package/lattice_brain/graph/retrieval_policy.py +0 -180
- package/lattice_brain/graph/retrieval_reads.py +0 -769
- package/lattice_brain/graph/retrieval_vector/__init__.py +0 -42
- package/lattice_brain/graph/retrieval_vector/fingerprint.py +0 -97
- package/lattice_brain/graph/retrieval_vector/indexing.py +0 -347
- package/lattice_brain/graph/retrieval_vector/search.py +0 -560
- package/lattice_brain/graph/retrieval_vector/status.py +0 -374
- package/lattice_brain/graph/schema.py +0 -792
- package/lattice_brain/graph/store.py +0 -268
- package/lattice_brain/graph/vector_index/__init__.py +0 -85
- package/lattice_brain/graph/vector_index/base.py +0 -167
- package/lattice_brain/graph/vector_index/brute_force.py +0 -110
- package/lattice_brain/graph/vector_index/hnsw.py +0 -290
- package/lattice_brain/graph/vector_index/jobs.py +0 -287
- package/lattice_brain/graph/vector_index/quantized.py +0 -148
- package/lattice_brain/graph/vector_index/selector.py +0 -161
- package/lattice_brain/graph/write_master.py +0 -308
- package/lattice_brain/ingestion/_contract.py +0 -90
- package/lattice_brain/ingestion/folder_scan.py +0 -57
- package/lattice_brain/ingestion/folders.py +0 -258
- package/lattice_brain/ingestion/jobs_api.py +0 -107
- package/lattice_brain/ingestion/routing.py +0 -295
- package/lattice_brain/ingestion_jobs.py +0 -380
- package/lattice_brain/memory.py +0 -75
- package/lattice_brain/portability/__init__.py +0 -90
- package/lattice_brain/portability/_contract.py +0 -42
- package/lattice_brain/portability/backups.py +0 -338
- package/lattice_brain/portability/bundles.py +0 -136
- package/lattice_brain/portability/constants.py +0 -93
- package/lattice_brain/portability/fsops.py +0 -138
- package/lattice_brain/portability/service.py +0 -41
- package/lattice_brain/portability/sharing.py +0 -710
- package/lattice_brain/quality.py +0 -543
- package/lattice_brain/retrieval_benchmark_fixtures.py +0 -92
- package/lattice_brain/runtime/agent_runtime.py +0 -859
- package/lattice_brain/runtime/contracts.py +0 -460
- package/lattice_brain/runtime/multi_agent.py +0 -942
- package/lattice_brain/runtime/statuses.py +0 -10
- package/lattice_brain/sealed_box.py +0 -240
- package/lattice_brain/self_model.py +0 -675
- package/lattice_brain/sensitivity.py +0 -94
- package/lattice_brain/storage/__init__.py +0 -22
- package/lattice_brain/storage/base.py +0 -100
- package/lattice_brain/storage/docker.py +0 -105
- package/lattice_brain/storage/factory.py +0 -31
- package/lattice_brain/storage/migration.py +0 -191
- package/lattice_brain/storage/postgres.py +0 -123
- package/lattice_brain/storage/sqlite.py +0 -143
- package/lattice_brain/synthesis.py +0 -824
- package/lattice_brain/workflow.py +0 -497
- package/latticeai/api/admin.py +0 -471
- package/latticeai/api/agent_registry.py +0 -105
- package/latticeai/api/agents.py +0 -228
- package/latticeai/api/auth.py +0 -383
- package/latticeai/api/automation_intelligence.py +0 -401
- package/latticeai/api/brain_intelligence.py +0 -199
- package/latticeai/api/browser.py +0 -493
- package/latticeai/api/change_proposals.py +0 -89
- package/latticeai/api/chat.py +0 -572
- package/latticeai/api/chat_agent_http.py +0 -892
- package/latticeai/api/chat_contracts.py +0 -73
- package/latticeai/api/chat_documents.py +0 -276
- package/latticeai/api/chat_helpers.py +0 -460
- package/latticeai/api/chat_history.py +0 -101
- package/latticeai/api/chat_hybrid.py +0 -113
- package/latticeai/api/chat_intents.py +0 -646
- package/latticeai/api/chat_stream.py +0 -216
- package/latticeai/api/chronicle.py +0 -63
- package/latticeai/api/command_center.py +0 -51
- package/latticeai/api/computer_use.py +0 -474
- package/latticeai/api/evidence_actions.py +0 -48
- package/latticeai/api/features.py +0 -70
- package/latticeai/api/funnel_metrics.py +0 -31
- package/latticeai/api/garden.py +0 -34
- package/latticeai/api/hooks.py +0 -165
- package/latticeai/api/index_jobs.py +0 -145
- package/latticeai/api/invitations.py +0 -100
- package/latticeai/api/knowledge_graph.py +0 -536
- package/latticeai/api/marketplace.py +0 -105
- package/latticeai/api/mcp.py +0 -482
- package/latticeai/api/memory.py +0 -270
- package/latticeai/api/network.py +0 -81
- package/latticeai/api/network_boundary.py +0 -225
- package/latticeai/api/permission_mode.py +0 -61
- package/latticeai/api/permissions.py +0 -436
- package/latticeai/api/plugins.py +0 -126
- package/latticeai/api/portability.py +0 -391
- package/latticeai/api/project_sessions.py +0 -114
- package/latticeai/api/realtime.py +0 -118
- package/latticeai/api/review_queue.py +0 -364
- package/latticeai/api/security_dashboard.py +0 -604
- package/latticeai/api/setup.py +0 -319
- package/latticeai/api/static_routes.py +0 -354
- package/latticeai/api/ui_redirects.py +0 -26
- package/latticeai/api/workflow_designer.py +0 -394
- package/latticeai/api/workspace.py +0 -856
- package/latticeai/api/workspace_scope.py +0 -125
- package/latticeai/core/agent/__init__.py +0 -93
- package/latticeai/core/agent/_contract.py +0 -79
- package/latticeai/core/agent/context.py +0 -57
- package/latticeai/core/agent/deps.py +0 -125
- package/latticeai/core/agent/execution.py +0 -622
- package/latticeai/core/agent/planning.py +0 -145
- package/latticeai/core/agent/recovery.py +0 -157
- package/latticeai/core/agent/runtime.py +0 -210
- package/latticeai/core/agent/verification.py +0 -231
- package/latticeai/core/agent_eval.py +0 -739
- package/latticeai/core/agent_helpers.py +0 -493
- package/latticeai/core/agent_profiles.py +0 -110
- package/latticeai/core/agent_prompts.py +0 -171
- package/latticeai/core/agent_registry.py +0 -232
- package/latticeai/core/agent_state.py +0 -41
- package/latticeai/core/agent_trace.py +0 -104
- package/latticeai/core/artifact_ledger.py +0 -109
- package/latticeai/core/audit.py +0 -260
- package/latticeai/core/builtin_hooks.py +0 -105
- package/latticeai/core/context_builder.py +0 -394
- package/latticeai/core/document_generator.py +0 -103
- package/latticeai/core/enterprise.py +0 -154
- package/latticeai/core/enterprise_admin.py +0 -158
- package/latticeai/core/file_generation/__init__.py +0 -115
- package/latticeai/core/file_generation/bundles.py +0 -76
- package/latticeai/core/file_generation/extraction.py +0 -154
- package/latticeai/core/file_generation/inference.py +0 -235
- package/latticeai/core/file_generation/orchestration.py +0 -152
- package/latticeai/core/file_generation/prompting.py +0 -117
- package/latticeai/core/file_generation/repair.py +0 -114
- package/latticeai/core/file_generation/sanitize.py +0 -61
- package/latticeai/core/file_generation/validation.py +0 -201
- package/latticeai/core/invitations.py +0 -132
- package/latticeai/core/legacy_compatibility.py +0 -243
- package/latticeai/core/logging_safety.py +0 -46
- package/latticeai/core/marketplace.py +0 -293
- package/latticeai/core/mcp_catalog.py +0 -452
- package/latticeai/core/mcp_registry.py +0 -506
- package/latticeai/core/network_boundary.py +0 -168
- package/latticeai/core/oidc.py +0 -208
- package/latticeai/core/plugins.py +0 -432
- package/latticeai/core/product_hardening.py +0 -218
- package/latticeai/core/project_sessions.py +0 -337
- package/latticeai/core/realtime.py +0 -238
- package/latticeai/core/run_explain.py +0 -426
- package/latticeai/core/run_store.py +0 -252
- package/latticeai/core/timezones.py +0 -80
- package/latticeai/core/workspace_computer_memory.py +0 -84
- package/latticeai/core/workspace_graph_trace.py +0 -155
- package/latticeai/core/workspace_indexing.py +0 -102
- package/latticeai/core/workspace_memory.py +0 -77
- package/latticeai/core/workspace_onboarding.py +0 -104
- package/latticeai/core/workspace_os.py +0 -978
- package/latticeai/core/workspace_os_constants.py +0 -126
- package/latticeai/core/workspace_os_state.py +0 -180
- package/latticeai/core/workspace_os_utils.py +0 -103
- package/latticeai/core/workspace_permissions.py +0 -101
- package/latticeai/core/workspace_plugins.py +0 -97
- package/latticeai/core/workspace_relationships.py +0 -99
- package/latticeai/core/workspace_reorganization.py +0 -335
- package/latticeai/core/workspace_review_items.py +0 -112
- package/latticeai/core/workspace_runs.py +0 -726
- package/latticeai/core/workspace_skills.py +0 -109
- package/latticeai/core/workspace_snapshots.py +0 -198
- package/latticeai/core/workspace_timeline.py +0 -110
- package/latticeai/integrations/__init__.py +0 -0
- package/latticeai/integrations/telegram_bot/__init__.py +0 -123
- package/latticeai/integrations/telegram_bot/__main__.py +0 -17
- package/latticeai/integrations/telegram_bot/config.py +0 -86
- package/latticeai/integrations/telegram_bot/dispatch.py +0 -311
- package/latticeai/integrations/telegram_bot/flows.py +0 -478
- package/latticeai/integrations/telegram_bot/helpers.py +0 -322
- package/latticeai/integrations/telegram_bot/screens.py +0 -394
- package/latticeai/runtime/audit_runtime.py +0 -76
- package/latticeai/runtime/automation_runtime.py +0 -81
- package/latticeai/runtime/chat_wiring.py +0 -141
- package/latticeai/runtime/context_runtime.py +0 -66
- package/latticeai/runtime/feature_toggle_wiring.py +0 -157
- package/latticeai/runtime/history_runtime.py +0 -163
- package/latticeai/runtime/history_writer.py +0 -138
- package/latticeai/runtime/hooks_runtime.py +0 -77
- package/latticeai/runtime/model_wiring.py +0 -68
- package/latticeai/runtime/namespace_runtime.py +0 -163
- package/latticeai/runtime/network_boundary_wiring.py +0 -117
- package/latticeai/runtime/network_config_runtime.py +0 -56
- package/latticeai/runtime/permission_mode_wiring.py +0 -112
- package/latticeai/runtime/persistence_runtime.py +0 -159
- package/latticeai/runtime/platform_runtime_wiring.py +0 -89
- package/latticeai/runtime/review_wiring.py +0 -42
- package/latticeai/runtime/router_registration.py +0 -693
- package/latticeai/runtime/service_singletons.py +0 -55
- package/latticeai/runtime/sso_config_runtime.py +0 -128
- package/latticeai/runtime/user_key_runtime.py +0 -106
- package/latticeai/runtime/web_runtime.py +0 -92
- package/latticeai/server_app.py +0 -51
- package/latticeai/services/app_context.py +0 -130
- package/latticeai/services/automation_execution.py +0 -266
- package/latticeai/services/automation_intelligence.py +0 -614
- package/latticeai/services/brain_automation.py +0 -191
- package/latticeai/services/brain_intelligence/__init__.py +0 -58
- package/latticeai/services/brain_intelligence/_contract.py +0 -71
- package/latticeai/services/brain_intelligence/consistency.py +0 -193
- package/latticeai/services/brain_intelligence/constants.py +0 -47
- package/latticeai/services/brain_intelligence/digest.py +0 -258
- package/latticeai/services/brain_intelligence/health.py +0 -331
- package/latticeai/services/brain_intelligence/proposals.py +0 -259
- package/latticeai/services/brain_intelligence/sampling.py +0 -84
- package/latticeai/services/brain_intelligence/service.py +0 -48
- package/latticeai/services/change_proposals.py +0 -471
- package/latticeai/services/chat_service.py +0 -243
- package/latticeai/services/chronicle.py +0 -555
- package/latticeai/services/cloud_egress_audit.py +0 -85
- package/latticeai/services/cloud_extraction.py +0 -129
- package/latticeai/services/cloud_streaming.py +0 -268
- package/latticeai/services/cloud_token_guard.py +0 -84
- package/latticeai/services/command_center.py +0 -548
- package/latticeai/services/evidence_actions.py +0 -258
- package/latticeai/services/feature_toggles.py +0 -502
- package/latticeai/services/folder_watch.py +0 -520
- package/latticeai/services/funnel_metrics.py +0 -307
- package/latticeai/services/hybrid_chat.py +0 -316
- package/latticeai/services/hybrid_context.py +0 -228
- package/latticeai/services/hybrid_policy.py +0 -129
- package/latticeai/services/interop_bridges.py +0 -978
- package/latticeai/services/local_knowledge.py +0 -465
- package/latticeai/services/memory_service/__init__.py +0 -52
- package/latticeai/services/memory_service/_contract.py +0 -100
- package/latticeai/services/memory_service/brief.py +0 -431
- package/latticeai/services/memory_service/constants.py +0 -57
- package/latticeai/services/memory_service/maintenance.py +0 -138
- package/latticeai/services/memory_service/manager.py +0 -186
- package/latticeai/services/memory_service/proof.py +0 -136
- package/latticeai/services/memory_service/recall.py +0 -225
- package/latticeai/services/memory_service/service.py +0 -48
- package/latticeai/services/memory_service/stores.py +0 -110
- package/latticeai/services/mode_store.py +0 -132
- package/latticeai/services/model_recommendation.py +0 -224
- package/latticeai/services/model_runtime/cloud.py +0 -87
- package/latticeai/services/network_boundary_service.py +0 -117
- package/latticeai/services/obsidian_bridge.py +0 -609
- package/latticeai/services/openai_compatible_adapter.py +0 -101
- package/latticeai/services/permission_mode_service.py +0 -122
- package/latticeai/services/platform_runtime.py +0 -366
- package/latticeai/services/review_queue.py +0 -380
- package/latticeai/services/router_context.py +0 -59
- package/latticeai/services/run_executor.py +0 -387
- package/latticeai/services/self_model_service.py +0 -171
- package/latticeai/services/setup_detection.py +0 -147
- package/latticeai/services/triggers.py +0 -378
- package/latticeai/services/upload_service.py +0 -172
- package/latticeai/services/workspace_service.py +0 -165
- package/latticeai/setup/__init__.py +0 -25
- package/latticeai/setup/auto_setup.py +0 -846
- package/latticeai/setup/demo_corpus.py +0 -98
- package/latticeai/setup/wizard/__init__.py +0 -126
- package/latticeai/setup/wizard/catalog.py +0 -175
- package/latticeai/setup/wizard/detect.py +0 -302
- package/latticeai/setup/wizard/install.py +0 -348
- package/latticeai/setup/wizard/paths.py +0 -165
- package/latticeai/setup/wizard/plans.py +0 -74
- package/latticeai/setup/wizard/recommend.py +0 -320
- package/scripts/bench_agent_smoke.py +0 -409
- package/scripts/bench_models.py +0 -540
- package/scripts/bench_vector_index.py +0 -295
- package/scripts/funnel_soft_gate.py +0 -192
- package/scripts/generate_agent_loop_fixtures.py +0 -994
- package/scripts/generate_rust_parity_fixtures.py +0 -908
- package/scripts/migrate_brain_storage.py +0 -57
- package/scripts/parity_fixture_corpus_context.py +0 -162
- package/scripts/parity_fixture_corpus_docgen.py +0 -341
- package/scripts/profile_kg.py +0 -355
- package/server.py +0 -30
- package/static/app/assets/arrow-left-kfsrk0mv.js +0 -1
- package/static/app/assets/circle-check-qqLug9nU.js +0 -1
- package/static/app/assets/index-DxmOfNRi.css +0 -2
- package/static/app/assets/search-BcHqkjoy.js +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
"""A picture as a first-class memory: what
|
|
1
|
+
"""A picture as a first-class memory: what can be observed about one file.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
:func:`extract_image_facts` observes one file — size, OCR, caption, vector, each
|
|
4
|
+
with its own status — and answers with :class:`ImageFacts`. Turning those facts
|
|
5
|
+
into an ``Image`` node with its chunks, concepts and source link was the other
|
|
6
|
+
half of this module; ``lattice-core``'s graph write engine owns it since
|
|
7
|
+
v11.6.0, and this side never learned what a node was anyway.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
@@ -12,20 +12,14 @@ from __future__ import annotations
|
|
|
12
12
|
import base64
|
|
13
13
|
import io
|
|
14
14
|
from dataclasses import dataclass
|
|
15
|
-
from pathlib import Path
|
|
16
15
|
from typing import Any, Callable, Dict, List, Optional
|
|
17
16
|
|
|
18
17
|
from ..quiet import quiet
|
|
19
|
-
from ..utils import utc_now_iso
|
|
20
18
|
from .common import (
|
|
21
19
|
MAX_INDEX_TEXT_CHARS,
|
|
22
20
|
MAX_THUMBNAIL_CHARS,
|
|
23
21
|
MODALITY_IMAGE,
|
|
24
|
-
SUMMARY_CHARS,
|
|
25
22
|
THUMBNAIL_EDGE,
|
|
26
|
-
_sha256_file,
|
|
27
|
-
_sha256_text,
|
|
28
|
-
_split_index_text,
|
|
29
23
|
)
|
|
30
24
|
from .ports import MultimodalPorts
|
|
31
25
|
|
|
@@ -244,255 +238,3 @@ def image_quality_score(facts: ImageFacts) -> Dict[str, Any]:
|
|
|
244
238
|
return {"score": round(max(0.0, min(1.0, score)), 4), "reasons": reasons}
|
|
245
239
|
|
|
246
240
|
|
|
247
|
-
def image_node_id(content_hash: str, workspace_id: Optional[str] = None) -> str:
|
|
248
|
-
"""Workspace-scoped, content-addressed id — re-ingesting is idempotent."""
|
|
249
|
-
scoped = f"{workspace_id or 'legacy-global'}|{content_hash}"
|
|
250
|
-
return f"image:{_sha256_text(scoped)[:24]}"
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
def write_image_memory(
|
|
254
|
-
store: Any,
|
|
255
|
-
*,
|
|
256
|
-
path: Path,
|
|
257
|
-
facts: ImageFacts,
|
|
258
|
-
title: str,
|
|
259
|
-
source_type: str = MODALITY_IMAGE,
|
|
260
|
-
source_uri: Optional[str] = None,
|
|
261
|
-
owner: Optional[str] = None,
|
|
262
|
-
workspace_id: Optional[str] = None,
|
|
263
|
-
conversation_id: Optional[str] = None,
|
|
264
|
-
captured_at: Optional[str] = None,
|
|
265
|
-
modified_at: Optional[str] = None,
|
|
266
|
-
permissions: Optional[Dict[str, Any]] = None,
|
|
267
|
-
extra_metadata: Optional[Dict[str, Any]] = None,
|
|
268
|
-
) -> Dict[str, Any]:
|
|
269
|
-
"""Write one ``Image`` node (plus ``ImageText``/chunks) into the graph.
|
|
270
|
-
|
|
271
|
-
The node is the image itself rather than a ``Document`` that happens to be
|
|
272
|
-
a picture, because that is what the rest of the product reasons about: the
|
|
273
|
-
graph already declares ``Image``/``ImageText``/``CONTAINS_IMAGE``, and
|
|
274
|
-
``hybrid_search`` already ranks ``Image`` as a first-class result type.
|
|
275
|
-
|
|
276
|
-
Uses the same cross-mixin write door (``_upsert_node``/``_upsert_edge``/
|
|
277
|
-
``_upsert_chunk``) that every other ingest path uses — the store exposes no
|
|
278
|
-
public node writer, and inventing a second one for images would be a
|
|
279
|
-
parallel write path to keep in sync forever.
|
|
280
|
-
"""
|
|
281
|
-
captured_at = captured_at or utc_now_iso()
|
|
282
|
-
content_hash = _sha256_file(path)
|
|
283
|
-
node_id = image_node_id(content_hash, workspace_id)
|
|
284
|
-
index_text = facts.index_text()
|
|
285
|
-
metadata: Dict[str, Any] = {
|
|
286
|
-
"filename": path.name,
|
|
287
|
-
"file_path": str(path),
|
|
288
|
-
"ext": path.suffix.lower(),
|
|
289
|
-
"bytes": path.stat().st_size,
|
|
290
|
-
"sha256": content_hash,
|
|
291
|
-
"content_hash": content_hash,
|
|
292
|
-
"source_type": source_type,
|
|
293
|
-
"source_uri": source_uri or str(path),
|
|
294
|
-
"captured_at": captured_at,
|
|
295
|
-
"modified_at": modified_at,
|
|
296
|
-
"owner": owner,
|
|
297
|
-
"workspace_id": workspace_id,
|
|
298
|
-
"permissions": permissions or {},
|
|
299
|
-
"conversation_id": conversation_id,
|
|
300
|
-
**facts.as_metadata(),
|
|
301
|
-
**(extra_metadata or {}),
|
|
302
|
-
}
|
|
303
|
-
# Honest summary: when nothing could be read out of the picture, say so
|
|
304
|
-
# rather than leaving a blank card that looks like a failed render.
|
|
305
|
-
summary = index_text[:SUMMARY_CHARS] or f"[{MODALITY_IMAGE}] {path.name}"
|
|
306
|
-
chunk_ids: List[str] = []
|
|
307
|
-
|
|
308
|
-
with store._connect() as conn:
|
|
309
|
-
duplicate = (
|
|
310
|
-
conn.execute("SELECT 1 FROM nodes WHERE id=? LIMIT 1", (node_id,)).fetchone()
|
|
311
|
-
is not None
|
|
312
|
-
)
|
|
313
|
-
store._upsert_node(
|
|
314
|
-
conn,
|
|
315
|
-
node_id,
|
|
316
|
-
"Image",
|
|
317
|
-
title or path.name,
|
|
318
|
-
summary=summary,
|
|
319
|
-
metadata=metadata,
|
|
320
|
-
raw=metadata,
|
|
321
|
-
owner=owner,
|
|
322
|
-
workspace_id=workspace_id,
|
|
323
|
-
)
|
|
324
|
-
if facts.ocr_text:
|
|
325
|
-
image_text_id = f"imagetext:{_sha256_text(f'{node_id}:ocr')[:24]}"
|
|
326
|
-
store._upsert_node(
|
|
327
|
-
conn,
|
|
328
|
-
image_text_id,
|
|
329
|
-
"ImageText",
|
|
330
|
-
f"{path.name} OCR",
|
|
331
|
-
summary=facts.ocr_text[:700],
|
|
332
|
-
metadata={
|
|
333
|
-
"source_node": node_id,
|
|
334
|
-
"chars": len(facts.ocr_text),
|
|
335
|
-
"workspace_id": workspace_id,
|
|
336
|
-
},
|
|
337
|
-
owner=owner,
|
|
338
|
-
workspace_id=workspace_id,
|
|
339
|
-
)
|
|
340
|
-
store._upsert_edge(
|
|
341
|
-
conn,
|
|
342
|
-
node_id,
|
|
343
|
-
image_text_id,
|
|
344
|
-
"포함함",
|
|
345
|
-
weight=0.8,
|
|
346
|
-
metadata={"source": "ocr", "workspace_id": workspace_id},
|
|
347
|
-
)
|
|
348
|
-
for index, piece in enumerate(_split_index_text(index_text)):
|
|
349
|
-
chunk_id = f"chunk:{_sha256_text(f'{node_id}:{index}:{piece}')[:24]}"
|
|
350
|
-
chunk_ids.append(chunk_id)
|
|
351
|
-
chunk_meta = {
|
|
352
|
-
"index": index,
|
|
353
|
-
"source_node": node_id,
|
|
354
|
-
"workspace_id": workspace_id,
|
|
355
|
-
"modality": MODALITY_IMAGE,
|
|
356
|
-
}
|
|
357
|
-
store._upsert_node(
|
|
358
|
-
conn,
|
|
359
|
-
chunk_id,
|
|
360
|
-
"Chunk",
|
|
361
|
-
f"{path.name} chunk {index + 1}",
|
|
362
|
-
summary=piece[:SUMMARY_CHARS],
|
|
363
|
-
metadata=chunk_meta,
|
|
364
|
-
owner=owner,
|
|
365
|
-
workspace_id=workspace_id,
|
|
366
|
-
)
|
|
367
|
-
store._upsert_chunk(
|
|
368
|
-
conn,
|
|
369
|
-
chunk_id=chunk_id,
|
|
370
|
-
source_node=node_id,
|
|
371
|
-
text=piece,
|
|
372
|
-
metadata=chunk_meta,
|
|
373
|
-
)
|
|
374
|
-
store._upsert_edge(conn, node_id, chunk_id, "포함함")
|
|
375
|
-
# Concepts come from what is *in* the picture, never from its name:
|
|
376
|
-
# "IMG_2381" is not a topic, and turning filenames into concept nodes
|
|
377
|
-
# would fill the graph with hubs that mean nothing.
|
|
378
|
-
concept_ids = _attach_concepts(
|
|
379
|
-
store,
|
|
380
|
-
conn,
|
|
381
|
-
node_id=node_id,
|
|
382
|
-
text=index_text,
|
|
383
|
-
owner=owner,
|
|
384
|
-
workspace_id=workspace_id,
|
|
385
|
-
)
|
|
386
|
-
source_node_id = _attach_source(
|
|
387
|
-
store,
|
|
388
|
-
conn,
|
|
389
|
-
node_id=node_id,
|
|
390
|
-
source_type=source_type,
|
|
391
|
-
source_uri=source_uri or str(path),
|
|
392
|
-
title=title or path.name,
|
|
393
|
-
content_hash=content_hash,
|
|
394
|
-
captured_at=captured_at,
|
|
395
|
-
owner=owner,
|
|
396
|
-
workspace_id=workspace_id,
|
|
397
|
-
)
|
|
398
|
-
metadata["concepts"] = concept_ids
|
|
399
|
-
return {
|
|
400
|
-
"node_id": node_id,
|
|
401
|
-
"type": "Image",
|
|
402
|
-
"title": title or path.name,
|
|
403
|
-
"sha256": content_hash,
|
|
404
|
-
"content_hash": content_hash,
|
|
405
|
-
"source_node_id": source_node_id,
|
|
406
|
-
"chunk_ids": chunk_ids,
|
|
407
|
-
"chunk_count": len(chunk_ids),
|
|
408
|
-
"duplicate": duplicate,
|
|
409
|
-
"captured_at": captured_at,
|
|
410
|
-
"metadata": metadata,
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
def _attach_concepts(
|
|
415
|
-
store: Any,
|
|
416
|
-
conn: Any,
|
|
417
|
-
*,
|
|
418
|
-
node_id: str,
|
|
419
|
-
text: str,
|
|
420
|
-
owner: Optional[str],
|
|
421
|
-
workspace_id: Optional[str],
|
|
422
|
-
) -> List[str]:
|
|
423
|
-
"""Pull concepts out of the caption/OCR so the picture joins the graph.
|
|
424
|
-
|
|
425
|
-
This is what "the caption contributes to the graph" means concretely: the
|
|
426
|
-
same extractor every text door uses, over the same text, producing the same
|
|
427
|
-
``Concept``/``Feature``/… nodes and ``포함함`` edges — so a photo of a
|
|
428
|
-
whiteboard about Q3 planning is one hop from every note about Q3 planning,
|
|
429
|
-
instead of being an island that only exact search can reach.
|
|
430
|
-
|
|
431
|
-
An image with nothing readable in it yields no concepts, which is correct:
|
|
432
|
-
there is nothing to say about it.
|
|
433
|
-
"""
|
|
434
|
-
body = str(text or "").strip()
|
|
435
|
-
if not body:
|
|
436
|
-
return []
|
|
437
|
-
from ..graph._kg_common import ( # local: keeps this module's import light
|
|
438
|
-
_classify_node_type,
|
|
439
|
-
_extract_concepts,
|
|
440
|
-
)
|
|
441
|
-
|
|
442
|
-
# The id derivation is imported rather than re-derived: two copies of a
|
|
443
|
-
# node-id rule diverge, and a diverged id is a duplicate concept nobody
|
|
444
|
-
# can see.
|
|
445
|
-
from ..graph.ingest import _scoped_slug_id
|
|
446
|
-
|
|
447
|
-
concept_ids: List[str] = []
|
|
448
|
-
for concept in _extract_concepts(body, limit=10):
|
|
449
|
-
node_type = _classify_node_type(concept, body)
|
|
450
|
-
concept_id = _scoped_slug_id(node_type.lower(), concept, workspace_id)
|
|
451
|
-
store._upsert_node(
|
|
452
|
-
conn,
|
|
453
|
-
concept_id,
|
|
454
|
-
node_type,
|
|
455
|
-
concept,
|
|
456
|
-
metadata={
|
|
457
|
-
"auto_extracted": True,
|
|
458
|
-
"source_node": node_id,
|
|
459
|
-
"modality": MODALITY_IMAGE,
|
|
460
|
-
"workspace_id": workspace_id,
|
|
461
|
-
},
|
|
462
|
-
owner=owner,
|
|
463
|
-
workspace_id=workspace_id,
|
|
464
|
-
)
|
|
465
|
-
store._upsert_edge(conn, node_id, concept_id, "포함함", weight=0.8)
|
|
466
|
-
concept_ids.append(concept_id)
|
|
467
|
-
return concept_ids
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
def _attach_source(
|
|
471
|
-
store: Any,
|
|
472
|
-
conn: Any,
|
|
473
|
-
*,
|
|
474
|
-
node_id: str,
|
|
475
|
-
source_type: str,
|
|
476
|
-
source_uri: str,
|
|
477
|
-
title: str,
|
|
478
|
-
content_hash: str,
|
|
479
|
-
captured_at: str,
|
|
480
|
-
owner: Optional[str],
|
|
481
|
-
workspace_id: Optional[str],
|
|
482
|
-
) -> Optional[str]:
|
|
483
|
-
"""Link the image to a ``Source`` node when the store can make one."""
|
|
484
|
-
attach = getattr(store, "_attach_source_node", None)
|
|
485
|
-
if not callable(attach):
|
|
486
|
-
return None
|
|
487
|
-
return str(
|
|
488
|
-
attach(
|
|
489
|
-
conn,
|
|
490
|
-
node_id,
|
|
491
|
-
source_type=source_type,
|
|
492
|
-
source_uri=source_uri,
|
|
493
|
-
title=title,
|
|
494
|
-
content_hash=content_hash,
|
|
495
|
-
captured_at=captured_at,
|
|
496
|
-
extra={"owner": owner, "workspace_id": workspace_id, "modality": MODALITY_IMAGE},
|
|
497
|
-
)
|
|
498
|
-
)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
``ports.py`` beside the decoder it
|
|
1
|
+
"""What can be read off a video file: its container facts and its subtitles.
|
|
2
|
+
|
|
3
|
+
Writing the ``Video`` node, the keyframe stills and the edges between them is
|
|
4
|
+
``lattice-core``'s since v11.6.0. What is left is the reading:
|
|
5
|
+
:func:`read_video_facts` opens the container, :func:`find_subtitle` looks for
|
|
6
|
+
the ``.srt``/``.vtt`` beside it and :func:`parse_subtitles` turns cues into
|
|
7
|
+
plain text. Keyframe *extraction* lives in ``ports.py`` beside the decoder it
|
|
8
|
+
falls back to.
|
|
8
9
|
"""
|
|
9
10
|
|
|
10
11
|
from __future__ import annotations
|
|
@@ -15,22 +16,10 @@ from pathlib import Path
|
|
|
15
16
|
from typing import Any, Dict, List, Optional
|
|
16
17
|
|
|
17
18
|
from ..quiet import quiet
|
|
18
|
-
from ..utils import utc_now_iso
|
|
19
19
|
from .common import (
|
|
20
20
|
MAX_INDEX_TEXT_CHARS,
|
|
21
|
-
MODALITY_IMAGE,
|
|
22
21
|
MODALITY_VIDEO,
|
|
23
22
|
SUBTITLE_EXTENSIONS,
|
|
24
|
-
SUMMARY_CHARS,
|
|
25
|
-
_sha256_file,
|
|
26
|
-
_sha256_text,
|
|
27
|
-
_split_index_text,
|
|
28
|
-
)
|
|
29
|
-
from .images import (
|
|
30
|
-
_attach_concepts,
|
|
31
|
-
_attach_source,
|
|
32
|
-
extract_image_facts,
|
|
33
|
-
write_image_memory,
|
|
34
23
|
)
|
|
35
24
|
from .ports import DEFAULT_KEYFRAMES, MultimodalPorts, extract_keyframes
|
|
36
25
|
|
|
@@ -180,231 +169,3 @@ def video_quality_score(facts: VideoFacts) -> Dict[str, Any]:
|
|
|
180
169
|
return {"score": round(max(0.0, min(1.0, score)), 4), "reasons": reasons}
|
|
181
170
|
|
|
182
171
|
|
|
183
|
-
def video_node_id(content_hash: str, workspace_id: Optional[str] = None) -> str:
|
|
184
|
-
"""Workspace-scoped, content-addressed id — re-ingesting is idempotent."""
|
|
185
|
-
scoped = f"{workspace_id or 'legacy-global'}|{content_hash}"
|
|
186
|
-
return f"video:{_sha256_text(scoped)[:24]}"
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
def video_frame_dir(blob_dir: Any, content_hash: str) -> Path:
|
|
190
|
-
"""Where this video's stills live: content-addressed, stable across runs.
|
|
191
|
-
|
|
192
|
-
Deliberately under the Brain's own blob directory rather than a temp dir.
|
|
193
|
-
A frame referenced by an ``Image`` node has to still be there the next time
|
|
194
|
-
someone opens that memory, and a backup that copies the blobs copies these.
|
|
195
|
-
"""
|
|
196
|
-
return Path(str(blob_dir)) / "video_frames" / str(content_hash)[:32]
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
def write_video_memory(
|
|
200
|
-
store: Any,
|
|
201
|
-
*,
|
|
202
|
-
path: Path,
|
|
203
|
-
facts: VideoFacts,
|
|
204
|
-
title: str,
|
|
205
|
-
source_type: str = MODALITY_VIDEO,
|
|
206
|
-
source_uri: Optional[str] = None,
|
|
207
|
-
owner: Optional[str] = None,
|
|
208
|
-
workspace_id: Optional[str] = None,
|
|
209
|
-
conversation_id: Optional[str] = None,
|
|
210
|
-
captured_at: Optional[str] = None,
|
|
211
|
-
modified_at: Optional[str] = None,
|
|
212
|
-
permissions: Optional[Dict[str, Any]] = None,
|
|
213
|
-
extra_metadata: Optional[Dict[str, Any]] = None,
|
|
214
|
-
ports: Optional[MultimodalPorts] = None,
|
|
215
|
-
) -> Dict[str, Any]:
|
|
216
|
-
"""Write one ``Video`` node, its keyframes, and its subtitles.
|
|
217
|
-
|
|
218
|
-
Each keyframe goes through the **existing image path** — the same
|
|
219
|
-
:func:`extract_image_facts` and :func:`write_image_memory` a photograph
|
|
220
|
-
uses — so a still from a video is OCR'd, captioned, vectorized and made
|
|
221
|
-
searchable by exactly the machinery that already does that, and joined to
|
|
222
|
-
its video by ``CONTAINS_IMAGE``. Subtitles ride the ordinary text index as
|
|
223
|
-
chunks. Nothing about video gets its own retrieval path.
|
|
224
|
-
|
|
225
|
-
The frames are written before the video node so their (short-lived) write
|
|
226
|
-
transactions never nest inside the video's.
|
|
227
|
-
"""
|
|
228
|
-
ports = ports or MultimodalPorts()
|
|
229
|
-
captured_at = captured_at or utc_now_iso()
|
|
230
|
-
content_hash = _sha256_file(path)
|
|
231
|
-
node_id = video_node_id(content_hash, workspace_id)
|
|
232
|
-
frames = _write_keyframes(
|
|
233
|
-
store,
|
|
234
|
-
facts=facts,
|
|
235
|
-
title=title,
|
|
236
|
-
owner=owner,
|
|
237
|
-
workspace_id=workspace_id,
|
|
238
|
-
conversation_id=conversation_id,
|
|
239
|
-
captured_at=captured_at,
|
|
240
|
-
permissions=permissions,
|
|
241
|
-
ports=ports,
|
|
242
|
-
)
|
|
243
|
-
body = facts.subtitle_text or (
|
|
244
|
-
f"[{MODALITY_VIDEO}] {title}\n"
|
|
245
|
-
"이 영상에는 자막이 없어 말의 내용은 검색되지 않습니다 — "
|
|
246
|
-
"대신 대표 장면 이미지로 찾을 수 있습니다."
|
|
247
|
-
)
|
|
248
|
-
metadata: Dict[str, Any] = {
|
|
249
|
-
"filename": path.name,
|
|
250
|
-
"file_path": str(path),
|
|
251
|
-
"ext": path.suffix.lower(),
|
|
252
|
-
"bytes": path.stat().st_size,
|
|
253
|
-
"sha256": content_hash,
|
|
254
|
-
"content_hash": content_hash,
|
|
255
|
-
"source_type": source_type,
|
|
256
|
-
"source_uri": source_uri or str(path),
|
|
257
|
-
"captured_at": captured_at,
|
|
258
|
-
"modified_at": modified_at,
|
|
259
|
-
"owner": owner,
|
|
260
|
-
"workspace_id": workspace_id,
|
|
261
|
-
"permissions": permissions or {},
|
|
262
|
-
"conversation_id": conversation_id,
|
|
263
|
-
"keyframe_nodes": [frame["node_id"] for frame in frames],
|
|
264
|
-
**facts.as_metadata(),
|
|
265
|
-
**(extra_metadata or {}),
|
|
266
|
-
}
|
|
267
|
-
# Honest card: a video nobody captioned says so, rather than rendering as a
|
|
268
|
-
# blank summary that looks like a failed read.
|
|
269
|
-
summary = body[:SUMMARY_CHARS]
|
|
270
|
-
chunk_ids: List[str] = []
|
|
271
|
-
with store._connect() as conn:
|
|
272
|
-
duplicate = (
|
|
273
|
-
conn.execute("SELECT 1 FROM nodes WHERE id=? LIMIT 1", (node_id,)).fetchone()
|
|
274
|
-
is not None
|
|
275
|
-
)
|
|
276
|
-
store._upsert_node(
|
|
277
|
-
conn,
|
|
278
|
-
node_id,
|
|
279
|
-
VIDEO_NODE_TYPE,
|
|
280
|
-
title or path.name,
|
|
281
|
-
summary=summary,
|
|
282
|
-
metadata=metadata,
|
|
283
|
-
raw=metadata,
|
|
284
|
-
owner=owner,
|
|
285
|
-
workspace_id=workspace_id,
|
|
286
|
-
)
|
|
287
|
-
for frame in frames:
|
|
288
|
-
store._upsert_edge(
|
|
289
|
-
conn,
|
|
290
|
-
node_id,
|
|
291
|
-
frame["node_id"],
|
|
292
|
-
VIDEO_FRAME_RELATION,
|
|
293
|
-
weight=0.8,
|
|
294
|
-
metadata={
|
|
295
|
-
"source": VIDEO_FRAME_SOURCE_TYPE,
|
|
296
|
-
"index": frame["index"],
|
|
297
|
-
"workspace_id": workspace_id,
|
|
298
|
-
},
|
|
299
|
-
)
|
|
300
|
-
for index, piece in enumerate(_split_index_text(facts.subtitle_text)):
|
|
301
|
-
chunk_id = f"chunk:{_sha256_text(f'{node_id}:{index}:{piece}')[:24]}"
|
|
302
|
-
chunk_ids.append(chunk_id)
|
|
303
|
-
chunk_meta = {
|
|
304
|
-
"index": index,
|
|
305
|
-
"source_node": node_id,
|
|
306
|
-
"workspace_id": workspace_id,
|
|
307
|
-
"modality": MODALITY_VIDEO,
|
|
308
|
-
}
|
|
309
|
-
store._upsert_node(
|
|
310
|
-
conn,
|
|
311
|
-
chunk_id,
|
|
312
|
-
"Chunk",
|
|
313
|
-
f"{path.name} chunk {index + 1}",
|
|
314
|
-
summary=piece[:SUMMARY_CHARS],
|
|
315
|
-
metadata=chunk_meta,
|
|
316
|
-
owner=owner,
|
|
317
|
-
workspace_id=workspace_id,
|
|
318
|
-
)
|
|
319
|
-
store._upsert_chunk(
|
|
320
|
-
conn,
|
|
321
|
-
chunk_id=chunk_id,
|
|
322
|
-
source_node=node_id,
|
|
323
|
-
text=piece,
|
|
324
|
-
metadata=chunk_meta,
|
|
325
|
-
)
|
|
326
|
-
store._upsert_edge(conn, node_id, chunk_id, "포함함")
|
|
327
|
-
concept_ids = _attach_concepts(
|
|
328
|
-
store,
|
|
329
|
-
conn,
|
|
330
|
-
node_id=node_id,
|
|
331
|
-
text=facts.subtitle_text,
|
|
332
|
-
owner=owner,
|
|
333
|
-
workspace_id=workspace_id,
|
|
334
|
-
)
|
|
335
|
-
source_node_id = _attach_source(
|
|
336
|
-
store,
|
|
337
|
-
conn,
|
|
338
|
-
node_id=node_id,
|
|
339
|
-
source_type=source_type,
|
|
340
|
-
source_uri=source_uri or str(path),
|
|
341
|
-
title=title or path.name,
|
|
342
|
-
content_hash=content_hash,
|
|
343
|
-
captured_at=captured_at,
|
|
344
|
-
owner=owner,
|
|
345
|
-
workspace_id=workspace_id,
|
|
346
|
-
)
|
|
347
|
-
metadata["concepts"] = concept_ids
|
|
348
|
-
return {
|
|
349
|
-
"node_id": node_id,
|
|
350
|
-
"type": VIDEO_NODE_TYPE,
|
|
351
|
-
"title": title or path.name,
|
|
352
|
-
"sha256": content_hash,
|
|
353
|
-
"content_hash": content_hash,
|
|
354
|
-
"source_node_id": source_node_id,
|
|
355
|
-
"chunk_ids": chunk_ids,
|
|
356
|
-
"chunk_count": len(chunk_ids),
|
|
357
|
-
"duplicate": duplicate,
|
|
358
|
-
"captured_at": captured_at,
|
|
359
|
-
"keyframes": frames,
|
|
360
|
-
"metadata": metadata,
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
def _write_keyframes(
|
|
365
|
-
store: Any,
|
|
366
|
-
*,
|
|
367
|
-
facts: VideoFacts,
|
|
368
|
-
title: str,
|
|
369
|
-
owner: Optional[str],
|
|
370
|
-
workspace_id: Optional[str],
|
|
371
|
-
conversation_id: Optional[str],
|
|
372
|
-
captured_at: str,
|
|
373
|
-
permissions: Optional[Dict[str, Any]],
|
|
374
|
-
ports: MultimodalPorts,
|
|
375
|
-
) -> List[Dict[str, Any]]:
|
|
376
|
-
"""Every extracted still, through the ordinary image door."""
|
|
377
|
-
written: List[Dict[str, Any]] = []
|
|
378
|
-
for index, frame_path in enumerate(facts.keyframes):
|
|
379
|
-
frame = Path(frame_path)
|
|
380
|
-
image_facts = extract_image_facts(str(frame), ports=ports)
|
|
381
|
-
if not image_facts.readable:
|
|
382
|
-
# A frame ffmpeg wrote that Pillow cannot open is a state worth
|
|
383
|
-
# skipping, not worth failing the whole video over.
|
|
384
|
-
continue
|
|
385
|
-
result = write_image_memory(
|
|
386
|
-
store,
|
|
387
|
-
path=frame,
|
|
388
|
-
facts=image_facts,
|
|
389
|
-
title=f"{title} · 장면 {index + 1}",
|
|
390
|
-
source_type=VIDEO_FRAME_SOURCE_TYPE,
|
|
391
|
-
source_uri=str(frame),
|
|
392
|
-
owner=owner,
|
|
393
|
-
workspace_id=workspace_id,
|
|
394
|
-
conversation_id=conversation_id,
|
|
395
|
-
captured_at=captured_at,
|
|
396
|
-
permissions=permissions,
|
|
397
|
-
extra_metadata={
|
|
398
|
-
"modality": MODALITY_IMAGE,
|
|
399
|
-
"video_path": facts.path,
|
|
400
|
-
"keyframe_index": index,
|
|
401
|
-
},
|
|
402
|
-
)
|
|
403
|
-
written.append({
|
|
404
|
-
"node_id": result["node_id"],
|
|
405
|
-
"index": index,
|
|
406
|
-
"path": str(frame),
|
|
407
|
-
"ocr_status": image_facts.ocr_status,
|
|
408
|
-
"vision_embedding": image_facts.embedding_status,
|
|
409
|
-
})
|
|
410
|
-
return written
|
|
@@ -1,90 +1,19 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""The tool dispatch lifecycle.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
``
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
- multi_agent.py (핵심 실행체)
|
|
9
|
-
책임: OrchestrationContext, AgentContextPacket, AgentHandoff, AgentRunResult,
|
|
10
|
-
MultiAgentOrchestrator, default_role_runner, llm_role_runner,
|
|
11
|
-
CORE_PIPELINE/AGENT_ROLES, handoff/review/retry 타임라인 생성.
|
|
12
|
-
의존: 없음 (순수, deterministic/LLM runner 주입).
|
|
13
|
-
진입: orchestrator_factory 로 AgentRuntime에 주입됨.
|
|
14
|
-
|
|
15
|
-
- hooks.py (라이프사이클 확장)
|
|
16
|
-
책임: HookContext/HookResult, HooksRegistry (builtin+user, order/enabled persist),
|
|
17
|
-
dispatch_tool (pre_tool/post_tool 통합), fire_hook/run_hooks,
|
|
18
|
-
BUILTIN_HOOKS (redact, memory-snapshot, tool-permission-gate 등).
|
|
19
|
-
의존: subprocess (user command hooks).
|
|
20
|
-
진입: AgentRuntime, tool_dispatch, api/tools, core/agent 등에 주입/등록.
|
|
21
|
-
|
|
22
|
-
- agent_runtime.py (공개 퍼사드 / 바운더리)
|
|
23
|
-
책임: AgentRuntime (store+orchestrator_factory+workspace_graph+audit+hooks 주입),
|
|
24
|
-
start/reserve_run/complete_reserved_run, stop, status/health/config,
|
|
25
|
-
list_runs/get_run/events/replay, _fire_pre_run / _post_run_hooks.
|
|
26
|
-
의존: .multi_agent, .hooks (간접), store (WORKSPACE_OS).
|
|
27
|
-
진입점: **여기가 제품 경계**. app_factory에서 AGENT_RUNTIME으로 생성,
|
|
28
|
-
api/agents.py (런타임 라우터), RunExecutor (async agent/workflow),
|
|
29
|
-
workflow_designer에 주입. frontend BrainAutomationPanel / Act 가
|
|
30
|
-
/agents/* 를 통해 이 바운더리만 의존.
|
|
31
|
-
|
|
32
|
-
- __init__.py : lazy re-export (import 비용 최소화).
|
|
33
|
-
|
|
34
|
-
실제 진입점 매핑 (app_factory + 호출 스택):
|
|
35
|
-
app_factory.py:1508
|
|
36
|
-
AGENT_RUNTIME = AgentRuntime(store=WORKSPACE_OS, orchestrator_factory=PLATFORM.build_orchestrator, hooks=HOOKS_REGISTRY, ...)
|
|
37
|
-
RUN_EXECUTOR = RunExecutor(..., agent_runtime=AGENT_RUNTIME)
|
|
38
|
-
AGENT_RUNTIME.attach_executor(RUN_EXECUTOR)
|
|
39
|
-
create_agents_router(..., agent_runtime=AGENT_RUNTIME)
|
|
40
|
-
create_workflow_designer_router(..., run_executor=RUN_EXECUTOR, trigger_service=TRIGGER_SERVICE)
|
|
41
|
-
api/agents.py:49
|
|
42
|
-
from lattice_brain.runtime.agent_runtime import AgentRuntime
|
|
43
|
-
runtime = agent_runtime or AgentRuntime(...) # fallback
|
|
44
|
-
/agents/api/runtime/* , POST /agents (start via runtime.start or executor)
|
|
45
|
-
api/chat.py
|
|
46
|
-
from latticeai.services.tool_dispatch import build_agent_runtime
|
|
47
|
-
-> latticeai/core/agent/:SingleAgentRuntime (별도 state/plan/transcript 머신, single-agent /agent 경로. dispatch_tool 만 공유)
|
|
48
|
-
latticeai/services/tool_dispatch.py:14
|
|
49
|
-
from latticeai.core.agent import SingleAgentRuntime
|
|
50
|
-
(tool governance + core single-agent용)
|
|
51
|
-
latticeai/services/platform_runtime.py
|
|
52
|
-
from lattice_brain.runtime.{hooks, multi_agent}
|
|
53
|
-
tests: test_hooks_dispatch.py, test_t7_triggers.py 등에서 직접 import lattice_brain.runtime.*
|
|
54
|
-
|
|
55
|
-
core/tool_registry.py (신규) + services/tool_dispatch.py 가 tool build 주도.
|
|
56
|
-
lattice_brain/runtime 는 multi-agent + hooks + facade 에 집중. core/agent 는 chat/agent 단일 루프.
|
|
57
|
-
기존 latticeai.core.agent.AgentRuntime import 는 SingleAgentRuntime 호환 alias 로 유지.
|
|
58
|
-
|
|
59
|
-
이 매핑으로 중복 제거 및 wiring 명확화 완료 (feat(Act, automation) 방향).
|
|
3
|
+
This subsystem held the multi-agent orchestrator, the agent runtime facade and
|
|
4
|
+
the persisted hooks registry. Orchestration is ``lattice-agent``'s and the hooks
|
|
5
|
+
platform is ``lattice-platform``'s since v11.6.0; what remains is
|
|
6
|
+
:func:`~lattice_brain.runtime.hooks.dispatch_tool` and the two context objects a
|
|
7
|
+
bound hook runner is handed.
|
|
60
8
|
"""
|
|
61
9
|
|
|
62
10
|
from __future__ import annotations
|
|
63
11
|
|
|
64
|
-
__all__ = [
|
|
65
|
-
"AgentRuntime",
|
|
66
|
-
"AgentRuntimeUnavailable",
|
|
67
|
-
"MultiAgentOrchestrator",
|
|
68
|
-
"RuntimeBoundaryProtocol",
|
|
69
|
-
"HooksRegistry",
|
|
70
|
-
"dispatch_tool",
|
|
71
|
-
]
|
|
12
|
+
__all__ = ["HookContext", "HookResult", "dispatch_tool"]
|
|
72
13
|
|
|
73
14
|
|
|
74
15
|
def __getattr__(name: str):
|
|
75
|
-
if name in
|
|
76
|
-
from . import agent_runtime
|
|
77
|
-
|
|
78
|
-
return getattr(agent_runtime, name)
|
|
79
|
-
if name == "MultiAgentOrchestrator":
|
|
80
|
-
from .multi_agent import MultiAgentOrchestrator
|
|
81
|
-
|
|
82
|
-
return MultiAgentOrchestrator
|
|
83
|
-
if name == "RuntimeBoundaryProtocol":
|
|
84
|
-
from .contracts import RuntimeBoundaryProtocol
|
|
85
|
-
|
|
86
|
-
return RuntimeBoundaryProtocol
|
|
87
|
-
if name in {"HooksRegistry", "dispatch_tool"}:
|
|
16
|
+
if name in set(__all__):
|
|
88
17
|
from . import hooks
|
|
89
18
|
|
|
90
19
|
return getattr(hooks, name)
|