agenta 0.70.1__py3-none-any.whl → 0.75.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.
- agenta/__init__.py +9 -3
- agenta/sdk/__init__.py +2 -4
- agenta/sdk/agenta_init.py +22 -75
- agenta/sdk/assets.py +57 -0
- agenta/sdk/context/serving.py +2 -0
- agenta/sdk/contexts/routing.py +2 -0
- agenta/sdk/contexts/running.py +3 -2
- agenta/sdk/decorators/running.py +8 -4
- agenta/sdk/decorators/serving.py +65 -26
- agenta/sdk/decorators/tracing.py +51 -30
- agenta/sdk/engines/tracing/inline.py +8 -1
- agenta/sdk/engines/tracing/processors.py +23 -12
- agenta/sdk/evaluations/preview/evaluate.py +36 -8
- agenta/sdk/evaluations/runs.py +2 -1
- agenta/sdk/litellm/mockllm.py +2 -2
- agenta/sdk/managers/config.py +3 -1
- agenta/sdk/managers/secrets.py +25 -8
- agenta/sdk/managers/testsets.py +143 -227
- agenta/sdk/middleware/config.py +3 -1
- agenta/sdk/middleware/otel.py +3 -1
- agenta/sdk/middleware/vault.py +33 -18
- agenta/sdk/middlewares/routing/otel.py +1 -1
- agenta/sdk/middlewares/running/vault.py +33 -17
- agenta/sdk/router.py +30 -5
- agenta/sdk/tracing/inline.py +8 -1
- agenta/sdk/tracing/processors.py +8 -3
- agenta/sdk/tracing/propagation.py +9 -12
- agenta/sdk/types.py +19 -21
- agenta/sdk/utils/client.py +10 -9
- agenta/sdk/utils/lazy.py +253 -0
- agenta/sdk/workflows/builtin.py +2 -0
- agenta/sdk/workflows/configurations.py +1 -0
- agenta/sdk/workflows/handlers.py +236 -81
- agenta/sdk/workflows/interfaces.py +47 -0
- agenta/sdk/workflows/runners/base.py +6 -2
- agenta/sdk/workflows/runners/daytona.py +250 -131
- agenta/sdk/workflows/runners/local.py +22 -56
- agenta/sdk/workflows/runners/registry.py +1 -1
- agenta/sdk/workflows/sandbox.py +17 -5
- agenta/sdk/workflows/templates.py +81 -0
- agenta/sdk/workflows/utils.py +6 -0
- {agenta-0.70.1.dist-info → agenta-0.75.0.dist-info}/METADATA +4 -8
- {agenta-0.70.1.dist-info → agenta-0.75.0.dist-info}/RECORD +44 -44
- agenta/config.py +0 -25
- agenta/config.toml +0 -4
- {agenta-0.70.1.dist-info → agenta-0.75.0.dist-info}/WHEEL +0 -0
agenta/__init__.py
CHANGED
|
@@ -8,7 +8,7 @@ from .sdk import assets as assets
|
|
|
8
8
|
# evaluations
|
|
9
9
|
from .sdk import testsets as testsets
|
|
10
10
|
from .sdk import tracer
|
|
11
|
-
from .sdk.agenta_init import AgentaSingleton
|
|
11
|
+
from .sdk.agenta_init import AgentaSingleton
|
|
12
12
|
from .sdk.agenta_init import init as _init
|
|
13
13
|
from .sdk.context.running import workflow_mode_enabled
|
|
14
14
|
from .sdk.decorators.running import (
|
|
@@ -18,7 +18,6 @@ from .sdk.decorators.running import (
|
|
|
18
18
|
)
|
|
19
19
|
from .sdk.decorators.serving import app, route
|
|
20
20
|
from .sdk.decorators.tracing import instrument
|
|
21
|
-
from .sdk.litellm import litellm as callbacks
|
|
22
21
|
from .sdk.managers.apps import AppManager
|
|
23
22
|
from .sdk.managers.config import ConfigManager
|
|
24
23
|
from .sdk.managers.deployment import DeploymentManager
|
|
@@ -46,7 +45,6 @@ from .sdk.utils.costs import calculate_token_usage
|
|
|
46
45
|
from .sdk.utils.logging import get_module_logger
|
|
47
46
|
from .sdk.utils.preinit import PreInitObject
|
|
48
47
|
|
|
49
|
-
config = PreInitObject("agenta.config", Config)
|
|
50
48
|
DEFAULT_AGENTA_SINGLETON_INSTANCE = AgentaSingleton()
|
|
51
49
|
|
|
52
50
|
types = client_types
|
|
@@ -58,6 +56,14 @@ tracing = DEFAULT_AGENTA_SINGLETON_INSTANCE.tracing # type: ignore
|
|
|
58
56
|
tracer = get_tracer(tracing)
|
|
59
57
|
|
|
60
58
|
|
|
59
|
+
def __getattr__(name: str):
|
|
60
|
+
if name == "callbacks":
|
|
61
|
+
from .sdk.litellm import litellm as callbacks
|
|
62
|
+
|
|
63
|
+
return callbacks
|
|
64
|
+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
|
65
|
+
|
|
66
|
+
|
|
61
67
|
def init(
|
|
62
68
|
host: Optional[str] = None,
|
|
63
69
|
api_url: Optional[str] = None,
|
agenta/sdk/__init__.py
CHANGED
|
@@ -73,7 +73,7 @@ from agenta.sdk.decorators.running import (
|
|
|
73
73
|
)
|
|
74
74
|
from agenta.sdk.decorators.serving import route, app
|
|
75
75
|
from .tracing.conventions import Reference
|
|
76
|
-
from .agenta_init import
|
|
76
|
+
from .agenta_init import AgentaSingleton, init as _init
|
|
77
77
|
from .utils.costs import calculate_token_usage
|
|
78
78
|
from .managers.apps import AppManager
|
|
79
79
|
from .managers.vault import VaultManager
|
|
@@ -83,9 +83,7 @@ from .managers.variant import VariantManager
|
|
|
83
83
|
from .managers.deployment import DeploymentManager
|
|
84
84
|
from .managers import testsets as testsets
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
config = PreInitObject("agenta.config", Config)
|
|
88
|
-
DEFAULT_AGENTA_SINGLETON_INSTANCE = AgentaSingleton()
|
|
86
|
+
DEFAULT_AGENTA_SINGLETON_INSTANCE: AgentaSingleton = AgentaSingleton()
|
|
89
87
|
|
|
90
88
|
types = client_types
|
|
91
89
|
|
agenta/sdk/agenta_init.py
CHANGED
|
@@ -2,8 +2,7 @@ from importlib.metadata import version
|
|
|
2
2
|
from os import getenv
|
|
3
3
|
from typing import Any, Callable, Optional
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import toml
|
|
5
|
+
import httpx
|
|
7
6
|
from agenta.client.client import AgentaApi, AsyncAgentaApi
|
|
8
7
|
from agenta.sdk.contexts.routing import RoutingContext
|
|
9
8
|
from agenta.sdk.tracing import Tracing
|
|
@@ -86,22 +85,13 @@ class AgentaSingleton:
|
|
|
86
85
|
|
|
87
86
|
log.info("Agenta - SDK ver: %s", version("agenta"))
|
|
88
87
|
|
|
89
|
-
|
|
90
|
-
if config_fname:
|
|
91
|
-
config = toml.load(config_fname)
|
|
92
|
-
|
|
93
|
-
_host = (
|
|
94
|
-
host
|
|
95
|
-
or getenv("AGENTA_HOST")
|
|
96
|
-
or config.get("host")
|
|
97
|
-
or "https://cloud.agenta.ai"
|
|
98
|
-
)
|
|
88
|
+
_host = host or getenv("AGENTA_HOST") or "https://cloud.agenta.ai"
|
|
99
89
|
|
|
100
90
|
_api_url = (
|
|
101
91
|
api_url
|
|
92
|
+
#
|
|
102
93
|
or getenv("AGENTA_API_INTERNAL_URL")
|
|
103
94
|
or getenv("AGENTA_API_URL")
|
|
104
|
-
or config.get("api_url")
|
|
105
95
|
or None # NO FALLBACK
|
|
106
96
|
)
|
|
107
97
|
|
|
@@ -127,29 +117,29 @@ class AgentaSingleton:
|
|
|
127
117
|
|
|
128
118
|
self.api_key = (
|
|
129
119
|
api_key
|
|
120
|
+
#
|
|
130
121
|
or getenv("AGENTA_API_KEY")
|
|
131
|
-
or config.get("api_key")
|
|
132
122
|
or None # NO FALLBACK
|
|
133
123
|
)
|
|
134
124
|
|
|
135
125
|
if self.api_key is None:
|
|
136
|
-
log.
|
|
137
|
-
"API key is required. Please set AGENTA_API_KEY environment variable or pass api_key parameter in ag.init()."
|
|
126
|
+
log.warning(
|
|
127
|
+
"API key is required (in most cases). Please set AGENTA_API_KEY environment variable or pass api_key parameter in ag.init()."
|
|
138
128
|
)
|
|
139
129
|
|
|
140
130
|
log.info("Agenta - API URL: %s", self.api_url)
|
|
141
131
|
|
|
142
132
|
self.scope_type = (
|
|
143
133
|
scope_type
|
|
134
|
+
#
|
|
144
135
|
or getenv("AGENTA_SCOPE_TYPE")
|
|
145
|
-
or config.get("scope_type")
|
|
146
136
|
or None # NO FALLBACK
|
|
147
137
|
)
|
|
148
138
|
|
|
149
139
|
self.scope_id = (
|
|
150
140
|
scope_id
|
|
141
|
+
#
|
|
151
142
|
or getenv("AGENTA_SCOPE_ID")
|
|
152
|
-
or config.get("scope_id")
|
|
153
143
|
or None # NO FALLBACK
|
|
154
144
|
)
|
|
155
145
|
|
|
@@ -173,11 +163,6 @@ class AgentaSingleton:
|
|
|
173
163
|
api_key=self.api_key if self.api_key else "",
|
|
174
164
|
)
|
|
175
165
|
|
|
176
|
-
self.config = Config(
|
|
177
|
-
host=self.host,
|
|
178
|
-
api_key=self.api_key,
|
|
179
|
-
)
|
|
180
|
-
|
|
181
166
|
# Reset cached scope info on re-init
|
|
182
167
|
self.organization_id = None
|
|
183
168
|
self.workspace_id = None
|
|
@@ -190,21 +175,21 @@ class AgentaSingleton:
|
|
|
190
175
|
and self.workspace_id is not None
|
|
191
176
|
and self.project_id is not None
|
|
192
177
|
):
|
|
193
|
-
return
|
|
178
|
+
return None
|
|
194
179
|
|
|
195
180
|
if self.api_url is None or self.api_key is None:
|
|
196
181
|
log.error("API URL or API key is not set. Please call ag.init() first.")
|
|
197
|
-
return
|
|
182
|
+
return None
|
|
198
183
|
|
|
199
184
|
try:
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
185
|
+
with httpx.Client() as client:
|
|
186
|
+
response = client.get(
|
|
187
|
+
f"{self.api_url}/projects/current",
|
|
188
|
+
headers={"Authorization": f"ApiKey {self.api_key}"},
|
|
189
|
+
timeout=10,
|
|
190
|
+
)
|
|
191
|
+
response.raise_for_status()
|
|
192
|
+
project_info = response.json()
|
|
208
193
|
|
|
209
194
|
if not project_info:
|
|
210
195
|
log.error(
|
|
@@ -226,7 +211,7 @@ class AgentaSingleton:
|
|
|
226
211
|
|
|
227
212
|
except Exception as e:
|
|
228
213
|
log.error(f"Failed to fetch scope information: {e}")
|
|
229
|
-
return
|
|
214
|
+
return None
|
|
230
215
|
|
|
231
216
|
if self.organization_id and self.workspace_id and self.project_id:
|
|
232
217
|
return (
|
|
@@ -238,50 +223,16 @@ class AgentaSingleton:
|
|
|
238
223
|
return None
|
|
239
224
|
|
|
240
225
|
|
|
241
|
-
class Config:
|
|
242
|
-
def __init__(
|
|
243
|
-
self,
|
|
244
|
-
**kwargs,
|
|
245
|
-
):
|
|
246
|
-
self.default_parameters = {**kwargs}
|
|
247
|
-
|
|
248
|
-
def set_default(self, **kwargs):
|
|
249
|
-
self.default_parameters.update(kwargs)
|
|
250
|
-
|
|
251
|
-
def get_default(self):
|
|
252
|
-
return self.default_parameters
|
|
253
|
-
|
|
254
|
-
def __getattr__(self, key):
|
|
255
|
-
context = RoutingContext.get()
|
|
256
|
-
|
|
257
|
-
parameters = context.parameters
|
|
258
|
-
|
|
259
|
-
if not parameters:
|
|
260
|
-
return None
|
|
261
|
-
|
|
262
|
-
if key in parameters:
|
|
263
|
-
value = parameters[key]
|
|
264
|
-
|
|
265
|
-
if isinstance(value, dict):
|
|
266
|
-
nested_config = Config()
|
|
267
|
-
nested_config.set_default(**value)
|
|
268
|
-
|
|
269
|
-
return nested_config
|
|
270
|
-
|
|
271
|
-
return value
|
|
272
|
-
|
|
273
|
-
return None
|
|
274
|
-
|
|
275
|
-
|
|
276
226
|
def init(
|
|
277
227
|
host: Optional[str] = None,
|
|
278
228
|
api_url: Optional[str] = None,
|
|
279
229
|
api_key: Optional[str] = None,
|
|
280
|
-
config_fname: Optional[str] = None,
|
|
281
230
|
redact: Optional[Callable[..., Any]] = None,
|
|
282
231
|
redact_on_error: Optional[bool] = True,
|
|
283
232
|
scope_type: Optional[str] = None,
|
|
284
233
|
scope_id: Optional[str] = None,
|
|
234
|
+
# DEPRECATED
|
|
235
|
+
config_fname: Optional[str] = None,
|
|
285
236
|
):
|
|
286
237
|
"""Main function to initialize the agenta sdk.
|
|
287
238
|
|
|
@@ -306,14 +257,10 @@ def init(
|
|
|
306
257
|
host=host,
|
|
307
258
|
api_url=api_url,
|
|
308
259
|
api_key=api_key,
|
|
309
|
-
config_fname=config_fname,
|
|
310
260
|
redact=redact,
|
|
311
261
|
redact_on_error=redact_on_error,
|
|
312
262
|
scope_type=scope_type,
|
|
313
263
|
scope_id=scope_id,
|
|
314
264
|
)
|
|
315
265
|
|
|
316
|
-
set_global(
|
|
317
|
-
config=singleton.config,
|
|
318
|
-
tracing=singleton.tracing,
|
|
319
|
-
)
|
|
266
|
+
set_global(tracing=singleton.tracing)
|
agenta/sdk/assets.py
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
from typing import Dict, Optional, Tuple
|
|
2
|
+
|
|
3
|
+
from litellm import cost_calculator
|
|
4
|
+
|
|
5
|
+
|
|
1
6
|
supported_llm_models = {
|
|
2
7
|
"anthropic": [
|
|
3
8
|
"anthropic/claude-sonnet-4-5",
|
|
@@ -206,6 +211,58 @@ supported_llm_models = {
|
|
|
206
211
|
|
|
207
212
|
providers_list = list(supported_llm_models.keys())
|
|
208
213
|
|
|
214
|
+
|
|
215
|
+
def _get_model_costs(model: str) -> Optional[Tuple[float, float]]:
|
|
216
|
+
"""
|
|
217
|
+
Get the input and output costs per 1M tokens for a model.
|
|
218
|
+
|
|
219
|
+
Uses litellm's cost_calculator (same as tracing/inline.py) for consistency.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
model: The model name (e.g., "gpt-4o" or "anthropic/claude-3-opus-20240229")
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
Tuple of (input_cost, output_cost) per 1M tokens, or None if not found.
|
|
226
|
+
"""
|
|
227
|
+
try:
|
|
228
|
+
costs = cost_calculator.cost_per_token(
|
|
229
|
+
model=model,
|
|
230
|
+
prompt_tokens=1_000_000,
|
|
231
|
+
completion_tokens=1_000_000,
|
|
232
|
+
)
|
|
233
|
+
if costs:
|
|
234
|
+
input_cost, output_cost = costs
|
|
235
|
+
if input_cost > 0 or output_cost > 0:
|
|
236
|
+
return (input_cost, output_cost)
|
|
237
|
+
except Exception:
|
|
238
|
+
pass
|
|
239
|
+
return None
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _build_model_metadata() -> Dict[str, Dict[str, Dict[str, float]]]:
|
|
243
|
+
"""
|
|
244
|
+
Build metadata dictionary with costs for all supported models.
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
Nested dict: {provider: {model: {"input": cost, "output": cost}}}
|
|
248
|
+
"""
|
|
249
|
+
metadata: Dict[str, Dict[str, Dict[str, float]]] = {}
|
|
250
|
+
|
|
251
|
+
for provider, models in supported_llm_models.items():
|
|
252
|
+
metadata[provider] = {}
|
|
253
|
+
for model in models:
|
|
254
|
+
costs = _get_model_costs(model)
|
|
255
|
+
if costs:
|
|
256
|
+
metadata[provider][model] = {
|
|
257
|
+
"input": costs[0],
|
|
258
|
+
"output": costs[1],
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return metadata
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
model_metadata = _build_model_metadata()
|
|
265
|
+
|
|
209
266
|
model_to_provider_mapping = {
|
|
210
267
|
model: provider
|
|
211
268
|
for provider, models in supported_llm_models.items()
|
agenta/sdk/context/serving.py
CHANGED
|
@@ -9,6 +9,8 @@ from pydantic import BaseModel
|
|
|
9
9
|
class RoutingContext(BaseModel):
|
|
10
10
|
parameters: Optional[Dict[str, Any]] = None
|
|
11
11
|
secrets: Optional[List[Any]] = None
|
|
12
|
+
local_secrets: Optional[List[Any]] = None
|
|
13
|
+
vault_secrets: Optional[List[Any]] = None
|
|
12
14
|
mock: Optional[str] = None
|
|
13
15
|
|
|
14
16
|
@classmethod
|
agenta/sdk/contexts/routing.py
CHANGED
|
@@ -8,6 +8,8 @@ from pydantic import BaseModel
|
|
|
8
8
|
class RoutingContext(BaseModel):
|
|
9
9
|
parameters: Optional[dict] = None
|
|
10
10
|
secrets: Optional[list] = None
|
|
11
|
+
local_secrets: Optional[list] = None
|
|
12
|
+
vault_secrets: Optional[list] = None
|
|
11
13
|
mock: Optional[str] = None
|
|
12
14
|
|
|
13
15
|
@classmethod
|
agenta/sdk/contexts/running.py
CHANGED
|
@@ -2,7 +2,6 @@ from typing import Optional, Union, Callable
|
|
|
2
2
|
from contextvars import Token, ContextVar
|
|
3
3
|
from contextlib import contextmanager
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
from pydantic import BaseModel
|
|
7
6
|
|
|
8
7
|
from agenta.sdk.models.workflows import (
|
|
@@ -24,8 +23,10 @@ class RunningContext(BaseModel):
|
|
|
24
23
|
parameters: Optional[dict] = None
|
|
25
24
|
schemas: Optional[dict] = None
|
|
26
25
|
|
|
27
|
-
secrets: Optional[list] = None
|
|
28
26
|
credentials: Optional[str] = None
|
|
27
|
+
secrets: Optional[list] = None
|
|
28
|
+
local_secrets: Optional[list] = None
|
|
29
|
+
vault_secrets: Optional[list] = None
|
|
29
30
|
|
|
30
31
|
handler: Optional[Callable] = None
|
|
31
32
|
|
agenta/sdk/decorators/running.py
CHANGED
|
@@ -321,10 +321,14 @@ class workflow:
|
|
|
321
321
|
_tags = {**(self.tags or {}), **(request.tags or {})}
|
|
322
322
|
_meta = {**(self.meta or {}), **(request.meta or {})}
|
|
323
323
|
|
|
324
|
-
credentials =
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
324
|
+
credentials = (
|
|
325
|
+
credentials
|
|
326
|
+
or request.credentials
|
|
327
|
+
or (
|
|
328
|
+
f"ApiKey {ag.DEFAULT_AGENTA_SINGLETON_INSTANCE.api_key}"
|
|
329
|
+
if ag.DEFAULT_AGENTA_SINGLETON_INSTANCE.api_key
|
|
330
|
+
else None
|
|
331
|
+
)
|
|
328
332
|
)
|
|
329
333
|
|
|
330
334
|
with tracing_context_manager(TracingContext.get()):
|
agenta/sdk/decorators/serving.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Type, Any, Callable, Dict, Optional, Tuple, List
|
|
1
|
+
from typing import Type, Any, Callable, Dict, Optional, Tuple, List, TYPE_CHECKING
|
|
2
2
|
from inspect import (
|
|
3
3
|
iscoroutinefunction,
|
|
4
4
|
isgenerator,
|
|
@@ -14,19 +14,14 @@ from uuid import UUID
|
|
|
14
14
|
from pydantic import BaseModel, HttpUrl, ValidationError
|
|
15
15
|
from os import environ
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
StreamingResponse
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
from agenta.sdk.middleware.vault import VaultMiddleware
|
|
26
|
-
from agenta.sdk.middleware.config import ConfigMiddleware
|
|
27
|
-
from agenta.sdk.middleware.otel import OTelMiddleware
|
|
28
|
-
from agenta.sdk.middleware.auth import AuthHTTPMiddleware
|
|
29
|
-
from agenta.sdk.middleware.cors import CORSMiddleware
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from fastapi import Request, HTTPException, Body
|
|
19
|
+
from starlette.responses import Response as StarletteResponse, StreamingResponse
|
|
20
|
+
else:
|
|
21
|
+
# Lazy imports - only loaded when @entrypoint or @route is used
|
|
22
|
+
Request = None
|
|
23
|
+
HTTPException = None
|
|
24
|
+
Body = None
|
|
30
25
|
|
|
31
26
|
from agenta.sdk.contexts.routing import (
|
|
32
27
|
routing_context_manager,
|
|
@@ -40,6 +35,7 @@ from agenta.sdk.router import router
|
|
|
40
35
|
from agenta.sdk.utils.exceptions import suppress, display_exception
|
|
41
36
|
from agenta.sdk.utils.logging import get_module_logger
|
|
42
37
|
from agenta.sdk.utils.helpers import get_current_version
|
|
38
|
+
from agenta.sdk.utils.lazy import _load_fastapi, _load_starlette_responses
|
|
43
39
|
from agenta.sdk.types import (
|
|
44
40
|
MultipleChoice,
|
|
45
41
|
BaseResponse,
|
|
@@ -53,12 +49,33 @@ log = get_module_logger(__name__)
|
|
|
53
49
|
|
|
54
50
|
AGENTA_RUNTIME_PREFIX = environ.get("AGENTA_RUNTIME_PREFIX", "")
|
|
55
51
|
|
|
56
|
-
app = FastAPI(
|
|
57
|
-
docs_url=f"{AGENTA_RUNTIME_PREFIX}/docs", # Swagger UI
|
|
58
|
-
openapi_url=f"{AGENTA_RUNTIME_PREFIX}/openapi.json", # OpenAPI schema
|
|
59
|
-
)
|
|
60
52
|
|
|
61
|
-
|
|
53
|
+
# Lazy FastAPI initialization
|
|
54
|
+
class _LazyApp:
|
|
55
|
+
"""Lazy wrapper for FastAPI app - only imported when accessed."""
|
|
56
|
+
|
|
57
|
+
_app = None
|
|
58
|
+
|
|
59
|
+
def _get_app(self):
|
|
60
|
+
if self._app is None:
|
|
61
|
+
fastapi = _load_fastapi()
|
|
62
|
+
from agenta.sdk.router import get_router
|
|
63
|
+
|
|
64
|
+
self._app = fastapi.FastAPI(
|
|
65
|
+
docs_url=f"{AGENTA_RUNTIME_PREFIX}/docs", # Swagger UI
|
|
66
|
+
openapi_url=f"{AGENTA_RUNTIME_PREFIX}/openapi.json", # OpenAPI schema
|
|
67
|
+
)
|
|
68
|
+
self._app.include_router(get_router(), prefix=AGENTA_RUNTIME_PREFIX)
|
|
69
|
+
return self._app
|
|
70
|
+
|
|
71
|
+
def __getattr__(self, name):
|
|
72
|
+
return getattr(self._get_app(), name)
|
|
73
|
+
|
|
74
|
+
async def __call__(self, scope, receive, send):
|
|
75
|
+
return await self._get_app()(scope, receive, send)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
app = _LazyApp() # type: ignore
|
|
62
79
|
|
|
63
80
|
|
|
64
81
|
class PathValidator(BaseModel):
|
|
@@ -142,16 +159,32 @@ class entrypoint:
|
|
|
142
159
|
route_path: str = "",
|
|
143
160
|
config_schema: Optional[BaseModel] = None,
|
|
144
161
|
):
|
|
162
|
+
# Lazy import fastapi components - only loaded when decorator is used
|
|
163
|
+
fastapi = _load_fastapi()
|
|
164
|
+
|
|
145
165
|
self.func = func
|
|
146
166
|
self.route_path = route_path
|
|
147
167
|
self.config_schema = config_schema
|
|
148
168
|
|
|
169
|
+
# Store for use in methods
|
|
170
|
+
self._Request = fastapi.Request
|
|
171
|
+
self._HTTPException = fastapi.HTTPException
|
|
172
|
+
self._Body = fastapi.Body
|
|
173
|
+
|
|
149
174
|
signature_parameters = signature(func).parameters
|
|
150
175
|
config, default_parameters = self.parse_config()
|
|
151
176
|
|
|
152
177
|
### --- Middleware --- #
|
|
153
178
|
if not entrypoint._middleware:
|
|
154
179
|
entrypoint._middleware = True
|
|
180
|
+
from agenta.sdk.middleware.mock import MockMiddleware
|
|
181
|
+
from agenta.sdk.middleware.inline import InlineMiddleware
|
|
182
|
+
from agenta.sdk.middleware.vault import VaultMiddleware
|
|
183
|
+
from agenta.sdk.middleware.config import ConfigMiddleware
|
|
184
|
+
from agenta.sdk.middleware.otel import OTelMiddleware
|
|
185
|
+
from agenta.sdk.middleware.auth import AuthHTTPMiddleware
|
|
186
|
+
from agenta.sdk.middleware.cors import CORSMiddleware
|
|
187
|
+
|
|
155
188
|
app.add_middleware(MockMiddleware)
|
|
156
189
|
app.add_middleware(InlineMiddleware)
|
|
157
190
|
app.add_middleware(VaultMiddleware)
|
|
@@ -179,7 +212,7 @@ class entrypoint:
|
|
|
179
212
|
request.state.config["parameters"] is None
|
|
180
213
|
or request.state.config["references"] is None
|
|
181
214
|
):
|
|
182
|
-
raise
|
|
215
|
+
raise self._HTTPException(
|
|
183
216
|
status_code=400,
|
|
184
217
|
detail="Config not found based on provided references.",
|
|
185
218
|
)
|
|
@@ -320,12 +353,12 @@ class entrypoint:
|
|
|
320
353
|
|
|
321
354
|
async def execute_wrapper(
|
|
322
355
|
self,
|
|
323
|
-
request: Request,
|
|
356
|
+
request: "Request", # type: ignore
|
|
324
357
|
*args,
|
|
325
358
|
**kwargs,
|
|
326
359
|
):
|
|
327
360
|
if not request:
|
|
328
|
-
raise
|
|
361
|
+
raise self._HTTPException(status_code=500, detail="Missing 'request'.")
|
|
329
362
|
|
|
330
363
|
state = request.state
|
|
331
364
|
traceparent = state.otel.get("traceparent")
|
|
@@ -334,6 +367,8 @@ class entrypoint:
|
|
|
334
367
|
parameters = state.config.get("parameters")
|
|
335
368
|
references = state.config.get("references")
|
|
336
369
|
secrets = state.vault.get("secrets")
|
|
370
|
+
local_secrets = state.vault.get("local_secrets")
|
|
371
|
+
vault_secrets = state.vault.get("vault_secrets")
|
|
337
372
|
inline = state.inline
|
|
338
373
|
mock = state.mock
|
|
339
374
|
|
|
@@ -341,6 +376,8 @@ class entrypoint:
|
|
|
341
376
|
context=RoutingContext(
|
|
342
377
|
parameters=parameters,
|
|
343
378
|
secrets=secrets,
|
|
379
|
+
local_secrets=local_secrets,
|
|
380
|
+
vault_secrets=vault_secrets,
|
|
344
381
|
mock=mock,
|
|
345
382
|
)
|
|
346
383
|
):
|
|
@@ -370,6 +407,8 @@ class entrypoint:
|
|
|
370
407
|
result: Any,
|
|
371
408
|
inline: bool,
|
|
372
409
|
):
|
|
410
|
+
StarletteResponse, StreamingResponse = _load_starlette_responses()
|
|
411
|
+
|
|
373
412
|
data = None
|
|
374
413
|
content_type = "text/plain"
|
|
375
414
|
|
|
@@ -474,7 +513,7 @@ class entrypoint:
|
|
|
474
513
|
span_id,
|
|
475
514
|
) = await self.fetch_inline_trace(inline)
|
|
476
515
|
|
|
477
|
-
raise
|
|
516
|
+
raise self._HTTPException(
|
|
478
517
|
status_code=status_code,
|
|
479
518
|
detail=dict(
|
|
480
519
|
message=str(error),
|
|
@@ -586,7 +625,7 @@ class entrypoint:
|
|
|
586
625
|
Parameter(
|
|
587
626
|
"request",
|
|
588
627
|
kind=Parameter.POSITIONAL_OR_KEYWORD,
|
|
589
|
-
annotation=
|
|
628
|
+
annotation=self._Request,
|
|
590
629
|
),
|
|
591
630
|
*original_sig.parameters.values(),
|
|
592
631
|
]
|
|
@@ -649,7 +688,7 @@ class entrypoint:
|
|
|
649
688
|
name=self._config_key,
|
|
650
689
|
kind=Parameter.KEYWORD_ONLY,
|
|
651
690
|
annotation=type(config_instance), # Get the actual class type
|
|
652
|
-
default=
|
|
691
|
+
default=self._Body(config_instance), # Use the instance directly
|
|
653
692
|
)
|
|
654
693
|
)
|
|
655
694
|
|
|
@@ -663,7 +702,7 @@ class entrypoint:
|
|
|
663
702
|
Parameter(
|
|
664
703
|
name,
|
|
665
704
|
Parameter.KEYWORD_ONLY,
|
|
666
|
-
default=
|
|
705
|
+
default=self._Body(..., embed=True),
|
|
667
706
|
annotation=param.default.__class__.__bases__[
|
|
668
707
|
0
|
|
669
708
|
], # determines and get the base (parent/inheritance) type of the sdk-type at run-time. \
|