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/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)