agenta 0.34.7__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 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(
@@ -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
@@ -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
- if inline:
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 --- #
@@ -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 as 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
- with ag.tracer.start_as_current_span(func.__name__, kind=self.kind):
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
- with ag.tracer.start_as_current_span(func.__name__, kind=self.kind):
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[trace_id] = context.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
  }
@@ -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.middleware.cache import TTLLRUCache, CACHE_CAPACITY, CACHE_TTL
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(capacity=CACHE_CAPACITY, ttl=CACHE_TTL)
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
 
@@ -10,7 +10,7 @@ from fastapi import Request, FastAPI
10
10
 
11
11
  import httpx
12
12
 
13
- from agenta.sdk.middleware.cache import TTLLRUCache, CACHE_CAPACITY, CACHE_TTL
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(capacity=CACHE_CAPACITY, ttl=CACHE_TTL)
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("application_id")
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("application_slug")
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
- application_version = (
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=application_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("variant_id")
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("variant_slug")
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("variant_version")
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("environment_id")
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("environment_slug")
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("environment_version")
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]):
@@ -3,9 +3,7 @@ from typing import Callable
3
3
  from starlette.middleware.base import BaseHTTPMiddleware
4
4
  from fastapi import Request, FastAPI
5
5
 
6
-
7
6
  from agenta.sdk.utils.exceptions import suppress
8
-
9
7
  from agenta.sdk.utils.constants import TRUTHY
10
8
 
11
9
 
@@ -3,7 +3,6 @@ from typing import Callable
3
3
  from starlette.middleware.base import BaseHTTPMiddleware
4
4
  from fastapi import Request, FastAPI
5
5
 
6
-
7
6
  from agenta.sdk.utils.exceptions import suppress
8
7
 
9
8
 
@@ -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 = await self._get_baggage(request)
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
@@ -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, get_args
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.middleware.cache import TTLLRUCache, CACHE_CAPACITY, CACHE_TTL
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(capacity=CACHE_CAPACITY, ttl=CACHE_TTL)
29
+ _cache = TTLLRUCache()
30
30
 
31
31
 
32
32
  class VaultMiddleware(BaseHTTPMiddleware):
@@ -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__(self, registry: Dict[str, List[ReadableSpan]]):
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__(self, *args, credentials: Dict[int, str] = None, **kwargs):
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
- credentials = None
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
- if self.credentials:
77
- trace_ids = set(span.get_span_context().trace_id for span in spans)
95
+ grouped_spans[credentials].append(span)
78
96
 
79
- if len(trace_ids) == 1:
80
- trace_id = trace_ids.pop()
97
+ serialized_spans = []
81
98
 
82
- if trace_id in self.credentials:
83
- credentials = self.credentials.pop(trace_id)
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
- with exporting_context_manager(
86
- context=ExportingContext(
87
- credentials=credentials,
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
@@ -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,
@@ -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
- 12 * 60 * 60 * 1000, # 12 hours
34
+ 60 * 60 * 1000 if inline else _DEFAULT_SCHEDULE_DELAY_MILLIS,
32
35
  _DEFAULT_MAX_EXPORT_BATCH_SIZE,
33
- 500, # < 1 second (0.5 seconds)
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.spans: Dict[int, List[ReadableSpan]] = dict()
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
- if span.context.trace_id not in self._registry:
58
- self._registry[span.context.trace_id] = dict()
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
- self._registry[span.context.trace_id][span.context.span_id] = True
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
- if self.done:
67
- return
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
- if span.context.trace_id not in self.spans:
70
- self.spans[span.context.trace_id] = list()
85
+ self._spans[span.context.trace_id].append(span)
71
86
 
72
- self.spans[span.context.trace_id].append(span)
87
+ del self._registry[span.context.trace_id][span.context.span_id]
73
88
 
74
- del self._registry[span.context.trace_id][span.context.span_id]
89
+ if len(self._registry[span.context.trace_id]) == 0:
90
+ self.export(span.context.trace_id)
91
+ # --- INLINE
75
92
 
76
- if len(self._registry[span.context.trace_id]) == 0:
77
- self.export(span.context.trace_id)
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
- spans = self.spans[trace_id]
102
+ # --- INLINE
103
+ if self.inline:
104
+ spans = self._spans[trace_id]
84
105
 
85
- for span in spans:
86
- self.queue.appendleft(span)
106
+ for span in spans:
107
+ self.queue.appendleft(span)
87
108
 
88
- with self.condition:
89
- self.condition.notify()
109
+ with self.condition:
110
+ self.condition.notify()
90
111
 
91
- del self.spans[trace_id]
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
- try:
109
- is_ready = self._exporter.is_ready(trace_id)
110
- except: # pylint: disable=bare-except
111
- pass
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 = self._exporter.fetch(trace_id) # type: ignore
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
@@ -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: Dict[int, str] = dict()
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
- service_id: Optional[str] = None,
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
- # TRACE PROCESSORS -- INLINE
83
- self.inline = TraceProcessor(
84
- InlineExporter(
85
- registry=self.inline_spans,
86
- ),
87
- references=self.references,
88
- )
89
- self.tracer_provider.add_span_processor(self.inline)
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 trace_id is not None:
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
- is_ready = self.inline.is_ready(trace_id)
218
+ if self.inline and trace_id:
219
+ is_ready = self.inline.is_ready(trace_id)
221
220
 
222
- if is_ready is True:
223
- otel_spans = self.inline.fetch(trace_id)
221
+ if is_ready is True:
222
+ otel_spans = self.inline.fetch(trace_id)
224
223
 
225
- if otel_spans:
226
- _inline_trace = parse_inline_trace(otel_spans)
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.34.7
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 (>=1.27.0,<2.0.0)
26
- Requires-Dist: opentelemetry-exporter-otlp (>=1.27.0,<2.0.0)
27
- Requires-Dist: opentelemetry-sdk (>=1.27.0,<2.0.0)
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=4BkwWvLOGd82BElGfv60iDqr32OKvJqivRWSpAXjRGs,8019
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=zp7T_wLVkR-V1c0k7UAN69rwH9VV7MhoZD_IdNu-_RE,649
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=FMRc6dSCc1lMblr8Eykj3A2tVI_N-Jjm6IJ6HxTY4CY,24730
209
- agenta/sdk/decorators/tracing.py,sha256=Wf7KWR1NknXlQEdB2sjMFrcSI-tSSvO9XubN-Ro5tkU,9216
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=c-afSm0YkiHyyaLegvtFs6WuWZTl2Q7Fq_iUoxqBbQc,616
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=XC0EKRu0H0a0SQTrUsDmkhHGfNFcKMNjL9Btna6ON_U,9835
224
- agenta/sdk/middleware/cache.py,sha256=-_e3_6f4cPfpuUpGRK_ZQvUx_54ez68db4SnBwfuESk,1078
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=utPuSn34WI_yfLK46xRYlJhRdvKvNA1Z1lWhhOLXoa8,903
228
- agenta/sdk/middleware/mock.py,sha256=kMG-yefb4N39Ds7VuniwjW5_IPWbnauhiLFyC1Bkyak,760
229
- agenta/sdk/middleware/otel.py,sha256=fXAYxKOdrCuWdDAYyWSCX4VLh5aNCWicAFi2C-p7wZY,1043
230
- agenta/sdk/middleware/vault.py,sha256=AsRa1mo9cNAh7p162U3NUhMlJpRzxOnojacqXL8nYnA,4313
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=PuSbamuLa4e9eGBM6tp7Smh5R41Jz07VBoBtMnkbtz8,2687
236
- agenta/sdk/tracing/inline.py,sha256=BQecJkZDlR85aodfvye-LuDhouee3RjLuUl7GMpfsSw,31282
237
- agenta/sdk/tracing/processors.py,sha256=d7MvJ_DRAP0RAlp8V8XrrY-m8pJ03nLxKoq15RnjedA,3334
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=iZCIggUkR5f2zVcq0o7T8-to7pcFO349gUEBYXnBMzg,6888
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.34.7.dist-info/METADATA,sha256=SbzyEjQcGFOd3ORWnKANw70hCCLIga2zCqHaeAVRJ8o,29848
252
- agenta-0.34.7.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
253
- agenta-0.34.7.dist-info/entry_points.txt,sha256=PDiu8_8AsL7ibU9v4iNoOKR1S7F2rdxjlEprjM9QOgo,46
254
- agenta-0.34.7.dist-info/RECORD,,
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,,
@@ -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
- # ---