axel-protocol 2.1.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.
axel/__init__.py ADDED
@@ -0,0 +1,80 @@
1
+ """
2
+ axel — Agent eXchange Language
3
+ ================================
4
+ A universal protocol for multi-LLM networks.
5
+
6
+ Quick start:
7
+ from axel import AXELClient, AXELAgent, AXELServer
8
+
9
+ # Start the hub (with optional auth + persistence)
10
+ server = AXELServer(key="secret", db="axel.db")
11
+ server.start_background()
12
+
13
+ # Connect an agent
14
+ client = AXELClient("http://localhost:7331", "my_agent",
15
+ model="claude-3-5-haiku", provider="anthropic",
16
+ caps=["research", "summarize"],
17
+ key="secret")
18
+
19
+ # Execute a task on another agent
20
+ result = client.execute("writer", "draft_content", {"topic": "AI"})
21
+
22
+ Testing:
23
+ from axel.testing import MockServer, MessageCapture
24
+
25
+ with MockServer() as url:
26
+ c = AXELClient(url, "tester", model="x", provider="mock", caps=[])
27
+ ...
28
+ """
29
+
30
+ from axel.core import AXELBuilder, AXELMessage
31
+ from axel.client import AXELClient, AXELAgent
32
+ from axel.server import AXELServer
33
+ from axel.universal import SmartMemory
34
+ from axel.adapters import (
35
+ # bundled
36
+ MockAdapter, AnthropicAdapter, OpenAIAdapter, OllamaAdapter, OpenRouterAdapter,
37
+ # optional (lazy-loaded)
38
+ GeminiAdapter, MistralAdapter, GroqAdapter,
39
+ TogetherAdapter, CohereAdapter, LiteLLMAdapter, BedrockAdapter,
40
+ # factory
41
+ make_adapter, get_adapter,
42
+ )
43
+
44
+ __version__ = "2.1.0"
45
+ __author__ = "Sector X"
46
+ __license__ = "MIT"
47
+
48
+ __all__ = [
49
+ # Core message types
50
+ "AXELMessage",
51
+ "AXELBuilder",
52
+ # Client SDK
53
+ "AXELClient",
54
+ "AXELAgent",
55
+ # Server
56
+ "AXELServer",
57
+ # Memory
58
+ "SmartMemory",
59
+ # Adapters — bundled
60
+ "MockAdapter",
61
+ "AnthropicAdapter",
62
+ "OpenAIAdapter",
63
+ "OllamaAdapter",
64
+ "OpenRouterAdapter",
65
+ # Adapters — optional providers
66
+ "GeminiAdapter",
67
+ "MistralAdapter",
68
+ "GroqAdapter",
69
+ "TogetherAdapter",
70
+ "CohereAdapter",
71
+ "LiteLLMAdapter",
72
+ "BedrockAdapter",
73
+ # Factory helpers
74
+ "make_adapter",
75
+ "get_adapter",
76
+ # Testing utilities
77
+ "testing",
78
+ # Persistence
79
+ "persistence",
80
+ ]
@@ -0,0 +1,148 @@
1
+ """
2
+ axel.adapters — LLM provider adapters for the AXEL network
3
+ ===========================================================
4
+
5
+ All adapters share the same interface: instantiate with an agent_id + model,
6
+ then call adapter.execute(task_msg) to get back an AXELMessage.
7
+
8
+ Quick reference
9
+ ---------------
10
+ Provider Class Install Env var
11
+ ────────────── ──────────────── ──────────────────────── ────────────────────────
12
+ OpenRouter OpenRouterAdapter pip install openai OPENROUTER_API_KEY ← start here
13
+ Anthropic AnthropicAdapter (bundled) ANTHROPIC_API_KEY
14
+ OpenAI OpenAIAdapter (bundled) OPENAI_API_KEY
15
+ Ollama (local) OllamaAdapter (bundled) —
16
+ Google Gemini GeminiAdapter google-generativeai GOOGLE_API_KEY
17
+ Mistral AI MistralAdapter mistralai MISTRAL_API_KEY
18
+ Groq GroqAdapter groq GROQ_API_KEY
19
+ Together AI TogetherAdapter together TOGETHER_API_KEY
20
+ Cohere CohereAdapter cohere COHERE_API_KEY
21
+ AWS Bedrock BedrockAdapter boto3 AWS_* credentials
22
+ Any (LiteLLM) LiteLLMAdapter litellm provider-specific
23
+ Mock (testing) MockAdapter (bundled) —
24
+
25
+ Usage
26
+ -----
27
+ from axel.adapters import GeminiAdapter, GroqAdapter, LiteLLMAdapter
28
+
29
+ # Google Gemini researcher
30
+ researcher = GeminiAdapter("researcher",
31
+ model="gemini-1.5-flash",
32
+ capabilities=["research", "summarize"])
33
+
34
+ # Groq fast reviewer
35
+ reviewer = GroqAdapter("reviewer",
36
+ model="llama-3.1-8b-instant",
37
+ capabilities=["review", "score"])
38
+
39
+ # LiteLLM — any model, one adapter
40
+ writer = LiteLLMAdapter("writer",
41
+ model="groq/llama-3.3-70b-versatile",
42
+ capabilities=["write", "edit"])
43
+
44
+ # Register with the AXEL server bridge
45
+ bridge.add_adapter("researcher", researcher)
46
+ bridge.add_adapter("reviewer", reviewer)
47
+ bridge.add_adapter("writer", writer)
48
+ """
49
+
50
+ # ── Bundled adapters (no extra install) ───────────────────────────
51
+ from axel.universal import (
52
+ AnthropicAdapter,
53
+ OpenAIAdapter,
54
+ OllamaAdapter,
55
+ OpenRouterAdapter,
56
+ MockAdapter,
57
+ BaseAdapter,
58
+ )
59
+
60
+ # ── Optional provider adapters ────────────────────────────────────
61
+ # All use lazy imports internally — importing these classes is free.
62
+ # The provider SDK is only loaded when execute() is first called.
63
+
64
+ from axel.adapters.gemini import GeminiAdapter
65
+ from axel.adapters.mistral import MistralAdapter
66
+ from axel.adapters.groq import GroqAdapter
67
+ from axel.adapters.together import TogetherAdapter
68
+ from axel.adapters.cohere import CohereAdapter
69
+ from axel.adapters.litellm import LiteLLMAdapter
70
+ from axel.adapters.bedrock import BedrockAdapter
71
+
72
+ __all__ = [
73
+ # Bundled
74
+ "BaseAdapter",
75
+ "MockAdapter",
76
+ "AnthropicAdapter",
77
+ "OpenAIAdapter",
78
+ "OllamaAdapter",
79
+ "OpenRouterAdapter",
80
+ # Optional
81
+ "GeminiAdapter",
82
+ "MistralAdapter",
83
+ "GroqAdapter",
84
+ "TogetherAdapter",
85
+ "CohereAdapter",
86
+ "LiteLLMAdapter",
87
+ "BedrockAdapter",
88
+ ]
89
+
90
+ # ── Provider registry — useful for dynamic instantiation ──────────
91
+ PROVIDERS: dict[str, type] = {
92
+ "anthropic": AnthropicAdapter,
93
+ "openai": OpenAIAdapter,
94
+ "ollama": OllamaAdapter,
95
+ "google": GeminiAdapter,
96
+ "gemini": GeminiAdapter,
97
+ "mistral": MistralAdapter,
98
+ "groq": GroqAdapter,
99
+ "together": TogetherAdapter,
100
+ "cohere": CohereAdapter,
101
+ "litellm": LiteLLMAdapter,
102
+ "bedrock": BedrockAdapter,
103
+ "aws": BedrockAdapter,
104
+ "openrouter": OpenRouterAdapter,
105
+ "mock": MockAdapter,
106
+ }
107
+
108
+
109
+ def get_adapter(provider: str) -> type:
110
+ """
111
+ Lookup an adapter class by provider name (case-insensitive).
112
+
113
+ Example::
114
+
115
+ AdapterClass = get_adapter("groq")
116
+ adapter = AdapterClass("my_agent", model="llama-3.1-8b-instant")
117
+ """
118
+ key = provider.lower()
119
+ if key not in PROVIDERS:
120
+ available = ", ".join(sorted(PROVIDERS))
121
+ raise ValueError(
122
+ f"Unknown provider '{provider}'. Available: {available}"
123
+ )
124
+ return PROVIDERS[key]
125
+
126
+
127
+ def make_adapter(
128
+ agent_id: str,
129
+ provider: str,
130
+ model: str,
131
+ capabilities: list[str] | None = None,
132
+ **kwargs,
133
+ ) -> BaseAdapter:
134
+ """
135
+ Factory: create any adapter by provider name.
136
+
137
+ Example::
138
+
139
+ adapter = make_adapter("researcher", "groq",
140
+ "llama-3.3-70b-versatile",
141
+ capabilities=["research"])
142
+ result = adapter.execute(task_msg)
143
+ """
144
+ AdapterClass = get_adapter(provider)
145
+ # MockAdapter has a simplified signature
146
+ if AdapterClass is MockAdapter:
147
+ return AdapterClass(agent_id)
148
+ return AdapterClass(agent_id, model=model, capabilities=capabilities, **kwargs)
@@ -0,0 +1,153 @@
1
+ """
2
+ axel/adapters/bedrock.py — AWS Bedrock adapter for AXEL
3
+
4
+ Bedrock gives access to frontier models (Claude, Llama, Mistral, Titan)
5
+ inside your AWS VPC — no data leaves your cloud environment.
6
+ Ideal for enterprise deployments with strict data residency requirements.
7
+
8
+ Supported model IDs (as of 2024):
9
+ anthropic.claude-3-5-haiku-20241022-v1:0 Claude 3.5 Haiku
10
+ anthropic.claude-3-5-sonnet-20241022-v2:0 Claude 3.5 Sonnet
11
+ anthropic.claude-3-opus-20240229-v1:0 Claude 3 Opus
12
+ meta.llama3-1-70b-instruct-v1:0 Llama 3.1 70B
13
+ meta.llama3-1-8b-instruct-v1:0 Llama 3.1 8B
14
+ mistral.mistral-large-2402-v1:0 Mistral Large
15
+ amazon.titan-text-premier-v1:0 Amazon Titan
16
+
17
+ Install:
18
+ pip install boto3
19
+
20
+ Auth:
21
+ AWS credentials via env vars, ~/.aws/credentials, or IAM role:
22
+ export AWS_ACCESS_KEY_ID=...
23
+ export AWS_SECRET_ACCESS_KEY=...
24
+ export AWS_DEFAULT_REGION=us-east-1
25
+
26
+ Permissions needed:
27
+ bedrock:InvokeModel on arn:aws:bedrock:*::foundation-model/*
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import json
33
+ import os
34
+ import re
35
+ from typing import List, Optional
36
+
37
+ from axel.universal import BaseAdapter, make_system_prompt, AXELMessage
38
+
39
+
40
+ class BedrockAdapter(BaseAdapter):
41
+ """
42
+ Execute AXEL TASK messages using AWS Bedrock.
43
+
44
+ Bedrock uses the Converse API (unified chat interface across all models),
45
+ so this adapter works consistently regardless of which model is selected.
46
+
47
+ Example::
48
+
49
+ adapter = BedrockAdapter(
50
+ "secure_researcher",
51
+ model_id="anthropic.claude-3-5-haiku-20241022-v1:0",
52
+ region="us-east-1",
53
+ capabilities=["research", "summarize"],
54
+ )
55
+ result = adapter.execute(task_msg)
56
+
57
+ Notes:
58
+ - Model access must be enabled in the Bedrock console for your region
59
+ - Cross-region inference profiles are supported (prefix with region code)
60
+ """
61
+
62
+ PROVIDER = "bedrock"
63
+ DEFAULT_MODEL = "anthropic.claude-3-5-haiku-20241022-v1:0"
64
+
65
+ def __init__(
66
+ self,
67
+ agent_id: str,
68
+ model_id: str = DEFAULT_MODEL,
69
+ capabilities: Optional[List[str]] = None,
70
+ region: Optional[str] = None,
71
+ aws_access_key_id: Optional[str] = None,
72
+ aws_secret_access_key: Optional[str] = None,
73
+ aws_session_token: Optional[str] = None,
74
+ max_tokens: int = 2048,
75
+ temperature: float = 0.7,
76
+ ):
77
+ system = make_system_prompt(agent_id, capabilities or [], f"bedrock/{model_id}")
78
+ super().__init__(agent_id, system)
79
+ self.model_id = model_id
80
+ self.max_tokens = max_tokens
81
+ self.temperature = temperature
82
+ self._region = region or os.environ.get("AWS_DEFAULT_REGION", "us-east-1")
83
+ self._key_id = aws_access_key_id
84
+ self._secret = aws_secret_access_key
85
+ self._token = aws_session_token
86
+ self._client = None
87
+
88
+ def _get_client(self):
89
+ if self._client is None:
90
+ try:
91
+ import boto3
92
+ except ImportError:
93
+ raise RuntimeError(
94
+ "boto3 not installed.\n"
95
+ "Run: pip install boto3"
96
+ )
97
+ session_kwargs = {}
98
+ if self._key_id:
99
+ session_kwargs["aws_access_key_id"] = self._key_id
100
+ if self._secret:
101
+ session_kwargs["aws_secret_access_key"] = self._secret
102
+ if self._token:
103
+ session_kwargs["aws_session_token"] = self._token
104
+
105
+ session = boto3.Session(**session_kwargs) if session_kwargs else boto3.Session()
106
+ self._client = session.client("bedrock-runtime", region_name=self._region)
107
+ return self._client
108
+
109
+ def execute(self, task_msg: AXELMessage) -> AXELMessage:
110
+ action = task_msg.body.get("act", "unknown")
111
+ args = task_msg.body.get("args", {})
112
+ ctx = task_msg.body.get("ctx", {})
113
+
114
+ user_content = (
115
+ f"AXEL TASK:\n{task_msg.to_json(compact=False)}\n\n"
116
+ f"Execute '{action}' with args: {json.dumps(args)}\n"
117
+ f"Context: {json.dumps(ctx)}\n\n"
118
+ f"Respond with a single compact AXEL OK or ER JSON message."
119
+ )
120
+
121
+ try:
122
+ client = self._get_client()
123
+
124
+ # Bedrock Converse API — unified interface across all models
125
+ resp = client.converse(
126
+ modelId=self.model_id,
127
+ system=[{"text": self.system_prompt}],
128
+ messages=[{"role": "user", "content": [{"text": user_content}]}],
129
+ inferenceConfig={
130
+ "maxTokens": self.max_tokens,
131
+ "temperature": self.temperature,
132
+ },
133
+ )
134
+ raw = resp["output"]["message"]["content"][0]["text"].strip()
135
+
136
+ try:
137
+ match = re.search(r"\{.*\}", raw, re.DOTALL)
138
+ if match:
139
+ parsed = json.loads(match.group())
140
+ return AXELMessage.from_dict({
141
+ **parsed,
142
+ "fr": self.agent_id,
143
+ "to": task_msg.fr,
144
+ "cid": task_msg.cid,
145
+ "re": task_msg.id,
146
+ })
147
+ except Exception:
148
+ pass
149
+
150
+ return self._ok(task_msg, {"text": raw})
151
+
152
+ except Exception as exc:
153
+ return self._err(task_msg, "INTERNAL", str(exc), retry=True)
@@ -0,0 +1,121 @@
1
+ """
2
+ axel/adapters/cohere.py — Cohere adapter for AXEL
3
+
4
+ Cohere specialises in enterprise NLP: RAG, classification, reranking, and embeddings.
5
+ Their Command R+ model is particularly strong for tool-use and multi-step reasoning.
6
+
7
+ Supported models (as of 2024):
8
+ command-r-plus best quality, tool-use, 128k context
9
+ command-r balanced speed/quality, 128k context
10
+ command-light fast and cheap
11
+ command production-ready general model
12
+
13
+ Install:
14
+ pip install cohere
15
+
16
+ Auth:
17
+ export COHERE_API_KEY=your_key
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import json
23
+ import os
24
+ import re
25
+ from typing import List, Optional
26
+
27
+ from axel.universal import BaseAdapter, make_system_prompt, AXELMessage
28
+
29
+
30
+ class CohereAdapter(BaseAdapter):
31
+ """
32
+ Execute AXEL TASK messages using Cohere.
33
+
34
+ Cohere's Command R+ is excellent for RAG-augmented tasks and tool use,
35
+ making it well-suited for research and retrieval agent roles in a network.
36
+
37
+ Example::
38
+
39
+ adapter = CohereAdapter(
40
+ "rag_researcher",
41
+ model="command-r-plus",
42
+ capabilities=["research", "rag_query", "rerank"],
43
+ )
44
+ result = adapter.execute(task_msg)
45
+ """
46
+
47
+ PROVIDER = "cohere"
48
+ DEFAULT_MODEL = "command-r-plus"
49
+
50
+ def __init__(
51
+ self,
52
+ agent_id: str,
53
+ model: str = DEFAULT_MODEL,
54
+ capabilities: Optional[List[str]] = None,
55
+ api_key: Optional[str] = None,
56
+ temperature: float = 0.7,
57
+ max_tokens: int = 2048,
58
+ ):
59
+ system = make_system_prompt(agent_id, capabilities or [], f"cohere/{model}")
60
+ super().__init__(agent_id, system)
61
+ self.model = model
62
+ self.temperature = temperature
63
+ self.max_tokens = max_tokens
64
+ self._api_key = api_key or os.environ.get("COHERE_API_KEY")
65
+ self._client = None
66
+
67
+ def _get_client(self):
68
+ if self._client is None:
69
+ try:
70
+ import cohere
71
+ except ImportError:
72
+ raise RuntimeError(
73
+ "Cohere SDK not installed.\n"
74
+ "Run: pip install cohere"
75
+ )
76
+ self._client = cohere.ClientV2(api_key=self._api_key)
77
+ return self._client
78
+
79
+ def execute(self, task_msg: AXELMessage) -> AXELMessage:
80
+ action = task_msg.body.get("act", "unknown")
81
+ args = task_msg.body.get("args", {})
82
+ ctx = task_msg.body.get("ctx", {})
83
+
84
+ user_content = (
85
+ f"AXEL TASK:\n{task_msg.to_json(compact=False)}\n\n"
86
+ f"Execute '{action}' with args: {json.dumps(args)}\n"
87
+ f"Context: {json.dumps(ctx)}\n\n"
88
+ f"Respond with a single compact AXEL OK or ER JSON message."
89
+ )
90
+
91
+ try:
92
+ client = self._get_client()
93
+ resp = client.chat(
94
+ model=self.model,
95
+ messages=[
96
+ {"role": "system", "content": self.system_prompt},
97
+ {"role": "user", "content": user_content},
98
+ ],
99
+ temperature=self.temperature,
100
+ max_tokens=self.max_tokens,
101
+ )
102
+ raw = resp.message.content[0].text.strip()
103
+
104
+ try:
105
+ match = re.search(r"\{.*\}", raw, re.DOTALL)
106
+ if match:
107
+ parsed = json.loads(match.group())
108
+ return AXELMessage.from_dict({
109
+ **parsed,
110
+ "fr": self.agent_id,
111
+ "to": task_msg.fr,
112
+ "cid": task_msg.cid,
113
+ "re": task_msg.id,
114
+ })
115
+ except Exception:
116
+ pass
117
+
118
+ return self._ok(task_msg, {"text": raw})
119
+
120
+ except Exception as exc:
121
+ return self._err(task_msg, "INTERNAL", str(exc), retry=True)
@@ -0,0 +1,119 @@
1
+ """
2
+ axel/adapters/gemini.py — Google Gemini adapter for AXEL
3
+
4
+ Supported models (as of 2024):
5
+ gemini-1.5-flash fast, cheap, 1M context
6
+ gemini-1.5-pro flagship, multimodal, 2M context
7
+ gemini-2.0-flash next-gen fast model
8
+
9
+ Install:
10
+ pip install google-generativeai
11
+
12
+ Auth:
13
+ export GOOGLE_API_KEY=your_key
14
+ # OR pass api_key= directly
15
+ # OR use Application Default Credentials (ADC) on GCP/Vertex
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import json
21
+ import os
22
+ import re
23
+ from typing import List, Optional
24
+
25
+ from axel.universal import BaseAdapter, make_system_prompt, AXELMessage
26
+
27
+
28
+ class GeminiAdapter(BaseAdapter):
29
+ """
30
+ Execute AXEL TASK messages using Google Gemini.
31
+
32
+ Example::
33
+
34
+ adapter = GeminiAdapter(
35
+ "researcher",
36
+ model="gemini-1.5-flash",
37
+ capabilities=["research", "summarize"],
38
+ )
39
+ result = adapter.execute(task_msg)
40
+ """
41
+
42
+ PROVIDER = "google"
43
+ DEFAULT_MODEL = "gemini-1.5-flash"
44
+
45
+ def __init__(
46
+ self,
47
+ agent_id: str,
48
+ model: str = DEFAULT_MODEL,
49
+ capabilities: Optional[List[str]] = None,
50
+ api_key: Optional[str] = None,
51
+ temperature: float = 0.7,
52
+ max_tokens: int = 2048,
53
+ ):
54
+ system = make_system_prompt(agent_id, capabilities or [], f"gemini/{model}")
55
+ super().__init__(agent_id, system)
56
+ self.model = model
57
+ self.temperature = temperature
58
+ self.max_tokens = max_tokens
59
+ self._api_key = api_key or os.environ.get("GOOGLE_API_KEY")
60
+ self._client = None
61
+
62
+ def _get_client(self):
63
+ if self._client is None:
64
+ try:
65
+ import google.generativeai as genai
66
+ except ImportError:
67
+ raise RuntimeError(
68
+ "Google Generative AI SDK not installed.\n"
69
+ "Run: pip install google-generativeai"
70
+ )
71
+ if self._api_key:
72
+ genai.configure(api_key=self._api_key)
73
+ # else relies on GOOGLE_API_KEY env var or ADC
74
+ self._client = genai.GenerativeModel(
75
+ model_name=self.model,
76
+ system_instruction=self.system_prompt,
77
+ generation_config={
78
+ "temperature": self.temperature,
79
+ "max_output_tokens": self.max_tokens,
80
+ },
81
+ )
82
+ return self._client
83
+
84
+ def execute(self, task_msg: AXELMessage) -> AXELMessage:
85
+ action = task_msg.body.get("act", "unknown")
86
+ args = task_msg.body.get("args", {})
87
+ ctx = task_msg.body.get("ctx", {})
88
+
89
+ prompt = (
90
+ f"AXEL TASK received:\n"
91
+ f"{task_msg.to_json(compact=False)}\n\n"
92
+ f"Execute action '{action}' with args: {json.dumps(args)}\n"
93
+ f"Context: {json.dumps(ctx)}\n\n"
94
+ f"Respond with a single compact AXEL OK or ER JSON message."
95
+ )
96
+
97
+ try:
98
+ model = self._get_client()
99
+ resp = model.generate_content(prompt)
100
+ raw = resp.text.strip()
101
+
102
+ try:
103
+ match = re.search(r"\{.*\}", raw, re.DOTALL)
104
+ if match:
105
+ parsed = json.loads(match.group())
106
+ return AXELMessage.from_dict({
107
+ **parsed,
108
+ "fr": self.agent_id,
109
+ "to": task_msg.fr,
110
+ "cid": task_msg.cid,
111
+ "re": task_msg.id,
112
+ })
113
+ except Exception:
114
+ pass
115
+
116
+ return self._ok(task_msg, {"text": raw})
117
+
118
+ except Exception as exc:
119
+ return self._err(task_msg, "INTERNAL", str(exc), retry=True)