smartify-ai 0.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.
- smartify/__init__.py +3 -0
- smartify/agents/__init__.py +0 -0
- smartify/agents/adapters/__init__.py +13 -0
- smartify/agents/adapters/anthropic.py +253 -0
- smartify/agents/adapters/openai.py +289 -0
- smartify/api/__init__.py +26 -0
- smartify/api/auth.py +352 -0
- smartify/api/errors.py +380 -0
- smartify/api/events.py +345 -0
- smartify/api/server.py +992 -0
- smartify/cli/__init__.py +1 -0
- smartify/cli/main.py +430 -0
- smartify/engine/__init__.py +64 -0
- smartify/engine/approval.py +479 -0
- smartify/engine/orchestrator.py +1365 -0
- smartify/engine/scheduler.py +380 -0
- smartify/engine/spark.py +294 -0
- smartify/guardrails/__init__.py +22 -0
- smartify/guardrails/breakers.py +409 -0
- smartify/models/__init__.py +61 -0
- smartify/models/grid.py +625 -0
- smartify/notifications/__init__.py +22 -0
- smartify/notifications/webhook.py +556 -0
- smartify/state/__init__.py +46 -0
- smartify/state/checkpoint.py +558 -0
- smartify/state/resume.py +301 -0
- smartify/state/store.py +370 -0
- smartify/tools/__init__.py +17 -0
- smartify/tools/base.py +196 -0
- smartify/tools/builtin/__init__.py +79 -0
- smartify/tools/builtin/file.py +464 -0
- smartify/tools/builtin/http.py +195 -0
- smartify/tools/builtin/shell.py +137 -0
- smartify/tools/mcp/__init__.py +33 -0
- smartify/tools/mcp/adapter.py +157 -0
- smartify/tools/mcp/client.py +334 -0
- smartify/tools/mcp/registry.py +130 -0
- smartify/validator/__init__.py +0 -0
- smartify/validator/validate.py +271 -0
- smartify/workspace/__init__.py +5 -0
- smartify/workspace/manager.py +248 -0
- smartify_ai-0.1.0.dist-info/METADATA +201 -0
- smartify_ai-0.1.0.dist-info/RECORD +46 -0
- smartify_ai-0.1.0.dist-info/WHEEL +4 -0
- smartify_ai-0.1.0.dist-info/entry_points.txt +2 -0
- smartify_ai-0.1.0.dist-info/licenses/LICENSE +21 -0
smartify/cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI module for Smartify."""
|
smartify/cli/main.py
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
"""Smartify CLI - Main entry point."""
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich.table import Table
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Optional
|
|
8
|
+
import yaml
|
|
9
|
+
|
|
10
|
+
from smartify import __version__
|
|
11
|
+
|
|
12
|
+
app = typer.Typer(
|
|
13
|
+
name="smartify",
|
|
14
|
+
help="Local runtime for Smartify Grid specifications",
|
|
15
|
+
no_args_is_help=True,
|
|
16
|
+
)
|
|
17
|
+
console = Console()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def version_callback(value: bool) -> None:
|
|
21
|
+
"""Print version and exit."""
|
|
22
|
+
if value:
|
|
23
|
+
console.print(f"[bold blue]smartify[/bold blue] version {__version__}")
|
|
24
|
+
raise typer.Exit()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@app.callback()
|
|
28
|
+
def main(
|
|
29
|
+
version: bool = typer.Option(
|
|
30
|
+
False,
|
|
31
|
+
"--version",
|
|
32
|
+
"-v",
|
|
33
|
+
help="Show version and exit",
|
|
34
|
+
callback=version_callback,
|
|
35
|
+
is_eager=True,
|
|
36
|
+
),
|
|
37
|
+
) -> None:
|
|
38
|
+
"""Smartify - Local runtime for Grid specifications."""
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@app.command()
|
|
43
|
+
def validate(
|
|
44
|
+
grid_file: Path = typer.Argument(
|
|
45
|
+
...,
|
|
46
|
+
help="Path to Grid YAML file",
|
|
47
|
+
exists=True,
|
|
48
|
+
readable=True,
|
|
49
|
+
),
|
|
50
|
+
verbose: bool = typer.Option(False, "--verbose", "-v", help="Show detailed output"),
|
|
51
|
+
) -> None:
|
|
52
|
+
"""Validate a Grid YAML specification."""
|
|
53
|
+
from smartify.validator.validate import validate_grid_file
|
|
54
|
+
|
|
55
|
+
console.print(f"[bold]Validating[/bold] {grid_file}")
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
result = validate_grid_file(grid_file)
|
|
59
|
+
|
|
60
|
+
if result.is_valid:
|
|
61
|
+
console.print("[bold green]✓[/bold green] Grid specification is valid")
|
|
62
|
+
|
|
63
|
+
if verbose and result.grid:
|
|
64
|
+
console.print(f"\n[bold]Grid ID:[/bold] {result.grid.metadata.id}")
|
|
65
|
+
console.print(f"[bold]Name:[/bold] {result.grid.metadata.name}")
|
|
66
|
+
console.print(f"[bold]Nodes:[/bold] {len(result.grid.topology.nodes)}")
|
|
67
|
+
else:
|
|
68
|
+
console.print("[bold red]✗[/bold red] Validation failed")
|
|
69
|
+
|
|
70
|
+
for error in result.errors:
|
|
71
|
+
console.print(f" [red]•[/red] {error}")
|
|
72
|
+
|
|
73
|
+
raise typer.Exit(1)
|
|
74
|
+
|
|
75
|
+
if result.warnings:
|
|
76
|
+
console.print(f"\n[yellow]Warnings ({len(result.warnings)}):[/yellow]")
|
|
77
|
+
for warning in result.warnings:
|
|
78
|
+
console.print(f" [yellow]•[/yellow] {warning}")
|
|
79
|
+
|
|
80
|
+
except Exception as e:
|
|
81
|
+
console.print(f"[bold red]Error:[/bold red] {e}")
|
|
82
|
+
raise typer.Exit(1)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@app.command()
|
|
86
|
+
def run(
|
|
87
|
+
grid_file: Path = typer.Argument(
|
|
88
|
+
...,
|
|
89
|
+
help="Path to Grid YAML file",
|
|
90
|
+
exists=True,
|
|
91
|
+
readable=True,
|
|
92
|
+
),
|
|
93
|
+
input: list[str] = typer.Option(
|
|
94
|
+
[],
|
|
95
|
+
"--input",
|
|
96
|
+
"-i",
|
|
97
|
+
help="Input parameters (key=value)",
|
|
98
|
+
),
|
|
99
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Validate only, don't execute"),
|
|
100
|
+
watch: bool = typer.Option(False, "--watch", "-w", help="Watch execution progress"),
|
|
101
|
+
verbose: bool = typer.Option(False, "--verbose", "-v", help="Show detailed output"),
|
|
102
|
+
) -> None:
|
|
103
|
+
"""Run a Grid specification."""
|
|
104
|
+
import asyncio
|
|
105
|
+
from smartify.engine.orchestrator import Orchestrator, ExecutionError
|
|
106
|
+
|
|
107
|
+
console.print(f"[bold]Running[/bold] {grid_file}")
|
|
108
|
+
|
|
109
|
+
# Parse inputs
|
|
110
|
+
inputs = {}
|
|
111
|
+
for inp in input:
|
|
112
|
+
if "=" in inp:
|
|
113
|
+
key, value = inp.split("=", 1)
|
|
114
|
+
inputs[key] = value
|
|
115
|
+
else:
|
|
116
|
+
console.print(f"[yellow]Warning:[/yellow] Invalid input format: {inp}")
|
|
117
|
+
|
|
118
|
+
if inputs:
|
|
119
|
+
console.print(f"[bold]Inputs:[/bold] {inputs}")
|
|
120
|
+
|
|
121
|
+
if dry_run:
|
|
122
|
+
console.print("[yellow]Dry run mode - validating only[/yellow]")
|
|
123
|
+
from smartify.validator.validate import validate_grid_file
|
|
124
|
+
result = validate_grid_file(grid_file)
|
|
125
|
+
if result.is_valid:
|
|
126
|
+
console.print("[bold green]✓[/bold green] Grid is valid and ready to run")
|
|
127
|
+
|
|
128
|
+
# Show execution plan
|
|
129
|
+
if verbose and result.grid:
|
|
130
|
+
from smartify.engine.scheduler import DAGScheduler
|
|
131
|
+
scheduler = DAGScheduler(result.grid)
|
|
132
|
+
scheduler.build_graph()
|
|
133
|
+
console.print("\n[bold]Execution Plan:[/bold]")
|
|
134
|
+
console.print(scheduler.visualize())
|
|
135
|
+
else:
|
|
136
|
+
console.print("[bold red]✗[/bold red] Grid validation failed")
|
|
137
|
+
for error in result.errors:
|
|
138
|
+
console.print(f" [red]•[/red] {error}")
|
|
139
|
+
raise typer.Exit(1)
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
async def _run():
|
|
143
|
+
import os
|
|
144
|
+
from smartify.agents.adapters import AnthropicAdapter
|
|
145
|
+
|
|
146
|
+
orchestrator = Orchestrator()
|
|
147
|
+
|
|
148
|
+
# Auto-register LLM adapter from environment
|
|
149
|
+
if os.environ.get("ANTHROPIC_API_KEY"):
|
|
150
|
+
adapter = AnthropicAdapter()
|
|
151
|
+
orchestrator.register_llm_adapter("anthropic", adapter)
|
|
152
|
+
orchestrator.register_llm_adapter("default", adapter)
|
|
153
|
+
console.print("[green]✓[/green] Anthropic LLM adapter configured")
|
|
154
|
+
else:
|
|
155
|
+
console.print("[yellow]Warning:[/yellow] ANTHROPIC_API_KEY not set - LLM calls will fail")
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
# Load grid
|
|
159
|
+
grid_run = await orchestrator.load_grid(grid_file, inputs=inputs)
|
|
160
|
+
console.print(f"[green]✓[/green] Loaded grid '{grid_run.grid.id}'")
|
|
161
|
+
|
|
162
|
+
if verbose:
|
|
163
|
+
console.print(f" Nodes: {len(grid_run.grid.topology.nodes)}")
|
|
164
|
+
|
|
165
|
+
# Energize
|
|
166
|
+
await orchestrator.energize(grid_run)
|
|
167
|
+
console.print(f"[green]✓[/green] Grid energized")
|
|
168
|
+
|
|
169
|
+
# Execute
|
|
170
|
+
console.print("[bold]Executing...[/bold]")
|
|
171
|
+
result = await orchestrator.execute(grid_run)
|
|
172
|
+
|
|
173
|
+
# Show results
|
|
174
|
+
if result.get('state') == 'completed':
|
|
175
|
+
console.print("\n[bold green]✓ Grid completed successfully[/bold green]")
|
|
176
|
+
else:
|
|
177
|
+
console.print(f"\n[bold red]✗ Grid failed: {result.get('error')}[/bold red]")
|
|
178
|
+
|
|
179
|
+
if verbose:
|
|
180
|
+
console.print("\n[bold]Results:[/bold]")
|
|
181
|
+
import json
|
|
182
|
+
console.print(json.dumps(result, indent=2, default=str))
|
|
183
|
+
|
|
184
|
+
return result
|
|
185
|
+
|
|
186
|
+
except ExecutionError as e:
|
|
187
|
+
console.print(f"[bold red]Execution error:[/bold red] {e}")
|
|
188
|
+
raise typer.Exit(1)
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
asyncio.run(_run())
|
|
192
|
+
except KeyboardInterrupt:
|
|
193
|
+
console.print("\n[yellow]Execution interrupted[/yellow]")
|
|
194
|
+
raise typer.Exit(130)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@app.command()
|
|
198
|
+
def status(
|
|
199
|
+
run_id: str = typer.Argument(..., help="Run ID to check"),
|
|
200
|
+
db: str = typer.Option("smartify_runs.db", "--db", help="Database file path"),
|
|
201
|
+
) -> None:
|
|
202
|
+
"""Check status of a grid run."""
|
|
203
|
+
from smartify.state import SQLiteStore, RunStatus
|
|
204
|
+
|
|
205
|
+
store = SQLiteStore(db)
|
|
206
|
+
run = store.get_run(run_id)
|
|
207
|
+
|
|
208
|
+
if not run:
|
|
209
|
+
console.print(f"[red]Run not found:[/red] {run_id}")
|
|
210
|
+
raise typer.Exit(1)
|
|
211
|
+
|
|
212
|
+
# Status color
|
|
213
|
+
status_colors = {
|
|
214
|
+
RunStatus.PENDING: "yellow",
|
|
215
|
+
RunStatus.RUNNING: "blue",
|
|
216
|
+
RunStatus.PAUSED: "yellow",
|
|
217
|
+
RunStatus.COMPLETED: "green",
|
|
218
|
+
RunStatus.FAILED: "red",
|
|
219
|
+
RunStatus.STOPPED: "dim",
|
|
220
|
+
}
|
|
221
|
+
color = status_colors.get(run.status, "white")
|
|
222
|
+
|
|
223
|
+
console.print(f"\n[bold]Run:[/bold] {run.run_id}")
|
|
224
|
+
console.print(f"[bold]Grid:[/bold] {run.grid_name} ({run.grid_id})")
|
|
225
|
+
console.print(f"[bold]Status:[/bold] [{color}]{run.status.value}[/{color}]")
|
|
226
|
+
console.print(f"[bold]Created:[/bold] {run.created_at.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
227
|
+
|
|
228
|
+
if run.started_at:
|
|
229
|
+
console.print(f"[bold]Started:[/bold] {run.started_at.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
230
|
+
if run.completed_at:
|
|
231
|
+
console.print(f"[bold]Completed:[/bold] {run.completed_at.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
232
|
+
|
|
233
|
+
console.print(f"[bold]Tokens:[/bold] {run.total_tokens:,}")
|
|
234
|
+
console.print(f"[bold]Cost:[/bold] ${run.total_cost:.4f}")
|
|
235
|
+
|
|
236
|
+
if run.error:
|
|
237
|
+
console.print(f"[bold red]Error:[/bold red] {run.error}")
|
|
238
|
+
|
|
239
|
+
# Show node outputs
|
|
240
|
+
outputs = store.get_node_outputs(run_id)
|
|
241
|
+
if outputs:
|
|
242
|
+
console.print(f"\n[bold]Node Outputs:[/bold] {len(outputs)}")
|
|
243
|
+
for out in outputs:
|
|
244
|
+
status_icon = "[green]✓[/green]" if out.success else "[red]✗[/red]"
|
|
245
|
+
console.print(f" {status_icon} {out.node_id} ({out.tokens_used} tokens)")
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@app.command()
|
|
249
|
+
def stop(
|
|
250
|
+
run_id: str = typer.Argument(..., help="Run ID to stop"),
|
|
251
|
+
db: str = typer.Option("smartify_runs.db", "--db", help="Database file path"),
|
|
252
|
+
force: bool = typer.Option(False, "--force", "-f", help="Force stop immediately"),
|
|
253
|
+
) -> None:
|
|
254
|
+
"""Stop a running grid."""
|
|
255
|
+
from smartify.state import SQLiteStore, RunStatus
|
|
256
|
+
|
|
257
|
+
store = SQLiteStore(db)
|
|
258
|
+
run = store.get_run(run_id)
|
|
259
|
+
|
|
260
|
+
if not run:
|
|
261
|
+
console.print(f"[red]Run not found:[/red] {run_id}")
|
|
262
|
+
raise typer.Exit(1)
|
|
263
|
+
|
|
264
|
+
if run.status not in (RunStatus.RUNNING, RunStatus.PAUSED):
|
|
265
|
+
console.print(f"[yellow]Run is not active:[/yellow] {run.status.value}")
|
|
266
|
+
raise typer.Exit(1)
|
|
267
|
+
|
|
268
|
+
from datetime import datetime
|
|
269
|
+
run.status = RunStatus.STOPPED
|
|
270
|
+
run.completed_at = datetime.now()
|
|
271
|
+
store.save_run(run)
|
|
272
|
+
|
|
273
|
+
console.print(f"[green]✓[/green] Run stopped: {run_id}")
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@app.command(name="list")
|
|
277
|
+
def list_runs(
|
|
278
|
+
limit: int = typer.Option(10, "--limit", "-n", help="Number of runs to show"),
|
|
279
|
+
status_filter: Optional[str] = typer.Option(None, "--status", "-s", help="Filter by status"),
|
|
280
|
+
grid_id: Optional[str] = typer.Option(None, "--grid", "-g", help="Filter by grid ID"),
|
|
281
|
+
db: str = typer.Option("smartify_runs.db", "--db", help="Database file path"),
|
|
282
|
+
) -> None:
|
|
283
|
+
"""List grid runs."""
|
|
284
|
+
from smartify.state import SQLiteStore, RunStatus
|
|
285
|
+
|
|
286
|
+
store = SQLiteStore(db)
|
|
287
|
+
|
|
288
|
+
# Parse status filter
|
|
289
|
+
status = None
|
|
290
|
+
if status_filter:
|
|
291
|
+
try:
|
|
292
|
+
status = RunStatus(status_filter.lower())
|
|
293
|
+
except ValueError:
|
|
294
|
+
console.print(f"[red]Invalid status:[/red] {status_filter}")
|
|
295
|
+
console.print(f"Valid: {', '.join(s.value for s in RunStatus)}")
|
|
296
|
+
raise typer.Exit(1)
|
|
297
|
+
|
|
298
|
+
runs = store.list_runs(status=status, grid_id=grid_id, limit=limit)
|
|
299
|
+
|
|
300
|
+
if not runs:
|
|
301
|
+
console.print("[dim]No runs found[/dim]")
|
|
302
|
+
return
|
|
303
|
+
|
|
304
|
+
# Create table
|
|
305
|
+
table = Table(title="Grid Runs")
|
|
306
|
+
table.add_column("Run ID", style="cyan")
|
|
307
|
+
table.add_column("Grid", style="blue")
|
|
308
|
+
table.add_column("Status")
|
|
309
|
+
table.add_column("Tokens", justify="right")
|
|
310
|
+
table.add_column("Cost", justify="right")
|
|
311
|
+
table.add_column("Created")
|
|
312
|
+
|
|
313
|
+
for run in runs:
|
|
314
|
+
status_colors = {
|
|
315
|
+
RunStatus.PENDING: "yellow",
|
|
316
|
+
RunStatus.RUNNING: "blue",
|
|
317
|
+
RunStatus.PAUSED: "yellow",
|
|
318
|
+
RunStatus.COMPLETED: "green",
|
|
319
|
+
RunStatus.FAILED: "red",
|
|
320
|
+
RunStatus.STOPPED: "dim",
|
|
321
|
+
}
|
|
322
|
+
color = status_colors.get(run.status, "white")
|
|
323
|
+
|
|
324
|
+
table.add_row(
|
|
325
|
+
run.run_id[:12] + "..." if len(run.run_id) > 15 else run.run_id,
|
|
326
|
+
run.grid_name[:20],
|
|
327
|
+
f"[{color}]{run.status.value}[/{color}]",
|
|
328
|
+
f"{run.total_tokens:,}",
|
|
329
|
+
f"${run.total_cost:.4f}",
|
|
330
|
+
run.created_at.strftime("%m-%d %H:%M"),
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
console.print(table)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
@app.command()
|
|
337
|
+
def serve(
|
|
338
|
+
host: str = typer.Option("0.0.0.0", "--host", "-h", help="Host to bind to"),
|
|
339
|
+
port: int = typer.Option(8080, "--port", "-p", help="Port to bind to"),
|
|
340
|
+
reload: bool = typer.Option(False, "--reload", "-r", help="Enable auto-reload (dev mode)"),
|
|
341
|
+
) -> None:
|
|
342
|
+
"""Start the Smartify API server."""
|
|
343
|
+
from smartify.api.server import run_server
|
|
344
|
+
|
|
345
|
+
console.print(f"[bold]Starting Smartify API server[/bold]")
|
|
346
|
+
console.print(f" [dim]Host:[/dim] {host}")
|
|
347
|
+
console.print(f" [dim]Port:[/dim] {port}")
|
|
348
|
+
console.print(f" [dim]Docs:[/dim] http://{host}:{port}/docs")
|
|
349
|
+
|
|
350
|
+
run_server(host=host, port=port, reload=reload)
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
@app.command()
|
|
354
|
+
def info(
|
|
355
|
+
grid_file: Path = typer.Argument(
|
|
356
|
+
...,
|
|
357
|
+
help="Path to Grid YAML file",
|
|
358
|
+
exists=True,
|
|
359
|
+
readable=True,
|
|
360
|
+
),
|
|
361
|
+
) -> None:
|
|
362
|
+
"""Show information about a Grid specification."""
|
|
363
|
+
from smartify.validator.validate import validate_grid_file
|
|
364
|
+
|
|
365
|
+
try:
|
|
366
|
+
result = validate_grid_file(grid_file)
|
|
367
|
+
|
|
368
|
+
if not result.is_valid or not result.grid:
|
|
369
|
+
console.print("[bold red]Error:[/bold red] Invalid grid file")
|
|
370
|
+
for error in result.errors:
|
|
371
|
+
console.print(f" [red]•[/red] {error}")
|
|
372
|
+
raise typer.Exit(1)
|
|
373
|
+
|
|
374
|
+
grid = result.grid
|
|
375
|
+
|
|
376
|
+
# Grid metadata
|
|
377
|
+
console.print("\n[bold blue]Grid Information[/bold blue]")
|
|
378
|
+
console.print(f" [bold]ID:[/bold] {grid.metadata.id}")
|
|
379
|
+
console.print(f" [bold]Name:[/bold] {grid.metadata.name}")
|
|
380
|
+
if grid.metadata.description:
|
|
381
|
+
console.print(f" [bold]Description:[/bold] {grid.metadata.description}")
|
|
382
|
+
console.print(f" [bold]Version:[/bold] {grid.metadata.version}")
|
|
383
|
+
|
|
384
|
+
# Topology
|
|
385
|
+
console.print("\n[bold blue]Topology[/bold blue]")
|
|
386
|
+
|
|
387
|
+
# Count node types
|
|
388
|
+
node_counts = {}
|
|
389
|
+
for node in grid.topology.nodes:
|
|
390
|
+
kind = node.kind.value if hasattr(node.kind, 'value') else str(node.kind)
|
|
391
|
+
node_counts[kind] = node_counts.get(kind, 0) + 1
|
|
392
|
+
|
|
393
|
+
for kind, count in node_counts.items():
|
|
394
|
+
console.print(f" [bold]{kind}:[/bold] {count}")
|
|
395
|
+
|
|
396
|
+
# Node table
|
|
397
|
+
table = Table(title="Nodes")
|
|
398
|
+
table.add_column("ID", style="cyan")
|
|
399
|
+
table.add_column("Kind", style="green")
|
|
400
|
+
table.add_column("Name")
|
|
401
|
+
table.add_column("Parent", style="dim")
|
|
402
|
+
|
|
403
|
+
for node in grid.topology.nodes:
|
|
404
|
+
kind = node.kind.value if hasattr(node.kind, 'value') else str(node.kind)
|
|
405
|
+
table.add_row(node.id, kind, node.name, node.parent or "-")
|
|
406
|
+
|
|
407
|
+
console.print(table)
|
|
408
|
+
|
|
409
|
+
# Inputs
|
|
410
|
+
if grid.inputs:
|
|
411
|
+
console.print("\n[bold blue]Inputs[/bold blue]")
|
|
412
|
+
for inp in grid.inputs:
|
|
413
|
+
req = "[red]*[/red]" if inp.required else ""
|
|
414
|
+
default = f" (default: {inp.default})" if inp.default is not None else ""
|
|
415
|
+
console.print(f" {req}[bold]{inp.name}[/bold]: {inp.type}{default}")
|
|
416
|
+
|
|
417
|
+
# Agents
|
|
418
|
+
if grid.agents:
|
|
419
|
+
console.print("\n[bold blue]Agents[/bold blue]")
|
|
420
|
+
for name, agent in grid.agents.items():
|
|
421
|
+
model = agent.modelPolicy.preferred if agent.modelPolicy else "default"
|
|
422
|
+
console.print(f" [bold]{name}:[/bold] {model}")
|
|
423
|
+
|
|
424
|
+
except Exception as e:
|
|
425
|
+
console.print(f"[bold red]Error:[/bold red] {e}")
|
|
426
|
+
raise typer.Exit(1)
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
if __name__ == "__main__":
|
|
430
|
+
app()
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Smartify execution engine.
|
|
2
|
+
|
|
3
|
+
Provides grid orchestration and scheduling:
|
|
4
|
+
- DAGScheduler: Determines execution order based on dependencies
|
|
5
|
+
- Orchestrator: Core execution engine managing grid lifecycle
|
|
6
|
+
- SparkManager: Dynamic agent spawning for parallel workloads
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from smartify.engine.scheduler import DAGScheduler, NodeState, NodeExecution
|
|
10
|
+
from smartify.engine.orchestrator import (
|
|
11
|
+
Orchestrator,
|
|
12
|
+
ExecutionContext,
|
|
13
|
+
GridRun,
|
|
14
|
+
NodeResult,
|
|
15
|
+
ExecutionError,
|
|
16
|
+
GridLifecycleError,
|
|
17
|
+
LLMAdapter,
|
|
18
|
+
ToolAdapter,
|
|
19
|
+
)
|
|
20
|
+
from smartify.engine.spark import (
|
|
21
|
+
SparkManager,
|
|
22
|
+
SparkRequest,
|
|
23
|
+
SparkNode,
|
|
24
|
+
analyze_workload_for_sparks,
|
|
25
|
+
)
|
|
26
|
+
from smartify.engine.approval import (
|
|
27
|
+
ApprovalManager,
|
|
28
|
+
ApprovalRequest,
|
|
29
|
+
ApprovalStatus,
|
|
30
|
+
SlackNotificationChannel,
|
|
31
|
+
WebhookNotificationChannel,
|
|
32
|
+
get_approval_manager,
|
|
33
|
+
configure_approval_manager,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
# Scheduler
|
|
38
|
+
"DAGScheduler",
|
|
39
|
+
"NodeState",
|
|
40
|
+
"NodeExecution",
|
|
41
|
+
# Orchestrator
|
|
42
|
+
"Orchestrator",
|
|
43
|
+
"ExecutionContext",
|
|
44
|
+
"GridRun",
|
|
45
|
+
"NodeResult",
|
|
46
|
+
"ExecutionError",
|
|
47
|
+
"GridLifecycleError",
|
|
48
|
+
# Protocols
|
|
49
|
+
"LLMAdapter",
|
|
50
|
+
"ToolAdapter",
|
|
51
|
+
# Spark (dynamic spawning)
|
|
52
|
+
"SparkManager",
|
|
53
|
+
"SparkRequest",
|
|
54
|
+
"SparkNode",
|
|
55
|
+
"analyze_workload_for_sparks",
|
|
56
|
+
# Approval (human-in-the-loop)
|
|
57
|
+
"ApprovalManager",
|
|
58
|
+
"ApprovalRequest",
|
|
59
|
+
"ApprovalStatus",
|
|
60
|
+
"SlackNotificationChannel",
|
|
61
|
+
"WebhookNotificationChannel",
|
|
62
|
+
"get_approval_manager",
|
|
63
|
+
"configure_approval_manager",
|
|
64
|
+
]
|