apsimo 1.3.0__py3-none-any.whl
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.
- apsimo/__init__.py +38 -0
- apsimo/__main__.py +6 -0
- apsimo/agent/__init__.py +6 -0
- apsimo/agent/client.py +276 -0
- apsimo/agent/models.py +46 -0
- apsimo/agents/__init__.py +20 -0
- apsimo/agents/models.py +264 -0
- apsimo/agents/store.py +861 -0
- apsimo/agents/websocket.py +522 -0
- apsimo/api/__init__.py +1 -0
- apsimo/api/auth_telemetry.py +287 -0
- apsimo/api/authority.py +1203 -0
- apsimo/api/contact_grants.py +347 -0
- apsimo/api/middleware.py +483 -0
- apsimo/api/routers/__init__.py +1 -0
- apsimo/api/routers/commitment_work.py +265 -0
- apsimo/api/routers/context_gate.py +123 -0
- apsimo/api/routers/executions.py +140 -0
- apsimo/api/routers/followup_plans.py +147 -0
- apsimo/api/routers/governed_actions.py +162 -0
- apsimo/api/routers/host.py +14473 -0
- apsimo/api/routers/initiative_work.py +115 -0
- apsimo/api/routers/mining.py +104 -0
- apsimo/api/routers/observations.py +110 -0
- apsimo/api/routers/social_state.py +225 -0
- apsimo/api/routers/task_queue.py +2715 -0
- apsimo/api/routers/temporal_followups.py +251 -0
- apsimo/api/routers/transport.py +110 -0
- apsimo/api/routers/transport_ingress_api.py +240 -0
- apsimo/api/schemas/__init__.py +1 -0
- apsimo/api/schemas/host.py +1949 -0
- apsimo/autonomy/cli.py +110 -0
- apsimo/autonomy/condition_worker.py +437 -0
- apsimo/autonomy/config.py +424 -0
- apsimo/autonomy/loop.py +4316 -0
- apsimo/autonomy/registry.py +339 -0
- apsimo/autonomy/scheduler.py +1822 -0
- apsimo/autonomy/synthesis.py +449 -0
- apsimo/backup.py +962 -0
- apsimo/beliefs/__init__.py +23 -0
- apsimo/beliefs/contradictions.py +109 -0
- apsimo/beliefs/decay.py +61 -0
- apsimo/beliefs/engine.py +479 -0
- apsimo/beliefs/models.py +67 -0
- apsimo/beliefs/promotion.py +41 -0
- apsimo/beliefs/resolve.py +58 -0
- apsimo/beliefs/source_claims.py +690 -0
- apsimo/beliefs/source_projection.py +883 -0
- apsimo/beliefs/source_time.py +208 -0
- apsimo/beliefs/store.py +133 -0
- apsimo/briefings/aggregators.py +824 -0
- apsimo/briefings/composer.py +420 -0
- apsimo/briefings/config.py +55 -0
- apsimo/briefings/delivery.py +439 -0
- apsimo/briefings/engagement.py +97 -0
- apsimo/briefings/engine.py +274 -0
- apsimo/briefings/enhancer.py +99 -0
- apsimo/briefings/models.py +183 -0
- apsimo/briefings/scheduler.py +382 -0
- apsimo/briefings/store.py +435 -0
- apsimo/chain/__init__.py +48 -0
- apsimo/chain/block.py +100 -0
- apsimo/chain/cli.py +704 -0
- apsimo/chain/genesis.py +443 -0
- apsimo/chain/identity.py +416 -0
- apsimo/chain/keys.py +1025 -0
- apsimo/chain/local_keys.py +187 -0
- apsimo/chain/manager.py +290 -0
- apsimo/chain/node.py +163 -0
- apsimo/chain/plugin_transactions.py +371 -0
- apsimo/chain/protocol.py +220 -0
- apsimo/chain/state_machine.py +676 -0
- apsimo/chain/storage.py +503 -0
- apsimo/chain/transactions.py +250 -0
- apsimo/chain/validation.py +397 -0
- apsimo/channels/__init__.py +1 -0
- apsimo/channels/manifest.py +31 -0
- apsimo/channels/migrations/001_channels_schema.sql +12 -0
- apsimo/channels/phone_gateways.py +42 -0
- apsimo/channels/presence.py +188 -0
- apsimo/channels/router.py +235 -0
- apsimo/channels/store.py +231 -0
- apsimo/cli.py +2688 -0
- apsimo/cognition/__init__.py +11 -0
- apsimo/cognition/charter.py +398 -0
- apsimo/cognition/drive_governance.py +3530 -0
- apsimo/cognition/evidence_pipeline.py +1627 -0
- apsimo/cognition/external_events.py +932 -0
- apsimo/cognition/goal_spine.py +3488 -0
- apsimo/cognition/introspection.py +214 -0
- apsimo/cognition/prompt.py +150 -0
- apsimo/cognition/runtime.py +108 -0
- apsimo/cognition/trigger.py +154 -0
- apsimo/commitments/__init__.py +18 -0
- apsimo/commitments/local_work.py +355 -0
- apsimo/commitments/store.py +1052 -0
- apsimo/commitments/work.py +91 -0
- apsimo/compat.py +53 -0
- apsimo/compression/__init__.py +467 -0
- apsimo/connectors/__init__.py +21 -0
- apsimo/connectors/base.py +152 -0
- apsimo/connectors/caldav_calendar.py +125 -0
- apsimo/connectors/fs_documents.py +85 -0
- apsimo/connectors/imap_email.py +138 -0
- apsimo/connectors/manager.py +218 -0
- apsimo/connectors/webhook_pull.py +88 -0
- apsimo/contacts/__init__.py +33 -0
- apsimo/contacts/comms.py +357 -0
- apsimo/contacts/config.py +79 -0
- apsimo/contacts/exporters/__init__.py +1 -0
- apsimo/contacts/exporters/vcard.py +71 -0
- apsimo/contacts/identity_links.py +251 -0
- apsimo/contacts/importer.py +280 -0
- apsimo/contacts/importers/__init__.py +1 -0
- apsimo/contacts/importers/batch.py +43 -0
- apsimo/contacts/importers/macos_contacts.py +101 -0
- apsimo/contacts/migrations/001_contacts_schema.sql +141 -0
- apsimo/contacts/migrations/002_trust_scopes.sql +36 -0
- apsimo/contacts/migrations/003_open_gateway_enum.sql +32 -0
- apsimo/contacts/migrations/004_contact_provision_operations.sql +18 -0
- apsimo/contacts/migrations/005_identity_links.sql +27 -0
- apsimo/contacts/models.py +308 -0
- apsimo/contacts/scoring.py +16 -0
- apsimo/contacts/store.py +1623 -0
- apsimo/contacts/transport_ingress.py +252 -0
- apsimo/contacts/world_bridge.py +314 -0
- apsimo/contextgate/__init__.py +69 -0
- apsimo/contextgate/chunker.py +169 -0
- apsimo/contextgate/estimate.py +54 -0
- apsimo/contextgate/gate.py +313 -0
- apsimo/contextgate/retrieve.py +115 -0
- apsimo/delivery/__init__.py +16 -0
- apsimo/delivery/bridge.py +1260 -0
- apsimo/delivery/channels.py +526 -0
- apsimo/delivery/classification.py +50 -0
- apsimo/delivery/rate_limiter.py +268 -0
- apsimo/delivery/reachout_policy.py +206 -0
- apsimo/directed/__init__.py +22 -0
- apsimo/directed/audit.py +167 -0
- apsimo/directed/intake.py +95 -0
- apsimo/directed/models.py +191 -0
- apsimo/directed/service.py +509 -0
- apsimo/directives/__init__.py +25 -0
- apsimo/directives/evidence.py +87 -0
- apsimo/directives/extractor.py +188 -0
- apsimo/directives/guard.py +364 -0
- apsimo/directives/models.py +206 -0
- apsimo/directives/service.py +372 -0
- apsimo/directives/store.py +167 -0
- apsimo/doctor.py +2173 -0
- apsimo/environment.py +43 -0
- apsimo/events/__init__.py +33 -0
- apsimo/events/broadcaster.py +98 -0
- apsimo/events/bus.py +217 -0
- apsimo/events/journal.py +863 -0
- apsimo/events/stream.py +131 -0
- apsimo/events/types.py +150 -0
- apsimo/execution_results.py +357 -0
- apsimo/feedback/__init__.py +5 -0
- apsimo/feedback/store.py +76 -0
- apsimo/feeds/__init__.py +19 -0
- apsimo/feeds/cli.py +84 -0
- apsimo/feeds/engine.py +437 -0
- apsimo/feeds/example-feed.yaml +77 -0
- apsimo/feeds/hermes_cron.py +126 -0
- apsimo/feeds/manager.py +235 -0
- apsimo/feeds/spec.py +250 -0
- apsimo/feeds/template.py +202 -0
- apsimo/gate/__init__.py +18 -0
- apsimo/gate/audit.py +61 -0
- apsimo/gate/communication_policy.py +166 -0
- apsimo/gate/config.py +72 -0
- apsimo/gate/context_provenance.py +170 -0
- apsimo/gate/env_risk.py +226 -0
- apsimo/gate/guard_audit.py +353 -0
- apsimo/gate/layers/__init__.py +1 -0
- apsimo/gate/layers/base.py +15 -0
- apsimo/gate/layers/l1_recipient.py +66 -0
- apsimo/gate/layers/l2_pii.py +134 -0
- apsimo/gate/layers/l3_cross_context.py +50 -0
- apsimo/gate/layers/l4_trust_tier.py +78 -0
- apsimo/gate/layers/l5_injection.py +199 -0
- apsimo/gate/layers/l6_review.py +86 -0
- apsimo/gate/layers/l7_delay.py +100 -0
- apsimo/gate/layers/tom2_epistemic.py +185 -0
- apsimo/gate/models.py +64 -0
- apsimo/gate/pending_dispatch.py +5 -0
- apsimo/gate/pipeline.py +206 -0
- apsimo/gate/rejection.py +259 -0
- apsimo/gate/response_guard.py +700 -0
- apsimo/gate/rulesets/injection_v1.yaml +51 -0
- apsimo/gate/surface_policy.py +189 -0
- apsimo/gate/taint.py +226 -0
- apsimo/genesis.json +9 -0
- apsimo/goals/__init__.py +100 -0
- apsimo/goals/config.py +38 -0
- apsimo/goals/decomposer.py +421 -0
- apsimo/goals/engine.py +617 -0
- apsimo/goals/inference.py +354 -0
- apsimo/goals/models.py +302 -0
- apsimo/goals/priority.py +270 -0
- apsimo/goals/queue_bridge.py +149 -0
- apsimo/goals/replan.py +450 -0
- apsimo/goals/schema.sql +89 -0
- apsimo/goals/store.py +692 -0
- apsimo/governed_actions.py +1708 -0
- apsimo/harness_integration/__init__.py +45 -0
- apsimo/harness_integration/context.py +41 -0
- apsimo/harness_integration/skills.py +231 -0
- apsimo/identity/__init__.py +26 -0
- apsimo/identity/participants.py +181 -0
- apsimo/identity/resolver.py +329 -0
- apsimo/identity_bootstrap/__init__.py +5 -0
- apsimo/identity_bootstrap/builder.py +208 -0
- apsimo/identity_bootstrap/corpus.py +443 -0
- apsimo/identity_bootstrap/models.py +54 -0
- apsimo/identity_bootstrap/runner.py +353 -0
- apsimo/identity_bootstrap/seeders/__init__.py +25 -0
- apsimo/identity_bootstrap/seeders/briefings.py +109 -0
- apsimo/identity_bootstrap/seeders/chain.py +57 -0
- apsimo/identity_bootstrap/seeders/goals.py +128 -0
- apsimo/identity_bootstrap/seeders/memory.py +191 -0
- apsimo/identity_bootstrap/seeders/neo4j_cognition.py +79 -0
- apsimo/identity_bootstrap/seeders/relationship.py +152 -0
- apsimo/identity_bootstrap/seeders/sessions.py +67 -0
- apsimo/identity_bootstrap/seeders/skills.py +92 -0
- apsimo/identity_bootstrap/seeders/task_queue.py +72 -0
- apsimo/identity_bootstrap/seeders/world_model.py +143 -0
- apsimo/identity_bootstrap/self_query.py +92 -0
- apsimo/identity_bootstrap/self_reflection.py +155 -0
- apsimo/identity_bootstrap/skill.py +37 -0
- apsimo/identity_bootstrap/verifier.py +436 -0
- apsimo/initiatives/__init__.py +20 -0
- apsimo/initiatives/action_registry.py +454 -0
- apsimo/initiatives/approval_authority.py +2105 -0
- apsimo/initiatives/approval_policy.py +123 -0
- apsimo/initiatives/assignment.py +263 -0
- apsimo/initiatives/backup_evidence.py +100 -0
- apsimo/initiatives/context_freshness.py +103 -0
- apsimo/initiatives/models.py +318 -0
- apsimo/initiatives/native_work.py +270 -0
- apsimo/initiatives/standing_approvals.py +232 -0
- apsimo/initiatives/store.py +1081 -0
- apsimo/initiatives/temporal_followup.py +410 -0
- apsimo/intelligence/__init__.py +1 -0
- apsimo/intelligence/cognition/__init__.py +24 -0
- apsimo/intelligence/cognition/gap_detector.py +148 -0
- apsimo/intelligence/cognition/metalearner.py +547 -0
- apsimo/intelligence/cognition/metrics_collector.py +217 -0
- apsimo/intelligence/cognition/performance_index.py +299 -0
- apsimo/intelligence/cognition/registry.py +192 -0
- apsimo/intelligence/cognition/strategy_adjuster.py +222 -0
- apsimo/intelligence/cognition/types.py +16 -0
- apsimo/intelligence/components/__init__.py +66 -0
- apsimo/intelligence/components/anomaly_detector.py +413 -0
- apsimo/intelligence/components/initiative_engine.py +2643 -0
- apsimo/intelligence/components/preference_learner.py +521 -0
- apsimo/intelligence/components/research_orchestrator.py +358 -0
- apsimo/intelligence/components/self_directed_thinker.py +221 -0
- apsimo/intelligence/components/self_reflector.py +252 -0
- apsimo/intelligence/components/session_continuity.py +154 -0
- apsimo/intelligence/components/task_planner.py +320 -0
- apsimo/intelligence/components/tool_learner.py +217 -0
- apsimo/intelligence/graph/__init__.py +79 -0
- apsimo/intelligence/graph/client.py +2483 -0
- apsimo/intelligence/graph/consolidator.py +405 -0
- apsimo/intelligence/graph/distiller.py +312 -0
- apsimo/intelligence/graph/migrations.py +129 -0
- apsimo/intelligence/graph/queries.py +248 -0
- apsimo/intelligence/graph/recall.py +281 -0
- apsimo/intelligence/graph/reconciler.py +144 -0
- apsimo/intelligence/graph/schema.py +337 -0
- apsimo/intelligence/graph/selection.py +252 -0
- apsimo/intelligence/learning/__init__.py +17 -0
- apsimo/intelligence/learning/continuous_learner.py +245 -0
- apsimo/intelligence/learning/feedback_store.py +321 -0
- apsimo/intelligence/mind_model/__init__.py +1 -0
- apsimo/intelligence/mind_model/graph_baseline.py +136 -0
- apsimo/intelligence/mind_model/signal_collector.py +361 -0
- apsimo/intelligence/relationships/__init__.py +11 -0
- apsimo/intelligence/relationships/profiler.py +389 -0
- apsimo/intelligence/relationships/scorer.py +560 -0
- apsimo/intelligence/relationships/signal_floor.py +66 -0
- apsimo/intelligence/relationships/trust_tiers.py +300 -0
- apsimo/intelligence/synthesis/__init__.py +40 -0
- apsimo/intelligence/synthesis/connection_discoverer.py +379 -0
- apsimo/intelligence/synthesis/cross_domain_analyzer.py +287 -0
- apsimo/intelligence/synthesis/insight_deliverer.py +171 -0
- apsimo/intelligence/synthesis/insight_store.py +79 -0
- apsimo/intelligence/synthesis/insight_validator.py +183 -0
- apsimo/intelligence/synthesis/novelty_scorer.py +267 -0
- apsimo/intelligence/turn_middleware/__init__.py +15 -0
- apsimo/intelligence/turn_middleware/memory_sync.py +119 -0
- apsimo/mcp/__init__.py +41 -0
- apsimo/mcp/__main__.py +6 -0
- apsimo/mcp/config.py +287 -0
- apsimo/mcp/server.py +501 -0
- apsimo/migrations.py +187 -0
- apsimo/mining/__init__.py +27 -0
- apsimo/mining/corpus.py +239 -0
- apsimo/mining/escalations.py +289 -0
- apsimo/mining/models.py +169 -0
- apsimo/mining/store.py +210 -0
- apsimo/models/__init__.py +30 -0
- apsimo/models/memory.py +80 -0
- apsimo/models/mesh.py +72 -0
- apsimo/models/person.py +104 -0
- apsimo/models/signal.py +108 -0
- apsimo/observations/__init__.py +15 -0
- apsimo/observations/store.py +277 -0
- apsimo/patterns/__init__.py +6 -0
- apsimo/patterns/extract.py +187 -0
- apsimo/patterns/store.py +227 -0
- apsimo/persona/__init__.py +1 -0
- apsimo/persona/engine.py +611 -0
- apsimo/persona/manifest.py +140 -0
- apsimo/projects/__init__.py +28 -0
- apsimo/projects/engine.py +1681 -0
- apsimo/projects/event_outbox.py +188 -0
- apsimo/projects/models.py +216 -0
- apsimo/projects/planner.py +181 -0
- apsimo/projects/store.py +1446 -0
- apsimo/proposals/__init__.py +12 -0
- apsimo/proposals/engine.py +114 -0
- apsimo/proposals/models.py +207 -0
- apsimo/qualification/__init__.py +1 -0
- apsimo/qualification/cases.py +75 -0
- apsimo/qualification/cli.py +51 -0
- apsimo/qualification/memory_cases.py +209 -0
- apsimo/qualification/records.py +92 -0
- apsimo/qualification/report.py +87 -0
- apsimo/qualification/runner.py +311 -0
- apsimo/qualification/structured_cases.py +131 -0
- apsimo/reasoning/__init__.py +13 -0
- apsimo/reasoning/executor.py +506 -0
- apsimo/reasoning/loop.py +373 -0
- apsimo/reasoning/native_tools/__init__.py +16 -0
- apsimo/reasoning/native_tools/calculate.py +141 -0
- apsimo/reasoning/native_tools/file_ops.py +150 -0
- apsimo/reasoning/native_tools/web_search.py +49 -0
- apsimo/reasoning/tool_policy.py +182 -0
- apsimo/redact/__init__.py +176 -0
- apsimo/repos/__init__.py +5 -0
- apsimo/repos/mirrors.py +204 -0
- apsimo/research/__init__.py +41 -0
- apsimo/research/artifact.py +482 -0
- apsimo/research/gatherer.py +387 -0
- apsimo/research/pipeline.py +513 -0
- apsimo/research/search/__init__.py +7 -0
- apsimo/research/search/base.py +41 -0
- apsimo/research/search/brave.py +59 -0
- apsimo/research/search/cache.py +51 -0
- apsimo/research/search/duckduckgo.py +103 -0
- apsimo/research/search/orchestrator.py +119 -0
- apsimo/research/search/serpapi.py +59 -0
- apsimo/research/search/tavily.py +59 -0
- apsimo/research/synthesizer.py +309 -0
- apsimo/router/__init__.py +30 -0
- apsimo/router/complexity_scorer.py +148 -0
- apsimo/router/endpoints.py +153 -0
- apsimo/router/fallback.py +58 -0
- apsimo/router/functions.py +243 -0
- apsimo/router/native_policy.py +52 -0
- apsimo/router/router.py +762 -0
- apsimo/router/self_learning.py +174 -0
- apsimo/router/tiers.py +677 -0
- apsimo/sandbox/__init__.py +21 -0
- apsimo/sandbox/backend.py +195 -0
- apsimo/sandbox/manager.py +173 -0
- apsimo/scope_bounds.py +7 -0
- apsimo/secrets/__init__.py +6 -0
- apsimo/secrets/backends/__init__.py +8 -0
- apsimo/secrets/backends/base.py +42 -0
- apsimo/secrets/backends/env.py +110 -0
- apsimo/secrets/backends/keyring.py +72 -0
- apsimo/secrets/backends/onepassword.py +232 -0
- apsimo/secrets/cli.py +191 -0
- apsimo/secrets/manager.py +160 -0
- apsimo/secrets/migration.py +101 -0
- apsimo/secrets/types.py +98 -0
- apsimo/seed.py +41 -0
- apsimo/self_model/__init__.py +37 -0
- apsimo/self_model/appraisals.py +673 -0
- apsimo/self_model/benchmark.py +1314 -0
- apsimo/self_model/brief.py +40 -0
- apsimo/self_model/event_concerns.py +1128 -0
- apsimo/self_model/execution_forecasts.py +353 -0
- apsimo/self_model/expectations.py +1595 -0
- apsimo/self_model/experiments.py +1150 -0
- apsimo/self_model/journal.py +148 -0
- apsimo/self_model/judgments.py +705 -0
- apsimo/self_model/native_outcomes.py +55 -0
- apsimo/self_model/params.py +220 -0
- apsimo/self_model/perspective.py +246 -0
- apsimo/self_model/reconcile.py +183 -0
- apsimo/self_model/reply_forecasts.py +381 -0
- apsimo/self_model/runtime_forecasts.py +296 -0
- apsimo/self_model/runtime_models.py +67 -0
- apsimo/self_model/settlement.py +207 -0
- apsimo/self_model/situation.py +1731 -0
- apsimo/self_model/store.py +883 -0
- apsimo/self_model/supervised.py +137 -0
- apsimo/self_model/thinker.py +99 -0
- apsimo/self_model/trust.py +388 -0
- apsimo/self_model/workspace.py +2388 -0
- apsimo/server.py +4197 -0
- apsimo/services/__init__.py +1 -0
- apsimo/services/agent_bridge.py +474 -0
- apsimo/services/initiative_executor.py +914 -0
- apsimo/services/instance.py +297 -0
- apsimo/sessions/__init__.py +22 -0
- apsimo/sessions/config.py +13 -0
- apsimo/sessions/context_loader.py +88 -0
- apsimo/sessions/federation_session.py +75 -0
- apsimo/sessions/isolated_session.py +98 -0
- apsimo/sessions/reports.py +84 -0
- apsimo/sessions/store.py +148 -0
- apsimo/setup.py +2818 -0
- apsimo/setup_hermes.py +879 -0
- apsimo/setup_local_work.py +218 -0
- apsimo/setup_native_goals.py +134 -0
- apsimo/setup_native_reviews.py +115 -0
- apsimo/skills/__init__.py +10 -0
- apsimo/skills/base.py +108 -0
- apsimo/skills/budget.py +28 -0
- apsimo/skills/executor.py +493 -0
- apsimo/skills/executors/__init__.py +1 -0
- apsimo/skills/executors/behavioral_correction.py +75 -0
- apsimo/skills/executors/capability_gap.py +38 -0
- apsimo/skills/executors/data_quality.py +163 -0
- apsimo/skills/executors/knowledge_acquisition.py +41 -0
- apsimo/skills/executors/operational_hygiene.py +185 -0
- apsimo/skills/executors/subsystem_health.py +169 -0
- apsimo/skills/hermes_export.py +431 -0
- apsimo/skills/index.py +123 -0
- apsimo/skills/learning/__init__.py +21 -0
- apsimo/skills/learning/novelty_detector.py +206 -0
- apsimo/skills/learning/pattern_extractor.py +199 -0
- apsimo/skills/learning/triggers.py +159 -0
- apsimo/skills/loader.py +246 -0
- apsimo/skills/migrations/002_progressive_loading.sql +6 -0
- apsimo/skills/migrations/backfill_triggers.py +20 -0
- apsimo/skills/models.py +202 -0
- apsimo/skills/packager.py +128 -0
- apsimo/skills/protocols.py +70 -0
- apsimo/skills/registry.py +191 -0
- apsimo/skills/runtime.py +58 -0
- apsimo/skills/sandbox_runner.py +229 -0
- apsimo/skills/scheduler.py +129 -0
- apsimo/skills/schema.py +79 -0
- apsimo/skills/security/__init__.py +12 -0
- apsimo/skills/security/guards.py +53 -0
- apsimo/skills/security/scanner.py +223 -0
- apsimo/skills_memory/__init__.py +26 -0
- apsimo/skills_memory/distill.py +159 -0
- apsimo/skills_memory/models.py +85 -0
- apsimo/skills_memory/retrieve.py +62 -0
- apsimo/skills_memory/store.py +172 -0
- apsimo/surprise/__init__.py +6 -0
- apsimo/surprise/accumulation.py +57 -0
- apsimo/surprise/scorer.py +102 -0
- apsimo/surprise/store.py +203 -0
- apsimo/task_queue/__init__.py +69 -0
- apsimo/task_queue/action_receipts.py +148 -0
- apsimo/task_queue/approval_relay_canary.py +108 -0
- apsimo/task_queue/config.py +85 -0
- apsimo/task_queue/contract.py +361 -0
- apsimo/task_queue/events.py +130 -0
- apsimo/task_queue/governor.py +1031 -0
- apsimo/task_queue/handlers/__init__.py +16 -0
- apsimo/task_queue/handlers/base.py +37 -0
- apsimo/task_queue/handlers/inference.py +640 -0
- apsimo/task_queue/handlers/monitoring.py +116 -0
- apsimo/task_queue/handlers/registry.py +75 -0
- apsimo/task_queue/handlers/subtask_handler.py +173 -0
- apsimo/task_queue/handlers/system_maintenance.py +147 -0
- apsimo/task_queue/mesh_integration.py +111 -0
- apsimo/task_queue/models.py +317 -0
- apsimo/task_queue/queue_manager.py +8286 -0
- apsimo/task_queue/routing.py +287 -0
- apsimo/task_queue/scheduler.py +252 -0
- apsimo/task_queue/schema.sql +197 -0
- apsimo/task_queue/work_control.py +342 -0
- apsimo/task_queue/worker.py +993 -0
- apsimo/telemetry.py +145 -0
- apsimo/tom/__init__.py +6 -0
- apsimo/tom/affect.py +387 -0
- apsimo/tom/approvals.py +171 -0
- apsimo/tom/arcs.py +896 -0
- apsimo/tom/asymmetry.py +131 -0
- apsimo/tom/eligibility.py +248 -0
- apsimo/tom/engagement.py +214 -0
- apsimo/tom/exposure.py +214 -0
- apsimo/tom/extractor.py +306 -0
- apsimo/tom/fact_adapters.py +144 -0
- apsimo/tom/facts.py +326 -0
- apsimo/tom/integration.py +592 -0
- apsimo/tom/leveled.py +118 -0
- apsimo/tom/levels.py +247 -0
- apsimo/tom/recipient_audit.py +995 -0
- apsimo/tom/recipient_simulator.py +593 -0
- apsimo/tom/source_lineage.py +93 -0
- apsimo/tom/tom2.py +277 -0
- apsimo/tom/visibility.py +559 -0
- apsimo/tom/visibility_store.py +414 -0
- apsimo/tools/__init__.py +0 -0
- apsimo/tools/definitions.py +740 -0
- apsimo/tools/handlers.py +943 -0
- apsimo/toolsmith/__init__.py +26 -0
- apsimo/toolsmith/authority.py +166 -0
- apsimo/toolsmith/engine.py +559 -0
- apsimo/toolsmith/integrity.py +100 -0
- apsimo/toolsmith/miner.py +145 -0
- apsimo/toolsmith/policy.py +110 -0
- apsimo/toolsmith/registry.py +635 -0
- apsimo/turns/__init__.py +17 -0
- apsimo/turns/audio.py +134 -0
- apsimo/turns/documents.py +235 -0
- apsimo/turns/executions.py +486 -0
- apsimo/turns/hermes_history.py +245 -0
- apsimo/turns/hermes_kanban.py +268 -0
- apsimo/turns/hermes_work.py +96 -0
- apsimo/turns/idempotency.py +752 -0
- apsimo/turns/local_work.py +115 -0
- apsimo/turns/media.py +581 -0
- apsimo/turns/reported_workers.py +196 -0
- apsimo/turns/source_annotations.py +283 -0
- apsimo/turns/source_attribution.py +154 -0
- apsimo/turns/source_read.py +351 -0
- apsimo/turns/source_vectors.py +263 -0
- apsimo/turns/video.py +210 -0
- apsimo/util/autonomy_preset.py +220 -0
- apsimo/util/instance.py +92 -0
- apsimo/util/model_output.py +25 -0
- apsimo/util/quiet_hours.py +27 -0
- apsimo/util/session_safety.py +37 -0
- apsimo/util/temporal.py +343 -0
- apsimo/vector/__init__.py +75 -0
- apsimo/vector/backfill.py +171 -0
- apsimo/vector/caption.py +114 -0
- apsimo/vector/collections.py +51 -0
- apsimo/vector/config.py +102 -0
- apsimo/vector/embedder.py +670 -0
- apsimo/vector/image_preprocess.py +406 -0
- apsimo/vector/image_store.py +296 -0
- apsimo/vector/indexes.py +162 -0
- apsimo/vector/migrate.py +334 -0
- apsimo/vector/multimodal_provider.py +417 -0
- apsimo/vector/multimodal_types.py +87 -0
- apsimo/vector/openai_provider.py +119 -0
- apsimo/vector/query.py +49 -0
- apsimo/vector/reranker.py +565 -0
- apsimo/vector/safety_image.py +159 -0
- apsimo/vector/scanner.py +197 -0
- apsimo/vector/setup.py +289 -0
- apsimo/vector/store.py +533 -0
- apsimo/vector/tiers.py +263 -0
- apsimo/work_orders.py +925 -0
- apsimo/workers/__init__.py +21 -0
- apsimo/workers/agent_bridge.py +640 -0
- apsimo/workers/colony_worker.py +382 -0
- apsimo/workers/queue_worker.py +441 -0
- apsimo/workers/skills_sync.py +152 -0
- apsimo/world_model/__init__.py +71 -0
- apsimo/world_model/causal_maintenance.py +131 -0
- apsimo/world_model/causal_policy.py +43 -0
- apsimo/world_model/causal_query.py +125 -0
- apsimo/world_model/confidence.py +54 -0
- apsimo/world_model/config.py +64 -0
- apsimo/world_model/constants.py +97 -0
- apsimo/world_model/entities.py +145 -0
- apsimo/world_model/expectation_resolvers.py +177 -0
- apsimo/world_model/extraction/__init__.py +7 -0
- apsimo/world_model/extraction/base.py +62 -0
- apsimo/world_model/extraction/conversation_extractor.py +262 -0
- apsimo/world_model/extraction/detector.py +74 -0
- apsimo/world_model/extraction/document_extractor.py +78 -0
- apsimo/world_model/extraction/formats/__init__.py +24 -0
- apsimo/world_model/extraction/formats/csv_fmt.py +68 -0
- apsimo/world_model/extraction/formats/html_fmt.py +72 -0
- apsimo/world_model/extraction/formats/json_fmt.py +68 -0
- apsimo/world_model/extraction/formats/pdf.py +43 -0
- apsimo/world_model/extraction/formats/text.py +27 -0
- apsimo/world_model/extraction/llm_extractor.py +164 -0
- apsimo/world_model/extraction/pipeline.py +73 -0
- apsimo/world_model/integrations/__init__.py +5 -0
- apsimo/world_model/integrations/mind_model_bridge.py +115 -0
- apsimo/world_model/integrations/social_intel_bridge.py +120 -0
- apsimo/world_model/jobs/__init__.py +4 -0
- apsimo/world_model/jobs/extraction_job.py +168 -0
- apsimo/world_model/llm_extract.py +572 -0
- apsimo/world_model/neo4j/__init__.py +5 -0
- apsimo/world_model/neo4j/backend.py +654 -0
- apsimo/world_model/observations.py +155 -0
- apsimo/world_model/populator.py +307 -0
- apsimo/world_model/postgres/__init__.py +1 -0
- apsimo/world_model/postgres/backend.py +683 -0
- apsimo/world_model/relationships.py +25 -0
- apsimo/world_model/resolution/__init__.py +13 -0
- apsimo/world_model/resolution/entity_resolver.py +232 -0
- apsimo/world_model/resolution/merge_audit.py +16 -0
- apsimo/world_model/resolution/merge_workflow.py +117 -0
- apsimo/world_model/source_reports.py +121 -0
- apsimo/world_model/sqlite/__init__.py +4 -0
- apsimo/world_model/sqlite/backend.py +855 -0
- apsimo/world_model/sqlite/schema.sql +132 -0
- apsimo/world_model/store.py +545 -0
- apsimo-1.3.0.dist-info/METADATA +78 -0
- apsimo-1.3.0.dist-info/RECORD +614 -0
- apsimo-1.3.0.dist-info/WHEEL +5 -0
- apsimo-1.3.0.dist-info/entry_points.txt +11 -0
- apsimo-1.3.0.dist-info/licenses/LICENSE +21 -0
- apsimo-1.3.0.dist-info/top_level.txt +2 -0
- colony_sidecar/__init__.py +4 -0
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
"""LLM-assisted world-model extraction (batch, journaled).
|
|
2
|
+
|
|
3
|
+
Complements the rule-based WorldModelPopulator: on a daily batch cadence
|
|
4
|
+
(never per-turn), recent conversation memories are handed to a fast
|
|
5
|
+
classifier LLM which proposes structured entities + relationships as STRICT
|
|
6
|
+
JSON. Code validates everything (known types, name quality, confidence
|
|
7
|
+
floor, boundary check, resolver dedup) before any write, and every write is
|
|
8
|
+
recorded in the unified action journal so the owner can review exactly what
|
|
9
|
+
was added and why.
|
|
10
|
+
|
|
11
|
+
Endpoint: env-driven OpenAI-compatible chat endpoint
|
|
12
|
+
(COLONY_WORLD_LLM_BASE_URL / _MODEL / _API_KEY, falling back to the
|
|
13
|
+
COLONY_INTROSPECT_* fast-classifier endpoint convention).
|
|
14
|
+
|
|
15
|
+
Modes (COLONY_WORLD_LLM_EXTRACT): off (default) | shadow (log the would-be
|
|
16
|
+
writes) | live (write + journal).
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import json
|
|
22
|
+
import logging
|
|
23
|
+
import os
|
|
24
|
+
import re
|
|
25
|
+
from typing import Any, Dict, List, Optional
|
|
26
|
+
|
|
27
|
+
from apsimo.world_model.constants import CAUSAL_RELATIONSHIP_TYPES
|
|
28
|
+
from apsimo.world_model.populator import _is_low_quality, _looks_like_fragment
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
_ENTITY_TYPES = {"person", "company", "project", "product", "location",
|
|
33
|
+
"event", "concept"}
|
|
34
|
+
_REL_TYPES = {"WM_WORKS_AT", "WM_KNOWS", "WM_PART_OF", "WM_RELATED_TO",
|
|
35
|
+
"WM_LOCATED_IN", "WM_BUILDS"}
|
|
36
|
+
_MIN_CONF = 0.5
|
|
37
|
+
|
|
38
|
+
# Legacy causal extraction is a hypothesis with a conservative creation
|
|
39
|
+
# ceiling. Repeat batches are not independent predictive evidence.
|
|
40
|
+
_CAUSAL_CREATE_CEILING = 0.5
|
|
41
|
+
|
|
42
|
+
_SYSTEM_PROMPT = """\
|
|
43
|
+
You extract structured world knowledge from conversation excerpts for a
|
|
44
|
+
personal intelligence system. Extract only REAL named entities the owner's
|
|
45
|
+
world contains (people, companies, projects, products, locations, events,
|
|
46
|
+
concepts) and clearly stated relationships between them.
|
|
47
|
+
|
|
48
|
+
Rules:
|
|
49
|
+
- Only entities explicitly named in the text. Never invent, never guess.
|
|
50
|
+
- Skip generic words, greetings, dates, the assistant itself, and anything
|
|
51
|
+
that is not a proper name.
|
|
52
|
+
- confidence reflects how clearly the text establishes the entity (0-1).
|
|
53
|
+
- Weight owner relevance: entities recurring in the excerpt or tied to the
|
|
54
|
+
owner's work, projects, or relationships score higher; incidental one-off
|
|
55
|
+
names score low (<=0.4).
|
|
56
|
+
|
|
57
|
+
Respond with ONLY a JSON object (no prose, no markdown fences):
|
|
58
|
+
{"entities": [{"name": str, "type": one of ["person","company","project",
|
|
59
|
+
"product","location","event","concept"], "confidence": float}],
|
|
60
|
+
"relationships": [{"source": str, "rel": one of ["WM_WORKS_AT","WM_KNOWS",
|
|
61
|
+
"WM_PART_OF","WM_RELATED_TO","WM_LOCATED_IN","WM_BUILDS"], "target": str,
|
|
62
|
+
"confidence": float}],
|
|
63
|
+
"observations": [{"entity": str, "property": "lowercase_property_key", "value": str, "temporal_status": "current",
|
|
64
|
+
"evidence": "exact verbatim sentence from excerpt containing both entity and value"}]}
|
|
65
|
+
Observations are reported claims only, never proof or direct sensor observations.
|
|
66
|
+
Only unqualified present-tense states belong in observations. Omit future, past,
|
|
67
|
+
dated, conditional or uncertain assertions; receiving a report today does not
|
|
68
|
+
make its described state current. Never clip a date/tense qualifier off a quote.
|
|
69
|
+
Do not infer personality, authority or trust. Omit vague, negated or uncertain claims.
|
|
70
|
+
Never turn a question, example, instruction or an assistant's paraphrase into a fact."""
|
|
71
|
+
|
|
72
|
+
_CAUSAL_PROMPT = """
|
|
73
|
+
|
|
74
|
+
Additionally extract clearly stated CAUSAL claims between those same
|
|
75
|
+
entities as "causal": [{"source": str, "rel": one of ["WM_CAUSES",
|
|
76
|
+
"WM_ENABLES","WM_BLOCKS","WM_INHIBITS"], "target": str, "evidence": str,
|
|
77
|
+
"confidence": float}].
|
|
78
|
+
Causal rules:
|
|
79
|
+
- Only when the text itself asserts the causal link. Never infer one.
|
|
80
|
+
- "evidence" MUST be a short verbatim quote copied character-for-character
|
|
81
|
+
from the excerpt; a claim without its exact quote will be discarded."""
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def llm_extract_mode() -> str:
|
|
85
|
+
from apsimo.util.autonomy_preset import resolve
|
|
86
|
+
return resolve("COLONY_WORLD_LLM_EXTRACT",
|
|
87
|
+
("off", "shadow", "live"), "off")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def world_supervised_max_writes() -> int:
|
|
91
|
+
"""Per-run cap on supervised world-model writes (H1.5, default 25)."""
|
|
92
|
+
try:
|
|
93
|
+
v = int(os.environ.get("COLONY_WORLD_SUPERVISED_MAX_WRITES", "25"))
|
|
94
|
+
return v if v > 0 else 25
|
|
95
|
+
except (TypeError, ValueError):
|
|
96
|
+
return 25
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def causal_extract_mode() -> str:
|
|
100
|
+
"""Effective causal-extraction mode: min(COLONY_CAUSAL_EXTRACT,
|
|
101
|
+
COLONY_WORLD_LLM_EXTRACT) over off < shadow < live — causal extraction
|
|
102
|
+
can never be MORE live than the extractor that carries it."""
|
|
103
|
+
from apsimo.util.autonomy_preset import resolve
|
|
104
|
+
order = ("off", "shadow", "live")
|
|
105
|
+
causal = resolve("COLONY_CAUSAL_EXTRACT", order, "off")
|
|
106
|
+
return order[min(order.index(causal), order.index(llm_extract_mode()))]
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _endpoint() -> Dict[str, str]:
|
|
110
|
+
base = (os.environ.get("COLONY_WORLD_LLM_BASE_URL", "")
|
|
111
|
+
or os.environ.get("COLONY_INTROSPECT_BASE_URL", "")).rstrip("/")
|
|
112
|
+
model = (os.environ.get("COLONY_WORLD_LLM_MODEL", "")
|
|
113
|
+
or os.environ.get("COLONY_INTROSPECT_MODEL", ""))
|
|
114
|
+
key = (os.environ.get("COLONY_WORLD_LLM_API_KEY", "")
|
|
115
|
+
or os.environ.get("COLONY_INTROSPECT_API_KEY", ""))
|
|
116
|
+
return {"base": base, "model": model, "key": key}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _parse_obj(content: str) -> Optional[dict]:
|
|
120
|
+
text = (content or "").strip()
|
|
121
|
+
if "```" in text:
|
|
122
|
+
m = re.search(r"```(?:json)?\s*(.*?)```", text, re.DOTALL)
|
|
123
|
+
if m:
|
|
124
|
+
text = m.group(1).strip()
|
|
125
|
+
if not text.startswith("{"):
|
|
126
|
+
m = re.search(r"\{.*\}", text, re.DOTALL)
|
|
127
|
+
if not m:
|
|
128
|
+
return None
|
|
129
|
+
text = m.group(0)
|
|
130
|
+
try:
|
|
131
|
+
data = json.loads(text)
|
|
132
|
+
except json.JSONDecodeError:
|
|
133
|
+
return None
|
|
134
|
+
return data if isinstance(data, dict) else None
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class WorldLLMExtractor:
|
|
138
|
+
def __init__(self, store: Any, *, graph: Any = None,
|
|
139
|
+
directive_manager: Any = None, journal: Any = None,
|
|
140
|
+
self_model: Any = None, source_ledger: Any = None, router_provider: Any = None) -> None:
|
|
141
|
+
self._store = store
|
|
142
|
+
self._graph = graph
|
|
143
|
+
self._directives = directive_manager
|
|
144
|
+
self._journal = journal
|
|
145
|
+
self._self_model = self_model
|
|
146
|
+
self._source_ledger = source_ledger
|
|
147
|
+
self._router_provider = router_provider
|
|
148
|
+
self._resolver = None
|
|
149
|
+
if store is not None:
|
|
150
|
+
try:
|
|
151
|
+
from apsimo.world_model.resolution.entity_resolver import (
|
|
152
|
+
EntityResolver,
|
|
153
|
+
)
|
|
154
|
+
self._resolver = EntityResolver(store)
|
|
155
|
+
except Exception:
|
|
156
|
+
pass
|
|
157
|
+
self.last_report: Dict[str, Any] = {}
|
|
158
|
+
|
|
159
|
+
# -- source text -------------------------------------------------------
|
|
160
|
+
|
|
161
|
+
_RECENT_MEMORY_QUERY = (
|
|
162
|
+
"""MATCH (m:Memory)
|
|
163
|
+
WHERE m.created_at >= datetime() - duration({hours: $hours})
|
|
164
|
+
AND m.type IN ['episodic', 'semantic']
|
|
165
|
+
AND m.superseded_by IS NULL
|
|
166
|
+
RETURN m.content AS content
|
|
167
|
+
ORDER BY m.created_at DESC LIMIT $limit""")
|
|
168
|
+
|
|
169
|
+
async def _recent_memory_texts(self, hours: float = 24.0,
|
|
170
|
+
limit: int = 30) -> List[str]:
|
|
171
|
+
if self._graph is None or not hasattr(self._graph, "run_query"):
|
|
172
|
+
return []
|
|
173
|
+
try:
|
|
174
|
+
# Register this exact parameterized read query with the graph
|
|
175
|
+
# client's Cypher allowlist (single-sourced here).
|
|
176
|
+
if hasattr(type(self._graph), "register_allowed_cypher"):
|
|
177
|
+
type(self._graph).register_allowed_cypher(
|
|
178
|
+
self._RECENT_MEMORY_QUERY)
|
|
179
|
+
rows = await self._graph.run_query(
|
|
180
|
+
self._RECENT_MEMORY_QUERY,
|
|
181
|
+
{"hours": int(hours), "limit": int(limit)})
|
|
182
|
+
return [str(r.get("content") or "") for r in rows or []
|
|
183
|
+
if r.get("content")]
|
|
184
|
+
except Exception as exc:
|
|
185
|
+
logger.info("world llm-extract memory query failed: %s", exc)
|
|
186
|
+
return []
|
|
187
|
+
|
|
188
|
+
# -- one LLM batch -------------------------------------------------------
|
|
189
|
+
@staticmethod
|
|
190
|
+
def _excerpt(texts: List[str]) -> str:
|
|
191
|
+
"""The exact excerpt sent to the LLM — also the anti-fabrication
|
|
192
|
+
reference that causal evidence quotes are pinned against."""
|
|
193
|
+
return "\n---\n".join(t[:600] for t in texts)[:8000]
|
|
194
|
+
|
|
195
|
+
async def _llm_batch(self, texts: List[str]) -> Optional[dict]:
|
|
196
|
+
if self._router_provider is not None:
|
|
197
|
+
# Production uses the same discoverable extraction role as source
|
|
198
|
+
# learning. Re-read the router each batch so rebinding needs no restart.
|
|
199
|
+
import asyncio
|
|
200
|
+
from apsimo.beliefs.source_claims import final_text, extraction_timeout_seconds
|
|
201
|
+
router = self._router_provider()
|
|
202
|
+
if getattr(router, 'supports_function_routing', False) is not True:
|
|
203
|
+
return None
|
|
204
|
+
prompt = _SYSTEM_PROMPT + (_CAUSAL_PROMPT if causal_extract_mode() != 'off' else '')
|
|
205
|
+
response = await asyncio.wait_for(router.complete(messages=[
|
|
206
|
+
{'role': 'system', 'content': prompt}, {'role': 'user', 'content': self._excerpt(texts)}],
|
|
207
|
+
context={'function_role': 'extraction', 'task': 'world_source_reports',
|
|
208
|
+
'max_output_tokens': 900, 'allow_fallback': True}),
|
|
209
|
+
timeout=extraction_timeout_seconds(router))
|
|
210
|
+
self.last_report['processor'] = {'function_role': 'extraction',
|
|
211
|
+
'model_id': getattr(response, 'model_id', None),
|
|
212
|
+
'model_revision': getattr(response, 'model_revision', None),
|
|
213
|
+
'config_revision': getattr(response, 'config_revision', None)}
|
|
214
|
+
return _parse_obj(final_text(response))
|
|
215
|
+
ep = _endpoint()
|
|
216
|
+
if not ep["base"] or not ep["model"]:
|
|
217
|
+
return None
|
|
218
|
+
try:
|
|
219
|
+
import aiohttp
|
|
220
|
+
except ImportError:
|
|
221
|
+
return None
|
|
222
|
+
excerpt = self._excerpt(texts)
|
|
223
|
+
system_prompt = _SYSTEM_PROMPT
|
|
224
|
+
if causal_extract_mode() != "off":
|
|
225
|
+
system_prompt = _SYSTEM_PROMPT + _CAUSAL_PROMPT
|
|
226
|
+
payload = {
|
|
227
|
+
"model": ep["model"], "temperature": 0, "max_tokens": 900,
|
|
228
|
+
"messages": [{"role": "system", "content": system_prompt},
|
|
229
|
+
{"role": "user", "content": excerpt}],
|
|
230
|
+
}
|
|
231
|
+
headers = {"Content-Type": "application/json"}
|
|
232
|
+
if ep["key"]:
|
|
233
|
+
headers["Authorization"] = f"Bearer {ep['key']}"
|
|
234
|
+
try:
|
|
235
|
+
# Keep the per-request timeout WELL under the autonomy tick
|
|
236
|
+
# budget: when the tick's wait_for cancels mid-request, aiohttp's
|
|
237
|
+
# session unwind gets interrupted and leaks ("Unclosed client
|
|
238
|
+
# session" spam). A clean inner timeout aborts and closes.
|
|
239
|
+
timeout = aiohttp.ClientTimeout(total=float(
|
|
240
|
+
os.environ.get("COLONY_WORLD_LLM_TIMEOUT", "40")))
|
|
241
|
+
async with aiohttp.ClientSession() as s:
|
|
242
|
+
async with s.post(ep["base"] + "/chat/completions",
|
|
243
|
+
json=payload, headers=headers,
|
|
244
|
+
timeout=timeout) as resp:
|
|
245
|
+
if resp.status != 200:
|
|
246
|
+
return None
|
|
247
|
+
data = await resp.json()
|
|
248
|
+
return _parse_obj(data["choices"][0]["message"]["content"])
|
|
249
|
+
except Exception as exc:
|
|
250
|
+
logger.debug("world llm-extract call failed: %s", exc)
|
|
251
|
+
return None
|
|
252
|
+
|
|
253
|
+
def _boundary_ok(self, name: str) -> bool:
|
|
254
|
+
if self._directives is None:
|
|
255
|
+
# No directive manager configured: nothing to check against
|
|
256
|
+
# (distinct from "checked and allowed" — see log line).
|
|
257
|
+
logger.debug("world llm-extract boundary UNCHECKED for %r "
|
|
258
|
+
"(no directive manager)", name)
|
|
259
|
+
return True
|
|
260
|
+
try:
|
|
261
|
+
from apsimo.directives import Action
|
|
262
|
+
return self._directives.check(
|
|
263
|
+
Action(kind="populate", text=name, target=name)).allowed
|
|
264
|
+
except Exception:
|
|
265
|
+
# Fail closed, matching directives.guard.boundary_fail_closed():
|
|
266
|
+
# an error while evaluating an owner boundary must refuse.
|
|
267
|
+
logger.warning("world llm-extract boundary check errored for %r "
|
|
268
|
+
"— failing closed", name, exc_info=True)
|
|
269
|
+
return False
|
|
270
|
+
|
|
271
|
+
# -- supervised rung (H1.5) -----------------------------------------------
|
|
272
|
+
|
|
273
|
+
def _effective_mode(self) -> str:
|
|
274
|
+
"""Env mode, graduated by the trust engine ONLY when the world_model
|
|
275
|
+
domain is explicitly enrolled in COLONY_SUPERVISED_LIVE_DOMAINS —
|
|
276
|
+
otherwise the env mode is returned untouched (regression lock: a
|
|
277
|
+
deployment that never enrolls the domain sees zero behavior change,
|
|
278
|
+
whatever the trust stage says)."""
|
|
279
|
+
env = llm_extract_mode()
|
|
280
|
+
try:
|
|
281
|
+
from apsimo.self_model.supervised import (
|
|
282
|
+
effective_mode, supervised_domains,
|
|
283
|
+
)
|
|
284
|
+
if "world_model" not in supervised_domains():
|
|
285
|
+
return env
|
|
286
|
+
return effective_mode(
|
|
287
|
+
"world_model", env,
|
|
288
|
+
getattr(self._self_model, "trust", None))
|
|
289
|
+
except Exception:
|
|
290
|
+
return env
|
|
291
|
+
|
|
292
|
+
def _may_write(self, mode: str, op: str, report: Dict[str, Any]) -> bool:
|
|
293
|
+
"""live => yes. supervised => only ops pinned reversible for the
|
|
294
|
+
world_model domain, and only under the per-run write cap. Anything
|
|
295
|
+
else => no."""
|
|
296
|
+
if mode == "live":
|
|
297
|
+
return True
|
|
298
|
+
if mode != "supervised":
|
|
299
|
+
return False
|
|
300
|
+
from apsimo.self_model.supervised import reversible
|
|
301
|
+
if not reversible("world_model", op):
|
|
302
|
+
return False
|
|
303
|
+
if report.get("writes", 0) >= world_supervised_max_writes():
|
|
304
|
+
report["supervised_capped"] = report.get("supervised_capped", 0) + 1
|
|
305
|
+
return False
|
|
306
|
+
return True
|
|
307
|
+
|
|
308
|
+
def _record_outcome(self, mode: str, report: Dict[str, Any]) -> None:
|
|
309
|
+
"""Real trust outcomes for the world_model domain — but only when
|
|
310
|
+
the domain is enrolled on the rung, and only when the run actually
|
|
311
|
+
wrote something (a no-op run earns nothing; see H1.3)."""
|
|
312
|
+
if self._self_model is None:
|
|
313
|
+
return
|
|
314
|
+
try:
|
|
315
|
+
from apsimo.self_model.supervised import supervised_domains
|
|
316
|
+
if "world_model" not in supervised_domains():
|
|
317
|
+
return
|
|
318
|
+
if mode not in ("live", "supervised"):
|
|
319
|
+
return
|
|
320
|
+
if report.get("writes", 0) > 0:
|
|
321
|
+
self._self_model.record("world_model", "success", shadow=False)
|
|
322
|
+
except Exception:
|
|
323
|
+
pass
|
|
324
|
+
|
|
325
|
+
# -- main run -------------------------------------------------------------
|
|
326
|
+
async def run(self, texts: Optional[List[str]] = None) -> Dict[str, Any]:
|
|
327
|
+
mode = self._effective_mode()
|
|
328
|
+
cmode = causal_extract_mode()
|
|
329
|
+
report: Dict[str, Any] = {"mode": mode, "batches": 0, "created": [],
|
|
330
|
+
"merged": [], "relationships": [],
|
|
331
|
+
"skipped": 0, "causal_mode": cmode,
|
|
332
|
+
"causal": [], "causal_corroborated": [],
|
|
333
|
+
"causal_skipped": 0,
|
|
334
|
+
"writes": 0, "supervised_capped": 0}
|
|
335
|
+
self.last_report = report
|
|
336
|
+
if mode == "off" or self._store is None:
|
|
337
|
+
return report
|
|
338
|
+
self._seen_rels: set = set()
|
|
339
|
+
source_batches = None
|
|
340
|
+
if texts is None and self._source_ledger is not None:
|
|
341
|
+
from .source_reports import recent_batches
|
|
342
|
+
source_batches = recent_batches(self._source_ledger)
|
|
343
|
+
texts = [source['content'] for batch in source_batches for source in batch]
|
|
344
|
+
elif texts is None:
|
|
345
|
+
texts = await self._recent_memory_texts()
|
|
346
|
+
report["texts"] = len(texts or [])
|
|
347
|
+
if not texts:
|
|
348
|
+
logger.info("world-llm-extract[%s]: no recent memory texts to "
|
|
349
|
+
"process", mode)
|
|
350
|
+
return report
|
|
351
|
+
|
|
352
|
+
batches = [([source['content'] for source in batch], batch) for batch in source_batches] if source_batches is not None else [
|
|
353
|
+
(texts[i:i + 10], []) for i in range(0, len(texts), 10)]
|
|
354
|
+
for batch, sources in batches:
|
|
355
|
+
name_to_id: Dict[str, str] = {}
|
|
356
|
+
data = await self._llm_batch(batch)
|
|
357
|
+
if not data:
|
|
358
|
+
continue
|
|
359
|
+
report["batches"] += 1
|
|
360
|
+
for e in (data.get("entities") or []):
|
|
361
|
+
if not isinstance(e, dict):
|
|
362
|
+
continue
|
|
363
|
+
name = str(e.get("name", "")).strip()[:120]
|
|
364
|
+
etype = str(e.get("type", "")).strip().lower()
|
|
365
|
+
try:
|
|
366
|
+
conf = float(e.get("confidence", 0))
|
|
367
|
+
except (TypeError, ValueError):
|
|
368
|
+
conf = 0.0
|
|
369
|
+
if (not name or etype not in _ENTITY_TYPES
|
|
370
|
+
or conf < _MIN_CONF
|
|
371
|
+
or _looks_like_fragment(name)
|
|
372
|
+
or _is_low_quality(name, etype)):
|
|
373
|
+
report["skipped"] += 1
|
|
374
|
+
continue
|
|
375
|
+
if not self._boundary_ok(name):
|
|
376
|
+
report["skipped"] += 1
|
|
377
|
+
continue
|
|
378
|
+
eid = await self._upsert(name, etype, conf, mode, report)
|
|
379
|
+
if eid:
|
|
380
|
+
name_to_id[name.lower()] = eid
|
|
381
|
+
from .source_reports import record_reports
|
|
382
|
+
await record_reports(self, data.get('observations'), sources, name_to_id, mode, report)
|
|
383
|
+
for r in (data.get("relationships") or []):
|
|
384
|
+
if not isinstance(r, dict):
|
|
385
|
+
continue
|
|
386
|
+
rel = str(r.get("rel", "")).strip().upper()
|
|
387
|
+
src = str(r.get("source", "")).strip().lower()
|
|
388
|
+
tgt = str(r.get("target", "")).strip().lower()
|
|
389
|
+
if (rel not in _REL_TYPES or src not in name_to_id
|
|
390
|
+
or tgt not in name_to_id or src == tgt):
|
|
391
|
+
continue
|
|
392
|
+
await self._upsert_rel(name_to_id[src], rel,
|
|
393
|
+
name_to_id[tgt],
|
|
394
|
+
float(r.get("confidence", 0.5) or 0.5),
|
|
395
|
+
mode, report)
|
|
396
|
+
if cmode != "off":
|
|
397
|
+
excerpt_lower = self._excerpt(batch).lower()
|
|
398
|
+
for c in (data.get("causal") or []):
|
|
399
|
+
if not isinstance(c, dict):
|
|
400
|
+
continue
|
|
401
|
+
rel = str(c.get("rel", "")).strip().upper()
|
|
402
|
+
src = str(c.get("source", "")).strip().lower()
|
|
403
|
+
tgt = str(c.get("target", "")).strip().lower()
|
|
404
|
+
evidence = str(c.get("evidence", "")).strip()
|
|
405
|
+
try:
|
|
406
|
+
conf = float(c.get("confidence", 0.5) or 0.5)
|
|
407
|
+
except (TypeError, ValueError):
|
|
408
|
+
conf = 0.5
|
|
409
|
+
if (rel not in CAUSAL_RELATIONSHIP_TYPES
|
|
410
|
+
or src not in name_to_id or tgt not in name_to_id
|
|
411
|
+
or src == tgt):
|
|
412
|
+
report["causal_skipped"] += 1
|
|
413
|
+
continue
|
|
414
|
+
# Anti-fabrication pin: the evidence quote must actually
|
|
415
|
+
# occur in the source excerpt (case-insensitive). A causal
|
|
416
|
+
# claim the model cannot quote is a fabrication and is
|
|
417
|
+
# discarded, whatever its confidence.
|
|
418
|
+
if not evidence or evidence.lower() not in excerpt_lower:
|
|
419
|
+
report["causal_skipped"] += 1
|
|
420
|
+
continue
|
|
421
|
+
await self._upsert_causal(
|
|
422
|
+
name_to_id[src], rel, name_to_id[tgt],
|
|
423
|
+
evidence, conf, cmode, report)
|
|
424
|
+
logger.info(
|
|
425
|
+
"world-llm-extract[%s]: %d batch(es), created=%d merged=%d "
|
|
426
|
+
"rel=%d skipped=%d writes=%d", mode, report["batches"],
|
|
427
|
+
len(report["created"]), len(report["merged"]),
|
|
428
|
+
len(report["relationships"]), report["skipped"],
|
|
429
|
+
report["writes"])
|
|
430
|
+
self._record_outcome(mode, report)
|
|
431
|
+
return report
|
|
432
|
+
|
|
433
|
+
async def _upsert(self, name: str, etype: str, conf: float, mode: str,
|
|
434
|
+
report: Dict[str, Any]) -> Optional[str]:
|
|
435
|
+
action, matched = "create", None
|
|
436
|
+
if self._resolver is not None:
|
|
437
|
+
try:
|
|
438
|
+
from apsimo.world_model.extraction.conversation_extractor import (
|
|
439
|
+
ExtractionCandidate,
|
|
440
|
+
)
|
|
441
|
+
cand = ExtractionCandidate(text=name, entity_type=etype,
|
|
442
|
+
start_char=0, end_char=len(name),
|
|
443
|
+
confidence=conf,
|
|
444
|
+
context_window=name)
|
|
445
|
+
res = await self._resolver.resolve(cand, etype)
|
|
446
|
+
action = getattr(res.action, "value", str(res.action))
|
|
447
|
+
matched = res.matched_entity_id
|
|
448
|
+
except Exception:
|
|
449
|
+
pass
|
|
450
|
+
if action == "merge" and matched:
|
|
451
|
+
report["merged"].append({"name": name, "into": matched})
|
|
452
|
+
if self._may_write(mode, "alias_merge", report):
|
|
453
|
+
try:
|
|
454
|
+
await self._store.add_entity_alias(matched, name)
|
|
455
|
+
report["writes"] = report.get("writes", 0) + 1
|
|
456
|
+
except Exception:
|
|
457
|
+
pass
|
|
458
|
+
self._journal_write(f"merged alias {name!r} into {matched}",
|
|
459
|
+
conf, matched)
|
|
460
|
+
return matched
|
|
461
|
+
if action == "propose":
|
|
462
|
+
report["skipped"] += 1
|
|
463
|
+
return matched
|
|
464
|
+
report["created"].append({"name": name, "type": etype,
|
|
465
|
+
"confidence": round(conf, 2)})
|
|
466
|
+
if not self._may_write(mode, "entity_upsert", report):
|
|
467
|
+
return None
|
|
468
|
+
try:
|
|
469
|
+
from apsimo.world_model.entities import ENTITY_CLASS_MAP, BaseEntity
|
|
470
|
+
from apsimo.world_model.sqlite.backend import _generate_id
|
|
471
|
+
cls = ENTITY_CLASS_MAP.get(etype, BaseEntity)
|
|
472
|
+
ent = cls(id=_generate_id("we"), name=name, entity_type=etype,
|
|
473
|
+
confidence=conf)
|
|
474
|
+
await self._store.upsert_entity(ent)
|
|
475
|
+
report["writes"] = report.get("writes", 0) + 1
|
|
476
|
+
self._journal_write(f"created {etype} entity {name!r}", conf,
|
|
477
|
+
ent.id)
|
|
478
|
+
return ent.id
|
|
479
|
+
except Exception as exc:
|
|
480
|
+
logger.debug("llm-extract upsert failed for %r: %s", name, exc)
|
|
481
|
+
return None
|
|
482
|
+
|
|
483
|
+
async def _upsert_rel(self, src_id: str, rel: str, tgt_id: str,
|
|
484
|
+
conf: float, mode: str,
|
|
485
|
+
report: Dict[str, Any]) -> None:
|
|
486
|
+
key = (src_id, rel, tgt_id)
|
|
487
|
+
if key in self._seen_rels:
|
|
488
|
+
return
|
|
489
|
+
self._seen_rels.add(key)
|
|
490
|
+
report["relationships"].append(
|
|
491
|
+
{"source": src_id, "rel": rel, "target": tgt_id})
|
|
492
|
+
if mode not in ("live", "supervised"):
|
|
493
|
+
return
|
|
494
|
+
try:
|
|
495
|
+
# Repeated batches do not create another edge or improve its
|
|
496
|
+
# confidence. Qualified observations own new source evidence.
|
|
497
|
+
try:
|
|
498
|
+
existing = await self._store.query_relationships(
|
|
499
|
+
source_id=src_id, target_id=tgt_id,
|
|
500
|
+
relationship_type=rel, min_confidence=0.0, limit=1)
|
|
501
|
+
except Exception:
|
|
502
|
+
existing = []
|
|
503
|
+
if existing:
|
|
504
|
+
return
|
|
505
|
+
# Creation remains an explicit live-mode operation.
|
|
506
|
+
if not self._may_write(mode, "edge_create", report):
|
|
507
|
+
return
|
|
508
|
+
from apsimo.world_model.relationships import WorldRelationship
|
|
509
|
+
await self._store.upsert_relationship(WorldRelationship(
|
|
510
|
+
id="", source_id=src_id, target_id=tgt_id,
|
|
511
|
+
relationship_type=rel, confidence=min(0.7, conf)))
|
|
512
|
+
report["writes"] = report.get("writes", 0) + 1
|
|
513
|
+
self._journal_write(f"linked {src_id} -{rel}-> {tgt_id}", conf,
|
|
514
|
+
src_id)
|
|
515
|
+
except Exception:
|
|
516
|
+
logger.debug("llm-extract relationship upsert failed",
|
|
517
|
+
exc_info=True)
|
|
518
|
+
|
|
519
|
+
async def _upsert_causal(self, src_id: str, rel: str, tgt_id: str,
|
|
520
|
+
evidence: str, conf: float, mode: str,
|
|
521
|
+
report: Dict[str, Any]) -> None:
|
|
522
|
+
"""Retain a reported causal hypothesis without repeat-confidence boosts.
|
|
523
|
+
|
|
524
|
+
Repeat extraction does not refresh its support clock. Shadow writes no
|
|
525
|
+
graph state; independent outcomes belong to the forecast lifecycle.
|
|
526
|
+
"""
|
|
527
|
+
key = (src_id, rel, tgt_id)
|
|
528
|
+
if key in self._seen_rels:
|
|
529
|
+
return
|
|
530
|
+
self._seen_rels.add(key)
|
|
531
|
+
report["causal"].append({"source": src_id, "rel": rel,
|
|
532
|
+
"target": tgt_id,
|
|
533
|
+
"evidence": evidence[:200],
|
|
534
|
+
"confidence": round(conf, 2)})
|
|
535
|
+
if mode != "live":
|
|
536
|
+
return
|
|
537
|
+
try:
|
|
538
|
+
try:
|
|
539
|
+
existing = await self._store.query_relationships(
|
|
540
|
+
source_id=src_id, target_id=tgt_id,
|
|
541
|
+
relationship_type=rel, min_confidence=0.0, limit=1)
|
|
542
|
+
except Exception:
|
|
543
|
+
existing = []
|
|
544
|
+
from datetime import datetime, timezone
|
|
545
|
+
now_iso = datetime.now(timezone.utc).isoformat()
|
|
546
|
+
if existing:
|
|
547
|
+
report.setdefault("causal_repeated", []).append(existing[0].id)
|
|
548
|
+
return
|
|
549
|
+
from apsimo.world_model.relationships import WorldRelationship
|
|
550
|
+
create_conf = min(_CAUSAL_CREATE_CEILING, conf)
|
|
551
|
+
created = await self._store.upsert_relationship(WorldRelationship(
|
|
552
|
+
id="", source_id=src_id, target_id=tgt_id,
|
|
553
|
+
relationship_type=rel, confidence=create_conf,
|
|
554
|
+
properties={"evidence": evidence[:300],
|
|
555
|
+
"extraction": "llm_causal",
|
|
556
|
+
"last_support_at": now_iso}))
|
|
557
|
+
self._journal_write(
|
|
558
|
+
f"causal {src_id} -{rel}-> {tgt_id}", create_conf, src_id)
|
|
559
|
+
except Exception:
|
|
560
|
+
logger.debug("llm-extract causal upsert failed", exc_info=True)
|
|
561
|
+
|
|
562
|
+
def _journal_write(self, description: str, conf: float, ref: str) -> None:
|
|
563
|
+
if self._journal is None:
|
|
564
|
+
return
|
|
565
|
+
try:
|
|
566
|
+
self._journal.record(
|
|
567
|
+
"world_model", description,
|
|
568
|
+
reasoning="LLM extraction from recent conversation memories",
|
|
569
|
+
confidence=conf, reversibility="reversible",
|
|
570
|
+
decision="acted", ref=str(ref))
|
|
571
|
+
except Exception:
|
|
572
|
+
pass
|