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/sdk/decorators/tracing.py
CHANGED
|
@@ -1,36 +1,27 @@
|
|
|
1
1
|
# /agenta/sdk/decorators/tracing.py
|
|
2
2
|
|
|
3
|
-
from typing import Callable, Optional, Any, Dict, List, Union
|
|
4
|
-
|
|
5
|
-
from opentelemetry import context as otel_context
|
|
6
|
-
from opentelemetry.context import attach, detach
|
|
7
|
-
|
|
8
|
-
|
|
9
3
|
from functools import wraps
|
|
10
|
-
from itertools import chain
|
|
11
4
|
from inspect import (
|
|
12
5
|
getfullargspec,
|
|
6
|
+
isasyncgenfunction,
|
|
13
7
|
iscoroutinefunction,
|
|
14
8
|
isgeneratorfunction,
|
|
15
|
-
isasyncgenfunction,
|
|
16
9
|
)
|
|
10
|
+
from itertools import chain
|
|
11
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
|
17
12
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
from opentelemetry import baggage
|
|
21
|
-
from opentelemetry.context import attach, detach, get_current
|
|
22
|
-
from opentelemetry.baggage import set_baggage, get_all
|
|
23
|
-
|
|
24
|
-
from agenta.sdk.utils.logging import get_module_logger
|
|
25
|
-
from agenta.sdk.utils.exceptions import suppress
|
|
13
|
+
import agenta as ag
|
|
26
14
|
from agenta.sdk.contexts.tracing import (
|
|
27
15
|
TracingContext,
|
|
28
16
|
tracing_context_manager,
|
|
29
17
|
)
|
|
30
18
|
from agenta.sdk.tracing.conventions import parse_span_kind
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
19
|
+
from agenta.sdk.utils.exceptions import suppress
|
|
20
|
+
from agenta.sdk.utils.logging import get_module_logger
|
|
21
|
+
from opentelemetry import context as otel_context
|
|
22
|
+
from opentelemetry.baggage import get_all, set_baggage
|
|
23
|
+
from opentelemetry.context import attach, detach, get_current
|
|
24
|
+
from pydantic import BaseModel
|
|
34
25
|
|
|
35
26
|
log = get_module_logger(__name__)
|
|
36
27
|
|
|
@@ -88,11 +79,12 @@ class instrument: # pylint: disable=invalid-name
|
|
|
88
79
|
with tracing_context_manager(context=TracingContext.get()):
|
|
89
80
|
# debug_otel_context("[BEFORE STREAM] [BEFORE SETUP]")
|
|
90
81
|
|
|
91
|
-
captured_ctx = otel_context.get_current()
|
|
92
|
-
|
|
93
82
|
self._parse_type_and_kind()
|
|
94
83
|
|
|
95
|
-
self._attach_baggage()
|
|
84
|
+
baggage_token = self._attach_baggage()
|
|
85
|
+
|
|
86
|
+
# Capture AFTER baggage attach so we do not wipe it later.
|
|
87
|
+
captured_ctx = otel_context.get_current()
|
|
96
88
|
|
|
97
89
|
ctx = self._get_traceparent()
|
|
98
90
|
|
|
@@ -141,6 +133,7 @@ class instrument: # pylint: disable=invalid-name
|
|
|
141
133
|
otel_context.detach(otel_token)
|
|
142
134
|
|
|
143
135
|
# debug_otel_context("[WITHIN STREAM] [AFTER DETACH]")
|
|
136
|
+
self._detach_baggage(baggage_token)
|
|
144
137
|
|
|
145
138
|
return wrapped_generator()
|
|
146
139
|
|
|
@@ -311,15 +304,43 @@ class instrument: # pylint: disable=invalid-name
|
|
|
311
304
|
|
|
312
305
|
def _attach_baggage(self):
|
|
313
306
|
context = TracingContext.get()
|
|
307
|
+
otel_ctx = get_current()
|
|
308
|
+
|
|
309
|
+
# 1. Propagate any incoming `ag.*` baggage as-is (for example
|
|
310
|
+
# `ag.meta.session_id`) so all nested spans inherit it.
|
|
311
|
+
if context.baggage:
|
|
312
|
+
for k, v in context.baggage.items():
|
|
313
|
+
if not isinstance(k, str) or not k.startswith("ag."):
|
|
314
|
+
continue
|
|
315
|
+
if v is None:
|
|
316
|
+
continue
|
|
317
|
+
otel_ctx = set_baggage(name=k, value=str(v), context=otel_ctx)
|
|
318
|
+
|
|
319
|
+
# 2. Propagate Agenta references in baggage (used for linking traces to
|
|
320
|
+
# application/variant/environment).
|
|
321
|
+
if context.references:
|
|
322
|
+
for k, v in context.references.items():
|
|
323
|
+
if v is None:
|
|
324
|
+
continue
|
|
325
|
+
if isinstance(v, BaseModel):
|
|
326
|
+
try:
|
|
327
|
+
v = v.model_dump(mode="json", exclude_none=True)
|
|
328
|
+
except Exception: # pylint: disable=bare-except
|
|
329
|
+
pass
|
|
330
|
+
if isinstance(v, dict):
|
|
331
|
+
for field, value in v.items():
|
|
332
|
+
otel_ctx = set_baggage(
|
|
333
|
+
name=f"ag.refs.{k}.{field}",
|
|
334
|
+
value=str(value),
|
|
335
|
+
context=otel_ctx,
|
|
336
|
+
)
|
|
337
|
+
continue
|
|
338
|
+
otel_ctx = set_baggage(
|
|
339
|
+
name=f"ag.refs.{k}", value=str(v), context=otel_ctx
|
|
340
|
+
)
|
|
314
341
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
token = None
|
|
318
|
-
if references:
|
|
319
|
-
for k, v in references.items():
|
|
320
|
-
token = attach(baggage.set_baggage(f"ag.refs.{k}", v))
|
|
321
|
-
|
|
322
|
-
return token
|
|
342
|
+
# Attach once so we can reliably detach later.
|
|
343
|
+
return attach(otel_ctx)
|
|
323
344
|
|
|
324
345
|
def _detach_baggage(
|
|
325
346
|
self,
|
|
@@ -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
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
from typing import Optional, Dict, List
|
|
2
1
|
from threading import Lock
|
|
2
|
+
from typing import Dict, List, Optional
|
|
3
3
|
|
|
4
|
+
from agenta.sdk.models.tracing import BaseModel
|
|
5
|
+
from agenta.sdk.utils.logging import get_module_logger
|
|
4
6
|
from opentelemetry.baggage import get_all as get_baggage
|
|
5
7
|
from opentelemetry.context import Context
|
|
6
8
|
from opentelemetry.sdk.trace import Span, SpanProcessor
|
|
7
9
|
from opentelemetry.sdk.trace.export import (
|
|
8
|
-
SpanExporter,
|
|
9
|
-
ReadableSpan,
|
|
10
10
|
BatchSpanProcessor,
|
|
11
|
+
ReadableSpan,
|
|
12
|
+
SpanExporter,
|
|
11
13
|
)
|
|
12
14
|
|
|
13
|
-
from agenta.sdk.utils.logging import get_module_logger
|
|
14
|
-
from agenta.sdk.engines.tracing.conventions import Reference
|
|
15
|
-
|
|
16
15
|
log = get_module_logger(__name__)
|
|
17
16
|
|
|
18
17
|
|
|
@@ -51,15 +50,27 @@ class TraceProcessor(SpanProcessor):
|
|
|
51
50
|
parent_context: Optional[Context] = None,
|
|
52
51
|
) -> None:
|
|
53
52
|
for key in self.references.keys():
|
|
54
|
-
|
|
53
|
+
ref = self.references[key]
|
|
54
|
+
if ref is None:
|
|
55
|
+
continue
|
|
56
|
+
if isinstance(ref, BaseModel):
|
|
57
|
+
try:
|
|
58
|
+
ref = ref.model_dump(mode="json", exclude_none=True)
|
|
59
|
+
except Exception: # pylint: disable=bare-except
|
|
60
|
+
pass
|
|
61
|
+
if isinstance(ref, dict):
|
|
62
|
+
for field, value in ref.items():
|
|
63
|
+
span.set_attribute(f"ag.refs.{key}.{field}", str(value))
|
|
64
|
+
else:
|
|
65
|
+
span.set_attribute(f"ag.refs.{key}", str(ref))
|
|
55
66
|
|
|
56
67
|
baggage = get_baggage(parent_context)
|
|
57
68
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
69
|
+
# Copy any `ag.*` baggage entries onto the span attributes so they can be
|
|
70
|
+
# used for filtering and grouping (for example `ag.meta.session_id`).
|
|
71
|
+
for key, value in baggage.items():
|
|
72
|
+
if key.startswith("ag."):
|
|
73
|
+
span.set_attribute(key, value)
|
|
63
74
|
|
|
64
75
|
trace_id = span.context.trace_id
|
|
65
76
|
span_id = span.context.span_id
|
|
@@ -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
|
-
|
|
129
|
+
created_revision = await acreate_testset(
|
|
130
130
|
data=testcases_data,
|
|
131
131
|
)
|
|
132
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
agenta/sdk/evaluations/runs.py
CHANGED
agenta/sdk/litellm/mockllm.py
CHANGED
|
@@ -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
|
|
agenta/sdk/managers/config.py
CHANGED
|
@@ -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
|
|
agenta/sdk/managers/secrets.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|