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.
- agents/__init__.py +31 -0
- agents/background_monitor.py +453 -0
- agents/learning.py +460 -0
- agents/planner.py +421 -0
- agents/transcriber.py +341 -0
- civilization/__init__.py +51 -0
- civilization/api/__init__.py +7 -0
- civilization/api/civilization_api.py +361 -0
- civilization/graph/__init__.py +19 -0
- civilization/graph/knowledge_linker.py +348 -0
- civilization/graph/org_graph.py +348 -0
- civilization/graph/process_graph.py +386 -0
- civilization/ingestion/__init__.py +20 -0
- civilization/ingestion/chunker.py +273 -0
- civilization/ingestion/document_ingestor.py +358 -0
- civilization/ingestion/embedder.py +262 -0
- civilization/ingestion/extractor.py +224 -0
- civilization/interview/__init__.py +19 -0
- civilization/interview/gap_detector.py +380 -0
- civilization/interview/interviewer.py +323 -0
- civilization/interview/questioner.py +363 -0
- civilization/retrieval/__init__.py +17 -0
- civilization/retrieval/civilization_rag.py +316 -0
- civilization/retrieval/context_injector.py +342 -0
- civilization/retrieval/relevance_ranker.py +287 -0
- civilization/schema.py +299 -0
- civilization/store.py +177 -0
- civilization/sync/__init__.py +16 -0
- civilization/sync/confluence_sync.py +317 -0
- civilization/sync/gdrive_sync.py +262 -0
- civilization/sync/notion_sync.py +237 -0
- civilization/versioning/__init__.py +19 -0
- civilization/versioning/diff_engine.py +351 -0
- civilization/versioning/staleness_detector.py +303 -0
- civilization/versioning/version_manager.py +293 -0
- cli/__init__.py +1 -0
- cli/doctor.py +334 -0
- cli/hyperclaw.py +1357 -0
- cli/onboarding.py +469 -0
- core/__init__.py +1 -0
- core/connectors/__init__.py +119 -0
- core/connectors/infrastructure.py +772 -0
- core/connectors/messaging.py +586 -0
- core/connectors/productivity.py +764 -0
- core/connectors/smarthome.py +528 -0
- core/connectors/social.py +730 -0
- core/hyperrouter/__init__.py +23 -0
- core/hyperrouter/bandit.py +218 -0
- core/hyperrouter/fast_loop.py +68 -0
- core/hyperrouter/slow_loop.py +143 -0
- core/hyperstate/__init__.py +24 -0
- core/hyperstate/certifier.py +154 -0
- core/hyperstate/schema.py +160 -0
- core/hyperstate/state_manager.py +103 -0
- core/hyperstate/store.py +163 -0
- core/providers/__init__.py +121 -0
- core/providers/embeddings.py +389 -0
- core/providers/image.py +534 -0
- core/providers/llm.py +1935 -0
- core/providers/search.py +608 -0
- core/providers/voice.py +474 -0
- hyperagents-1.2.3rc5.post3.dist-info/METADATA +469 -0
- hyperagents-1.2.3rc5.post3.dist-info/RECORD +361 -0
- hyperagents-1.2.3rc5.post3.dist-info/WHEEL +5 -0
- hyperagents-1.2.3rc5.post3.dist-info/entry_points.txt +6 -0
- hyperagents-1.2.3rc5.post3.dist-info/licenses/LICENSE +21 -0
- hyperagents-1.2.3rc5.post3.dist-info/top_level.txt +13 -0
- hyperclaw/__init__.py +67 -0
- hyperclaw/__main__.py +220 -0
- hyperclaw/agent.py +775 -0
- hyperclaw/agent_coordinator.py +577 -0
- hyperclaw/anthropic_params.py +225 -0
- hyperclaw/api_utils.py +85 -0
- hyperclaw/approval_telegram.py +209 -0
- hyperclaw/approval_terminal.py +80 -0
- hyperclaw/attachments.py +177 -0
- hyperclaw/branding.py +55 -0
- hyperclaw/brief/__init__.py +11 -0
- hyperclaw/brief/cli.py +204 -0
- hyperclaw/brief/collect.py +146 -0
- hyperclaw/brief/compose.py +333 -0
- hyperclaw/brief/diff.py +110 -0
- hyperclaw/brief/errors.py +99 -0
- hyperclaw/brief/google.py +269 -0
- hyperclaw/brief/google_auth.py +294 -0
- hyperclaw/brief/links.py +124 -0
- hyperclaw/brief/service.py +424 -0
- hyperclaw/brief/store.py +205 -0
- hyperclaw/brief/tasks.py +200 -0
- hyperclaw/chat_scope.py +115 -0
- hyperclaw/cli_tui.py +497 -0
- hyperclaw/client_guard.py +717 -0
- hyperclaw/connection_check.py +113 -0
- hyperclaw/cost_guard.py +1728 -0
- hyperclaw/default_config/agents.yaml +534 -0
- hyperclaw/default_config/hyperclaw.yaml +27 -0
- hyperclaw/default_config/models.yaml +161 -0
- hyperclaw/deployment.py +39 -0
- hyperclaw/digest/__init__.py +9 -0
- hyperclaw/digest/cli.py +117 -0
- hyperclaw/digest/errors.py +100 -0
- hyperclaw/digest/gitlab.py +187 -0
- hyperclaw/digest/http.py +113 -0
- hyperclaw/digest/redact.py +32 -0
- hyperclaw/digest/service.py +271 -0
- hyperclaw/digest/slack.py +223 -0
- hyperclaw/digest/state.py +124 -0
- hyperclaw/digest/summarize.py +198 -0
- hyperclaw/digest/tokens.py +143 -0
- hyperclaw/distribution.py +46 -0
- hyperclaw/documents.py +351 -0
- hyperclaw/feeds.py +339 -0
- hyperclaw/google_workspace/__init__.py +5 -0
- hyperclaw/google_workspace/api.py +151 -0
- hyperclaw/google_workspace/auth.py +397 -0
- hyperclaw/google_workspace/calendar.py +157 -0
- hyperclaw/google_workspace/cli.py +72 -0
- hyperclaw/google_workspace/drive.py +236 -0
- hyperclaw/google_workspace/errors.py +58 -0
- hyperclaw/google_workspace/gmail.py +295 -0
- hyperclaw/google_workspace/meet.py +122 -0
- hyperclaw/google_workspace/tools.py +319 -0
- hyperclaw/hyperspeed.py +57 -0
- hyperclaw/integrations_layer.py +1427 -0
- hyperclaw/live_state.py +188 -0
- hyperclaw/managed_config.py +245 -0
- hyperclaw/media_hub.py +179 -0
- hyperclaw/memory_manager.py +584 -0
- hyperclaw/model_catalog.py +551 -0
- hyperclaw/model_policy.py +211 -0
- hyperclaw/model_router.py +595 -0
- hyperclaw/model_selector.py +72 -0
- hyperclaw/naming.py +284 -0
- hyperclaw/notices.py +65 -0
- hyperclaw/oai_stream.py +106 -0
- hyperclaw/orchestrator.py +591 -0
- hyperclaw/outbox.py +82 -0
- hyperclaw/persona.py +367 -0
- hyperclaw/prometheus.py +219 -0
- hyperclaw/prompt_assembly.py +157 -0
- hyperclaw/provider_setup.py +146 -0
- hyperclaw/providers.py +340 -0
- hyperclaw/qualification.py +341 -0
- hyperclaw/qualification_keys/hyper-d292fd127cef5a3e.pub +1 -0
- hyperclaw/qualification_records/anthropic--claude-sonnet-5-5.json +14 -0
- hyperclaw/reasoning_display.py +35 -0
- hyperclaw/scheduler.py +454 -0
- hyperclaw/server.py +847 -0
- hyperclaw/setup.py +627 -0
- hyperclaw/skills.py +717 -0
- hyperclaw/solomon.py +442 -0
- hyperclaw/swarm.py +533 -0
- hyperclaw/taint.py +832 -0
- hyperclaw/team/__init__.py +28 -0
- hyperclaw/team/dispatcher.py +431 -0
- hyperclaw/team/genesis.py +106 -0
- hyperclaw/team/lead.py +110 -0
- hyperclaw/team/model.py +135 -0
- hyperclaw/team/morning.py +238 -0
- hyperclaw/team/repos.py +66 -0
- hyperclaw/team/roster.py +264 -0
- hyperclaw/team/specialist.py +235 -0
- hyperclaw/team/status.py +163 -0
- hyperclaw/team/watchers.py +112 -0
- hyperclaw/teamsetup/__init__.py +17 -0
- hyperclaw/teamsetup/checks.py +208 -0
- hyperclaw/teamsetup/item.py +391 -0
- hyperclaw/teamsetup/oauth_loopback.py +84 -0
- hyperclaw/teamsetup/onboard.py +550 -0
- hyperclaw/teamsetup/profile.py +82 -0
- hyperclaw/teamsetup/secrets.py +313 -0
- hyperclaw/teamsetup/selfcheck.py +295 -0
- hyperclaw/teamsetup/service.py +495 -0
- hyperclaw/teamsetup/slack_oauth.py +442 -0
- hyperclaw/teamsetup/tty.py +62 -0
- hyperclaw/telegram_bot.py +1106 -0
- hyperclaw/telegram_intake.py +126 -0
- hyperclaw/telegram_pairing.py +360 -0
- hyperclaw/terminal_provider.py +323 -0
- hyperclaw/tools/__init__.py +1 -0
- hyperclaw/tools/approval.py +949 -0
- hyperclaw/tools/args.py +212 -0
- hyperclaw/tools/catalog.py +244 -0
- hyperclaw/tools/classify.py +266 -0
- hyperclaw/tools/effects.py +724 -0
- hyperclaw/tools/egress.py +210 -0
- hyperclaw/tools/envelope.py +188 -0
- hyperclaw/tools/guard.py +367 -0
- hyperclaw/tools/ledger.py +339 -0
- hyperclaw/tools/receipts.py +70 -0
- hyperclaw/tools/registry.py +318 -0
- hyperclaw/tools/service_guard.py +151 -0
- hyperclaw/tools/surfaces.py +54 -0
- hyperclaw/tools/workspace.py +87 -0
- hyperclaw/trading/__init__.py +77 -0
- hyperclaw/truth_guard.py +791 -0
- hyperclaw/tts.py +124 -0
- hyperclaw/tui.py +4255 -0
- hyperclaw/tui_bridge.py +938 -0
- hyperclaw/unattended.py +60 -0
- hyperclaw/updates/__init__.py +54 -0
- hyperclaw/updates/check.py +131 -0
- hyperclaw/updates/index.py +178 -0
- hyperclaw/updates/install.py +110 -0
- hyperclaw/updates/managed.py +161 -0
- hyperclaw/updates/manifest.py +138 -0
- hyperclaw/updates/notices.py +86 -0
- hyperclaw/updates/paths.py +53 -0
- hyperclaw/updates/release.py +146 -0
- hyperclaw/updates/upgrade.py +253 -0
- hyperclaw/updates/versioning.py +104 -0
- hyperclaw/userconfig.py +119 -0
- hyperclaw/xai.py +83 -0
- integrations/__init__.py +31 -0
- integrations/automation/__init__.py +1 -0
- integrations/automation/make.py +87 -0
- integrations/automation/zapier.py +87 -0
- integrations/base.py +476 -0
- integrations/communication/__init__.py +1 -0
- integrations/communication/twilio.py +279 -0
- integrations/communication/zoom.py +290 -0
- integrations/data/__init__.py +1 -0
- integrations/data/google_analytics.py +135 -0
- integrations/data/postgres.py +182 -0
- integrations/data/supabase_connector.py +202 -0
- integrations/data/weather_feeds.py +450 -0
- integrations/developer/__init__.py +9 -0
- integrations/developer/github.py +406 -0
- integrations/developer/gitlab.py +298 -0
- integrations/enterprise/__init__.py +13 -0
- integrations/enterprise/confluence.py +266 -0
- integrations/enterprise/hubspot.py +293 -0
- integrations/enterprise/jira.py +314 -0
- integrations/enterprise/salesforce.py +286 -0
- integrations/finance/__init__.py +1 -0
- integrations/finance/polymarket_client.py +247 -0
- integrations/finance/quickbooks.py +281 -0
- integrations/finance/stripe.py +261 -0
- integrations/gateway.py +238 -0
- integrations/messaging/__init__.py +32 -0
- integrations/messaging/discord.py +159 -0
- integrations/messaging/email.py +401 -0
- integrations/messaging/imessage.py +443 -0
- integrations/messaging/signal.py +175 -0
- integrations/messaging/slack.py +217 -0
- integrations/messaging/sms.py +175 -0
- integrations/messaging/teams.py +208 -0
- integrations/messaging/telegram.py +260 -0
- integrations/messaging/whatsapp.py +203 -0
- integrations/productivity/__init__.py +17 -0
- integrations/productivity/airtable.py +217 -0
- integrations/productivity/asana.py +248 -0
- integrations/productivity/google/__init__.py +19 -0
- integrations/productivity/google/base.py +89 -0
- integrations/productivity/google/calendar.py +272 -0
- integrations/productivity/google/docs.py +202 -0
- integrations/productivity/google/drive.py +268 -0
- integrations/productivity/google/gmail.py +279 -0
- integrations/productivity/google/meet.py +218 -0
- integrations/productivity/google/sheets.py +222 -0
- integrations/productivity/google/tasks.py +199 -0
- integrations/productivity/linear.py +279 -0
- integrations/productivity/microsoft/__init__.py +13 -0
- integrations/productivity/microsoft/base.py +93 -0
- integrations/productivity/microsoft/calendar.py +207 -0
- integrations/productivity/microsoft/onedrive.py +222 -0
- integrations/productivity/microsoft/outlook.py +241 -0
- integrations/productivity/microsoft/sharepoint.py +211 -0
- integrations/productivity/notion.py +243 -0
- integrations/productivity/todoist.py +250 -0
- integrations/productivity/trello.py +244 -0
- integrations/storage/__init__.py +1 -0
- integrations/storage/box.py +279 -0
- integrations/storage/dropbox.py +215 -0
- integrations/telegram_inbound.py +179 -0
- integrations/telegram_poller.py +151 -0
- memory/__init__.py +1 -0
- memory/causal_graph.py +125 -0
- memory/impact_tracker.py +88 -0
- memory/vector_memory.py +436 -0
- models/__init__.py +12 -0
- models/chatjimmy_client.py +210 -0
- models/claude_client.py +235 -0
- models/claude_code_subagent.py +192 -0
- models/openai_client.py +120 -0
- models/router.py +162 -0
- recursive/discovery_loop.py +118 -0
- recursive/optimization_loop.py +135 -0
- recursive/validation_loop.py +38 -0
- security/__init__.py +1 -0
- security/audit_logger.py +106 -0
- security/filesystem_guard.py +100 -0
- security/hypershield.py +99 -0
- security/network_guard.py +97 -0
- security/policy_engine.py +198 -0
- swarm/__init__.py +1 -0
- swarm/agents/__init__.py +0 -0
- swarm/agents/base.py +230 -0
- swarm/agents/business/__init__.py +0 -0
- swarm/agents/business/counsel.py +31 -0
- swarm/agents/business/herald.py +32 -0
- swarm/agents/business/ledger.py +26 -0
- swarm/agents/business/nexus.py +30 -0
- swarm/agents/business/ops.py +30 -0
- swarm/agents/business/pipeline.py +28 -0
- swarm/agents/business/revenue.py +30 -0
- swarm/agents/business/sovereign.py +31 -0
- swarm/agents/business/strategos.py +27 -0
- swarm/agents/business/talent.py +27 -0
- swarm/agents/business/venture.py +32 -0
- swarm/agents/comms/__init__.py +1 -0
- swarm/agents/comms/cipher.py +32 -0
- swarm/agents/comms/echo_comms.py +31 -0
- swarm/agents/comms/envoy.py +31 -0
- swarm/agents/comms/herald_scheduler.py +33 -0
- swarm/agents/comms/pulse.py +29 -0
- swarm/agents/creative/__init__.py +0 -0
- swarm/agents/creative/author.py +27 -0
- swarm/agents/creative/lens.py +25 -0
- swarm/agents/executive/__init__.py +1 -0
- swarm/agents/intelligence/__init__.py +1 -0
- swarm/agents/intelligence/arbiter.py +30 -0
- swarm/agents/intelligence/sentinel.py +30 -0
- swarm/agents/media/__init__.py +0 -0
- swarm/agents/personal/__init__.py +0 -0
- swarm/agents/personal/atlas.py +27 -0
- swarm/agents/personal/hearth.py +26 -0
- swarm/agents/personal/midas.py +27 -0
- swarm/agents/personal/navigator.py +27 -0
- swarm/agents/personal/nourish.py +31 -0
- swarm/agents/personal/vitals.py +33 -0
- swarm/agents/recursive/__init__.py +0 -0
- swarm/agents/recursive/alchemist.py +73 -0
- swarm/agents/recursive/calibrator.py +45 -0
- swarm/agents/recursive/scout.py +97 -0
- swarm/agents/scientific/__init__.py +0 -0
- swarm/agents/scientific/cosmos.py +36 -0
- swarm/agents/scientific/gaia.py +26 -0
- swarm/agents/scientific/medicus.py +33 -0
- swarm/agents/scientific/oracle_agent.py +39 -0
- swarm/agents/scientific/scribe.py +39 -0
- swarm/agents/talent/__init__.py +0 -0
- swarm/agents/talent/deal.py +28 -0
- swarm/agents/talent/roster.py +29 -0
- swarm/agents/talent/scout.py +29 -0
- swarm/agents/talent/stage.py +27 -0
- swarm/agents/tech/__init__.py +0 -0
- swarm/agents/tech/aegis.py +28 -0
- swarm/agents/tech/bridge.py +28 -0
- swarm/agents/tech/forge.py +28 -0
- swarm/agents/trading/__init__.py +1 -0
- swarm/agents/trading/global_prediction_engine.py +611 -0
- swarm/agents/trading/polymarket_trader.py +165 -0
- swarm/agents/trading/prediction_strategist.py +218 -0
- swarm/autogen_bridge.py +68 -0
- swarm/bid_protocol.py +196 -0
- swarm/dispatcher.py +527 -0
- swarm/nexus.py +181 -0
- swarm/registry.py +112 -0
- telegram_listener.py +403 -0
- 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
|