agenta 0.27.0a8__py3-none-any.whl → 0.27.0a12__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.

Files changed (62) hide show
  1. agenta/__init__.py +21 -3
  2. agenta/client/backend/__init__.py +14 -0
  3. agenta/client/backend/apps/client.py +28 -20
  4. agenta/client/backend/client.py +25 -2
  5. agenta/client/backend/containers/client.py +5 -1
  6. agenta/client/backend/core/__init__.py +2 -1
  7. agenta/client/backend/core/client_wrapper.py +6 -6
  8. agenta/client/backend/core/file.py +33 -11
  9. agenta/client/backend/core/http_client.py +24 -18
  10. agenta/client/backend/core/pydantic_utilities.py +144 -29
  11. agenta/client/backend/core/request_options.py +3 -0
  12. agenta/client/backend/core/serialization.py +139 -42
  13. agenta/client/backend/evaluations/client.py +7 -2
  14. agenta/client/backend/evaluators/client.py +349 -1
  15. agenta/client/backend/observability/client.py +11 -2
  16. agenta/client/backend/testsets/client.py +10 -10
  17. agenta/client/backend/types/__init__.py +14 -0
  18. agenta/client/backend/types/app.py +1 -0
  19. agenta/client/backend/types/app_variant_response.py +3 -1
  20. agenta/client/backend/types/config_dto.py +32 -0
  21. agenta/client/backend/types/config_response_model.py +32 -0
  22. agenta/client/backend/types/create_span.py +3 -2
  23. agenta/client/backend/types/environment_output.py +1 -0
  24. agenta/client/backend/types/environment_output_extended.py +1 -0
  25. agenta/client/backend/types/evaluation.py +1 -2
  26. agenta/client/backend/types/evaluator.py +2 -0
  27. agenta/client/backend/types/evaluator_config.py +1 -0
  28. agenta/client/backend/types/evaluator_mapping_output_interface.py +21 -0
  29. agenta/client/backend/types/evaluator_output_interface.py +21 -0
  30. agenta/client/backend/types/human_evaluation.py +1 -2
  31. agenta/client/backend/types/lifecycle_dto.py +24 -0
  32. agenta/client/backend/types/llm_tokens.py +2 -2
  33. agenta/client/backend/types/reference_dto.py +23 -0
  34. agenta/client/backend/types/reference_request_model.py +23 -0
  35. agenta/client/backend/types/span.py +1 -0
  36. agenta/client/backend/types/span_detail.py +7 -1
  37. agenta/client/backend/types/test_set_output_response.py +5 -2
  38. agenta/client/backend/types/trace_detail.py +7 -1
  39. agenta/client/backend/types/with_pagination.py +4 -2
  40. agenta/client/backend/variants/client.py +1565 -272
  41. agenta/sdk/__init__.py +19 -5
  42. agenta/sdk/agenta_init.py +21 -7
  43. agenta/sdk/context/routing.py +6 -5
  44. agenta/sdk/decorators/routing.py +16 -5
  45. agenta/sdk/decorators/tracing.py +16 -9
  46. agenta/sdk/litellm/litellm.py +47 -36
  47. agenta/sdk/managers/__init__.py +6 -0
  48. agenta/sdk/managers/config.py +318 -0
  49. agenta/sdk/managers/deployment.py +45 -0
  50. agenta/sdk/managers/shared.py +639 -0
  51. agenta/sdk/managers/variant.py +182 -0
  52. agenta/sdk/tracing/exporters.py +10 -1
  53. agenta/sdk/tracing/inline.py +45 -0
  54. agenta/sdk/tracing/processors.py +34 -7
  55. agenta/sdk/tracing/tracing.py +0 -10
  56. agenta/sdk/types.py +47 -2
  57. agenta/sdk/utils/exceptions.py +31 -1
  58. {agenta-0.27.0a8.dist-info → agenta-0.27.0a12.dist-info}/METADATA +1 -1
  59. {agenta-0.27.0a8.dist-info → agenta-0.27.0a12.dist-info}/RECORD +61 -50
  60. agenta/sdk/config_manager.py +0 -205
  61. {agenta-0.27.0a8.dist-info → agenta-0.27.0a12.dist-info}/WHEEL +0 -0
  62. {agenta-0.27.0a8.dist-info → agenta-0.27.0a12.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,182 @@
1
+ from typing import Optional
2
+
3
+ from agenta.sdk.managers.shared import SharedManager
4
+
5
+
6
+ class VariantManager(SharedManager):
7
+ @classmethod
8
+ def create(
9
+ cls,
10
+ *,
11
+ parameters: dict,
12
+ variant_slug: str,
13
+ #
14
+ app_id: Optional[str] = None,
15
+ app_slug: Optional[str] = None,
16
+ ):
17
+ variant = SharedManager.add(
18
+ app_id=app_id,
19
+ app_slug=app_slug,
20
+ variant_slug=variant_slug,
21
+ )
22
+
23
+ if variant:
24
+ variant = SharedManager.commit(
25
+ parameters=parameters,
26
+ app_id=app_id,
27
+ app_slug=app_slug,
28
+ variant_slug=variant_slug,
29
+ )
30
+
31
+ return variant
32
+
33
+ @classmethod
34
+ async def acreate(
35
+ cls,
36
+ *,
37
+ parameters: dict,
38
+ variant_slug: str,
39
+ #
40
+ app_id: Optional[str] = None,
41
+ app_slug: Optional[str] = None,
42
+ ):
43
+ variant = await SharedManager.aadd(
44
+ app_id=app_id,
45
+ app_slug=app_slug,
46
+ variant_slug=variant_slug,
47
+ )
48
+ if variant:
49
+ variant = await SharedManager.acommit(
50
+ parameters=parameters,
51
+ app_id=app_id,
52
+ app_slug=app_slug,
53
+ variant_slug=variant_slug,
54
+ )
55
+
56
+ return variant
57
+
58
+ @classmethod
59
+ def commit(
60
+ cls,
61
+ *,
62
+ parameters: dict,
63
+ variant_slug: str,
64
+ #
65
+ app_id: Optional[str] = None,
66
+ app_slug: Optional[str] = None,
67
+ ):
68
+ variant = SharedManager.commit(
69
+ parameters=parameters,
70
+ app_id=app_id,
71
+ app_slug=app_slug,
72
+ variant_slug=variant_slug,
73
+ )
74
+ return variant
75
+
76
+ @classmethod
77
+ async def acommit(
78
+ cls,
79
+ *,
80
+ parameters: dict,
81
+ variant_slug: str,
82
+ #
83
+ app_id: Optional[str] = None,
84
+ app_slug: Optional[str] = None,
85
+ ):
86
+ variant = await SharedManager.acommit(
87
+ parameters=parameters,
88
+ app_id=app_id,
89
+ app_slug=app_slug,
90
+ variant_slug=variant_slug,
91
+ )
92
+ return variant
93
+
94
+ @classmethod
95
+ def delete(
96
+ cls,
97
+ *,
98
+ variant_slug: str,
99
+ #
100
+ app_id: Optional[str] = None,
101
+ app_slug: Optional[str] = None,
102
+ ):
103
+ message = SharedManager.delete(
104
+ app_id=app_id,
105
+ app_slug=app_slug,
106
+ variant_slug=variant_slug,
107
+ )
108
+ return message
109
+
110
+ @classmethod
111
+ async def adelete(
112
+ cls,
113
+ *,
114
+ variant_slug: str,
115
+ #
116
+ app_id: Optional[str] = None,
117
+ app_slug: Optional[str] = None,
118
+ ):
119
+ message = await SharedManager.adelete(
120
+ app_id=app_id,
121
+ app_slug=app_slug,
122
+ variant_slug=variant_slug,
123
+ )
124
+ return message
125
+
126
+ @classmethod
127
+ def list(
128
+ cls,
129
+ *,
130
+ app_id: Optional[str] = None,
131
+ app_slug: Optional[str] = None,
132
+ ):
133
+ variants = SharedManager.list(
134
+ app_id=app_id,
135
+ app_slug=app_slug,
136
+ )
137
+ return variants
138
+
139
+ @classmethod
140
+ async def alist(
141
+ cls,
142
+ *,
143
+ app_id: Optional[str] = None,
144
+ app_slug: Optional[str] = None,
145
+ ):
146
+ variants = await SharedManager.alist(
147
+ app_id=app_id,
148
+ app_slug=app_slug,
149
+ )
150
+ return variants
151
+
152
+ @classmethod
153
+ def history(
154
+ cls,
155
+ *,
156
+ variant_slug: str,
157
+ #
158
+ app_id: Optional[str] = None,
159
+ app_slug: Optional[str] = None,
160
+ ):
161
+ variants = SharedManager.history(
162
+ app_id=app_id,
163
+ app_slug=app_slug,
164
+ variant_slug=variant_slug,
165
+ )
166
+ return variants
167
+
168
+ @classmethod
169
+ async def ahistory(
170
+ cls,
171
+ *,
172
+ variant_slug: str,
173
+ #
174
+ app_id: Optional[str] = None,
175
+ app_slug: Optional[str] = None,
176
+ ):
177
+ variants = await SharedManager.ahistory(
178
+ app_id=app_id,
179
+ app_slug=app_slug,
180
+ variant_slug=variant_slug,
181
+ )
182
+ return variants
@@ -38,13 +38,22 @@ class InlineTraceExporter(SpanExporter):
38
38
  def force_flush(self, timeout_millis: int = 30000) -> bool:
39
39
  return True
40
40
 
41
+ def is_ready(
42
+ self,
43
+ trace_id: int,
44
+ ) -> bool:
45
+ is_ready = trace_id in self._registry
46
+
47
+ return is_ready
48
+
41
49
  def fetch(
42
50
  self,
43
51
  trace_id: int,
44
52
  ) -> List[ReadableSpan]:
45
53
  trace = self._registry.get(trace_id, [])
46
54
 
47
- del self._registry[trace_id]
55
+ if trace_id in self._registry:
56
+ del self._registry[trace_id]
48
57
 
49
58
  return trace
50
59
 
@@ -412,9 +412,12 @@ def _connect_tree_dfs(
412
412
  ### apis.fastapi.observability.opentelemetry.semconv ###
413
413
  ### ------------------------------------------------ ###
414
414
 
415
+ from json import loads
416
+
415
417
  VERSION = "0.4.1"
416
418
 
417
419
  V_0_4_1_ATTRIBUTES_EXACT = [
420
+ # OPENLLMETRY
418
421
  ("gen_ai.system", "ag.meta.system"),
419
422
  ("gen_ai.request.base_url", "ag.meta.request.base_url"),
420
423
  ("gen_ai.request.endpoint", "ag.meta.request.endpoint"),
@@ -439,12 +442,35 @@ V_0_4_1_ATTRIBUTES_EXACT = [
439
442
  ("db.vector.query.top_k", "ag.meta.request.top_k"),
440
443
  ("pinecone.query.top_k", "ag.meta.request.top_k"),
441
444
  ("traceloop.span.kind", "ag.type.node"),
445
+ ("traceloop.entity.name", "ag.node.name"),
446
+ # OPENINFERENCE
447
+ ("output.value", "ag.data.outputs"),
448
+ ("input.value", "ag.data.inputs"),
449
+ ("embedding.model_name", "ag.meta.request.model"),
450
+ ("llm.invocation_parameters", "ag.meta.request"),
451
+ ("llm.model_name", "ag.meta.request.model"),
452
+ ("llm.provider", "ag.meta.provider"),
453
+ ("llm.system", "ag.meta.system"),
442
454
  ]
443
455
  V_0_4_1_ATTRIBUTES_PREFIX = [
456
+ # OPENLLMETRY
444
457
  ("gen_ai.prompt", "ag.data.inputs.prompt"),
445
458
  ("gen_ai.completion", "ag.data.outputs.completion"),
459
+ ("llm.request.functions", "ag.data.inputs.functions"),
460
+ ("llm.request.tools", "ag.data.inputs.tools"),
461
+ # OPENINFERENCE
462
+ ("llm.token_count", "ag.metrics.unit.tokens"),
463
+ ("llm.input_messages", "ag.data.inputs.prompt"),
464
+ ("llm.output_messages", "ag.data.outputs.completion"),
465
+ ]
466
+
467
+ V_0_4_1_ATTRIBUTES_DYNAMIC = [
468
+ # OPENLLMETRY
469
+ ("traceloop.entity.input", lambda x: ("ag.data.inputs", loads(x).get("inputs"))),
470
+ ("traceloop.entity.output", lambda x: ("ag.data.outputs", loads(x).get("outputs"))),
446
471
  ]
447
472
 
473
+
448
474
  V_0_4_1_MAPS = {
449
475
  "attributes": {
450
476
  "exact": {
@@ -455,6 +481,9 @@ V_0_4_1_MAPS = {
455
481
  "from": {otel: agenta for otel, agenta in V_0_4_1_ATTRIBUTES_PREFIX[::-1]},
456
482
  "to": {agenta: otel for otel, agenta in V_0_4_1_ATTRIBUTES_PREFIX[::-1]},
457
483
  },
484
+ "dynamic": {
485
+ "from": {otel: agenta for otel, agenta in V_0_4_1_ATTRIBUTES_DYNAMIC[::-1]}
486
+ },
458
487
  },
459
488
  }
460
489
  V_0_4_1_KEYS = {
@@ -467,6 +496,9 @@ V_0_4_1_KEYS = {
467
496
  "from": list(V_0_4_1_MAPS["attributes"]["prefix"]["from"].keys()),
468
497
  "to": list(V_0_4_1_MAPS["attributes"]["prefix"]["to"].keys()),
469
498
  },
499
+ "dynamic": {
500
+ "from": list(V_0_4_1_MAPS["attributes"]["dynamic"]["from"].keys()),
501
+ },
470
502
  },
471
503
  }
472
504
 
@@ -480,6 +512,7 @@ KEYS = {
480
512
 
481
513
  CODEX = {"maps": MAPS[VERSION], "keys": KEYS[VERSION]}
482
514
 
515
+
483
516
  ### ------------------------------------------------ ###
484
517
  ### apis.fastapi.observability.opentelemetry.semconv ###
485
518
  ########################################################
@@ -653,6 +686,18 @@ def _parse_from_semconv(
653
686
 
654
687
  del attributes[old_key]
655
688
 
689
+ for dynamic_key in CODEX["keys"]["attributes"]["dynamic"]["from"]:
690
+ if old_key == dynamic_key:
691
+ try:
692
+ new_key, new_value = CODEX["maps"]["attributes"]["dynamic"][
693
+ "from"
694
+ ][dynamic_key](value)
695
+
696
+ attributes[new_key] = new_value
697
+
698
+ except: # pylint: disable=bare-except
699
+ pass
700
+
656
701
 
657
702
  def _parse_from_links(
658
703
  otel_span_dto: OTelSpanDTO,
@@ -1,4 +1,4 @@
1
- from typing import Optional, Dict
1
+ from typing import Optional, Dict, List
2
2
 
3
3
  from opentelemetry.context import Context
4
4
  from opentelemetry.sdk.trace import Span
@@ -7,6 +7,7 @@ from opentelemetry.sdk.trace.export import (
7
7
  ReadableSpan,
8
8
  BatchSpanProcessor,
9
9
  _DEFAULT_MAX_QUEUE_SIZE,
10
+ _DEFAULT_MAX_EXPORT_BATCH_SIZE,
10
11
  )
11
12
 
12
13
  from agenta.sdk.utils.logging import log
@@ -27,14 +28,15 @@ class TraceProcessor(BatchSpanProcessor):
27
28
  super().__init__(
28
29
  span_exporter,
29
30
  _DEFAULT_MAX_QUEUE_SIZE,
30
- 60 * 60 * 1000, # 1 hour
31
- _DEFAULT_MAX_QUEUE_SIZE,
31
+ 12 * 60 * 60 * 1000, # 12 hours
32
+ _DEFAULT_MAX_EXPORT_BATCH_SIZE,
32
33
  500, # < 1 second (0.5 seconds)
33
34
  )
34
35
 
35
36
  self._registry = dict()
36
37
  self._exporter = span_exporter
37
38
  self.references = references or dict()
39
+ self.spans: Dict[int, List[ReadableSpan]] = dict()
38
40
 
39
41
  def on_start(
40
42
  self,
@@ -55,12 +57,32 @@ class TraceProcessor(BatchSpanProcessor):
55
57
  self,
56
58
  span: ReadableSpan,
57
59
  ):
58
- super().on_end(span)
60
+ if self.done:
61
+ return
62
+
63
+ if span.context.trace_id not in self.spans:
64
+ self.spans[span.context.trace_id] = list()
65
+
66
+ self.spans[span.context.trace_id].append(span)
59
67
 
60
68
  del self._registry[span.context.trace_id][span.context.span_id]
61
69
 
62
- if self.is_ready(span.get_span_context().trace_id):
63
- self.force_flush()
70
+ if len(self._registry[span.context.trace_id]) == 0:
71
+ self.export(span.context.trace_id)
72
+
73
+ def export(
74
+ self,
75
+ trace_id: int,
76
+ ):
77
+ spans = self.spans[trace_id]
78
+
79
+ for span in spans:
80
+ self.queue.appendleft(span)
81
+
82
+ with self.condition:
83
+ self.condition.notify()
84
+
85
+ del self.spans[trace_id]
64
86
 
65
87
  def force_flush(
66
88
  self,
@@ -77,7 +99,12 @@ class TraceProcessor(BatchSpanProcessor):
77
99
  self,
78
100
  trace_id: Optional[int] = None,
79
101
  ) -> bool:
80
- is_ready = not len(self._registry.get(trace_id, {}))
102
+ is_ready = True
103
+
104
+ try:
105
+ is_ready = self._exporter.is_ready(trace_id)
106
+ except: # pylint: disable=bare-except
107
+ pass
81
108
 
82
109
  return is_ready
83
110
 
@@ -1,6 +1,5 @@
1
1
  from typing import Optional, Any, Dict
2
2
  from enum import Enum
3
- from uuid import UUID
4
3
 
5
4
  from httpx import get as check
6
5
 
@@ -156,15 +155,6 @@ class Tracing(metaclass=Singleton):
156
155
 
157
156
  for key in refs.keys():
158
157
  if key in [_.value for _ in Reference.__members__.values()]:
159
- # TYPE AND FORMAT CHECKING
160
- if key.endswith(".id"):
161
- try:
162
- refs[key] = str(UUID(refs[key]))
163
- except: # pylint: disable=bare-except
164
- refs[key] = None
165
-
166
- refs[key] = str(refs[key])
167
-
168
158
  # ADD REFERENCE TO THIS SPAN
169
159
  span.set_attribute(
170
160
  key.value if isinstance(key, Enum) else key,
agenta/sdk/types.py CHANGED
@@ -1,9 +1,8 @@
1
1
  import json
2
+ from dataclasses import dataclass
2
3
  from typing import Dict, List, Optional, Any, Union
3
4
 
4
5
  from pydantic import ConfigDict, BaseModel, HttpUrl
5
- from dataclasses import dataclass
6
- from typing import Union
7
6
 
8
7
 
9
8
  @dataclass
@@ -186,3 +185,49 @@ class FileInputURL(HttpUrl):
186
185
  @classmethod
187
186
  def __schema_type_properties__(cls) -> dict:
188
187
  return {"x-parameter": "file_url", "type": "string"}
188
+
189
+
190
+ class Context(BaseModel):
191
+ model_config = ConfigDict(extra="allow")
192
+
193
+ def to_json(self):
194
+ return self.model_dump()
195
+
196
+ @classmethod
197
+ def from_json(cls, json_str: str):
198
+ data = json.loads(json_str)
199
+ return cls(**data)
200
+
201
+
202
+ class ReferencesResponse(BaseModel):
203
+ app_id: Optional[str] = None
204
+ app_slug: Optional[str] = None
205
+ variant_id: Optional[str] = None
206
+ variant_slug: Optional[str] = None
207
+ variant_version: Optional[int] = None
208
+ environment_id: Optional[str] = None
209
+ environment_slug: Optional[str] = None
210
+ environment_version: Optional[int] = None
211
+
212
+ def __str__(self):
213
+ return str(self.model_dump(exclude_none=True))
214
+
215
+
216
+ class LifecyclesResponse(ReferencesResponse):
217
+ committed_at: Optional[str] = None
218
+ committed_by: Optional[str] = None
219
+ committed_by_id: Optional[str] = None
220
+ deployed_at: Optional[str] = None
221
+ deployed_by: Optional[str] = None
222
+ deployed_by_id: Optional[str] = None
223
+
224
+ def __str__(self):
225
+ return str(self.model_dump(exclude_none=True))
226
+
227
+
228
+ class ConfigurationResponse(LifecyclesResponse):
229
+ params: Dict[str, Any]
230
+
231
+
232
+ class DeploymentResponse(LifecyclesResponse):
233
+ pass
@@ -1,9 +1,12 @@
1
1
  from contextlib import AbstractContextManager
2
2
  from traceback import format_exc
3
+ from functools import wraps
4
+ from inspect import iscoroutinefunction
5
+
3
6
  from agenta.sdk.utils.logging import log
4
7
 
5
8
 
6
- class suppress(AbstractContextManager):
9
+ class suppress(AbstractContextManager): # pylint: disable=invalid-name
7
10
  def __init__(self):
8
11
  pass
9
12
 
@@ -20,3 +23,30 @@ class suppress(AbstractContextManager):
20
23
  log.error(format_exc().strip("\n"))
21
24
  log.error("-------------------------------------------------")
22
25
  return True
26
+
27
+
28
+ def handle_exceptions():
29
+ def decorator(func):
30
+ is_coroutine_function = iscoroutinefunction(func)
31
+
32
+ @wraps(func)
33
+ async def async_wrapper(*args, **kwargs):
34
+ try:
35
+ return await func(*args, **kwargs)
36
+ except Exception as e:
37
+ log.error("--- HANDLING EXCEPTION ---")
38
+ log.error("--------------------------")
39
+ raise e
40
+
41
+ @wraps(func)
42
+ def sync_wrapper(*args, **kwargs):
43
+ try:
44
+ return func(*args, **kwargs)
45
+ except Exception as e:
46
+ log.error("--- HANDLING EXCEPTION ---")
47
+ log.error("--------------------------")
48
+ raise e
49
+
50
+ return async_wrapper if is_coroutine_function else sync_wrapper
51
+
52
+ return decorator
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: agenta
3
- Version: 0.27.0a8
3
+ Version: 0.27.0a12
4
4
  Summary: The SDK for agenta is an open-source LLMOps platform.
5
5
  Home-page: https://agenta.ai
6
6
  Keywords: LLMOps,LLM,evaluation,prompt engineering