hyperagents 1.2.3rc5.post3__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 (361) hide show
  1. agents/__init__.py +31 -0
  2. agents/background_monitor.py +453 -0
  3. agents/learning.py +460 -0
  4. agents/planner.py +421 -0
  5. agents/transcriber.py +341 -0
  6. civilization/__init__.py +51 -0
  7. civilization/api/__init__.py +7 -0
  8. civilization/api/civilization_api.py +361 -0
  9. civilization/graph/__init__.py +19 -0
  10. civilization/graph/knowledge_linker.py +348 -0
  11. civilization/graph/org_graph.py +348 -0
  12. civilization/graph/process_graph.py +386 -0
  13. civilization/ingestion/__init__.py +20 -0
  14. civilization/ingestion/chunker.py +273 -0
  15. civilization/ingestion/document_ingestor.py +358 -0
  16. civilization/ingestion/embedder.py +262 -0
  17. civilization/ingestion/extractor.py +224 -0
  18. civilization/interview/__init__.py +19 -0
  19. civilization/interview/gap_detector.py +380 -0
  20. civilization/interview/interviewer.py +323 -0
  21. civilization/interview/questioner.py +363 -0
  22. civilization/retrieval/__init__.py +17 -0
  23. civilization/retrieval/civilization_rag.py +316 -0
  24. civilization/retrieval/context_injector.py +342 -0
  25. civilization/retrieval/relevance_ranker.py +287 -0
  26. civilization/schema.py +299 -0
  27. civilization/store.py +177 -0
  28. civilization/sync/__init__.py +16 -0
  29. civilization/sync/confluence_sync.py +317 -0
  30. civilization/sync/gdrive_sync.py +262 -0
  31. civilization/sync/notion_sync.py +237 -0
  32. civilization/versioning/__init__.py +19 -0
  33. civilization/versioning/diff_engine.py +351 -0
  34. civilization/versioning/staleness_detector.py +303 -0
  35. civilization/versioning/version_manager.py +293 -0
  36. cli/__init__.py +1 -0
  37. cli/doctor.py +334 -0
  38. cli/hyperclaw.py +1357 -0
  39. cli/onboarding.py +469 -0
  40. core/__init__.py +1 -0
  41. core/connectors/__init__.py +119 -0
  42. core/connectors/infrastructure.py +772 -0
  43. core/connectors/messaging.py +586 -0
  44. core/connectors/productivity.py +764 -0
  45. core/connectors/smarthome.py +528 -0
  46. core/connectors/social.py +730 -0
  47. core/hyperrouter/__init__.py +23 -0
  48. core/hyperrouter/bandit.py +218 -0
  49. core/hyperrouter/fast_loop.py +68 -0
  50. core/hyperrouter/slow_loop.py +143 -0
  51. core/hyperstate/__init__.py +24 -0
  52. core/hyperstate/certifier.py +154 -0
  53. core/hyperstate/schema.py +160 -0
  54. core/hyperstate/state_manager.py +103 -0
  55. core/hyperstate/store.py +163 -0
  56. core/providers/__init__.py +121 -0
  57. core/providers/embeddings.py +389 -0
  58. core/providers/image.py +534 -0
  59. core/providers/llm.py +1935 -0
  60. core/providers/search.py +608 -0
  61. core/providers/voice.py +474 -0
  62. hyperagents-1.2.3rc5.post3.dist-info/METADATA +469 -0
  63. hyperagents-1.2.3rc5.post3.dist-info/RECORD +361 -0
  64. hyperagents-1.2.3rc5.post3.dist-info/WHEEL +5 -0
  65. hyperagents-1.2.3rc5.post3.dist-info/entry_points.txt +6 -0
  66. hyperagents-1.2.3rc5.post3.dist-info/licenses/LICENSE +21 -0
  67. hyperagents-1.2.3rc5.post3.dist-info/top_level.txt +13 -0
  68. hyperclaw/__init__.py +67 -0
  69. hyperclaw/__main__.py +220 -0
  70. hyperclaw/agent.py +775 -0
  71. hyperclaw/agent_coordinator.py +577 -0
  72. hyperclaw/anthropic_params.py +225 -0
  73. hyperclaw/api_utils.py +85 -0
  74. hyperclaw/approval_telegram.py +209 -0
  75. hyperclaw/approval_terminal.py +80 -0
  76. hyperclaw/attachments.py +177 -0
  77. hyperclaw/branding.py +55 -0
  78. hyperclaw/brief/__init__.py +11 -0
  79. hyperclaw/brief/cli.py +204 -0
  80. hyperclaw/brief/collect.py +146 -0
  81. hyperclaw/brief/compose.py +333 -0
  82. hyperclaw/brief/diff.py +110 -0
  83. hyperclaw/brief/errors.py +99 -0
  84. hyperclaw/brief/google.py +269 -0
  85. hyperclaw/brief/google_auth.py +294 -0
  86. hyperclaw/brief/links.py +124 -0
  87. hyperclaw/brief/service.py +424 -0
  88. hyperclaw/brief/store.py +205 -0
  89. hyperclaw/brief/tasks.py +200 -0
  90. hyperclaw/chat_scope.py +115 -0
  91. hyperclaw/cli_tui.py +497 -0
  92. hyperclaw/client_guard.py +717 -0
  93. hyperclaw/connection_check.py +113 -0
  94. hyperclaw/cost_guard.py +1728 -0
  95. hyperclaw/default_config/agents.yaml +534 -0
  96. hyperclaw/default_config/hyperclaw.yaml +27 -0
  97. hyperclaw/default_config/models.yaml +161 -0
  98. hyperclaw/deployment.py +39 -0
  99. hyperclaw/digest/__init__.py +9 -0
  100. hyperclaw/digest/cli.py +117 -0
  101. hyperclaw/digest/errors.py +100 -0
  102. hyperclaw/digest/gitlab.py +187 -0
  103. hyperclaw/digest/http.py +113 -0
  104. hyperclaw/digest/redact.py +32 -0
  105. hyperclaw/digest/service.py +271 -0
  106. hyperclaw/digest/slack.py +223 -0
  107. hyperclaw/digest/state.py +124 -0
  108. hyperclaw/digest/summarize.py +198 -0
  109. hyperclaw/digest/tokens.py +143 -0
  110. hyperclaw/distribution.py +46 -0
  111. hyperclaw/documents.py +351 -0
  112. hyperclaw/feeds.py +339 -0
  113. hyperclaw/google_workspace/__init__.py +5 -0
  114. hyperclaw/google_workspace/api.py +151 -0
  115. hyperclaw/google_workspace/auth.py +397 -0
  116. hyperclaw/google_workspace/calendar.py +157 -0
  117. hyperclaw/google_workspace/cli.py +72 -0
  118. hyperclaw/google_workspace/drive.py +236 -0
  119. hyperclaw/google_workspace/errors.py +58 -0
  120. hyperclaw/google_workspace/gmail.py +295 -0
  121. hyperclaw/google_workspace/meet.py +122 -0
  122. hyperclaw/google_workspace/tools.py +319 -0
  123. hyperclaw/hyperspeed.py +57 -0
  124. hyperclaw/integrations_layer.py +1427 -0
  125. hyperclaw/live_state.py +188 -0
  126. hyperclaw/managed_config.py +245 -0
  127. hyperclaw/media_hub.py +179 -0
  128. hyperclaw/memory_manager.py +584 -0
  129. hyperclaw/model_catalog.py +551 -0
  130. hyperclaw/model_policy.py +211 -0
  131. hyperclaw/model_router.py +595 -0
  132. hyperclaw/model_selector.py +72 -0
  133. hyperclaw/naming.py +284 -0
  134. hyperclaw/notices.py +65 -0
  135. hyperclaw/oai_stream.py +106 -0
  136. hyperclaw/orchestrator.py +591 -0
  137. hyperclaw/outbox.py +82 -0
  138. hyperclaw/persona.py +367 -0
  139. hyperclaw/prometheus.py +219 -0
  140. hyperclaw/prompt_assembly.py +157 -0
  141. hyperclaw/provider_setup.py +146 -0
  142. hyperclaw/providers.py +340 -0
  143. hyperclaw/qualification.py +341 -0
  144. hyperclaw/qualification_keys/hyper-d292fd127cef5a3e.pub +1 -0
  145. hyperclaw/qualification_records/anthropic--claude-sonnet-5-5.json +14 -0
  146. hyperclaw/reasoning_display.py +35 -0
  147. hyperclaw/scheduler.py +454 -0
  148. hyperclaw/server.py +847 -0
  149. hyperclaw/setup.py +627 -0
  150. hyperclaw/skills.py +717 -0
  151. hyperclaw/solomon.py +442 -0
  152. hyperclaw/swarm.py +533 -0
  153. hyperclaw/taint.py +832 -0
  154. hyperclaw/team/__init__.py +28 -0
  155. hyperclaw/team/dispatcher.py +431 -0
  156. hyperclaw/team/genesis.py +106 -0
  157. hyperclaw/team/lead.py +110 -0
  158. hyperclaw/team/model.py +135 -0
  159. hyperclaw/team/morning.py +238 -0
  160. hyperclaw/team/repos.py +66 -0
  161. hyperclaw/team/roster.py +264 -0
  162. hyperclaw/team/specialist.py +235 -0
  163. hyperclaw/team/status.py +163 -0
  164. hyperclaw/team/watchers.py +112 -0
  165. hyperclaw/teamsetup/__init__.py +17 -0
  166. hyperclaw/teamsetup/checks.py +208 -0
  167. hyperclaw/teamsetup/item.py +391 -0
  168. hyperclaw/teamsetup/oauth_loopback.py +84 -0
  169. hyperclaw/teamsetup/onboard.py +550 -0
  170. hyperclaw/teamsetup/profile.py +82 -0
  171. hyperclaw/teamsetup/secrets.py +313 -0
  172. hyperclaw/teamsetup/selfcheck.py +295 -0
  173. hyperclaw/teamsetup/service.py +495 -0
  174. hyperclaw/teamsetup/slack_oauth.py +442 -0
  175. hyperclaw/teamsetup/tty.py +62 -0
  176. hyperclaw/telegram_bot.py +1106 -0
  177. hyperclaw/telegram_intake.py +126 -0
  178. hyperclaw/telegram_pairing.py +360 -0
  179. hyperclaw/terminal_provider.py +323 -0
  180. hyperclaw/tools/__init__.py +1 -0
  181. hyperclaw/tools/approval.py +949 -0
  182. hyperclaw/tools/args.py +212 -0
  183. hyperclaw/tools/catalog.py +244 -0
  184. hyperclaw/tools/classify.py +266 -0
  185. hyperclaw/tools/effects.py +724 -0
  186. hyperclaw/tools/egress.py +210 -0
  187. hyperclaw/tools/envelope.py +188 -0
  188. hyperclaw/tools/guard.py +367 -0
  189. hyperclaw/tools/ledger.py +339 -0
  190. hyperclaw/tools/receipts.py +70 -0
  191. hyperclaw/tools/registry.py +318 -0
  192. hyperclaw/tools/service_guard.py +151 -0
  193. hyperclaw/tools/surfaces.py +54 -0
  194. hyperclaw/tools/workspace.py +87 -0
  195. hyperclaw/trading/__init__.py +77 -0
  196. hyperclaw/truth_guard.py +791 -0
  197. hyperclaw/tts.py +124 -0
  198. hyperclaw/tui.py +4255 -0
  199. hyperclaw/tui_bridge.py +938 -0
  200. hyperclaw/unattended.py +60 -0
  201. hyperclaw/updates/__init__.py +54 -0
  202. hyperclaw/updates/check.py +131 -0
  203. hyperclaw/updates/index.py +178 -0
  204. hyperclaw/updates/install.py +110 -0
  205. hyperclaw/updates/managed.py +161 -0
  206. hyperclaw/updates/manifest.py +138 -0
  207. hyperclaw/updates/notices.py +86 -0
  208. hyperclaw/updates/paths.py +53 -0
  209. hyperclaw/updates/release.py +146 -0
  210. hyperclaw/updates/upgrade.py +253 -0
  211. hyperclaw/updates/versioning.py +104 -0
  212. hyperclaw/userconfig.py +119 -0
  213. hyperclaw/xai.py +83 -0
  214. integrations/__init__.py +31 -0
  215. integrations/automation/__init__.py +1 -0
  216. integrations/automation/make.py +87 -0
  217. integrations/automation/zapier.py +87 -0
  218. integrations/base.py +476 -0
  219. integrations/communication/__init__.py +1 -0
  220. integrations/communication/twilio.py +279 -0
  221. integrations/communication/zoom.py +290 -0
  222. integrations/data/__init__.py +1 -0
  223. integrations/data/google_analytics.py +135 -0
  224. integrations/data/postgres.py +182 -0
  225. integrations/data/supabase_connector.py +202 -0
  226. integrations/data/weather_feeds.py +450 -0
  227. integrations/developer/__init__.py +9 -0
  228. integrations/developer/github.py +406 -0
  229. integrations/developer/gitlab.py +298 -0
  230. integrations/enterprise/__init__.py +13 -0
  231. integrations/enterprise/confluence.py +266 -0
  232. integrations/enterprise/hubspot.py +293 -0
  233. integrations/enterprise/jira.py +314 -0
  234. integrations/enterprise/salesforce.py +286 -0
  235. integrations/finance/__init__.py +1 -0
  236. integrations/finance/polymarket_client.py +247 -0
  237. integrations/finance/quickbooks.py +281 -0
  238. integrations/finance/stripe.py +261 -0
  239. integrations/gateway.py +238 -0
  240. integrations/messaging/__init__.py +32 -0
  241. integrations/messaging/discord.py +159 -0
  242. integrations/messaging/email.py +401 -0
  243. integrations/messaging/imessage.py +443 -0
  244. integrations/messaging/signal.py +175 -0
  245. integrations/messaging/slack.py +217 -0
  246. integrations/messaging/sms.py +175 -0
  247. integrations/messaging/teams.py +208 -0
  248. integrations/messaging/telegram.py +260 -0
  249. integrations/messaging/whatsapp.py +203 -0
  250. integrations/productivity/__init__.py +17 -0
  251. integrations/productivity/airtable.py +217 -0
  252. integrations/productivity/asana.py +248 -0
  253. integrations/productivity/google/__init__.py +19 -0
  254. integrations/productivity/google/base.py +89 -0
  255. integrations/productivity/google/calendar.py +272 -0
  256. integrations/productivity/google/docs.py +202 -0
  257. integrations/productivity/google/drive.py +268 -0
  258. integrations/productivity/google/gmail.py +279 -0
  259. integrations/productivity/google/meet.py +218 -0
  260. integrations/productivity/google/sheets.py +222 -0
  261. integrations/productivity/google/tasks.py +199 -0
  262. integrations/productivity/linear.py +279 -0
  263. integrations/productivity/microsoft/__init__.py +13 -0
  264. integrations/productivity/microsoft/base.py +93 -0
  265. integrations/productivity/microsoft/calendar.py +207 -0
  266. integrations/productivity/microsoft/onedrive.py +222 -0
  267. integrations/productivity/microsoft/outlook.py +241 -0
  268. integrations/productivity/microsoft/sharepoint.py +211 -0
  269. integrations/productivity/notion.py +243 -0
  270. integrations/productivity/todoist.py +250 -0
  271. integrations/productivity/trello.py +244 -0
  272. integrations/storage/__init__.py +1 -0
  273. integrations/storage/box.py +279 -0
  274. integrations/storage/dropbox.py +215 -0
  275. integrations/telegram_inbound.py +179 -0
  276. integrations/telegram_poller.py +151 -0
  277. memory/__init__.py +1 -0
  278. memory/causal_graph.py +125 -0
  279. memory/impact_tracker.py +88 -0
  280. memory/vector_memory.py +436 -0
  281. models/__init__.py +12 -0
  282. models/chatjimmy_client.py +210 -0
  283. models/claude_client.py +235 -0
  284. models/claude_code_subagent.py +192 -0
  285. models/openai_client.py +120 -0
  286. models/router.py +162 -0
  287. recursive/discovery_loop.py +118 -0
  288. recursive/optimization_loop.py +135 -0
  289. recursive/validation_loop.py +38 -0
  290. security/__init__.py +1 -0
  291. security/audit_logger.py +106 -0
  292. security/filesystem_guard.py +100 -0
  293. security/hypershield.py +99 -0
  294. security/network_guard.py +97 -0
  295. security/policy_engine.py +198 -0
  296. swarm/__init__.py +1 -0
  297. swarm/agents/__init__.py +0 -0
  298. swarm/agents/base.py +230 -0
  299. swarm/agents/business/__init__.py +0 -0
  300. swarm/agents/business/counsel.py +31 -0
  301. swarm/agents/business/herald.py +32 -0
  302. swarm/agents/business/ledger.py +26 -0
  303. swarm/agents/business/nexus.py +30 -0
  304. swarm/agents/business/ops.py +30 -0
  305. swarm/agents/business/pipeline.py +28 -0
  306. swarm/agents/business/revenue.py +30 -0
  307. swarm/agents/business/sovereign.py +31 -0
  308. swarm/agents/business/strategos.py +27 -0
  309. swarm/agents/business/talent.py +27 -0
  310. swarm/agents/business/venture.py +32 -0
  311. swarm/agents/comms/__init__.py +1 -0
  312. swarm/agents/comms/cipher.py +32 -0
  313. swarm/agents/comms/echo_comms.py +31 -0
  314. swarm/agents/comms/envoy.py +31 -0
  315. swarm/agents/comms/herald_scheduler.py +33 -0
  316. swarm/agents/comms/pulse.py +29 -0
  317. swarm/agents/creative/__init__.py +0 -0
  318. swarm/agents/creative/author.py +27 -0
  319. swarm/agents/creative/lens.py +25 -0
  320. swarm/agents/executive/__init__.py +1 -0
  321. swarm/agents/intelligence/__init__.py +1 -0
  322. swarm/agents/intelligence/arbiter.py +30 -0
  323. swarm/agents/intelligence/sentinel.py +30 -0
  324. swarm/agents/media/__init__.py +0 -0
  325. swarm/agents/personal/__init__.py +0 -0
  326. swarm/agents/personal/atlas.py +27 -0
  327. swarm/agents/personal/hearth.py +26 -0
  328. swarm/agents/personal/midas.py +27 -0
  329. swarm/agents/personal/navigator.py +27 -0
  330. swarm/agents/personal/nourish.py +31 -0
  331. swarm/agents/personal/vitals.py +33 -0
  332. swarm/agents/recursive/__init__.py +0 -0
  333. swarm/agents/recursive/alchemist.py +73 -0
  334. swarm/agents/recursive/calibrator.py +45 -0
  335. swarm/agents/recursive/scout.py +97 -0
  336. swarm/agents/scientific/__init__.py +0 -0
  337. swarm/agents/scientific/cosmos.py +36 -0
  338. swarm/agents/scientific/gaia.py +26 -0
  339. swarm/agents/scientific/medicus.py +33 -0
  340. swarm/agents/scientific/oracle_agent.py +39 -0
  341. swarm/agents/scientific/scribe.py +39 -0
  342. swarm/agents/talent/__init__.py +0 -0
  343. swarm/agents/talent/deal.py +28 -0
  344. swarm/agents/talent/roster.py +29 -0
  345. swarm/agents/talent/scout.py +29 -0
  346. swarm/agents/talent/stage.py +27 -0
  347. swarm/agents/tech/__init__.py +0 -0
  348. swarm/agents/tech/aegis.py +28 -0
  349. swarm/agents/tech/bridge.py +28 -0
  350. swarm/agents/tech/forge.py +28 -0
  351. swarm/agents/trading/__init__.py +1 -0
  352. swarm/agents/trading/global_prediction_engine.py +611 -0
  353. swarm/agents/trading/polymarket_trader.py +165 -0
  354. swarm/agents/trading/prediction_strategist.py +218 -0
  355. swarm/autogen_bridge.py +68 -0
  356. swarm/bid_protocol.py +196 -0
  357. swarm/dispatcher.py +527 -0
  358. swarm/nexus.py +181 -0
  359. swarm/registry.py +112 -0
  360. telegram_listener.py +403 -0
  361. ui/tui/app.py +214 -0
@@ -0,0 +1,287 @@
1
+ """
2
+ Relevance ranking for retrieved civilization nodes.
3
+ Provides multi-factor ranking beyond simple vector similarity.
4
+ """
5
+ from __future__ import annotations
6
+ import logging
7
+ from dataclasses import dataclass, field
8
+ from datetime import datetime
9
+ from typing import Any, Callable
10
+ from uuid import UUID
11
+
12
+ from ..schema import CivilizationNode, NodeType, NodeStatus
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ @dataclass
18
+ class RankingFactors:
19
+ """Individual factors contributing to ranking score."""
20
+ semantic_score: float = 0.0 # Vector similarity
21
+ recency_score: float = 0.0 # How recently updated
22
+ status_score: float = 0.0 # Node status (active > deprecated)
23
+ type_match_score: float = 0.0 # How well type matches query intent
24
+ popularity_score: float = 0.0 # Usage/reference frequency
25
+ owner_score: float = 0.0 # Whether owner is relevant
26
+ tag_match_score: float = 0.0 # Tag overlap with query
27
+
28
+
29
+ @dataclass
30
+ class RankingResult:
31
+ """Result of ranking a single node."""
32
+ node: CivilizationNode
33
+ final_score: float
34
+ factors: RankingFactors
35
+ rank: int = 0
36
+ explanation: str = ""
37
+
38
+
39
+ @dataclass
40
+ class RankingConfig:
41
+ """Configuration for relevance ranking."""
42
+ # Factor weights (should sum to ~1.0)
43
+ semantic_weight: float = 0.4
44
+ recency_weight: float = 0.15
45
+ status_weight: float = 0.1
46
+ type_match_weight: float = 0.15
47
+ popularity_weight: float = 0.1
48
+ tag_match_weight: float = 0.1
49
+
50
+ # Recency decay
51
+ recency_half_life_days: int = 30
52
+
53
+ # Type preferences for different query intents
54
+ type_preferences: dict[str, list[NodeType]] = field(default_factory=dict)
55
+
56
+
57
+ class RelevanceRanker:
58
+ """
59
+ Multi-factor relevance ranker for civilization nodes.
60
+ Combines semantic similarity with recency, status, and other factors.
61
+ """
62
+
63
+ DEFAULT_TYPE_PREFERENCES = {
64
+ "how to": [NodeType.SOP, NodeType.CHECKLIST, NodeType.RUNBOOK],
65
+ "procedure": [NodeType.SOP, NodeType.CHECKLIST],
66
+ "incident": [NodeType.RUNBOOK, NodeType.SOP],
67
+ "who is": [NodeType.PERSON, NodeType.ROLE, NodeType.JOB_DESCRIPTION],
68
+ "role": [NodeType.ROLE, NodeType.JOB_DESCRIPTION],
69
+ "policy": [NodeType.POLICY],
70
+ "workflow": [NodeType.WORKFLOW, NodeType.SOP],
71
+ "client": [NodeType.CLIENT_PROFILE],
72
+ "organization": [NodeType.ORG_CHART, NodeType.ROLE],
73
+ }
74
+
75
+ STATUS_SCORES = {
76
+ NodeStatus.ACTIVE: 1.0,
77
+ NodeStatus.UNDER_REVIEW: 0.8,
78
+ NodeStatus.DRAFT: 0.6,
79
+ NodeStatus.DEPRECATED: 0.3,
80
+ NodeStatus.ARCHIVED: 0.1,
81
+ }
82
+
83
+ def __init__(self, config: RankingConfig | None = None):
84
+ self.config = config or RankingConfig()
85
+ self.config.type_preferences = {
86
+ **self.DEFAULT_TYPE_PREFERENCES,
87
+ **self.config.type_preferences,
88
+ }
89
+ self._popularity_cache: dict[UUID, float] = {}
90
+
91
+ def rank(
92
+ self,
93
+ nodes: list[tuple[CivilizationNode, float]], # (node, semantic_score)
94
+ query: str,
95
+ query_tags: list[str] | None = None,
96
+ preferred_types: list[NodeType] | None = None,
97
+ ) -> list[RankingResult]:
98
+ """
99
+ Rank nodes by multi-factor relevance.
100
+
101
+ Args:
102
+ nodes: List of (node, semantic_score) tuples
103
+ query: The original query text
104
+ query_tags: Optional tags to match against
105
+ preferred_types: Optional type preferences
106
+
107
+ Returns:
108
+ Sorted list of RankingResults (highest first)
109
+ """
110
+ results = []
111
+
112
+ # Detect query intent
113
+ detected_types = preferred_types or self._detect_type_intent(query)
114
+
115
+ for node, semantic_score in nodes:
116
+ factors = self._compute_factors(
117
+ node=node,
118
+ semantic_score=semantic_score,
119
+ query=query,
120
+ query_tags=query_tags or [],
121
+ preferred_types=detected_types,
122
+ )
123
+
124
+ final_score = self._compute_final_score(factors)
125
+
126
+ results.append(RankingResult(
127
+ node=node,
128
+ final_score=final_score,
129
+ factors=factors,
130
+ explanation=self._generate_explanation(factors),
131
+ ))
132
+
133
+ # Sort by final score
134
+ results.sort(key=lambda r: r.final_score, reverse=True)
135
+
136
+ # Assign ranks
137
+ for i, result in enumerate(results):
138
+ result.rank = i + 1
139
+
140
+ return results
141
+
142
+ def _compute_factors(
143
+ self,
144
+ node: CivilizationNode,
145
+ semantic_score: float,
146
+ query: str,
147
+ query_tags: list[str],
148
+ preferred_types: list[NodeType],
149
+ ) -> RankingFactors:
150
+ """Compute all ranking factors for a node."""
151
+ return RankingFactors(
152
+ semantic_score=semantic_score,
153
+ recency_score=self._compute_recency_score(node),
154
+ status_score=self._compute_status_score(node),
155
+ type_match_score=self._compute_type_match_score(node, preferred_types),
156
+ popularity_score=self._compute_popularity_score(node),
157
+ tag_match_score=self._compute_tag_match_score(node, query_tags),
158
+ )
159
+
160
+ def _compute_final_score(self, factors: RankingFactors) -> float:
161
+ """Compute weighted final score from factors."""
162
+ cfg = self.config
163
+ return (
164
+ factors.semantic_score * cfg.semantic_weight +
165
+ factors.recency_score * cfg.recency_weight +
166
+ factors.status_score * cfg.status_weight +
167
+ factors.type_match_score * cfg.type_match_weight +
168
+ factors.popularity_score * cfg.popularity_weight +
169
+ factors.tag_match_score * cfg.tag_match_weight
170
+ )
171
+
172
+ def _compute_recency_score(self, node: CivilizationNode) -> float:
173
+ """Compute recency score with exponential decay."""
174
+ import math
175
+ days_old = (datetime.utcnow() - node.updated_at).days
176
+ half_life = self.config.recency_half_life_days
177
+ return math.exp(-0.693 * days_old / half_life)
178
+
179
+ def _compute_status_score(self, node: CivilizationNode) -> float:
180
+ """Get score based on node status."""
181
+ return self.STATUS_SCORES.get(node.status, 0.5)
182
+
183
+ def _compute_type_match_score(
184
+ self,
185
+ node: CivilizationNode,
186
+ preferred_types: list[NodeType],
187
+ ) -> float:
188
+ """Score based on how well type matches preferences."""
189
+ if not preferred_types:
190
+ return 0.5 # Neutral
191
+
192
+ if node.node_type in preferred_types:
193
+ # Higher score for earlier in preference list
194
+ index = preferred_types.index(node.node_type)
195
+ return 1.0 - (index * 0.2) # 1.0, 0.8, 0.6, ...
196
+ return 0.3
197
+
198
+ def _compute_popularity_score(self, node: CivilizationNode) -> float:
199
+ """Get popularity score (cached or computed)."""
200
+ if node.id in self._popularity_cache:
201
+ return self._popularity_cache[node.id]
202
+ # Default to neutral - could be enhanced with usage tracking
203
+ return 0.5
204
+
205
+ def _compute_tag_match_score(
206
+ self,
207
+ node: CivilizationNode,
208
+ query_tags: list[str],
209
+ ) -> float:
210
+ """Score based on tag overlap."""
211
+ if not query_tags or not node.tags:
212
+ return 0.5 # Neutral
213
+
214
+ node_tags_lower = set(t.lower() for t in node.tags)
215
+ query_tags_lower = set(t.lower() for t in query_tags)
216
+
217
+ overlap = len(node_tags_lower & query_tags_lower)
218
+ max_possible = min(len(node_tags_lower), len(query_tags_lower))
219
+
220
+ if max_possible == 0:
221
+ return 0.5
222
+ return overlap / max_possible
223
+
224
+ def _detect_type_intent(self, query: str) -> list[NodeType]:
225
+ """Detect preferred node types from query text."""
226
+ query_lower = query.lower()
227
+
228
+ for pattern, types in self.config.type_preferences.items():
229
+ if pattern in query_lower:
230
+ return types
231
+
232
+ return [] # No specific preference
233
+
234
+ def _generate_explanation(self, factors: RankingFactors) -> str:
235
+ """Generate human-readable explanation of ranking."""
236
+ parts = []
237
+
238
+ if factors.semantic_score > 0.8:
239
+ parts.append("high semantic match")
240
+ elif factors.semantic_score > 0.6:
241
+ parts.append("moderate semantic match")
242
+
243
+ if factors.recency_score > 0.8:
244
+ parts.append("recently updated")
245
+ elif factors.recency_score < 0.3:
246
+ parts.append("may be outdated")
247
+
248
+ if factors.type_match_score > 0.8:
249
+ parts.append("matches expected type")
250
+
251
+ if factors.status_score < 0.5:
252
+ parts.append("non-active status")
253
+
254
+ if factors.tag_match_score > 0.7:
255
+ parts.append("matching tags")
256
+
257
+ return ", ".join(parts) if parts else "standard relevance"
258
+
259
+ def update_popularity(self, node_id: UUID, score: float) -> None:
260
+ """Update popularity score for a node."""
261
+ self._popularity_cache[node_id] = min(1.0, max(0.0, score))
262
+
263
+ def boost_node(
264
+ self,
265
+ results: list[RankingResult],
266
+ node_id: UUID,
267
+ boost_factor: float = 1.5,
268
+ ) -> list[RankingResult]:
269
+ """Boost a specific node's ranking."""
270
+ for result in results:
271
+ if result.node.id == node_id:
272
+ result.final_score *= boost_factor
273
+
274
+ # Re-sort and re-rank
275
+ results.sort(key=lambda r: r.final_score, reverse=True)
276
+ for i, result in enumerate(results):
277
+ result.rank = i + 1
278
+
279
+ return results
280
+
281
+ def filter_by_threshold(
282
+ self,
283
+ results: list[RankingResult],
284
+ min_score: float = 0.3,
285
+ ) -> list[RankingResult]:
286
+ """Filter results below a score threshold."""
287
+ return [r for r in results if r.final_score >= min_score]
civilization/schema.py ADDED
@@ -0,0 +1,299 @@
1
+ """
2
+ Pydantic v2 models for all Civilization Knowledge Layer node types.
3
+ Every node has: id, org_id, version, created_at, updated_at, embedding.
4
+ """
5
+ from __future__ import annotations
6
+ from enum import Enum
7
+ from pydantic import BaseModel, Field, field_validator
8
+ from uuid import UUID, uuid4
9
+ from datetime import datetime
10
+ from typing import Any
11
+
12
+
13
+ class NodeType(str, Enum):
14
+ SOP = "sop"
15
+ JOB_DESCRIPTION = "job_description"
16
+ CHECKLIST = "checklist"
17
+ RUNBOOK = "runbook"
18
+ WORKFLOW = "workflow"
19
+ ORG_CHART = "org_chart"
20
+ ROLE = "role"
21
+ PERSON = "person"
22
+ CLIENT_PROFILE = "client_profile"
23
+ PERSONAL_ROUTINE = "personal_routine"
24
+ POLICY = "policy"
25
+ KNOWLEDGE_ARTICLE = "knowledge_article"
26
+
27
+
28
+ class NodeStatus(str, Enum):
29
+ DRAFT = "draft"
30
+ ACTIVE = "active"
31
+ UNDER_REVIEW = "under_review"
32
+ DEPRECATED = "deprecated"
33
+ ARCHIVED = "archived"
34
+
35
+
36
+ class CivilizationNode(BaseModel):
37
+ id: UUID = Field(default_factory=uuid4)
38
+ org_id: str
39
+ node_type: NodeType
40
+ title: str
41
+ status: NodeStatus = NodeStatus.ACTIVE
42
+ version: str = "1.0.0"
43
+ owner_id: str | None = None
44
+ tags: list[str] = []
45
+ source: str | None = None
46
+ source_url: str | None = None
47
+ embedding: list[float] | None = None
48
+ metadata: dict[str, Any] = {}
49
+ created_at: datetime = Field(default_factory=datetime.utcnow)
50
+ updated_at: datetime = Field(default_factory=datetime.utcnow)
51
+
52
+
53
+ class SOPStep(BaseModel):
54
+ step_number: int
55
+ title: str
56
+ description: str
57
+ responsible_role: str | None = None
58
+ tools_required: list[str] = []
59
+ estimated_duration_minutes: int | None = None
60
+ decision_points: list[str] = []
61
+ on_failure: str | None = None
62
+ substeps: list["SOPStep"] = []
63
+
64
+
65
+ class SOP(CivilizationNode):
66
+ node_type: NodeType = NodeType.SOP
67
+ purpose: str
68
+ scope: str
69
+ triggers: list[str] = []
70
+ steps: list[SOPStep]
71
+ roles_involved: list[str] = []
72
+ tools_required: list[str] = []
73
+ estimated_total_minutes: int | None = None
74
+ success_criteria: list[str] = []
75
+ related_sops: list[UUID] = []
76
+ kpis: list[str] = []
77
+
78
+
79
+ class JobDescription(CivilizationNode):
80
+ node_type: NodeType = NodeType.JOB_DESCRIPTION
81
+ role_title: str
82
+ department: str
83
+ reports_to: str | None = None
84
+ direct_reports: list[str] = []
85
+ level: str | None = None
86
+ employment_type: str = "full_time"
87
+ summary: str
88
+ responsibilities: list[str]
89
+ required_skills: list[str]
90
+ preferred_skills: list[str] = []
91
+ required_experience_years: int | None = None
92
+ tools_used: list[str] = []
93
+ kpis: list[str] = []
94
+ okrs: list[str] = []
95
+ success_metrics: list[str] = []
96
+ compensation_band: str | None = None
97
+ related_roles: list[UUID] = []
98
+
99
+
100
+ class Role(CivilizationNode):
101
+ node_type: NodeType = NodeType.ROLE
102
+ role_title: str
103
+ department: str
104
+ accountabilities: list[str]
105
+ responsibilities: list[str]
106
+ decision_authority: list[str]
107
+ escalation_path: list[str]
108
+ interfaces: list[str]
109
+ tools: list[str] = []
110
+ sops_owned: list[UUID] = []
111
+ checklists_owned: list[UUID] = []
112
+
113
+
114
+ class Person(CivilizationNode):
115
+ node_type: NodeType = NodeType.PERSON
116
+ name: str
117
+ email: str | None = None
118
+ role_id: UUID | None = None
119
+ manager_id: UUID | None = None
120
+ direct_report_ids: list[UUID] = []
121
+ department: str | None = None
122
+ location: str | None = None
123
+ timezone: str | None = None
124
+ communication_preferences: dict = {}
125
+ expertise_areas: list[str] = []
126
+ current_projects: list[str] = []
127
+ personal_routine_id: UUID | None = None
128
+
129
+
130
+ class ChecklistItem(BaseModel):
131
+ item_number: int
132
+ description: str
133
+ required: bool = True
134
+ verification_method: str | None = None
135
+ responsible_role: str | None = None
136
+ time_estimate_minutes: int | None = None
137
+ notes: str | None = None
138
+
139
+
140
+ class Checklist(CivilizationNode):
141
+ node_type: NodeType = NodeType.CHECKLIST
142
+ purpose: str
143
+ frequency: str | None = None
144
+ trigger: str | None = None
145
+ items: list[ChecklistItem]
146
+ roles_involved: list[str] = []
147
+ estimated_total_minutes: int | None = None
148
+ related_sop_id: UUID | None = None
149
+
150
+
151
+ class RunbookStep(BaseModel):
152
+ step_number: int
153
+ action: str
154
+ command: str | None = None
155
+ expected_output: str | None = None
156
+ on_success: str | None = None
157
+ on_failure: str | None = None
158
+ rollback: str | None = None
159
+
160
+
161
+ class Runbook(CivilizationNode):
162
+ node_type: NodeType = NodeType.RUNBOOK
163
+ system: str
164
+ scenario: str
165
+ severity: str | None = None
166
+ prerequisites: list[str] = []
167
+ steps: list[RunbookStep]
168
+ rollback_procedure: list[str] = []
169
+ escalation_contacts: list[str] = []
170
+ post_incident_actions: list[str] = []
171
+
172
+
173
+ class WorkflowNode(BaseModel):
174
+ node_id: str
175
+ label: str
176
+ node_type: str # "start"|"end"|"task"|"decision"|"approval"
177
+ responsible_role: str | None = None
178
+ tool: str | None = None
179
+ description: str | None = None
180
+
181
+
182
+ class WorkflowEdge(BaseModel):
183
+ from_node: str
184
+ to_node: str
185
+ condition: str | None = None
186
+
187
+
188
+ class Workflow(CivilizationNode):
189
+ node_type: NodeType = NodeType.WORKFLOW
190
+ description: str
191
+ department: str | None = None
192
+ nodes: list[WorkflowNode]
193
+ edges: list[WorkflowEdge]
194
+ triggers: list[str] = []
195
+ sla_hours: int | None = None
196
+ related_sop_id: UUID | None = None
197
+
198
+
199
+ class OrgUnit(BaseModel):
200
+ unit_id: str
201
+ name: str
202
+ unit_type: str
203
+ head_person_id: UUID | None = None
204
+ parent_unit_id: str | None = None
205
+ member_ids: list[UUID] = []
206
+ mission: str | None = None
207
+ budget_owner: bool = False
208
+
209
+
210
+ class OrgChart(CivilizationNode):
211
+ node_type: NodeType = NodeType.ORG_CHART
212
+ organization_name: str
213
+ units: list[OrgUnit]
214
+ total_headcount: int | None = None
215
+ last_updated: datetime = Field(default_factory=datetime.utcnow)
216
+
217
+
218
+ class ClientProfile(CivilizationNode):
219
+ node_type: NodeType = NodeType.CLIENT_PROFILE
220
+ client_name: str
221
+ industry: str | None = None
222
+ tier: str | None = None
223
+ relationship_owner_id: UUID | None = None
224
+ key_contacts: list[dict] = []
225
+ contract_value_usd: float | None = None
226
+ contract_start: datetime | None = None
227
+ contract_end: datetime | None = None
228
+ health_score: float | None = None
229
+ goals: list[str] = []
230
+ pain_points: list[str] = []
231
+ preferences: dict = {}
232
+ active_projects: list[str] = []
233
+ communication_cadence: str | None = None
234
+ notes: str | None = None
235
+ related_sops: list[UUID] = []
236
+
237
+ @field_validator("health_score")
238
+ @classmethod
239
+ def validate_health_score(cls, v):
240
+ if v is not None and not (0.0 <= v <= 1.0):
241
+ raise ValueError("health_score must be between 0.0 and 1.0")
242
+ return v
243
+
244
+
245
+ class RoutineBlock(BaseModel):
246
+ time: str # "06:00" 24h format
247
+ duration_minutes: int
248
+ activity: str
249
+ category: str
250
+ priority: str = "medium"
251
+ tools: list[str] = []
252
+ notes: str | None = None
253
+
254
+ @field_validator("time")
255
+ @classmethod
256
+ def validate_time_format(cls, v):
257
+ import re
258
+ if not re.match(r"^\d{2}:\d{2}$", v):
259
+ raise ValueError("time must be in HH:MM format")
260
+ return v
261
+
262
+
263
+ class PersonalRoutine(CivilizationNode):
264
+ node_type: NodeType = NodeType.PERSONAL_ROUTINE
265
+ person_id: UUID
266
+ routine_type: str
267
+ day_of_week: list[str] = []
268
+ blocks: list[RoutineBlock]
269
+ goals: list[str] = []
270
+ non_negotiables: list[str] = []
271
+ flexibility_rules: list[str] = []
272
+
273
+
274
+ class Policy(CivilizationNode):
275
+ node_type: NodeType = NodeType.POLICY
276
+ policy_name: str
277
+ category: str
278
+ applies_to: list[str] = []
279
+ effective_date: datetime | None = None
280
+ review_date: datetime | None = None
281
+ summary: str
282
+ full_text: str
283
+ exceptions: list[str] = []
284
+ related_policies: list[UUID] = []
285
+ compliance_requirements: list[str] = []
286
+
287
+
288
+ class KnowledgeArticle(CivilizationNode):
289
+ node_type: NodeType = NodeType.KNOWLEDGE_ARTICLE
290
+ topic: str
291
+ category: str
292
+ summary: str
293
+ content: str
294
+ author_id: UUID | None = None
295
+ reviewers: list[UUID] = []
296
+ related_articles: list[UUID] = []
297
+ related_sops: list[UUID] = []
298
+ faq: list[dict] = []
299
+ last_reviewed: datetime | None = None