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,534 @@
1
+ """
2
+ Image Provider Abstraction Layer.
3
+ Unified interface for image generation and vision providers.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ import os
8
+ import logging
9
+ from abc import ABC, abstractmethod
10
+ from dataclasses import dataclass
11
+ from pathlib import Path
12
+
13
+ import httpx
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ @dataclass
19
+ class ImageResult:
20
+ """Image generation result."""
21
+ url: str | None = None
22
+ data: bytes | None = None
23
+ revised_prompt: str | None = None
24
+ raw: dict | None = None
25
+
26
+
27
+ @dataclass
28
+ class VisionResult:
29
+ """Vision/image understanding result."""
30
+ content: str
31
+ raw: dict | None = None
32
+
33
+
34
+ class ImageProvider(ABC):
35
+ """Abstract base for image generation providers."""
36
+
37
+ name: str = "base"
38
+
39
+ @abstractmethod
40
+ async def generate(
41
+ self,
42
+ prompt: str,
43
+ size: str = "1024x1024",
44
+ quality: str = "standard",
45
+ n: int = 1,
46
+ **kwargs,
47
+ ) -> list[ImageResult]:
48
+ """Generate images from a text prompt."""
49
+ pass
50
+
51
+
52
+ class VisionProvider(ABC):
53
+ """Abstract base for vision/image understanding providers."""
54
+
55
+ name: str = "base"
56
+
57
+ def __init_subclass__(cls, **kwargs):
58
+ # A vision answer is a model call: it goes through the cost guard (spec §9).
59
+ super().__init_subclass__(**kwargs)
60
+ from hyperclaw.cost_guard import guard_provider_class
61
+ guard_provider_class(cls, coroutines=("analyze",), streams=(), path="vision")
62
+
63
+ @abstractmethod
64
+ async def analyze(
65
+ self,
66
+ image: bytes | str | Path,
67
+ prompt: str = "Describe this image in detail.",
68
+ **kwargs,
69
+ ) -> VisionResult:
70
+ """Analyze an image and return description."""
71
+ pass
72
+
73
+
74
+ # ═══════════════════════════════════════════════════════════════════════════════
75
+ # OPENAI DALL-E
76
+ # ═══════════════════════════════════════════════════════════════════════════════
77
+
78
+ class DALLEProvider(ImageProvider):
79
+ """OpenAI DALL-E image generation."""
80
+
81
+ name = "dalle"
82
+
83
+ MODELS = ["dall-e-3", "dall-e-2"]
84
+
85
+ def __init__(self, api_key: str | None = None):
86
+ self.api_key = api_key or os.environ.get("OPENAI_API_KEY")
87
+ self.base_url = "https://api.openai.com/v1"
88
+
89
+ async def generate(
90
+ self,
91
+ prompt: str,
92
+ size: str = "1024x1024",
93
+ quality: str = "standard",
94
+ n: int = 1,
95
+ model: str = "dall-e-3",
96
+ **kwargs,
97
+ ) -> list[ImageResult]:
98
+ async with httpx.AsyncClient() as client:
99
+ response = await client.post(
100
+ f"{self.base_url}/images/generations",
101
+ headers={"Authorization": f"Bearer {self.api_key}"},
102
+ json={
103
+ "model": model,
104
+ "prompt": prompt,
105
+ "size": size,
106
+ "quality": quality,
107
+ "n": n,
108
+ },
109
+ timeout=120.0,
110
+ )
111
+ response.raise_for_status()
112
+ data = response.json()
113
+
114
+ return [
115
+ ImageResult(
116
+ url=img.get("url"),
117
+ revised_prompt=img.get("revised_prompt"),
118
+ raw=img,
119
+ )
120
+ for img in data["data"]
121
+ ]
122
+
123
+
124
+ # ═══════════════════════════════════════════════════════════════════════════════
125
+ # STABILITY AI
126
+ # ═══════════════════════════════════════════════════════════════════════════════
127
+
128
+ class StabilityProvider(ImageProvider):
129
+ """Stability AI image generation (Stable Diffusion)."""
130
+
131
+ name = "stability"
132
+
133
+ MODELS = ["stable-diffusion-xl-1024-v1-0", "stable-diffusion-v1-6"]
134
+
135
+ def __init__(self, api_key: str | None = None):
136
+ self.api_key = api_key or os.environ.get("STABILITY_API_KEY")
137
+ self.base_url = "https://api.stability.ai/v1"
138
+
139
+ async def generate(
140
+ self,
141
+ prompt: str,
142
+ size: str = "1024x1024",
143
+ quality: str = "standard",
144
+ n: int = 1,
145
+ model: str = "stable-diffusion-xl-1024-v1-0",
146
+ **kwargs,
147
+ ) -> list[ImageResult]:
148
+ width, height = map(int, size.split("x"))
149
+
150
+ async with httpx.AsyncClient() as client:
151
+ response = await client.post(
152
+ f"{self.base_url}/generation/{model}/text-to-image",
153
+ headers={
154
+ "Authorization": f"Bearer {self.api_key}",
155
+ "Content-Type": "application/json",
156
+ "Accept": "application/json",
157
+ },
158
+ json={
159
+ "text_prompts": [{"text": prompt, "weight": 1}],
160
+ "height": height,
161
+ "width": width,
162
+ "samples": n,
163
+ "steps": 30 if quality == "standard" else 50,
164
+ },
165
+ timeout=120.0,
166
+ )
167
+ response.raise_for_status()
168
+ data = response.json()
169
+
170
+ import base64
171
+ return [
172
+ ImageResult(
173
+ data=base64.b64decode(artifact["base64"]),
174
+ raw=artifact,
175
+ )
176
+ for artifact in data["artifacts"]
177
+ ]
178
+
179
+
180
+ # ═══════════════════════════════════════════════════════════════════════════════
181
+ # MIDJOURNEY (via unofficial API)
182
+ # ═══════════════════════════════════════════════════════════════════════════════
183
+
184
+ class MidjourneyProvider(ImageProvider):
185
+ """Midjourney image generation (requires proxy service)."""
186
+
187
+ name = "midjourney"
188
+
189
+ def __init__(self, api_key: str | None = None, base_url: str | None = None):
190
+ self.api_key = api_key or os.environ.get("MIDJOURNEY_API_KEY")
191
+ self.base_url = base_url or os.environ.get("MIDJOURNEY_API_URL", "https://api.mymidjourney.ai/v1")
192
+
193
+ async def generate(
194
+ self,
195
+ prompt: str,
196
+ size: str = "1024x1024",
197
+ quality: str = "standard",
198
+ n: int = 1,
199
+ **kwargs,
200
+ ) -> list[ImageResult]:
201
+ async with httpx.AsyncClient() as client:
202
+ response = await client.post(
203
+ f"{self.base_url}/imagine",
204
+ headers={"Authorization": f"Bearer {self.api_key}"},
205
+ json={"prompt": prompt},
206
+ timeout=300.0,
207
+ )
208
+ response.raise_for_status()
209
+ data = response.json()
210
+
211
+ # Wait for completion
212
+ task_id = data.get("taskId")
213
+ if task_id:
214
+ import asyncio
215
+ while True:
216
+ status_response = await client.get(
217
+ f"{self.base_url}/task/{task_id}",
218
+ headers={"Authorization": f"Bearer {self.api_key}"},
219
+ timeout=30.0,
220
+ )
221
+ status_response.raise_for_status()
222
+ status_data = status_response.json()
223
+
224
+ if status_data.get("status") == "completed":
225
+ return [
226
+ ImageResult(url=img_url, raw=status_data)
227
+ for img_url in status_data.get("imageUrls", [])
228
+ ]
229
+ elif status_data.get("status") == "failed":
230
+ raise RuntimeError(f"Midjourney generation failed: {status_data}")
231
+
232
+ await asyncio.sleep(5)
233
+
234
+ return [ImageResult(raw=data)]
235
+
236
+
237
+ # ═══════════════════════════════════════════════════════════════════════════════
238
+ # REPLICATE
239
+ # ═══════════════════════════════════════════════════════════════════════════════
240
+
241
+ class ReplicateProvider(ImageProvider):
242
+ """Replicate model hosting for image generation."""
243
+
244
+ name = "replicate"
245
+
246
+ def __init__(self, api_key: str | None = None):
247
+ self.api_key = api_key or os.environ.get("REPLICATE_API_TOKEN")
248
+ self.base_url = "https://api.replicate.com/v1"
249
+
250
+ async def generate(
251
+ self,
252
+ prompt: str,
253
+ size: str = "1024x1024",
254
+ quality: str = "standard",
255
+ n: int = 1,
256
+ model: str = "stability-ai/sdxl:39ed52f2a78e934b3ba6e2a89f5b1c712de7dfea535525255b1aa35c5565e08b",
257
+ **kwargs,
258
+ ) -> list[ImageResult]:
259
+ width, height = map(int, size.split("x"))
260
+
261
+ async with httpx.AsyncClient() as client:
262
+ # Create prediction
263
+ response = await client.post(
264
+ f"{self.base_url}/predictions",
265
+ headers={"Authorization": f"Token {self.api_key}"},
266
+ json={
267
+ "version": model.split(":")[-1] if ":" in model else model,
268
+ "input": {
269
+ "prompt": prompt,
270
+ "width": width,
271
+ "height": height,
272
+ "num_outputs": n,
273
+ },
274
+ },
275
+ timeout=30.0,
276
+ )
277
+ response.raise_for_status()
278
+ prediction = response.json()
279
+
280
+ # Poll for completion
281
+ import asyncio
282
+ while prediction["status"] not in ["succeeded", "failed", "canceled"]:
283
+ await asyncio.sleep(2)
284
+ status_response = await client.get(
285
+ prediction["urls"]["get"],
286
+ headers={"Authorization": f"Token {self.api_key}"},
287
+ timeout=30.0,
288
+ )
289
+ status_response.raise_for_status()
290
+ prediction = status_response.json()
291
+
292
+ if prediction["status"] != "succeeded":
293
+ raise RuntimeError(f"Replicate generation failed: {prediction}")
294
+
295
+ output = prediction.get("output", [])
296
+ if isinstance(output, str):
297
+ output = [output]
298
+
299
+ return [ImageResult(url=url, raw=prediction) for url in output]
300
+
301
+
302
+ # ═══════════════════════════════════════════════════════════════════════════════
303
+ # VISION PROVIDERS
304
+ # ═══════════════════════════════════════════════════════════════════════════════
305
+
306
+ class OpenAIVisionProvider(VisionProvider):
307
+ """OpenAI GPT-4 Vision."""
308
+
309
+ name = "openai-vision"
310
+
311
+ def __init__(self, api_key: str | None = None):
312
+ self.api_key = api_key or os.environ.get("OPENAI_API_KEY")
313
+ self.base_url = "https://api.openai.com/v1"
314
+
315
+ async def analyze(
316
+ self,
317
+ image: bytes | str | Path,
318
+ prompt: str = "Describe this image in detail.",
319
+ model: str = "gpt-4o",
320
+ **kwargs,
321
+ ) -> VisionResult:
322
+ import base64
323
+
324
+ if isinstance(image, Path):
325
+ image = image.read_bytes()
326
+
327
+ if isinstance(image, bytes):
328
+ image_content = {
329
+ "type": "image_url",
330
+ "image_url": {
331
+ "url": f"data:image/jpeg;base64,{base64.b64encode(image).decode()}"
332
+ },
333
+ }
334
+ else:
335
+ image_content = {"type": "image_url", "image_url": {"url": image}}
336
+
337
+ async with httpx.AsyncClient() as client:
338
+ response = await client.post(
339
+ f"{self.base_url}/chat/completions",
340
+ headers={"Authorization": f"Bearer {self.api_key}"},
341
+ json={
342
+ "model": model,
343
+ "messages": [
344
+ {
345
+ "role": "user",
346
+ "content": [
347
+ {"type": "text", "text": prompt},
348
+ image_content,
349
+ ],
350
+ }
351
+ ],
352
+ "max_tokens": int(kwargs.get("max_tokens") or 1024),
353
+ },
354
+ timeout=120.0,
355
+ )
356
+ response.raise_for_status()
357
+ data = response.json()
358
+
359
+ return VisionResult(
360
+ content=data["choices"][0]["message"]["content"],
361
+ raw=data,
362
+ )
363
+
364
+
365
+ class AnthropicVisionProvider(VisionProvider):
366
+ """Anthropic Claude Vision."""
367
+
368
+ name = "claude-vision"
369
+
370
+ def __init__(self, api_key: str | None = None):
371
+ self.api_key = api_key or os.environ.get("ANTHROPIC_API_KEY")
372
+ self.base_url = "https://api.anthropic.com/v1"
373
+
374
+ async def analyze(
375
+ self,
376
+ image: bytes | str | Path,
377
+ prompt: str = "Describe this image in detail.",
378
+ model: str = "claude-sonnet-4-6",
379
+ **kwargs,
380
+ ) -> VisionResult:
381
+ import base64
382
+
383
+ if isinstance(image, Path):
384
+ image = image.read_bytes()
385
+
386
+ if isinstance(image, bytes):
387
+ image_content = {
388
+ "type": "image",
389
+ "source": {
390
+ "type": "base64",
391
+ "media_type": "image/jpeg",
392
+ "data": base64.b64encode(image).decode(),
393
+ },
394
+ }
395
+ else:
396
+ image_content = {
397
+ "type": "image",
398
+ "source": {"type": "url", "url": image},
399
+ }
400
+
401
+ async with httpx.AsyncClient() as client:
402
+ response = await client.post(
403
+ f"{self.base_url}/messages",
404
+ headers={
405
+ "x-api-key": self.api_key,
406
+ "anthropic-version": "2023-06-01",
407
+ "content-type": "application/json",
408
+ },
409
+ json={
410
+ "model": model,
411
+ "max_tokens": int(kwargs.get("max_tokens") or 1024),
412
+ "messages": [
413
+ {
414
+ "role": "user",
415
+ "content": [
416
+ image_content,
417
+ {"type": "text", "text": prompt},
418
+ ],
419
+ }
420
+ ],
421
+ },
422
+ timeout=120.0,
423
+ )
424
+ response.raise_for_status()
425
+ data = response.json()
426
+
427
+ return VisionResult(
428
+ content=data["content"][0]["text"],
429
+ raw=data,
430
+ )
431
+
432
+
433
+ class GeminiVisionProvider(VisionProvider):
434
+ """Google Gemini Vision."""
435
+
436
+ name = "gemini-vision"
437
+
438
+ def __init__(self, api_key: str | None = None):
439
+ self.api_key = api_key or os.environ.get("GEMINI_API_KEY") or os.environ.get("GOOGLE_API_KEY")
440
+ self.base_url = "https://generativelanguage.googleapis.com/v1beta"
441
+
442
+ async def analyze(
443
+ self,
444
+ image: bytes | str | Path,
445
+ prompt: str = "Describe this image in detail.",
446
+ model: str = "gemini-1.5-pro",
447
+ **kwargs,
448
+ ) -> VisionResult:
449
+ import base64
450
+
451
+ if isinstance(image, Path):
452
+ image = image.read_bytes()
453
+
454
+ if isinstance(image, bytes):
455
+ image_part = {
456
+ "inline_data": {
457
+ "mime_type": "image/jpeg",
458
+ "data": base64.b64encode(image).decode(),
459
+ }
460
+ }
461
+ else:
462
+ image_part = {"file_data": {"file_uri": image, "mime_type": "image/jpeg"}}
463
+
464
+ async with httpx.AsyncClient() as client:
465
+ response = await client.post(
466
+ f"{self.base_url}/models/{model}:generateContent",
467
+ params={"key": self.api_key},
468
+ json={
469
+ "contents": [
470
+ {
471
+ "parts": [
472
+ image_part,
473
+ {"text": prompt},
474
+ ]
475
+ }
476
+ ],
477
+ "generationConfig": {
478
+ "maxOutputTokens": int(kwargs.get("max_tokens") or 1024),
479
+ },
480
+ },
481
+ timeout=120.0,
482
+ )
483
+ response.raise_for_status()
484
+ data = response.json()
485
+
486
+ return VisionResult(
487
+ content=data["candidates"][0]["content"]["parts"][0]["text"],
488
+ raw=data,
489
+ )
490
+
491
+
492
+ # ═══════════════════════════════════════════════════════════════════════════════
493
+ # PROVIDER FACTORY
494
+ # ═══════════════════════════════════════════════════════════════════════════════
495
+
496
+ IMAGE_PROVIDERS: dict[str, type[ImageProvider]] = {
497
+ "dalle": DALLEProvider,
498
+ "openai": DALLEProvider,
499
+ "stability": StabilityProvider,
500
+ "midjourney": MidjourneyProvider,
501
+ "replicate": ReplicateProvider,
502
+ }
503
+
504
+ VISION_PROVIDERS: dict[str, type[VisionProvider]] = {
505
+ "openai": OpenAIVisionProvider,
506
+ "anthropic": AnthropicVisionProvider,
507
+ "claude": AnthropicVisionProvider,
508
+ "gemini": GeminiVisionProvider,
509
+ "google": GeminiVisionProvider,
510
+ }
511
+
512
+
513
+ def get_image_provider(name: str | None = None, **kwargs) -> ImageProvider:
514
+ """Get an image generation provider by name."""
515
+ name = name or os.environ.get("HYPERCLAW_IMAGE_PROVIDER", "dalle")
516
+ name = name.lower()
517
+
518
+ if name not in IMAGE_PROVIDERS:
519
+ available = ", ".join(sorted(IMAGE_PROVIDERS.keys()))
520
+ raise ValueError(f"Unknown image provider: {name}. Available: {available}")
521
+
522
+ return IMAGE_PROVIDERS[name](**kwargs)
523
+
524
+
525
+ def get_vision_provider(name: str | None = None, **kwargs) -> VisionProvider:
526
+ """Get a vision/image understanding provider by name."""
527
+ name = name or os.environ.get("HYPERCLAW_VISION_PROVIDER", "anthropic")
528
+ name = name.lower()
529
+
530
+ if name not in VISION_PROVIDERS:
531
+ available = ", ".join(sorted(VISION_PROVIDERS.keys()))
532
+ raise ValueError(f"Unknown vision provider: {name}. Available: {available}")
533
+
534
+ return VISION_PROVIDERS[name](**kwargs)