antz-studio 0.1.0__tar.gz
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.
- antz_studio-0.1.0/PKG-INFO +34 -0
- antz_studio-0.1.0/antz/__init__.py +29 -0
- antz_studio-0.1.0/antz/cli.py +545 -0
- antz_studio-0.1.0/antz/client.py +91 -0
- antz_studio-0.1.0/antz/config.py +97 -0
- antz_studio-0.1.0/antz/db_connector.py +35 -0
- antz_studio-0.1.0/antz/decorators.py +250 -0
- antz_studio-0.1.0/antz/exceptions.py +35 -0
- antz_studio-0.1.0/antz/hive_mode.py +6 -0
- antz_studio-0.1.0/antz/privacy.py +24 -0
- antz_studio-0.1.0/antz_studio.egg-info/PKG-INFO +34 -0
- antz_studio-0.1.0/antz_studio.egg-info/SOURCES.txt +17 -0
- antz_studio-0.1.0/antz_studio.egg-info/dependency_links.txt +1 -0
- antz_studio-0.1.0/antz_studio.egg-info/entry_points.txt +2 -0
- antz_studio-0.1.0/antz_studio.egg-info/requires.txt +20 -0
- antz_studio-0.1.0/antz_studio.egg-info/top_level.txt +1 -0
- antz_studio-0.1.0/pyproject.toml +60 -0
- antz_studio-0.1.0/setup.cfg +4 -0
- antz_studio-0.1.0/tests/test_decorators.py +201 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: antz-studio
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Antz Studio -- Sovereign Agentic OS
|
|
5
|
+
Author-email: Antz Studio <gabriel@antz.studio>
|
|
6
|
+
License: AGPL-3.0
|
|
7
|
+
Project-URL: Homepage, https://antz.studio
|
|
8
|
+
Project-URL: Repository, https://github.com/Gabbsx7/antz-studio-aos
|
|
9
|
+
Keywords: agents,sovereign-ai,agentic-os,llm,observability,nest,colony,hive
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
Requires-Dist: httpx>=0.27
|
|
18
|
+
Requires-Dist: typer>=0.12
|
|
19
|
+
Requires-Dist: pyyaml>=6.0
|
|
20
|
+
Requires-Dist: opentelemetry-api>=1.24
|
|
21
|
+
Requires-Dist: opentelemetry-sdk>=1.24
|
|
22
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.24
|
|
23
|
+
Requires-Dist: rich>=13.0
|
|
24
|
+
Requires-Dist: pydantic>=2.0
|
|
25
|
+
Requires-Dist: sqlalchemy>=2.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-mock>=3.12; extra == "dev"
|
|
30
|
+
Provides-Extra: cloud
|
|
31
|
+
Requires-Dist: azure-mgmt-compute>=30.0.0; extra == "cloud"
|
|
32
|
+
Requires-Dist: azure-mgmt-network>=25.0.0; extra == "cloud"
|
|
33
|
+
Requires-Dist: azure-mgmt-resource>=23.0.0; extra == "cloud"
|
|
34
|
+
Requires-Dist: azure-identity>=1.15.0; extra == "cloud"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Antz Studio -- Colony SDK
|
|
2
|
+
# Sovereign Agentic OS - antz.studio
|
|
3
|
+
|
|
4
|
+
from antz.decorators import agent, tool, memory
|
|
5
|
+
from antz.client import AntzClient
|
|
6
|
+
from antz.config import AntzConfig
|
|
7
|
+
from antz.hive_mode import HiveMode
|
|
8
|
+
from antz.db_connector import db_connector
|
|
9
|
+
from antz.privacy import add_gaussian_noise
|
|
10
|
+
from antz.exceptions import (
|
|
11
|
+
AntzConnectionError,
|
|
12
|
+
ConstitutionViolationError,
|
|
13
|
+
AntzAuthError,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__version__ = "0.1.0"
|
|
17
|
+
__all__ = [
|
|
18
|
+
"agent",
|
|
19
|
+
"tool",
|
|
20
|
+
"memory",
|
|
21
|
+
"HiveMode",
|
|
22
|
+
"db_connector",
|
|
23
|
+
"add_gaussian_noise",
|
|
24
|
+
"AntzClient",
|
|
25
|
+
"AntzConfig",
|
|
26
|
+
"AntzConnectionError",
|
|
27
|
+
"ConstitutionViolationError",
|
|
28
|
+
"AntzAuthError",
|
|
29
|
+
]
|
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
import yaml
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.panel import Panel
|
|
12
|
+
from rich.table import Table
|
|
13
|
+
|
|
14
|
+
app = typer.Typer(help="Ant'z Studio — Sovereign Agentic OS CLI", add_completion=False)
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# ── antz login ────────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
@app.command()
|
|
21
|
+
def login(
|
|
22
|
+
nest: str = typer.Option(..., "--nest", help="Nest URL (e.g. http://nest.local:8001)"),
|
|
23
|
+
api_key: str = typer.Option(..., "--api-key", help="Nest API key"),
|
|
24
|
+
mode: str = typer.Option("isolated", "--mode", help="isolated | shared | sovereign"),
|
|
25
|
+
):
|
|
26
|
+
"""Authenticate with a Nest and save credentials locally."""
|
|
27
|
+
from antz.config import AntzConfig
|
|
28
|
+
from antz.client import AntzClient
|
|
29
|
+
|
|
30
|
+
console.print(f"[dim]Connecting to Nest at {nest}...[/dim]")
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
cfg = AntzConfig(nest_url=nest, api_key=api_key, mode=mode)
|
|
34
|
+
client = AntzClient(cfg)
|
|
35
|
+
health = client.health()
|
|
36
|
+
|
|
37
|
+
AntzConfig.save_global(nest_url=nest, api_key=api_key, mode=mode)
|
|
38
|
+
|
|
39
|
+
console.print(Panel.fit(
|
|
40
|
+
f"[green]✓ Connected[/green]\n\n"
|
|
41
|
+
f" Nest: [cyan]{nest}[/cyan]\n"
|
|
42
|
+
f" Mode: [yellow]{mode}[/yellow]\n"
|
|
43
|
+
f" Version: {health.get('version', 'unknown')}\n\n"
|
|
44
|
+
f"Credentials saved to [dim]~/.antz/config.yaml[/dim]",
|
|
45
|
+
title="Ant'z Studio",
|
|
46
|
+
border_style="green",
|
|
47
|
+
))
|
|
48
|
+
|
|
49
|
+
except Exception as e:
|
|
50
|
+
console.print(f"[red]✗ Connection failed:[/red] {e}")
|
|
51
|
+
console.print("[dim]Hint: make sure the Nest is running and the API key is correct.[/dim]")
|
|
52
|
+
raise typer.Exit(1)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# ── antz status ───────────────────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
@app.command()
|
|
58
|
+
def status():
|
|
59
|
+
"""Check connection to the configured Nest."""
|
|
60
|
+
from antz.config import AntzConfig
|
|
61
|
+
from antz.client import AntzClient
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
cfg = AntzConfig.load()
|
|
65
|
+
client = AntzClient(cfg)
|
|
66
|
+
health = client.health()
|
|
67
|
+
|
|
68
|
+
table = Table(show_header=False, box=None, padding=(0, 2))
|
|
69
|
+
table.add_row("[dim]Nest URL[/dim]", f"[cyan]{cfg.nest_url}[/cyan]")
|
|
70
|
+
table.add_row("[dim]Mode[/dim]", f"[yellow]{cfg.mode}[/yellow]")
|
|
71
|
+
table.add_row("[dim]Status[/dim]", "[green]● online[/green]")
|
|
72
|
+
table.add_row("[dim]Version[/dim]", health.get("version", "unknown"))
|
|
73
|
+
|
|
74
|
+
console.print(Panel(table, title="Ant'z Nest Status", border_style="green"))
|
|
75
|
+
|
|
76
|
+
except Exception as e:
|
|
77
|
+
console.print(f"[red]✗ Cannot reach Nest:[/red] {e}")
|
|
78
|
+
console.print("[dim]Run: antz login --nest <url> --api-key <key>[/dim]")
|
|
79
|
+
raise typer.Exit(1)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# ── antz init colony ──────────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
@app.command()
|
|
85
|
+
def init(
|
|
86
|
+
resource: str = typer.Argument(..., help="Resource type: colony"),
|
|
87
|
+
name: str = typer.Argument(..., help="Colony name (e.g. financial-credit-analysis)"),
|
|
88
|
+
framework: str = typer.Option("langgraph", "--framework", "-f",
|
|
89
|
+
help="langgraph | crewai | mixed"),
|
|
90
|
+
):
|
|
91
|
+
"""Scaffold a new Colony with the chosen framework template."""
|
|
92
|
+
if resource != "colony":
|
|
93
|
+
console.print(f"[red]Unknown resource '{resource}'. Currently supported: colony[/red]")
|
|
94
|
+
raise typer.Exit(1)
|
|
95
|
+
|
|
96
|
+
target = Path.cwd() / f"colony-{name}"
|
|
97
|
+
|
|
98
|
+
if target.exists():
|
|
99
|
+
console.print(f"[red]✗ Directory '{target.name}' already exists.[/red]")
|
|
100
|
+
raise typer.Exit(1)
|
|
101
|
+
|
|
102
|
+
_scaffold_colony(name=name, framework=framework, target=target)
|
|
103
|
+
|
|
104
|
+
console.print(Panel.fit(
|
|
105
|
+
f"[green]✓ Colony scaffolded[/green]\n\n"
|
|
106
|
+
f" Name: [cyan]{name}[/cyan]\n"
|
|
107
|
+
f" Framework: [yellow]{framework}[/yellow]\n"
|
|
108
|
+
f" Directory: [dim]{target}[/dim]\n\n"
|
|
109
|
+
f"Next steps:\n"
|
|
110
|
+
f" [dim]cd colony-{name}[/dim]\n"
|
|
111
|
+
f" [dim]code .[/dim] # open in VSCode\n"
|
|
112
|
+
f" [dim]antz run --demo[/dim] # run the example agent",
|
|
113
|
+
title="Colony created",
|
|
114
|
+
border_style="cyan",
|
|
115
|
+
))
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# ── antz run ──────────────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
@app.command()
|
|
121
|
+
def run(
|
|
122
|
+
input_file: Optional[Path] = typer.Option(None, "--input", "-i",
|
|
123
|
+
help="JSON input file for the agent"),
|
|
124
|
+
demo: bool = typer.Option(False, "--demo", help="Run the built-in demo agent"),
|
|
125
|
+
colony_dir: Optional[Path] = typer.Option(None, "--colony", "-c",
|
|
126
|
+
help="Colony directory (default: cwd)"),
|
|
127
|
+
):
|
|
128
|
+
"""Execute the Colony agent."""
|
|
129
|
+
cwd = colony_dir or Path.cwd()
|
|
130
|
+
cfg_file = cwd / "antz.config.yaml"
|
|
131
|
+
|
|
132
|
+
if not cfg_file.exists():
|
|
133
|
+
console.print(
|
|
134
|
+
"[red]✗ No antz.config.yaml found.[/red]\n"
|
|
135
|
+
"[dim]Run from inside a Colony directory, or use: antz init colony <name>[/dim]"
|
|
136
|
+
)
|
|
137
|
+
raise typer.Exit(1)
|
|
138
|
+
|
|
139
|
+
with open(cfg_file) as f:
|
|
140
|
+
colony_cfg = yaml.safe_load(f) or {}
|
|
141
|
+
|
|
142
|
+
colony_id = colony_cfg.get("colony_id", "unknown")
|
|
143
|
+
entrypoint = colony_cfg.get("entrypoint", "agents/main.py")
|
|
144
|
+
|
|
145
|
+
console.print(f"[dim]Running Colony [cyan]{colony_id}[/cyan]...[/dim]")
|
|
146
|
+
|
|
147
|
+
if demo:
|
|
148
|
+
_run_demo(cwd, colony_cfg)
|
|
149
|
+
return
|
|
150
|
+
|
|
151
|
+
entry_path = cwd / entrypoint
|
|
152
|
+
if not entry_path.exists():
|
|
153
|
+
console.print(f"[red]✗ Entrypoint not found: {entry_path}[/red]")
|
|
154
|
+
raise typer.Exit(1)
|
|
155
|
+
|
|
156
|
+
cmd = [sys.executable, str(entry_path)]
|
|
157
|
+
if input_file:
|
|
158
|
+
cmd += ["--input", str(input_file)]
|
|
159
|
+
|
|
160
|
+
result = subprocess.run(cmd, cwd=str(cwd))
|
|
161
|
+
raise typer.Exit(result.returncode)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# ── antz version ─────────────────────────────────────────────────────────────
|
|
165
|
+
|
|
166
|
+
@app.command()
|
|
167
|
+
def version():
|
|
168
|
+
"""Show SDK version."""
|
|
169
|
+
from antz import __version__
|
|
170
|
+
console.print(f"antz-sdk [cyan]{__version__}[/cyan]")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
# ── Internal helpers ──────────────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
def _scaffold_colony(name: str, framework: str, target: Path) -> None:
|
|
176
|
+
"""Create Colony directory structure from template."""
|
|
177
|
+
target.mkdir(parents=True)
|
|
178
|
+
|
|
179
|
+
(target / "agents").mkdir()
|
|
180
|
+
(target / "tools").mkdir()
|
|
181
|
+
(target / "workflows").mkdir()
|
|
182
|
+
(target / "tests").mkdir()
|
|
183
|
+
|
|
184
|
+
# antz.config.yaml
|
|
185
|
+
config = {
|
|
186
|
+
"colony_id": name,
|
|
187
|
+
"framework": framework,
|
|
188
|
+
"entrypoint": "agents/main.py",
|
|
189
|
+
"nest_url": "http://localhost:8001",
|
|
190
|
+
"mode": "isolated",
|
|
191
|
+
}
|
|
192
|
+
with open(target / "antz.config.yaml", "w", encoding="utf-8") as f:
|
|
193
|
+
yaml.dump(config, f, default_flow_style=False)
|
|
194
|
+
|
|
195
|
+
# constitution.yaml
|
|
196
|
+
constitution = {
|
|
197
|
+
"agent_id": f"{name}-v1",
|
|
198
|
+
"version": "1.0",
|
|
199
|
+
"allowed_tools": [],
|
|
200
|
+
"prohibited_actions": ["delete_record", "export_data", "modify_audit_log"],
|
|
201
|
+
"spend_limits": {"max_per_action_usd": 5.0, "max_per_day_usd": 500.0},
|
|
202
|
+
"data_policy": {
|
|
203
|
+
"can_send_outside_nest": False,
|
|
204
|
+
"can_log_pii": False,
|
|
205
|
+
"audit_all_tool_calls": True,
|
|
206
|
+
},
|
|
207
|
+
}
|
|
208
|
+
with open(target / "constitution.yaml", "w", encoding="utf-8") as f:
|
|
209
|
+
yaml.dump(constitution, f, default_flow_style=False)
|
|
210
|
+
|
|
211
|
+
# agents/main.py
|
|
212
|
+
agent_code = _agent_template(name=name, framework=framework)
|
|
213
|
+
with open(target / "agents" / "main.py", "w", encoding="utf-8") as f:
|
|
214
|
+
f.write(agent_code)
|
|
215
|
+
|
|
216
|
+
# README
|
|
217
|
+
with open(target / "README.md", "w", encoding="utf-8") as f:
|
|
218
|
+
f.write(_readme_template(name=name, framework=framework))
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _agent_template(name: str, framework: str) -> str:
|
|
222
|
+
safe_name = name.replace("-", "_")
|
|
223
|
+
|
|
224
|
+
if framework == "langgraph":
|
|
225
|
+
return f'''# Colony: {name}
|
|
226
|
+
# Framework: LangGraph
|
|
227
|
+
# Ant'z SDK injects: observability, audit, constitution validation, memory
|
|
228
|
+
|
|
229
|
+
from antz import agent, tool, memory
|
|
230
|
+
from typing import TypedDict
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
# ── State ─────────────────────────────────────────────────────────────────────
|
|
234
|
+
|
|
235
|
+
class AgentState(TypedDict):
|
|
236
|
+
input: dict
|
|
237
|
+
result: dict
|
|
238
|
+
status: str
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
# ── Tools ─────────────────────────────────────────────────────────────────────
|
|
242
|
+
|
|
243
|
+
@tool("{safe_name}-lookup")
|
|
244
|
+
def lookup_data(query: str) -> dict:
|
|
245
|
+
"""
|
|
246
|
+
Replace with your actual tool logic.
|
|
247
|
+
Ant'z automatically logs this call and validates against constitution.
|
|
248
|
+
"""
|
|
249
|
+
return {{"data": f"mock result for: {{query}}", "confidence": 0.9}}
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
# ── Main agent ────────────────────────────────────────────────────────────────
|
|
253
|
+
|
|
254
|
+
@agent("{safe_name}-v1")
|
|
255
|
+
@memory(namespace="{safe_name}")
|
|
256
|
+
def run_agent(input_data: dict, memory_context: list = None) -> dict:
|
|
257
|
+
"""
|
|
258
|
+
Main agent entrypoint.
|
|
259
|
+
memory_context is auto-populated with relevant past runs from Hive.
|
|
260
|
+
Replace the logic below with your LangGraph workflow.
|
|
261
|
+
"""
|
|
262
|
+
past = memory_context or []
|
|
263
|
+
context = past[0]["content"] if past else "No prior context."
|
|
264
|
+
|
|
265
|
+
result = lookup_data(str(input_data))
|
|
266
|
+
|
|
267
|
+
return {{
|
|
268
|
+
"status": "success",
|
|
269
|
+
"colony": "{name}",
|
|
270
|
+
"result": result,
|
|
271
|
+
"memory_hits": len(past),
|
|
272
|
+
"context_used": context,
|
|
273
|
+
}}
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
# ── Demo entrypoint ───────────────────────────────────────────────────────────
|
|
277
|
+
|
|
278
|
+
if __name__ == "__main__":
|
|
279
|
+
import json, sys
|
|
280
|
+
|
|
281
|
+
sample_input = {{"query": "demo run", "source": "antz-cli"}}
|
|
282
|
+
|
|
283
|
+
if "--input" in sys.argv:
|
|
284
|
+
idx = sys.argv.index("--input")
|
|
285
|
+
with open(sys.argv[idx + 1]) as f:
|
|
286
|
+
sample_input = json.load(f)
|
|
287
|
+
|
|
288
|
+
output = run_agent(sample_input)
|
|
289
|
+
print(json.dumps(output, indent=2))
|
|
290
|
+
'''
|
|
291
|
+
|
|
292
|
+
elif framework == "crewai":
|
|
293
|
+
return f'''# Colony: {name}
|
|
294
|
+
# Framework: CrewAI
|
|
295
|
+
# Ant'z SDK injects: observability, audit, constitution validation, memory
|
|
296
|
+
|
|
297
|
+
from antz import agent, tool, memory
|
|
298
|
+
from crewai import Agent, Task, Crew
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
# ── Tools ─────────────────────────────────────────────────────────────────────
|
|
302
|
+
|
|
303
|
+
@tool("{safe_name}-lookup")
|
|
304
|
+
def lookup_data(query: str) -> str:
|
|
305
|
+
"""Replace with your actual tool logic."""
|
|
306
|
+
return f"mock result for: {{query}}"
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
# ── Crew definition ───────────────────────────────────────────────────────────
|
|
310
|
+
|
|
311
|
+
def build_crew():
|
|
312
|
+
analyst = Agent(
|
|
313
|
+
role="Data Analyst",
|
|
314
|
+
goal="Analyze input data and produce a structured report",
|
|
315
|
+
backstory="Expert analyst with deep domain knowledge",
|
|
316
|
+
verbose=False,
|
|
317
|
+
)
|
|
318
|
+
task = Task(
|
|
319
|
+
description="Analyze the provided data and return a structured summary.",
|
|
320
|
+
expected_output="A JSON-formatted analysis report",
|
|
321
|
+
agent=analyst,
|
|
322
|
+
)
|
|
323
|
+
return Crew(agents=[analyst], tasks=[task], verbose=False)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
# ── Main agent ────────────────────────────────────────────────────────────────
|
|
327
|
+
|
|
328
|
+
@agent("{safe_name}-v1")
|
|
329
|
+
@memory(namespace="{safe_name}")
|
|
330
|
+
def run_agent(input_data: dict, memory_context: list = None) -> dict:
|
|
331
|
+
"""
|
|
332
|
+
Main agent entrypoint.
|
|
333
|
+
memory_context is auto-populated with relevant past runs from Hive.
|
|
334
|
+
"""
|
|
335
|
+
past = memory_context or []
|
|
336
|
+
crew = build_crew()
|
|
337
|
+
result = crew.kickoff(inputs=input_data)
|
|
338
|
+
return {{
|
|
339
|
+
"status": "success",
|
|
340
|
+
"colony": "{name}",
|
|
341
|
+
"result": str(result),
|
|
342
|
+
"memory_hits": len(past),
|
|
343
|
+
}}
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
# ── Demo entrypoint ───────────────────────────────────────────────────────────
|
|
347
|
+
|
|
348
|
+
if __name__ == "__main__":
|
|
349
|
+
import json, sys
|
|
350
|
+
|
|
351
|
+
sample_input = {{"query": "demo run", "source": "antz-cli"}}
|
|
352
|
+
|
|
353
|
+
if "--input" in sys.argv:
|
|
354
|
+
idx = sys.argv.index("--input")
|
|
355
|
+
with open(sys.argv[idx + 1]) as f:
|
|
356
|
+
sample_input = json.load(f)
|
|
357
|
+
|
|
358
|
+
output = run_agent(sample_input)
|
|
359
|
+
print(json.dumps(output, indent=2))
|
|
360
|
+
'''
|
|
361
|
+
|
|
362
|
+
else: # mixed
|
|
363
|
+
return f'''# Colony: {name}
|
|
364
|
+
# Framework: Mixed (LangGraph + raw LiteLLM)
|
|
365
|
+
# Ant'z SDK injects: observability, audit, constitution validation, memory
|
|
366
|
+
|
|
367
|
+
from antz import agent, tool, memory
|
|
368
|
+
import httpx
|
|
369
|
+
import os
|
|
370
|
+
|
|
371
|
+
LITELLM_URL = os.getenv("ANTZ_LITELLM_URL", "http://localhost:4000")
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
# ── Tools ─────────────────────────────────────────────────────────────────────
|
|
375
|
+
|
|
376
|
+
@tool("{safe_name}-llm-call")
|
|
377
|
+
def call_llm(prompt: str) -> str:
|
|
378
|
+
"""Direct LiteLLM call through the Nest — stays sovereign."""
|
|
379
|
+
r = httpx.post(
|
|
380
|
+
f"{{LITELLM_URL}}/chat/completions",
|
|
381
|
+
json={{
|
|
382
|
+
"model": "antz-default",
|
|
383
|
+
"messages": [{{"role": "user", "content": prompt}}],
|
|
384
|
+
"max_tokens": 512,
|
|
385
|
+
}},
|
|
386
|
+
timeout=30,
|
|
387
|
+
)
|
|
388
|
+
r.raise_for_status()
|
|
389
|
+
return r.json()["choices"][0]["message"]["content"]
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
# ── Main agent ────────────────────────────────────────────────────────────────
|
|
393
|
+
|
|
394
|
+
@agent("{safe_name}-v1")
|
|
395
|
+
@memory(namespace="{safe_name}")
|
|
396
|
+
def run_agent(input_data: dict, memory_context: list = None) -> dict:
|
|
397
|
+
"""
|
|
398
|
+
Main agent with memory context from Hive.
|
|
399
|
+
memory_context is auto-populated with relevant past runs.
|
|
400
|
+
"""
|
|
401
|
+
past = memory_context or []
|
|
402
|
+
context_str = "\\n".join(str(m) for m in past[:3]) if past else "No prior context."
|
|
403
|
+
|
|
404
|
+
prompt = f"""
|
|
405
|
+
Context from previous runs:
|
|
406
|
+
{{context_str}}
|
|
407
|
+
|
|
408
|
+
Current input: {{input_data}}
|
|
409
|
+
|
|
410
|
+
Analyze and respond with a structured result.
|
|
411
|
+
"""
|
|
412
|
+
|
|
413
|
+
response = call_llm(prompt)
|
|
414
|
+
|
|
415
|
+
return {{
|
|
416
|
+
"status": "success",
|
|
417
|
+
"colony": "{name}",
|
|
418
|
+
"result": response,
|
|
419
|
+
"memory_hits": len(past),
|
|
420
|
+
}}
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
# ── Demo entrypoint ───────────────────────────────────────────────────────────
|
|
424
|
+
|
|
425
|
+
if __name__ == "__main__":
|
|
426
|
+
import json, sys
|
|
427
|
+
|
|
428
|
+
sample_input = {{"query": "demo run", "source": "antz-cli"}}
|
|
429
|
+
|
|
430
|
+
if "--input" in sys.argv:
|
|
431
|
+
idx = sys.argv.index("--input")
|
|
432
|
+
with open(sys.argv[idx + 1]) as f:
|
|
433
|
+
sample_input = json.load(f)
|
|
434
|
+
|
|
435
|
+
output = run_agent(sample_input)
|
|
436
|
+
print(json.dumps(output, indent=2))
|
|
437
|
+
'''
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def _readme_template(name: str, framework: str) -> str:
|
|
441
|
+
return f"""# Colony: {name}
|
|
442
|
+
|
|
443
|
+
Framework: `{framework}`
|
|
444
|
+
|
|
445
|
+
## Run
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
# Demo (no input required)
|
|
449
|
+
antz run --demo
|
|
450
|
+
|
|
451
|
+
# With input file
|
|
452
|
+
antz run --input data/sample.json
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
## Structure
|
|
456
|
+
|
|
457
|
+
```
|
|
458
|
+
agents/main.py # Main agent logic — edit this
|
|
459
|
+
tools/ # Add your tools here
|
|
460
|
+
workflows/ # Workflow definitions
|
|
461
|
+
constitution.yaml # Agent behavioral constraints
|
|
462
|
+
antz.config.yaml # Colony configuration
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
## Constitution
|
|
466
|
+
|
|
467
|
+
Edit `constitution.yaml` to define what this agent can and cannot do.
|
|
468
|
+
The Ant'z Hive validates every tool call against these rules at runtime.
|
|
469
|
+
|
|
470
|
+
## Observability
|
|
471
|
+
|
|
472
|
+
All executions are automatically traced. View them in the Nest Grafana dashboard.
|
|
473
|
+
"""
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def _run_demo(cwd: Path, colony_cfg: dict) -> None:
|
|
477
|
+
"""Run the demo mode — executes agents/main.py with sample data."""
|
|
478
|
+
entry = cwd / "agents" / "main.py"
|
|
479
|
+
if not entry.exists():
|
|
480
|
+
console.print("[red]✗ agents/main.py not found.[/red]")
|
|
481
|
+
raise typer.Exit(1)
|
|
482
|
+
|
|
483
|
+
console.print("[dim]Running demo agent...[/dim]\n")
|
|
484
|
+
result = subprocess.run([sys.executable, str(entry)], cwd=str(cwd))
|
|
485
|
+
raise typer.Exit(result.returncode)
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
# -- antz nest ----------------------------------------------------------------
|
|
491
|
+
|
|
492
|
+
@app.command("nest")
|
|
493
|
+
def nest_cmd(
|
|
494
|
+
action: str = typer.Argument(..., help="status"),
|
|
495
|
+
tenant: str = typer.Option(..., "--tenant", "-t", help="Tenant ID"),
|
|
496
|
+
watch: bool = typer.Option(False, "--watch", "-w"),
|
|
497
|
+
api_key: str = typer.Option(None, "--api-key"),
|
|
498
|
+
):
|
|
499
|
+
"""Manage Nest provisioning and status."""
|
|
500
|
+
import httpx, os, time
|
|
501
|
+
cp_url = os.getenv("ANTZ_CONTROL_PLANE_URL", "https://control.antz.studio")
|
|
502
|
+
if not api_key:
|
|
503
|
+
api_key = typer.prompt("Control Plane API Key")
|
|
504
|
+
STATUS = {
|
|
505
|
+
"pending": "Aguardando...", "provisioning": "Iniciando...",
|
|
506
|
+
"creating_vm": "Criando VM...", "vm_created": "VM criada...",
|
|
507
|
+
"wireguard_ready": "WireGuard pronto...", "provisioning_nest": "Instalando Nest...",
|
|
508
|
+
"sending_email": "Enviando email...", "active": "Nest ativo!", "failed": "Falhou",
|
|
509
|
+
}
|
|
510
|
+
def get():
|
|
511
|
+
try:
|
|
512
|
+
r = httpx.get(f"{cp_url}/tenants/{tenant}", headers={"x-api-key": api_key}, timeout=10)
|
|
513
|
+
return r.json() if r.status_code == 200 else None
|
|
514
|
+
except Exception:
|
|
515
|
+
return None
|
|
516
|
+
data = get()
|
|
517
|
+
if not data:
|
|
518
|
+
console.print(f"[red]Tenant {tenant!r} nao encontrado.[/red]")
|
|
519
|
+
raise typer.Exit(1)
|
|
520
|
+
if not watch:
|
|
521
|
+
console.print(f"Tenant: [cyan]{tenant}[/cyan]")
|
|
522
|
+
console.print(f"Status: {STATUS.get(data.get('status','?'), data.get('status','?'))}")
|
|
523
|
+
if data.get("nest_url"):
|
|
524
|
+
console.print(f"URL: [green]{data['nest_url']}[/green]")
|
|
525
|
+
return
|
|
526
|
+
last = None
|
|
527
|
+
while True:
|
|
528
|
+
data = get()
|
|
529
|
+
if not data:
|
|
530
|
+
time.sleep(10); continue
|
|
531
|
+
s = data.get("status", "")
|
|
532
|
+
if s != last:
|
|
533
|
+
console.print(STATUS.get(s, s)); last = s
|
|
534
|
+
if s == "active":
|
|
535
|
+
url = data.get("nest_url", "")
|
|
536
|
+
console.print(f"[green]Nest ativo em {url}[/green]")
|
|
537
|
+
console.print(f"[dim]antz login --nest {url} --api-key SUA_CHAVE[/dim]")
|
|
538
|
+
raise typer.Exit(0)
|
|
539
|
+
if s == "failed":
|
|
540
|
+
console.print("[red]Falhou.[/red]"); raise typer.Exit(1)
|
|
541
|
+
time.sleep(15)
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
if __name__ == "__main__":
|
|
545
|
+
app()
|