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
@@ -15,10 +15,15 @@ log = get_module_logger(__name__)
15
15
 
16
16
  class SecretsManager:
17
17
  @staticmethod
18
- def get_from_route() -> Optional[List[Dict[str, Any]]]:
18
+ def get_from_route(scope: str = "all") -> Optional[List[Dict[str, Any]]]:
19
19
  context = RoutingContext.get()
20
20
 
21
- secrets = context.secrets
21
+ if scope == "local":
22
+ secrets = context.local_secrets
23
+ elif scope == "vault":
24
+ secrets = context.vault_secrets
25
+ else:
26
+ secrets = context.secrets
22
27
 
23
28
  if not secrets:
24
29
  return []
@@ -140,7 +145,7 @@ class SecretsManager:
140
145
  return modified_model
141
146
 
142
147
  @staticmethod
143
- def get_provider_settings(model: str) -> Optional[Dict]:
148
+ def get_provider_settings(model: str, scope: str = "all") -> Optional[Dict]:
144
149
  """
145
150
  Builds the LLM request with appropriate kwargs based on the custom provider/model
146
151
 
@@ -154,7 +159,7 @@ class SecretsManager:
154
159
  request_provider_model = model
155
160
 
156
161
  # STEP 1: get vault secrets from route context and transform it
157
- secrets = SecretsManager.get_from_route()
162
+ secrets = SecretsManager.get_from_route(scope=scope)
158
163
  if not secrets:
159
164
  return None
160
165
 
@@ -231,7 +236,7 @@ class SecretsManager:
231
236
  return provider_settings
232
237
 
233
238
  @staticmethod
234
- async def retrieve_secrets():
239
+ async def retrieve_secrets() -> tuple[list, list, list]:
235
240
  return await get_secrets(
236
241
  f"{ag.DEFAULT_AGENTA_SINGLETON_INSTANCE.host}/api",
237
242
  RunningContext.get().credentials,
@@ -241,14 +246,20 @@ class SecretsManager:
241
246
  async def ensure_secrets_in_workflow():
242
247
  ctx = RunningContext.get()
243
248
 
244
- ctx.secrets = await SecretsManager.retrieve_secrets()
249
+ secrets, vault_secrets, local_secrets = await SecretsManager.retrieve_secrets()
250
+
251
+ ctx.secrets = secrets
252
+ ctx.vault_secrets = vault_secrets
253
+ ctx.local_secrets = local_secrets
245
254
 
246
255
  RunningContext.set(ctx)
247
256
 
248
257
  return ctx.secrets
249
258
 
250
259
  @staticmethod
251
- def get_provider_settings_from_workflow(model: str) -> Optional[Dict]:
260
+ def get_provider_settings_from_workflow(
261
+ model: str, scope: str = "all"
262
+ ) -> Optional[Dict]:
252
263
  """
253
264
  Builds the LLM request with appropriate kwargs based on the custom provider/model
254
265
 
@@ -262,7 +273,13 @@ class SecretsManager:
262
273
  request_provider_model = model
263
274
 
264
275
  # STEP 1: get vault secrets from route context and transform it
265
- secrets = RunningContext.get().secrets
276
+ ctx = RunningContext.get()
277
+ if scope == "local":
278
+ secrets = ctx.local_secrets
279
+ elif scope == "vault":
280
+ secrets = ctx.vault_secrets
281
+ else:
282
+ secrets = ctx.secrets
266
283
  if not secrets:
267
284
  return None
268
285
 
@@ -1,248 +1,212 @@
1
1
  from typing import List, Dict, Any, Optional
2
- from uuid import UUID
2
+ from uuid import UUID, uuid4
3
3
 
4
4
  from agenta.sdk.utils.client import authed_api
5
5
  from agenta.sdk.utils.references import get_slug_from_name_and_id
6
6
  from agenta.sdk.models.testsets import (
7
- LegacyTestset,
8
- #
9
- Testcase,
10
7
  TestsetRevisionData,
11
8
  TestsetRevision,
12
9
  #
13
10
  TestsetRevisionResponse,
11
+ #
12
+ SimpleTestsetResponse,
14
13
  )
15
14
 
16
15
 
17
- async def _create_legacy_testset(
16
+ def _normalize_csvdata(
17
+ data: List[Dict[str, Any]] | TestsetRevisionData,
18
+ ) -> List[Dict[str, Any]]:
19
+ if isinstance(data, TestsetRevisionData) and data.testcases:
20
+ return [testcase.data for testcase in data.testcases]
21
+
22
+ if isinstance(data, list):
23
+ return data
24
+
25
+ return []
26
+
27
+
28
+ async def _create_simple_testset(
18
29
  *,
19
30
  csvdata: List[Dict[str, Any]],
20
31
  name: str,
21
32
  testset_id: Optional[UUID] = None,
22
33
  ) -> Optional[TestsetRevision]:
34
+ slug_seed = testset_id or uuid4()
35
+
36
+ payload = {
37
+ "testset": {
38
+ "slug": get_slug_from_name_and_id(name, slug_seed),
39
+ "name": name,
40
+ "data": {
41
+ "testcases": [
42
+ {"data": testcase_data}
43
+ for testcase_data in csvdata
44
+ if isinstance(testcase_data, dict)
45
+ ]
46
+ },
47
+ }
48
+ }
49
+
23
50
  response = authed_api()(
24
51
  method="POST",
25
- endpoint="/testsets/",
26
- json={
27
- "testset_id": str(testset_id) if testset_id else None,
28
- "name": name,
29
- "csvdata": csvdata,
30
- },
52
+ endpoint="/preview/simple/testsets/",
53
+ json=payload,
31
54
  )
32
55
 
33
56
  if response.status_code != 200:
34
57
  print("Failed to create testset:", response.status_code, response.text)
35
58
  return None
36
59
 
37
- legacy_testset = LegacyTestset(**response.json())
38
-
39
- # print(" --- legacy_testset:", legacy_testset)
60
+ simple_testset_response = SimpleTestsetResponse(**response.json())
61
+ simple_testset = simple_testset_response.testset
40
62
 
41
- if not legacy_testset.id or not legacy_testset.name:
63
+ if not simple_testset or not simple_testset.id or not simple_testset.data:
42
64
  return None
43
65
 
44
- testset_revision = TestsetRevision(
45
- id=UUID(legacy_testset.id),
46
- slug=get_slug_from_name_and_id(
47
- name=legacy_testset.name,
48
- id=UUID(legacy_testset.id),
49
- ),
50
- name=legacy_testset.name,
51
- data=TestsetRevisionData(
52
- testcases=[
53
- Testcase(
54
- data=testcase_data,
55
- testset_id=UUID(legacy_testset.id),
56
- )
57
- for testcase_data in csvdata
58
- ]
59
- ),
60
- )
66
+ retrieved = await _retrieve_testset(testset_id=simple_testset.id)
61
67
 
62
- # print(" --- testset_revision:", testset_revision)
68
+ if retrieved:
69
+ return retrieved
63
70
 
64
- return testset_revision
71
+ return TestsetRevision(
72
+ id=simple_testset.id,
73
+ slug=simple_testset.slug,
74
+ name=simple_testset.name,
75
+ data=simple_testset.data,
76
+ testset_id=simple_testset.id,
77
+ )
65
78
 
66
79
 
67
- async def _fetch_legacy_testset(
80
+ async def _fetch_simple_testset(
68
81
  testset_id: Optional[UUID] = None,
69
- #
70
82
  name: Optional[str] = None,
71
83
  ) -> Optional[TestsetRevision]:
72
- legacy_testset = None
84
+ if not testset_id and not name:
85
+ return None
73
86
 
74
87
  if testset_id:
75
88
  response = authed_api()(
76
89
  method="GET",
77
- endpoint=f"/testsets/{testset_id}",
90
+ endpoint=f"/preview/simple/testsets/{testset_id}",
78
91
  )
79
92
 
80
- if response.status_code != 200:
81
- if response.status_code != 404:
82
- print("Failed to fetch testset:", response.status_code, response.text)
93
+ if response.status_code == 200:
94
+ simple_testset_response = SimpleTestsetResponse(**response.json())
95
+ simple_testset = simple_testset_response.testset
96
+
97
+ if simple_testset and simple_testset.id and simple_testset.data:
98
+ retrieved = await _retrieve_testset(
99
+ testset_id=UUID(str(simple_testset.id))
100
+ )
101
+
102
+ if retrieved:
103
+ return retrieved
104
+
105
+ return TestsetRevision(
106
+ id=simple_testset.id,
107
+ slug=simple_testset.slug,
108
+ name=simple_testset.name,
109
+ data=simple_testset.data,
110
+ testset_id=simple_testset.id,
111
+ )
112
+
113
+ elif response.status_code != 404:
114
+ print("Failed to fetch testset:", response.status_code, response.text)
83
115
  return None
84
116
 
85
- legacy_testset = LegacyTestset(**response.json())
86
- elif name:
117
+ if name:
87
118
  response = authed_api()(
88
- method="GET",
89
- endpoint="/testsets/",
90
- params={"name": name},
119
+ method="POST",
120
+ endpoint="/preview/simple/testsets/query",
121
+ json={"testset": {"name": name}},
91
122
  )
92
123
 
93
124
  if response.status_code != 200:
94
125
  print("Failed to list testsets:", response.status_code, response.text)
95
126
  return None
96
127
 
97
- _testsets = response.json()
128
+ testsets = response.json().get("testsets", [])
98
129
 
99
- for testset in _testsets:
100
- _id = testset.pop("_id", None)
101
- testset["id"] = _id
130
+ if testsets:
131
+ first = testsets[0]
102
132
 
103
- legacy_testsets = [LegacyTestset(**testset) for testset in _testsets]
133
+ if first.get("id"):
134
+ return await _fetch_simple_testset(testset_id=UUID(first["id"]))
104
135
 
105
- if len(legacy_testsets) != 1:
106
- print("Expected exactly one testset with name:", name)
107
- return None
108
-
109
- legacy_testset = legacy_testsets[0]
136
+ return None
110
137
 
111
- # print(" --- legacy_testset:", legacy_testset)
112
138
 
113
- if not legacy_testset.id or not legacy_testset.name:
114
- return None
115
-
116
- testset_revision = TestsetRevision(
117
- testset_id=UUID(legacy_testset.id),
118
- slug=get_slug_from_name_and_id(
119
- name=legacy_testset.name,
120
- id=UUID(legacy_testset.id),
121
- ),
122
- name=legacy_testset.name,
123
- data=(
124
- TestsetRevisionData(
125
- testcases=[
126
- Testcase(
127
- data=testcase_data,
128
- testset_id=UUID(legacy_testset.id),
129
- )
130
- for testcase_data in legacy_testset.csvdata
131
- ]
132
- )
133
- if legacy_testset.csvdata
134
- else None
135
- ),
136
- )
137
-
138
- # print(" --- testset_revision:", testset_revision)
139
-
140
- return testset_revision
141
-
142
-
143
- async def _edit_legacy_testset(
139
+ async def _edit_simple_testset(
144
140
  *,
145
141
  testset_id: UUID,
146
142
  csvdata: List[Dict[str, Any]],
147
143
  name: Optional[str] = None,
148
144
  ) -> Optional[TestsetRevision]:
145
+ payload = {
146
+ "testset": {
147
+ "id": str(testset_id),
148
+ "name": name,
149
+ "data": {
150
+ "testcases": [
151
+ {"data": testcase_data}
152
+ for testcase_data in csvdata
153
+ if isinstance(testcase_data, dict)
154
+ ]
155
+ },
156
+ }
157
+ }
158
+
149
159
  response = authed_api()(
150
160
  method="PUT",
151
- endpoint=f"/testsets/{testset_id}",
152
- json={
153
- "name": name,
154
- "csvdata": csvdata,
155
- },
161
+ endpoint=f"/preview/simple/testsets/{testset_id}",
162
+ json=payload,
156
163
  )
157
164
 
158
165
  if response.status_code != 200:
159
166
  print("Failed to edit testset:", response.status_code, response.text)
160
167
  return None
161
168
 
162
- response = authed_api()(
163
- method="GET",
164
- endpoint=f"/testsets/{testset_id}",
165
- )
166
-
167
- legacy_testset = LegacyTestset(**response.json())
169
+ simple_testset_response = SimpleTestsetResponse(**response.json())
170
+ simple_testset = simple_testset_response.testset
168
171
 
169
- # print(" --- legacy_testset:", legacy_testset)
170
-
171
- if not legacy_testset.id or not legacy_testset.name:
172
+ if not simple_testset or not simple_testset.id or not simple_testset.data:
172
173
  return None
173
174
 
174
- testset_revision = TestsetRevision(
175
- id=UUID(legacy_testset.id),
176
- slug=get_slug_from_name_and_id(
177
- name=legacy_testset.name,
178
- id=UUID(legacy_testset.id),
179
- ),
180
- name=legacy_testset.name,
181
- data=(
182
- TestsetRevisionData(
183
- testcases=[
184
- Testcase(
185
- data=testcase_data,
186
- testset_id=UUID(legacy_testset.id),
187
- )
188
- for testcase_data in legacy_testset.csvdata
189
- ]
190
- )
191
- if legacy_testset.csvdata
192
- else None
193
- ),
175
+ return TestsetRevision(
176
+ id=simple_testset.id,
177
+ slug=simple_testset.slug,
178
+ name=simple_testset.name,
179
+ data=simple_testset.data,
180
+ testset_id=simple_testset.id,
194
181
  )
195
182
 
196
- # print(" --- testset_revision:", testset_revision)
197
-
198
- return testset_revision
199
183
 
200
-
201
- async def _list_legacy_testsets(
184
+ async def _list_simple_testsets(
202
185
  #
203
186
  ) -> List[TestsetRevision]:
204
187
  response = authed_api()(
205
- method="GET",
206
- endpoint="/testsets/",
188
+ method="POST",
189
+ endpoint="/preview/simple/testsets/query",
190
+ json={},
207
191
  )
208
192
 
209
193
  if response.status_code != 200:
210
194
  print("Failed to list testsets:", response.status_code, response.text)
211
195
  return []
212
196
 
213
- legacy_testsets = [LegacyTestset(**testset) for testset in response.json()]
214
-
215
- # print(" --- legacy_testsets:", legacy_testsets)
216
-
217
- testset_revisions = [
218
- TestsetRevision(
219
- id=UUID(legacy_testset.id),
220
- slug=get_slug_from_name_and_id(
221
- name=legacy_testset.name,
222
- id=UUID(legacy_testset.id),
223
- ),
224
- name=legacy_testset.name,
225
- data=(
226
- TestsetRevisionData(
227
- testcases=[
228
- Testcase(
229
- data=testcase_data,
230
- testset_id=UUID(legacy_testset.id),
231
- )
232
- for testcase_data in legacy_testset.csvdata
233
- ]
234
- )
235
- if legacy_testset.csvdata
236
- else None
237
- ),
238
- )
239
- for legacy_testset in legacy_testsets
240
- if legacy_testset.id and legacy_testset.name
241
- ]
197
+ testsets = response.json().get("testsets", [])
198
+ revisions = []
199
+
200
+ for ts in testsets:
201
+ if not ts.get("id"):
202
+ continue
203
+
204
+ fetched = await _fetch_simple_testset(testset_id=UUID(ts["id"]))
242
205
 
243
- # print(" --- testset_revisions:", testset_revisions)
206
+ if fetched:
207
+ revisions.append(fetched)
244
208
 
245
- return testset_revisions
209
+ return revisions
246
210
 
247
211
 
248
212
  async def _retrieve_testset(
@@ -266,8 +230,6 @@ async def _retrieve_testset(
266
230
  ),
267
231
  }
268
232
 
269
- # print(" --- payload:", payload)
270
-
271
233
  response = authed_api()(
272
234
  method="POST",
273
235
  endpoint="/preview/testsets/revisions/retrieve",
@@ -277,14 +239,10 @@ async def _retrieve_testset(
277
239
 
278
240
  testset_revision_response = TestsetRevisionResponse(**response.json())
279
241
 
280
- testset_revision = testset_revision_response.testset_revision
281
-
282
- # print(" --- testset_revision:", testset_revision)
242
+ return testset_revision_response.testset_revision
283
243
 
284
- return testset_revision
285
244
 
286
-
287
- async def _sync_legacy_testset(
245
+ async def _sync_simple_testset(
288
246
  *,
289
247
  testset_id: Optional[UUID] = None,
290
248
  #
@@ -293,11 +251,7 @@ async def _sync_legacy_testset(
293
251
  name: Optional[str] = None,
294
252
  ) -> Optional[TestsetRevision]:
295
253
  try:
296
- # print("\n--------- UPSERT TESTSET")
297
-
298
- # print(" ---:", testset_revision_data.model_dump(mode="json", exclude_none=True))
299
-
300
- testset_revision = await _fetch_legacy_testset(
254
+ testset_revision = await _fetch_simple_testset(
301
255
  testset_id=testset_id,
302
256
  name=name,
303
257
  )
@@ -306,34 +260,18 @@ async def _sync_legacy_testset(
306
260
  print("[ERROR]: Failed to prepare testset:", e)
307
261
  return None
308
262
 
309
- # print("Fetch response:", testset_revision)
310
-
311
263
  if testset_revision and testset_revision.testset_id:
312
- # print(" --- Editing testset...", testset_id)
313
-
314
- testset_revision = await _edit_legacy_testset(
264
+ return await _edit_simple_testset(
315
265
  testset_id=testset_revision.testset_id,
316
266
  name=name,
317
267
  csvdata=csvdata,
318
268
  )
319
269
 
320
- # print("Edit response:", testset_revision)
321
-
322
- else:
323
- # print(" --- Creating testset...", name, data)
324
-
325
- testset_revision = await _create_legacy_testset(
326
- testset_id=testset_id,
327
- name=name,
328
- csvdata=csvdata,
329
- )
330
-
331
- if not testset_revision or not testset_revision.id:
332
- return None
333
-
334
- # print(" --- testset_revision:", testset_revision)
335
-
336
- return testset_revision
270
+ return await _create_simple_testset(
271
+ name=name or "Testset",
272
+ csvdata=csvdata,
273
+ testset_id=testset_id,
274
+ )
337
275
 
338
276
 
339
277
  async def aupsert(
@@ -344,15 +282,9 @@ async def aupsert(
344
282
  #
345
283
  data: List[Dict[str, Any]] | TestsetRevisionData,
346
284
  ) -> Optional[TestsetRevision]:
347
- csvdata = list()
348
- if isinstance(data, TestsetRevisionData) and data.testcases:
349
- csvdata = [testcase.data for testcase in data.testcases]
350
- elif isinstance(data, list):
351
- csvdata = data
352
- else:
353
- csvdata = list()
285
+ csvdata = _normalize_csvdata(data)
354
286
 
355
- return await _sync_legacy_testset(
287
+ return await _sync_simple_testset(
356
288
  testset_id=testset_id,
357
289
  name=name,
358
290
  csvdata=csvdata, # type: ignore
@@ -367,15 +299,9 @@ async def acreate(
367
299
  #
368
300
  data: List[Dict[str, Any]] | TestsetRevisionData,
369
301
  ) -> Optional[TestsetRevision]:
370
- csvdata = list()
371
- if isinstance(data, TestsetRevisionData) and data.testcases:
372
- csvdata = [testcase.data for testcase in data.testcases]
373
- elif isinstance(data, list):
374
- csvdata = data
375
- else:
376
- csvdata = list()
302
+ csvdata = _normalize_csvdata(data)
377
303
 
378
- return await _create_legacy_testset(
304
+ return await _create_simple_testset(
379
305
  testset_id=(
380
306
  testset_id
381
307
  if isinstance(testset_id, UUID)
@@ -383,7 +309,7 @@ async def acreate(
383
309
  if testset_id
384
310
  else None
385
311
  ),
386
- name=name,
312
+ name=name or "Testset",
387
313
  csvdata=csvdata, # type: ignore
388
314
  )
389
315
 
@@ -396,15 +322,9 @@ async def aedit(
396
322
  #
397
323
  data: List[Dict[str, Any]] | TestsetRevisionData,
398
324
  ) -> Optional[TestsetRevision]:
399
- csvdata = list()
400
- if isinstance(data, TestsetRevisionData) and data.testcases:
401
- csvdata = [testcase.data for testcase in data.testcases]
402
- elif isinstance(data, list):
403
- csvdata = data
404
- else:
405
- csvdata = list()
325
+ csvdata = _normalize_csvdata(data)
406
326
 
407
- return await _edit_legacy_testset(
327
+ return await _edit_simple_testset(
408
328
  testset_id=testset_id if isinstance(testset_id, UUID) else UUID(testset_id),
409
329
  name=name,
410
330
  csvdata=csvdata, # type: ignore
@@ -415,7 +335,7 @@ async def afetch(
415
335
  *,
416
336
  testset_id: UUID | str,
417
337
  ) -> Optional[TestsetRevision]:
418
- return await _fetch_legacy_testset(
338
+ return await _fetch_simple_testset(
419
339
  testset_id=testset_id if isinstance(testset_id, UUID) else UUID(testset_id)
420
340
  )
421
341
 
@@ -423,7 +343,7 @@ async def afetch(
423
343
  async def alist(
424
344
  #
425
345
  ) -> List[TestsetRevision]:
426
- return await _list_legacy_testsets()
346
+ return await _list_simple_testsets()
427
347
 
428
348
 
429
349
  async def aretrieve(
@@ -431,11 +351,7 @@ async def aretrieve(
431
351
  #
432
352
  testset_revision_id: Optional[UUID] = None,
433
353
  ) -> Optional[TestsetRevision]:
434
- # print("\n--------- RETRIEVE TESTSET")
435
-
436
- response = await _retrieve_testset(
354
+ return await _retrieve_testset(
437
355
  testset_id=testset_id,
438
356
  testset_revision_id=testset_revision_id,
439
357
  )
440
-
441
- return response