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,320 @@
|
|
|
1
|
+
"""Task Planner — break complex requests into subtasks.
|
|
2
|
+
|
|
3
|
+
Capabilities:
|
|
4
|
+
- Dependency detection
|
|
5
|
+
- Parallel task identification
|
|
6
|
+
- Resource estimation
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from enum import Enum
|
|
13
|
+
from typing import Any, Dict, List, Optional, Set
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
# Patterns for identifying independently-executable task types
|
|
20
|
+
_INDEPENDENT_TASK_PATTERNS = [
|
|
21
|
+
re.compile(r'\b(research|look up|find out|search for)\b'),
|
|
22
|
+
re.compile(r'\b(write|draft|compose|create)\b'),
|
|
23
|
+
re.compile(r'\b(schedule|book|reserve|arrange)\b'),
|
|
24
|
+
re.compile(r'\b(notify|email|message|send)\b'),
|
|
25
|
+
re.compile(r'\b(download|fetch|retrieve)\b'),
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
# Patterns indicating an explicit sequential dependency between tasks
|
|
29
|
+
_DEPENDENCY_PATTERNS = [
|
|
30
|
+
re.compile(r'\b(then|after|once|following|based on|using the|with the)\b'),
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
# Keywords indicating sequential ordering (creates dependencies)
|
|
34
|
+
_SEQUENTIAL_PATTERN = re.compile(
|
|
35
|
+
r"\s+(?:then|next|finally|after that|subsequently|afterward|before)\s+",
|
|
36
|
+
re.IGNORECASE,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Keywords indicating parallel tasks (no dependencies)
|
|
40
|
+
_PARALLEL_PATTERN = re.compile(
|
|
41
|
+
r"\s+(?:and also|also|simultaneously|at the same time|in parallel)\s+",
|
|
42
|
+
re.IGNORECASE,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Keywords that suggest higher effort
|
|
46
|
+
_HIGH_EFFORT_WORDS = frozenset(
|
|
47
|
+
{"research", "analyze", "analyse", "comprehensive", "detailed",
|
|
48
|
+
"thorough", "complex", "deep", "investigate", "audit"}
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Keywords that suggest lower effort
|
|
52
|
+
_LOW_EFFORT_WORDS = frozenset(
|
|
53
|
+
{"quick", "simple", "brief", "short", "fast", "small", "minor",
|
|
54
|
+
"trivial", "basic", "easy"}
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Keywords that elevate priority
|
|
58
|
+
_CRITICAL_WORDS = frozenset(
|
|
59
|
+
{"critical", "urgent", "immediately", "asap", "emergency", "blocker"}
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
_HIGH_PRIO_WORDS = frozenset(
|
|
63
|
+
{"important", "priority", "soon", "required", "must"}
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class TaskPriority(str, Enum):
|
|
68
|
+
"""Priority levels for subtasks."""
|
|
69
|
+
|
|
70
|
+
CRITICAL = "critical"
|
|
71
|
+
HIGH = "high"
|
|
72
|
+
MEDIUM = "medium"
|
|
73
|
+
LOW = "low"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@dataclass
|
|
77
|
+
class SubTask:
|
|
78
|
+
"""A subtask in a decomposition.
|
|
79
|
+
|
|
80
|
+
Attributes:
|
|
81
|
+
id: Unique subtask identifier
|
|
82
|
+
description: Human-readable description
|
|
83
|
+
priority: Execution priority
|
|
84
|
+
dependencies: IDs of subtasks that must complete first
|
|
85
|
+
estimated_effort: Relative effort units (1.0 = baseline)
|
|
86
|
+
assigned_to: Optional node ID for mesh distribution
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
id: str
|
|
90
|
+
description: str
|
|
91
|
+
priority: TaskPriority = TaskPriority.MEDIUM
|
|
92
|
+
dependencies: List[str] = field(default_factory=list)
|
|
93
|
+
estimated_effort: float = 1.0
|
|
94
|
+
assigned_to: Optional[str] = None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@dataclass
|
|
98
|
+
class TaskPlan:
|
|
99
|
+
"""A complete task decomposition.
|
|
100
|
+
|
|
101
|
+
Attributes:
|
|
102
|
+
id: Unique plan identifier
|
|
103
|
+
description: The original request
|
|
104
|
+
subtasks: Ordered list of subtasks
|
|
105
|
+
parallel_groups: Groups of subtask IDs that can execute concurrently
|
|
106
|
+
total_effort: Sum of all subtask efforts
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
id: str
|
|
110
|
+
description: str
|
|
111
|
+
subtasks: List[SubTask] = field(default_factory=list)
|
|
112
|
+
parallel_groups: List[List[str]] = field(default_factory=list)
|
|
113
|
+
total_effort: float = 0.0
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class TaskPlanner:
|
|
117
|
+
"""Decompose complex requests into executable plans.
|
|
118
|
+
|
|
119
|
+
Takes a natural-language request, breaks it into subtasks,
|
|
120
|
+
identifies dependencies, and groups parallelizable work.
|
|
121
|
+
|
|
122
|
+
Decomposition rules:
|
|
123
|
+
- Sequential keywords ("then", "next", "finally"…) create ordered dependencies.
|
|
124
|
+
- Parallel keywords ("and also", "simultaneously"…) create independent tasks.
|
|
125
|
+
- Plain "and" splits into parallel tasks when no sequential markers are present.
|
|
126
|
+
- A single-sentence request creates one subtask.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
graph_client: Colony graph client for context retrieval
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
def __init__(self, graph_client: Any) -> None:
|
|
133
|
+
self.graph = graph_client
|
|
134
|
+
|
|
135
|
+
async def plan(self, request: str, context: Optional[Dict[str, Any]] = None) -> TaskPlan:
|
|
136
|
+
"""Create execution plan for a request.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
request: Natural-language task description
|
|
140
|
+
context: Optional context for better decomposition
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
TaskPlan with subtasks and parallel groups
|
|
144
|
+
"""
|
|
145
|
+
subtasks = await self._decompose(request)
|
|
146
|
+
|
|
147
|
+
await self._identify_dependencies(subtasks)
|
|
148
|
+
|
|
149
|
+
parallel_groups = self._find_parallel_groups(subtasks)
|
|
150
|
+
|
|
151
|
+
total_effort = sum(s.estimated_effort for s in subtasks)
|
|
152
|
+
|
|
153
|
+
plan = TaskPlan(
|
|
154
|
+
id=f"plan-{hash(request) % 10000:04d}",
|
|
155
|
+
description=request,
|
|
156
|
+
subtasks=subtasks,
|
|
157
|
+
parallel_groups=parallel_groups,
|
|
158
|
+
total_effort=total_effort,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
logger.debug(
|
|
162
|
+
"Created plan %s: %d subtasks, %d parallel groups, effort=%.1f",
|
|
163
|
+
plan.id,
|
|
164
|
+
len(subtasks),
|
|
165
|
+
len(parallel_groups),
|
|
166
|
+
total_effort,
|
|
167
|
+
)
|
|
168
|
+
return plan
|
|
169
|
+
|
|
170
|
+
async def _decompose(self, request: str) -> List[SubTask]:
|
|
171
|
+
"""Decompose request into subtasks using keyword rules.
|
|
172
|
+
|
|
173
|
+
Sequential markers create ordered dependencies; parallel markers
|
|
174
|
+
and plain conjunctions create independent groups.
|
|
175
|
+
"""
|
|
176
|
+
# Check for sequential ordering markers first
|
|
177
|
+
seq_parts = _SEQUENTIAL_PATTERN.split(request)
|
|
178
|
+
if len(seq_parts) > 1:
|
|
179
|
+
return self._build_subtasks(seq_parts, sequential=True)
|
|
180
|
+
|
|
181
|
+
# Check for explicit parallel markers
|
|
182
|
+
par_parts = _PARALLEL_PATTERN.split(request)
|
|
183
|
+
if len(par_parts) > 1:
|
|
184
|
+
return self._build_subtasks(par_parts, sequential=False)
|
|
185
|
+
|
|
186
|
+
# Fall back to splitting on plain "and" (parallel by default)
|
|
187
|
+
and_parts = re.split(r"\s+and\s+", request, flags=re.IGNORECASE)
|
|
188
|
+
if len(and_parts) > 1:
|
|
189
|
+
return self._build_subtasks(and_parts, sequential=False)
|
|
190
|
+
|
|
191
|
+
# Single task
|
|
192
|
+
return [self._make_subtask("subtask-1", request.strip() or request)]
|
|
193
|
+
|
|
194
|
+
def _build_subtasks(self, parts: List[str], *, sequential: bool) -> List[SubTask]:
|
|
195
|
+
"""Build subtask list from text parts.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
parts: Text fragments representing individual tasks
|
|
199
|
+
sequential: If True, each task depends on the previous one
|
|
200
|
+
"""
|
|
201
|
+
subtasks: List[SubTask] = []
|
|
202
|
+
for i, part in enumerate(parts):
|
|
203
|
+
part = part.strip()
|
|
204
|
+
if not part:
|
|
205
|
+
continue
|
|
206
|
+
st = self._make_subtask(f"subtask-{len(subtasks) + 1}", part)
|
|
207
|
+
if sequential and subtasks:
|
|
208
|
+
st.dependencies.append(subtasks[-1].id)
|
|
209
|
+
subtasks.append(st)
|
|
210
|
+
return subtasks or [self._make_subtask("subtask-1", "Analyse request")]
|
|
211
|
+
|
|
212
|
+
def _make_subtask(self, task_id: str, description: str) -> SubTask:
|
|
213
|
+
"""Construct a SubTask, inferring priority and effort from keywords."""
|
|
214
|
+
lower = description.lower()
|
|
215
|
+
|
|
216
|
+
# Priority
|
|
217
|
+
if any(w in lower for w in _CRITICAL_WORDS):
|
|
218
|
+
priority = TaskPriority.CRITICAL
|
|
219
|
+
elif any(w in lower for w in _HIGH_PRIO_WORDS):
|
|
220
|
+
priority = TaskPriority.HIGH
|
|
221
|
+
else:
|
|
222
|
+
priority = TaskPriority.MEDIUM
|
|
223
|
+
|
|
224
|
+
# Effort
|
|
225
|
+
if any(w in lower for w in _HIGH_EFFORT_WORDS):
|
|
226
|
+
effort = 2.0
|
|
227
|
+
elif any(w in lower for w in _LOW_EFFORT_WORDS):
|
|
228
|
+
effort = 0.5
|
|
229
|
+
else:
|
|
230
|
+
effort = 1.0
|
|
231
|
+
|
|
232
|
+
return SubTask(
|
|
233
|
+
id=task_id,
|
|
234
|
+
description=description,
|
|
235
|
+
priority=priority,
|
|
236
|
+
estimated_effort=effort,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
async def _identify_dependencies(self, subtasks: List[SubTask]) -> None:
|
|
240
|
+
"""Heuristic dependency analysis between subtasks.
|
|
241
|
+
|
|
242
|
+
``_decompose`` creates a linear dependency chain by default.
|
|
243
|
+
This method clears dependencies between pairs of tasks whose
|
|
244
|
+
descriptions suggest they are genuinely independent — i.e. no
|
|
245
|
+
explicit ordering language and both match independence patterns.
|
|
246
|
+
|
|
247
|
+
Sequential tasks already have dependencies set by ``_decompose``.
|
|
248
|
+
An LLM-assisted Phase 2 can be wired in future when a router is
|
|
249
|
+
available.
|
|
250
|
+
"""
|
|
251
|
+
if len(subtasks) < 2:
|
|
252
|
+
return
|
|
253
|
+
|
|
254
|
+
for i, task in enumerate(subtasks):
|
|
255
|
+
if i == 0:
|
|
256
|
+
continue
|
|
257
|
+
|
|
258
|
+
desc_lower = task.description.lower()
|
|
259
|
+
has_explicit_dep = any(p.search(desc_lower) for p in _DEPENDENCY_PATTERNS)
|
|
260
|
+
if has_explicit_dep:
|
|
261
|
+
continue # Keep the sequential dependency
|
|
262
|
+
|
|
263
|
+
is_independent_type = any(p.search(desc_lower) for p in _INDEPENDENT_TASK_PATTERNS)
|
|
264
|
+
if not is_independent_type:
|
|
265
|
+
continue
|
|
266
|
+
|
|
267
|
+
prev = subtasks[i - 1]
|
|
268
|
+
prev_lower = prev.description.lower()
|
|
269
|
+
prev_is_independent = any(p.search(prev_lower) for p in _INDEPENDENT_TASK_PATTERNS)
|
|
270
|
+
if prev_is_independent:
|
|
271
|
+
# Both tasks are independently typed — allow parallel execution
|
|
272
|
+
task.dependencies = [
|
|
273
|
+
d for d in task.dependencies if d != prev.id
|
|
274
|
+
]
|
|
275
|
+
logger.debug(
|
|
276
|
+
"TaskPlanner: cleared dependency %s → %s "
|
|
277
|
+
"(both tasks appear independent)",
|
|
278
|
+
prev.id,
|
|
279
|
+
task.id,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
def _find_parallel_groups(self, subtasks: List[SubTask]) -> List[List[str]]:
|
|
283
|
+
"""Find subtasks that can run in parallel using topological levels.
|
|
284
|
+
|
|
285
|
+
Performs a level-order topological sort: tasks at level 0 have no
|
|
286
|
+
dependencies, tasks at level 1 depend only on level-0 tasks, etc.
|
|
287
|
+
Tasks within the same level can execute concurrently.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
subtasks: All subtasks in the plan
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
List of groups (each group is a list of subtask IDs that can
|
|
294
|
+
run concurrently).
|
|
295
|
+
"""
|
|
296
|
+
if not subtasks:
|
|
297
|
+
return []
|
|
298
|
+
|
|
299
|
+
remaining: Dict[str, SubTask] = {s.id: s for s in subtasks}
|
|
300
|
+
completed: Set[str] = set()
|
|
301
|
+
groups: List[List[str]] = []
|
|
302
|
+
|
|
303
|
+
while remaining:
|
|
304
|
+
ready = [
|
|
305
|
+
sid
|
|
306
|
+
for sid, s in remaining.items()
|
|
307
|
+
if all(dep in completed for dep in s.dependencies)
|
|
308
|
+
]
|
|
309
|
+
|
|
310
|
+
if not ready:
|
|
311
|
+
# Cycle or unresolvable — dump remainder into one group
|
|
312
|
+
groups.append(list(remaining.keys()))
|
|
313
|
+
break
|
|
314
|
+
|
|
315
|
+
groups.append(ready)
|
|
316
|
+
for sid in ready:
|
|
317
|
+
completed.add(sid)
|
|
318
|
+
del remaining[sid]
|
|
319
|
+
|
|
320
|
+
return groups
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"""Tool Learner — learn user's preferred tools for different tasks.
|
|
2
|
+
|
|
3
|
+
Tracks:
|
|
4
|
+
- Which tools are used for which task types
|
|
5
|
+
- Success rates per tool
|
|
6
|
+
- User satisfaction signals (explicit + implicit)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class ToolUsage:
|
|
20
|
+
"""Record of a tool being used for a task.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
tool_name: Name of the tool used
|
|
24
|
+
task_type: Category of task the tool was applied to
|
|
25
|
+
success: Whether the tool usage succeeded
|
|
26
|
+
user_feedback: Optional explicit feedback ("helpful", "not_helpful", None)
|
|
27
|
+
timestamp: When the usage occurred
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
tool_name: str
|
|
31
|
+
task_type: str
|
|
32
|
+
success: bool
|
|
33
|
+
user_feedback: Optional[str] = None
|
|
34
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class ToolPreference:
|
|
39
|
+
"""Learned preference for a tool on a task type.
|
|
40
|
+
|
|
41
|
+
Attributes:
|
|
42
|
+
tool_name: Name of the preferred tool
|
|
43
|
+
task_type: Task category this preference applies to
|
|
44
|
+
success_rate: Running success rate (0-1)
|
|
45
|
+
usage_count: Total number of times this tool was used for this task type
|
|
46
|
+
last_used: Most recent usage timestamp
|
|
47
|
+
user_rating: Recency-weighted user satisfaction score (0-1)
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
tool_name: str
|
|
51
|
+
task_type: str
|
|
52
|
+
success_rate: float
|
|
53
|
+
usage_count: int
|
|
54
|
+
last_used: datetime
|
|
55
|
+
user_rating: float
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class ToolLearner:
|
|
59
|
+
"""Learn and apply tool preferences from usage patterns.
|
|
60
|
+
|
|
61
|
+
Maintains in-memory preference records per task type, updated on
|
|
62
|
+
each recorded usage. Preferred tool selection blends success rate
|
|
63
|
+
(60%) with user rating (40%).
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
graph_client: Colony graph client for persistent storage
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
def __init__(self, graph_client: Any) -> None:
|
|
70
|
+
self.graph = graph_client
|
|
71
|
+
self._preferences: Dict[str, List[ToolPreference]] = {}
|
|
72
|
+
|
|
73
|
+
async def record_usage(self, usage: ToolUsage) -> None:
|
|
74
|
+
"""Record a tool usage event and update preferences.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
usage: The tool usage to record
|
|
78
|
+
"""
|
|
79
|
+
key = usage.task_type
|
|
80
|
+
if key not in self._preferences:
|
|
81
|
+
self._preferences[key] = []
|
|
82
|
+
|
|
83
|
+
# Find or create preference record
|
|
84
|
+
pref = next(
|
|
85
|
+
(p for p in self._preferences[key] if p.tool_name == usage.tool_name),
|
|
86
|
+
None,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
if pref:
|
|
90
|
+
old_count = pref.usage_count
|
|
91
|
+
pref.usage_count += 1
|
|
92
|
+
pref.success_rate = (
|
|
93
|
+
(pref.success_rate * old_count + (1.0 if usage.success else 0.0))
|
|
94
|
+
/ pref.usage_count
|
|
95
|
+
)
|
|
96
|
+
pref.last_used = usage.timestamp
|
|
97
|
+
else:
|
|
98
|
+
self._preferences[key].append(
|
|
99
|
+
ToolPreference(
|
|
100
|
+
tool_name=usage.tool_name,
|
|
101
|
+
task_type=usage.task_type,
|
|
102
|
+
success_rate=1.0 if usage.success else 0.0,
|
|
103
|
+
usage_count=1,
|
|
104
|
+
last_used=usage.timestamp,
|
|
105
|
+
user_rating=0.5,
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
logger.debug(
|
|
110
|
+
"Recorded tool usage: %s for %s (success=%s)",
|
|
111
|
+
usage.tool_name,
|
|
112
|
+
usage.task_type,
|
|
113
|
+
usage.success,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
async def get_preferred_tool(self, task_type: str) -> Optional[str]:
|
|
117
|
+
"""Get the preferred tool for a task type.
|
|
118
|
+
|
|
119
|
+
Selection blends success rate (60%) with user rating (40%).
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
task_type: The task category to find a tool for
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Tool name or None if no preferences exist
|
|
126
|
+
"""
|
|
127
|
+
if task_type not in self._preferences:
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
prefs = self._preferences[task_type]
|
|
131
|
+
if not prefs:
|
|
132
|
+
return None
|
|
133
|
+
|
|
134
|
+
sorted_prefs = sorted(
|
|
135
|
+
prefs,
|
|
136
|
+
key=lambda p: (p.success_rate * 0.6 + p.user_rating * 0.4),
|
|
137
|
+
reverse=True,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
return sorted_prefs[0].tool_name
|
|
141
|
+
|
|
142
|
+
async def get_all_preferences(self, task_type: str) -> List[ToolPreference]:
|
|
143
|
+
"""Get all tool preferences for a task type, sorted by score.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
task_type: The task category to query
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Sorted list of preferences (best first)
|
|
150
|
+
"""
|
|
151
|
+
prefs = self._preferences.get(task_type, [])
|
|
152
|
+
return sorted(
|
|
153
|
+
prefs,
|
|
154
|
+
key=lambda p: (p.success_rate * 0.6 + p.user_rating * 0.4),
|
|
155
|
+
reverse=True,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
async def update_user_rating(
|
|
159
|
+
self,
|
|
160
|
+
tool_name: str,
|
|
161
|
+
task_type: str,
|
|
162
|
+
rating: float,
|
|
163
|
+
) -> None:
|
|
164
|
+
"""Update the user satisfaction rating for a tool on a task type.
|
|
165
|
+
|
|
166
|
+
Uses an exponential moving average (alpha=0.3) so recent feedback
|
|
167
|
+
is weighted more heavily without fully discarding history.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
tool_name: Name of the tool to rate
|
|
171
|
+
task_type: Task category this rating applies to
|
|
172
|
+
rating: New rating value (0-1)
|
|
173
|
+
"""
|
|
174
|
+
rating = max(0.0, min(1.0, rating))
|
|
175
|
+
prefs = self._preferences.get(task_type, [])
|
|
176
|
+
pref = next((p for p in prefs if p.tool_name == tool_name), None)
|
|
177
|
+
if pref is None:
|
|
178
|
+
logger.warning("No preference record for %s on %s", tool_name, task_type)
|
|
179
|
+
return
|
|
180
|
+
alpha = 0.3
|
|
181
|
+
pref.user_rating = alpha * rating + (1 - alpha) * pref.user_rating
|
|
182
|
+
logger.debug(
|
|
183
|
+
"Updated user rating for %s on %s: %.2f",
|
|
184
|
+
tool_name,
|
|
185
|
+
task_type,
|
|
186
|
+
pref.user_rating,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
async def get_usage_stats(self, task_type: str) -> Dict[str, Any]:
|
|
190
|
+
"""Return summary statistics for all tools used on a task type.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
task_type: The task category to summarise
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
Dict with 'total_tools', 'total_usages', 'best_tool', and per-tool stats.
|
|
197
|
+
"""
|
|
198
|
+
prefs = self._preferences.get(task_type, [])
|
|
199
|
+
if not prefs:
|
|
200
|
+
return {"total_tools": 0, "total_usages": 0, "best_tool": None, "tools": []}
|
|
201
|
+
|
|
202
|
+
best = await self.get_preferred_tool(task_type)
|
|
203
|
+
return {
|
|
204
|
+
"total_tools": len(prefs),
|
|
205
|
+
"total_usages": sum(p.usage_count for p in prefs),
|
|
206
|
+
"best_tool": best,
|
|
207
|
+
"tools": [
|
|
208
|
+
{
|
|
209
|
+
"name": p.tool_name,
|
|
210
|
+
"usage_count": p.usage_count,
|
|
211
|
+
"success_rate": p.success_rate,
|
|
212
|
+
"user_rating": p.user_rating,
|
|
213
|
+
"score": p.success_rate * 0.6 + p.user_rating * 0.4,
|
|
214
|
+
}
|
|
215
|
+
for p in prefs
|
|
216
|
+
],
|
|
217
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Colony Graph Memory System.
|
|
2
|
+
|
|
3
|
+
Public API:
|
|
4
|
+
- ``ColonyGraph`` — async Neo4j client for memory storage & recall
|
|
5
|
+
- ``GraphConfig`` — connection settings dataclass
|
|
6
|
+
- ``run_migrations`` — idempotent schema setup
|
|
7
|
+
- Schema types (``Owner``, ``Person``, ``Memory``, ``Entity``, etc.)
|
|
8
|
+
- ``EdgeType`` / ``TrustTier`` enums
|
|
9
|
+
- Cypher query constants in ``queries``
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .client import ColonyGraph, GraphConfig
|
|
13
|
+
from .migrations import run_migrations, SCHEMA_V1
|
|
14
|
+
from .queries import (
|
|
15
|
+
COMPUTE_RELATIONSHIP_SCORES,
|
|
16
|
+
DECAY_ALL,
|
|
17
|
+
ENSURE_OWNER,
|
|
18
|
+
FIND_NEGLECTED_RELATIONSHIPS,
|
|
19
|
+
FIND_SHARED_ENTITIES,
|
|
20
|
+
GET_PERSON_WITH_SIGNALS,
|
|
21
|
+
LINK_PERSON_TO_OWNER,
|
|
22
|
+
PRUNE_WEAK_MEMORIES,
|
|
23
|
+
RECALL_BY_ENTITY,
|
|
24
|
+
RECALL_BY_TYPE,
|
|
25
|
+
RECORD_SCORE_CHANGE,
|
|
26
|
+
STORE_MEMORY,
|
|
27
|
+
STORE_SIGNAL,
|
|
28
|
+
TOUCH_MEMORY,
|
|
29
|
+
TRAVERSE_MEMORY_CONNECTIONS,
|
|
30
|
+
)
|
|
31
|
+
from .schema import (
|
|
32
|
+
Context,
|
|
33
|
+
EdgeType,
|
|
34
|
+
Entity,
|
|
35
|
+
Memory,
|
|
36
|
+
Owner,
|
|
37
|
+
Person,
|
|
38
|
+
Prediction,
|
|
39
|
+
ScoreEvent,
|
|
40
|
+
Signal,
|
|
41
|
+
TrustTier,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
# Client
|
|
46
|
+
"ColonyGraph",
|
|
47
|
+
"GraphConfig",
|
|
48
|
+
# Migrations
|
|
49
|
+
"run_migrations",
|
|
50
|
+
"SCHEMA_V1",
|
|
51
|
+
# Schema — nodes
|
|
52
|
+
"Owner",
|
|
53
|
+
"Person",
|
|
54
|
+
"Memory",
|
|
55
|
+
"Entity",
|
|
56
|
+
"Signal",
|
|
57
|
+
"Context",
|
|
58
|
+
"ScoreEvent",
|
|
59
|
+
"Prediction",
|
|
60
|
+
# Schema — enums
|
|
61
|
+
"EdgeType",
|
|
62
|
+
"TrustTier",
|
|
63
|
+
# Query constants
|
|
64
|
+
"STORE_MEMORY",
|
|
65
|
+
"RECALL_BY_ENTITY",
|
|
66
|
+
"RECALL_BY_TYPE",
|
|
67
|
+
"DECAY_ALL",
|
|
68
|
+
"PRUNE_WEAK_MEMORIES",
|
|
69
|
+
"TOUCH_MEMORY",
|
|
70
|
+
"GET_PERSON_WITH_SIGNALS",
|
|
71
|
+
"STORE_SIGNAL",
|
|
72
|
+
"COMPUTE_RELATIONSHIP_SCORES",
|
|
73
|
+
"RECORD_SCORE_CHANGE",
|
|
74
|
+
"FIND_NEGLECTED_RELATIONSHIPS",
|
|
75
|
+
"TRAVERSE_MEMORY_CONNECTIONS",
|
|
76
|
+
"FIND_SHARED_ENTITIES",
|
|
77
|
+
"ENSURE_OWNER",
|
|
78
|
+
"LINK_PERSON_TO_OWNER",
|
|
79
|
+
]
|