hypermind 0.11.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. hmctl/__init__.py +7 -0
  2. hmctl/cli.py +947 -0
  3. hypermind/__init__.py +208 -0
  4. hypermind/_deprecations.py +108 -0
  5. hypermind/agent.py +2896 -0
  6. hypermind/agent_card.py +127 -0
  7. hypermind/api/__init__.py +32 -0
  8. hypermind/api/app.py +686 -0
  9. hypermind/capability.py +923 -0
  10. hypermind/consult.py +493 -0
  11. hypermind/consult_bias.py +112 -0
  12. hypermind/contract_net.py +219 -0
  13. hypermind/crypto/__init__.py +21 -0
  14. hypermind/crypto/cose_encrypt.py +126 -0
  15. hypermind/crypto/dkg.py +246 -0
  16. hypermind/crypto/domain.py +59 -0
  17. hypermind/crypto/frost.py +771 -0
  18. hypermind/crypto/hashing.py +38 -0
  19. hypermind/crypto/hlc.py +163 -0
  20. hypermind/crypto/hpke.py +277 -0
  21. hypermind/crypto/kms.py +196 -0
  22. hypermind/crypto/kms_backends/__init__.py +56 -0
  23. hypermind/crypto/kms_backends/aws.py +128 -0
  24. hypermind/crypto/kms_backends/azure.py +114 -0
  25. hypermind/crypto/kms_backends/gcp.py +97 -0
  26. hypermind/crypto/kms_backends/vault.py +120 -0
  27. hypermind/crypto/namespace_root.py +204 -0
  28. hypermind/crypto/pq.py +152 -0
  29. hypermind/crypto/room_epoch.py +97 -0
  30. hypermind/crypto/signing.py +245 -0
  31. hypermind/deliberation.py +355 -0
  32. hypermind/did_web.py +256 -0
  33. hypermind/dispute.py +858 -0
  34. hypermind/errors.py +218 -0
  35. hypermind/eval/__init__.py +49 -0
  36. hypermind/eval/calibration.py +307 -0
  37. hypermind/eval/comparison.py +251 -0
  38. hypermind/eval/replay.py +202 -0
  39. hypermind/eval/swarm_iq.py +718 -0
  40. hypermind/knowledge/__init__.py +17 -0
  41. hypermind/knowledge/citation_graph.py +141 -0
  42. hypermind/knowledge/correlated_agreement.py +167 -0
  43. hypermind/knowledge/embedding_registry.py +115 -0
  44. hypermind/knowledge/kernel_store.py +197 -0
  45. hypermind/knowledge/rekor.py +173 -0
  46. hypermind/knowledge/reputation.py +364 -0
  47. hypermind/knowledge/swarm_memory.py +523 -0
  48. hypermind/knowledge/transparency.py +409 -0
  49. hypermind/mcp/__init__.py +60 -0
  50. hypermind/mcp/bridge.py +151 -0
  51. hypermind/mcp/client.py +142 -0
  52. hypermind/mcp/provenance.py +214 -0
  53. hypermind/mcp/relata_tools.py +152 -0
  54. hypermind/mcp/server.py +248 -0
  55. hypermind/mcp/transport.py +206 -0
  56. hypermind/mind/__init__.py +150 -0
  57. hypermind/mind/active.py +234 -0
  58. hypermind/mind/belief.py +369 -0
  59. hypermind/mind/deliberator.py +625 -0
  60. hypermind/mind/diversity.py +144 -0
  61. hypermind/mind/evolve.py +184 -0
  62. hypermind/mind/federation.py +154 -0
  63. hypermind/mind/goals.py +117 -0
  64. hypermind/mind/integration.py +190 -0
  65. hypermind/mind/mediator.py +74 -0
  66. hypermind/mind/memory_store.py +129 -0
  67. hypermind/mind/policy.py +307 -0
  68. hypermind/mind/records.py +260 -0
  69. hypermind/mind/roles.py +190 -0
  70. hypermind/mind/sybil_guard.py +41 -0
  71. hypermind/mind/world_model.py +166 -0
  72. hypermind/namespace.py +515 -0
  73. hypermind/namespace_policy.py +254 -0
  74. hypermind/observability/__init__.py +25 -0
  75. hypermind/observability/asgi.py +214 -0
  76. hypermind/observability/cost.py +277 -0
  77. hypermind/observability/otel_export.py +200 -0
  78. hypermind/observability/recorder.py +344 -0
  79. hypermind/observability/swarm_trace.py +438 -0
  80. hypermind/pin_policy.py +116 -0
  81. hypermind/py.typed +0 -0
  82. hypermind/responders/__init__.py +87 -0
  83. hypermind/responders/anthropic.py +159 -0
  84. hypermind/responders/base.py +162 -0
  85. hypermind/responders/openai.py +199 -0
  86. hypermind/responders/router.py +254 -0
  87. hypermind/responders/structured.py +287 -0
  88. hypermind/responders/tools.py +444 -0
  89. hypermind/revocation.py +270 -0
  90. hypermind/rule_ids.py +135 -0
  91. hypermind/schemas/encrypted_statement.cddl +28 -0
  92. hypermind/schemas/namespace_accept.cddl +20 -0
  93. hypermind/schemas/namespace_create.cddl +25 -0
  94. hypermind/schemas/namespace_founding_attest.cddl +30 -0
  95. hypermind/schemas/namespace_invite.cddl +22 -0
  96. hypermind/schemas/wire-v0.1.cddl +73 -0
  97. hypermind/simlab/__init__.py +15 -0
  98. hypermind/simlab/_persona_registry.py +93 -0
  99. hypermind/simlab/_scenario_impl.py +2778 -0
  100. hypermind/simlab/app.py +2538 -0
  101. hypermind/simlab/backends/__init__.py +27 -0
  102. hypermind/simlab/backends/anchor.py +88 -0
  103. hypermind/simlab/backends/local.py +32 -0
  104. hypermind/simlab/backends/server.py +79 -0
  105. hypermind/simlab/cli_command.py +108 -0
  106. hypermind/simlab/config.py +106 -0
  107. hypermind/simlab/deployment.py +26 -0
  108. hypermind/simlab/knowledge.py +716 -0
  109. hypermind/simlab/learning.py +295 -0
  110. hypermind/simlab/modes/__init__.py +115 -0
  111. hypermind/simlab/modes/_eval_real_llm.py +373 -0
  112. hypermind/simlab/modes/aar.py +611 -0
  113. hypermind/simlab/modes/backtest.py +610 -0
  114. hypermind/simlab/modes/binary_forecast.py +69 -0
  115. hypermind/simlab/modes/conformance.py +1869 -0
  116. hypermind/simlab/modes/evaluation.py +2271 -0
  117. hypermind/simlab/modes/governance.py +648 -0
  118. hypermind/simlab/modes/redteam.py +534 -0
  119. hypermind/simlab/modes/tabletop.py +797 -0
  120. hypermind/simlab/modes/tournament.py +448 -0
  121. hypermind/simlab/modes/whatif.py +523 -0
  122. hypermind/simlab/namespace.py +125 -0
  123. hypermind/simlab/personas.py +477 -0
  124. hypermind/simlab/prompt_generator.py +172 -0
  125. hypermind/simlab/question_sets/geopolitics_q20.json +122 -0
  126. hypermind/simlab/question_sets/governance_q5.json +67 -0
  127. hypermind/simlab/question_sets/msft_outlook_q10.json +62 -0
  128. hypermind/simlab/question_sets/whatif_q3.json +38 -0
  129. hypermind/simlab/registry.py +325 -0
  130. hypermind/simlab/runner.py +239 -0
  131. hypermind/simlab/scenario.py +147 -0
  132. hypermind/simlab/swarms.py +180 -0
  133. hypermind/simlab/tool_handlers/__init__.py +4 -0
  134. hypermind/simlab/tool_handlers/alpha_vantage.py +39 -0
  135. hypermind/simlab/tool_handlers/brave.py +46 -0
  136. hypermind/simlab/tool_handlers/newsapi.py +50 -0
  137. hypermind/simlab/tool_handlers/openweather.py +46 -0
  138. hypermind/simlab/tool_handlers/pinecone.py +52 -0
  139. hypermind/simlab/tool_handlers/qdrant.py +57 -0
  140. hypermind/simlab/tool_handlers/query_knowledge_base.py +21 -0
  141. hypermind/simlab/tool_handlers/relata_recall.py +61 -0
  142. hypermind/simlab/tool_handlers/retrieve_document.py +21 -0
  143. hypermind/simlab/tool_handlers/serper.py +46 -0
  144. hypermind/simlab/tool_handlers/tavily.py +53 -0
  145. hypermind/simlab/tool_handlers/weaviate.py +65 -0
  146. hypermind/simlab/tool_handlers/wikipedia.py +64 -0
  147. hypermind/simlab/tool_handlers/wolfram.py +48 -0
  148. hypermind/simlab/tool_handlers/yahoo_finance.py +49 -0
  149. hypermind/simlab/tool_registry.py +568 -0
  150. hypermind/simlab/topic_adapter.py +338 -0
  151. hypermind/simlab/topic_questions.py +259 -0
  152. hypermind/storage/__init__.py +69 -0
  153. hypermind/storage/backend.py +71 -0
  154. hypermind/storage/relata.py +245 -0
  155. hypermind/storage/sqlite.py +258 -0
  156. hypermind/sync.py +191 -0
  157. hypermind/tal.py +175 -0
  158. hypermind/tasks.py +334 -0
  159. hypermind/testing.py +16 -0
  160. hypermind/tools/__init__.py +32 -0
  161. hypermind/tools/registry.py +61 -0
  162. hypermind/transport/__init__.py +22 -0
  163. hypermind/transport/anti_entropy.py +708 -0
  164. hypermind/transport/bus.py +206 -0
  165. hypermind/transport/knows_delta.py +204 -0
  166. hypermind/transport/libp2p.py +298 -0
  167. hypermind/transport/placement.py +58 -0
  168. hypermind/transport/tcp.py +797 -0
  169. hypermind/transport/tls_profile.py +417 -0
  170. hypermind/types.py +59 -0
  171. hypermind/uncertainty.py +222 -0
  172. hypermind/wire.py +897 -0
  173. hypermind/workflow/__init__.py +72 -0
  174. hypermind/workflow/engine.py +655 -0
  175. hypermind/workflow/plan.py +182 -0
  176. hypermind/workflow/repair.py +203 -0
  177. hypermind-0.11.0.dist-info/METADATA +524 -0
  178. hypermind-0.11.0.dist-info/RECORD +181 -0
  179. hypermind-0.11.0.dist-info/WHEEL +4 -0
  180. hypermind-0.11.0.dist-info/entry_points.txt +2 -0
  181. hypermind-0.11.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,50 @@
1
+ """NewsAPI headlines handler."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import urllib.parse
7
+ import urllib.request
8
+ from typing import Any
9
+
10
+
11
+ async def handle(args: dict[str, Any], credentials: dict[str, str]) -> str:
12
+ api_key = credentials.get("api_key", "")
13
+ if not api_key:
14
+ return json.dumps({"error": "newsapi_headlines: no api_key configured"})
15
+ qs = urllib.parse.urlencode(
16
+ {
17
+ "q": args.get("query", ""),
18
+ "pageSize": int(args.get("page_size", 5)),
19
+ "apiKey": api_key,
20
+ "language": "en",
21
+ "sortBy": "publishedAt",
22
+ }
23
+ )
24
+ try:
25
+ with urllib.request.urlopen(f"https://newsapi.org/v2/everything?{qs}", timeout=10) as resp:
26
+ data = json.loads(resp.read())
27
+ if data.get("status") != "ok":
28
+ return json.dumps({"error": data.get("message", "NewsAPI error"), "results": []})
29
+ articles = [
30
+ {
31
+ "title": a.get("title", ""),
32
+ "url": a.get("url", ""),
33
+ "description": a.get("description", ""),
34
+ "publishedAt": a.get("publishedAt", ""),
35
+ }
36
+ for a in data.get("articles", [])
37
+ ]
38
+ return json.dumps({"query": args.get("query"), "results": articles})
39
+ except Exception as e:
40
+ return json.dumps({"error": str(e), "results": []})
41
+
42
+
43
+ async def test_connection(credentials: dict[str, str]) -> tuple[bool, str]:
44
+ try:
45
+ result = json.loads(await handle({"query": "technology", "page_size": 1}, credentials))
46
+ if "error" in result:
47
+ return False, result["error"]
48
+ return True, f"Connected — {len(result.get('results', []))} article(s)"
49
+ except Exception as e:
50
+ return False, str(e)
@@ -0,0 +1,46 @@
1
+ """OpenWeatherMap handler."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import urllib.parse
7
+ import urllib.request
8
+ from typing import Any
9
+
10
+
11
+ async def handle(args: dict[str, Any], credentials: dict[str, str]) -> str:
12
+ api_key = credentials.get("api_key", "")
13
+ if not api_key:
14
+ return json.dumps({"error": "openweathermap: no api_key configured"})
15
+ location = args.get("location", "")
16
+ units = args.get("units", "metric")
17
+ qs = urllib.parse.urlencode({"q": location, "appid": api_key, "units": units})
18
+ try:
19
+ with urllib.request.urlopen(
20
+ f"https://api.openweathermap.org/data/2.5/weather?{qs}", timeout=10
21
+ ) as resp:
22
+ data = json.loads(resp.read())
23
+ return json.dumps(
24
+ {
25
+ "location": data.get("name", location),
26
+ "country": data.get("sys", {}).get("country", ""),
27
+ "temperature": data.get("main", {}).get("temp"),
28
+ "feels_like": data.get("main", {}).get("feels_like"),
29
+ "humidity": data.get("main", {}).get("humidity"),
30
+ "description": data.get("weather", [{}])[0].get("description", ""),
31
+ "wind_speed": data.get("wind", {}).get("speed"),
32
+ "units": units,
33
+ }
34
+ )
35
+ except Exception as e:
36
+ return json.dumps({"error": str(e), "location": location})
37
+
38
+
39
+ async def test_connection(credentials: dict[str, str]) -> tuple[bool, str]:
40
+ try:
41
+ result = json.loads(await handle({"location": "London", "units": "metric"}, credentials))
42
+ if "error" in result:
43
+ return False, result["error"]
44
+ return True, f"Connected — {result.get('location')}: {result.get('temperature')}°C"
45
+ except Exception as e:
46
+ return False, str(e)
@@ -0,0 +1,52 @@
1
+ """Pinecone vector search handler."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import urllib.parse
7
+ import urllib.request
8
+ from typing import Any
9
+
10
+
11
+ async def handle(args: dict[str, Any], credentials: dict[str, str]) -> str:
12
+ api_key = credentials.get("api_key", "")
13
+ index_name = credentials.get("index_name", "")
14
+ if not api_key or not index_name:
15
+ return json.dumps({"error": "pinecone_search: api_key and index_name required"})
16
+ try:
17
+ req = urllib.request.Request(
18
+ f"https://api.pinecone.io/indexes/{urllib.parse.quote(index_name)}",
19
+ headers={"Api-Key": api_key, "Accept": "application/json"},
20
+ )
21
+ with urllib.request.urlopen(req, timeout=10) as resp:
22
+ idx_data = json.loads(resp.read())
23
+ host = idx_data.get("host", "")
24
+ if not host:
25
+ return json.dumps({"error": "Could not resolve Pinecone index host", "matches": []})
26
+ # Query with zero vector (semantic query requires embeddings — return placeholder)
27
+ return json.dumps(
28
+ {
29
+ "query": args.get("query"),
30
+ "note": "Pinecone requires pre-computed embeddings. Configure an embedding model to enable full semantic search.",
31
+ "matches": [],
32
+ }
33
+ )
34
+ except Exception as e:
35
+ return json.dumps({"error": str(e), "matches": []})
36
+
37
+
38
+ async def test_connection(credentials: dict[str, str]) -> tuple[bool, str]:
39
+ api_key = credentials.get("api_key", "")
40
+ if not api_key:
41
+ return False, "pinecone_search: api_key required"
42
+ try:
43
+ req = urllib.request.Request(
44
+ "https://api.pinecone.io/indexes",
45
+ headers={"Api-Key": api_key, "Accept": "application/json"},
46
+ )
47
+ with urllib.request.urlopen(req, timeout=10) as resp:
48
+ data = json.loads(resp.read())
49
+ indexes = [i.get("name") for i in data.get("indexes", [])]
50
+ return True, f"Connected — {len(indexes)} index(es): {', '.join(indexes[:3]) or 'none'}"
51
+ except Exception as e:
52
+ return False, str(e)
@@ -0,0 +1,57 @@
1
+ """Qdrant vector search handler."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import urllib.parse
7
+ import urllib.request
8
+ from typing import Any
9
+
10
+
11
+ async def handle(args: dict[str, Any], credentials: dict[str, str]) -> str:
12
+ base_url = credentials.get("base_url", "http://localhost:6333").rstrip("/")
13
+ collection = credentials.get("collection", "")
14
+ api_key = credentials.get("api_key", "")
15
+ if not collection:
16
+ return json.dumps({"error": "qdrant_search: collection name required", "matches": []})
17
+ headers: dict[str, str] = {"Content-Type": "application/json"}
18
+ if api_key:
19
+ headers["api-key"] = api_key
20
+ # Scroll endpoint to verify connectivity + return note about embeddings
21
+ try:
22
+ req = urllib.request.Request(
23
+ f"{base_url}/collections/{urllib.parse.quote(collection)}/points/scroll",
24
+ data=json.dumps({"limit": 1, "with_payload": True}).encode(),
25
+ headers=headers,
26
+ method="POST",
27
+ )
28
+ with urllib.request.urlopen(req, timeout=10) as resp:
29
+ data = json.loads(resp.read())
30
+ return json.dumps(
31
+ {
32
+ "query": args.get("query"),
33
+ "note": "Qdrant requires pre-computed query vectors. Configure an embedding model to enable full semantic search.",
34
+ "matches": [],
35
+ "collection_ok": data.get("status") == "ok",
36
+ }
37
+ )
38
+ except Exception as e:
39
+ return json.dumps({"error": str(e), "matches": []})
40
+
41
+
42
+ async def test_connection(credentials: dict[str, str]) -> tuple[bool, str]:
43
+ base_url = credentials.get("base_url", "http://localhost:6333").rstrip("/")
44
+ api_key = credentials.get("api_key", "")
45
+ headers: dict[str, str] = {}
46
+ if api_key:
47
+ headers["api-key"] = api_key
48
+ try:
49
+ req = urllib.request.Request(f"{base_url}/collections", headers=headers)
50
+ with urllib.request.urlopen(req, timeout=10) as resp:
51
+ data = json.loads(resp.read())
52
+ collections = [c.get("name") for c in data.get("result", {}).get("collections", [])]
53
+ return True, f"Connected to Qdrant — {len(collections)} collection(s)"
54
+ except Exception as e:
55
+ return False, str(e)
56
+
57
+
@@ -0,0 +1,21 @@
1
+ """HyperMind knowledge base query stub — searches sealed federation claims."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from typing import Any
7
+
8
+
9
+ async def handle(args: dict[str, Any], credentials: dict[str, str]) -> str:
10
+ query = args.get("query", "")
11
+ return json.dumps(
12
+ {
13
+ "query": query,
14
+ "matches": [],
15
+ "note": "HyperMind KB stub — connects to the federation knowledge index in full deployments.",
16
+ }
17
+ )
18
+
19
+
20
+ async def test_connection(credentials: dict[str, str]) -> tuple[bool, str]:
21
+ return True, "Stub handler active — no external connection needed"
@@ -0,0 +1,61 @@
1
+ """Relata memory recall tool — lets agents search their persistent memory during deliberation.
2
+
3
+ Registered under the name ``relata_recall``. Requires the agent to have a
4
+ :class:`~hypermind.mind.memory_store.RelataMemoryStore` attached via
5
+ :meth:`~hypermind.agent.HyperMindAgent.attach_memory`. Falls back gracefully
6
+ (returns empty list) when no store is attached or RELATA_URL is unset.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Any
12
+
13
+
14
+ def relata_recall() -> dict[str, Any]:
15
+ """Return an OpenAI-compatible tool definition for Relata memory recall."""
16
+ return {
17
+ "type": "function",
18
+ "function": {
19
+ "name": "relata_recall",
20
+ "description": (
21
+ "Search this agent's persistent memory for past learnings, experiences, "
22
+ "and research notes. Returns the top-k most relevant items ranked by "
23
+ "confidence × recency × relevance. Use this BEFORE reasoning about any "
24
+ "topic to surface what you already know."
25
+ ),
26
+ "parameters": {
27
+ "type": "object",
28
+ "properties": {
29
+ "query": {
30
+ "type": "string",
31
+ "description": "Natural-language search query, e.g. 'NK missile test probability'",
32
+ },
33
+ "top_k": {
34
+ "type": "integer",
35
+ "description": "Max results to return (1–10, default 5)",
36
+ "default": 5,
37
+ },
38
+ },
39
+ "required": ["query"],
40
+ },
41
+ },
42
+ }
43
+
44
+
45
+ def handle_relata_recall(args: dict[str, Any], *, agent: Any = None) -> str:
46
+ """Execute a relata_recall tool call. agent should be the HyperMindAgent instance."""
47
+ query = args.get("query", "")
48
+ top_k = max(1, min(10, int(args.get("top_k", 5))))
49
+
50
+ if agent is None or getattr(agent, "_memory", None) is None:
51
+ return "[]"
52
+
53
+ try:
54
+ rows = agent._memory.recall(query, top_k=top_k)
55
+ import json
56
+ return json.dumps(
57
+ [{"content": r.get("content", ""), "score": r.get("score", 0)} for r in rows],
58
+ ensure_ascii=False,
59
+ )
60
+ except Exception as e:
61
+ return f"[recall error: {e}]"
@@ -0,0 +1,21 @@
1
+ """Built-in document retrieval stub — backwards-compatible with existing tool_affinity."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from typing import Any
7
+
8
+
9
+ async def handle(args: dict[str, Any], credentials: dict[str, str]) -> str:
10
+ doc_id = args.get("doc_id", "")
11
+ return json.dumps(
12
+ {
13
+ "doc_id": doc_id,
14
+ "found": False,
15
+ "note": "Document retrieval stub — configure a real backend handler.",
16
+ }
17
+ )
18
+
19
+
20
+ async def test_connection(credentials: dict[str, str]) -> tuple[bool, str]:
21
+ return True, "Stub handler active — no external connection needed"
@@ -0,0 +1,46 @@
1
+ """Serper (Google) Web Search handler."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import urllib.request
7
+ from typing import Any
8
+
9
+
10
+ async def handle(args: dict[str, Any], credentials: dict[str, str]) -> str:
11
+ api_key = credentials.get("api_key", "")
12
+ if not api_key:
13
+ return json.dumps({"error": "serper_web_search: no api_key configured"})
14
+ payload = json.dumps(
15
+ {"q": args.get("query", ""), "num": int(args.get("max_results", 5))}
16
+ ).encode()
17
+ req = urllib.request.Request(
18
+ "https://google.serper.dev/search",
19
+ data=payload,
20
+ headers={"X-API-KEY": api_key, "Content-Type": "application/json"},
21
+ method="POST",
22
+ )
23
+ try:
24
+ with urllib.request.urlopen(req, timeout=10) as resp:
25
+ data = json.loads(resp.read())
26
+ results = [
27
+ {
28
+ "title": r.get("title", ""),
29
+ "url": r.get("link", ""),
30
+ "description": r.get("snippet", ""),
31
+ }
32
+ for r in data.get("organic", [])
33
+ ]
34
+ return json.dumps({"query": args.get("query"), "results": results})
35
+ except Exception as e:
36
+ return json.dumps({"error": str(e), "results": []})
37
+
38
+
39
+ async def test_connection(credentials: dict[str, str]) -> tuple[bool, str]:
40
+ try:
41
+ result = json.loads(await handle({"query": "test", "max_results": 1}, credentials))
42
+ if "error" in result:
43
+ return False, result["error"]
44
+ return True, f"Connected — {len(result.get('results', []))} result(s)"
45
+ except Exception as e:
46
+ return False, str(e)
@@ -0,0 +1,53 @@
1
+ """Tavily AI Search handler."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import urllib.request
7
+ from typing import Any
8
+
9
+
10
+ async def handle(args: dict[str, Any], credentials: dict[str, str]) -> str:
11
+ api_key = credentials.get("api_key", "")
12
+ if not api_key:
13
+ return json.dumps({"error": "tavily_search: no api_key configured"})
14
+ payload = json.dumps(
15
+ {
16
+ "api_key": api_key,
17
+ "query": args.get("query", ""),
18
+ "max_results": int(args.get("max_results", 5)),
19
+ "search_depth": "basic",
20
+ }
21
+ ).encode()
22
+ req = urllib.request.Request(
23
+ "https://api.tavily.com/search",
24
+ data=payload,
25
+ headers={"Content-Type": "application/json"},
26
+ method="POST",
27
+ )
28
+ try:
29
+ with urllib.request.urlopen(req, timeout=15) as resp:
30
+ data = json.loads(resp.read())
31
+ results = [
32
+ {
33
+ "title": r.get("title", ""),
34
+ "url": r.get("url", ""),
35
+ "description": r.get("content", "")[:300],
36
+ }
37
+ for r in data.get("results", [])
38
+ ]
39
+ return json.dumps({"query": args.get("query"), "results": results})
40
+ except Exception as e:
41
+ return json.dumps({"error": str(e), "results": []})
42
+
43
+
44
+ async def test_connection(credentials: dict[str, str]) -> tuple[bool, str]:
45
+ try:
46
+ result = json.loads(
47
+ await handle({"query": "latest AI news", "max_results": 1}, credentials)
48
+ )
49
+ if "error" in result:
50
+ return False, result["error"]
51
+ return True, f"Connected — {len(result.get('results', []))} result(s)"
52
+ except Exception as e:
53
+ return False, str(e)
@@ -0,0 +1,65 @@
1
+ """Weaviate semantic search handler."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import urllib.parse
7
+ import urllib.request
8
+ from typing import Any
9
+
10
+
11
+ async def handle(args: dict[str, Any], credentials: dict[str, str]) -> str:
12
+ base_url = credentials.get("base_url", "http://localhost:8080").rstrip("/")
13
+ class_name = credentials.get("class_name", "")
14
+ api_key = credentials.get("api_key", "")
15
+ if not class_name:
16
+ return json.dumps({"error": "weaviate_search: class_name required", "matches": []})
17
+ headers: dict[str, str] = {"Content-Type": "application/json"}
18
+ if api_key:
19
+ headers["Authorization"] = f"Bearer {api_key}"
20
+ graphql_query = json.dumps(
21
+ {
22
+ "query": f"""{{
23
+ Get {{
24
+ {class_name}(limit: {int(args.get("top_k", 5))}, bm25: {{query: {json.dumps(args.get("query", ""))}}}) {{
25
+ _additional {{ id score }}
26
+ }}
27
+ }}
28
+ }}"""
29
+ }
30
+ ).encode()
31
+ try:
32
+ req = urllib.request.Request(
33
+ f"{base_url}/v1/graphql",
34
+ data=graphql_query,
35
+ headers=headers,
36
+ method="POST",
37
+ )
38
+ with urllib.request.urlopen(req, timeout=10) as resp:
39
+ data = json.loads(resp.read())
40
+ if "errors" in data:
41
+ return json.dumps({"error": str(data["errors"]), "matches": []})
42
+ hits = data.get("data", {}).get("Get", {}).get(class_name, [])
43
+ return json.dumps(
44
+ {"query": args.get("query"), "matches": hits[: int(args.get("top_k", 5))]}
45
+ )
46
+ except Exception as e:
47
+ return json.dumps({"error": str(e), "matches": []})
48
+
49
+
50
+ async def test_connection(credentials: dict[str, str]) -> tuple[bool, str]:
51
+ base_url = credentials.get("base_url", "http://localhost:8080").rstrip("/")
52
+ api_key = credentials.get("api_key", "")
53
+ headers: dict[str, str] = {}
54
+ if api_key:
55
+ headers["Authorization"] = f"Bearer {api_key}"
56
+ try:
57
+ req = urllib.request.Request(f"{base_url}/v1/schema", headers=headers)
58
+ with urllib.request.urlopen(req, timeout=10) as resp:
59
+ data = json.loads(resp.read())
60
+ classes = [c.get("class") for c in data.get("classes", [])]
61
+ return True, f"Connected to Weaviate — {len(classes)} class(es)"
62
+ except Exception as e:
63
+ return False, str(e)
64
+
65
+
@@ -0,0 +1,64 @@
1
+ """Wikipedia search handler — no API key required."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import urllib.parse
7
+ import urllib.request
8
+ from typing import Any
9
+
10
+ _UA = "HyperMindAgent/0.9 (https://github.com/ZySec-AI/hypermind; tool_handler)"
11
+
12
+
13
+ async def handle(args: dict[str, Any], credentials: dict[str, str]) -> str:
14
+ query = args.get("query", "")
15
+ # Step 1: search for article titles
16
+ search_qs = urllib.parse.urlencode(
17
+ {
18
+ "action": "query",
19
+ "list": "search",
20
+ "srsearch": query,
21
+ "srlimit": int(args.get("max_results", 3)),
22
+ "format": "json",
23
+ }
24
+ )
25
+ try:
26
+ req = urllib.request.Request(
27
+ f"https://en.wikipedia.org/w/api.php?{search_qs}",
28
+ headers={"User-Agent": _UA},
29
+ )
30
+ with urllib.request.urlopen(req, timeout=10) as resp:
31
+ search_data = json.loads(resp.read())
32
+ hits = search_data.get("query", {}).get("search", [])
33
+ if not hits:
34
+ return json.dumps({"query": query, "results": []})
35
+ results = []
36
+ for hit in hits[:3]:
37
+ title = hit.get("title", "")
38
+ snippet = (
39
+ hit.get("snippet", "")
40
+ .replace('<span class="searchmatch">', "")
41
+ .replace("</span>", "")
42
+ )
43
+ results.append(
44
+ {
45
+ "title": title,
46
+ "snippet": snippet,
47
+ "url": f"https://en.wikipedia.org/wiki/{urllib.parse.quote(title)}",
48
+ }
49
+ )
50
+ return json.dumps({"query": query, "results": results})
51
+ except Exception as e:
52
+ return json.dumps({"error": str(e), "query": query, "results": []})
53
+
54
+
55
+ async def test_connection(credentials: dict[str, str]) -> tuple[bool, str]:
56
+ try:
57
+ result = json.loads(
58
+ await handle({"query": "artificial intelligence", "max_results": 1}, credentials)
59
+ )
60
+ if "error" in result:
61
+ return False, result["error"]
62
+ return True, f"Connected — Wikipedia returning {len(result.get('results', []))} result(s)"
63
+ except Exception as e:
64
+ return False, str(e)
@@ -0,0 +1,48 @@
1
+ """Wolfram Alpha handler."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import urllib.parse
7
+ import urllib.request
8
+ from typing import Any
9
+
10
+
11
+ async def handle(args: dict[str, Any], credentials: dict[str, str]) -> str:
12
+ api_key = credentials.get("api_key", "")
13
+ if not api_key:
14
+ return json.dumps({"error": "wolfram_alpha: no api_key configured"})
15
+ query = args.get("query", "")
16
+ qs = urllib.parse.urlencode(
17
+ {"appid": api_key, "input": query, "output": "json", "format": "plaintext"}
18
+ )
19
+ try:
20
+ with urllib.request.urlopen(
21
+ f"https://api.wolframalpha.com/v2/query?{qs}", timeout=15
22
+ ) as resp:
23
+ data = json.loads(resp.read())
24
+ if not data.get("queryresult", {}).get("success"):
25
+ return json.dumps(
26
+ {"query": query, "answer": None, "error": "No result from Wolfram Alpha"}
27
+ )
28
+ pods = data["queryresult"].get("pods", [])
29
+ answers = []
30
+ for pod in pods[:4]:
31
+ title = pod.get("title", "")
32
+ for sub in pod.get("subpods", []):
33
+ text = sub.get("plaintext", "").strip()
34
+ if text:
35
+ answers.append({"title": title, "text": text})
36
+ return json.dumps({"query": query, "answers": answers})
37
+ except Exception as e:
38
+ return json.dumps({"error": str(e), "query": query})
39
+
40
+
41
+ async def test_connection(credentials: dict[str, str]) -> tuple[bool, str]:
42
+ try:
43
+ result = json.loads(await handle({"query": "2+2"}, credentials))
44
+ if "error" in result:
45
+ return False, result["error"]
46
+ return True, "Connected — Wolfram Alpha responding"
47
+ except Exception as e:
48
+ return False, str(e)
@@ -0,0 +1,49 @@
1
+ """Yahoo Finance handler — no API key required."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import urllib.parse
7
+ import urllib.request
8
+ from typing import Any
9
+
10
+
11
+ async def handle(args: dict[str, Any], credentials: dict[str, str]) -> str:
12
+ symbol = args.get("symbol", "").upper()
13
+ period = args.get("period", "1mo")
14
+ url = f"https://query1.finance.yahoo.com/v8/finance/chart/{urllib.parse.quote(symbol)}?range={period}&interval=1d"
15
+ headers = {"User-Agent": "Mozilla/5.0"}
16
+ req = urllib.request.Request(url, headers=headers)
17
+ try:
18
+ with urllib.request.urlopen(req, timeout=10) as resp:
19
+ data = json.loads(resp.read())
20
+ result = data.get("chart", {}).get("result", [])
21
+ if not result:
22
+ return json.dumps({"error": f"No data for {symbol}", "symbol": symbol})
23
+ meta = result[0].get("meta", {})
24
+ return json.dumps(
25
+ {
26
+ "symbol": symbol,
27
+ "regularMarketPrice": meta.get("regularMarketPrice"),
28
+ "currency": meta.get("currency"),
29
+ "exchangeName": meta.get("exchangeName"),
30
+ "fiftyTwoWeekHigh": meta.get("fiftyTwoWeekHigh"),
31
+ "fiftyTwoWeekLow": meta.get("fiftyTwoWeekLow"),
32
+ "period": period,
33
+ }
34
+ )
35
+ except Exception as e:
36
+ return json.dumps({"error": str(e), "symbol": symbol})
37
+
38
+
39
+ async def test_connection(credentials: dict[str, str]) -> tuple[bool, str]:
40
+ try:
41
+ result = json.loads(await handle({"symbol": "AAPL", "period": "5d"}, credentials))
42
+ if "error" in result:
43
+ return False, result["error"]
44
+ return (
45
+ True,
46
+ f"Connected — AAPL at {result.get('regularMarketPrice', '?')} {result.get('currency', '')}",
47
+ )
48
+ except Exception as e:
49
+ return False, str(e)