agenta 0.72.4__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.
Files changed (38) hide show
  1. agenta/__init__.py +9 -3
  2. agenta/sdk/__init__.py +2 -4
  3. agenta/sdk/agenta_init.py +22 -75
  4. agenta/sdk/context/serving.py +2 -0
  5. agenta/sdk/contexts/routing.py +2 -0
  6. agenta/sdk/contexts/running.py +3 -2
  7. agenta/sdk/decorators/running.py +8 -4
  8. agenta/sdk/decorators/serving.py +82 -41
  9. agenta/sdk/engines/tracing/inline.py +8 -1
  10. agenta/sdk/evaluations/preview/evaluate.py +36 -8
  11. agenta/sdk/evaluations/runs.py +2 -1
  12. agenta/sdk/litellm/mockllm.py +2 -2
  13. agenta/sdk/managers/config.py +3 -1
  14. agenta/sdk/managers/secrets.py +25 -8
  15. agenta/sdk/managers/testsets.py +143 -227
  16. agenta/sdk/middleware/vault.py +33 -18
  17. agenta/sdk/middlewares/running/vault.py +33 -17
  18. agenta/sdk/router.py +30 -5
  19. agenta/sdk/tracing/inline.py +8 -1
  20. agenta/sdk/types.py +13 -19
  21. agenta/sdk/utils/client.py +10 -9
  22. agenta/sdk/utils/lazy.py +253 -0
  23. agenta/sdk/workflows/builtin.py +2 -0
  24. agenta/sdk/workflows/configurations.py +1 -0
  25. agenta/sdk/workflows/handlers.py +236 -81
  26. agenta/sdk/workflows/interfaces.py +47 -0
  27. agenta/sdk/workflows/runners/base.py +6 -2
  28. agenta/sdk/workflows/runners/daytona.py +250 -131
  29. agenta/sdk/workflows/runners/local.py +22 -56
  30. agenta/sdk/workflows/runners/registry.py +1 -1
  31. agenta/sdk/workflows/sandbox.py +17 -5
  32. agenta/sdk/workflows/templates.py +81 -0
  33. agenta/sdk/workflows/utils.py +6 -0
  34. {agenta-0.72.4.dist-info → agenta-0.75.0.dist-info}/METADATA +4 -8
  35. {agenta-0.72.4.dist-info → agenta-0.75.0.dist-info}/RECORD +36 -36
  36. agenta/config.py +0 -25
  37. agenta/config.toml +0 -4
  38. {agenta-0.72.4.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, Config
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 Config, AgentaSingleton, init as _init
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 requests
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
- config = {}
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.error(
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
- response = requests.get(
201
- f"{self.api_url}/projects/current",
202
- headers={"Authorization": f"ApiKey {self.api_key}"},
203
- timeout=10,
204
- )
205
- response.raise_for_status()
206
-
207
- project_info = response.json()
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)
@@ -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
@@ -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
@@ -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
 
@@ -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 = credentials or (
325
- f"ApiKey {ag.DEFAULT_AGENTA_SINGLETON_INSTANCE.api_key}"
326
- if ag.DEFAULT_AGENTA_SINGLETON_INSTANCE.api_key
327
- else None
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()):
@@ -1,62 +1,81 @@
1
- from asyncio import sleep
2
- from functools import wraps
1
+ from typing import Type, Any, Callable, Dict, Optional, Tuple, List, TYPE_CHECKING
3
2
  from inspect import (
4
- Parameter,
5
- Signature,
6
- isasyncgen,
7
3
  iscoroutinefunction,
8
4
  isgenerator,
5
+ isasyncgen,
9
6
  signature,
7
+ Signature,
8
+ Parameter,
10
9
  )
11
- from os import environ
10
+ from functools import wraps
12
11
  from traceback import format_exception
13
- from typing import Any, Callable, Dict, List, Optional, Tuple, Type
12
+ from asyncio import sleep
14
13
  from uuid import UUID
14
+ from pydantic import BaseModel, HttpUrl, ValidationError
15
+ from os import environ
16
+
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
15
25
 
16
- import agenta as ag
17
26
  from agenta.sdk.contexts.routing import (
18
- RoutingContext,
19
27
  routing_context_manager,
28
+ RoutingContext,
20
29
  )
21
30
  from agenta.sdk.contexts.tracing import (
22
- TracingContext,
23
31
  tracing_context_manager,
32
+ TracingContext,
24
33
  )
25
- from agenta.sdk.middleware.auth import AuthHTTPMiddleware
26
- from agenta.sdk.middleware.config import ConfigMiddleware
27
- from agenta.sdk.middleware.cors import CORSMiddleware
28
- from agenta.sdk.middleware.inline import InlineMiddleware
29
- from agenta.sdk.middleware.mock import MockMiddleware
30
- from agenta.sdk.middleware.otel import OTelMiddleware
31
- from agenta.sdk.middleware.vault import VaultMiddleware
32
34
  from agenta.sdk.router import router
35
+ from agenta.sdk.utils.exceptions import suppress, display_exception
36
+ from agenta.sdk.utils.logging import get_module_logger
37
+ from agenta.sdk.utils.helpers import get_current_version
38
+ from agenta.sdk.utils.lazy import _load_fastapi, _load_starlette_responses
33
39
  from agenta.sdk.types import (
34
- BaseResponse,
35
40
  MultipleChoice,
41
+ BaseResponse,
36
42
  StreamResponse,
37
- )
38
- from agenta.sdk.utils.exceptions import display_exception, suppress
39
- from agenta.sdk.utils.helpers import get_current_version
40
- from agenta.sdk.utils.logging import get_module_logger
41
- from fastapi import Body, FastAPI, HTTPException, Request
42
- from pydantic import BaseModel, HttpUrl, ValidationError
43
- from starlette.responses import (
44
- Response as StarletteResponse,
45
- )
46
- from starlette.responses import (
47
- StreamingResponse,
43
+ MCField,
48
44
  )
49
45
 
46
+ import agenta as ag
47
+
50
48
  log = get_module_logger(__name__)
51
49
 
52
50
  AGENTA_RUNTIME_PREFIX = environ.get("AGENTA_RUNTIME_PREFIX", "")
53
51
 
54
- app = FastAPI(
55
- docs_url=f"{AGENTA_RUNTIME_PREFIX}/docs", # Swagger UI
56
- openapi_url=f"{AGENTA_RUNTIME_PREFIX}/openapi.json", # OpenAPI schema
57
- )
58
52
 
59
- app.include_router(router, prefix=AGENTA_RUNTIME_PREFIX)
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
60
79
 
61
80
 
62
81
  class PathValidator(BaseModel):
@@ -140,16 +159,32 @@ class entrypoint:
140
159
  route_path: str = "",
141
160
  config_schema: Optional[BaseModel] = None,
142
161
  ):
162
+ # Lazy import fastapi components - only loaded when decorator is used
163
+ fastapi = _load_fastapi()
164
+
143
165
  self.func = func
144
166
  self.route_path = route_path
145
167
  self.config_schema = config_schema
146
168
 
169
+ # Store for use in methods
170
+ self._Request = fastapi.Request
171
+ self._HTTPException = fastapi.HTTPException
172
+ self._Body = fastapi.Body
173
+
147
174
  signature_parameters = signature(func).parameters
148
175
  config, default_parameters = self.parse_config()
149
176
 
150
177
  ### --- Middleware --- #
151
178
  if not entrypoint._middleware:
152
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
+
153
188
  app.add_middleware(MockMiddleware)
154
189
  app.add_middleware(InlineMiddleware)
155
190
  app.add_middleware(VaultMiddleware)
@@ -177,7 +212,7 @@ class entrypoint:
177
212
  request.state.config["parameters"] is None
178
213
  or request.state.config["references"] is None
179
214
  ):
180
- raise HTTPException(
215
+ raise self._HTTPException(
181
216
  status_code=400,
182
217
  detail="Config not found based on provided references.",
183
218
  )
@@ -318,12 +353,12 @@ class entrypoint:
318
353
 
319
354
  async def execute_wrapper(
320
355
  self,
321
- request: Request,
356
+ request: "Request", # type: ignore
322
357
  *args,
323
358
  **kwargs,
324
359
  ):
325
360
  if not request:
326
- raise HTTPException(status_code=500, detail="Missing 'request'.")
361
+ raise self._HTTPException(status_code=500, detail="Missing 'request'.")
327
362
 
328
363
  state = request.state
329
364
  traceparent = state.otel.get("traceparent")
@@ -332,6 +367,8 @@ class entrypoint:
332
367
  parameters = state.config.get("parameters")
333
368
  references = state.config.get("references")
334
369
  secrets = state.vault.get("secrets")
370
+ local_secrets = state.vault.get("local_secrets")
371
+ vault_secrets = state.vault.get("vault_secrets")
335
372
  inline = state.inline
336
373
  mock = state.mock
337
374
 
@@ -339,6 +376,8 @@ class entrypoint:
339
376
  context=RoutingContext(
340
377
  parameters=parameters,
341
378
  secrets=secrets,
379
+ local_secrets=local_secrets,
380
+ vault_secrets=vault_secrets,
342
381
  mock=mock,
343
382
  )
344
383
  ):
@@ -368,6 +407,8 @@ class entrypoint:
368
407
  result: Any,
369
408
  inline: bool,
370
409
  ):
410
+ StarletteResponse, StreamingResponse = _load_starlette_responses()
411
+
371
412
  data = None
372
413
  content_type = "text/plain"
373
414
 
@@ -472,7 +513,7 @@ class entrypoint:
472
513
  span_id,
473
514
  ) = await self.fetch_inline_trace(inline)
474
515
 
475
- raise HTTPException(
516
+ raise self._HTTPException(
476
517
  status_code=status_code,
477
518
  detail=dict(
478
519
  message=str(error),
@@ -584,7 +625,7 @@ class entrypoint:
584
625
  Parameter(
585
626
  "request",
586
627
  kind=Parameter.POSITIONAL_OR_KEYWORD,
587
- annotation=Request,
628
+ annotation=self._Request,
588
629
  ),
589
630
  *original_sig.parameters.values(),
590
631
  ]
@@ -647,7 +688,7 @@ class entrypoint:
647
688
  name=self._config_key,
648
689
  kind=Parameter.KEYWORD_ONLY,
649
690
  annotation=type(config_instance), # Get the actual class type
650
- default=Body(config_instance), # Use the instance directly
691
+ default=self._Body(config_instance), # Use the instance directly
651
692
  )
652
693
  )
653
694
 
@@ -661,7 +702,7 @@ class entrypoint:
661
702
  Parameter(
662
703
  name,
663
704
  Parameter.KEYWORD_ONLY,
664
- default=Body(..., embed=True),
705
+ default=self._Body(..., embed=True),
665
706
  annotation=param.default.__class__.__bases__[
666
707
  0
667
708
  ], # determines and get the base (parent/inheritance) type of the sdk-type at run-time. \
@@ -957,9 +957,10 @@ def parse_to_agenta_span_dto(
957
957
  ########################################
958
958
 
959
959
 
960
- from litellm import cost_calculator
961
960
  from opentelemetry.sdk.trace import ReadableSpan
962
961
 
962
+ from agenta.sdk.utils.lazy import _load_litellm
963
+
963
964
  from agenta.sdk.types import AgentaNodeDto, AgentaNodesResponse
964
965
 
965
966
 
@@ -1120,6 +1121,12 @@ TYPES_WITH_COSTS = [
1120
1121
 
1121
1122
 
1122
1123
  def calculate_costs(span_idx: Dict[str, SpanDTO]):
1124
+ litellm = _load_litellm()
1125
+ if not litellm:
1126
+ return
1127
+
1128
+ cost_calculator = litellm.cost_calculator
1129
+
1123
1130
  for span in span_idx.values():
1124
1131
  if (
1125
1132
  span.node.type
@@ -126,10 +126,11 @@ async def _upsert_entities(
126
126
  for testcases_data in simple_evaluation_data.testset_steps:
127
127
  if isinstance(testcases_data, List):
128
128
  if all(isinstance(step, Dict) for step in testcases_data):
129
- testset_revision_id = await acreate_testset(
129
+ created_revision = await acreate_testset(
130
130
  data=testcases_data,
131
131
  )
132
- testset_steps[str(testset_revision_id)] = "custom"
132
+ if created_revision and created_revision.id:
133
+ testset_steps[str(created_revision.id)] = "custom"
133
134
 
134
135
  simple_evaluation_data.testset_steps = testset_steps
135
136
 
@@ -215,15 +216,16 @@ async def _retrieve_entities(
215
216
  Dict[UUID, EvaluatorRevision],
216
217
  ]:
217
218
  testset_revisions: Dict[UUID, TestsetRevision] = {}
218
- # for testset_revision_id, origin in simple_evaluation_data.testset_steps.items():
219
- # testset_revision = await retrieve_testset(
220
- # testset_revision_id=testset_revision_id,
221
- # )
222
- for testset_id, origin in simple_evaluation_data.testset_steps.items():
219
+ for testset_ref, origin in simple_evaluation_data.testset_steps.items():
223
220
  testset_revision = await aretrieve_testset(
224
- testset_id=testset_id,
221
+ testset_revision_id=testset_ref,
225
222
  )
226
223
 
224
+ if not testset_revision or not testset_revision.id:
225
+ testset_revision = await aretrieve_testset(
226
+ testset_id=testset_ref,
227
+ )
228
+
227
229
  if not testset_revision or not testset_revision.id:
228
230
  continue
229
231
 
@@ -308,6 +310,32 @@ async def aevaluate(
308
310
  "────────────────────────────────────────────────────────────────────────────"
309
311
  )
310
312
 
313
+ # Normalize testset_steps to revision ids (no JIT transfers in backend)
314
+ if simple_evaluation_data.testset_steps and isinstance(
315
+ simple_evaluation_data.testset_steps, dict
316
+ ):
317
+ normalized_testset_steps: Dict[str, Origin] = {}
318
+ for testset_id_str, origin in simple_evaluation_data.testset_steps.items():
319
+ try:
320
+ testset_uuid = UUID(str(testset_id_str))
321
+ except Exception:
322
+ continue
323
+
324
+ testset_revision = await aretrieve_testset(
325
+ testset_revision_id=testset_uuid,
326
+ )
327
+
328
+ if not testset_revision or not testset_revision.id:
329
+ # Fallback: treat as testset_id (latest revision)
330
+ testset_revision = await aretrieve_testset(
331
+ testset_id=testset_uuid,
332
+ )
333
+
334
+ if testset_revision and testset_revision.id:
335
+ normalized_testset_steps[str(testset_revision.id)] = origin
336
+
337
+ simple_evaluation_data.testset_steps = normalized_testset_steps
338
+
311
339
  suffix = _timestamp_suffix()
312
340
  name = f"{name}{suffix}"
313
341
 
@@ -68,7 +68,8 @@ async def acreate(
68
68
  repeats=repeats,
69
69
  ),
70
70
  #
71
- jit={"testsets": True, "evaluators": False},
71
+ # Default: expect callers to pass testset revision ids; no JIT migration
72
+ jit={"testsets": False, "evaluators": False},
72
73
  )
73
74
  )
74
75
 
@@ -2,9 +2,8 @@ from typing import Optional, Protocol, Any
2
2
  from os import environ
3
3
  from contextlib import contextmanager
4
4
 
5
- import litellm
6
-
7
5
  from agenta.sdk.utils.logging import get_module_logger
6
+ from agenta.sdk.utils.lazy import _load_litellm
8
7
 
9
8
  from agenta.sdk.litellm.mocks import MOCKS
10
9
  from agenta.sdk.contexts.routing import RoutingContext
@@ -81,6 +80,7 @@ async def acompletion(*args, **kwargs):
81
80
 
82
81
  return MOCKS[mock](*args, **kwargs)
83
82
 
83
+ litellm = _load_litellm(injected=globals().get("litellm"))
84
84
  if not litellm:
85
85
  raise ValueError("litellm not found")
86
86
 
@@ -2,10 +2,10 @@ import json
2
2
  from pathlib import Path
3
3
  from typing import Optional, Type, TypeVar, Dict, Any, Union
4
4
 
5
- import yaml
6
5
  from pydantic import BaseModel
7
6
 
8
7
  from agenta.sdk.utils.logging import get_module_logger
8
+ from agenta.sdk.utils.lazy import _load_yaml
9
9
  from agenta.sdk.managers.shared import SharedManager
10
10
  from agenta.sdk.contexts.routing import RoutingContext
11
11
 
@@ -174,6 +174,8 @@ class ConfigManager:
174
174
  """
175
175
  file_path = Path(filename)
176
176
 
177
+ yaml = _load_yaml()
178
+
177
179
  with open(file_path, "r", encoding="utf-8") as file:
178
180
  parameters = yaml.safe_load(file)
179
181