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.
Files changed (614) hide show
  1. apsimo/__init__.py +38 -0
  2. apsimo/__main__.py +6 -0
  3. apsimo/agent/__init__.py +6 -0
  4. apsimo/agent/client.py +276 -0
  5. apsimo/agent/models.py +46 -0
  6. apsimo/agents/__init__.py +20 -0
  7. apsimo/agents/models.py +264 -0
  8. apsimo/agents/store.py +861 -0
  9. apsimo/agents/websocket.py +522 -0
  10. apsimo/api/__init__.py +1 -0
  11. apsimo/api/auth_telemetry.py +287 -0
  12. apsimo/api/authority.py +1203 -0
  13. apsimo/api/contact_grants.py +347 -0
  14. apsimo/api/middleware.py +483 -0
  15. apsimo/api/routers/__init__.py +1 -0
  16. apsimo/api/routers/commitment_work.py +265 -0
  17. apsimo/api/routers/context_gate.py +123 -0
  18. apsimo/api/routers/executions.py +140 -0
  19. apsimo/api/routers/followup_plans.py +147 -0
  20. apsimo/api/routers/governed_actions.py +162 -0
  21. apsimo/api/routers/host.py +14473 -0
  22. apsimo/api/routers/initiative_work.py +115 -0
  23. apsimo/api/routers/mining.py +104 -0
  24. apsimo/api/routers/observations.py +110 -0
  25. apsimo/api/routers/social_state.py +225 -0
  26. apsimo/api/routers/task_queue.py +2715 -0
  27. apsimo/api/routers/temporal_followups.py +251 -0
  28. apsimo/api/routers/transport.py +110 -0
  29. apsimo/api/routers/transport_ingress_api.py +240 -0
  30. apsimo/api/schemas/__init__.py +1 -0
  31. apsimo/api/schemas/host.py +1949 -0
  32. apsimo/autonomy/cli.py +110 -0
  33. apsimo/autonomy/condition_worker.py +437 -0
  34. apsimo/autonomy/config.py +424 -0
  35. apsimo/autonomy/loop.py +4316 -0
  36. apsimo/autonomy/registry.py +339 -0
  37. apsimo/autonomy/scheduler.py +1822 -0
  38. apsimo/autonomy/synthesis.py +449 -0
  39. apsimo/backup.py +962 -0
  40. apsimo/beliefs/__init__.py +23 -0
  41. apsimo/beliefs/contradictions.py +109 -0
  42. apsimo/beliefs/decay.py +61 -0
  43. apsimo/beliefs/engine.py +479 -0
  44. apsimo/beliefs/models.py +67 -0
  45. apsimo/beliefs/promotion.py +41 -0
  46. apsimo/beliefs/resolve.py +58 -0
  47. apsimo/beliefs/source_claims.py +690 -0
  48. apsimo/beliefs/source_projection.py +883 -0
  49. apsimo/beliefs/source_time.py +208 -0
  50. apsimo/beliefs/store.py +133 -0
  51. apsimo/briefings/aggregators.py +824 -0
  52. apsimo/briefings/composer.py +420 -0
  53. apsimo/briefings/config.py +55 -0
  54. apsimo/briefings/delivery.py +439 -0
  55. apsimo/briefings/engagement.py +97 -0
  56. apsimo/briefings/engine.py +274 -0
  57. apsimo/briefings/enhancer.py +99 -0
  58. apsimo/briefings/models.py +183 -0
  59. apsimo/briefings/scheduler.py +382 -0
  60. apsimo/briefings/store.py +435 -0
  61. apsimo/chain/__init__.py +48 -0
  62. apsimo/chain/block.py +100 -0
  63. apsimo/chain/cli.py +704 -0
  64. apsimo/chain/genesis.py +443 -0
  65. apsimo/chain/identity.py +416 -0
  66. apsimo/chain/keys.py +1025 -0
  67. apsimo/chain/local_keys.py +187 -0
  68. apsimo/chain/manager.py +290 -0
  69. apsimo/chain/node.py +163 -0
  70. apsimo/chain/plugin_transactions.py +371 -0
  71. apsimo/chain/protocol.py +220 -0
  72. apsimo/chain/state_machine.py +676 -0
  73. apsimo/chain/storage.py +503 -0
  74. apsimo/chain/transactions.py +250 -0
  75. apsimo/chain/validation.py +397 -0
  76. apsimo/channels/__init__.py +1 -0
  77. apsimo/channels/manifest.py +31 -0
  78. apsimo/channels/migrations/001_channels_schema.sql +12 -0
  79. apsimo/channels/phone_gateways.py +42 -0
  80. apsimo/channels/presence.py +188 -0
  81. apsimo/channels/router.py +235 -0
  82. apsimo/channels/store.py +231 -0
  83. apsimo/cli.py +2688 -0
  84. apsimo/cognition/__init__.py +11 -0
  85. apsimo/cognition/charter.py +398 -0
  86. apsimo/cognition/drive_governance.py +3530 -0
  87. apsimo/cognition/evidence_pipeline.py +1627 -0
  88. apsimo/cognition/external_events.py +932 -0
  89. apsimo/cognition/goal_spine.py +3488 -0
  90. apsimo/cognition/introspection.py +214 -0
  91. apsimo/cognition/prompt.py +150 -0
  92. apsimo/cognition/runtime.py +108 -0
  93. apsimo/cognition/trigger.py +154 -0
  94. apsimo/commitments/__init__.py +18 -0
  95. apsimo/commitments/local_work.py +355 -0
  96. apsimo/commitments/store.py +1052 -0
  97. apsimo/commitments/work.py +91 -0
  98. apsimo/compat.py +53 -0
  99. apsimo/compression/__init__.py +467 -0
  100. apsimo/connectors/__init__.py +21 -0
  101. apsimo/connectors/base.py +152 -0
  102. apsimo/connectors/caldav_calendar.py +125 -0
  103. apsimo/connectors/fs_documents.py +85 -0
  104. apsimo/connectors/imap_email.py +138 -0
  105. apsimo/connectors/manager.py +218 -0
  106. apsimo/connectors/webhook_pull.py +88 -0
  107. apsimo/contacts/__init__.py +33 -0
  108. apsimo/contacts/comms.py +357 -0
  109. apsimo/contacts/config.py +79 -0
  110. apsimo/contacts/exporters/__init__.py +1 -0
  111. apsimo/contacts/exporters/vcard.py +71 -0
  112. apsimo/contacts/identity_links.py +251 -0
  113. apsimo/contacts/importer.py +280 -0
  114. apsimo/contacts/importers/__init__.py +1 -0
  115. apsimo/contacts/importers/batch.py +43 -0
  116. apsimo/contacts/importers/macos_contacts.py +101 -0
  117. apsimo/contacts/migrations/001_contacts_schema.sql +141 -0
  118. apsimo/contacts/migrations/002_trust_scopes.sql +36 -0
  119. apsimo/contacts/migrations/003_open_gateway_enum.sql +32 -0
  120. apsimo/contacts/migrations/004_contact_provision_operations.sql +18 -0
  121. apsimo/contacts/migrations/005_identity_links.sql +27 -0
  122. apsimo/contacts/models.py +308 -0
  123. apsimo/contacts/scoring.py +16 -0
  124. apsimo/contacts/store.py +1623 -0
  125. apsimo/contacts/transport_ingress.py +252 -0
  126. apsimo/contacts/world_bridge.py +314 -0
  127. apsimo/contextgate/__init__.py +69 -0
  128. apsimo/contextgate/chunker.py +169 -0
  129. apsimo/contextgate/estimate.py +54 -0
  130. apsimo/contextgate/gate.py +313 -0
  131. apsimo/contextgate/retrieve.py +115 -0
  132. apsimo/delivery/__init__.py +16 -0
  133. apsimo/delivery/bridge.py +1260 -0
  134. apsimo/delivery/channels.py +526 -0
  135. apsimo/delivery/classification.py +50 -0
  136. apsimo/delivery/rate_limiter.py +268 -0
  137. apsimo/delivery/reachout_policy.py +206 -0
  138. apsimo/directed/__init__.py +22 -0
  139. apsimo/directed/audit.py +167 -0
  140. apsimo/directed/intake.py +95 -0
  141. apsimo/directed/models.py +191 -0
  142. apsimo/directed/service.py +509 -0
  143. apsimo/directives/__init__.py +25 -0
  144. apsimo/directives/evidence.py +87 -0
  145. apsimo/directives/extractor.py +188 -0
  146. apsimo/directives/guard.py +364 -0
  147. apsimo/directives/models.py +206 -0
  148. apsimo/directives/service.py +372 -0
  149. apsimo/directives/store.py +167 -0
  150. apsimo/doctor.py +2173 -0
  151. apsimo/environment.py +43 -0
  152. apsimo/events/__init__.py +33 -0
  153. apsimo/events/broadcaster.py +98 -0
  154. apsimo/events/bus.py +217 -0
  155. apsimo/events/journal.py +863 -0
  156. apsimo/events/stream.py +131 -0
  157. apsimo/events/types.py +150 -0
  158. apsimo/execution_results.py +357 -0
  159. apsimo/feedback/__init__.py +5 -0
  160. apsimo/feedback/store.py +76 -0
  161. apsimo/feeds/__init__.py +19 -0
  162. apsimo/feeds/cli.py +84 -0
  163. apsimo/feeds/engine.py +437 -0
  164. apsimo/feeds/example-feed.yaml +77 -0
  165. apsimo/feeds/hermes_cron.py +126 -0
  166. apsimo/feeds/manager.py +235 -0
  167. apsimo/feeds/spec.py +250 -0
  168. apsimo/feeds/template.py +202 -0
  169. apsimo/gate/__init__.py +18 -0
  170. apsimo/gate/audit.py +61 -0
  171. apsimo/gate/communication_policy.py +166 -0
  172. apsimo/gate/config.py +72 -0
  173. apsimo/gate/context_provenance.py +170 -0
  174. apsimo/gate/env_risk.py +226 -0
  175. apsimo/gate/guard_audit.py +353 -0
  176. apsimo/gate/layers/__init__.py +1 -0
  177. apsimo/gate/layers/base.py +15 -0
  178. apsimo/gate/layers/l1_recipient.py +66 -0
  179. apsimo/gate/layers/l2_pii.py +134 -0
  180. apsimo/gate/layers/l3_cross_context.py +50 -0
  181. apsimo/gate/layers/l4_trust_tier.py +78 -0
  182. apsimo/gate/layers/l5_injection.py +199 -0
  183. apsimo/gate/layers/l6_review.py +86 -0
  184. apsimo/gate/layers/l7_delay.py +100 -0
  185. apsimo/gate/layers/tom2_epistemic.py +185 -0
  186. apsimo/gate/models.py +64 -0
  187. apsimo/gate/pending_dispatch.py +5 -0
  188. apsimo/gate/pipeline.py +206 -0
  189. apsimo/gate/rejection.py +259 -0
  190. apsimo/gate/response_guard.py +700 -0
  191. apsimo/gate/rulesets/injection_v1.yaml +51 -0
  192. apsimo/gate/surface_policy.py +189 -0
  193. apsimo/gate/taint.py +226 -0
  194. apsimo/genesis.json +9 -0
  195. apsimo/goals/__init__.py +100 -0
  196. apsimo/goals/config.py +38 -0
  197. apsimo/goals/decomposer.py +421 -0
  198. apsimo/goals/engine.py +617 -0
  199. apsimo/goals/inference.py +354 -0
  200. apsimo/goals/models.py +302 -0
  201. apsimo/goals/priority.py +270 -0
  202. apsimo/goals/queue_bridge.py +149 -0
  203. apsimo/goals/replan.py +450 -0
  204. apsimo/goals/schema.sql +89 -0
  205. apsimo/goals/store.py +692 -0
  206. apsimo/governed_actions.py +1708 -0
  207. apsimo/harness_integration/__init__.py +45 -0
  208. apsimo/harness_integration/context.py +41 -0
  209. apsimo/harness_integration/skills.py +231 -0
  210. apsimo/identity/__init__.py +26 -0
  211. apsimo/identity/participants.py +181 -0
  212. apsimo/identity/resolver.py +329 -0
  213. apsimo/identity_bootstrap/__init__.py +5 -0
  214. apsimo/identity_bootstrap/builder.py +208 -0
  215. apsimo/identity_bootstrap/corpus.py +443 -0
  216. apsimo/identity_bootstrap/models.py +54 -0
  217. apsimo/identity_bootstrap/runner.py +353 -0
  218. apsimo/identity_bootstrap/seeders/__init__.py +25 -0
  219. apsimo/identity_bootstrap/seeders/briefings.py +109 -0
  220. apsimo/identity_bootstrap/seeders/chain.py +57 -0
  221. apsimo/identity_bootstrap/seeders/goals.py +128 -0
  222. apsimo/identity_bootstrap/seeders/memory.py +191 -0
  223. apsimo/identity_bootstrap/seeders/neo4j_cognition.py +79 -0
  224. apsimo/identity_bootstrap/seeders/relationship.py +152 -0
  225. apsimo/identity_bootstrap/seeders/sessions.py +67 -0
  226. apsimo/identity_bootstrap/seeders/skills.py +92 -0
  227. apsimo/identity_bootstrap/seeders/task_queue.py +72 -0
  228. apsimo/identity_bootstrap/seeders/world_model.py +143 -0
  229. apsimo/identity_bootstrap/self_query.py +92 -0
  230. apsimo/identity_bootstrap/self_reflection.py +155 -0
  231. apsimo/identity_bootstrap/skill.py +37 -0
  232. apsimo/identity_bootstrap/verifier.py +436 -0
  233. apsimo/initiatives/__init__.py +20 -0
  234. apsimo/initiatives/action_registry.py +454 -0
  235. apsimo/initiatives/approval_authority.py +2105 -0
  236. apsimo/initiatives/approval_policy.py +123 -0
  237. apsimo/initiatives/assignment.py +263 -0
  238. apsimo/initiatives/backup_evidence.py +100 -0
  239. apsimo/initiatives/context_freshness.py +103 -0
  240. apsimo/initiatives/models.py +318 -0
  241. apsimo/initiatives/native_work.py +270 -0
  242. apsimo/initiatives/standing_approvals.py +232 -0
  243. apsimo/initiatives/store.py +1081 -0
  244. apsimo/initiatives/temporal_followup.py +410 -0
  245. apsimo/intelligence/__init__.py +1 -0
  246. apsimo/intelligence/cognition/__init__.py +24 -0
  247. apsimo/intelligence/cognition/gap_detector.py +148 -0
  248. apsimo/intelligence/cognition/metalearner.py +547 -0
  249. apsimo/intelligence/cognition/metrics_collector.py +217 -0
  250. apsimo/intelligence/cognition/performance_index.py +299 -0
  251. apsimo/intelligence/cognition/registry.py +192 -0
  252. apsimo/intelligence/cognition/strategy_adjuster.py +222 -0
  253. apsimo/intelligence/cognition/types.py +16 -0
  254. apsimo/intelligence/components/__init__.py +66 -0
  255. apsimo/intelligence/components/anomaly_detector.py +413 -0
  256. apsimo/intelligence/components/initiative_engine.py +2643 -0
  257. apsimo/intelligence/components/preference_learner.py +521 -0
  258. apsimo/intelligence/components/research_orchestrator.py +358 -0
  259. apsimo/intelligence/components/self_directed_thinker.py +221 -0
  260. apsimo/intelligence/components/self_reflector.py +252 -0
  261. apsimo/intelligence/components/session_continuity.py +154 -0
  262. apsimo/intelligence/components/task_planner.py +320 -0
  263. apsimo/intelligence/components/tool_learner.py +217 -0
  264. apsimo/intelligence/graph/__init__.py +79 -0
  265. apsimo/intelligence/graph/client.py +2483 -0
  266. apsimo/intelligence/graph/consolidator.py +405 -0
  267. apsimo/intelligence/graph/distiller.py +312 -0
  268. apsimo/intelligence/graph/migrations.py +129 -0
  269. apsimo/intelligence/graph/queries.py +248 -0
  270. apsimo/intelligence/graph/recall.py +281 -0
  271. apsimo/intelligence/graph/reconciler.py +144 -0
  272. apsimo/intelligence/graph/schema.py +337 -0
  273. apsimo/intelligence/graph/selection.py +252 -0
  274. apsimo/intelligence/learning/__init__.py +17 -0
  275. apsimo/intelligence/learning/continuous_learner.py +245 -0
  276. apsimo/intelligence/learning/feedback_store.py +321 -0
  277. apsimo/intelligence/mind_model/__init__.py +1 -0
  278. apsimo/intelligence/mind_model/graph_baseline.py +136 -0
  279. apsimo/intelligence/mind_model/signal_collector.py +361 -0
  280. apsimo/intelligence/relationships/__init__.py +11 -0
  281. apsimo/intelligence/relationships/profiler.py +389 -0
  282. apsimo/intelligence/relationships/scorer.py +560 -0
  283. apsimo/intelligence/relationships/signal_floor.py +66 -0
  284. apsimo/intelligence/relationships/trust_tiers.py +300 -0
  285. apsimo/intelligence/synthesis/__init__.py +40 -0
  286. apsimo/intelligence/synthesis/connection_discoverer.py +379 -0
  287. apsimo/intelligence/synthesis/cross_domain_analyzer.py +287 -0
  288. apsimo/intelligence/synthesis/insight_deliverer.py +171 -0
  289. apsimo/intelligence/synthesis/insight_store.py +79 -0
  290. apsimo/intelligence/synthesis/insight_validator.py +183 -0
  291. apsimo/intelligence/synthesis/novelty_scorer.py +267 -0
  292. apsimo/intelligence/turn_middleware/__init__.py +15 -0
  293. apsimo/intelligence/turn_middleware/memory_sync.py +119 -0
  294. apsimo/mcp/__init__.py +41 -0
  295. apsimo/mcp/__main__.py +6 -0
  296. apsimo/mcp/config.py +287 -0
  297. apsimo/mcp/server.py +501 -0
  298. apsimo/migrations.py +187 -0
  299. apsimo/mining/__init__.py +27 -0
  300. apsimo/mining/corpus.py +239 -0
  301. apsimo/mining/escalations.py +289 -0
  302. apsimo/mining/models.py +169 -0
  303. apsimo/mining/store.py +210 -0
  304. apsimo/models/__init__.py +30 -0
  305. apsimo/models/memory.py +80 -0
  306. apsimo/models/mesh.py +72 -0
  307. apsimo/models/person.py +104 -0
  308. apsimo/models/signal.py +108 -0
  309. apsimo/observations/__init__.py +15 -0
  310. apsimo/observations/store.py +277 -0
  311. apsimo/patterns/__init__.py +6 -0
  312. apsimo/patterns/extract.py +187 -0
  313. apsimo/patterns/store.py +227 -0
  314. apsimo/persona/__init__.py +1 -0
  315. apsimo/persona/engine.py +611 -0
  316. apsimo/persona/manifest.py +140 -0
  317. apsimo/projects/__init__.py +28 -0
  318. apsimo/projects/engine.py +1681 -0
  319. apsimo/projects/event_outbox.py +188 -0
  320. apsimo/projects/models.py +216 -0
  321. apsimo/projects/planner.py +181 -0
  322. apsimo/projects/store.py +1446 -0
  323. apsimo/proposals/__init__.py +12 -0
  324. apsimo/proposals/engine.py +114 -0
  325. apsimo/proposals/models.py +207 -0
  326. apsimo/qualification/__init__.py +1 -0
  327. apsimo/qualification/cases.py +75 -0
  328. apsimo/qualification/cli.py +51 -0
  329. apsimo/qualification/memory_cases.py +209 -0
  330. apsimo/qualification/records.py +92 -0
  331. apsimo/qualification/report.py +87 -0
  332. apsimo/qualification/runner.py +311 -0
  333. apsimo/qualification/structured_cases.py +131 -0
  334. apsimo/reasoning/__init__.py +13 -0
  335. apsimo/reasoning/executor.py +506 -0
  336. apsimo/reasoning/loop.py +373 -0
  337. apsimo/reasoning/native_tools/__init__.py +16 -0
  338. apsimo/reasoning/native_tools/calculate.py +141 -0
  339. apsimo/reasoning/native_tools/file_ops.py +150 -0
  340. apsimo/reasoning/native_tools/web_search.py +49 -0
  341. apsimo/reasoning/tool_policy.py +182 -0
  342. apsimo/redact/__init__.py +176 -0
  343. apsimo/repos/__init__.py +5 -0
  344. apsimo/repos/mirrors.py +204 -0
  345. apsimo/research/__init__.py +41 -0
  346. apsimo/research/artifact.py +482 -0
  347. apsimo/research/gatherer.py +387 -0
  348. apsimo/research/pipeline.py +513 -0
  349. apsimo/research/search/__init__.py +7 -0
  350. apsimo/research/search/base.py +41 -0
  351. apsimo/research/search/brave.py +59 -0
  352. apsimo/research/search/cache.py +51 -0
  353. apsimo/research/search/duckduckgo.py +103 -0
  354. apsimo/research/search/orchestrator.py +119 -0
  355. apsimo/research/search/serpapi.py +59 -0
  356. apsimo/research/search/tavily.py +59 -0
  357. apsimo/research/synthesizer.py +309 -0
  358. apsimo/router/__init__.py +30 -0
  359. apsimo/router/complexity_scorer.py +148 -0
  360. apsimo/router/endpoints.py +153 -0
  361. apsimo/router/fallback.py +58 -0
  362. apsimo/router/functions.py +243 -0
  363. apsimo/router/native_policy.py +52 -0
  364. apsimo/router/router.py +762 -0
  365. apsimo/router/self_learning.py +174 -0
  366. apsimo/router/tiers.py +677 -0
  367. apsimo/sandbox/__init__.py +21 -0
  368. apsimo/sandbox/backend.py +195 -0
  369. apsimo/sandbox/manager.py +173 -0
  370. apsimo/scope_bounds.py +7 -0
  371. apsimo/secrets/__init__.py +6 -0
  372. apsimo/secrets/backends/__init__.py +8 -0
  373. apsimo/secrets/backends/base.py +42 -0
  374. apsimo/secrets/backends/env.py +110 -0
  375. apsimo/secrets/backends/keyring.py +72 -0
  376. apsimo/secrets/backends/onepassword.py +232 -0
  377. apsimo/secrets/cli.py +191 -0
  378. apsimo/secrets/manager.py +160 -0
  379. apsimo/secrets/migration.py +101 -0
  380. apsimo/secrets/types.py +98 -0
  381. apsimo/seed.py +41 -0
  382. apsimo/self_model/__init__.py +37 -0
  383. apsimo/self_model/appraisals.py +673 -0
  384. apsimo/self_model/benchmark.py +1314 -0
  385. apsimo/self_model/brief.py +40 -0
  386. apsimo/self_model/event_concerns.py +1128 -0
  387. apsimo/self_model/execution_forecasts.py +353 -0
  388. apsimo/self_model/expectations.py +1595 -0
  389. apsimo/self_model/experiments.py +1150 -0
  390. apsimo/self_model/journal.py +148 -0
  391. apsimo/self_model/judgments.py +705 -0
  392. apsimo/self_model/native_outcomes.py +55 -0
  393. apsimo/self_model/params.py +220 -0
  394. apsimo/self_model/perspective.py +246 -0
  395. apsimo/self_model/reconcile.py +183 -0
  396. apsimo/self_model/reply_forecasts.py +381 -0
  397. apsimo/self_model/runtime_forecasts.py +296 -0
  398. apsimo/self_model/runtime_models.py +67 -0
  399. apsimo/self_model/settlement.py +207 -0
  400. apsimo/self_model/situation.py +1731 -0
  401. apsimo/self_model/store.py +883 -0
  402. apsimo/self_model/supervised.py +137 -0
  403. apsimo/self_model/thinker.py +99 -0
  404. apsimo/self_model/trust.py +388 -0
  405. apsimo/self_model/workspace.py +2388 -0
  406. apsimo/server.py +4197 -0
  407. apsimo/services/__init__.py +1 -0
  408. apsimo/services/agent_bridge.py +474 -0
  409. apsimo/services/initiative_executor.py +914 -0
  410. apsimo/services/instance.py +297 -0
  411. apsimo/sessions/__init__.py +22 -0
  412. apsimo/sessions/config.py +13 -0
  413. apsimo/sessions/context_loader.py +88 -0
  414. apsimo/sessions/federation_session.py +75 -0
  415. apsimo/sessions/isolated_session.py +98 -0
  416. apsimo/sessions/reports.py +84 -0
  417. apsimo/sessions/store.py +148 -0
  418. apsimo/setup.py +2818 -0
  419. apsimo/setup_hermes.py +879 -0
  420. apsimo/setup_local_work.py +218 -0
  421. apsimo/setup_native_goals.py +134 -0
  422. apsimo/setup_native_reviews.py +115 -0
  423. apsimo/skills/__init__.py +10 -0
  424. apsimo/skills/base.py +108 -0
  425. apsimo/skills/budget.py +28 -0
  426. apsimo/skills/executor.py +493 -0
  427. apsimo/skills/executors/__init__.py +1 -0
  428. apsimo/skills/executors/behavioral_correction.py +75 -0
  429. apsimo/skills/executors/capability_gap.py +38 -0
  430. apsimo/skills/executors/data_quality.py +163 -0
  431. apsimo/skills/executors/knowledge_acquisition.py +41 -0
  432. apsimo/skills/executors/operational_hygiene.py +185 -0
  433. apsimo/skills/executors/subsystem_health.py +169 -0
  434. apsimo/skills/hermes_export.py +431 -0
  435. apsimo/skills/index.py +123 -0
  436. apsimo/skills/learning/__init__.py +21 -0
  437. apsimo/skills/learning/novelty_detector.py +206 -0
  438. apsimo/skills/learning/pattern_extractor.py +199 -0
  439. apsimo/skills/learning/triggers.py +159 -0
  440. apsimo/skills/loader.py +246 -0
  441. apsimo/skills/migrations/002_progressive_loading.sql +6 -0
  442. apsimo/skills/migrations/backfill_triggers.py +20 -0
  443. apsimo/skills/models.py +202 -0
  444. apsimo/skills/packager.py +128 -0
  445. apsimo/skills/protocols.py +70 -0
  446. apsimo/skills/registry.py +191 -0
  447. apsimo/skills/runtime.py +58 -0
  448. apsimo/skills/sandbox_runner.py +229 -0
  449. apsimo/skills/scheduler.py +129 -0
  450. apsimo/skills/schema.py +79 -0
  451. apsimo/skills/security/__init__.py +12 -0
  452. apsimo/skills/security/guards.py +53 -0
  453. apsimo/skills/security/scanner.py +223 -0
  454. apsimo/skills_memory/__init__.py +26 -0
  455. apsimo/skills_memory/distill.py +159 -0
  456. apsimo/skills_memory/models.py +85 -0
  457. apsimo/skills_memory/retrieve.py +62 -0
  458. apsimo/skills_memory/store.py +172 -0
  459. apsimo/surprise/__init__.py +6 -0
  460. apsimo/surprise/accumulation.py +57 -0
  461. apsimo/surprise/scorer.py +102 -0
  462. apsimo/surprise/store.py +203 -0
  463. apsimo/task_queue/__init__.py +69 -0
  464. apsimo/task_queue/action_receipts.py +148 -0
  465. apsimo/task_queue/approval_relay_canary.py +108 -0
  466. apsimo/task_queue/config.py +85 -0
  467. apsimo/task_queue/contract.py +361 -0
  468. apsimo/task_queue/events.py +130 -0
  469. apsimo/task_queue/governor.py +1031 -0
  470. apsimo/task_queue/handlers/__init__.py +16 -0
  471. apsimo/task_queue/handlers/base.py +37 -0
  472. apsimo/task_queue/handlers/inference.py +640 -0
  473. apsimo/task_queue/handlers/monitoring.py +116 -0
  474. apsimo/task_queue/handlers/registry.py +75 -0
  475. apsimo/task_queue/handlers/subtask_handler.py +173 -0
  476. apsimo/task_queue/handlers/system_maintenance.py +147 -0
  477. apsimo/task_queue/mesh_integration.py +111 -0
  478. apsimo/task_queue/models.py +317 -0
  479. apsimo/task_queue/queue_manager.py +8286 -0
  480. apsimo/task_queue/routing.py +287 -0
  481. apsimo/task_queue/scheduler.py +252 -0
  482. apsimo/task_queue/schema.sql +197 -0
  483. apsimo/task_queue/work_control.py +342 -0
  484. apsimo/task_queue/worker.py +993 -0
  485. apsimo/telemetry.py +145 -0
  486. apsimo/tom/__init__.py +6 -0
  487. apsimo/tom/affect.py +387 -0
  488. apsimo/tom/approvals.py +171 -0
  489. apsimo/tom/arcs.py +896 -0
  490. apsimo/tom/asymmetry.py +131 -0
  491. apsimo/tom/eligibility.py +248 -0
  492. apsimo/tom/engagement.py +214 -0
  493. apsimo/tom/exposure.py +214 -0
  494. apsimo/tom/extractor.py +306 -0
  495. apsimo/tom/fact_adapters.py +144 -0
  496. apsimo/tom/facts.py +326 -0
  497. apsimo/tom/integration.py +592 -0
  498. apsimo/tom/leveled.py +118 -0
  499. apsimo/tom/levels.py +247 -0
  500. apsimo/tom/recipient_audit.py +995 -0
  501. apsimo/tom/recipient_simulator.py +593 -0
  502. apsimo/tom/source_lineage.py +93 -0
  503. apsimo/tom/tom2.py +277 -0
  504. apsimo/tom/visibility.py +559 -0
  505. apsimo/tom/visibility_store.py +414 -0
  506. apsimo/tools/__init__.py +0 -0
  507. apsimo/tools/definitions.py +740 -0
  508. apsimo/tools/handlers.py +943 -0
  509. apsimo/toolsmith/__init__.py +26 -0
  510. apsimo/toolsmith/authority.py +166 -0
  511. apsimo/toolsmith/engine.py +559 -0
  512. apsimo/toolsmith/integrity.py +100 -0
  513. apsimo/toolsmith/miner.py +145 -0
  514. apsimo/toolsmith/policy.py +110 -0
  515. apsimo/toolsmith/registry.py +635 -0
  516. apsimo/turns/__init__.py +17 -0
  517. apsimo/turns/audio.py +134 -0
  518. apsimo/turns/documents.py +235 -0
  519. apsimo/turns/executions.py +486 -0
  520. apsimo/turns/hermes_history.py +245 -0
  521. apsimo/turns/hermes_kanban.py +268 -0
  522. apsimo/turns/hermes_work.py +96 -0
  523. apsimo/turns/idempotency.py +752 -0
  524. apsimo/turns/local_work.py +115 -0
  525. apsimo/turns/media.py +581 -0
  526. apsimo/turns/reported_workers.py +196 -0
  527. apsimo/turns/source_annotations.py +283 -0
  528. apsimo/turns/source_attribution.py +154 -0
  529. apsimo/turns/source_read.py +351 -0
  530. apsimo/turns/source_vectors.py +263 -0
  531. apsimo/turns/video.py +210 -0
  532. apsimo/util/autonomy_preset.py +220 -0
  533. apsimo/util/instance.py +92 -0
  534. apsimo/util/model_output.py +25 -0
  535. apsimo/util/quiet_hours.py +27 -0
  536. apsimo/util/session_safety.py +37 -0
  537. apsimo/util/temporal.py +343 -0
  538. apsimo/vector/__init__.py +75 -0
  539. apsimo/vector/backfill.py +171 -0
  540. apsimo/vector/caption.py +114 -0
  541. apsimo/vector/collections.py +51 -0
  542. apsimo/vector/config.py +102 -0
  543. apsimo/vector/embedder.py +670 -0
  544. apsimo/vector/image_preprocess.py +406 -0
  545. apsimo/vector/image_store.py +296 -0
  546. apsimo/vector/indexes.py +162 -0
  547. apsimo/vector/migrate.py +334 -0
  548. apsimo/vector/multimodal_provider.py +417 -0
  549. apsimo/vector/multimodal_types.py +87 -0
  550. apsimo/vector/openai_provider.py +119 -0
  551. apsimo/vector/query.py +49 -0
  552. apsimo/vector/reranker.py +565 -0
  553. apsimo/vector/safety_image.py +159 -0
  554. apsimo/vector/scanner.py +197 -0
  555. apsimo/vector/setup.py +289 -0
  556. apsimo/vector/store.py +533 -0
  557. apsimo/vector/tiers.py +263 -0
  558. apsimo/work_orders.py +925 -0
  559. apsimo/workers/__init__.py +21 -0
  560. apsimo/workers/agent_bridge.py +640 -0
  561. apsimo/workers/colony_worker.py +382 -0
  562. apsimo/workers/queue_worker.py +441 -0
  563. apsimo/workers/skills_sync.py +152 -0
  564. apsimo/world_model/__init__.py +71 -0
  565. apsimo/world_model/causal_maintenance.py +131 -0
  566. apsimo/world_model/causal_policy.py +43 -0
  567. apsimo/world_model/causal_query.py +125 -0
  568. apsimo/world_model/confidence.py +54 -0
  569. apsimo/world_model/config.py +64 -0
  570. apsimo/world_model/constants.py +97 -0
  571. apsimo/world_model/entities.py +145 -0
  572. apsimo/world_model/expectation_resolvers.py +177 -0
  573. apsimo/world_model/extraction/__init__.py +7 -0
  574. apsimo/world_model/extraction/base.py +62 -0
  575. apsimo/world_model/extraction/conversation_extractor.py +262 -0
  576. apsimo/world_model/extraction/detector.py +74 -0
  577. apsimo/world_model/extraction/document_extractor.py +78 -0
  578. apsimo/world_model/extraction/formats/__init__.py +24 -0
  579. apsimo/world_model/extraction/formats/csv_fmt.py +68 -0
  580. apsimo/world_model/extraction/formats/html_fmt.py +72 -0
  581. apsimo/world_model/extraction/formats/json_fmt.py +68 -0
  582. apsimo/world_model/extraction/formats/pdf.py +43 -0
  583. apsimo/world_model/extraction/formats/text.py +27 -0
  584. apsimo/world_model/extraction/llm_extractor.py +164 -0
  585. apsimo/world_model/extraction/pipeline.py +73 -0
  586. apsimo/world_model/integrations/__init__.py +5 -0
  587. apsimo/world_model/integrations/mind_model_bridge.py +115 -0
  588. apsimo/world_model/integrations/social_intel_bridge.py +120 -0
  589. apsimo/world_model/jobs/__init__.py +4 -0
  590. apsimo/world_model/jobs/extraction_job.py +168 -0
  591. apsimo/world_model/llm_extract.py +572 -0
  592. apsimo/world_model/neo4j/__init__.py +5 -0
  593. apsimo/world_model/neo4j/backend.py +654 -0
  594. apsimo/world_model/observations.py +155 -0
  595. apsimo/world_model/populator.py +307 -0
  596. apsimo/world_model/postgres/__init__.py +1 -0
  597. apsimo/world_model/postgres/backend.py +683 -0
  598. apsimo/world_model/relationships.py +25 -0
  599. apsimo/world_model/resolution/__init__.py +13 -0
  600. apsimo/world_model/resolution/entity_resolver.py +232 -0
  601. apsimo/world_model/resolution/merge_audit.py +16 -0
  602. apsimo/world_model/resolution/merge_workflow.py +117 -0
  603. apsimo/world_model/source_reports.py +121 -0
  604. apsimo/world_model/sqlite/__init__.py +4 -0
  605. apsimo/world_model/sqlite/backend.py +855 -0
  606. apsimo/world_model/sqlite/schema.sql +132 -0
  607. apsimo/world_model/store.py +545 -0
  608. apsimo-1.3.0.dist-info/METADATA +78 -0
  609. apsimo-1.3.0.dist-info/RECORD +614 -0
  610. apsimo-1.3.0.dist-info/WHEEL +5 -0
  611. apsimo-1.3.0.dist-info/entry_points.txt +11 -0
  612. apsimo-1.3.0.dist-info/licenses/LICENSE +21 -0
  613. apsimo-1.3.0.dist-info/top_level.txt +2 -0
  614. colony_sidecar/__init__.py +4 -0
@@ -0,0 +1,68 @@
1
+ """CSV entity extractor."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import csv
6
+ import io
7
+ import logging
8
+ from typing import Any, Dict, List
9
+
10
+ from apsimo.world_model.extraction.base import FormatExtractor, ExtractedEntity
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class CSVExtractor(FormatExtractor):
16
+ """Extract entities from CSV data.
17
+
18
+ Each row becomes an entity. Column headers are used as attribute keys.
19
+ A 'name' or 'title' column (if present) is used as the entity name.
20
+ """
21
+
22
+ def supported_formats(self) -> List[str]:
23
+ return ["text/csv"]
24
+
25
+ async def extract_text(self, content: bytes, metadata: Dict[str, Any]) -> str:
26
+ try:
27
+ text = content.decode("utf-8")
28
+ except UnicodeDecodeError:
29
+ try:
30
+ text = content.decode("latin-1")
31
+ except Exception:
32
+ return ""
33
+ return text
34
+
35
+ async def extract_entities(self, content: bytes, metadata: Dict[str, Any]) -> List[ExtractedEntity]:
36
+ try:
37
+ text = content.decode("utf-8")
38
+ except UnicodeDecodeError:
39
+ try:
40
+ text = content.decode("latin-1")
41
+ except Exception as e:
42
+ logger.warning("Failed to decode CSV: %s", e)
43
+ return []
44
+
45
+ reader = csv.DictReader(io.StringIO(text))
46
+ if not reader.fieldnames:
47
+ return []
48
+
49
+ entities = []
50
+ for i, row in enumerate(reader):
51
+ # Find the name column
52
+ name = row.get("name") or row.get("title") or row.get("Name") or row.get("Title") or f"row-{i}"
53
+ etype = row.get("type") or row.get("Type") or "entity"
54
+
55
+ # All other columns become attributes
56
+ attrs = {}
57
+ for k, v in row.items():
58
+ if k and k.lower() not in ("name", "title", "type") and v:
59
+ attrs[k] = v
60
+
61
+ entities.append(ExtractedEntity(
62
+ name=str(name).strip(),
63
+ entity_type=str(etype).strip(),
64
+ attributes=attrs,
65
+ confidence=0.85,
66
+ ))
67
+
68
+ return entities
@@ -0,0 +1,72 @@
1
+ """HTML entity extractor (requires BeautifulSoup)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import Any, Dict, List
7
+
8
+ from apsimo.world_model.extraction.base import FormatExtractor, ExtractedEntity
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ try:
13
+ from bs4 import BeautifulSoup
14
+ HAS_BS4 = True
15
+ except ImportError:
16
+ HAS_BS4 = False
17
+
18
+
19
+ class HTMLExtractor(FormatExtractor):
20
+ """Extract text and entities from HTML via BeautifulSoup."""
21
+
22
+ def supported_formats(self) -> List[str]:
23
+ return ["text/html"]
24
+
25
+ async def extract_text(self, content: bytes, metadata: Dict[str, Any]) -> str:
26
+ if not HAS_BS4:
27
+ logger.warning("BeautifulSoup not installed — cannot extract HTML text")
28
+ return ""
29
+
30
+ try:
31
+ soup = BeautifulSoup(content, "html.parser")
32
+ # Remove script and style elements
33
+ for tag in soup(["script", "style", "nav", "footer", "header"]):
34
+ tag.decompose()
35
+ return soup.get_text(separator="\n", strip=True)
36
+ except Exception as e:
37
+ logger.warning("Failed to extract HTML text: %s", e)
38
+ return ""
39
+
40
+ async def extract_entities(self, content: bytes, metadata: Dict[str, Any]) -> List[ExtractedEntity]:
41
+ if not HAS_BS4:
42
+ return []
43
+
44
+ try:
45
+ soup = BeautifulSoup(content, "html.parser")
46
+ entities = []
47
+
48
+ # Extract metadata entities
49
+ title = soup.find("title")
50
+ if title and title.string:
51
+ entities.append(ExtractedEntity(
52
+ name=title.string.strip(),
53
+ entity_type="webpage",
54
+ attributes={"type": "title"},
55
+ confidence=0.95,
56
+ ))
57
+
58
+ # Extract links as relationship hints
59
+ for a in soup.find_all("a", href=True)[:20]:
60
+ link_text = a.get_text(strip=True)
61
+ if link_text:
62
+ entities.append(ExtractedEntity(
63
+ name=link_text,
64
+ entity_type="link",
65
+ attributes={"href": a["href"]},
66
+ confidence=0.7,
67
+ ))
68
+
69
+ return entities
70
+ except Exception as e:
71
+ logger.warning("Failed to extract HTML entities: %s", e)
72
+ return []
@@ -0,0 +1,68 @@
1
+ """JSON entity extractor."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import logging
7
+ from typing import Any, Dict, List
8
+
9
+ from apsimo.world_model.extraction.base import FormatExtractor, ExtractedEntity
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class JSONExtractor(FormatExtractor):
15
+ """Extract entities from structured JSON data.
16
+
17
+ Handles two patterns:
18
+ 1. Array of objects: each object becomes an entity
19
+ 2. Nested objects: top-level keys become entity types
20
+ """
21
+
22
+ def supported_formats(self) -> List[str]:
23
+ return ["application/json"]
24
+
25
+ async def extract_text(self, content: bytes, metadata: Dict[str, Any]) -> str:
26
+ try:
27
+ data = json.loads(content)
28
+ return json.dumps(data, indent=2, ensure_ascii=False)
29
+ except (json.JSONDecodeError, UnicodeDecodeError):
30
+ return ""
31
+
32
+ async def extract_entities(self, content: bytes, metadata: Dict[str, Any]) -> List[ExtractedEntity]:
33
+ try:
34
+ data = json.loads(content)
35
+ except (json.JSONDecodeError, UnicodeDecodeError) as e:
36
+ logger.warning("Failed to parse JSON: %s", e)
37
+ return []
38
+
39
+ entities = []
40
+
41
+ # Array of objects — each becomes an entity
42
+ if isinstance(data, list):
43
+ for i, item in enumerate(data):
44
+ if isinstance(item, dict):
45
+ name = item.get("name") or item.get("title") or item.get("id") or f"item-{i}"
46
+ etype = item.get("type", "entity")
47
+ attrs = {k: v for k, v in item.items() if k not in ("name", "title", "id", "type")}
48
+ entities.append(ExtractedEntity(
49
+ name=str(name),
50
+ entity_type=str(etype),
51
+ attributes=attrs,
52
+ confidence=0.9,
53
+ ))
54
+
55
+ # Single object — top-level keys become attributes
56
+ elif isinstance(data, dict):
57
+ # If it has a 'name' or 'title', treat as one entity
58
+ name = data.get("name") or data.get("title") or metadata.get("filename", "json-object")
59
+ etype = data.get("type", "entity")
60
+ attrs = {k: v for k, v in data.items() if k not in ("name", "title", "type") and isinstance(v, (str, int, float, bool))}
61
+ entities.append(ExtractedEntity(
62
+ name=str(name),
63
+ entity_type=str(etype),
64
+ attributes=attrs,
65
+ confidence=0.9,
66
+ ))
67
+
68
+ return entities
@@ -0,0 +1,43 @@
1
+ """PDF entity extractor (requires PyMuPDF)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import Any, Dict, List
7
+
8
+ from apsimo.world_model.extraction.base import FormatExtractor, ExtractedEntity
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ try:
13
+ import fitz # PyMuPDF
14
+ HAS_PYMUPDF = True
15
+ except ImportError:
16
+ HAS_PYMUPDF = False
17
+
18
+
19
+ class PDFExtractor(FormatExtractor):
20
+ """Extract text and entities from PDF files via PyMuPDF."""
21
+
22
+ def supported_formats(self) -> List[str]:
23
+ return ["application/pdf"]
24
+
25
+ async def extract_text(self, content: bytes, metadata: Dict[str, Any]) -> str:
26
+ if not HAS_PYMUPDF:
27
+ logger.warning("PyMuPDF not installed — cannot extract PDF text")
28
+ return ""
29
+
30
+ try:
31
+ doc = fitz.open(stream=content, filetype="pdf")
32
+ text_parts = []
33
+ for page in doc:
34
+ text_parts.append(page.get_text())
35
+ doc.close()
36
+ return "\n\n".join(text_parts)
37
+ except Exception as e:
38
+ logger.warning("Failed to extract PDF text: %s", e)
39
+ return ""
40
+
41
+ async def extract_entities(self, content: bytes, metadata: Dict[str, Any]) -> List[ExtractedEntity]:
42
+ # PDFs don't have structured entities — rely on text extraction + LLM
43
+ return []
@@ -0,0 +1,27 @@
1
+ """Plain text extractor."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import Any, Dict, List
7
+
8
+ from apsimo.world_model.extraction.base import FormatExtractor, ExtractedEntity
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class TextExtractor(FormatExtractor):
14
+ """Extract entities from plain text (via LLM)."""
15
+
16
+ def supported_formats(self) -> List[str]:
17
+ return ["text/plain", "text/markdown"]
18
+
19
+ async def extract_text(self, content: bytes, metadata: Dict[str, Any]) -> str:
20
+ try:
21
+ return content.decode("utf-8")
22
+ except UnicodeDecodeError:
23
+ try:
24
+ return content.decode("latin-1")
25
+ except Exception:
26
+ logger.warning("Failed to decode text content")
27
+ return ""
@@ -0,0 +1,164 @@
1
+ """LLM-backed entity extractor — the fallback path for ExtractionPipeline.
2
+
3
+ ``ExtractionPipeline`` first tries each ``FormatExtractor``'s structured
4
+ ``extract_entities`` pass. When that returns an empty list (common for
5
+ unstructured PDFs, plain HTML, free-form text) the pipeline falls back to
6
+ ``extract_text`` + this LLM extractor.
7
+
8
+ The LLM is prompted to return a strict JSON array; anything else (prose,
9
+ partial JSON, network errors) degrades to an empty list so the pipeline
10
+ stays non-fatal.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import json
16
+ import logging
17
+ import re
18
+ from typing import Any, Awaitable, Callable, Dict, List, Optional
19
+
20
+ from apsimo.world_model.extraction.base import ExtractedEntity
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ LlmExtractFn = Callable[[str, Dict[str, Any]], Awaitable[List[ExtractedEntity]]]
25
+
26
+ # Plenty of room for a useful document but bounded so we don't blow tokens.
27
+ MAX_INPUT_CHARS = 12_000
28
+ MAX_RESPONSE_TOKENS = 1_024
29
+
30
+ _SYSTEM_PROMPT = (
31
+ "You are an entity extraction engine. Read the document and return a "
32
+ "JSON array of entities. Each entity must be an object with keys: "
33
+ '"name" (string, required), "type" (one of: person, company, project, '
34
+ 'product, location, event, concept), "attributes" (object, optional), '
35
+ '"confidence" (number 0-1, optional). Return ONLY the JSON array: '
36
+ "no prose, no code fences, no explanation. Return [] if no entities "
37
+ "are present. Do not invent entities not grounded in the text."
38
+ )
39
+
40
+
41
+ def build_llm_extract_fn(
42
+ llm_router: Any,
43
+ *,
44
+ max_input_chars: int = MAX_INPUT_CHARS,
45
+ max_response_tokens: int = MAX_RESPONSE_TOKENS,
46
+ ) -> LlmExtractFn:
47
+ """Build an ``llm_extract_fn`` bound to the given ``LLMRouter``.
48
+
49
+ Parameters
50
+ ----------
51
+ llm_router :
52
+ An object with an ``async complete(messages, ...)`` method that
53
+ returns an object with a ``.content`` attribute (the ``LLMRouter``
54
+ from ``apsimo.router.router``).
55
+ max_input_chars :
56
+ Truncate the text to this many characters before sending to the LLM.
57
+ max_response_tokens :
58
+ Cap on LLM output tokens.
59
+ """
60
+
61
+ async def extract(text: str, metadata: Dict[str, Any]) -> List[ExtractedEntity]:
62
+ snippet = (text or "").strip()
63
+ if not snippet:
64
+ return []
65
+ if len(snippet) > max_input_chars:
66
+ snippet = snippet[:max_input_chars]
67
+ user_prompt = _build_user_prompt(snippet, metadata)
68
+ try:
69
+ resp = await llm_router.complete(
70
+ messages=[
71
+ {"role": "system", "content": _SYSTEM_PROMPT},
72
+ {"role": "user", "content": user_prompt},
73
+ ],
74
+ context={"task": "entity_extraction", "max_tokens": max_response_tokens},
75
+ )
76
+ except Exception as exc:
77
+ logger.warning("LLM entity extraction call failed: %s", exc)
78
+ return []
79
+
80
+ content = getattr(resp, "content", "") or ""
81
+ return _parse_entity_array(content)
82
+
83
+ return extract
84
+
85
+
86
+ def _build_user_prompt(text: str, metadata: Dict[str, Any]) -> str:
87
+ hints: List[str] = []
88
+ filename = metadata.get("filename") or metadata.get("source")
89
+ if filename:
90
+ hints.append(f"Source: {filename}")
91
+ if metadata.get("mime_type"):
92
+ hints.append(f"MIME: {metadata['mime_type']}")
93
+ prefix = ("\n".join(hints) + "\n\n") if hints else ""
94
+ return f"{prefix}Document:\n---\n{text}\n---\n\nReturn the JSON array."
95
+
96
+
97
+ _TYPE_SYNONYMS = {
98
+ "organization": "company", "org": "company", "business": "company",
99
+ "place": "location",
100
+ }
101
+
102
+
103
+ def _parse_entity_array(raw: str) -> List[ExtractedEntity]:
104
+ """Best-effort parse of an LLM response into ``ExtractedEntity`` objects.
105
+
106
+ Accepts either a bare JSON array or an object with an ``entities`` key
107
+ (LLMs occasionally wrap the array). Tolerates code fences.
108
+ """
109
+ if not raw:
110
+ return []
111
+ candidate = _strip_code_fence(raw).strip()
112
+ try:
113
+ parsed = json.loads(candidate)
114
+ except json.JSONDecodeError:
115
+ # Fall back to the first [...] block in the response.
116
+ match = re.search(r"\[.*\]", candidate, re.DOTALL)
117
+ if not match:
118
+ logger.debug("LLM extractor: no JSON array in response")
119
+ return []
120
+ try:
121
+ parsed = json.loads(match.group(0))
122
+ except json.JSONDecodeError as exc:
123
+ logger.debug("LLM extractor: JSON parse failed (%s)", exc)
124
+ return []
125
+
126
+ if isinstance(parsed, dict):
127
+ parsed = parsed.get("entities") or parsed.get("items") or []
128
+ if not isinstance(parsed, list):
129
+ return []
130
+
131
+ entities: List[ExtractedEntity] = []
132
+ for item in parsed:
133
+ if not isinstance(item, dict):
134
+ continue
135
+ name = item.get("name")
136
+ entity_type = item.get("type") or item.get("entity_type")
137
+ if not isinstance(name, str) or not isinstance(entity_type, str):
138
+ continue
139
+ entity_type = _TYPE_SYNONYMS.get(
140
+ entity_type.strip().lower(), entity_type.strip().lower())
141
+ attributes = item.get("attributes")
142
+ if not isinstance(attributes, dict):
143
+ attributes = None
144
+ confidence = item.get("confidence", 0.7)
145
+ try:
146
+ confidence = float(confidence)
147
+ except (TypeError, ValueError):
148
+ confidence = 0.7
149
+ entities.append(
150
+ ExtractedEntity(
151
+ name=name.strip(),
152
+ entity_type=entity_type.strip(),
153
+ attributes=attributes,
154
+ confidence=max(0.0, min(1.0, confidence)),
155
+ )
156
+ )
157
+ return entities
158
+
159
+
160
+ _CODE_FENCE = re.compile(r"^```(?:json)?\s*|\s*```$", re.MULTILINE)
161
+
162
+
163
+ def _strip_code_fence(text: str) -> str:
164
+ return _CODE_FENCE.sub("", text)
@@ -0,0 +1,73 @@
1
+ """Extraction pipeline — orchestrates format detection and entity extraction."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import Any, Callable, Dict, List, Optional
7
+
8
+ from apsimo.world_model.extraction.base import ExtractedEntity, FormatExtractor
9
+ from apsimo.world_model.extraction.detector import FormatDetector
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class ExtractionPipeline:
15
+ """Orchestrate format detection and entity extraction.
16
+
17
+ Flow:
18
+ 1. Detect format
19
+ 2. Try structured extraction (fast, no LLM)
20
+ 3. Fall back to text extraction + LLM
21
+ """
22
+
23
+ def __init__(
24
+ self,
25
+ extractors: List[FormatExtractor],
26
+ llm_extract_fn: Optional[Callable] = None,
27
+ ):
28
+ self._extractors: Dict[str, FormatExtractor] = {}
29
+ for ext in extractors:
30
+ for fmt in ext.supported_formats():
31
+ self._extractors[fmt] = ext
32
+ self._detector = FormatDetector()
33
+ self._llm_extract_fn = llm_extract_fn
34
+
35
+ async def extract(
36
+ self,
37
+ content: bytes,
38
+ filename: str = "",
39
+ mime_type: str = "",
40
+ metadata: Optional[Dict[str, Any]] = None,
41
+ ) -> List[ExtractedEntity]:
42
+ """Extract entities from content.
43
+
44
+ 1. Detect format
45
+ 2. Try structured extraction (fast, no LLM)
46
+ 3. Fall back to text extraction + LLM
47
+ """
48
+ metadata = metadata or {}
49
+
50
+ # Detect format
51
+ fmt = self._detector.detect(content, filename=filename, mime_type=mime_type)
52
+ logger.debug("Detected format: %s", fmt)
53
+
54
+ # Find extractor
55
+ extractor = self._extractors.get(fmt)
56
+ if extractor is None:
57
+ logger.warning("No extractor for format '%s' — skipping", fmt)
58
+ return []
59
+
60
+ # 1. Try structured extraction first
61
+ entities = await extractor.extract_entities(content, metadata)
62
+
63
+ # 2. Fall back to text + LLM
64
+ if not entities:
65
+ text = await extractor.extract_text(content, metadata)
66
+ if text and self._llm_extract_fn:
67
+ try:
68
+ entities = await self._llm_extract_fn(text, metadata)
69
+ except Exception as e:
70
+ logger.warning("LLM extraction failed: %s", e)
71
+
72
+ logger.info("Extracted %d entities from %s format", len(entities), fmt)
73
+ return entities
@@ -0,0 +1,5 @@
1
+ """World Model integration bridges."""
2
+ from .mind_model_bridge import MindModelBridge
3
+ from .social_intel_bridge import SocialIntelBridge
4
+
5
+ __all__ = ["MindModelBridge", "SocialIntelBridge"]
@@ -0,0 +1,115 @@
1
+ """Mind Model bridge: provides world model context to the mind model pipeline."""
2
+
3
+ from typing import Any, Dict, List, Optional, TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from ..store import WorldModelStore
7
+
8
+
9
+ class MindModelBridge:
10
+ """Provides world model context to the mind model pipeline."""
11
+
12
+ def __init__(self, store: "WorldModelStore") -> None:
13
+ self._store = store
14
+
15
+ async def get_context_for_person(
16
+ self,
17
+ person_id: str,
18
+ as_of: Optional[str] = None,
19
+ ) -> Dict[str, Any]:
20
+ """Fetch world model context relevant to a person's mental state.
21
+
22
+ Returns employer, active projects, upcoming events, and shared
23
+ concepts that may inform state estimation.
24
+
25
+ Args:
26
+ person_id: The world model entity ID of the person.
27
+ as_of: Optional ISO8601 datetime for temporal queries.
28
+
29
+ Returns:
30
+ Dict with 'employer', 'projects', 'events', 'concepts' keys.
31
+ """
32
+ context: Dict[str, Any] = {
33
+ "employer": None,
34
+ "projects": [],
35
+ "events": [],
36
+ "concepts": [],
37
+ }
38
+
39
+ # Current employer
40
+ employer_rels = await self._store.query_relationships(
41
+ source_id=person_id,
42
+ relationship_type="WM_WORKS_AT",
43
+ active_only=(as_of is None),
44
+ min_confidence=0.30,
45
+ limit=1,
46
+ )
47
+ if employer_rels:
48
+ employer = await self._store.get_entity(employer_rels[0].target_id)
49
+ context["employer"] = employer
50
+
51
+ # Active project memberships
52
+ project_rels = await self._store.query_relationships(
53
+ source_id=person_id,
54
+ relationship_type="WM_MEMBER_OF",
55
+ target_types=["project"],
56
+ active_only=(as_of is None),
57
+ min_confidence=0.30,
58
+ limit=10,
59
+ )
60
+ for rel in project_rels:
61
+ project = await self._store.get_entity(rel.target_id)
62
+ if project:
63
+ context["projects"].append(project)
64
+
65
+ # Upcoming events (attended)
66
+ event_rels = await self._store.query_relationships(
67
+ source_id=person_id,
68
+ relationship_type="WM_ATTENDED",
69
+ target_types=["event"],
70
+ min_confidence=0.30,
71
+ limit=10,
72
+ )
73
+ for rel in event_rels:
74
+ event = await self._store.get_entity(rel.target_id)
75
+ if event:
76
+ context["events"].append(event)
77
+
78
+ # Tagged concepts
79
+ concept_rels = await self._store.query_relationships(
80
+ source_id=person_id,
81
+ relationship_type="WM_TAGGED_WITH",
82
+ target_types=["concept"],
83
+ min_confidence=0.30,
84
+ limit=10,
85
+ )
86
+ for rel in concept_rels:
87
+ concept = await self._store.get_entity(rel.target_id)
88
+ if concept:
89
+ context["concepts"].append(concept)
90
+
91
+ return context
92
+
93
+ async def enrich_mind_signal(
94
+ self,
95
+ person_id: str,
96
+ signal_text: str,
97
+ ) -> Optional[str]:
98
+ """Cross-reference a text signal with world model entities.
99
+
100
+ Returns the entity ID if a matching EventEntity or ConceptEntity
101
+ is found, otherwise None.
102
+ """
103
+ if not signal_text:
104
+ return None
105
+
106
+ # Search events and concepts
107
+ candidates = await self._store.find_entities(
108
+ query=signal_text,
109
+ min_confidence=0.30,
110
+ limit=5,
111
+ )
112
+ for candidate in candidates:
113
+ if candidate.entity_type in ("event", "concept"):
114
+ return candidate.id
115
+ return None