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
package/lattice_brain/quality.py
DELETED
|
@@ -1,543 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
lattice_brain/quality.py
|
|
4
|
-
Pure Python Quality Layer for Lattice Brain (v6.4+ hardening)
|
|
5
|
-
- Does not modify any DB schema or existing APIs
|
|
6
|
-
- Self-contained, dependency-free core (stdlib only)
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
from __future__ import annotations
|
|
10
|
-
|
|
11
|
-
import hashlib
|
|
12
|
-
import math
|
|
13
|
-
import re
|
|
14
|
-
import time
|
|
15
|
-
from collections import defaultdict
|
|
16
|
-
from dataclasses import dataclass, field
|
|
17
|
-
from typing import Any, Dict, List, Optional
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
# -----------------------------
|
|
21
|
-
# 1. Embedding Fallback Labelling + Drift/Reindex Plan
|
|
22
|
-
# -----------------------------
|
|
23
|
-
@dataclass
|
|
24
|
-
class EmbeddingLabel:
|
|
25
|
-
vector_id: str
|
|
26
|
-
label: str
|
|
27
|
-
confidence: float
|
|
28
|
-
drift_score: float = 0.0
|
|
29
|
-
needs_reindex: bool = False
|
|
30
|
-
|
|
31
|
-
class EmbeddingFallbackLabeller:
|
|
32
|
-
"""Fallback labelling when primary embedding unavailable + drift detection"""
|
|
33
|
-
|
|
34
|
-
def __init__(self, drift_threshold: float = 0.15):
|
|
35
|
-
self.drift_threshold = drift_threshold
|
|
36
|
-
self.label_cache: Dict[str, EmbeddingLabel] = {}
|
|
37
|
-
self.vector_cache: Dict[str, List[float]] = {}
|
|
38
|
-
|
|
39
|
-
def label(self, vector_id: str, embedding: Optional[List[float]] = None,
|
|
40
|
-
metadata: Optional[Dict] = None) -> EmbeddingLabel:
|
|
41
|
-
if embedding is None:
|
|
42
|
-
label = "unembedded_fallback"
|
|
43
|
-
conf = 0.3
|
|
44
|
-
else:
|
|
45
|
-
# Simple hash-based pseudo-label for determinism
|
|
46
|
-
# Not a security hash: a short, stable label for grouping
|
|
47
|
-
# vectors in reports. usedforsecurity=False states that and
|
|
48
|
-
# keeps it working on FIPS builds where md5 is restricted.
|
|
49
|
-
h = hashlib.md5(
|
|
50
|
-
str(embedding[:4]).encode(), usedforsecurity=False
|
|
51
|
-
).hexdigest()[:8]
|
|
52
|
-
label = f"emb_cluster_{h}"
|
|
53
|
-
conf = 0.85
|
|
54
|
-
|
|
55
|
-
drift = self._compute_drift(vector_id, embedding)
|
|
56
|
-
needs_reindex = drift > self.drift_threshold
|
|
57
|
-
|
|
58
|
-
el = EmbeddingLabel(vector_id, label, conf, drift, needs_reindex)
|
|
59
|
-
self.label_cache[vector_id] = el
|
|
60
|
-
if embedding is not None:
|
|
61
|
-
self.vector_cache[vector_id] = [float(value) for value in embedding]
|
|
62
|
-
return el
|
|
63
|
-
|
|
64
|
-
def _compute_drift(self, vector_id: str, new_emb: Optional[List[float]]) -> float:
|
|
65
|
-
old = self.vector_cache.get(vector_id)
|
|
66
|
-
if old is None or new_emb is None:
|
|
67
|
-
return 0.0
|
|
68
|
-
return self._cosine_distance(old, new_emb)
|
|
69
|
-
|
|
70
|
-
def _cosine_distance(self, a: List[float], b: List[float]) -> float:
|
|
71
|
-
if not a or not b or len(a) != len(b):
|
|
72
|
-
return 1.0
|
|
73
|
-
dot = sum(x * y for x, y in zip(a, b, strict=True))
|
|
74
|
-
na = math.sqrt(sum(x*x for x in a))
|
|
75
|
-
nb = math.sqrt(sum(x*x for x in b))
|
|
76
|
-
return 1.0 - (dot / (na*nb + 1e-9))
|
|
77
|
-
|
|
78
|
-
def generate_reindex_plan(self) -> List[str]:
|
|
79
|
-
return [vid for vid, lab in self.label_cache.items() if lab.needs_reindex]
|
|
80
|
-
|
|
81
|
-
# -----------------------------
|
|
82
|
-
# 2. BM25 Lexical Scoring + Hybrid Fusion + Reranker Interface
|
|
83
|
-
# -----------------------------
|
|
84
|
-
class BM25Scorer:
|
|
85
|
-
def __init__(self, k1: float = 1.5, b: float = 0.75):
|
|
86
|
-
self.k1 = k1
|
|
87
|
-
self.b = b
|
|
88
|
-
self.doc_freqs: Dict[str, int] = defaultdict(int)
|
|
89
|
-
self.doc_lens: Dict[str, int] = {}
|
|
90
|
-
self.corpus_size = 0
|
|
91
|
-
self.avgdl = 0.0
|
|
92
|
-
|
|
93
|
-
def fit(self, corpus: Dict[str, str]):
|
|
94
|
-
self.corpus_size = len(corpus)
|
|
95
|
-
total_len = 0
|
|
96
|
-
for did, text in corpus.items():
|
|
97
|
-
tokens = self._tokenize(text)
|
|
98
|
-
self.doc_lens[did] = len(tokens)
|
|
99
|
-
total_len += len(tokens)
|
|
100
|
-
for t in set(tokens):
|
|
101
|
-
self.doc_freqs[t] += 1
|
|
102
|
-
self.avgdl = total_len / max(1, self.corpus_size)
|
|
103
|
-
|
|
104
|
-
def score(self, query: str, doc_id: str, doc_text: str) -> float:
|
|
105
|
-
tokens = self._tokenize(query)
|
|
106
|
-
doc_tokens = self._tokenize(doc_text)
|
|
107
|
-
score = 0.0
|
|
108
|
-
for t in tokens:
|
|
109
|
-
if t not in doc_tokens:
|
|
110
|
-
continue
|
|
111
|
-
tf = doc_tokens.count(t)
|
|
112
|
-
df = self.doc_freqs.get(t, 0)
|
|
113
|
-
idf = math.log((self.corpus_size - df + 0.5) / (df + 0.5) + 1)
|
|
114
|
-
denom = tf + self.k1 * (1 - self.b + self.b * self.doc_lens.get(doc_id, 0) / self.avgdl)
|
|
115
|
-
score += idf * tf * (self.k1 + 1) / (denom + 1e-9)
|
|
116
|
-
return score
|
|
117
|
-
|
|
118
|
-
def _tokenize(self, text: str) -> List[str]:
|
|
119
|
-
return re.findall(r'\w+', text.lower())
|
|
120
|
-
|
|
121
|
-
class HybridFusion:
|
|
122
|
-
"""Hybrid fusion of lexical (BM25) + vector scores"""
|
|
123
|
-
def __init__(self, alpha: float = 0.6):
|
|
124
|
-
self.alpha = alpha # weight for vector score
|
|
125
|
-
self.bm25 = BM25Scorer()
|
|
126
|
-
|
|
127
|
-
def fuse(self, query: str, candidates: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
128
|
-
# candidates: [{"id": , "text": , "vector_score": }]
|
|
129
|
-
corpus = {c["id"]: c.get("text", "") for c in candidates}
|
|
130
|
-
self.bm25.fit(corpus)
|
|
131
|
-
results = []
|
|
132
|
-
for c in candidates:
|
|
133
|
-
lex = self.bm25.score(query, c["id"], c.get("text", ""))
|
|
134
|
-
vec = c.get("vector_score", 0.5)
|
|
135
|
-
fused = self.alpha * vec + (1 - self.alpha) * (lex / 10.0) # normalize rough
|
|
136
|
-
c["fused_score"] = round(fused, 4)
|
|
137
|
-
results.append(c)
|
|
138
|
-
return sorted(results, key=lambda x: x["fused_score"], reverse=True)
|
|
139
|
-
|
|
140
|
-
class RerankerInterface:
|
|
141
|
-
"""Pluggable reranker interface.
|
|
142
|
-
|
|
143
|
-
Default is identity (fused score). When
|
|
144
|
-
``LATTICEAI_CROSS_ENCODER_RERANK=1`` and a CrossEncoder is importable,
|
|
145
|
-
delegates to :func:`lattice_brain.graph.rerank.rerank_matches`. Failures
|
|
146
|
-
never raise — always returns a ranked list.
|
|
147
|
-
"""
|
|
148
|
-
|
|
149
|
-
def rerank(self, query: str, candidates: List[Dict], top_k: int = 5) -> List[Dict]:
|
|
150
|
-
try:
|
|
151
|
-
from lattice_brain.graph.rerank import rerank_matches
|
|
152
|
-
|
|
153
|
-
# Map fused_score → score so the shared helper ranks consistently.
|
|
154
|
-
prepared = []
|
|
155
|
-
for c in candidates:
|
|
156
|
-
item = dict(c)
|
|
157
|
-
if "score" not in item:
|
|
158
|
-
item["score"] = item.get("fused_score", 0.0)
|
|
159
|
-
prepared.append(item)
|
|
160
|
-
result = rerank_matches(query, prepared, top_k=top_k)
|
|
161
|
-
return list(result.get("matches") or [])
|
|
162
|
-
except Exception: # noqa: BLE001 — quality path must never raise
|
|
163
|
-
for c in candidates:
|
|
164
|
-
c["rerank_score"] = c.get("fused_score", 0.0)
|
|
165
|
-
return sorted(
|
|
166
|
-
candidates, key=lambda x: x.get("rerank_score", 0), reverse=True
|
|
167
|
-
)[:top_k]
|
|
168
|
-
|
|
169
|
-
# -----------------------------
|
|
170
|
-
# 3. Memory Candidate Extraction / Scoring / Dedupe / Merge / Conflict / Retention
|
|
171
|
-
# -----------------------------
|
|
172
|
-
|
|
173
|
-
# Public content-signature helpers (v9.6.x graph-layer proactive seam).
|
|
174
|
-
# Extracted from MemoryQualityManager so the graph layer
|
|
175
|
-
# (lattice_brain.graph.proactive) and future ingestion gating can reuse the
|
|
176
|
-
# exact same dedupe semantics without instantiating the manager. Behaviour is
|
|
177
|
-
# byte-for-byte identical to the pre-existing private logic.
|
|
178
|
-
|
|
179
|
-
_SIGNATURE_STOPWORDS = {
|
|
180
|
-
"a", "an", "and", "for", "i", "is", "it", "mode", "the", "to",
|
|
181
|
-
"user", "users", "does", "do", "not", "like", "likes", "prefer",
|
|
182
|
-
"prefers", "want", "wants",
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
def dedupe_key(content: str) -> str:
|
|
187
|
-
"""Stable near-exact signature for a piece of content.
|
|
188
|
-
|
|
189
|
-
sha256 prefix over the normalized text head plus a coarse length bucket —
|
|
190
|
-
the same key ``MemoryQualityManager.dedupe`` uses to collapse duplicates.
|
|
191
|
-
Two texts with the same key are treated as exact/near-exact duplicates.
|
|
192
|
-
"""
|
|
193
|
-
text = str(content or "")
|
|
194
|
-
norm = " ".join(text.lower().split())[:200]
|
|
195
|
-
return hashlib.sha256((norm + f"|{len(text)//50}").encode()).hexdigest()[:16]
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
def content_signature(content: str) -> set:
|
|
199
|
-
"""Token-set signature (stopword-filtered) for overlap/jaccard comparison.
|
|
200
|
-
|
|
201
|
-
Mirrors ``MemoryQualityManager._content_signature`` — kept public so
|
|
202
|
-
graph-layer duplicate/contradiction detection shares one definition.
|
|
203
|
-
"""
|
|
204
|
-
tokens = set(re.findall(r"\w+", str(content or "").lower()))
|
|
205
|
-
return {
|
|
206
|
-
token
|
|
207
|
-
for token in tokens
|
|
208
|
-
if len(token) > 2 and token not in _SIGNATURE_STOPWORDS
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
@dataclass
|
|
213
|
-
class MemoryCandidate:
|
|
214
|
-
id: str
|
|
215
|
-
content: str
|
|
216
|
-
score: float = 0.0
|
|
217
|
-
source: str = "unknown"
|
|
218
|
-
timestamp: float = field(default_factory=time.time)
|
|
219
|
-
conflicts: List[str] = field(default_factory=list)
|
|
220
|
-
|
|
221
|
-
class MemoryQualityManager:
|
|
222
|
-
_NEGATION_PATTERNS = (
|
|
223
|
-
"not",
|
|
224
|
-
"does not",
|
|
225
|
-
"don't",
|
|
226
|
-
"do not",
|
|
227
|
-
"싫어",
|
|
228
|
-
"원하지 않",
|
|
229
|
-
"하지 않",
|
|
230
|
-
"반대",
|
|
231
|
-
)
|
|
232
|
-
_POSITIVE_PATTERNS = (
|
|
233
|
-
"prefers",
|
|
234
|
-
"prefer",
|
|
235
|
-
"likes",
|
|
236
|
-
"like",
|
|
237
|
-
"wants",
|
|
238
|
-
"want",
|
|
239
|
-
"좋아",
|
|
240
|
-
"선호",
|
|
241
|
-
"원해",
|
|
242
|
-
)
|
|
243
|
-
|
|
244
|
-
def extract_candidates(self, memories: List[Dict]) -> List[MemoryCandidate]:
|
|
245
|
-
return [MemoryCandidate(m["id"], m["content"], m.get("score", 0.6), m.get("source", "mem")) for m in memories]
|
|
246
|
-
|
|
247
|
-
def score_candidates(self, cands: List[MemoryCandidate], query: str) -> List[MemoryCandidate]:
|
|
248
|
-
for c in cands:
|
|
249
|
-
# simple lexical overlap score
|
|
250
|
-
overlap = sum(1 for w in query.lower().split() if w in c.content.lower())
|
|
251
|
-
c.score = min(1.0, 0.4 + overlap * 0.15)
|
|
252
|
-
return sorted(cands, key=lambda x: x.score, reverse=True)
|
|
253
|
-
|
|
254
|
-
def dedupe(self, cands: List[MemoryCandidate], threshold: float = 0.85) -> List[MemoryCandidate]:
|
|
255
|
-
"""Advanced content dedup (KG scale slice).
|
|
256
|
-
|
|
257
|
-
Uses sha256 prefix + normalized length bucket for better collision resistance
|
|
258
|
-
than plain md5. Future: plug embedding cosine or simhash here.
|
|
259
|
-
"""
|
|
260
|
-
kept = []
|
|
261
|
-
seen = set()
|
|
262
|
-
for c in cands:
|
|
263
|
-
h = dedupe_key(c.content)
|
|
264
|
-
if h not in seen:
|
|
265
|
-
seen.add(h)
|
|
266
|
-
kept.append(c)
|
|
267
|
-
return kept
|
|
268
|
-
|
|
269
|
-
def merge(self, cands: List[MemoryCandidate]) -> List[MemoryCandidate]:
|
|
270
|
-
# naive merge by content prefix
|
|
271
|
-
merged: Dict[str, MemoryCandidate] = {}
|
|
272
|
-
for c in cands:
|
|
273
|
-
key = c.content[:30]
|
|
274
|
-
if key not in merged or c.score > merged[key].score:
|
|
275
|
-
merged[key] = c
|
|
276
|
-
return list(merged.values())
|
|
277
|
-
|
|
278
|
-
def detect_conflicts(self, cands: List[MemoryCandidate]) -> List[MemoryCandidate]:
|
|
279
|
-
"""Flag local contradiction signals before proactive synthesis.
|
|
280
|
-
|
|
281
|
-
The heuristic stays deterministic/offline: direct negation marks a row
|
|
282
|
-
as risky, and pairwise token overlap catches common preference conflicts
|
|
283
|
-
such as "prefers light mode" vs "does not like light mode".
|
|
284
|
-
"""
|
|
285
|
-
for c in cands:
|
|
286
|
-
lowered = c.content.lower()
|
|
287
|
-
if any(pattern in lowered for pattern in self._NEGATION_PATTERNS):
|
|
288
|
-
c.conflicts.append("conflict:possible_negation")
|
|
289
|
-
|
|
290
|
-
signatures = [(c, self._content_signature(c.content)) for c in cands]
|
|
291
|
-
for left_index, (left, left_sig) in enumerate(signatures):
|
|
292
|
-
for right, right_sig in signatures[left_index + 1:]:
|
|
293
|
-
if not left_sig or len(left_sig & right_sig) < 2:
|
|
294
|
-
continue
|
|
295
|
-
left_negative = self._is_negative(left.content)
|
|
296
|
-
right_negative = self._is_negative(right.content)
|
|
297
|
-
left_positive = self._is_positive(left.content)
|
|
298
|
-
right_positive = self._is_positive(right.content)
|
|
299
|
-
if left_negative == right_negative:
|
|
300
|
-
continue
|
|
301
|
-
if not (left_positive or right_positive):
|
|
302
|
-
continue
|
|
303
|
-
left.conflicts.append(f"conflict:contradicts:{right.id}")
|
|
304
|
-
right.conflicts.append(f"conflict:contradicts:{left.id}")
|
|
305
|
-
return cands
|
|
306
|
-
|
|
307
|
-
def _is_negative(self, content: str) -> bool:
|
|
308
|
-
lowered = content.lower()
|
|
309
|
-
return any(pattern in lowered for pattern in self._NEGATION_PATTERNS)
|
|
310
|
-
|
|
311
|
-
def _is_positive(self, content: str) -> bool:
|
|
312
|
-
lowered = content.lower()
|
|
313
|
-
return any(pattern in lowered for pattern in self._POSITIVE_PATTERNS)
|
|
314
|
-
|
|
315
|
-
def _content_signature(self, content: str) -> set[str]:
|
|
316
|
-
return content_signature(content)
|
|
317
|
-
|
|
318
|
-
# --- Large candidate #4 slice: proactive / temporal contradiction detection ---
|
|
319
|
-
def detect_temporal_contradictions(self, memories: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
320
|
-
"""Simple temporal + contradiction detector over memory list (proactive synthesis extension).
|
|
321
|
-
|
|
322
|
-
Looks at negation keywords across similar contents + differing timestamps.
|
|
323
|
-
Real version would walk graph historical projections + LLM judge.
|
|
324
|
-
Returns flagged items with 'contradiction' tag.
|
|
325
|
-
"""
|
|
326
|
-
flagged = []
|
|
327
|
-
neg_tokens = ("not ", "반대", "거짓", "no longer", "never", "중단", "사용하지")
|
|
328
|
-
pos_group = []
|
|
329
|
-
neg_group = []
|
|
330
|
-
for m in memories:
|
|
331
|
-
c = (m.get("content") or "").lower()
|
|
332
|
-
if any(t in c for t in neg_tokens):
|
|
333
|
-
neg_group.append(m)
|
|
334
|
-
else:
|
|
335
|
-
pos_group.append(m)
|
|
336
|
-
# cross check: if both pos and neg exist with time spread
|
|
337
|
-
all_t = [m.get("timestamp") or m.get("created_at") or 0 for m in memories]
|
|
338
|
-
if neg_group and pos_group and len(set(all_t)) > 1:
|
|
339
|
-
for g in (pos_group + neg_group):
|
|
340
|
-
gg = dict(g)
|
|
341
|
-
gg["proactive_flag"] = "contradiction:temporal_negation"
|
|
342
|
-
flagged.append(gg)
|
|
343
|
-
return flagged
|
|
344
|
-
|
|
345
|
-
def apply_retention(self, cands: List[MemoryCandidate], max_age_days: int = 90) -> List[MemoryCandidate]:
|
|
346
|
-
now = time.time()
|
|
347
|
-
return [c for c in cands if (now - c.timestamp) < max_age_days * 86400]
|
|
348
|
-
|
|
349
|
-
# -----------------------------
|
|
350
|
-
# 4. Graph Edge Validation / Confidence / Evidence / Duplicate Merge / Quality Metrics
|
|
351
|
-
# -----------------------------
|
|
352
|
-
@dataclass
|
|
353
|
-
class GraphEdgeQuality:
|
|
354
|
-
edge_id: str
|
|
355
|
-
confidence: float
|
|
356
|
-
evidence_count: int
|
|
357
|
-
is_duplicate: bool = False
|
|
358
|
-
quality_score: float = 0.0
|
|
359
|
-
|
|
360
|
-
class GraphEdgeQualityManager:
|
|
361
|
-
def validate_edge(self, edge: Dict[str, Any]) -> GraphEdgeQuality:
|
|
362
|
-
conf = edge.get("confidence", 0.7)
|
|
363
|
-
ev = len(edge.get("evidence", []))
|
|
364
|
-
q = min(1.0, conf * 0.75 + min(ev, 5) / 5 * 0.3)
|
|
365
|
-
return GraphEdgeQuality(edge.get("id", "e0"), conf, ev, quality_score=q)
|
|
366
|
-
|
|
367
|
-
def detect_duplicate_edges(self, edges: List[Dict]) -> List[str]:
|
|
368
|
-
seen: Dict[Any, Any] = {}
|
|
369
|
-
dups: List[str] = []
|
|
370
|
-
for e in edges:
|
|
371
|
-
key = (e.get("source"), e.get("target"), e.get("type"))
|
|
372
|
-
if key in seen:
|
|
373
|
-
dups.append(str(e.get("id") or ""))
|
|
374
|
-
else:
|
|
375
|
-
seen[key] = e.get("id")
|
|
376
|
-
return dups
|
|
377
|
-
|
|
378
|
-
def compute_quality_metrics(self, edges: List[Dict]) -> Dict[str, float]:
|
|
379
|
-
if not edges:
|
|
380
|
-
return {"avg_conf": 0.0, "avg_evidence": 0.0, "dup_rate": 0.0}
|
|
381
|
-
confs = [e.get("confidence", 0.5) for e in edges]
|
|
382
|
-
evs = [len(e.get("evidence", [])) for e in edges]
|
|
383
|
-
dups = len(self.detect_duplicate_edges(edges))
|
|
384
|
-
return {
|
|
385
|
-
"avg_conf": round(sum(confs)/len(confs), 3),
|
|
386
|
-
"avg_evidence": round(sum(evs)/len(evs), 2),
|
|
387
|
-
"dup_rate": round(dups / len(edges), 3)
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
# -----------------------------
|
|
391
|
-
# 5. Structured Context Assembly + Guardrails
|
|
392
|
-
# -----------------------------
|
|
393
|
-
@dataclass
|
|
394
|
-
class ContextGuardrails:
|
|
395
|
-
known: bool = True
|
|
396
|
-
inferred: bool = False
|
|
397
|
-
stale: bool = False
|
|
398
|
-
unknown: bool = False
|
|
399
|
-
confidence: float = 0.8
|
|
400
|
-
timestamp: float = field(default_factory=time.time)
|
|
401
|
-
attribution: str = "system"
|
|
402
|
-
|
|
403
|
-
class StructuredContextAssembler:
|
|
404
|
-
SECTIONS = ["Facts", "Decisions", "Preferences", "Relationships", "Projects", "Recent Events"]
|
|
405
|
-
|
|
406
|
-
def assemble(self, items: List[Dict[str, Any]]) -> Dict[str, List[Dict]]:
|
|
407
|
-
ctx: Dict[str, List[Dict]] = {s: [] for s in self.SECTIONS}
|
|
408
|
-
for item in items:
|
|
409
|
-
section = item.get("section", "Facts")
|
|
410
|
-
if section not in ctx:
|
|
411
|
-
section = "Facts"
|
|
412
|
-
guard = ContextGuardrails(
|
|
413
|
-
known=item.get("known", True),
|
|
414
|
-
inferred=item.get("inferred", False),
|
|
415
|
-
stale=item.get("stale", False),
|
|
416
|
-
unknown=item.get("unknown", False),
|
|
417
|
-
confidence=item.get("confidence", 0.75),
|
|
418
|
-
attribution=item.get("attribution", "user")
|
|
419
|
-
)
|
|
420
|
-
item["guardrails"] = guard.__dict__
|
|
421
|
-
ctx[section].append(item)
|
|
422
|
-
return ctx
|
|
423
|
-
|
|
424
|
-
def apply_guardrails(self, ctx: Dict[str, List[Dict]]) -> Dict[str, List[Dict]]:
|
|
425
|
-
cleaned = {}
|
|
426
|
-
for sec, items in ctx.items():
|
|
427
|
-
cleaned[sec] = [i for i in items if not i.get("guardrails", {}).get("unknown", False)]
|
|
428
|
-
return cleaned
|
|
429
|
-
|
|
430
|
-
# -----------------------------
|
|
431
|
-
# 6. Retrieval Benchmark Fixture Runner
|
|
432
|
-
# -----------------------------
|
|
433
|
-
def _relevance_grades(relevant: Any) -> Dict[str, float]:
|
|
434
|
-
"""Normalize a ``relevant`` spec into a ``{doc_id: grade}`` map.
|
|
435
|
-
|
|
436
|
-
Accepts a graded dict (``{"doc": 3}``) or a binary list/set/tuple (grade 1
|
|
437
|
-
for every id), so judged fixtures can express graded relevance for nDCG
|
|
438
|
-
while older binary fixtures keep working unchanged.
|
|
439
|
-
"""
|
|
440
|
-
if isinstance(relevant, dict):
|
|
441
|
-
return {str(k): float(v) for k, v in relevant.items()}
|
|
442
|
-
return {str(doc_id): 1.0 for doc_id in (relevant or [])}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
class RetrievalBenchmarkRunner:
|
|
446
|
-
def __init__(self):
|
|
447
|
-
self.results: List[Dict] = []
|
|
448
|
-
|
|
449
|
-
def run_fixture(self, fixture_name: str, queries: List[Any], top_k: int = 5) -> Dict[str, Any]:
|
|
450
|
-
start = time.time()
|
|
451
|
-
judged = [q for q in queries if isinstance(q, dict)]
|
|
452
|
-
if judged:
|
|
453
|
-
recalls = []
|
|
454
|
-
precisions = []
|
|
455
|
-
ndcgs = []
|
|
456
|
-
for query in judged:
|
|
457
|
-
grades = _relevance_grades(query.get("relevant"))
|
|
458
|
-
relevant = set(grades)
|
|
459
|
-
retrieved = list(query.get("retrieved") or [])[:top_k]
|
|
460
|
-
if not relevant:
|
|
461
|
-
continue
|
|
462
|
-
hits = [doc_id for doc_id in retrieved if doc_id in relevant]
|
|
463
|
-
recalls.append(len(hits) / len(relevant))
|
|
464
|
-
precisions.append(len(hits) / max(1, len(retrieved)))
|
|
465
|
-
# Graded DCG: each retrieved doc contributes its relevance grade
|
|
466
|
-
# discounted by log2(rank + 2). IDCG ranks the highest grades
|
|
467
|
-
# first, so nDCG rewards putting the *most* relevant docs on top.
|
|
468
|
-
# Binary fixtures fall back to grade 1 via _relevance_grades.
|
|
469
|
-
dcg = sum(
|
|
470
|
-
grades.get(doc_id, 0.0) / math.log2(rank + 2)
|
|
471
|
-
for rank, doc_id in enumerate(retrieved)
|
|
472
|
-
)
|
|
473
|
-
ideal_grades = sorted(grades.values(), reverse=True)[:top_k]
|
|
474
|
-
ideal = sum(g / math.log2(rank + 2) for rank, g in enumerate(ideal_grades))
|
|
475
|
-
ndcgs.append(dcg / ideal if ideal else 0.0)
|
|
476
|
-
recall = sum(recalls) / len(recalls) if recalls else 0.0
|
|
477
|
-
precision = sum(precisions) / len(precisions) if precisions else 0.0
|
|
478
|
-
ndcg = sum(ndcgs) / len(ndcgs) if ndcgs else 0.0
|
|
479
|
-
else:
|
|
480
|
-
recall = precision = ndcg = 0.0
|
|
481
|
-
must_include = [
|
|
482
|
-
bool(set(query.get("must_include") or []).intersection(set(list(query.get("retrieved") or [])[:top_k])))
|
|
483
|
-
for query in judged
|
|
484
|
-
if query.get("must_include")
|
|
485
|
-
]
|
|
486
|
-
must_include_hit_rate = sum(1 for hit in must_include if hit) / len(must_include) if must_include else 1.0
|
|
487
|
-
metrics = {
|
|
488
|
-
"fixture": fixture_name,
|
|
489
|
-
"queries": len(queries),
|
|
490
|
-
"avg_latency_ms": round((time.time() - start) * 1000 / max(1, len(queries)), 2),
|
|
491
|
-
"recall@5": round(recall, 4),
|
|
492
|
-
"precision@5": round(precision, 4),
|
|
493
|
-
"ndcg@5": round(ndcg, 4),
|
|
494
|
-
f"recall@{top_k}": round(recall, 4),
|
|
495
|
-
f"precision@{top_k}": round(precision, 4),
|
|
496
|
-
f"ndcg@{top_k}": round(ndcg, 4),
|
|
497
|
-
"must_include_hit_rate": round(must_include_hit_rate, 4),
|
|
498
|
-
"top_k": top_k,
|
|
499
|
-
"judged": len(judged),
|
|
500
|
-
}
|
|
501
|
-
self.results.append(metrics)
|
|
502
|
-
return metrics
|
|
503
|
-
|
|
504
|
-
def summary(self) -> Dict[str, Any]:
|
|
505
|
-
if not self.results:
|
|
506
|
-
return {"status": "no runs"}
|
|
507
|
-
return {
|
|
508
|
-
"total_runs": len(self.results),
|
|
509
|
-
"avg_recall": round(sum(r["recall@5"] for r in self.results) / len(self.results), 3)
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
# -----------------------------
|
|
513
|
-
# Main Quality Layer Facade
|
|
514
|
-
# -----------------------------
|
|
515
|
-
class LatticeBrainQuality:
|
|
516
|
-
"""Main entry point - pure Python quality hardening layer"""
|
|
517
|
-
def __init__(self):
|
|
518
|
-
self.embed_labeller = EmbeddingFallbackLabeller()
|
|
519
|
-
self.hybrid = HybridFusion()
|
|
520
|
-
self.reranker = RerankerInterface()
|
|
521
|
-
self.memory_mgr = MemoryQualityManager()
|
|
522
|
-
self.graph_mgr = GraphEdgeQualityManager()
|
|
523
|
-
self.context_assembler = StructuredContextAssembler()
|
|
524
|
-
self.benchmark = RetrievalBenchmarkRunner()
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
__all__ = [
|
|
528
|
-
"BM25Scorer",
|
|
529
|
-
"ContextGuardrails",
|
|
530
|
-
"content_signature",
|
|
531
|
-
"dedupe_key",
|
|
532
|
-
"EmbeddingFallbackLabeller",
|
|
533
|
-
"EmbeddingLabel",
|
|
534
|
-
"GraphEdgeQuality",
|
|
535
|
-
"GraphEdgeQualityManager",
|
|
536
|
-
"HybridFusion",
|
|
537
|
-
"LatticeBrainQuality",
|
|
538
|
-
"MemoryCandidate",
|
|
539
|
-
"MemoryQualityManager",
|
|
540
|
-
"RetrievalBenchmarkRunner",
|
|
541
|
-
"RerankerInterface",
|
|
542
|
-
"StructuredContextAssembler",
|
|
543
|
-
]
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
"""Corpus-scale retrieval fixture for the 7.5.0 Brain quality gate."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
FIXTURE_NAME = "7.5.0-corpus-scale-hybrid-retrieval"
|
|
6
|
-
TOP_K = 5
|
|
7
|
-
|
|
8
|
-
CORE_DOCUMENTS = [
|
|
9
|
-
{"id": "doc:agent-runtime-contract", "type": "Decision", "title": "Agent runtime contract", "content": "Workflow run, agent run, audit event, and realtime event records must carry the agent-run-contract/v1 family envelope with run id, runtime, status, timeline, and artifacts."},
|
|
10
|
-
{"id": "doc:workflow-observability", "type": "Document", "title": "Workflow observability", "content": "Workflow Designer persists queued, running, awaiting approval, cancelled, interrupted, partial, failed, and ok runs with replayable timeline events."},
|
|
11
|
-
{"id": "doc:audit-trust", "type": "Security", "title": "Audit trust", "content": "Audit events are append-only, redacted before persistence, and linked to execution contracts for admin review without leaking secrets."},
|
|
12
|
-
{"id": "doc:realtime-feed", "type": "Document", "title": "Realtime execution feed", "content": "The realtime SSE bus publishes workspace, agent, workflow, and execution events with workspace scope, event type, payload, sequence, and contract metadata."},
|
|
13
|
-
{"id": "doc:retrieval-benchmark", "type": "Benchmark", "title": "Retrieval benchmark strategy", "content": "Hybrid retrieval quality must be checked with corpus-scale fixtures, judged queries, recall, precision, ndcg, and must-include hit rate."},
|
|
14
|
-
{"id": "doc:hybrid-search", "type": "Architecture", "title": "Hybrid search", "content": "SearchService fuses keyword, vector, and graph retrieval channels with weighted scores and scoped workspace filtering."},
|
|
15
|
-
{"id": "doc:knowledge-graph", "type": "Architecture", "title": "Knowledge graph stabilization", "content": "The Knowledge Graph preserves legacy read compatibility, non-destructive migrations, vector indexing, relationship traversal, and provenance."},
|
|
16
|
-
{"id": "doc:local-first", "type": "Strategy", "title": "Local-first product wedge", "content": "Lattice AI is local-first: durable knowledge, private workspace memory, local LLM routing, and model-independent context are the product wedge."},
|
|
17
|
-
{"id": "doc:brain-proof", "type": "UX", "title": "Brain proof and citations", "content": "Brain proof shows durable recall, source citation, graph counts, vector counts, and model continuity evidence in chat."},
|
|
18
|
-
{"id": "doc:onboarding", "type": "UX", "title": "Five minute onboarding loop", "content": "The first loop asks users to add a source, ask a question, see Brain proof, and understand expected installation time."},
|
|
19
|
-
{"id": "doc:vscode-sync", "type": "Integration", "title": "VS Code extension state", "content": "VS Code extension integration shows app connection state, indexing status, command routing, and extension-to-app sync health."},
|
|
20
|
-
{"id": "doc:workspace-admin", "type": "Workspace", "title": "Workspace profile admin discovery", "content": "Personal workspace, organization workspace, profile, admin, members, roles, permissions, and security surfaces must be discoverable."},
|
|
21
|
-
{"id": "doc:empty-error-consent", "type": "UX", "title": "Empty error consent states", "content": "Empty states, error states, consent prompts, retries, and privacy feedback should be explicit across ingestion, graph, chat, and workspace flows."},
|
|
22
|
-
{"id": "doc:tool-registry", "type": "Architecture", "title": "Tool registry separation", "content": "ToolRegistry separates tool definitions, permissions, dispatch, audit, and UI discovery from AgentRuntime orchestration."},
|
|
23
|
-
{"id": "doc:config-centralization", "type": "Architecture", "title": "Config centralization", "content": "Configuration should be centralized into explicit objects instead of hidden globals so runtime, tests, and packaging are predictable."},
|
|
24
|
-
{"id": "doc:server-decomposition", "type": "Architecture", "title": "Server decomposition", "content": "Server decomposition moves routers, services, runtime context, and persistence seams into focused modules with dependency injection."},
|
|
25
|
-
{"id": "doc:incremental-ingestion", "type": "Pipeline", "title": "Incremental ingestion", "content": "Background indexing needs duplicate detection, conflict resolution, incremental updates, and merge behavior for large personal corpora."},
|
|
26
|
-
{"id": "doc:vector-index-evolution", "type": "Pipeline", "title": "Vector index evolution", "content": "sqlite-vec migration, HNSW evaluation, query caching, embedding caching, and retrieval caching support larger corpora."},
|
|
27
|
-
{"id": "doc:security-zero-trust", "type": "Security", "title": "Zero trust defaults", "content": "Zero-trust defaults include audit logging, secret redaction, dependency vulnerability monitoring, and automated security scanning."},
|
|
28
|
-
{"id": "doc:agentic-hitl", "type": "Workflow", "title": "Agentic human review", "content": "Agentic workflows need human-in-the-loop review, approval gates, rollback paths, review queues, and auditable decisions."},
|
|
29
|
-
{"id": "doc:temporal-reasoning", "type": "Brain", "title": "Temporal reasoning", "content": "Temporal graph states, historical memory, contradiction detection, synthesis, and recommendations make the Brain proactive."},
|
|
30
|
-
{"id": "doc:multimodal-memory", "type": "Brain", "title": "Multimodal memory", "content": "Images, audio, video, documents, and chat should ingest into one durable Brain with retrieval and citation proof."},
|
|
31
|
-
{"id": "doc:developer-api", "type": "API", "title": "Developer API", "content": "A secure developer API exposes third-party access to workspace knowledge, tools, runs, contracts, and audit trails."},
|
|
32
|
-
{"id": "doc:team-brains", "type": "Business", "title": "Team Brains", "content": "Team Brains, encrypted sharing, premium cloud sync, hosted models, plugin marketplace, and public benchmarks support monetization."},
|
|
33
|
-
{"id": "doc:docker-tauri", "type": "Deployment", "title": "Deployment operations", "content": "Docker Compose deployment, one-click installation, Tauri auto-updates, rollback support, and exact release artifacts improve operations."},
|
|
34
|
-
{"id": "doc:model-routing", "type": "Runtime", "title": "Model orchestration", "content": "Ollama, LM Studio, MLX, and cloud models need automatic fallback, routing, and replaceable model boundaries."},
|
|
35
|
-
{"id": "doc:strict-quality", "type": "Quality", "title": "Strict code quality", "content": "Async-first architecture, dependency injection, centralized exceptions, mypy or pyright, TypeScript strict mode, E2E tests, and property tests reduce regressions."},
|
|
36
|
-
{"id": "distractor:theme-editor", "type": "Distractor", "title": "Theme editor", "content": "Color palettes, typography, and sidebar icon sizing are useful visual polish topics."},
|
|
37
|
-
{"id": "distractor:calendar", "type": "Distractor", "title": "Calendar reminders", "content": "Calendar reminders and meeting agenda formatting are productivity integrations."},
|
|
38
|
-
{"id": "distractor:billing", "type": "Distractor", "title": "Billing invoices", "content": "Invoices, payment receipts, and subscription emails can be stored but are not the Brain quality gate."},
|
|
39
|
-
{"id": "distractor:mobile-shell", "type": "Distractor", "title": "Mobile shell", "content": "A mobile companion can improve access with push notifications and offline cache."},
|
|
40
|
-
{"id": "distractor:marketing", "type": "Distractor", "title": "Marketing landing page", "content": "Landing pages and hero copy shape positioning and campaign messaging."},
|
|
41
|
-
]
|
|
42
|
-
|
|
43
|
-
_DISTRACTOR_TOPICS = [
|
|
44
|
-
("finance", "quarterly planning", "budget variance vendor renewal procurement forecast"),
|
|
45
|
-
("design", "component polish", "spacing typography icon density color token accessibility"),
|
|
46
|
-
("support", "customer escalation", "ticket triage priority response macro satisfaction"),
|
|
47
|
-
("ops", "incident rotation", "oncall handoff status page maintenance window"),
|
|
48
|
-
("sales", "pipeline review", "lead scoring opportunity stage forecast account"),
|
|
49
|
-
("calendar", "meeting schedule", "agenda reminder availability invite followup"),
|
|
50
|
-
("email", "inbox policy", "thread label archive template unsubscribe"),
|
|
51
|
-
("mobile", "companion shell", "push notification offline cache deep link"),
|
|
52
|
-
("billing", "invoice lifecycle", "receipt tax subscription entitlement payment"),
|
|
53
|
-
("analytics", "dashboard report", "cohort funnel metric annotation export"),
|
|
54
|
-
("theme", "appearance settings", "palette contrast sidebar density motion"),
|
|
55
|
-
("legal", "compliance packet", "clause obligation renewal signature counterparty"),
|
|
56
|
-
]
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def _generated_distractors() -> list[dict[str, str]]:
|
|
60
|
-
"""Create a deterministic local corpus large enough to exercise ranking."""
|
|
61
|
-
docs: list[dict[str, str]] = []
|
|
62
|
-
for batch in range(20):
|
|
63
|
-
for topic, title, terms in _DISTRACTOR_TOPICS:
|
|
64
|
-
docs.append({
|
|
65
|
-
"id": f"distractor:{topic}:{batch:02d}",
|
|
66
|
-
"type": "Distractor",
|
|
67
|
-
"title": f"{title.title()} {batch + 1}",
|
|
68
|
-
"content": (
|
|
69
|
-
f"{title} note {batch + 1}. This record discusses {terms}. "
|
|
70
|
-
"It is intentionally unrelated to the judged Brain quality topics "
|
|
71
|
-
"and exists to make local ranking tests compete against realistic noise."
|
|
72
|
-
),
|
|
73
|
-
})
|
|
74
|
-
return docs
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
DOCUMENTS = CORE_DOCUMENTS + _generated_distractors()
|
|
78
|
-
|
|
79
|
-
QUERIES = [
|
|
80
|
-
{"query": "agent workflow audit realtime contract run status timeline", "relevant": {"doc:agent-runtime-contract": 3, "doc:workflow-observability": 2, "doc:audit-trust": 2, "doc:realtime-feed": 2}, "must_include": ["doc:agent-runtime-contract"]},
|
|
81
|
-
{"query": "workflow queued running cancelled interrupted replay approval", "relevant": {"doc:workflow-observability": 3, "doc:agentic-hitl": 2}, "must_include": ["doc:workflow-observability"]},
|
|
82
|
-
{"query": "audit log redacted secrets admin review contract", "relevant": {"doc:audit-trust": 3}, "must_include": ["doc:audit-trust"]},
|
|
83
|
-
{"query": "SSE realtime workspace event payload sequence contract", "relevant": {"doc:realtime-feed": 3, "doc:agent-runtime-contract": 2}, "must_include": ["doc:realtime-feed"]},
|
|
84
|
-
{"query": "hybrid retrieval keyword vector graph weighted scoped search", "relevant": {"doc:hybrid-search": 3, "doc:knowledge-graph": 2, "doc:retrieval-benchmark": 2}, "must_include": ["doc:hybrid-search"]},
|
|
85
|
-
{"query": "corpus scale recall precision ndcg judged queries", "relevant": {"doc:retrieval-benchmark": 3}, "must_include": ["doc:retrieval-benchmark"]},
|
|
86
|
-
{"query": "local first model independent durable memory brain proof citation", "relevant": {"doc:local-first": 3, "doc:brain-proof": 2}, "must_include": ["doc:local-first"]},
|
|
87
|
-
{"query": "incremental indexing duplicate conflict merge background ingestion", "relevant": {"doc:incremental-ingestion": 3}, "must_include": ["doc:incremental-ingestion"]},
|
|
88
|
-
{"query": "workspace admin profile organization members roles permissions", "relevant": {"doc:workspace-admin": 3}, "must_include": ["doc:workspace-admin"]},
|
|
89
|
-
{"query": "tool registry separation dependency injection runtime context", "relevant": {"doc:tool-registry": 3, "doc:server-decomposition": 2, "doc:config-centralization": 2}, "must_include": ["doc:tool-registry"]},
|
|
90
|
-
{"query": "human in the loop approval review queue rollback", "relevant": {"doc:agentic-hitl": 3}, "must_include": ["doc:agentic-hitl"]},
|
|
91
|
-
{"query": "model routing ollama lm studio mlx cloud fallback replaceable", "relevant": {"doc:model-routing": 3, "doc:local-first": 2}, "must_include": ["doc:model-routing"]},
|
|
92
|
-
]
|