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
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
"""Breaker system for Smartify grid execution.
|
|
2
|
+
|
|
3
|
+
Breakers are safety limits that can trip during execution:
|
|
4
|
+
- tokens: Total token usage limits
|
|
5
|
+
- time: Execution time limits
|
|
6
|
+
- cost: Cost limits in USD
|
|
7
|
+
- requests: Rate limiting (requests per minute, concurrent agents)
|
|
8
|
+
|
|
9
|
+
When a breaker trips, a configured action is taken:
|
|
10
|
+
- notify: Log warning, continue execution
|
|
11
|
+
- pause: Pause grid, await resume
|
|
12
|
+
- cooldown: Wait before continuing
|
|
13
|
+
- require_approval: Request human approval
|
|
14
|
+
- stop: Stop grid execution
|
|
15
|
+
- downgrade: Switch to cheaper model
|
|
16
|
+
- block: Block and fail immediately
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import asyncio
|
|
20
|
+
import logging
|
|
21
|
+
import time
|
|
22
|
+
from dataclasses import dataclass, field
|
|
23
|
+
from datetime import datetime
|
|
24
|
+
from enum import Enum
|
|
25
|
+
from typing import Any, Callable, Dict, List, Optional, Awaitable
|
|
26
|
+
|
|
27
|
+
from smartify.models.grid import (
|
|
28
|
+
BreakerSpec,
|
|
29
|
+
BreakerActions,
|
|
30
|
+
TripAction,
|
|
31
|
+
BreakerStatus,
|
|
32
|
+
TokenLimits,
|
|
33
|
+
TimeLimits,
|
|
34
|
+
CostLimits,
|
|
35
|
+
RequestLimits,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
logger = logging.getLogger(__name__)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class BreakerType(str, Enum):
|
|
43
|
+
"""Types of breakers."""
|
|
44
|
+
TOKENS = "tokens"
|
|
45
|
+
TIME = "time"
|
|
46
|
+
COST = "cost"
|
|
47
|
+
REQUESTS = "requests"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class BreakerTrip:
|
|
52
|
+
"""Record of a breaker trip event."""
|
|
53
|
+
breaker_type: BreakerType
|
|
54
|
+
action: TripAction
|
|
55
|
+
reason: str
|
|
56
|
+
current_value: float
|
|
57
|
+
limit_value: float
|
|
58
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
59
|
+
resolved: bool = False
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class RateLimitState:
|
|
64
|
+
"""Tracks request rate limiting state."""
|
|
65
|
+
window_start: float = field(default_factory=time.time)
|
|
66
|
+
request_count: int = 0
|
|
67
|
+
concurrent_count: int = 0
|
|
68
|
+
|
|
69
|
+
def reset_window(self) -> None:
|
|
70
|
+
"""Reset the rate limit window."""
|
|
71
|
+
self.window_start = time.time()
|
|
72
|
+
self.request_count = 0
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass
|
|
76
|
+
class BreakerState:
|
|
77
|
+
"""Current state of all breakers."""
|
|
78
|
+
# Cumulative tracking
|
|
79
|
+
total_tokens: int = 0
|
|
80
|
+
total_cost: float = 0.0
|
|
81
|
+
start_time: Optional[float] = None
|
|
82
|
+
|
|
83
|
+
# Rate limiting
|
|
84
|
+
rate_limit: RateLimitState = field(default_factory=RateLimitState)
|
|
85
|
+
|
|
86
|
+
# Trip history
|
|
87
|
+
trips: List[BreakerTrip] = field(default_factory=list)
|
|
88
|
+
|
|
89
|
+
# Current status
|
|
90
|
+
status: BreakerStatus = BreakerStatus.OK
|
|
91
|
+
paused: bool = False
|
|
92
|
+
cooldown_until: Optional[float] = None
|
|
93
|
+
awaiting_approval: bool = False
|
|
94
|
+
downgraded: bool = False
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def elapsed_seconds(self) -> float:
|
|
98
|
+
"""Get elapsed execution time in seconds."""
|
|
99
|
+
if self.start_time is None:
|
|
100
|
+
return 0.0
|
|
101
|
+
return time.time() - self.start_time
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class BreakerManager:
|
|
105
|
+
"""Manages breaker checking and trip actions during grid execution.
|
|
106
|
+
|
|
107
|
+
Usage:
|
|
108
|
+
manager = BreakerManager(spec, actions)
|
|
109
|
+
manager.start() # Start timing
|
|
110
|
+
|
|
111
|
+
# Before each node execution:
|
|
112
|
+
await manager.check_and_enforce()
|
|
113
|
+
|
|
114
|
+
# After LLM call:
|
|
115
|
+
manager.record_usage(tokens=1500, cost=0.02)
|
|
116
|
+
|
|
117
|
+
# After node completion:
|
|
118
|
+
manager.record_request_complete()
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
def __init__(
|
|
122
|
+
self,
|
|
123
|
+
spec: BreakerSpec,
|
|
124
|
+
actions: BreakerActions,
|
|
125
|
+
on_pause: Optional[Callable[[], Awaitable[None]]] = None,
|
|
126
|
+
on_approval_required: Optional[Callable[[str], Awaitable[bool]]] = None,
|
|
127
|
+
on_downgrade: Optional[Callable[[], Awaitable[str]]] = None,
|
|
128
|
+
):
|
|
129
|
+
"""Initialize the breaker manager.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
spec: Breaker configuration (limits)
|
|
133
|
+
actions: Trip actions configuration
|
|
134
|
+
on_pause: Async callback when pause action triggered
|
|
135
|
+
on_approval_required: Async callback for approval (returns True if approved)
|
|
136
|
+
on_downgrade: Async callback for downgrade (returns new model name)
|
|
137
|
+
"""
|
|
138
|
+
self.spec = spec
|
|
139
|
+
self.actions = actions
|
|
140
|
+
self.state = BreakerState()
|
|
141
|
+
|
|
142
|
+
# Callbacks
|
|
143
|
+
self._on_pause = on_pause
|
|
144
|
+
self._on_approval_required = on_approval_required
|
|
145
|
+
self._on_downgrade = on_downgrade
|
|
146
|
+
|
|
147
|
+
def start(self) -> None:
|
|
148
|
+
"""Start the breaker timers."""
|
|
149
|
+
self.state.start_time = time.time()
|
|
150
|
+
self.state.rate_limit = RateLimitState()
|
|
151
|
+
logger.debug("Breaker manager started")
|
|
152
|
+
|
|
153
|
+
def record_usage(self, tokens: int = 0, cost: float = 0.0) -> None:
|
|
154
|
+
"""Record token and cost usage."""
|
|
155
|
+
self.state.total_tokens += tokens
|
|
156
|
+
self.state.total_cost += cost
|
|
157
|
+
logger.debug(f"Usage recorded: +{tokens} tokens, +${cost:.4f}")
|
|
158
|
+
|
|
159
|
+
def record_request_start(self) -> None:
|
|
160
|
+
"""Record start of a request (for concurrent tracking)."""
|
|
161
|
+
self.state.rate_limit.concurrent_count += 1
|
|
162
|
+
|
|
163
|
+
def record_request_complete(self) -> None:
|
|
164
|
+
"""Record completion of a request."""
|
|
165
|
+
self.state.rate_limit.request_count += 1
|
|
166
|
+
self.state.rate_limit.concurrent_count = max(0, self.state.rate_limit.concurrent_count - 1)
|
|
167
|
+
|
|
168
|
+
async def check_and_enforce(self) -> Optional[BreakerTrip]:
|
|
169
|
+
"""Check all breakers and enforce trip actions.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
BreakerTrip if a breaker tripped, None otherwise.
|
|
173
|
+
|
|
174
|
+
Raises:
|
|
175
|
+
BreakerError: If block action triggered
|
|
176
|
+
"""
|
|
177
|
+
# Check cooldown first
|
|
178
|
+
if self.state.cooldown_until:
|
|
179
|
+
remaining = self.state.cooldown_until - time.time()
|
|
180
|
+
if remaining > 0:
|
|
181
|
+
logger.info(f"In cooldown, waiting {remaining:.1f}s")
|
|
182
|
+
await asyncio.sleep(remaining)
|
|
183
|
+
self.state.cooldown_until = None
|
|
184
|
+
|
|
185
|
+
# Check if paused
|
|
186
|
+
if self.state.paused:
|
|
187
|
+
logger.info("Execution paused, awaiting resume")
|
|
188
|
+
while self.state.paused:
|
|
189
|
+
await asyncio.sleep(0.5)
|
|
190
|
+
|
|
191
|
+
# Check each breaker type
|
|
192
|
+
trip = None
|
|
193
|
+
|
|
194
|
+
trip = trip or await self._check_tokens()
|
|
195
|
+
trip = trip or await self._check_time()
|
|
196
|
+
trip = trip or await self._check_cost()
|
|
197
|
+
trip = trip or await self._check_requests()
|
|
198
|
+
|
|
199
|
+
return trip
|
|
200
|
+
|
|
201
|
+
async def _check_tokens(self) -> Optional[BreakerTrip]:
|
|
202
|
+
"""Check token limits."""
|
|
203
|
+
if not self.spec.tokens:
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
limits = self.spec.tokens
|
|
207
|
+
limit = limits.maxTotalTokensPerRun
|
|
208
|
+
|
|
209
|
+
# Check warning threshold (80% of limit)
|
|
210
|
+
warn_at = int(limit * 0.8)
|
|
211
|
+
if self.state.total_tokens >= warn_at:
|
|
212
|
+
if self.state.status == BreakerStatus.OK:
|
|
213
|
+
self.state.status = BreakerStatus.WARNING
|
|
214
|
+
logger.warning(f"Token warning: {self.state.total_tokens}/{limit}")
|
|
215
|
+
|
|
216
|
+
# Check hard limit
|
|
217
|
+
if self.state.total_tokens >= limit:
|
|
218
|
+
return await self._trip(
|
|
219
|
+
BreakerType.TOKENS,
|
|
220
|
+
self.actions.onTokensLimit,
|
|
221
|
+
f"Token limit exceeded: {self.state.total_tokens}/{limit}",
|
|
222
|
+
self.state.total_tokens,
|
|
223
|
+
limit,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
return None
|
|
227
|
+
|
|
228
|
+
async def _check_time(self) -> Optional[BreakerTrip]:
|
|
229
|
+
"""Check time limits."""
|
|
230
|
+
if not self.spec.time:
|
|
231
|
+
return None
|
|
232
|
+
|
|
233
|
+
limits = self.spec.time
|
|
234
|
+
elapsed = self.state.elapsed_seconds
|
|
235
|
+
max_seconds = limits.maxRuntimeSeconds
|
|
236
|
+
|
|
237
|
+
# Check warning threshold (80% of limit)
|
|
238
|
+
warn_at = max_seconds * 0.8
|
|
239
|
+
if elapsed >= warn_at:
|
|
240
|
+
if self.state.status == BreakerStatus.OK:
|
|
241
|
+
self.state.status = BreakerStatus.WARNING
|
|
242
|
+
logger.warning(f"Time warning: {elapsed:.1f}s/{max_seconds}s")
|
|
243
|
+
|
|
244
|
+
# Check hard limit
|
|
245
|
+
if elapsed >= max_seconds:
|
|
246
|
+
return await self._trip(
|
|
247
|
+
BreakerType.TIME,
|
|
248
|
+
self.actions.onTimeLimit,
|
|
249
|
+
f"Time limit exceeded: {elapsed:.1f}s/{max_seconds}s",
|
|
250
|
+
elapsed,
|
|
251
|
+
max_seconds,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
return None
|
|
255
|
+
|
|
256
|
+
async def _check_cost(self) -> Optional[BreakerTrip]:
|
|
257
|
+
"""Check cost limits."""
|
|
258
|
+
if not self.spec.cost:
|
|
259
|
+
return None
|
|
260
|
+
|
|
261
|
+
limits = self.spec.cost
|
|
262
|
+
limit = limits.maxCostPerRun
|
|
263
|
+
|
|
264
|
+
# Check warning threshold (80% of limit)
|
|
265
|
+
warn_at = limit * 0.8
|
|
266
|
+
if self.state.total_cost >= warn_at:
|
|
267
|
+
if self.state.status == BreakerStatus.OK:
|
|
268
|
+
self.state.status = BreakerStatus.WARNING
|
|
269
|
+
logger.warning(f"Cost warning: ${self.state.total_cost:.4f}/${limit}")
|
|
270
|
+
|
|
271
|
+
# Check hard limit
|
|
272
|
+
if self.state.total_cost >= limit:
|
|
273
|
+
return await self._trip(
|
|
274
|
+
BreakerType.COST,
|
|
275
|
+
self.actions.onCostLimit,
|
|
276
|
+
f"Cost limit exceeded: ${self.state.total_cost:.4f}/${limit}",
|
|
277
|
+
self.state.total_cost,
|
|
278
|
+
limit,
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
return None
|
|
282
|
+
|
|
283
|
+
async def _check_requests(self) -> Optional[BreakerTrip]:
|
|
284
|
+
"""Check request rate limits."""
|
|
285
|
+
if not self.spec.requests:
|
|
286
|
+
return None
|
|
287
|
+
|
|
288
|
+
limits = self.spec.requests
|
|
289
|
+
rl = self.state.rate_limit
|
|
290
|
+
|
|
291
|
+
# Reset window if needed (1 minute windows)
|
|
292
|
+
if time.time() - rl.window_start >= 60:
|
|
293
|
+
rl.reset_window()
|
|
294
|
+
|
|
295
|
+
# Check requests per minute
|
|
296
|
+
if limits.maxRequestsPerMinute and rl.request_count >= limits.maxRequestsPerMinute:
|
|
297
|
+
return await self._trip(
|
|
298
|
+
BreakerType.REQUESTS,
|
|
299
|
+
self.actions.onRequestsLimit,
|
|
300
|
+
f"Rate limit exceeded: {rl.request_count}/{limits.maxRequestsPerMinute} req/min",
|
|
301
|
+
rl.request_count,
|
|
302
|
+
limits.maxRequestsPerMinute,
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
# Check concurrent agents
|
|
306
|
+
if limits.maxConcurrentAgents and rl.concurrent_count >= limits.maxConcurrentAgents:
|
|
307
|
+
return await self._trip(
|
|
308
|
+
BreakerType.REQUESTS,
|
|
309
|
+
self.actions.onRequestsLimit,
|
|
310
|
+
f"Concurrent limit exceeded: {rl.concurrent_count}/{limits.maxConcurrentAgents}",
|
|
311
|
+
rl.concurrent_count,
|
|
312
|
+
limits.maxConcurrentAgents,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
return None
|
|
316
|
+
|
|
317
|
+
async def _trip(
|
|
318
|
+
self,
|
|
319
|
+
breaker_type: BreakerType,
|
|
320
|
+
action: TripAction,
|
|
321
|
+
reason: str,
|
|
322
|
+
current: float,
|
|
323
|
+
limit: float,
|
|
324
|
+
) -> BreakerTrip:
|
|
325
|
+
"""Handle a breaker trip."""
|
|
326
|
+
trip = BreakerTrip(
|
|
327
|
+
breaker_type=breaker_type,
|
|
328
|
+
action=action,
|
|
329
|
+
reason=reason,
|
|
330
|
+
current_value=current,
|
|
331
|
+
limit_value=limit,
|
|
332
|
+
)
|
|
333
|
+
self.state.trips.append(trip)
|
|
334
|
+
self.state.status = BreakerStatus.TRIPPED
|
|
335
|
+
|
|
336
|
+
logger.warning(f"Breaker tripped: {reason} → action: {action.value}")
|
|
337
|
+
|
|
338
|
+
# Execute the trip action
|
|
339
|
+
await self._execute_action(action, reason)
|
|
340
|
+
|
|
341
|
+
return trip
|
|
342
|
+
|
|
343
|
+
async def _execute_action(self, action: TripAction, reason: str) -> None:
|
|
344
|
+
"""Execute a trip action."""
|
|
345
|
+
if action == TripAction.NOTIFY:
|
|
346
|
+
# Just log, already done
|
|
347
|
+
pass
|
|
348
|
+
|
|
349
|
+
elif action == TripAction.PAUSE:
|
|
350
|
+
self.state.paused = True
|
|
351
|
+
if self._on_pause:
|
|
352
|
+
await self._on_pause()
|
|
353
|
+
|
|
354
|
+
elif action == TripAction.COOLDOWN:
|
|
355
|
+
cooldown_secs = self.actions.cooldownSeconds
|
|
356
|
+
self.state.cooldown_until = time.time() + cooldown_secs
|
|
357
|
+
logger.info(f"Entering cooldown for {cooldown_secs}s")
|
|
358
|
+
await asyncio.sleep(cooldown_secs)
|
|
359
|
+
self.state.cooldown_until = None
|
|
360
|
+
|
|
361
|
+
elif action == TripAction.REQUIRE_APPROVAL:
|
|
362
|
+
self.state.awaiting_approval = True
|
|
363
|
+
if self._on_approval_required:
|
|
364
|
+
approved = await self._on_approval_required(reason)
|
|
365
|
+
if not approved:
|
|
366
|
+
raise BreakerError(f"Approval denied: {reason}")
|
|
367
|
+
self.state.awaiting_approval = False
|
|
368
|
+
|
|
369
|
+
elif action == TripAction.STOP:
|
|
370
|
+
raise BreakerError(f"Breaker stop: {reason}")
|
|
371
|
+
|
|
372
|
+
elif action == TripAction.DOWNGRADE:
|
|
373
|
+
self.state.downgraded = True
|
|
374
|
+
if self._on_downgrade:
|
|
375
|
+
new_model = await self._on_downgrade()
|
|
376
|
+
logger.info(f"Downgraded to model: {new_model}")
|
|
377
|
+
|
|
378
|
+
elif action == TripAction.BLOCK:
|
|
379
|
+
raise BreakerError(f"Breaker blocked: {reason}")
|
|
380
|
+
|
|
381
|
+
def resume(self) -> None:
|
|
382
|
+
"""Resume paused execution."""
|
|
383
|
+
self.state.paused = False
|
|
384
|
+
logger.info("Execution resumed")
|
|
385
|
+
|
|
386
|
+
def get_summary(self) -> Dict[str, Any]:
|
|
387
|
+
"""Get a summary of breaker state."""
|
|
388
|
+
return {
|
|
389
|
+
"status": self.state.status.value,
|
|
390
|
+
"total_tokens": self.state.total_tokens,
|
|
391
|
+
"total_cost": self.state.total_cost,
|
|
392
|
+
"elapsed_seconds": self.state.elapsed_seconds,
|
|
393
|
+
"trips": [
|
|
394
|
+
{
|
|
395
|
+
"type": t.breaker_type.value,
|
|
396
|
+
"action": t.action.value,
|
|
397
|
+
"reason": t.reason,
|
|
398
|
+
"timestamp": t.timestamp.isoformat(),
|
|
399
|
+
}
|
|
400
|
+
for t in self.state.trips
|
|
401
|
+
],
|
|
402
|
+
"paused": self.state.paused,
|
|
403
|
+
"downgraded": self.state.downgraded,
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
class BreakerError(Exception):
|
|
408
|
+
"""Raised when a breaker trips with stop/block action."""
|
|
409
|
+
pass
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Pydantic models for Smartify Grid specification."""
|
|
2
|
+
|
|
3
|
+
from smartify.models.grid import (
|
|
4
|
+
# Enums
|
|
5
|
+
NodeKind,
|
|
6
|
+
TriggerType,
|
|
7
|
+
ExecutionMode,
|
|
8
|
+
AutonomyMode,
|
|
9
|
+
TripAction,
|
|
10
|
+
BreakerStatus,
|
|
11
|
+
GridState,
|
|
12
|
+
WorkspaceType,
|
|
13
|
+
ArtifactType,
|
|
14
|
+
AggregateStrategy,
|
|
15
|
+
# Specs
|
|
16
|
+
GridSpec,
|
|
17
|
+
MetadataSpec,
|
|
18
|
+
TopologySpec,
|
|
19
|
+
NodeSpec,
|
|
20
|
+
EdgeSpec,
|
|
21
|
+
TriggerSpec,
|
|
22
|
+
InputSpec,
|
|
23
|
+
EnvironmentSpec,
|
|
24
|
+
OutputsSpec,
|
|
25
|
+
AgentSpec,
|
|
26
|
+
ToolsSpec,
|
|
27
|
+
GuardrailsSpec,
|
|
28
|
+
WorkspaceSpec,
|
|
29
|
+
BreakerSpec,
|
|
30
|
+
BreakerActions,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
# Enums
|
|
35
|
+
"NodeKind",
|
|
36
|
+
"TriggerType",
|
|
37
|
+
"ExecutionMode",
|
|
38
|
+
"AutonomyMode",
|
|
39
|
+
"TripAction",
|
|
40
|
+
"BreakerStatus",
|
|
41
|
+
"GridState",
|
|
42
|
+
"WorkspaceType",
|
|
43
|
+
"ArtifactType",
|
|
44
|
+
"AggregateStrategy",
|
|
45
|
+
# Specs
|
|
46
|
+
"GridSpec",
|
|
47
|
+
"MetadataSpec",
|
|
48
|
+
"TopologySpec",
|
|
49
|
+
"NodeSpec",
|
|
50
|
+
"EdgeSpec",
|
|
51
|
+
"TriggerSpec",
|
|
52
|
+
"InputSpec",
|
|
53
|
+
"EnvironmentSpec",
|
|
54
|
+
"OutputsSpec",
|
|
55
|
+
"AgentSpec",
|
|
56
|
+
"ToolsSpec",
|
|
57
|
+
"GuardrailsSpec",
|
|
58
|
+
"WorkspaceSpec",
|
|
59
|
+
"BreakerSpec",
|
|
60
|
+
"BreakerActions",
|
|
61
|
+
]
|