agenta 0.68.0__py3-none-any.whl → 0.72.4__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 +66 -36
- agenta/sdk/agenta_init.py +88 -10
- agenta/sdk/assets.py +73 -10
- agenta/sdk/decorators/serving.py +29 -31
- agenta/sdk/decorators/tracing.py +51 -30
- agenta/sdk/engines/tracing/processors.py +23 -12
- agenta/sdk/litellm/litellm.py +38 -30
- agenta/sdk/middleware/auth.py +19 -4
- agenta/sdk/middleware/config.py +3 -1
- agenta/sdk/middleware/otel.py +3 -4
- agenta/sdk/middleware/vault.py +20 -5
- agenta/sdk/middlewares/routing/otel.py +1 -1
- agenta/sdk/middlewares/running/vault.py +1 -1
- agenta/sdk/models/shared.py +1 -1
- agenta/sdk/tracing/exporters.py +1 -0
- agenta/sdk/tracing/processors.py +48 -40
- agenta/sdk/tracing/propagation.py +9 -12
- agenta/sdk/tracing/tracing.py +89 -0
- agenta/sdk/types.py +6 -2
- agenta/sdk/workflows/runners/daytona.py +0 -6
- agenta/sdk/workflows/runners/registry.py +19 -2
- {agenta-0.68.0.dist-info → agenta-0.72.4.dist-info}/METADATA +3 -2
- {agenta-0.68.0.dist-info → agenta-0.72.4.dist-info}/RECORD +24 -24
- {agenta-0.68.0.dist-info → agenta-0.72.4.dist-info}/WHEEL +0 -0
agenta/sdk/litellm/litellm.py
CHANGED
|
@@ -166,24 +166,27 @@ def litellm_handler():
|
|
|
166
166
|
namespace="metrics.unit.costs",
|
|
167
167
|
)
|
|
168
168
|
|
|
169
|
+
# Handle both dict and object attribute access for usage, and safely handle None
|
|
170
|
+
usage = getattr(response_obj, "usage", None)
|
|
171
|
+
if isinstance(usage, dict):
|
|
172
|
+
prompt_tokens = usage.get("prompt_tokens")
|
|
173
|
+
completion_tokens = usage.get("completion_tokens")
|
|
174
|
+
total_tokens = usage.get("total_tokens")
|
|
175
|
+
elif usage is not None:
|
|
176
|
+
prompt_tokens = getattr(usage, "prompt_tokens", None)
|
|
177
|
+
completion_tokens = getattr(usage, "completion_tokens", None)
|
|
178
|
+
total_tokens = getattr(usage, "total_tokens", None)
|
|
179
|
+
else:
|
|
180
|
+
prompt_tokens = completion_tokens = total_tokens = None
|
|
181
|
+
|
|
169
182
|
span.set_attributes(
|
|
170
183
|
attributes=(
|
|
171
184
|
{
|
|
172
|
-
"prompt": (
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
),
|
|
177
|
-
"completion": (
|
|
178
|
-
float(response_obj.usage.completion_tokens)
|
|
179
|
-
if response_obj.usage.completion_tokens
|
|
180
|
-
else None
|
|
181
|
-
),
|
|
182
|
-
"total": (
|
|
183
|
-
float(response_obj.usage.total_tokens)
|
|
184
|
-
if response_obj.usage.total_tokens
|
|
185
|
-
else None
|
|
186
|
-
),
|
|
185
|
+
"prompt": float(prompt_tokens) if prompt_tokens else None,
|
|
186
|
+
"completion": float(completion_tokens)
|
|
187
|
+
if completion_tokens
|
|
188
|
+
else None,
|
|
189
|
+
"total": float(total_tokens) if total_tokens else None,
|
|
187
190
|
}
|
|
188
191
|
),
|
|
189
192
|
namespace="metrics.unit.tokens",
|
|
@@ -300,24 +303,29 @@ def litellm_handler():
|
|
|
300
303
|
namespace="metrics.unit.costs",
|
|
301
304
|
)
|
|
302
305
|
|
|
306
|
+
# Handle both dict and object attribute access for usage
|
|
307
|
+
usage = getattr(response_obj, "usage", None)
|
|
308
|
+
if usage is None:
|
|
309
|
+
prompt_tokens = None
|
|
310
|
+
completion_tokens = None
|
|
311
|
+
total_tokens = None
|
|
312
|
+
elif isinstance(usage, dict):
|
|
313
|
+
prompt_tokens = usage.get("prompt_tokens")
|
|
314
|
+
completion_tokens = usage.get("completion_tokens")
|
|
315
|
+
total_tokens = usage.get("total_tokens")
|
|
316
|
+
else:
|
|
317
|
+
prompt_tokens = getattr(usage, "prompt_tokens", None)
|
|
318
|
+
completion_tokens = getattr(usage, "completion_tokens", None)
|
|
319
|
+
total_tokens = getattr(usage, "total_tokens", None)
|
|
320
|
+
|
|
303
321
|
span.set_attributes(
|
|
304
322
|
attributes=(
|
|
305
323
|
{
|
|
306
|
-
"prompt": (
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
),
|
|
311
|
-
"completion": (
|
|
312
|
-
float(response_obj.usage.completion_tokens)
|
|
313
|
-
if response_obj.usage.completion_tokens
|
|
314
|
-
else None
|
|
315
|
-
),
|
|
316
|
-
"total": (
|
|
317
|
-
float(response_obj.usage.total_tokens)
|
|
318
|
-
if response_obj.usage.total_tokens
|
|
319
|
-
else None
|
|
320
|
-
),
|
|
324
|
+
"prompt": float(prompt_tokens) if prompt_tokens else None,
|
|
325
|
+
"completion": float(completion_tokens)
|
|
326
|
+
if completion_tokens
|
|
327
|
+
else None,
|
|
328
|
+
"total": float(total_tokens) if total_tokens else None,
|
|
321
329
|
}
|
|
322
330
|
),
|
|
323
331
|
namespace="metrics.unit.tokens",
|
agenta/sdk/middleware/auth.py
CHANGED
|
@@ -17,14 +17,12 @@ import agenta as ag
|
|
|
17
17
|
|
|
18
18
|
log = get_module_logger(__name__)
|
|
19
19
|
|
|
20
|
-
AGENTA_RUNTIME_PREFIX = getenv("AGENTA_RUNTIME_PREFIX", "")
|
|
21
|
-
|
|
22
20
|
|
|
23
21
|
_CACHE_ENABLED = (
|
|
24
22
|
getenv("AGENTA_SERVICE_MIDDLEWARE_CACHE_ENABLED", "true").lower() in TRUTHY
|
|
25
23
|
)
|
|
26
24
|
|
|
27
|
-
_ALWAYS_ALLOW_LIST = [
|
|
25
|
+
_ALWAYS_ALLOW_LIST = ["/health"]
|
|
28
26
|
|
|
29
27
|
_cache = TTLLRUCache()
|
|
30
28
|
|
|
@@ -64,7 +62,7 @@ class AuthHTTPMiddleware(BaseHTTPMiddleware):
|
|
|
64
62
|
|
|
65
63
|
async def dispatch(self, request: Request, call_next: Callable):
|
|
66
64
|
try:
|
|
67
|
-
if request.url.path in _ALWAYS_ALLOW_LIST:
|
|
65
|
+
if _strip_service_prefix(request.url.path) in _ALWAYS_ALLOW_LIST:
|
|
68
66
|
request.state.auth = {}
|
|
69
67
|
|
|
70
68
|
else:
|
|
@@ -253,3 +251,20 @@ class AuthHTTPMiddleware(BaseHTTPMiddleware):
|
|
|
253
251
|
status_code=500,
|
|
254
252
|
content=f"Could not verify credentials: unexpected error - {str(exc)}. Please try again later or contact support if the issue persists.",
|
|
255
253
|
) from exc
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _strip_service_prefix(path: str) -> str:
|
|
257
|
+
if not path.startswith("/services/"):
|
|
258
|
+
return path
|
|
259
|
+
|
|
260
|
+
parts = path.split("/", 3)
|
|
261
|
+
if len(parts) < 4:
|
|
262
|
+
return "/"
|
|
263
|
+
|
|
264
|
+
service_name = parts[2]
|
|
265
|
+
remainder = parts[3]
|
|
266
|
+
|
|
267
|
+
if not service_name or not remainder or remainder.startswith("/"):
|
|
268
|
+
return path
|
|
269
|
+
|
|
270
|
+
return f"/{remainder}"
|
agenta/sdk/middleware/config.py
CHANGED
|
@@ -224,6 +224,7 @@ class ConfigMiddleware(BaseHTTPMiddleware):
|
|
|
224
224
|
baggage.get("ag.refs.variant.slug")
|
|
225
225
|
# ALTERNATIVE
|
|
226
226
|
or request.query_params.get("variant_slug")
|
|
227
|
+
or body.get("variant_slug")
|
|
227
228
|
# LEGACY
|
|
228
229
|
or baggage.get("variant_slug")
|
|
229
230
|
or request.query_params.get("config")
|
|
@@ -234,6 +235,7 @@ class ConfigMiddleware(BaseHTTPMiddleware):
|
|
|
234
235
|
baggage.get("ag.refs.variant.version")
|
|
235
236
|
# ALTERNATIVE
|
|
236
237
|
or request.query_params.get("variant_version")
|
|
238
|
+
or body.get("variant_version")
|
|
237
239
|
# LEGACY
|
|
238
240
|
or baggage.get("variant_version")
|
|
239
241
|
)
|
|
@@ -244,7 +246,7 @@ class ConfigMiddleware(BaseHTTPMiddleware):
|
|
|
244
246
|
return Reference(
|
|
245
247
|
id=variant_id,
|
|
246
248
|
slug=variant_slug,
|
|
247
|
-
version=variant_version,
|
|
249
|
+
version=str(variant_version) if variant_version is not None else None,
|
|
248
250
|
)
|
|
249
251
|
|
|
250
252
|
async def _parse_environment_ref(
|
agenta/sdk/middleware/otel.py
CHANGED
|
@@ -3,13 +3,12 @@ from typing import Callable
|
|
|
3
3
|
from starlette.middleware.base import BaseHTTPMiddleware
|
|
4
4
|
from fastapi import Request, FastAPI
|
|
5
5
|
|
|
6
|
-
from opentelemetry.baggage.propagation import W3CBaggagePropagator
|
|
7
|
-
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
|
|
8
|
-
|
|
9
6
|
from agenta.sdk.utils.exceptions import suppress
|
|
10
7
|
from agenta.sdk.tracing.propagation import extract
|
|
11
|
-
|
|
8
|
+
from agenta.sdk.utils.exceptions import suppress
|
|
12
9
|
from agenta.sdk.utils.logging import get_module_logger
|
|
10
|
+
from fastapi import FastAPI, Request
|
|
11
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
13
12
|
|
|
14
13
|
log = get_module_logger(__name__)
|
|
15
14
|
|
agenta/sdk/middleware/vault.py
CHANGED
|
@@ -21,11 +21,9 @@ import agenta as ag
|
|
|
21
21
|
log = get_module_logger(__name__)
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
AGENTA_RUNTIME_PREFIX = getenv("AGENTA_RUNTIME_PREFIX", "")
|
|
25
|
-
|
|
26
24
|
_ALWAYS_ALLOW_LIST = [
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
"/health",
|
|
26
|
+
"/openapi.json",
|
|
29
27
|
]
|
|
30
28
|
|
|
31
29
|
_PROVIDER_KINDS = [
|
|
@@ -116,7 +114,7 @@ class VaultMiddleware(BaseHTTPMiddleware):
|
|
|
116
114
|
allow_secrets = True
|
|
117
115
|
|
|
118
116
|
try:
|
|
119
|
-
if
|
|
117
|
+
if _strip_service_prefix(request.url.path) not in _ALWAYS_ALLOW_LIST:
|
|
120
118
|
await self._allow_local_secrets(credentials)
|
|
121
119
|
|
|
122
120
|
for provider_kind in _PROVIDER_KINDS:
|
|
@@ -331,3 +329,20 @@ class VaultMiddleware(BaseHTTPMiddleware):
|
|
|
331
329
|
status_code=500,
|
|
332
330
|
content=f"Could not verify credentials: unexpected error - {str(exc)}. Please try again later or contact support if the issue persists.",
|
|
333
331
|
) from exc
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def _strip_service_prefix(path: str) -> str:
|
|
335
|
+
if not path.startswith("/services/"):
|
|
336
|
+
return path
|
|
337
|
+
|
|
338
|
+
parts = path.split("/", 3)
|
|
339
|
+
if len(parts) < 4:
|
|
340
|
+
return "/"
|
|
341
|
+
|
|
342
|
+
service_name = parts[2]
|
|
343
|
+
remainder = parts[3]
|
|
344
|
+
|
|
345
|
+
if not service_name or not remainder or remainder.startswith("/"):
|
|
346
|
+
return path
|
|
347
|
+
|
|
348
|
+
return f"/{remainder}"
|
|
@@ -6,7 +6,7 @@ from fastapi import Request
|
|
|
6
6
|
|
|
7
7
|
from agenta.sdk.utils.logging import get_module_logger
|
|
8
8
|
from agenta.sdk.utils.exceptions import suppress
|
|
9
|
-
from agenta.sdk.
|
|
9
|
+
from agenta.sdk.tracing.propagation import extract
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
log = get_module_logger(__name__)
|
|
@@ -127,7 +127,7 @@ class VaultMiddleware:
|
|
|
127
127
|
request: WorkflowServiceRequest,
|
|
128
128
|
call_next: Callable[[WorkflowServiceRequest], Any],
|
|
129
129
|
):
|
|
130
|
-
api_url =
|
|
130
|
+
api_url = ag.DEFAULT_AGENTA_SINGLETON_INSTANCE.api_url
|
|
131
131
|
|
|
132
132
|
with suppress():
|
|
133
133
|
ctx = RunningContext.get()
|
agenta/sdk/models/shared.py
CHANGED
agenta/sdk/tracing/exporters.py
CHANGED
agenta/sdk/tracing/processors.py
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
from typing import Optional, Dict, List
|
|
2
1
|
from threading import Lock
|
|
3
|
-
from
|
|
4
|
-
from uuid import UUID
|
|
2
|
+
from typing import Dict, List, Optional
|
|
5
3
|
|
|
4
|
+
|
|
5
|
+
from agenta.sdk.contexts.tracing import TracingContext
|
|
6
|
+
from agenta.sdk.utils.logging import get_module_logger
|
|
6
7
|
from opentelemetry.baggage import get_all as get_baggage
|
|
7
8
|
from opentelemetry.context import Context
|
|
8
9
|
from opentelemetry.sdk.trace import Span, SpanProcessor
|
|
9
10
|
from opentelemetry.sdk.trace.export import (
|
|
10
|
-
SpanExporter,
|
|
11
|
-
ReadableSpan,
|
|
12
11
|
BatchSpanProcessor,
|
|
12
|
+
ReadableSpan,
|
|
13
|
+
SpanExporter,
|
|
13
14
|
)
|
|
14
15
|
from opentelemetry.trace import SpanContext
|
|
15
16
|
|
|
16
17
|
from agenta.sdk.utils.logging import get_module_logger
|
|
17
|
-
from agenta.sdk.tracing
|
|
18
|
-
|
|
18
|
+
from agenta.sdk.models.tracing import BaseModel
|
|
19
19
|
from agenta.sdk.contexts.tracing import TracingContext
|
|
20
20
|
|
|
21
21
|
log = get_module_logger(__name__)
|
|
@@ -65,15 +65,38 @@ class TraceProcessor(SpanProcessor):
|
|
|
65
65
|
# )
|
|
66
66
|
|
|
67
67
|
for key in self.references.keys():
|
|
68
|
-
|
|
68
|
+
ref = self.references[key]
|
|
69
|
+
if isinstance(ref, BaseModel):
|
|
70
|
+
try:
|
|
71
|
+
ref = ref.model_dump(mode="json", exclude_none=True)
|
|
72
|
+
except Exception: # pylint: disable=bare-except
|
|
73
|
+
pass
|
|
74
|
+
if isinstance(ref, dict):
|
|
75
|
+
for field, value in ref.items():
|
|
76
|
+
span.set_attribute(f"ag.refs.{key}.{field}", str(value))
|
|
69
77
|
|
|
70
78
|
baggage = get_baggage(parent_context)
|
|
71
79
|
|
|
72
80
|
for key in baggage.keys():
|
|
73
|
-
if key.startswith("ag.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
if key.startswith("ag."):
|
|
82
|
+
value = baggage[key]
|
|
83
|
+
|
|
84
|
+
if key.startswith("ag.refs."):
|
|
85
|
+
ref = value
|
|
86
|
+
if isinstance(value, BaseModel):
|
|
87
|
+
try:
|
|
88
|
+
ref = value.model_dump(mode="json", exclude_none=True) # type: ignore
|
|
89
|
+
except Exception: # pylint: disable=bare-except
|
|
90
|
+
pass
|
|
91
|
+
if isinstance(ref, dict):
|
|
92
|
+
for field, val in ref.items():
|
|
93
|
+
span.set_attribute(f"{key}.{field}", str(val))
|
|
94
|
+
elif isinstance(ref, (str, bool, int, float, bytes)):
|
|
95
|
+
span.set_attribute(key, ref)
|
|
96
|
+
else:
|
|
97
|
+
# Not a reference - only set if it's a valid attribute type
|
|
98
|
+
if isinstance(value, (str, bool, int, float, bytes)):
|
|
99
|
+
span.set_attribute(key, value)
|
|
77
100
|
|
|
78
101
|
context = TracingContext.get()
|
|
79
102
|
|
|
@@ -105,10 +128,11 @@ class TraceProcessor(SpanProcessor):
|
|
|
105
128
|
if not self.inline:
|
|
106
129
|
if context.links:
|
|
107
130
|
for key, link in context.links.items():
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
131
|
+
if isinstance(link, BaseModel):
|
|
132
|
+
try:
|
|
133
|
+
link = link.model_dump(mode="json", exclude_none=True)
|
|
134
|
+
except Exception:
|
|
135
|
+
pass
|
|
112
136
|
if not isinstance(link, dict):
|
|
113
137
|
continue
|
|
114
138
|
if not link.get("trace_id") or not link.get("span_id"):
|
|
@@ -127,30 +151,14 @@ class TraceProcessor(SpanProcessor):
|
|
|
127
151
|
|
|
128
152
|
if context.references:
|
|
129
153
|
for key, ref in context.references.items():
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if ref.get("id"):
|
|
140
|
-
span.set_attribute(
|
|
141
|
-
f"ag.refs.{key}.id",
|
|
142
|
-
str(ref.get("id")),
|
|
143
|
-
)
|
|
144
|
-
if ref.get("slug"):
|
|
145
|
-
span.set_attribute(
|
|
146
|
-
f"ag.refs.{key}.slug",
|
|
147
|
-
str(ref.get("slug")),
|
|
148
|
-
)
|
|
149
|
-
if ref.get("version"):
|
|
150
|
-
span.set_attribute(
|
|
151
|
-
f"ag.refs.{key}.version",
|
|
152
|
-
str(ref.get("version")),
|
|
153
|
-
)
|
|
154
|
+
if isinstance(ref, BaseModel):
|
|
155
|
+
try:
|
|
156
|
+
ref = ref.model_dump(mode="json", exclude_none=True)
|
|
157
|
+
except Exception:
|
|
158
|
+
pass
|
|
159
|
+
if isinstance(ref, dict):
|
|
160
|
+
for field, value in ref.items():
|
|
161
|
+
span.set_attribute(f"ag.refs.{key}.{field}", str(value))
|
|
154
162
|
|
|
155
163
|
trace_id = span.context.trace_id
|
|
156
164
|
span_id = span.context.span_id
|
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Any, Dict, Optional, Tuple
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
from opentelemetry.baggage.propagation import W3CBaggagePropagator
|
|
5
|
-
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
|
|
3
|
+
from agenta.sdk.contexts.tracing import TracingContext
|
|
6
4
|
from opentelemetry.baggage import set_baggage
|
|
5
|
+
from opentelemetry.baggage.propagation import W3CBaggagePropagator
|
|
7
6
|
from opentelemetry.context import get_current
|
|
8
|
-
|
|
9
|
-
from agenta.sdk.contexts.tracing import TracingContext
|
|
10
|
-
|
|
11
|
-
import agenta as ag
|
|
7
|
+
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
|
|
12
8
|
|
|
13
9
|
|
|
14
10
|
def extract(
|
|
@@ -47,11 +43,12 @@ def extract(
|
|
|
47
43
|
baggage = {}
|
|
48
44
|
|
|
49
45
|
try:
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
raw_baggage = (
|
|
47
|
+
headers.get("Baggage") # Uppercase
|
|
52
48
|
or headers.get("baggage") # Lowercase
|
|
53
|
-
or ""
|
|
54
|
-
|
|
49
|
+
or ""
|
|
50
|
+
)
|
|
51
|
+
_carrier = {"baggage": raw_baggage}
|
|
55
52
|
|
|
56
53
|
_context = W3CBaggagePropagator().extract(_carrier)
|
|
57
54
|
|
agenta/sdk/tracing/tracing.py
CHANGED
|
@@ -31,6 +31,7 @@ from agenta.sdk.tracing.conventions import Reference, is_valid_attribute_key
|
|
|
31
31
|
from agenta.sdk.tracing.propagation import extract, inject
|
|
32
32
|
from agenta.sdk.utils.cache import TTLLRUCache
|
|
33
33
|
|
|
34
|
+
import agenta as ag
|
|
34
35
|
|
|
35
36
|
log = get_module_logger(__name__)
|
|
36
37
|
|
|
@@ -215,6 +216,42 @@ class Tracing(metaclass=Singleton):
|
|
|
215
216
|
namespace="metrics",
|
|
216
217
|
)
|
|
217
218
|
|
|
219
|
+
def store_session(
|
|
220
|
+
self,
|
|
221
|
+
session_id: Optional[str] = None,
|
|
222
|
+
span: Optional[Span] = None,
|
|
223
|
+
):
|
|
224
|
+
"""Set session attributes on the current span.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
session_id: Unique identifier for the session
|
|
228
|
+
span: Optional span to set attributes on (defaults to current span)
|
|
229
|
+
"""
|
|
230
|
+
with suppress():
|
|
231
|
+
if span is None:
|
|
232
|
+
span = self.get_current_span()
|
|
233
|
+
|
|
234
|
+
if session_id:
|
|
235
|
+
span.set_attribute("id", session_id, namespace="session")
|
|
236
|
+
|
|
237
|
+
def store_user(
|
|
238
|
+
self,
|
|
239
|
+
user_id: Optional[str] = None,
|
|
240
|
+
span: Optional[Span] = None,
|
|
241
|
+
):
|
|
242
|
+
"""Set user attributes on the current span.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
user_id: Unique identifier for the user
|
|
246
|
+
span: Optional span to set attributes on (defaults to current span)
|
|
247
|
+
"""
|
|
248
|
+
with suppress():
|
|
249
|
+
if span is None:
|
|
250
|
+
span = self.get_current_span()
|
|
251
|
+
|
|
252
|
+
if user_id:
|
|
253
|
+
span.set_attribute("id", user_id, namespace="user")
|
|
254
|
+
|
|
218
255
|
def is_inline_trace_ready(
|
|
219
256
|
self,
|
|
220
257
|
trace_id: Optional[int] = None,
|
|
@@ -314,6 +351,58 @@ class Tracing(metaclass=Singleton):
|
|
|
314
351
|
|
|
315
352
|
return None
|
|
316
353
|
|
|
354
|
+
def get_trace_url(
|
|
355
|
+
self,
|
|
356
|
+
trace_id: Optional[str] = None,
|
|
357
|
+
) -> str:
|
|
358
|
+
"""
|
|
359
|
+
Build a URL to view a trace in the Agenta UI.
|
|
360
|
+
|
|
361
|
+
Automatically extracts the trace ID from the current tracing context
|
|
362
|
+
if not explicitly provided.
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
trace_id: Optional trace ID (hex string format). If not provided,
|
|
366
|
+
it will be automatically extracted from the current trace context.
|
|
367
|
+
|
|
368
|
+
Returns:
|
|
369
|
+
The full URL to view the trace in the observability dashboard
|
|
370
|
+
|
|
371
|
+
Raises:
|
|
372
|
+
RuntimeError: If the SDK is not initialized, no active trace context exists,
|
|
373
|
+
or scope info cannot be fetched
|
|
374
|
+
"""
|
|
375
|
+
if trace_id is None:
|
|
376
|
+
span_ctx = self.get_span_context()
|
|
377
|
+
if span_ctx is None or not span_ctx.is_valid:
|
|
378
|
+
raise RuntimeError(
|
|
379
|
+
"No active trace context found. "
|
|
380
|
+
"Make sure you call this within an instrumented function or span."
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
trace_id = f"{span_ctx.trace_id:032x}"
|
|
384
|
+
|
|
385
|
+
if not ag or not ag.DEFAULT_AGENTA_SINGLETON_INSTANCE:
|
|
386
|
+
raise RuntimeError(
|
|
387
|
+
"Agenta SDK is not initialized. Please call ag.init() first."
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
api_url = ag.DEFAULT_AGENTA_SINGLETON_INSTANCE.api_url
|
|
391
|
+
web_url = api_url.replace("/api", "") if api_url else None
|
|
392
|
+
|
|
393
|
+
(organization_id, workspace_id, project_id) = (
|
|
394
|
+
ag.DEFAULT_AGENTA_SINGLETON_INSTANCE.resolve_scopes()
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
if not web_url or not workspace_id or not project_id:
|
|
398
|
+
raise RuntimeError(
|
|
399
|
+
"Could not determine workspace/project context. Please call ag.init() first."
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
return (
|
|
403
|
+
f"{web_url}/w/{workspace_id}/p/{project_id}/observability?trace={trace_id}"
|
|
404
|
+
)
|
|
405
|
+
|
|
317
406
|
|
|
318
407
|
def get_tracer(
|
|
319
408
|
tracing: Tracing,
|
agenta/sdk/types.py
CHANGED
|
@@ -8,7 +8,7 @@ from pydantic import BaseModel, Field, model_validator, AliasChoices
|
|
|
8
8
|
from starlette.responses import StreamingResponse
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
from agenta.sdk.assets import supported_llm_models
|
|
11
|
+
from agenta.sdk.assets import supported_llm_models, model_metadata
|
|
12
12
|
from agenta.client.backend.types import AgentaNodesResponse, AgentaNodeDto
|
|
13
13
|
|
|
14
14
|
|
|
@@ -23,7 +23,11 @@ def MCField( # pylint: disable=invalid-name
|
|
|
23
23
|
) -> Field:
|
|
24
24
|
# Pydantic 2.12+ no longer allows post-creation mutation of field properties
|
|
25
25
|
if isinstance(choices, dict):
|
|
26
|
-
json_extra = {
|
|
26
|
+
json_extra = {
|
|
27
|
+
"choices": choices,
|
|
28
|
+
"x-parameter": "grouped_choice",
|
|
29
|
+
"x-model-metadata": model_metadata,
|
|
30
|
+
}
|
|
27
31
|
elif isinstance(choices, list):
|
|
28
32
|
json_extra = {"choices": choices, "x-parameter": "choice"}
|
|
29
33
|
else:
|
|
@@ -88,7 +88,6 @@ class DaytonaRunner(CodeRunner):
|
|
|
88
88
|
target=target,
|
|
89
89
|
)
|
|
90
90
|
self.daytona = Daytona(config)
|
|
91
|
-
# log.debug("Daytona client initialized")
|
|
92
91
|
|
|
93
92
|
except Exception as e:
|
|
94
93
|
raise RuntimeError(f"Failed to initialize Daytona client: {e}")
|
|
@@ -107,8 +106,6 @@ class DaytonaRunner(CodeRunner):
|
|
|
107
106
|
"Set it to the Daytona sandbox ID or snapshot name you want to use."
|
|
108
107
|
)
|
|
109
108
|
|
|
110
|
-
# log.debug(f"Creating sandbox from snapshot: {snapshot_id}")
|
|
111
|
-
|
|
112
109
|
from daytona import CreateSandboxFromSnapshotParams
|
|
113
110
|
|
|
114
111
|
sandbox = self.daytona.create(
|
|
@@ -118,9 +115,6 @@ class DaytonaRunner(CodeRunner):
|
|
|
118
115
|
)
|
|
119
116
|
)
|
|
120
117
|
|
|
121
|
-
# log.debug(
|
|
122
|
-
# f"Sandbox created: {sandbox.id if hasattr(sandbox, 'id') else sandbox}"
|
|
123
|
-
# )
|
|
124
118
|
return sandbox
|
|
125
119
|
|
|
126
120
|
except Exception as e:
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
2
4
|
from agenta.sdk.workflows.runners.base import CodeRunner
|
|
3
5
|
from agenta.sdk.workflows.runners.local import LocalRunner
|
|
4
|
-
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from agenta.sdk.workflows.runners.daytona import DaytonaRunner
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _get_daytona_runner() -> "DaytonaRunner":
|
|
12
|
+
from agenta.sdk.workflows.runners.daytona import DaytonaRunner
|
|
13
|
+
|
|
14
|
+
return DaytonaRunner()
|
|
5
15
|
|
|
6
16
|
|
|
7
17
|
def get_runner() -> CodeRunner:
|
|
@@ -21,7 +31,14 @@ def get_runner() -> CodeRunner:
|
|
|
21
31
|
runner_type = os.getenv("AGENTA_SERVICES_SANDBOX_RUNNER", "local").lower()
|
|
22
32
|
|
|
23
33
|
if runner_type == "daytona":
|
|
24
|
-
|
|
34
|
+
try:
|
|
35
|
+
return _get_daytona_runner()
|
|
36
|
+
except ImportError as exc:
|
|
37
|
+
raise ValueError(
|
|
38
|
+
"Daytona runner requires the 'daytona' package. "
|
|
39
|
+
"Install optional dependencies or set "
|
|
40
|
+
"AGENTA_SERVICES_SANDBOX_RUNNER=local."
|
|
41
|
+
) from exc
|
|
25
42
|
elif runner_type == "local":
|
|
26
43
|
return LocalRunner()
|
|
27
44
|
else:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agenta
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.72.4
|
|
4
4
|
Summary: The SDK for agenta is an open-source LLMOps platform.
|
|
5
5
|
Keywords: LLMOps,LLM,evaluation,prompt engineering
|
|
6
6
|
Author: Mahmoud Mabrouk
|
|
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.9
|
|
17
17
|
Classifier: Topic :: Software Development :: Libraries
|
|
18
18
|
Requires-Dist: daytona (>=0.121.0,<0.122.0)
|
|
19
|
-
Requires-Dist: fastapi (>=0.
|
|
19
|
+
Requires-Dist: fastapi (>=0.125)
|
|
20
20
|
Requires-Dist: httpx (>=0.28,<0.29)
|
|
21
21
|
Requires-Dist: importlib-metadata (>=8,<9)
|
|
22
22
|
Requires-Dist: jinja2 (>=3,<4)
|
|
@@ -26,6 +26,7 @@ Requires-Dist: opentelemetry-api (>=1,<2)
|
|
|
26
26
|
Requires-Dist: opentelemetry-exporter-otlp-proto-http (>=1,<2)
|
|
27
27
|
Requires-Dist: opentelemetry-instrumentation (>=0.59b0,<0.60)
|
|
28
28
|
Requires-Dist: opentelemetry-sdk (>=1,<2)
|
|
29
|
+
Requires-Dist: orjson (>=3,<4)
|
|
29
30
|
Requires-Dist: pydantic (>=2,<3)
|
|
30
31
|
Requires-Dist: python-dotenv (>=1,<2)
|
|
31
32
|
Requires-Dist: python-jsonpath (>=2,<3)
|