ainative-python 2.0.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.
- ainative/__init__.py +46 -0
- ainative/agent_coordination.py +249 -0
- ainative/agent_identity_system.py +1566 -0
- ainative/agent_learning.py +239 -0
- ainative/agent_orchestration.py +202 -0
- ainative/agent_state.py +231 -0
- ainative/agent_swarm/__init__.py +510 -0
- ainative/auth.py +113 -0
- ainative/cli.py +698 -0
- ainative/cli_utils/__init__.py +13 -0
- ainative/cli_utils/diff.py +292 -0
- ainative/cli_utils/formatters.py +227 -0
- ainative/client.py +272 -0
- ainative/commands/__init__.py +28 -0
- ainative/commands/agents.py +238 -0
- ainative/commands/coordination.py +108 -0
- ainative/commands/inspect.py +483 -0
- ainative/commands/learning.py +119 -0
- ainative/commands/local.py +544 -0
- ainative/commands/state.py +144 -0
- ainative/commands/swarm.py +184 -0
- ainative/commands/sync.py +157 -0
- ainative/commands/tasks.py +191 -0
- ainative/exceptions.py +87 -0
- ainative/zerodb/__init__.py +89 -0
- ainative/zerodb/analytics.py +232 -0
- ainative/zerodb/memory.py +260 -0
- ainative/zerodb/projects.py +224 -0
- ainative/zerodb/tables.py +362 -0
- ainative/zerodb/vectors.py +231 -0
- ainative_python-2.0.0.dist-info/METADATA +550 -0
- ainative_python-2.0.0.dist-info/RECORD +35 -0
- ainative_python-2.0.0.dist-info/WHEEL +5 -0
- ainative_python-2.0.0.dist-info/entry_points.txt +2 -0
- ainative_python-2.0.0.dist-info/top_level.txt +1 -0
ainative/client.py
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AINative SDK Main Client
|
|
3
|
+
|
|
4
|
+
Core client for interacting with AINative Studio APIs.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional, Dict, Any, Union
|
|
8
|
+
import httpx
|
|
9
|
+
from urllib.parse import urljoin
|
|
10
|
+
import json
|
|
11
|
+
import time
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
|
|
14
|
+
from .auth import AuthConfig, APIKeyAuth
|
|
15
|
+
from .exceptions import (
|
|
16
|
+
APIError,
|
|
17
|
+
NetworkError,
|
|
18
|
+
RateLimitError,
|
|
19
|
+
AuthenticationError,
|
|
20
|
+
)
|
|
21
|
+
from .zerodb import ZeroDBClient
|
|
22
|
+
from .agent_swarm import AgentSwarmClient
|
|
23
|
+
from .agent_orchestration import AgentOrchestrationClient
|
|
24
|
+
from .agent_coordination import AgentCoordinationClient
|
|
25
|
+
from .agent_learning import AgentLearningClient
|
|
26
|
+
from .agent_state import AgentStateClient
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class ClientConfig:
|
|
31
|
+
"""Configuration for the AINative client."""
|
|
32
|
+
|
|
33
|
+
base_url: str = "https://api.ainative.studio"
|
|
34
|
+
timeout: int = 30
|
|
35
|
+
max_retries: int = 3
|
|
36
|
+
retry_delay: float = 1.0
|
|
37
|
+
verify_ssl: bool = True
|
|
38
|
+
debug: bool = False
|
|
39
|
+
|
|
40
|
+
def __post_init__(self):
|
|
41
|
+
"""Validate and normalize configuration."""
|
|
42
|
+
# Ensure base URL doesn't end with slash
|
|
43
|
+
self.base_url = self.base_url.rstrip("/")
|
|
44
|
+
|
|
45
|
+
# Add API version if not present (and doesn't already contain /api/)
|
|
46
|
+
if "/api/" not in self.base_url:
|
|
47
|
+
self.base_url = f"{self.base_url}/api/v1"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class AINativeClient:
|
|
51
|
+
"""Main client for AINative Studio API operations."""
|
|
52
|
+
|
|
53
|
+
def __init__(
|
|
54
|
+
self,
|
|
55
|
+
api_key: Optional[str] = None,
|
|
56
|
+
api_secret: Optional[str] = None,
|
|
57
|
+
base_url: Optional[str] = None,
|
|
58
|
+
organization_id: Optional[str] = None,
|
|
59
|
+
config: Optional[ClientConfig] = None,
|
|
60
|
+
auth_config: Optional[AuthConfig] = None,
|
|
61
|
+
):
|
|
62
|
+
"""
|
|
63
|
+
Initialize AINative client.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
api_key: Your AINative API key
|
|
67
|
+
api_secret: Your AINative API secret (optional, for enhanced security)
|
|
68
|
+
base_url: Override default API base URL
|
|
69
|
+
organization_id: Organization ID for multi-tenant scenarios
|
|
70
|
+
config: Custom client configuration
|
|
71
|
+
auth_config: Custom authentication configuration
|
|
72
|
+
"""
|
|
73
|
+
# Set up configuration
|
|
74
|
+
self.config = config or ClientConfig()
|
|
75
|
+
if base_url:
|
|
76
|
+
self.config.base_url = base_url
|
|
77
|
+
|
|
78
|
+
# Set up authentication
|
|
79
|
+
if auth_config:
|
|
80
|
+
self.auth_config = auth_config
|
|
81
|
+
else:
|
|
82
|
+
self.auth_config = AuthConfig(
|
|
83
|
+
api_key=api_key,
|
|
84
|
+
api_secret=api_secret,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
self.auth = APIKeyAuth(self.auth_config)
|
|
88
|
+
self.organization_id = organization_id
|
|
89
|
+
|
|
90
|
+
# Initialize HTTP client
|
|
91
|
+
self._client = httpx.Client(
|
|
92
|
+
timeout=self.config.timeout,
|
|
93
|
+
verify=self.config.verify_ssl,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Initialize sub-clients
|
|
97
|
+
self._zerodb: Optional[ZeroDBClient] = None
|
|
98
|
+
self._agent_swarm: Optional[AgentSwarmClient] = None
|
|
99
|
+
self._agent_orchestration: Optional[AgentOrchestrationClient] = None
|
|
100
|
+
self._agent_coordination: Optional[AgentCoordinationClient] = None
|
|
101
|
+
self._agent_learning: Optional[AgentLearningClient] = None
|
|
102
|
+
self._agent_state: Optional[AgentStateClient] = None
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def zerodb(self) -> ZeroDBClient:
|
|
106
|
+
"""Get ZeroDB operations client."""
|
|
107
|
+
if not self._zerodb:
|
|
108
|
+
self._zerodb = ZeroDBClient(self)
|
|
109
|
+
return self._zerodb
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def agent_swarm(self) -> AgentSwarmClient:
|
|
113
|
+
"""Get Agent Swarm operations client."""
|
|
114
|
+
if not self._agent_swarm:
|
|
115
|
+
self._agent_swarm = AgentSwarmClient(self)
|
|
116
|
+
return self._agent_swarm
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def agent_orchestration(self) -> AgentOrchestrationClient:
|
|
120
|
+
"""Get Agent Orchestration operations client."""
|
|
121
|
+
if not self._agent_orchestration:
|
|
122
|
+
self._agent_orchestration = AgentOrchestrationClient(self)
|
|
123
|
+
return self._agent_orchestration
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def agent_coordination(self) -> AgentCoordinationClient:
|
|
127
|
+
"""Get Agent Coordination operations client."""
|
|
128
|
+
if not self._agent_coordination:
|
|
129
|
+
self._agent_coordination = AgentCoordinationClient(self)
|
|
130
|
+
return self._agent_coordination
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def agent_learning(self) -> AgentLearningClient:
|
|
134
|
+
"""Get Agent Learning operations client."""
|
|
135
|
+
if not self._agent_learning:
|
|
136
|
+
self._agent_learning = AgentLearningClient(self)
|
|
137
|
+
return self._agent_learning
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def agent_state(self) -> AgentStateClient:
|
|
141
|
+
"""Get Agent State operations client."""
|
|
142
|
+
if not self._agent_state:
|
|
143
|
+
self._agent_state = AgentStateClient(self)
|
|
144
|
+
return self._agent_state
|
|
145
|
+
|
|
146
|
+
def request(
|
|
147
|
+
self,
|
|
148
|
+
method: str,
|
|
149
|
+
endpoint: str,
|
|
150
|
+
data: Optional[Dict[str, Any]] = None,
|
|
151
|
+
params: Optional[Dict[str, Any]] = None,
|
|
152
|
+
headers: Optional[Dict[str, str]] = None,
|
|
153
|
+
**kwargs
|
|
154
|
+
) -> Dict[str, Any]:
|
|
155
|
+
"""
|
|
156
|
+
Make an authenticated request to the API.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
method: HTTP method (GET, POST, PUT, DELETE, etc.)
|
|
160
|
+
endpoint: API endpoint path
|
|
161
|
+
data: Request body data
|
|
162
|
+
params: Query parameters
|
|
163
|
+
headers: Additional headers
|
|
164
|
+
**kwargs: Additional arguments for httpx
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
Response data as dictionary
|
|
168
|
+
|
|
169
|
+
Raises:
|
|
170
|
+
APIError: For API-related errors
|
|
171
|
+
NetworkError: For network-related errors
|
|
172
|
+
RateLimitError: When rate limit is exceeded
|
|
173
|
+
"""
|
|
174
|
+
# Build full URL
|
|
175
|
+
url = urljoin(self.config.base_url, endpoint.lstrip("/"))
|
|
176
|
+
|
|
177
|
+
# Prepare headers
|
|
178
|
+
request_headers = self.auth.get_headers()
|
|
179
|
+
if headers:
|
|
180
|
+
request_headers.update(headers)
|
|
181
|
+
|
|
182
|
+
# Add organization ID if set
|
|
183
|
+
if self.organization_id:
|
|
184
|
+
request_headers["X-Organization-ID"] = self.organization_id
|
|
185
|
+
|
|
186
|
+
# Make request with retries
|
|
187
|
+
last_error = None
|
|
188
|
+
for attempt in range(self.config.max_retries):
|
|
189
|
+
try:
|
|
190
|
+
response = self._client.request(
|
|
191
|
+
method=method,
|
|
192
|
+
url=url,
|
|
193
|
+
json=data,
|
|
194
|
+
params=params,
|
|
195
|
+
headers=request_headers,
|
|
196
|
+
**kwargs
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# Handle rate limiting
|
|
200
|
+
if response.status_code == 429:
|
|
201
|
+
retry_after = int(response.headers.get("Retry-After", 60))
|
|
202
|
+
raise RateLimitError(retry_after=retry_after)
|
|
203
|
+
|
|
204
|
+
# Handle authentication errors
|
|
205
|
+
if response.status_code == 401:
|
|
206
|
+
raise AuthenticationError("Invalid API credentials")
|
|
207
|
+
|
|
208
|
+
# Handle other errors
|
|
209
|
+
if response.status_code >= 400:
|
|
210
|
+
raise APIError(
|
|
211
|
+
f"API error: {response.status_code}",
|
|
212
|
+
status_code=response.status_code,
|
|
213
|
+
response_body=response.text,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# Parse and return response
|
|
217
|
+
if response.text:
|
|
218
|
+
return response.json()
|
|
219
|
+
return {}
|
|
220
|
+
|
|
221
|
+
except httpx.NetworkError as e:
|
|
222
|
+
last_error = NetworkError(f"Network error: {str(e)}")
|
|
223
|
+
if attempt < self.config.max_retries - 1:
|
|
224
|
+
time.sleep(self.config.retry_delay * (attempt + 1))
|
|
225
|
+
continue
|
|
226
|
+
raise last_error
|
|
227
|
+
|
|
228
|
+
except httpx.TimeoutException:
|
|
229
|
+
last_error = NetworkError("Request timed out")
|
|
230
|
+
if attempt < self.config.max_retries - 1:
|
|
231
|
+
time.sleep(self.config.retry_delay * (attempt + 1))
|
|
232
|
+
continue
|
|
233
|
+
raise last_error
|
|
234
|
+
|
|
235
|
+
if last_error:
|
|
236
|
+
raise last_error
|
|
237
|
+
|
|
238
|
+
def get(self, endpoint: str, **kwargs) -> Dict[str, Any]:
|
|
239
|
+
"""Make a GET request."""
|
|
240
|
+
return self.request("GET", endpoint, **kwargs)
|
|
241
|
+
|
|
242
|
+
def post(self, endpoint: str, data: Optional[Dict[str, Any]] = None, **kwargs) -> Dict[str, Any]:
|
|
243
|
+
"""Make a POST request."""
|
|
244
|
+
return self.request("POST", endpoint, data=data, **kwargs)
|
|
245
|
+
|
|
246
|
+
def put(self, endpoint: str, data: Optional[Dict[str, Any]] = None, **kwargs) -> Dict[str, Any]:
|
|
247
|
+
"""Make a PUT request."""
|
|
248
|
+
return self.request("PUT", endpoint, data=data, **kwargs)
|
|
249
|
+
|
|
250
|
+
def delete(self, endpoint: str, **kwargs) -> Dict[str, Any]:
|
|
251
|
+
"""Make a DELETE request."""
|
|
252
|
+
return self.request("DELETE", endpoint, **kwargs)
|
|
253
|
+
|
|
254
|
+
def patch(self, endpoint: str, data: Optional[Dict[str, Any]] = None, **kwargs) -> Dict[str, Any]:
|
|
255
|
+
"""Make a PATCH request."""
|
|
256
|
+
return self.request("PATCH", endpoint, data=data, **kwargs)
|
|
257
|
+
|
|
258
|
+
def health_check(self) -> Dict[str, Any]:
|
|
259
|
+
"""Check API health status."""
|
|
260
|
+
return self.get("/health")
|
|
261
|
+
|
|
262
|
+
def close(self):
|
|
263
|
+
"""Close the HTTP client connection."""
|
|
264
|
+
self._client.close()
|
|
265
|
+
|
|
266
|
+
def __enter__(self):
|
|
267
|
+
"""Context manager entry."""
|
|
268
|
+
return self
|
|
269
|
+
|
|
270
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
271
|
+
"""Context manager exit."""
|
|
272
|
+
self.close()
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AINative CLI Module
|
|
3
|
+
|
|
4
|
+
Modular CLI structure with separate command groups.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .agents import agents_group
|
|
8
|
+
from .swarm import swarm_group
|
|
9
|
+
from .tasks import task_group
|
|
10
|
+
from .coordination import coordination_group
|
|
11
|
+
from .learning import learning_group
|
|
12
|
+
from .state import state_group
|
|
13
|
+
from .local import local_group
|
|
14
|
+
from .inspect import inspect_group
|
|
15
|
+
from .sync import sync_group
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"agents_group",
|
|
20
|
+
"swarm_group",
|
|
21
|
+
"task_group",
|
|
22
|
+
"coordination_group",
|
|
23
|
+
"learning_group",
|
|
24
|
+
"state_group",
|
|
25
|
+
"local_group",
|
|
26
|
+
"inspect_group",
|
|
27
|
+
"sync_group",
|
|
28
|
+
]
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent Identity CLI Commands
|
|
3
|
+
|
|
4
|
+
Commands for managing agent identities (list, show, create, export, preview).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
import json
|
|
9
|
+
import yaml
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
from rich.table import Table
|
|
14
|
+
from rich.panel import Panel
|
|
15
|
+
|
|
16
|
+
from ..agent_identity_system import AgentRegistry, AgentIdentity, SeedPrompt
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
console = Console()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@click.group(name="agents")
|
|
23
|
+
def agents_group():
|
|
24
|
+
"""Manage agent identities and configurations."""
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@agents_group.command(name="list")
|
|
29
|
+
@click.option("--format", type=click.Choice(["table", "json"]), default="table",
|
|
30
|
+
help="Output format")
|
|
31
|
+
def list_agents(format: str):
|
|
32
|
+
"""List all available agent identities."""
|
|
33
|
+
try:
|
|
34
|
+
registry = AgentRegistry()
|
|
35
|
+
agent_ids = registry.list_agents()
|
|
36
|
+
|
|
37
|
+
if format == "json":
|
|
38
|
+
output = []
|
|
39
|
+
for agent_id in agent_ids:
|
|
40
|
+
agent = registry.get_identity(agent_id)
|
|
41
|
+
output.append({
|
|
42
|
+
"id": agent.id,
|
|
43
|
+
"name": agent.name,
|
|
44
|
+
"role": agent.role_title,
|
|
45
|
+
"emoji": agent.emoji,
|
|
46
|
+
})
|
|
47
|
+
click.echo(json.dumps(output, indent=2))
|
|
48
|
+
else:
|
|
49
|
+
table = Table(title="Agent Identities")
|
|
50
|
+
table.add_column("ID", style="cyan")
|
|
51
|
+
table.add_column("Name", style="bold")
|
|
52
|
+
table.add_column("Role", style="dim")
|
|
53
|
+
table.add_column("Emoji", justify="center")
|
|
54
|
+
|
|
55
|
+
for agent_id in agent_ids:
|
|
56
|
+
agent = registry.get_identity(agent_id)
|
|
57
|
+
table.add_row(
|
|
58
|
+
agent.id,
|
|
59
|
+
agent.name,
|
|
60
|
+
agent.role_title,
|
|
61
|
+
agent.emoji
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
console.print(table)
|
|
65
|
+
|
|
66
|
+
except Exception as e:
|
|
67
|
+
click.echo(f"Error: {str(e)}", err=True)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@agents_group.command(name="show")
|
|
71
|
+
@click.argument("agent_id")
|
|
72
|
+
@click.option("--format", type=click.Choice(["panel", "json"]), default="panel",
|
|
73
|
+
help="Output format")
|
|
74
|
+
def show_agent(agent_id: str, format: str):
|
|
75
|
+
"""Show detailed information about an agent."""
|
|
76
|
+
try:
|
|
77
|
+
registry = AgentRegistry()
|
|
78
|
+
agent = registry.get_identity(agent_id)
|
|
79
|
+
|
|
80
|
+
if not agent:
|
|
81
|
+
click.echo(f"Error: Agent '{agent_id}' not found", err=True)
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
if format == "json":
|
|
85
|
+
output = {
|
|
86
|
+
"id": agent.id,
|
|
87
|
+
"name": agent.name,
|
|
88
|
+
"role_title": agent.role_title,
|
|
89
|
+
"color": agent.color,
|
|
90
|
+
"emoji": agent.emoji,
|
|
91
|
+
"secondary_emoji": agent.secondary_emoji,
|
|
92
|
+
"expertise": agent.expertise,
|
|
93
|
+
"temperature": agent.temperature,
|
|
94
|
+
"thinking_style": agent.thinking_style,
|
|
95
|
+
"verbosity": agent.verbosity,
|
|
96
|
+
}
|
|
97
|
+
click.echo(json.dumps(output, indent=2))
|
|
98
|
+
else:
|
|
99
|
+
content = f"""
|
|
100
|
+
[bold]Role:[/bold] {agent.role_title}
|
|
101
|
+
[bold]Emoji:[/bold] {agent.emoji} {agent.secondary_emoji}
|
|
102
|
+
[bold]Color:[/bold] {agent.color}
|
|
103
|
+
[bold]Thinking Style:[/bold] {agent.thinking_style}
|
|
104
|
+
[bold]Temperature:[/bold] {agent.temperature}
|
|
105
|
+
[bold]Verbosity:[/bold] {agent.verbosity}
|
|
106
|
+
|
|
107
|
+
[bold]Expertise:[/bold]
|
|
108
|
+
{chr(10).join(' • ' + e for e in agent.expertise)}
|
|
109
|
+
"""
|
|
110
|
+
panel = agent.create_panel(content.strip(), title=f"{agent.emoji} {agent.name}")
|
|
111
|
+
console.print(panel)
|
|
112
|
+
|
|
113
|
+
except Exception as e:
|
|
114
|
+
click.echo(f"Error: {str(e)}", err=True)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@agents_group.command(name="create")
|
|
118
|
+
@click.option("--id", required=True, help="Agent identifier (lowercase, no spaces)")
|
|
119
|
+
@click.option("--name", required=True, help="Display name")
|
|
120
|
+
@click.option("--role", required=True, help="Role title")
|
|
121
|
+
@click.option("--color", required=True, help="Hex color code (e.g., #FF6B6B)")
|
|
122
|
+
@click.option("--emoji", required=True, help="Primary emoji")
|
|
123
|
+
@click.option("--expertise", multiple=True, help="Areas of expertise")
|
|
124
|
+
@click.option("--temperature", type=float, default=0.5, help="Model temperature")
|
|
125
|
+
@click.option("--output", type=click.Path(), help="Output file path (YAML)")
|
|
126
|
+
def create_agent(id: str, name: str, role: str, color: str, emoji: str,
|
|
127
|
+
expertise: tuple, temperature: float, output: Optional[str]):
|
|
128
|
+
"""Create a new custom agent identity."""
|
|
129
|
+
try:
|
|
130
|
+
agent = AgentIdentity(
|
|
131
|
+
id=id,
|
|
132
|
+
name=name,
|
|
133
|
+
role_title=role,
|
|
134
|
+
color=color,
|
|
135
|
+
emoji=emoji,
|
|
136
|
+
expertise=list(expertise),
|
|
137
|
+
temperature=temperature
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
agent_data = {
|
|
141
|
+
"id": agent.id,
|
|
142
|
+
"name": agent.name,
|
|
143
|
+
"role_title": agent.role_title,
|
|
144
|
+
"color": agent.color,
|
|
145
|
+
"emoji": agent.emoji,
|
|
146
|
+
"secondary_emoji": agent.secondary_emoji,
|
|
147
|
+
"expertise": agent.expertise,
|
|
148
|
+
"temperature": agent.temperature,
|
|
149
|
+
"thinking_style": agent.thinking_style,
|
|
150
|
+
"verbosity": agent.verbosity,
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if output:
|
|
154
|
+
output_path = Path(output)
|
|
155
|
+
output_path.write_text(yaml.dump(agent_data, default_flow_style=False))
|
|
156
|
+
click.echo(f"Agent saved to: {output}")
|
|
157
|
+
else:
|
|
158
|
+
click.echo(yaml.dump(agent_data, default_flow_style=False))
|
|
159
|
+
|
|
160
|
+
console.print(Panel(f"[green]✓[/green] Agent '{name}' created successfully!",
|
|
161
|
+
style=color))
|
|
162
|
+
|
|
163
|
+
except Exception as e:
|
|
164
|
+
click.echo(f"Error: {str(e)}", err=True)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@agents_group.command(name="export")
|
|
168
|
+
@click.argument("agent_id")
|
|
169
|
+
@click.option("--output", type=click.Path(), help="Output file path")
|
|
170
|
+
@click.option("--format", type=click.Choice(["yaml", "json"]), default="yaml",
|
|
171
|
+
help="Export format")
|
|
172
|
+
def export_agent(agent_id: str, output: Optional[str], format: str):
|
|
173
|
+
"""Export an agent identity to file."""
|
|
174
|
+
try:
|
|
175
|
+
registry = AgentRegistry()
|
|
176
|
+
agent = registry.get_identity(agent_id)
|
|
177
|
+
|
|
178
|
+
if not agent:
|
|
179
|
+
click.echo(f"Error: Agent '{agent_id}' not found", err=True)
|
|
180
|
+
return
|
|
181
|
+
|
|
182
|
+
agent_data = {
|
|
183
|
+
"id": agent.id,
|
|
184
|
+
"name": agent.name,
|
|
185
|
+
"role_title": agent.role_title,
|
|
186
|
+
"color": agent.color,
|
|
187
|
+
"emoji": agent.emoji,
|
|
188
|
+
"secondary_emoji": agent.secondary_emoji,
|
|
189
|
+
"expertise": agent.expertise,
|
|
190
|
+
"temperature": agent.temperature,
|
|
191
|
+
"thinking_style": agent.thinking_style,
|
|
192
|
+
"verbosity": agent.verbosity,
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if format == "json":
|
|
196
|
+
content = json.dumps(agent_data, indent=2)
|
|
197
|
+
else:
|
|
198
|
+
content = yaml.dump(agent_data, default_flow_style=False)
|
|
199
|
+
|
|
200
|
+
if output:
|
|
201
|
+
Path(output).write_text(content)
|
|
202
|
+
click.echo(f"Agent exported to: {output}")
|
|
203
|
+
else:
|
|
204
|
+
click.echo(content)
|
|
205
|
+
|
|
206
|
+
except Exception as e:
|
|
207
|
+
click.echo(f"Error: {str(e)}", err=True)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@agents_group.command(name="preview")
|
|
211
|
+
@click.argument("agent_id")
|
|
212
|
+
@click.option("--message", default="Sample output from this agent",
|
|
213
|
+
help="Test message to display")
|
|
214
|
+
def preview_agent(agent_id: str, message: str):
|
|
215
|
+
"""Preview an agent's visual styling."""
|
|
216
|
+
try:
|
|
217
|
+
registry = AgentRegistry()
|
|
218
|
+
agent = registry.get_identity(agent_id)
|
|
219
|
+
|
|
220
|
+
if not agent:
|
|
221
|
+
click.echo(f"Error: Agent '{agent_id}' not found", err=True)
|
|
222
|
+
return
|
|
223
|
+
|
|
224
|
+
# Show name
|
|
225
|
+
console.print(agent.format_name())
|
|
226
|
+
console.print()
|
|
227
|
+
|
|
228
|
+
# Show panel with message
|
|
229
|
+
panel = agent.create_panel(message)
|
|
230
|
+
console.print(panel)
|
|
231
|
+
console.print()
|
|
232
|
+
|
|
233
|
+
# Show status
|
|
234
|
+
console.print(agent.format_status("Working on task..."))
|
|
235
|
+
console.print(agent.format_status("Task completed!"))
|
|
236
|
+
|
|
237
|
+
except Exception as e:
|
|
238
|
+
click.echo(f"Error: {str(e)}", err=True)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent Coordination CLI Commands
|
|
3
|
+
|
|
4
|
+
Commands for agent coordination (messages, workload, sync).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
import json
|
|
9
|
+
from typing import Optional
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.panel import Panel
|
|
12
|
+
|
|
13
|
+
from ..client import AINativeClient
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
console = Console()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_client() -> AINativeClient:
|
|
20
|
+
"""Get authenticated client."""
|
|
21
|
+
import os
|
|
22
|
+
from ..auth import AuthConfig
|
|
23
|
+
|
|
24
|
+
api_key = os.getenv("AINATIVE_API_KEY")
|
|
25
|
+
if not api_key:
|
|
26
|
+
raise click.ClickException("AINATIVE_API_KEY environment variable not set")
|
|
27
|
+
|
|
28
|
+
return AINativeClient(
|
|
29
|
+
auth_config=AuthConfig(api_key=api_key),
|
|
30
|
+
base_url=os.getenv("AINATIVE_BASE_URL"),
|
|
31
|
+
organization_id=os.getenv("AINATIVE_ORG_ID")
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@click.group(name="coord")
|
|
36
|
+
def coordination_group():
|
|
37
|
+
"""Agent coordination operations."""
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@coordination_group.command(name="message")
|
|
42
|
+
@click.option("--from", "from_agent", required=True, help="Sender agent ID")
|
|
43
|
+
@click.option("--to", "to_agent", required=True, help="Recipient agent ID")
|
|
44
|
+
@click.option("--message", required=True, help="Message content")
|
|
45
|
+
@click.option("--type", "msg_type", default="info", help="Message type")
|
|
46
|
+
def send_message(from_agent: str, to_agent: str, message: str, msg_type: str):
|
|
47
|
+
"""Send message between agents."""
|
|
48
|
+
try:
|
|
49
|
+
client = get_client()
|
|
50
|
+
result = client.agent_coordination.send_message(
|
|
51
|
+
from_agent_id=from_agent,
|
|
52
|
+
to_agent_id=to_agent,
|
|
53
|
+
message=message,
|
|
54
|
+
message_type=msg_type
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
console.print(Panel(
|
|
58
|
+
f"[green]✓[/green] Message sent",
|
|
59
|
+
title="Success"
|
|
60
|
+
))
|
|
61
|
+
console.print(json.dumps(result, indent=2))
|
|
62
|
+
|
|
63
|
+
except Exception as e:
|
|
64
|
+
click.echo(f"Error: {str(e)}", err=True)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@coordination_group.command(name="workload")
|
|
68
|
+
@click.option("--agent-id", help="Specific agent ID (optional)")
|
|
69
|
+
def get_workload(agent_id: Optional[str]):
|
|
70
|
+
"""Get agent workload statistics."""
|
|
71
|
+
try:
|
|
72
|
+
client = get_client()
|
|
73
|
+
result = client.agent_coordination.get_agent_workload(agent_id=agent_id)
|
|
74
|
+
|
|
75
|
+
console.print(Panel(
|
|
76
|
+
json.dumps(result, indent=2),
|
|
77
|
+
title="Agent Workload"
|
|
78
|
+
))
|
|
79
|
+
|
|
80
|
+
except Exception as e:
|
|
81
|
+
click.echo(f"Error: {str(e)}", err=True)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@coordination_group.command(name="distribute")
|
|
85
|
+
@click.option("--tasks", required=True, help="Task IDs (comma-separated)")
|
|
86
|
+
@click.option("--agents", required=True, help="Agent IDs (comma-separated)")
|
|
87
|
+
@click.option("--strategy", default="round_robin", help="Distribution strategy")
|
|
88
|
+
def distribute_workload(tasks: str, agents: str, strategy: str):
|
|
89
|
+
"""Distribute tasks across agents."""
|
|
90
|
+
try:
|
|
91
|
+
client = get_client()
|
|
92
|
+
task_list = [t.strip() for t in tasks.split(",")]
|
|
93
|
+
agent_list = [a.strip() for a in agents.split(",")]
|
|
94
|
+
|
|
95
|
+
result = client.agent_coordination.distribute_workload(
|
|
96
|
+
tasks=task_list,
|
|
97
|
+
agents=agent_list,
|
|
98
|
+
strategy=strategy
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
console.print(Panel(
|
|
102
|
+
f"[green]✓[/green] Workload distributed",
|
|
103
|
+
title="Success"
|
|
104
|
+
))
|
|
105
|
+
console.print(json.dumps(result, indent=2))
|
|
106
|
+
|
|
107
|
+
except Exception as e:
|
|
108
|
+
click.echo(f"Error: {str(e)}", err=True)
|