agenta 0.34.6__py3-none-any.whl → 0.35.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.
Potentially problematic release.
This version of agenta might be problematic. Click here for more details.
- agenta/sdk/agenta_init.py +0 -8
- agenta/sdk/context/tracing.py +2 -0
- agenta/sdk/decorators/routing.py +22 -3
- agenta/sdk/decorators/tracing.py +47 -14
- agenta/sdk/litellm/mocks/__init__.py +13 -0
- agenta/sdk/middleware/auth.py +2 -6
- agenta/sdk/middleware/config.py +24 -19
- agenta/sdk/middleware/inline.py +0 -2
- agenta/sdk/middleware/mock.py +0 -1
- agenta/sdk/middleware/otel.py +5 -20
- agenta/sdk/middleware/vault.py +3 -3
- agenta/sdk/tracing/exporters.py +36 -16
- agenta/sdk/tracing/inline.py +1 -1
- agenta/sdk/tracing/processors.py +62 -29
- agenta/sdk/tracing/propagation.py +100 -0
- agenta/sdk/tracing/tracing.py +40 -27
- agenta/sdk/utils/cache.py +53 -0
- {agenta-0.34.6.dist-info → agenta-0.35.0.dist-info}/METADATA +4 -4
- {agenta-0.34.6.dist-info → agenta-0.35.0.dist-info}/RECORD +21 -20
- agenta/sdk/middleware/cache.py +0 -47
- {agenta-0.34.6.dist-info → agenta-0.35.0.dist-info}/WHEEL +0 -0
- {agenta-0.34.6.dist-info → agenta-0.35.0.dist-info}/entry_points.txt +0 -0
agenta/sdk/agenta_init.py
CHANGED
|
@@ -89,11 +89,6 @@ class AgentaSingleton:
|
|
|
89
89
|
|
|
90
90
|
self.base_id = getenv("AGENTA_BASE_ID")
|
|
91
91
|
|
|
92
|
-
self.service_id = getenv("AGENTA_SERVICE_ID") or self.base_id or None
|
|
93
|
-
|
|
94
|
-
log.info("Agenta - Service ID: %s", self.service_id)
|
|
95
|
-
log.info("Agenta - Application ID: %s", self.app_id)
|
|
96
|
-
|
|
97
92
|
self.tracing = Tracing(
|
|
98
93
|
url=f"{self.host}/api/observability/v1/otlp/traces", # type: ignore
|
|
99
94
|
redact=redact,
|
|
@@ -102,9 +97,6 @@ class AgentaSingleton:
|
|
|
102
97
|
|
|
103
98
|
self.tracing.configure(
|
|
104
99
|
api_key=self.api_key,
|
|
105
|
-
service_id=self.service_id,
|
|
106
|
-
# DEPRECATING
|
|
107
|
-
app_id=self.app_id,
|
|
108
100
|
)
|
|
109
101
|
|
|
110
102
|
self.api = AgentaApi(
|
agenta/sdk/context/tracing.py
CHANGED
|
@@ -7,6 +7,8 @@ from pydantic import BaseModel
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class TracingContext(BaseModel):
|
|
10
|
+
traceparent: Optional[Dict[str, Any]] = None
|
|
11
|
+
baggage: Optional[Dict[str, Any]] = None
|
|
10
12
|
credentials: Optional[str] = None
|
|
11
13
|
parameters: Optional[Dict[str, Any]] = None
|
|
12
14
|
references: Optional[Dict[str, Any]] = None
|
agenta/sdk/decorators/routing.py
CHANGED
|
@@ -330,6 +330,8 @@ class entrypoint:
|
|
|
330
330
|
raise HTTPException(status_code=500, detail="Missing 'request'.")
|
|
331
331
|
|
|
332
332
|
state = request.state
|
|
333
|
+
traceparent = state.otel.get("traceparent")
|
|
334
|
+
baggage = state.otel.get("baggage")
|
|
333
335
|
credentials = state.auth.get("credentials")
|
|
334
336
|
parameters = state.config.get("parameters")
|
|
335
337
|
references = state.config.get("references")
|
|
@@ -346,6 +348,8 @@ class entrypoint:
|
|
|
346
348
|
):
|
|
347
349
|
with tracing_context_manager(
|
|
348
350
|
context=TracingContext(
|
|
351
|
+
traceparent=traceparent,
|
|
352
|
+
baggage=baggage,
|
|
349
353
|
credentials=credentials,
|
|
350
354
|
parameters=parameters,
|
|
351
355
|
references=references,
|
|
@@ -378,14 +382,13 @@ class entrypoint:
|
|
|
378
382
|
content_type = "application/json"
|
|
379
383
|
data = self.patch_result(result)
|
|
380
384
|
|
|
381
|
-
|
|
382
|
-
tree, tree_id = await self.fetch_inline_trace(inline)
|
|
385
|
+
tree, tree_id = await self.fetch_inline_trace(inline)
|
|
383
386
|
|
|
384
387
|
try:
|
|
385
388
|
return BaseResponse(
|
|
386
389
|
data=data, tree=tree, content_type=content_type, tree_id=tree_id
|
|
387
390
|
)
|
|
388
|
-
except:
|
|
391
|
+
except: # pylint: disable=bare-except
|
|
389
392
|
return BaseResponse(data=data, content_type=content_type)
|
|
390
393
|
|
|
391
394
|
def handle_failure(
|
|
@@ -397,6 +400,9 @@ class entrypoint:
|
|
|
397
400
|
status_code = (
|
|
398
401
|
getattr(error, "status_code") if hasattr(error, "status_code") else 500
|
|
399
402
|
)
|
|
403
|
+
if status_code in [401, 403]: # Reserved HTTP codes for auth middleware
|
|
404
|
+
status_code = 424 # Proxy Authentication Required
|
|
405
|
+
|
|
400
406
|
stacktrace = format_exception(error, value=error, tb=error.__traceback__) # type: ignore
|
|
401
407
|
|
|
402
408
|
raise HTTPException(
|
|
@@ -444,6 +450,18 @@ class entrypoint:
|
|
|
444
450
|
|
|
445
451
|
return data
|
|
446
452
|
|
|
453
|
+
async def fetch_inline_trace_id(
|
|
454
|
+
self,
|
|
455
|
+
):
|
|
456
|
+
context = tracing_context.get()
|
|
457
|
+
|
|
458
|
+
link = context.link
|
|
459
|
+
|
|
460
|
+
_tree_id = link.get("tree_id") if link else None # in int format
|
|
461
|
+
tree_id = str(UUID(int=_tree_id)) if _tree_id else None # in uuid_as_str format
|
|
462
|
+
|
|
463
|
+
return tree_id
|
|
464
|
+
|
|
447
465
|
async def fetch_inline_trace(
|
|
448
466
|
self,
|
|
449
467
|
inline: bool,
|
|
@@ -472,6 +490,7 @@ class entrypoint:
|
|
|
472
490
|
remaining_steps -= 1
|
|
473
491
|
|
|
474
492
|
tree = ag.tracing.get_inline_trace(_tree_id)
|
|
493
|
+
|
|
475
494
|
return tree, tree_id
|
|
476
495
|
|
|
477
496
|
# --- OpenAPI --- #
|
agenta/sdk/decorators/tracing.py
CHANGED
|
@@ -4,8 +4,9 @@ from functools import wraps
|
|
|
4
4
|
from itertools import chain
|
|
5
5
|
from inspect import iscoroutinefunction, getfullargspec
|
|
6
6
|
|
|
7
|
-
from opentelemetry import baggage
|
|
8
|
-
from opentelemetry.context import attach, detach
|
|
7
|
+
from opentelemetry import baggage
|
|
8
|
+
from opentelemetry.context import attach, detach, get_current
|
|
9
|
+
from opentelemetry.baggage import set_baggage, get_all
|
|
9
10
|
|
|
10
11
|
from agenta.sdk.utils.exceptions import suppress
|
|
11
12
|
from agenta.sdk.context.tracing import tracing_context
|
|
@@ -49,7 +50,15 @@ class instrument: # pylint: disable=invalid-name
|
|
|
49
50
|
|
|
50
51
|
token = self._attach_baggage()
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
ctx = self._get_traceparent()
|
|
54
|
+
|
|
55
|
+
with ag.tracer.start_as_current_span(
|
|
56
|
+
name=func.__name__,
|
|
57
|
+
kind=self.kind,
|
|
58
|
+
context=ctx,
|
|
59
|
+
):
|
|
60
|
+
self._set_link()
|
|
61
|
+
|
|
53
62
|
self._pre_instrument(func, *args, **kwargs)
|
|
54
63
|
|
|
55
64
|
result = await func(*args, **kwargs)
|
|
@@ -69,7 +78,15 @@ class instrument: # pylint: disable=invalid-name
|
|
|
69
78
|
|
|
70
79
|
token = self._attach_baggage()
|
|
71
80
|
|
|
72
|
-
|
|
81
|
+
ctx = self._get_traceparent()
|
|
82
|
+
|
|
83
|
+
with ag.tracer.start_as_current_span(
|
|
84
|
+
name=func.__name__,
|
|
85
|
+
kind=self.kind,
|
|
86
|
+
context=ctx,
|
|
87
|
+
):
|
|
88
|
+
self._set_link()
|
|
89
|
+
|
|
73
90
|
self._pre_instrument(func, *args, **kwargs)
|
|
74
91
|
|
|
75
92
|
result = func(*args, **kwargs)
|
|
@@ -90,6 +107,30 @@ class instrument: # pylint: disable=invalid-name
|
|
|
90
107
|
|
|
91
108
|
self.kind = parse_span_kind(self.type)
|
|
92
109
|
|
|
110
|
+
def _get_traceparent(self):
|
|
111
|
+
context = tracing_context.get()
|
|
112
|
+
|
|
113
|
+
traceparent = context.traceparent
|
|
114
|
+
|
|
115
|
+
if not context.link:
|
|
116
|
+
for key, value in get_all(get_current()).items():
|
|
117
|
+
traceparent = set_baggage(name=key, value=value, context=traceparent)
|
|
118
|
+
|
|
119
|
+
return traceparent
|
|
120
|
+
|
|
121
|
+
def _set_link(self):
|
|
122
|
+
span = ag.tracing.get_current_span()
|
|
123
|
+
|
|
124
|
+
context = tracing_context.get()
|
|
125
|
+
|
|
126
|
+
if not context.link:
|
|
127
|
+
context.link = {
|
|
128
|
+
"tree_id": span.get_span_context().trace_id,
|
|
129
|
+
"node_id": span.get_span_context().span_id,
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
tracing_context.set(context)
|
|
133
|
+
|
|
93
134
|
def _attach_baggage(self):
|
|
94
135
|
context = tracing_context.get()
|
|
95
136
|
|
|
@@ -122,7 +163,7 @@ class instrument: # pylint: disable=invalid-name
|
|
|
122
163
|
with suppress():
|
|
123
164
|
trace_id = span.context.trace_id
|
|
124
165
|
|
|
125
|
-
ag.tracing.credentials
|
|
166
|
+
ag.tracing.credentials.put(trace_id, context.credentials)
|
|
126
167
|
|
|
127
168
|
span.set_attributes(
|
|
128
169
|
attributes={"node": self.type},
|
|
@@ -153,6 +194,7 @@ class instrument: # pylint: disable=invalid-name
|
|
|
153
194
|
result,
|
|
154
195
|
):
|
|
155
196
|
span = ag.tracing.get_current_span()
|
|
197
|
+
|
|
156
198
|
with suppress():
|
|
157
199
|
cost = None
|
|
158
200
|
usage = {}
|
|
@@ -206,15 +248,6 @@ class instrument: # pylint: disable=invalid-name
|
|
|
206
248
|
|
|
207
249
|
span.set_status("OK")
|
|
208
250
|
|
|
209
|
-
with suppress():
|
|
210
|
-
if hasattr(span, "parent") and span.parent is None:
|
|
211
|
-
context = tracing_context.get()
|
|
212
|
-
context.link = {
|
|
213
|
-
"tree_id": span.get_span_context().trace_id,
|
|
214
|
-
"node_id": span.get_span_context().span_id,
|
|
215
|
-
}
|
|
216
|
-
tracing_context.set(context)
|
|
217
|
-
|
|
218
251
|
def _parse(
|
|
219
252
|
self,
|
|
220
253
|
func,
|
|
@@ -26,7 +26,20 @@ def hello_mock_response(*args, **kwargs) -> MockResponseModel:
|
|
|
26
26
|
],
|
|
27
27
|
)
|
|
28
28
|
|
|
29
|
+
def chat_mock_response(*args, **kwargs) -> MockResponseModel:
|
|
30
|
+
return MockResponseModel(
|
|
31
|
+
choices=[
|
|
32
|
+
MockChoiceModel(
|
|
33
|
+
message=MockMessageModel(
|
|
34
|
+
content="world",
|
|
35
|
+
role="assistant"
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
],
|
|
39
|
+
)
|
|
40
|
+
|
|
29
41
|
|
|
30
42
|
MOCKS: dict[str, Callable[..., MockResponseModel]] = {
|
|
31
43
|
"hello": hello_mock_response,
|
|
44
|
+
"chat": chat_mock_response
|
|
32
45
|
}
|
agenta/sdk/middleware/auth.py
CHANGED
|
@@ -8,7 +8,7 @@ from starlette.middleware.base import BaseHTTPMiddleware
|
|
|
8
8
|
from fastapi import FastAPI, Request
|
|
9
9
|
from fastapi.responses import JSONResponse
|
|
10
10
|
|
|
11
|
-
from agenta.sdk.
|
|
11
|
+
from agenta.sdk.utils.cache import TTLLRUCache
|
|
12
12
|
from agenta.sdk.utils.constants import TRUTHY
|
|
13
13
|
from agenta.sdk.utils.exceptions import display_exception
|
|
14
14
|
from agenta.sdk.utils.logging import log
|
|
@@ -22,7 +22,7 @@ _CACHE_ENABLED = getenv("AGENTA_MIDDLEWARE_CACHE_ENABLED", "false").lower() in T
|
|
|
22
22
|
|
|
23
23
|
_ALWAYS_ALLOW_LIST = [f"{AGENTA_RUNTIME_PREFIX}/health"]
|
|
24
24
|
|
|
25
|
-
_cache = TTLLRUCache(
|
|
25
|
+
_cache = TTLLRUCache()
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class DenyResponse(JSONResponse):
|
|
@@ -54,7 +54,6 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|
|
54
54
|
super().__init__(app)
|
|
55
55
|
|
|
56
56
|
self.host = ag.DEFAULT_AGENTA_SINGLETON_INSTANCE.host
|
|
57
|
-
self.resource_id = ag.DEFAULT_AGENTA_SINGLETON_INSTANCE.service_id
|
|
58
57
|
|
|
59
58
|
async def dispatch(self, request: Request, call_next: Callable):
|
|
60
59
|
try:
|
|
@@ -111,9 +110,6 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|
|
111
110
|
|
|
112
111
|
params = {"action": "run_service", "resource_type": "service"}
|
|
113
112
|
|
|
114
|
-
if self.resource_id:
|
|
115
|
-
params["resource_id"] = self.resource_id
|
|
116
|
-
|
|
117
113
|
if project_id:
|
|
118
114
|
params["project_id"] = project_id
|
|
119
115
|
|
agenta/sdk/middleware/config.py
CHANGED
|
@@ -10,7 +10,7 @@ from fastapi import Request, FastAPI
|
|
|
10
10
|
|
|
11
11
|
import httpx
|
|
12
12
|
|
|
13
|
-
from agenta.sdk.
|
|
13
|
+
from agenta.sdk.utils.cache import TTLLRUCache
|
|
14
14
|
from agenta.sdk.utils.constants import TRUTHY
|
|
15
15
|
from agenta.sdk.utils.exceptions import suppress
|
|
16
16
|
|
|
@@ -19,7 +19,7 @@ import agenta as ag
|
|
|
19
19
|
|
|
20
20
|
_CACHE_ENABLED = getenv("AGENTA_MIDDLEWARE_CACHE_ENABLED", "false").lower() in TRUTHY
|
|
21
21
|
|
|
22
|
-
_cache = TTLLRUCache(
|
|
22
|
+
_cache = TTLLRUCache()
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class Reference(BaseModel):
|
|
@@ -147,37 +147,32 @@ class ConfigMiddleware(BaseHTTPMiddleware):
|
|
|
147
147
|
|
|
148
148
|
application_id = (
|
|
149
149
|
# CLEANEST
|
|
150
|
-
baggage.get("
|
|
150
|
+
baggage.get("ag.refs.application.id")
|
|
151
151
|
# ALTERNATIVE
|
|
152
152
|
or request.query_params.get("application_id")
|
|
153
153
|
# LEGACY
|
|
154
|
+
or baggage.get("application_id")
|
|
154
155
|
or request.query_params.get("app_id")
|
|
155
156
|
or self.application_id
|
|
156
157
|
)
|
|
157
158
|
application_slug = (
|
|
158
159
|
# CLEANEST
|
|
159
|
-
baggage.get("
|
|
160
|
+
baggage.get("ag.refs.application.slug")
|
|
160
161
|
# ALTERNATIVE
|
|
161
162
|
or request.query_params.get("application_slug")
|
|
162
163
|
# LEGACY
|
|
164
|
+
or baggage.get("application_slug")
|
|
163
165
|
or request.query_params.get("app_slug")
|
|
164
166
|
or body.get("app")
|
|
165
167
|
)
|
|
166
168
|
|
|
167
|
-
|
|
168
|
-
# CLEANEST
|
|
169
|
-
baggage.get("application_version")
|
|
170
|
-
# ALTERNATIVE
|
|
171
|
-
or request.query_params.get("application_version")
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
if not any([application_id, application_slug, application_version]):
|
|
169
|
+
if not any([application_id, application_slug, None]):
|
|
175
170
|
return None
|
|
176
171
|
|
|
177
172
|
return Reference(
|
|
178
173
|
id=application_id,
|
|
179
174
|
slug=application_slug,
|
|
180
|
-
version=
|
|
175
|
+
version=None,
|
|
181
176
|
)
|
|
182
177
|
|
|
183
178
|
async def _parse_variant_ref(
|
|
@@ -194,24 +189,29 @@ class ConfigMiddleware(BaseHTTPMiddleware):
|
|
|
194
189
|
|
|
195
190
|
variant_id = (
|
|
196
191
|
# CLEANEST
|
|
197
|
-
baggage.get("
|
|
192
|
+
baggage.get("ag.refs.variant.id")
|
|
198
193
|
# ALTERNATIVE
|
|
199
194
|
or request.query_params.get("variant_id")
|
|
195
|
+
# LEGACY
|
|
196
|
+
or baggage.get("variant_id")
|
|
200
197
|
)
|
|
201
198
|
variant_slug = (
|
|
202
199
|
# CLEANEST
|
|
203
|
-
baggage.get("
|
|
200
|
+
baggage.get("ag.refs.variant.slug")
|
|
204
201
|
# ALTERNATIVE
|
|
205
202
|
or request.query_params.get("variant_slug")
|
|
206
203
|
# LEGACY
|
|
204
|
+
or baggage.get("variant_slug")
|
|
207
205
|
or request.query_params.get("config")
|
|
208
206
|
or body.get("config")
|
|
209
207
|
)
|
|
210
208
|
variant_version = (
|
|
211
209
|
# CLEANEST
|
|
212
|
-
baggage.get("
|
|
210
|
+
baggage.get("ag.refs.variant.version")
|
|
213
211
|
# ALTERNATIVE
|
|
214
212
|
or request.query_params.get("variant_version")
|
|
213
|
+
# LEGACY
|
|
214
|
+
or baggage.get("variant_version")
|
|
215
215
|
)
|
|
216
216
|
|
|
217
217
|
if not any([variant_id, variant_slug, variant_version]):
|
|
@@ -237,24 +237,29 @@ class ConfigMiddleware(BaseHTTPMiddleware):
|
|
|
237
237
|
|
|
238
238
|
environment_id = (
|
|
239
239
|
# CLEANEST
|
|
240
|
-
baggage.get("
|
|
240
|
+
baggage.get("ag.refs.environment.id")
|
|
241
241
|
# ALTERNATIVE
|
|
242
242
|
or request.query_params.get("environment_id")
|
|
243
|
+
# LEGACY
|
|
244
|
+
or baggage.get("environment_id")
|
|
243
245
|
)
|
|
244
246
|
environment_slug = (
|
|
245
247
|
# CLEANEST
|
|
246
|
-
baggage.get("
|
|
248
|
+
baggage.get("ag.refs.environment.slug")
|
|
247
249
|
# ALTERNATIVE
|
|
248
250
|
or request.query_params.get("environment_slug")
|
|
249
251
|
# LEGACY
|
|
252
|
+
or baggage.get("environment_slug")
|
|
250
253
|
or request.query_params.get("environment")
|
|
251
254
|
or body.get("environment")
|
|
252
255
|
)
|
|
253
256
|
environment_version = (
|
|
254
257
|
# CLEANEST
|
|
255
|
-
baggage.get("
|
|
258
|
+
baggage.get("ag.refs.environment.version")
|
|
256
259
|
# ALTERNATIVE
|
|
257
260
|
or request.query_params.get("environment_version")
|
|
261
|
+
# LEGACY
|
|
262
|
+
or baggage.get("environment_version")
|
|
258
263
|
)
|
|
259
264
|
|
|
260
265
|
if not any([environment_id, environment_slug, environment_version]):
|
agenta/sdk/middleware/inline.py
CHANGED
agenta/sdk/middleware/mock.py
CHANGED
agenta/sdk/middleware/otel.py
CHANGED
|
@@ -4,8 +4,10 @@ from starlette.middleware.base import BaseHTTPMiddleware
|
|
|
4
4
|
from fastapi import Request, FastAPI
|
|
5
5
|
|
|
6
6
|
from opentelemetry.baggage.propagation import W3CBaggagePropagator
|
|
7
|
+
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
|
|
7
8
|
|
|
8
9
|
from agenta.sdk.utils.exceptions import suppress
|
|
10
|
+
from agenta.sdk.tracing.propagation import extract
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
class OTelMiddleware(BaseHTTPMiddleware):
|
|
@@ -13,28 +15,11 @@ class OTelMiddleware(BaseHTTPMiddleware):
|
|
|
13
15
|
super().__init__(app)
|
|
14
16
|
|
|
15
17
|
async def dispatch(self, request: Request, call_next: Callable):
|
|
16
|
-
request.state.otel = {"baggage": {}}
|
|
18
|
+
request.state.otel = {"baggage": {}, "traceparent": None}
|
|
17
19
|
|
|
18
20
|
with suppress():
|
|
19
|
-
baggage =
|
|
21
|
+
_, traceparent, baggage = extract(request.headers)
|
|
20
22
|
|
|
21
|
-
request.state.otel = {"baggage": baggage}
|
|
23
|
+
request.state.otel = {"baggage": baggage, "traceparent": traceparent}
|
|
22
24
|
|
|
23
25
|
return await call_next(request)
|
|
24
|
-
|
|
25
|
-
async def _get_baggage(
|
|
26
|
-
self,
|
|
27
|
-
request,
|
|
28
|
-
):
|
|
29
|
-
_baggage = {"baggage": request.headers.get("Baggage", "")}
|
|
30
|
-
|
|
31
|
-
context = W3CBaggagePropagator().extract(_baggage)
|
|
32
|
-
|
|
33
|
-
baggage = {}
|
|
34
|
-
|
|
35
|
-
if context:
|
|
36
|
-
for partial in context.values():
|
|
37
|
-
for key, value in partial.items():
|
|
38
|
-
baggage[key] = value
|
|
39
|
-
|
|
40
|
-
return baggage
|
agenta/sdk/middleware/vault.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from os import getenv
|
|
2
2
|
from json import dumps
|
|
3
|
-
from typing import Callable, Dict, Optional, List, Any
|
|
3
|
+
from typing import Callable, Dict, Optional, List, Any
|
|
4
4
|
|
|
5
5
|
import httpx
|
|
6
6
|
from fastapi import FastAPI, Request
|
|
@@ -13,7 +13,7 @@ from agenta.client.backend.types.secret_dto import SecretDto as SecretDTO
|
|
|
13
13
|
from agenta.client.backend.types.provider_key_dto import (
|
|
14
14
|
ProviderKeyDto as ProviderKeyDTO,
|
|
15
15
|
)
|
|
16
|
-
from agenta.sdk.
|
|
16
|
+
from agenta.sdk.utils.cache import TTLLRUCache
|
|
17
17
|
|
|
18
18
|
import agenta as ag
|
|
19
19
|
|
|
@@ -26,7 +26,7 @@ for arg in ProviderKind.__args__: # type: ignore
|
|
|
26
26
|
|
|
27
27
|
_CACHE_ENABLED = getenv("AGENTA_MIDDLEWARE_CACHE_ENABLED", "false").lower() in TRUTHY
|
|
28
28
|
|
|
29
|
-
_cache = TTLLRUCache(
|
|
29
|
+
_cache = TTLLRUCache()
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
class VaultMiddleware(BaseHTTPMiddleware):
|
agenta/sdk/tracing/exporters.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Sequence, Dict, List
|
|
1
|
+
from typing import Sequence, Dict, List, Optional
|
|
2
2
|
|
|
3
3
|
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
4
4
|
from opentelemetry.sdk.trace.export import (
|
|
@@ -14,10 +14,14 @@ from agenta.sdk.context.exporting import (
|
|
|
14
14
|
exporting_context,
|
|
15
15
|
ExportingContext,
|
|
16
16
|
)
|
|
17
|
+
from agenta.sdk.utils.cache import TTLLRUCache
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
class InlineTraceExporter(SpanExporter):
|
|
20
|
-
def __init__(
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
registry: Dict[str, List[ReadableSpan]],
|
|
24
|
+
):
|
|
21
25
|
self._shutdown = False
|
|
22
26
|
self._registry = registry
|
|
23
27
|
|
|
@@ -65,29 +69,45 @@ class InlineTraceExporter(SpanExporter):
|
|
|
65
69
|
class OTLPExporter(OTLPSpanExporter):
|
|
66
70
|
_MAX_RETRY_TIMEOUT = 2
|
|
67
71
|
|
|
68
|
-
def __init__(
|
|
72
|
+
def __init__(
|
|
73
|
+
self,
|
|
74
|
+
*args,
|
|
75
|
+
credentials: Optional[TTLLRUCache] = None,
|
|
76
|
+
**kwargs,
|
|
77
|
+
):
|
|
69
78
|
super().__init__(*args, **kwargs)
|
|
70
79
|
|
|
71
80
|
self.credentials = credentials
|
|
72
81
|
|
|
73
82
|
def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
|
|
74
|
-
|
|
83
|
+
grouped_spans: Dict[str, List[str]] = {}
|
|
84
|
+
|
|
85
|
+
for span in spans:
|
|
86
|
+
trace_id = span.get_span_context().trace_id
|
|
87
|
+
|
|
88
|
+
credentials = None
|
|
89
|
+
if self.credentials:
|
|
90
|
+
credentials = self.credentials.get(trace_id)
|
|
91
|
+
|
|
92
|
+
if credentials not in grouped_spans:
|
|
93
|
+
grouped_spans[credentials] = []
|
|
75
94
|
|
|
76
|
-
|
|
77
|
-
trace_ids = set(span.get_span_context().trace_id for span in spans)
|
|
95
|
+
grouped_spans[credentials].append(span)
|
|
78
96
|
|
|
79
|
-
|
|
80
|
-
trace_id = trace_ids.pop()
|
|
97
|
+
serialized_spans = []
|
|
81
98
|
|
|
82
|
-
|
|
83
|
-
|
|
99
|
+
for credentials, _spans in grouped_spans.items():
|
|
100
|
+
with exporting_context_manager(
|
|
101
|
+
context=ExportingContext(
|
|
102
|
+
credentials=credentials,
|
|
103
|
+
)
|
|
104
|
+
):
|
|
105
|
+
serialized_spans.append(super().export(_spans))
|
|
84
106
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
):
|
|
90
|
-
return super().export(spans)
|
|
107
|
+
if all(serialized_spans):
|
|
108
|
+
return SpanExportResult.SUCCESS
|
|
109
|
+
else:
|
|
110
|
+
return SpanExportResult.FAILURE
|
|
91
111
|
|
|
92
112
|
def _export(self, serialized_data: bytes):
|
|
93
113
|
credentials = exporting_context.get().credentials
|
agenta/sdk/tracing/inline.py
CHANGED
|
@@ -1061,7 +1061,7 @@ def _parse_readable_spans(
|
|
|
1061
1061
|
trace_id=_int_to_hex(span.parent.trace_id, 128),
|
|
1062
1062
|
span_id=_int_to_hex(span.parent.span_id, 64),
|
|
1063
1063
|
)
|
|
1064
|
-
if span.parent
|
|
1064
|
+
if span.parent and not span.parent.is_remote
|
|
1065
1065
|
else None
|
|
1066
1066
|
),
|
|
1067
1067
|
links=otel_links if len(otel_links) > 0 else None,
|
agenta/sdk/tracing/processors.py
CHANGED
|
@@ -8,7 +8,9 @@ from opentelemetry.sdk.trace.export import (
|
|
|
8
8
|
ReadableSpan,
|
|
9
9
|
BatchSpanProcessor,
|
|
10
10
|
_DEFAULT_MAX_QUEUE_SIZE,
|
|
11
|
+
_DEFAULT_SCHEDULE_DELAY_MILLIS,
|
|
11
12
|
_DEFAULT_MAX_EXPORT_BATCH_SIZE,
|
|
13
|
+
_DEFAULT_EXPORT_TIMEOUT_MILLIS,
|
|
12
14
|
)
|
|
13
15
|
|
|
14
16
|
from agenta.sdk.utils.logging import log
|
|
@@ -20,6 +22,7 @@ class TraceProcessor(BatchSpanProcessor):
|
|
|
20
22
|
self,
|
|
21
23
|
span_exporter: SpanExporter,
|
|
22
24
|
references: Dict[str, str] = None,
|
|
25
|
+
inline: bool = False,
|
|
23
26
|
max_queue_size: int = None,
|
|
24
27
|
schedule_delay_millis: float = None,
|
|
25
28
|
max_export_batch_size: int = None,
|
|
@@ -28,67 +31,86 @@ class TraceProcessor(BatchSpanProcessor):
|
|
|
28
31
|
super().__init__(
|
|
29
32
|
span_exporter,
|
|
30
33
|
_DEFAULT_MAX_QUEUE_SIZE,
|
|
31
|
-
|
|
34
|
+
60 * 60 * 1000 if inline else _DEFAULT_SCHEDULE_DELAY_MILLIS,
|
|
32
35
|
_DEFAULT_MAX_EXPORT_BATCH_SIZE,
|
|
33
|
-
500, # < 1 second
|
|
36
|
+
500 if inline else _DEFAULT_EXPORT_TIMEOUT_MILLIS, # < 1 second
|
|
34
37
|
)
|
|
35
38
|
|
|
36
|
-
self._registry = dict()
|
|
37
|
-
self._exporter = span_exporter
|
|
38
39
|
self.references = references or dict()
|
|
39
|
-
self.
|
|
40
|
+
self.inline = inline is True
|
|
41
|
+
|
|
42
|
+
# --- INLINE
|
|
43
|
+
if self.inline:
|
|
44
|
+
self._registry = dict()
|
|
45
|
+
self._exporter = span_exporter
|
|
46
|
+
self._spans: Dict[int, List[ReadableSpan]] = dict()
|
|
47
|
+
# --- INLINE
|
|
40
48
|
|
|
41
49
|
def on_start(
|
|
42
50
|
self,
|
|
43
51
|
span: Span,
|
|
44
52
|
parent_context: Optional[Context] = None,
|
|
45
53
|
) -> None:
|
|
46
|
-
baggage = get_baggage(parent_context)
|
|
47
|
-
|
|
48
54
|
for key in self.references.keys():
|
|
49
55
|
span.set_attribute(f"ag.refs.{key}", self.references[key])
|
|
50
56
|
|
|
57
|
+
baggage = get_baggage(parent_context)
|
|
58
|
+
|
|
51
59
|
for key in baggage.keys():
|
|
52
60
|
if key.startswith("ag.refs."):
|
|
53
61
|
_key = key.replace("ag.refs.", "")
|
|
54
62
|
if _key in [_.value for _ in Reference.__members__.values()]:
|
|
55
63
|
span.set_attribute(key, baggage[key])
|
|
56
64
|
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
# --- INLINE
|
|
66
|
+
if self.inline:
|
|
67
|
+
if span.context.trace_id not in self._registry:
|
|
68
|
+
self._registry[span.context.trace_id] = dict()
|
|
59
69
|
|
|
60
|
-
|
|
70
|
+
self._registry[span.context.trace_id][span.context.span_id] = True
|
|
71
|
+
# --- INLINE
|
|
61
72
|
|
|
62
73
|
def on_end(
|
|
63
74
|
self,
|
|
64
75
|
span: ReadableSpan,
|
|
65
76
|
):
|
|
66
|
-
|
|
67
|
-
|
|
77
|
+
# --- INLINE
|
|
78
|
+
if self.inline:
|
|
79
|
+
if self.done:
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
if span.context.trace_id not in self._spans:
|
|
83
|
+
self._spans[span.context.trace_id] = list()
|
|
68
84
|
|
|
69
|
-
|
|
70
|
-
self.spans[span.context.trace_id] = list()
|
|
85
|
+
self._spans[span.context.trace_id].append(span)
|
|
71
86
|
|
|
72
|
-
|
|
87
|
+
del self._registry[span.context.trace_id][span.context.span_id]
|
|
73
88
|
|
|
74
|
-
|
|
89
|
+
if len(self._registry[span.context.trace_id]) == 0:
|
|
90
|
+
self.export(span.context.trace_id)
|
|
91
|
+
# --- INLINE
|
|
75
92
|
|
|
76
|
-
|
|
77
|
-
|
|
93
|
+
# --- DISTRIBUTED
|
|
94
|
+
else:
|
|
95
|
+
super().on_end(span)
|
|
96
|
+
# --- DISTRIBUTED
|
|
78
97
|
|
|
79
98
|
def export(
|
|
80
99
|
self,
|
|
81
100
|
trace_id: int,
|
|
82
101
|
):
|
|
83
|
-
|
|
102
|
+
# --- INLINE
|
|
103
|
+
if self.inline:
|
|
104
|
+
spans = self._spans[trace_id]
|
|
84
105
|
|
|
85
|
-
|
|
86
|
-
|
|
106
|
+
for span in spans:
|
|
107
|
+
self.queue.appendleft(span)
|
|
87
108
|
|
|
88
|
-
|
|
89
|
-
|
|
109
|
+
with self.condition:
|
|
110
|
+
self.condition.notify()
|
|
90
111
|
|
|
91
|
-
|
|
112
|
+
del self._spans[trace_id]
|
|
113
|
+
# --- INLINE
|
|
92
114
|
|
|
93
115
|
def force_flush(
|
|
94
116
|
self,
|
|
@@ -105,10 +127,13 @@ class TraceProcessor(BatchSpanProcessor):
|
|
|
105
127
|
) -> bool:
|
|
106
128
|
is_ready = True
|
|
107
129
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
130
|
+
# --- INLINE
|
|
131
|
+
if self.inline:
|
|
132
|
+
try:
|
|
133
|
+
is_ready = self._exporter.is_ready(trace_id)
|
|
134
|
+
except: # pylint: disable=bare-except
|
|
135
|
+
pass
|
|
136
|
+
# --- INLINE
|
|
112
137
|
|
|
113
138
|
return is_ready
|
|
114
139
|
|
|
@@ -116,6 +141,14 @@ class TraceProcessor(BatchSpanProcessor):
|
|
|
116
141
|
self,
|
|
117
142
|
trace_id: Optional[int] = None,
|
|
118
143
|
) -> Dict[str, ReadableSpan]:
|
|
119
|
-
trace =
|
|
144
|
+
trace = None
|
|
145
|
+
|
|
146
|
+
# --- INLINE
|
|
147
|
+
if self.inline:
|
|
148
|
+
try:
|
|
149
|
+
trace = self._exporter.fetch(trace_id) # type: ignore
|
|
150
|
+
except: # pylint: disable=bare-except
|
|
151
|
+
pass
|
|
152
|
+
# --- INLINE
|
|
120
153
|
|
|
121
154
|
return trace
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from typing import Tuple, Optional, Dict, Any
|
|
2
|
+
|
|
3
|
+
from opentelemetry.trace import Span, set_span_in_context, get_current_span
|
|
4
|
+
from opentelemetry.baggage.propagation import W3CBaggagePropagator
|
|
5
|
+
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
|
|
6
|
+
from opentelemetry.baggage import set_baggage
|
|
7
|
+
from opentelemetry.context import get_current
|
|
8
|
+
|
|
9
|
+
import agenta as ag
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def extract(
|
|
13
|
+
headers: Dict[str, str],
|
|
14
|
+
) -> Tuple[Optional[str], Optional[Any], Dict[str, str]]:
|
|
15
|
+
# --- Extract credentials --- #
|
|
16
|
+
credentials = None
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
credentials = (
|
|
20
|
+
headers.get("Authorization") # Uppercase
|
|
21
|
+
or headers.get("authorization") # Lowercase
|
|
22
|
+
or None
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
except: # pylint: disable=bare-except
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
# --- Extract traceparent --- #
|
|
29
|
+
traceparent = None
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
_carrier = {
|
|
33
|
+
"traceparent": headers.get("Traceparent") # Uppercase
|
|
34
|
+
or headers.get("traceparent") # Lowercase
|
|
35
|
+
or "",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
_context = TraceContextTextMapPropagator().extract(_carrier)
|
|
39
|
+
|
|
40
|
+
traceparent = _context
|
|
41
|
+
except: # pylint: disable=bare-except
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
# --- Extract baggage --- #
|
|
45
|
+
baggage = {}
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
_carrier = {
|
|
49
|
+
"baggage": headers.get("Baggage") # Uppercase
|
|
50
|
+
or headers.get("baggage") # Lowercase
|
|
51
|
+
or "",
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
_context = W3CBaggagePropagator().extract(_carrier)
|
|
55
|
+
|
|
56
|
+
if _context:
|
|
57
|
+
for partial in _context.values():
|
|
58
|
+
for key, value in partial.items():
|
|
59
|
+
baggage[key] = value
|
|
60
|
+
|
|
61
|
+
except: # pylint: disable=bare-except
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
# --- #
|
|
65
|
+
return credentials, traceparent, baggage
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def inject(
|
|
69
|
+
headers: Optional[Dict[str, str]] = None,
|
|
70
|
+
) -> Dict[str, str]:
|
|
71
|
+
headers = headers or {}
|
|
72
|
+
|
|
73
|
+
_context = get_current()
|
|
74
|
+
|
|
75
|
+
ctx = ag.sdk.context.tracing.tracing_context.get()
|
|
76
|
+
|
|
77
|
+
# --- Inject traceparent --- #
|
|
78
|
+
try:
|
|
79
|
+
TraceContextTextMapPropagator().inject(headers, context=_context)
|
|
80
|
+
|
|
81
|
+
except: # pylint: disable=bare-except
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
# --- Inject baggage --- #
|
|
85
|
+
try:
|
|
86
|
+
if ctx.baggage:
|
|
87
|
+
for key, value in ctx.baggage.items():
|
|
88
|
+
_context = set_baggage(key, value, context=_context)
|
|
89
|
+
|
|
90
|
+
W3CBaggagePropagator().inject(headers, context=_context)
|
|
91
|
+
|
|
92
|
+
except: # pylint: disable=bare-except
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
# --- Inject credentials --- #
|
|
96
|
+
if ctx.credentials:
|
|
97
|
+
headers["Authorization"] = ctx.credentials
|
|
98
|
+
|
|
99
|
+
# --- #
|
|
100
|
+
return headers
|
agenta/sdk/tracing/tracing.py
CHANGED
|
@@ -21,6 +21,8 @@ from agenta.sdk.tracing.exporters import InlineExporter, OTLPExporter
|
|
|
21
21
|
from agenta.sdk.tracing.spans import CustomSpan
|
|
22
22
|
from agenta.sdk.tracing.inline import parse_inline_trace
|
|
23
23
|
from agenta.sdk.tracing.conventions import Reference, is_valid_attribute_key
|
|
24
|
+
from agenta.sdk.tracing.propagation import extract, inject
|
|
25
|
+
from agenta.sdk.utils.cache import TTLLRUCache
|
|
24
26
|
|
|
25
27
|
|
|
26
28
|
class Tracing(metaclass=Singleton):
|
|
@@ -42,7 +44,7 @@ class Tracing(metaclass=Singleton):
|
|
|
42
44
|
# REFERENCES
|
|
43
45
|
self.references: Dict[str, str] = dict()
|
|
44
46
|
# CREDENTIALS
|
|
45
|
-
self.credentials:
|
|
47
|
+
self.credentials: TTLLRUCache = TTLLRUCache(ttl=(60 * 60)) # 1 hour x 512 keys
|
|
46
48
|
|
|
47
49
|
# TRACER PROVIDER
|
|
48
50
|
self.tracer_provider: Optional[TracerProvider] = None
|
|
@@ -62,37 +64,33 @@ class Tracing(metaclass=Singleton):
|
|
|
62
64
|
def configure(
|
|
63
65
|
self,
|
|
64
66
|
api_key: Optional[str] = None,
|
|
65
|
-
|
|
66
|
-
# DEPRECATING
|
|
67
|
-
app_id: Optional[str] = None,
|
|
67
|
+
inline: Optional[bool] = True,
|
|
68
68
|
):
|
|
69
69
|
# HEADERS (OTLP)
|
|
70
70
|
if api_key:
|
|
71
71
|
self.headers["Authorization"] = f"ApiKey {api_key}"
|
|
72
|
-
# REFERENCES
|
|
73
|
-
if service_id:
|
|
74
|
-
self.references["service.id"] = service_id
|
|
75
|
-
if app_id:
|
|
76
|
-
self.references["application.id"] = app_id
|
|
77
72
|
|
|
78
73
|
# TRACER PROVIDER
|
|
79
74
|
self.tracer_provider = TracerProvider(
|
|
80
75
|
resource=Resource(attributes={"service.name": "agenta-sdk"})
|
|
81
76
|
)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
77
|
+
|
|
78
|
+
# --- INLINE
|
|
79
|
+
if inline:
|
|
80
|
+
# TRACE PROCESSORS -- INLINE
|
|
81
|
+
self.inline = TraceProcessor(
|
|
82
|
+
InlineExporter(
|
|
83
|
+
registry=self.inline_spans,
|
|
84
|
+
),
|
|
85
|
+
references=self.references,
|
|
86
|
+
inline=inline,
|
|
87
|
+
)
|
|
88
|
+
self.tracer_provider.add_span_processor(self.inline)
|
|
89
|
+
# --- INLINE
|
|
90
|
+
|
|
90
91
|
# TRACE PROCESSORS -- OTLP
|
|
91
92
|
try:
|
|
92
|
-
log.info(
|
|
93
|
-
"Agenta - OLTP URL: %s",
|
|
94
|
-
self.otlp_url,
|
|
95
|
-
)
|
|
93
|
+
log.info("Agenta - OLTP URL: %s", self.otlp_url)
|
|
96
94
|
# check(
|
|
97
95
|
# self.otlp_url,
|
|
98
96
|
# headers=self.headers,
|
|
@@ -205,7 +203,7 @@ class Tracing(metaclass=Singleton):
|
|
|
205
203
|
is_ready = True
|
|
206
204
|
|
|
207
205
|
with suppress():
|
|
208
|
-
if
|
|
206
|
+
if self.inline and trace_id:
|
|
209
207
|
is_ready = self.inline.is_ready(trace_id)
|
|
210
208
|
|
|
211
209
|
return is_ready
|
|
@@ -217,16 +215,31 @@ class Tracing(metaclass=Singleton):
|
|
|
217
215
|
_inline_trace = {}
|
|
218
216
|
|
|
219
217
|
with suppress():
|
|
220
|
-
|
|
218
|
+
if self.inline and trace_id:
|
|
219
|
+
is_ready = self.inline.is_ready(trace_id)
|
|
221
220
|
|
|
222
|
-
|
|
223
|
-
|
|
221
|
+
if is_ready is True:
|
|
222
|
+
otel_spans = self.inline.fetch(trace_id)
|
|
224
223
|
|
|
225
|
-
|
|
226
|
-
|
|
224
|
+
if otel_spans:
|
|
225
|
+
_inline_trace = parse_inline_trace(otel_spans)
|
|
227
226
|
|
|
228
227
|
return _inline_trace
|
|
229
228
|
|
|
229
|
+
def extract(
|
|
230
|
+
self,
|
|
231
|
+
*args,
|
|
232
|
+
**kwargs,
|
|
233
|
+
):
|
|
234
|
+
return extract(*args, **kwargs)
|
|
235
|
+
|
|
236
|
+
def inject(
|
|
237
|
+
self,
|
|
238
|
+
*args,
|
|
239
|
+
**kwargs,
|
|
240
|
+
):
|
|
241
|
+
return inject(*args, **kwargs)
|
|
242
|
+
|
|
230
243
|
|
|
231
244
|
def get_tracer(
|
|
232
245
|
tracing: Tracing,
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from os import getenv
|
|
3
|
+
from time import time
|
|
4
|
+
from collections import OrderedDict
|
|
5
|
+
from threading import Lock
|
|
6
|
+
|
|
7
|
+
CACHE_CAPACITY = int(getenv("AGENTA_MIDDLEWARE_CACHE_CAPACITY", "512"))
|
|
8
|
+
CACHE_TTL = int(getenv("AGENTA_MIDDLEWARE_CACHE_TTL", str(5 * 60))) # 5 minutes
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TTLLRUCache:
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
capacity: Optional[int] = CACHE_CAPACITY,
|
|
15
|
+
ttl: Optional[int] = CACHE_TTL,
|
|
16
|
+
):
|
|
17
|
+
self.cache = OrderedDict()
|
|
18
|
+
self.capacity = capacity
|
|
19
|
+
self.ttl = ttl
|
|
20
|
+
self.lock = Lock()
|
|
21
|
+
|
|
22
|
+
def get(self, key):
|
|
23
|
+
with self.lock:
|
|
24
|
+
# Get
|
|
25
|
+
value, expiry = self.cache.get(key, (None, None))
|
|
26
|
+
|
|
27
|
+
# Null check
|
|
28
|
+
if value is None:
|
|
29
|
+
return None
|
|
30
|
+
|
|
31
|
+
# TTL check
|
|
32
|
+
if time() > expiry:
|
|
33
|
+
del self.cache[key]
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
# LRU update
|
|
37
|
+
self.cache.move_to_end(key)
|
|
38
|
+
|
|
39
|
+
return value
|
|
40
|
+
|
|
41
|
+
def put(self, key, value):
|
|
42
|
+
with self.lock:
|
|
43
|
+
try:
|
|
44
|
+
# LRU update
|
|
45
|
+
self.cache.move_to_end(key)
|
|
46
|
+
|
|
47
|
+
except KeyError:
|
|
48
|
+
# Capacity check
|
|
49
|
+
if len(self.cache) >= self.capacity:
|
|
50
|
+
self.cache.popitem(last=False)
|
|
51
|
+
|
|
52
|
+
# Put
|
|
53
|
+
self.cache[key] = (value, time() + self.ttl)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: agenta
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.35.0
|
|
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
|
|
@@ -22,9 +22,9 @@ Requires-Dist: fastapi (>=0.100.0)
|
|
|
22
22
|
Requires-Dist: httpx (>=0.24,<0.28)
|
|
23
23
|
Requires-Dist: importlib-metadata (>=8.0.0,<9.0)
|
|
24
24
|
Requires-Dist: litellm (>=1.48.0,<2.0.0)
|
|
25
|
-
Requires-Dist: opentelemetry-api (
|
|
26
|
-
Requires-Dist: opentelemetry-exporter-otlp (
|
|
27
|
-
Requires-Dist: opentelemetry-sdk (
|
|
25
|
+
Requires-Dist: opentelemetry-api (==1.27.0)
|
|
26
|
+
Requires-Dist: opentelemetry-exporter-otlp (==1.27.0)
|
|
27
|
+
Requires-Dist: opentelemetry-sdk (==1.27.0)
|
|
28
28
|
Requires-Dist: posthog (>=3.1.0,<4.0.0)
|
|
29
29
|
Requires-Dist: pydantic (>=2)
|
|
30
30
|
Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
|
|
@@ -197,20 +197,20 @@ agenta/docker/docker-assets/lambda_function.py,sha256=h4UZSSfqwpfsCgERv6frqwm_4J
|
|
|
197
197
|
agenta/docker/docker-assets/main.py,sha256=7MI-21n81U7N7A0GxebNi0cmGWtJKcR2sPB6FcH2QfA,251
|
|
198
198
|
agenta/docker/docker_utils.py,sha256=rKCSb3fDPizX8zwRBaLR0oQ0TnAmyJlzBhflICcwynE,3577
|
|
199
199
|
agenta/sdk/__init__.py,sha256=WFYRfWh6IoYPSzMG2WF2Xz5amLQtzfWVscT0Q9OLpFY,2109
|
|
200
|
-
agenta/sdk/agenta_init.py,sha256=
|
|
200
|
+
agenta/sdk/agenta_init.py,sha256=VmjOfzS5ioaUjsFxmSB1k4M4lDgFq73HbvqcId0IbuI,7719
|
|
201
201
|
agenta/sdk/assets.py,sha256=4FN1IfZfBx9g6jW8KzT9ei4BQei9-7MasxHHD9UdeMg,7752
|
|
202
202
|
agenta/sdk/client.py,sha256=trKyBOYFZRk0v5Eptxvh87yPf50Y9CqY6Qgv4Fy-VH4,2142
|
|
203
203
|
agenta/sdk/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
204
204
|
agenta/sdk/context/exporting.py,sha256=16X8fgMhl58gehSlqANX97FiKxx4TkGiG4d2B0-7ZX0,516
|
|
205
205
|
agenta/sdk/context/routing.py,sha256=FEsjw8EttI1SMyUo96ptcUsvHJnhoKwdr1szlkxxJNU,598
|
|
206
|
-
agenta/sdk/context/tracing.py,sha256=
|
|
206
|
+
agenta/sdk/context/tracing.py,sha256=xjErrXP1Nq1AfL-Cif1l-lNEfs12eQ3v_VCRgoKe7nY,743
|
|
207
207
|
agenta/sdk/decorators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
208
|
-
agenta/sdk/decorators/routing.py,sha256=
|
|
209
|
-
agenta/sdk/decorators/tracing.py,sha256=
|
|
208
|
+
agenta/sdk/decorators/routing.py,sha256=a6llLTQTs-eGZ1023hyboNVJBeYcqI4zKEShRLvhTXk,25374
|
|
209
|
+
agenta/sdk/decorators/tracing.py,sha256=tIi5gpwzvXd26pFW-7kYQtTFO8w1zzwFeAPY2Ldq4P4,9952
|
|
210
210
|
agenta/sdk/litellm/__init__.py,sha256=Bpz1gfHQc0MN1yolWcjifLWznv6GjHggvRGQSpxpihM,37
|
|
211
211
|
agenta/sdk/litellm/litellm.py,sha256=Xx_YJu05BYkmIe6uObjS6DwzjgwfNytGWf807Zh0vcU,10153
|
|
212
212
|
agenta/sdk/litellm/mockllm.py,sha256=8V6dqdv8eA4P-VoXIwHNYlIjHG189P14POSfSfluVw0,678
|
|
213
|
-
agenta/sdk/litellm/mocks/__init__.py,sha256=
|
|
213
|
+
agenta/sdk/litellm/mocks/__init__.py,sha256=UvunEgoDcVobBZXrqTnHt9ZEjJEFtqFHGiSCKfNiuHU,952
|
|
214
214
|
agenta/sdk/managers/__init__.py,sha256=SN-LRwG0pRRDV3u2Q4JiiSTigN3-mYpzGNM35RzT4mc,238
|
|
215
215
|
agenta/sdk/managers/apps.py,sha256=RSE5ER-a0IHo7iZrcQLshQZMemPTfAVsBfR8H1J-ryQ,1713
|
|
216
216
|
agenta/sdk/managers/config.py,sha256=2LFl7aBF7FKd6UKhqcbnDXzlP8Rsj7hnCu5wBllVQzc,7753
|
|
@@ -220,25 +220,26 @@ agenta/sdk/managers/shared.py,sha256=vUhO2thObHxCIowyiM_KFsxPY2q6Rv_lHr2Up8WvFjg
|
|
|
220
220
|
agenta/sdk/managers/variant.py,sha256=A5ga3mq3b0weUTXa9HO72MGaspthGcu1uK9K5OnP738,4172
|
|
221
221
|
agenta/sdk/managers/vault.py,sha256=054ce9X_xKa2M4NtQWz-GugO6q_pYVWCP3IxbAJJcRw,337
|
|
222
222
|
agenta/sdk/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
223
|
-
agenta/sdk/middleware/auth.py,sha256=
|
|
224
|
-
agenta/sdk/middleware/
|
|
225
|
-
agenta/sdk/middleware/config.py,sha256=Mckj-xwIWj06fpMLrz43Ookn8Q7MuvqkF1mDmG17l20,7614
|
|
223
|
+
agenta/sdk/middleware/auth.py,sha256=jdv9GIsfaWb_MsrzwR2iLhHY0YwKgIqqGQbczTWpC2k,9599
|
|
224
|
+
agenta/sdk/middleware/config.py,sha256=-THnZp-4svzduei-ioRdgyk0ZBRWK10_6pLF-4QKXlE,7824
|
|
226
225
|
agenta/sdk/middleware/cors.py,sha256=q3r7lGkrIdMcT_vuhsburMcjG7pyl7w0ycxrIrGJ2e8,921
|
|
227
|
-
agenta/sdk/middleware/inline.py,sha256=
|
|
228
|
-
agenta/sdk/middleware/mock.py,sha256=
|
|
229
|
-
agenta/sdk/middleware/otel.py,sha256=
|
|
230
|
-
agenta/sdk/middleware/vault.py,sha256=
|
|
226
|
+
agenta/sdk/middleware/inline.py,sha256=ee8E4XBGcRSrHTvblqX1yRXuTN_sxLm7lY1jnywrBG8,901
|
|
227
|
+
agenta/sdk/middleware/mock.py,sha256=bCUN9iJBxePyN9MBwBpJs-_iCNkUQeUjIIu3WElS1oQ,759
|
|
228
|
+
agenta/sdk/middleware/otel.py,sha256=ShaB7iVaUjdq6lOXf0vmZthQsDUjAgIrseyPeJzVkEU,840
|
|
229
|
+
agenta/sdk/middleware/vault.py,sha256=R4uLcGWuS0oigAjF-80jvqCScrF1_fpGpdYmA2D5A5A,4233
|
|
231
230
|
agenta/sdk/router.py,sha256=mOguvtOwl2wmyAgOuWTsf98pQwpNiUILKIo67W_hR3A,119
|
|
232
231
|
agenta/sdk/tracing/__init__.py,sha256=rQNe5-zT5Kt7_CDhq-lnUIi1EYTBVzVf_MbfcIxVD98,41
|
|
233
232
|
agenta/sdk/tracing/attributes.py,sha256=zh8JQZSeYCLBeIRSopKJx6QQ-WEgw08Cr64DS_WOcT8,3833
|
|
234
233
|
agenta/sdk/tracing/conventions.py,sha256=JBtznBXZ3aRkGKkLl7cPwdMNh3w1G-H2Ta2YrAxbr38,950
|
|
235
|
-
agenta/sdk/tracing/exporters.py,sha256=
|
|
236
|
-
agenta/sdk/tracing/inline.py,sha256=
|
|
237
|
-
agenta/sdk/tracing/processors.py,sha256=
|
|
234
|
+
agenta/sdk/tracing/exporters.py,sha256=mnqZXbien7uzEdwfm06BFOZnC8JVJvNWI4ODSyMUeSc,3160
|
|
235
|
+
agenta/sdk/tracing/inline.py,sha256=tY9mH1zMlJMqf2WWKJdakLtMHJKNeYRaynqvsPeqpTY,31312
|
|
236
|
+
agenta/sdk/tracing/processors.py,sha256=HR7pSzHtV8WWr2qEeAki0yznWWQCKCW3HGm1nbVgmEM,4252
|
|
237
|
+
agenta/sdk/tracing/propagation.py,sha256=EeOqDMqnh_MoEhGd1do_vy_tQBYUcoC8kpLqVoZeqg0,2561
|
|
238
238
|
agenta/sdk/tracing/spans.py,sha256=nqUOjjirBxB8Eacv8Qj4Ra_6rknGi3lbJdNyKmk5ODQ,3707
|
|
239
|
-
agenta/sdk/tracing/tracing.py,sha256=
|
|
239
|
+
agenta/sdk/tracing/tracing.py,sha256=kWEt9AdUnTetgq7mR-fnap2isgL3NBg44cG7WFhZ_GU,7162
|
|
240
240
|
agenta/sdk/types.py,sha256=HvFok4lEFxWow6WT71OrJ23WhTsj_FqqMyzlKByAqYk,19120
|
|
241
241
|
agenta/sdk/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
242
|
+
agenta/sdk/utils/cache.py,sha256=69pQCtmh10DiYoOdKv4t2fCYDQCuWZtH4uy36kXewpw,1372
|
|
242
243
|
agenta/sdk/utils/constants.py,sha256=zW3R4rjXOo2L5lz6q84l_zYuOM9u4mpPRHw_B1Dr_hI,67
|
|
243
244
|
agenta/sdk/utils/costs.py,sha256=i8C7ud__pThLS55XkN4YW8czXtGeXr2mx7jjcOFeiXg,5955
|
|
244
245
|
agenta/sdk/utils/exceptions.py,sha256=8FQP05ecHLpq6zSj3MbfdNp9SqP0a7iC_dA5uAe0vkE,1455
|
|
@@ -248,7 +249,7 @@ agenta/sdk/utils/logging.py,sha256=eFzEFuYpggfIhEKv09JZRqcDzkmZ482a_E2G-X0FK7Y,4
|
|
|
248
249
|
agenta/sdk/utils/preinit.py,sha256=YlJL7RLfel0R7DFp-jK7OV-z4ZIQJM0oupYlk7g8b5o,1278
|
|
249
250
|
agenta/sdk/utils/singleton.py,sha256=17Ph7LGnnV8HkPjImruKita2ni03Ari5jr0jqm__4sc,312
|
|
250
251
|
agenta/sdk/utils/timing.py,sha256=rmBPSBuUnIu-epocUCVk0KcM2r36HuDoxkFqOZgfPhc,1507
|
|
251
|
-
agenta-0.
|
|
252
|
-
agenta-0.
|
|
253
|
-
agenta-0.
|
|
254
|
-
agenta-0.
|
|
252
|
+
agenta-0.35.0.dist-info/METADATA,sha256=xmUP8deXxTXgcdI1_2n4ROYik563Smi2D99mAmBwgd4,29827
|
|
253
|
+
agenta-0.35.0.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
|
254
|
+
agenta-0.35.0.dist-info/entry_points.txt,sha256=PDiu8_8AsL7ibU9v4iNoOKR1S7F2rdxjlEprjM9QOgo,46
|
|
255
|
+
agenta-0.35.0.dist-info/RECORD,,
|
agenta/sdk/middleware/cache.py
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
from os import getenv
|
|
2
|
-
from time import time
|
|
3
|
-
from collections import OrderedDict
|
|
4
|
-
|
|
5
|
-
CACHE_CAPACITY = int(getenv("AGENTA_MIDDLEWARE_CACHE_CAPACITY", "512"))
|
|
6
|
-
CACHE_TTL = int(getenv("AGENTA_MIDDLEWARE_CACHE_TTL", str(5 * 60))) # 5 minutes
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class TTLLRUCache:
|
|
10
|
-
def __init__(self, capacity: int, ttl: int):
|
|
11
|
-
self.cache = OrderedDict()
|
|
12
|
-
self.capacity = capacity
|
|
13
|
-
self.ttl = ttl
|
|
14
|
-
|
|
15
|
-
def get(self, key):
|
|
16
|
-
# CACHE
|
|
17
|
-
if key not in self.cache:
|
|
18
|
-
return None
|
|
19
|
-
|
|
20
|
-
value, expiry = self.cache[key]
|
|
21
|
-
# -----
|
|
22
|
-
|
|
23
|
-
# TTL
|
|
24
|
-
if time() > expiry:
|
|
25
|
-
del self.cache[key]
|
|
26
|
-
|
|
27
|
-
return None
|
|
28
|
-
# ---
|
|
29
|
-
|
|
30
|
-
# LRU
|
|
31
|
-
self.cache.move_to_end(key)
|
|
32
|
-
# ---
|
|
33
|
-
|
|
34
|
-
return value
|
|
35
|
-
|
|
36
|
-
def put(self, key, value):
|
|
37
|
-
# CACHE
|
|
38
|
-
if key in self.cache:
|
|
39
|
-
del self.cache[key]
|
|
40
|
-
# CACHE & LRU
|
|
41
|
-
elif len(self.cache) >= self.capacity:
|
|
42
|
-
self.cache.popitem(last=False)
|
|
43
|
-
# -----------
|
|
44
|
-
|
|
45
|
-
# TTL
|
|
46
|
-
self.cache[key] = (value, time() + self.ttl)
|
|
47
|
-
# ---
|
|
File without changes
|
|
File without changes
|