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,211 @@
|
|
|
1
|
+
"""Model policy for HyperAgents (Vaughn, 2026-10-12; docs/MODEL-POLICY.md).
|
|
2
|
+
|
|
3
|
+
- HyperSpeed V2 is the default and recommended model. HyperAgents may also run
|
|
4
|
+
Claude, OpenAI or Grok (xAI) models, on every install; a Hyper-deployed
|
|
5
|
+
install only the models on its allowed list (hyperclaw.managed_config).
|
|
6
|
+
- Labels: Hyper's models by name ("HyperSpeed V2"); a model on a key Hyper
|
|
7
|
+
issued "<model name>, via Hyper"; a model on a key the user added
|
|
8
|
+
"your own provider (<provider>)".
|
|
9
|
+
- Consent: choosing a third-party model shows exactly one line,
|
|
10
|
+
CONSENT_LINE, and needs the owner's yes, recorded (time, provider, model)
|
|
11
|
+
in an owner-only file. No consent, no switch, and no automatic fallback to
|
|
12
|
+
that provider.
|
|
13
|
+
- Honesty: which model is answering is said at start-up, in /model, on every
|
|
14
|
+
switch and on every automatic fallback (one line naming the new model and
|
|
15
|
+
why).
|
|
16
|
+
- Keys: a key that looks like a RunPod key (rpa_) is never accepted for any
|
|
17
|
+
provider.
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import datetime as _dt
|
|
22
|
+
import json
|
|
23
|
+
import logging
|
|
24
|
+
import os
|
|
25
|
+
import re
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from typing import Optional
|
|
28
|
+
|
|
29
|
+
log = logging.getLogger("hyperclaw.model_policy")
|
|
30
|
+
|
|
31
|
+
CONSENT_LINE = "Messages, emails and documents you share will be processed by {provider}."
|
|
32
|
+
CONSENT_FILE = "model_consent.json"
|
|
33
|
+
|
|
34
|
+
# The provider named in the consent line.
|
|
35
|
+
PROVIDER_NAMES = {
|
|
36
|
+
"anthropic": "Anthropic",
|
|
37
|
+
"anthropic_compat": "the Anthropic-compatible endpoint you configured",
|
|
38
|
+
"openai": "OpenAI",
|
|
39
|
+
"openai_default": "the OpenAI-compatible endpoint you configured",
|
|
40
|
+
"openai_compat": "the OpenAI-compatible endpoint you configured",
|
|
41
|
+
"xai": "xAI",
|
|
42
|
+
"bedrock": "AWS Bedrock",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
RUNPOD_PREFIX = "rpa_"
|
|
46
|
+
RUNPOD_REFUSAL = ("That key can't be used here: it isn't a model provider key or a Hyper "
|
|
47
|
+
"access key. Nothing was saved.")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# ───────────────────────── who is third-party ─────────────────────────
|
|
51
|
+
|
|
52
|
+
def is_third_party(prov) -> bool:
|
|
53
|
+
return getattr(prov, "origin", "customer") != "hyper"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def provider_name(prov_or_name) -> str:
|
|
57
|
+
name = prov_or_name if isinstance(prov_or_name, str) else getattr(prov_or_name, "name", "")
|
|
58
|
+
if name in PROVIDER_NAMES:
|
|
59
|
+
return PROVIDER_NAMES[name]
|
|
60
|
+
label = "" if isinstance(prov_or_name, str) else getattr(prov_or_name, "label", "")
|
|
61
|
+
return label or name
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def consent_line(prov_or_name) -> str:
|
|
65
|
+
return CONSENT_LINE.format(provider=provider_name(prov_or_name))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# ───────────────────────── labels ─────────────────────────
|
|
69
|
+
|
|
70
|
+
_GPT = re.compile(r"^gpt-", re.I)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def pretty_model_name(provider: str, model: str) -> str:
|
|
74
|
+
"""A readable model name: managed name, known name, else from the id
|
|
75
|
+
("claude-sonnet-5-5" -> "Claude Sonnet 5.5")."""
|
|
76
|
+
from hyperclaw import managed_config, xai
|
|
77
|
+
named = managed_config.model_name(provider, model)
|
|
78
|
+
if named:
|
|
79
|
+
return named
|
|
80
|
+
if model in xai.NAMES:
|
|
81
|
+
return xai.NAMES[model]
|
|
82
|
+
if not model:
|
|
83
|
+
return provider
|
|
84
|
+
if _GPT.match(model):
|
|
85
|
+
return "GPT-" + model[4:]
|
|
86
|
+
words, nums = [], []
|
|
87
|
+
for part in model.split("-"):
|
|
88
|
+
if part.isdigit() and len(part) <= 2:
|
|
89
|
+
nums.append(part)
|
|
90
|
+
elif nums:
|
|
91
|
+
break # a date or snapshot suffix after the version: stop
|
|
92
|
+
else:
|
|
93
|
+
words.append(part[:1].upper() + part[1:])
|
|
94
|
+
return " ".join(words + ([".".join(nums)] if nums else [])) or model
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def model_label(prov, model: str) -> str:
|
|
98
|
+
"""How this provider + model is named to the user."""
|
|
99
|
+
from hyperclaw import managed_config
|
|
100
|
+
if not is_third_party(prov):
|
|
101
|
+
return getattr(prov, "display_label", "") or getattr(prov, "label", "") or prov.name
|
|
102
|
+
if managed_config.hyper_issued(prov.name):
|
|
103
|
+
return f"{pretty_model_name(prov.name, model)}, via Hyper"
|
|
104
|
+
return f"your own provider ({getattr(prov, 'label', '') or prov.name})"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def answering_text(label: str, model: str) -> str:
|
|
108
|
+
"""'Answering: HyperSpeed V2 · model hospitality'."""
|
|
109
|
+
return f"Answering: {label}" + (f" · model {model}" if model else "")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def fallback_notice(new_label: str, new_model: str, old_label: str) -> str:
|
|
113
|
+
"""One line when an automatic fallback changes the answering model."""
|
|
114
|
+
model = f" · model {new_model}" if new_model else ""
|
|
115
|
+
return f"Now answering: {new_label}{model}, because {old_label} didn't respond."
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# ───────────────────────── consent (owner-only file) ─────────────────────────
|
|
119
|
+
|
|
120
|
+
def consent_path() -> Path:
|
|
121
|
+
from hyperclaw.userconfig import root
|
|
122
|
+
return root() / "config" / CONSENT_FILE
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _read() -> list:
|
|
126
|
+
try:
|
|
127
|
+
data = json.loads(consent_path().read_text(encoding="utf-8"))
|
|
128
|
+
rows = data.get("consents") if isinstance(data, dict) else None
|
|
129
|
+
return [r for r in rows if isinstance(r, dict)] if isinstance(rows, list) else []
|
|
130
|
+
except Exception: # noqa: BLE001 -- missing or unreadable: no consent
|
|
131
|
+
return []
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def has_consent(provider: str) -> bool:
|
|
135
|
+
"""True when the owner agreed to this provider processing their data."""
|
|
136
|
+
return any(r.get("provider") == provider for r in _read())
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def consents() -> list:
|
|
140
|
+
return _read()
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def record_consent(provider: str, model: str) -> dict:
|
|
144
|
+
"""Append the owner's consent (UTC time, provider, model). The file is
|
|
145
|
+
owner read/write only (0600)."""
|
|
146
|
+
row = {"provider": provider, "model": model,
|
|
147
|
+
"provider_name": provider_name(provider),
|
|
148
|
+
"line": consent_line(provider),
|
|
149
|
+
"at": _dt.datetime.now(_dt.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")}
|
|
150
|
+
rows = _read() + [row]
|
|
151
|
+
p = consent_path()
|
|
152
|
+
p.parent.mkdir(parents=True, exist_ok=True)
|
|
153
|
+
fd = os.open(str(p), os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
|
|
154
|
+
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
|
155
|
+
json.dump({"consents": rows}, f, indent=1)
|
|
156
|
+
try:
|
|
157
|
+
os.chmod(p, 0o600)
|
|
158
|
+
except OSError:
|
|
159
|
+
pass
|
|
160
|
+
return row
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def needs_consent(prov) -> bool:
|
|
164
|
+
return is_third_party(prov) and not has_consent(prov.name)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def fallback_ladder(candidates: list) -> list:
|
|
168
|
+
"""An automatic fallback never moves to a third-party provider the owner
|
|
169
|
+
hasn't agreed to; the first rung (the configured or chosen route) stays."""
|
|
170
|
+
if not candidates:
|
|
171
|
+
return []
|
|
172
|
+
return [candidates[0]] + [(p, m) for p, m in candidates[1:] if not needs_consent(p)]
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
# ───────────────────────── keys ─────────────────────────
|
|
176
|
+
|
|
177
|
+
def looks_like_runpod_key(key: str) -> bool:
|
|
178
|
+
return (key or "").strip().lower().startswith(RUNPOD_PREFIX)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def check_key(key: str) -> str:
|
|
182
|
+
"""'' when the key may be saved; else the plain reason it is refused."""
|
|
183
|
+
if looks_like_runpod_key(key):
|
|
184
|
+
return RUNPOD_REFUSAL
|
|
185
|
+
return ""
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
# ───────────────────────── the allowed list, for people ─────────────────────────
|
|
189
|
+
|
|
190
|
+
def allowed_display(entries: Optional[list] = None) -> list:
|
|
191
|
+
"""The allowed list as names, HyperSpeed V2 first (onboarding, doctor,
|
|
192
|
+
provider list). [] when the install is not restricted."""
|
|
193
|
+
from hyperclaw import hyperspeed, managed_config
|
|
194
|
+
if entries is None:
|
|
195
|
+
entries = managed_config.allowed_models() or []
|
|
196
|
+
hyper, other = [], []
|
|
197
|
+
for e in entries:
|
|
198
|
+
prov, _, model = e.partition("/")
|
|
199
|
+
if prov == hyperspeed.PROVIDER_NAME:
|
|
200
|
+
if model in ("*", hyperspeed.model()):
|
|
201
|
+
hyper.insert(0, f"{hyperspeed.LABEL} (recommended)")
|
|
202
|
+
if model == "*":
|
|
203
|
+
hyper.append("other Hyper models")
|
|
204
|
+
elif model != hyperspeed.model():
|
|
205
|
+
hyper.append(f"{pretty_model_name(prov, model)} (Hyper)")
|
|
206
|
+
continue
|
|
207
|
+
base = (pretty_model_name(prov, model) if model != "*"
|
|
208
|
+
else f"{provider_name(prov)} models")
|
|
209
|
+
other.append(f"{base}, via Hyper" if managed_config.hyper_issued(prov)
|
|
210
|
+
else f"{base} (your own provider)")
|
|
211
|
+
return list(dict.fromkeys(hyper + other))
|