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.
@@ -0,0 +1,510 @@
1
+ """
2
+ Agent Swarm Module for AINative SDK
3
+
4
+ Provides interface for orchestrating and managing AI agent swarms.
5
+ """
6
+
7
+ from typing import TYPE_CHECKING, List, Dict, Any, Optional
8
+ from enum import Enum
9
+
10
+ if TYPE_CHECKING:
11
+ from ..client import AINativeClient
12
+
13
+
14
+ class AgentType(Enum):
15
+ """Types of agents available in the swarm."""
16
+ RESEARCHER = "researcher"
17
+ CODER = "coder"
18
+ REVIEWER = "reviewer"
19
+ TESTER = "tester"
20
+ DOCUMENTER = "documenter"
21
+ ANALYST = "analyst"
22
+ DESIGNER = "designer"
23
+ ORCHESTRATOR = "orchestrator"
24
+
25
+
26
+ class SwarmStatus(Enum):
27
+ """Status of agent swarm."""
28
+ IDLE = "idle"
29
+ STARTING = "starting"
30
+ RUNNING = "running"
31
+ PAUSED = "paused"
32
+ STOPPING = "stopping"
33
+ COMPLETED = "completed"
34
+ FAILED = "failed"
35
+
36
+
37
+ class AgentSwarmClient:
38
+ """Main client for Agent Swarm operations."""
39
+
40
+ def __init__(self, client: "AINativeClient"):
41
+ """
42
+ Initialize Agent Swarm client.
43
+
44
+ Args:
45
+ client: Parent AINative client instance
46
+ """
47
+ self.client = client
48
+ self.base_path = "/agent-swarm"
49
+
50
+ def start_swarm(
51
+ self,
52
+ project_id: str,
53
+ agents: List[Dict[str, Any]],
54
+ objective: str,
55
+ config: Optional[Dict[str, Any]] = None,
56
+ ) -> Dict[str, Any]:
57
+ """
58
+ Start a new agent swarm.
59
+
60
+ Args:
61
+ project_id: Project ID
62
+ agents: List of agent configurations
63
+ objective: Swarm objective/goal
64
+ config: Additional swarm configuration
65
+
66
+ Returns:
67
+ Swarm initialization details
68
+ """
69
+ data = {
70
+ "project_id": project_id,
71
+ "agents": agents,
72
+ "objective": objective,
73
+ "config": config or {},
74
+ }
75
+
76
+ return self.client.post(f"{self.base_path}/start", data=data)
77
+
78
+ def orchestrate(
79
+ self,
80
+ swarm_id: str,
81
+ task: str,
82
+ context: Optional[Dict[str, Any]] = None,
83
+ agents: Optional[List[str]] = None,
84
+ ) -> Dict[str, Any]:
85
+ """
86
+ Orchestrate agents for a specific task.
87
+
88
+ Args:
89
+ swarm_id: Swarm ID
90
+ task: Task description
91
+ context: Task context
92
+ agents: Specific agents to use (optional)
93
+
94
+ Returns:
95
+ Orchestration result
96
+ """
97
+ data = {
98
+ "swarm_id": swarm_id,
99
+ "task": task,
100
+ "context": context or {},
101
+ }
102
+
103
+ if agents:
104
+ data["agents"] = agents
105
+
106
+ return self.client.post(f"{self.base_path}/orchestrate", data=data)
107
+
108
+ def get_status(self, swarm_id: str) -> Dict[str, Any]:
109
+ """
110
+ Get swarm status.
111
+
112
+ Args:
113
+ swarm_id: Swarm ID
114
+
115
+ Returns:
116
+ Swarm status details
117
+ """
118
+ return self.client.get(f"{self.base_path}/{swarm_id}/status")
119
+
120
+ def get_metrics(
121
+ self,
122
+ swarm_id: Optional[str] = None,
123
+ project_id: Optional[str] = None,
124
+ ) -> Dict[str, Any]:
125
+ """
126
+ Get swarm performance metrics.
127
+
128
+ Args:
129
+ swarm_id: Optional swarm ID filter
130
+ project_id: Optional project ID filter
131
+
132
+ Returns:
133
+ Swarm metrics
134
+ """
135
+ params = {}
136
+ if swarm_id:
137
+ params["swarm_id"] = swarm_id
138
+ if project_id:
139
+ params["project_id"] = project_id
140
+
141
+ return self.client.get(f"{self.base_path}/metrics", params=params)
142
+
143
+ def get_agent_types(self) -> List[Dict[str, Any]]:
144
+ """
145
+ Get available agent types and their capabilities.
146
+
147
+ Returns:
148
+ List of agent types with descriptions
149
+ """
150
+ response = self.client.get(f"{self.base_path}/agent-types")
151
+ return response.get("agent_types", [])
152
+
153
+ def configure_agent(
154
+ self,
155
+ swarm_id: str,
156
+ agent_id: str,
157
+ config: Dict[str, Any],
158
+ ) -> Dict[str, Any]:
159
+ """
160
+ Configure a specific agent.
161
+
162
+ Args:
163
+ swarm_id: Swarm ID
164
+ agent_id: Agent ID
165
+ config: Agent configuration
166
+
167
+ Returns:
168
+ Configuration result
169
+ """
170
+ return self.client.put(
171
+ f"{self.base_path}/{swarm_id}/agents/{agent_id}/config",
172
+ data=config
173
+ )
174
+
175
+ def set_agent_prompt(
176
+ self,
177
+ swarm_id: str,
178
+ agent_id: str,
179
+ prompt: str,
180
+ system_prompt: Optional[str] = None,
181
+ ) -> Dict[str, Any]:
182
+ """
183
+ Set agent prompt configuration.
184
+
185
+ Args:
186
+ swarm_id: Swarm ID
187
+ agent_id: Agent ID
188
+ prompt: Main prompt
189
+ system_prompt: System prompt (optional)
190
+
191
+ Returns:
192
+ Prompt configuration result
193
+ """
194
+ data = {"prompt": prompt}
195
+ if system_prompt:
196
+ data["system_prompt"] = system_prompt
197
+
198
+ return self.client.post(
199
+ f"{self.base_path}/{swarm_id}/agents/{agent_id}/prompt",
200
+ data=data
201
+ )
202
+
203
+ def stop_swarm(self, swarm_id: str, force: bool = False) -> Dict[str, Any]:
204
+ """
205
+ Stop an agent swarm.
206
+
207
+ Args:
208
+ swarm_id: Swarm ID
209
+ force: Force stop without cleanup
210
+
211
+ Returns:
212
+ Stop confirmation
213
+ """
214
+ data = {"force": force}
215
+ return self.client.post(f"{self.base_path}/{swarm_id}/stop", data=data)
216
+
217
+ def pause_swarm(self, swarm_id: str) -> Dict[str, Any]:
218
+ """
219
+ Pause an agent swarm.
220
+
221
+ Args:
222
+ swarm_id: Swarm ID
223
+
224
+ Returns:
225
+ Pause confirmation
226
+ """
227
+ return self.client.post(f"{self.base_path}/{swarm_id}/pause")
228
+
229
+ def resume_swarm(self, swarm_id: str) -> Dict[str, Any]:
230
+ """
231
+ Resume a paused swarm.
232
+
233
+ Args:
234
+ swarm_id: Swarm ID
235
+
236
+ Returns:
237
+ Resume confirmation
238
+ """
239
+ return self.client.post(f"{self.base_path}/{swarm_id}/resume")
240
+
241
+ def get_swarm_history(
242
+ self,
243
+ swarm_id: str,
244
+ limit: int = 100,
245
+ ) -> List[Dict[str, Any]]:
246
+ """
247
+ Get swarm execution history.
248
+
249
+ Args:
250
+ swarm_id: Swarm ID
251
+ limit: Maximum number of history entries
252
+
253
+ Returns:
254
+ List of history entries
255
+ """
256
+ params = {"limit": limit}
257
+ response = self.client.get(
258
+ f"{self.base_path}/{swarm_id}/history",
259
+ params=params
260
+ )
261
+ return response.get("history", [])
262
+
263
+ def get_agent_communications(
264
+ self,
265
+ swarm_id: str,
266
+ agent_id: Optional[str] = None,
267
+ ) -> List[Dict[str, Any]]:
268
+ """
269
+ Get agent communication logs.
270
+
271
+ Args:
272
+ swarm_id: Swarm ID
273
+ agent_id: Optional specific agent ID
274
+
275
+ Returns:
276
+ List of communication entries
277
+ """
278
+ params = {}
279
+ if agent_id:
280
+ params["agent_id"] = agent_id
281
+
282
+ response = self.client.get(
283
+ f"{self.base_path}/{swarm_id}/communications",
284
+ params=params
285
+ )
286
+ return response.get("communications", [])
287
+
288
+ def create_agent(
289
+ self,
290
+ name: str,
291
+ agent_type: AgentType,
292
+ capabilities: List[str],
293
+ prompt: str,
294
+ config: Optional[Dict[str, Any]] = None,
295
+ ) -> Dict[str, Any]:
296
+ """
297
+ Create a custom agent template.
298
+
299
+ Args:
300
+ name: Agent name
301
+ agent_type: Type of agent
302
+ capabilities: List of capabilities
303
+ prompt: Agent prompt
304
+ config: Additional configuration
305
+
306
+ Returns:
307
+ Created agent details
308
+ """
309
+ data = {
310
+ "name": name,
311
+ "type": agent_type.value,
312
+ "capabilities": capabilities,
313
+ "prompt": prompt,
314
+ "config": config or {},
315
+ }
316
+
317
+ return self.client.post(f"{self.base_path}/agents", data=data)
318
+
319
+ def list_swarms(
320
+ self,
321
+ project_id: Optional[str] = None,
322
+ status: Optional[str] = None,
323
+ limit: int = 100,
324
+ offset: int = 0,
325
+ ) -> Dict[str, Any]:
326
+ """
327
+ List all swarms with optional filtering.
328
+
329
+ Args:
330
+ project_id: Filter by project ID
331
+ status: Filter by status
332
+ limit: Maximum number of results
333
+ offset: Pagination offset
334
+
335
+ Returns:
336
+ List of swarms with pagination metadata
337
+ """
338
+ params = {
339
+ "limit": limit,
340
+ "offset": offset,
341
+ }
342
+
343
+ if project_id:
344
+ params["project_id"] = project_id
345
+ if status:
346
+ params["status"] = status
347
+
348
+ return self.client.get(self.base_path, params=params)
349
+
350
+ def delete_swarm(self, swarm_id: str, force: bool = False) -> Dict[str, Any]:
351
+ """
352
+ Delete a swarm.
353
+
354
+ Args:
355
+ swarm_id: Swarm ID to delete
356
+ force: Force deletion without cleanup
357
+
358
+ Returns:
359
+ Deletion confirmation
360
+ """
361
+ params = {"force": str(force).lower()}
362
+ return self.client.delete(f"{self.base_path}/{swarm_id}", params=params)
363
+
364
+ def scale_swarm(
365
+ self,
366
+ swarm_id: str,
367
+ agent_counts: Dict[str, int],
368
+ ) -> Dict[str, Any]:
369
+ """
370
+ Scale swarm by adjusting agent counts.
371
+
372
+ Args:
373
+ swarm_id: Swarm ID
374
+ agent_counts: Dictionary mapping agent types to desired counts
375
+
376
+ Returns:
377
+ Scaling operation result
378
+ """
379
+ data = {"agent_counts": agent_counts}
380
+ return self.client.post(f"{self.base_path}/{swarm_id}/scale", data=data)
381
+
382
+ def get_analytics(
383
+ self,
384
+ swarm_id: str,
385
+ metric_types: Optional[List[str]] = None,
386
+ time_range: str = "7d",
387
+ ) -> Dict[str, Any]:
388
+ """
389
+ Get swarm analytics and performance metrics.
390
+
391
+ Args:
392
+ swarm_id: Swarm ID
393
+ metric_types: Specific metrics to retrieve
394
+ time_range: Time range (1d, 7d, 30d, all)
395
+
396
+ Returns:
397
+ Analytics data
398
+ """
399
+ params = {"time_range": time_range}
400
+
401
+ if metric_types:
402
+ params["metric_types"] = ",".join(metric_types)
403
+
404
+ return self.client.get(f"{self.base_path}/{swarm_id}/analytics", params=params)
405
+
406
+ def execute_parallel_tasks(
407
+ self,
408
+ swarm_id: str,
409
+ tasks: List[Dict[str, Any]],
410
+ max_concurrency: Optional[int] = None,
411
+ ) -> Dict[str, Any]:
412
+ """
413
+ Execute multiple tasks in parallel across swarm.
414
+
415
+ Args:
416
+ swarm_id: Swarm ID
417
+ tasks: List of task definitions
418
+ max_concurrency: Maximum concurrent executions
419
+
420
+ Returns:
421
+ Parallel execution results
422
+ """
423
+ data = {
424
+ "tasks": tasks,
425
+ }
426
+
427
+ if max_concurrency:
428
+ data["max_concurrency"] = max_concurrency
429
+
430
+ return self.client.post(
431
+ f"{self.base_path}/{swarm_id}/tasks/parallel",
432
+ data=data
433
+ )
434
+
435
+ def get_swarm_health(self, swarm_id: str) -> Dict[str, Any]:
436
+ """
437
+ Get swarm health status.
438
+
439
+ Args:
440
+ swarm_id: Swarm ID
441
+
442
+ Returns:
443
+ Health status details
444
+ """
445
+ return self.client.get(f"{self.base_path}/{swarm_id}/health")
446
+
447
+ def update_swarm_config(
448
+ self,
449
+ swarm_id: str,
450
+ config: Dict[str, Any],
451
+ ) -> Dict[str, Any]:
452
+ """
453
+ Update swarm configuration.
454
+
455
+ Args:
456
+ swarm_id: Swarm ID
457
+ config: New configuration settings
458
+
459
+ Returns:
460
+ Updated configuration
461
+ """
462
+ return self.client.put(f"{self.base_path}/{swarm_id}/config", data=config)
463
+
464
+ def get_agent_status(
465
+ self,
466
+ swarm_id: str,
467
+ agent_id: str,
468
+ ) -> Dict[str, Any]:
469
+ """
470
+ Get detailed status of a specific agent in the swarm.
471
+
472
+ Args:
473
+ swarm_id: Swarm ID
474
+ agent_id: Agent ID
475
+
476
+ Returns:
477
+ Agent status details
478
+ """
479
+ return self.client.get(f"{self.base_path}/{swarm_id}/agents/{agent_id}/status")
480
+
481
+ def broadcast_message(
482
+ self,
483
+ swarm_id: str,
484
+ message: str,
485
+ target_agents: Optional[List[str]] = None,
486
+ ) -> Dict[str, Any]:
487
+ """
488
+ Broadcast a message to all or specific agents in swarm.
489
+
490
+ Args:
491
+ swarm_id: Swarm ID
492
+ message: Message to broadcast
493
+ target_agents: Optional list of specific agent IDs
494
+
495
+ Returns:
496
+ Broadcast confirmation
497
+ """
498
+ data = {"message": message}
499
+
500
+ if target_agents:
501
+ data["target_agents"] = target_agents
502
+
503
+ return self.client.post(f"{self.base_path}/{swarm_id}/broadcast", data=data)
504
+
505
+
506
+ __all__ = [
507
+ "AgentSwarmClient",
508
+ "AgentType",
509
+ "SwarmStatus",
510
+ ]
ainative/auth.py ADDED
@@ -0,0 +1,113 @@
1
+ """
2
+ AINative SDK Authentication Module
3
+
4
+ Handles API key authentication and authorization for AINative Studio APIs.
5
+ """
6
+
7
+ import os
8
+ from typing import Optional, Dict, Any
9
+ from dataclasses import dataclass
10
+ import time
11
+ import hashlib
12
+ import hmac
13
+ import base64
14
+
15
+
16
+ @dataclass
17
+ class AuthConfig:
18
+ """Configuration for authentication."""
19
+
20
+ api_key: Optional[str] = None
21
+ api_secret: Optional[str] = None
22
+ environment: str = "production"
23
+ auto_refresh: bool = True
24
+ timeout: int = 30
25
+
26
+ def __post_init__(self):
27
+ """Load from environment variables if not provided."""
28
+ if not self.api_key:
29
+ self.api_key = os.getenv("AINATIVE_API_KEY")
30
+ if not self.api_secret:
31
+ self.api_secret = os.getenv("AINATIVE_API_SECRET")
32
+
33
+ # Validate environment
34
+ valid_environments = ["production", "staging", "development", "local"]
35
+ if self.environment not in valid_environments:
36
+ raise ValueError(f"Invalid environment: {self.environment}")
37
+
38
+ @property
39
+ def is_configured(self) -> bool:
40
+ """Check if authentication is properly configured."""
41
+ return bool(self.api_key)
42
+
43
+
44
+ class APIKeyAuth:
45
+ """Handles API key authentication for requests."""
46
+
47
+ def __init__(self, config: AuthConfig):
48
+ self.config = config
49
+ self._token_cache: Optional[Dict[str, Any]] = None
50
+ self._token_expiry: float = 0
51
+
52
+ def get_headers(self) -> Dict[str, str]:
53
+ """Get authentication headers for API requests."""
54
+ if not self.config.api_key:
55
+ raise ValueError("API key not configured")
56
+
57
+ headers = {
58
+ "X-API-Key": self.config.api_key,
59
+ "X-SDK-Version": "0.1.0",
60
+ "X-SDK-Language": "Python",
61
+ }
62
+
63
+ # Add signature if API secret is provided
64
+ if self.config.api_secret:
65
+ timestamp = str(int(time.time()))
66
+ signature = self._generate_signature(timestamp)
67
+ headers.update({
68
+ "X-Timestamp": timestamp,
69
+ "X-Signature": signature,
70
+ })
71
+
72
+ return headers
73
+
74
+ def _generate_signature(self, timestamp: str) -> str:
75
+ """Generate HMAC signature for request."""
76
+ message = f"{self.config.api_key}{timestamp}"
77
+ signature = hmac.new(
78
+ self.config.api_secret.encode(),
79
+ message.encode(),
80
+ hashlib.sha256
81
+ ).digest()
82
+ return base64.b64encode(signature).decode()
83
+
84
+ def get_bearer_token(self) -> Optional[str]:
85
+ """Get Bearer token if using OAuth flow (future enhancement)."""
86
+ # Placeholder for future OAuth implementation
87
+ return None
88
+
89
+ def validate_credentials(self) -> bool:
90
+ """Validate that credentials are properly configured."""
91
+ return self.config.is_configured
92
+
93
+ def refresh_token(self) -> bool:
94
+ """Refresh authentication token if needed (future enhancement)."""
95
+ # Placeholder for future token refresh logic
96
+ return True
97
+
98
+
99
+ class MultiTenantAuth(APIKeyAuth):
100
+ """Extended authentication for multi-tenant scenarios."""
101
+
102
+ def __init__(self, config: AuthConfig, organization_id: Optional[str] = None):
103
+ super().__init__(config)
104
+ self.organization_id = organization_id or os.getenv("AINATIVE_ORG_ID")
105
+
106
+ def get_headers(self) -> Dict[str, str]:
107
+ """Get headers including organization context."""
108
+ headers = super().get_headers()
109
+
110
+ if self.organization_id:
111
+ headers["X-Organization-ID"] = self.organization_id
112
+
113
+ return headers