agenta 0.52.6__py3-none-any.whl → 0.63.2__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.
Files changed (271) hide show
  1. agenta/__init__.py +12 -3
  2. agenta/client/__init__.py +4 -4
  3. agenta/client/backend/__init__.py +4 -4
  4. agenta/client/backend/api_keys/client.py +2 -2
  5. agenta/client/backend/billing/client.py +2 -2
  6. agenta/client/backend/billing/raw_client.py +2 -2
  7. agenta/client/backend/client.py +56 -48
  8. agenta/client/backend/core/client_wrapper.py +2 -2
  9. agenta/client/backend/core/file.py +3 -1
  10. agenta/client/backend/core/http_client.py +3 -3
  11. agenta/client/backend/core/pydantic_utilities.py +13 -3
  12. agenta/client/backend/human_evaluations/client.py +2 -2
  13. agenta/client/backend/human_evaluations/raw_client.py +2 -2
  14. agenta/client/backend/organization/client.py +46 -34
  15. agenta/client/backend/organization/raw_client.py +32 -26
  16. agenta/client/backend/raw_client.py +26 -26
  17. agenta/client/backend/testsets/client.py +18 -18
  18. agenta/client/backend/testsets/raw_client.py +30 -30
  19. agenta/client/backend/types/__init__.py +4 -4
  20. agenta/client/backend/types/account_request.py +3 -1
  21. agenta/client/backend/types/account_response.py +3 -1
  22. agenta/client/backend/types/agenta_node_dto.py +3 -1
  23. agenta/client/backend/types/agenta_nodes_response.py +3 -1
  24. agenta/client/backend/types/agenta_root_dto.py +3 -1
  25. agenta/client/backend/types/agenta_roots_response.py +3 -1
  26. agenta/client/backend/types/agenta_tree_dto.py +3 -1
  27. agenta/client/backend/types/agenta_trees_response.py +3 -1
  28. agenta/client/backend/types/aggregated_result.py +3 -1
  29. agenta/client/backend/types/analytics_response.py +3 -1
  30. agenta/client/backend/types/annotation.py +6 -4
  31. agenta/client/backend/types/annotation_create.py +3 -1
  32. agenta/client/backend/types/annotation_edit.py +3 -1
  33. agenta/client/backend/types/annotation_link.py +3 -1
  34. agenta/client/backend/types/annotation_link_response.py +3 -1
  35. agenta/client/backend/types/annotation_query.py +3 -1
  36. agenta/client/backend/types/annotation_query_request.py +3 -1
  37. agenta/client/backend/types/annotation_reference.py +3 -1
  38. agenta/client/backend/types/annotation_references.py +3 -1
  39. agenta/client/backend/types/annotation_response.py +3 -1
  40. agenta/client/backend/types/annotations_response.py +3 -1
  41. agenta/client/backend/types/app.py +3 -1
  42. agenta/client/backend/types/app_variant_response.py +3 -1
  43. agenta/client/backend/types/app_variant_revision.py +3 -1
  44. agenta/client/backend/types/artifact.py +6 -4
  45. agenta/client/backend/types/base_output.py +3 -1
  46. agenta/client/backend/types/body_fetch_workflow_revision.py +3 -1
  47. agenta/client/backend/types/body_import_testset.py +3 -1
  48. agenta/client/backend/types/bucket_dto.py +3 -1
  49. agenta/client/backend/types/collect_status_response.py +3 -1
  50. agenta/client/backend/types/config_db.py +3 -1
  51. agenta/client/backend/types/config_dto.py +3 -1
  52. agenta/client/backend/types/config_response_model.py +3 -1
  53. agenta/client/backend/types/correct_answer.py +3 -1
  54. agenta/client/backend/types/create_app_output.py +3 -1
  55. agenta/client/backend/types/custom_model_settings_dto.py +3 -1
  56. agenta/client/backend/types/custom_provider_dto.py +3 -1
  57. agenta/client/backend/types/custom_provider_kind.py +1 -1
  58. agenta/client/backend/types/custom_provider_settings_dto.py +3 -1
  59. agenta/client/backend/types/delete_evaluation.py +3 -1
  60. agenta/client/backend/types/environment_output.py +3 -1
  61. agenta/client/backend/types/environment_output_extended.py +3 -1
  62. agenta/client/backend/types/environment_revision.py +3 -1
  63. agenta/client/backend/types/error.py +3 -1
  64. agenta/client/backend/types/evaluation.py +3 -1
  65. agenta/client/backend/types/evaluation_scenario.py +3 -1
  66. agenta/client/backend/types/evaluation_scenario_input.py +3 -1
  67. agenta/client/backend/types/evaluation_scenario_output.py +3 -1
  68. agenta/client/backend/types/evaluation_scenario_result.py +3 -1
  69. agenta/client/backend/types/evaluator.py +6 -4
  70. agenta/client/backend/types/evaluator_config.py +6 -4
  71. agenta/client/backend/types/evaluator_flags.py +3 -1
  72. agenta/client/backend/types/evaluator_mapping_output_interface.py +3 -1
  73. agenta/client/backend/types/evaluator_output_interface.py +3 -1
  74. agenta/client/backend/types/evaluator_query.py +3 -1
  75. agenta/client/backend/types/evaluator_query_request.py +3 -1
  76. agenta/client/backend/types/evaluator_request.py +3 -1
  77. agenta/client/backend/types/evaluator_response.py +3 -1
  78. agenta/client/backend/types/evaluators_response.py +3 -1
  79. agenta/client/backend/types/exception_dto.py +3 -1
  80. agenta/client/backend/types/extended_o_tel_tracing_response.py +3 -1
  81. agenta/client/backend/types/get_config_response.py +3 -1
  82. agenta/client/backend/types/header.py +3 -1
  83. agenta/client/backend/types/http_validation_error.py +3 -1
  84. agenta/client/backend/types/human_evaluation.py +3 -1
  85. agenta/client/backend/types/human_evaluation_scenario.py +3 -1
  86. agenta/client/backend/types/human_evaluation_scenario_input.py +3 -1
  87. agenta/client/backend/types/human_evaluation_scenario_output.py +3 -1
  88. agenta/client/backend/types/invite_request.py +3 -1
  89. agenta/client/backend/types/legacy_analytics_response.py +3 -1
  90. agenta/client/backend/types/legacy_data_point.py +3 -1
  91. agenta/client/backend/types/legacy_evaluator.py +3 -1
  92. agenta/client/backend/types/legacy_scope_request.py +3 -1
  93. agenta/client/backend/types/legacy_scopes_response.py +3 -1
  94. agenta/client/backend/types/legacy_subscription_request.py +3 -1
  95. agenta/client/backend/types/legacy_user_request.py +3 -1
  96. agenta/client/backend/types/legacy_user_response.py +3 -1
  97. agenta/client/backend/types/lifecycle_dto.py +3 -1
  98. agenta/client/backend/types/link_dto.py +3 -1
  99. agenta/client/backend/types/list_api_keys_response.py +3 -1
  100. agenta/client/backend/types/llm_run_rate_limit.py +3 -1
  101. agenta/client/backend/types/meta_request.py +3 -1
  102. agenta/client/backend/types/metrics_dto.py +3 -1
  103. agenta/client/backend/types/new_testset.py +3 -1
  104. agenta/client/backend/types/node_dto.py +3 -1
  105. agenta/client/backend/types/o_tel_context_dto.py +3 -1
  106. agenta/client/backend/types/o_tel_event.py +6 -4
  107. agenta/client/backend/types/o_tel_event_dto.py +3 -1
  108. agenta/client/backend/types/o_tel_extra_dto.py +3 -1
  109. agenta/client/backend/types/o_tel_flat_span.py +6 -4
  110. agenta/client/backend/types/o_tel_link.py +6 -4
  111. agenta/client/backend/types/o_tel_link_dto.py +3 -1
  112. agenta/client/backend/types/o_tel_links_response.py +3 -1
  113. agenta/client/backend/types/o_tel_span.py +1 -1
  114. agenta/client/backend/types/o_tel_span_dto.py +3 -1
  115. agenta/client/backend/types/o_tel_spans_tree.py +3 -1
  116. agenta/client/backend/types/o_tel_tracing_data_response.py +3 -1
  117. agenta/client/backend/types/o_tel_tracing_request.py +3 -1
  118. agenta/client/backend/types/o_tel_tracing_response.py +3 -1
  119. agenta/client/backend/types/organization.py +3 -1
  120. agenta/client/backend/types/organization_details.py +3 -1
  121. agenta/client/backend/types/organization_membership_request.py +3 -1
  122. agenta/client/backend/types/organization_output.py +3 -1
  123. agenta/client/backend/types/organization_request.py +3 -1
  124. agenta/client/backend/types/parent_dto.py +3 -1
  125. agenta/client/backend/types/project_membership_request.py +3 -1
  126. agenta/client/backend/types/project_request.py +3 -1
  127. agenta/client/backend/types/project_scope.py +3 -1
  128. agenta/client/backend/types/projects_response.py +3 -1
  129. agenta/client/backend/types/reference.py +6 -4
  130. agenta/client/backend/types/reference_dto.py +3 -1
  131. agenta/client/backend/types/reference_request_model.py +3 -1
  132. agenta/client/backend/types/result.py +3 -1
  133. agenta/client/backend/types/root_dto.py +3 -1
  134. agenta/client/backend/types/scopes_response_model.py +3 -1
  135. agenta/client/backend/types/secret_dto.py +3 -1
  136. agenta/client/backend/types/secret_response_dto.py +3 -1
  137. agenta/client/backend/types/simple_evaluation_output.py +3 -1
  138. agenta/client/backend/types/span_dto.py +6 -4
  139. agenta/client/backend/types/standard_provider_dto.py +3 -1
  140. agenta/client/backend/types/standard_provider_settings_dto.py +3 -1
  141. agenta/client/backend/types/status_dto.py +3 -1
  142. agenta/client/backend/types/tags_request.py +3 -1
  143. agenta/client/backend/types/testcase_response.py +6 -4
  144. agenta/client/backend/types/testset.py +6 -4
  145. agenta/client/backend/types/{test_set_output_response.py → testset_output_response.py} +4 -2
  146. agenta/client/backend/types/testset_request.py +3 -1
  147. agenta/client/backend/types/testset_response.py +3 -1
  148. agenta/client/backend/types/{test_set_simple_response.py → testset_simple_response.py} +4 -2
  149. agenta/client/backend/types/testsets_response.py +3 -1
  150. agenta/client/backend/types/time_dto.py +3 -1
  151. agenta/client/backend/types/tree_dto.py +3 -1
  152. agenta/client/backend/types/update_app_output.py +3 -1
  153. agenta/client/backend/types/user_request.py +3 -1
  154. agenta/client/backend/types/validation_error.py +3 -1
  155. agenta/client/backend/types/workflow_artifact.py +6 -4
  156. agenta/client/backend/types/workflow_data.py +3 -1
  157. agenta/client/backend/types/workflow_flags.py +3 -1
  158. agenta/client/backend/types/workflow_request.py +3 -1
  159. agenta/client/backend/types/workflow_response.py +3 -1
  160. agenta/client/backend/types/workflow_revision.py +6 -4
  161. agenta/client/backend/types/workflow_revision_request.py +3 -1
  162. agenta/client/backend/types/workflow_revision_response.py +3 -1
  163. agenta/client/backend/types/workflow_revisions_response.py +3 -1
  164. agenta/client/backend/types/workflow_variant.py +6 -4
  165. agenta/client/backend/types/workflow_variant_request.py +3 -1
  166. agenta/client/backend/types/workflow_variant_response.py +3 -1
  167. agenta/client/backend/types/workflow_variants_response.py +3 -1
  168. agenta/client/backend/types/workflows_response.py +3 -1
  169. agenta/client/backend/types/workspace.py +3 -1
  170. agenta/client/backend/types/workspace_member_response.py +3 -1
  171. agenta/client/backend/types/workspace_membership_request.py +3 -1
  172. agenta/client/backend/types/workspace_permission.py +3 -1
  173. agenta/client/backend/types/workspace_request.py +3 -1
  174. agenta/client/backend/types/workspace_response.py +3 -1
  175. agenta/client/backend/vault/raw_client.py +4 -4
  176. agenta/client/backend/workspace/client.py +2 -2
  177. agenta/client/client.py +102 -88
  178. agenta/sdk/__init__.py +52 -3
  179. agenta/sdk/agenta_init.py +43 -16
  180. agenta/sdk/assets.py +23 -15
  181. agenta/sdk/context/serving.py +20 -8
  182. agenta/sdk/context/tracing.py +40 -22
  183. agenta/sdk/contexts/__init__.py +0 -0
  184. agenta/sdk/contexts/routing.py +38 -0
  185. agenta/sdk/contexts/running.py +57 -0
  186. agenta/sdk/contexts/tracing.py +86 -0
  187. agenta/sdk/decorators/__init__.py +1 -0
  188. agenta/sdk/decorators/routing.py +284 -0
  189. agenta/sdk/decorators/running.py +692 -98
  190. agenta/sdk/decorators/serving.py +20 -21
  191. agenta/sdk/decorators/tracing.py +176 -131
  192. agenta/sdk/engines/__init__.py +0 -0
  193. agenta/sdk/engines/running/__init__.py +0 -0
  194. agenta/sdk/engines/running/utils.py +17 -0
  195. agenta/sdk/engines/tracing/__init__.py +1 -0
  196. agenta/sdk/engines/tracing/attributes.py +185 -0
  197. agenta/sdk/engines/tracing/conventions.py +49 -0
  198. agenta/sdk/engines/tracing/exporters.py +130 -0
  199. agenta/sdk/engines/tracing/inline.py +1154 -0
  200. agenta/sdk/engines/tracing/processors.py +190 -0
  201. agenta/sdk/engines/tracing/propagation.py +102 -0
  202. agenta/sdk/engines/tracing/spans.py +136 -0
  203. agenta/sdk/engines/tracing/tracing.py +324 -0
  204. agenta/sdk/evaluations/__init__.py +2 -0
  205. agenta/sdk/evaluations/metrics.py +37 -0
  206. agenta/sdk/evaluations/preview/__init__.py +0 -0
  207. agenta/sdk/evaluations/preview/evaluate.py +765 -0
  208. agenta/sdk/evaluations/preview/utils.py +861 -0
  209. agenta/sdk/evaluations/results.py +66 -0
  210. agenta/sdk/evaluations/runs.py +153 -0
  211. agenta/sdk/evaluations/scenarios.py +48 -0
  212. agenta/sdk/litellm/litellm.py +12 -0
  213. agenta/sdk/litellm/mockllm.py +6 -8
  214. agenta/sdk/litellm/mocks/__init__.py +5 -5
  215. agenta/sdk/managers/applications.py +304 -0
  216. agenta/sdk/managers/config.py +2 -2
  217. agenta/sdk/managers/evaluations.py +0 -0
  218. agenta/sdk/managers/evaluators.py +303 -0
  219. agenta/sdk/managers/secrets.py +161 -24
  220. agenta/sdk/managers/shared.py +3 -1
  221. agenta/sdk/managers/testsets.py +441 -0
  222. agenta/sdk/managers/vault.py +3 -3
  223. agenta/sdk/middleware/auth.py +0 -176
  224. agenta/sdk/middleware/config.py +27 -9
  225. agenta/sdk/middleware/vault.py +204 -9
  226. agenta/sdk/middlewares/__init__.py +0 -0
  227. agenta/sdk/middlewares/routing/__init__.py +0 -0
  228. agenta/sdk/middlewares/routing/auth.py +263 -0
  229. agenta/sdk/middlewares/routing/cors.py +30 -0
  230. agenta/sdk/middlewares/routing/otel.py +29 -0
  231. agenta/sdk/middlewares/running/__init__.py +0 -0
  232. agenta/sdk/middlewares/running/normalizer.py +321 -0
  233. agenta/sdk/middlewares/running/resolver.py +161 -0
  234. agenta/sdk/middlewares/running/vault.py +140 -0
  235. agenta/sdk/models/__init__.py +0 -0
  236. agenta/sdk/models/blobs.py +33 -0
  237. agenta/sdk/models/evaluations.py +119 -0
  238. agenta/sdk/models/git.py +126 -0
  239. agenta/sdk/models/shared.py +167 -0
  240. agenta/sdk/models/testsets.py +163 -0
  241. agenta/sdk/models/tracing.py +202 -0
  242. agenta/sdk/models/workflows.py +753 -0
  243. agenta/sdk/tracing/attributes.py +4 -4
  244. agenta/sdk/tracing/exporters.py +67 -17
  245. agenta/sdk/tracing/inline.py +37 -45
  246. agenta/sdk/tracing/processors.py +97 -0
  247. agenta/sdk/tracing/propagation.py +3 -1
  248. agenta/sdk/tracing/spans.py +4 -0
  249. agenta/sdk/tracing/tracing.py +13 -15
  250. agenta/sdk/types.py +222 -22
  251. agenta/sdk/utils/cache.py +1 -1
  252. agenta/sdk/utils/client.py +38 -0
  253. agenta/sdk/utils/helpers.py +13 -12
  254. agenta/sdk/utils/logging.py +18 -78
  255. agenta/sdk/utils/references.py +23 -0
  256. agenta/sdk/workflows/builtin.py +600 -0
  257. agenta/sdk/workflows/configurations.py +22 -0
  258. agenta/sdk/workflows/errors.py +292 -0
  259. agenta/sdk/workflows/handlers.py +1791 -0
  260. agenta/sdk/workflows/interfaces.py +948 -0
  261. agenta/sdk/workflows/sandbox.py +118 -0
  262. agenta/sdk/workflows/utils.py +303 -6
  263. {agenta-0.52.6.dist-info → agenta-0.63.2.dist-info}/METADATA +37 -33
  264. agenta-0.63.2.dist-info/RECORD +421 -0
  265. {agenta-0.52.6.dist-info → agenta-0.63.2.dist-info}/WHEEL +1 -1
  266. agenta/sdk/middleware/adapt.py +0 -253
  267. agenta/sdk/middleware/base.py +0 -40
  268. agenta/sdk/middleware/flags.py +0 -40
  269. agenta/sdk/workflows/types.py +0 -472
  270. agenta-0.52.6.dist-info/RECORD +0 -371
  271. /agenta/sdk/{workflows → engines/running}/registry.py +0 -0
@@ -0,0 +1,185 @@
1
+ from json import loads, dumps
2
+ from typing import Optional, Union, Sequence, Any, Dict
3
+
4
+ Primitive = Union[str, int, float, bool, bytes]
5
+ PrimitivesSequence = Sequence[Primitive]
6
+ Attribute = Union[Primitive, PrimitivesSequence]
7
+
8
+
9
+ def _marshal(
10
+ unmarshalled: Dict[str, Any],
11
+ *,
12
+ parent_key: Optional[str] = "",
13
+ depth: Optional[int] = 0,
14
+ max_depth: Optional[int] = None,
15
+ ) -> Dict[str, Any]:
16
+ """
17
+ Marshals a dictionary of unmarshalled attributes into a flat dictionary
18
+
19
+ Example:
20
+ unmarshalled = {
21
+ "ag": {
22
+ "type": "tree",
23
+ "node": {
24
+ "name": "root",
25
+ "children": [
26
+ {
27
+ "name": "child1",
28
+ },
29
+ {
30
+ "name": "child2",
31
+ }
32
+ ]
33
+ }
34
+ }
35
+ }
36
+ marshalled = {
37
+ "ag.type": "tree",
38
+ "ag.node.name": "root",
39
+ "ag.node.children.0.name": "child1",
40
+ "ag.node.children.1.name": "child2"
41
+ }
42
+ """
43
+ marshalled = {}
44
+
45
+ # If max_depth is set and we've reached it,
46
+ # just return the unmarshalled attributes
47
+ if max_depth is not None and depth >= max_depth:
48
+ marshalled[parent_key] = unmarshalled
49
+ # MISSING ENCODING TO JSON IF NOT PRIMITIVE
50
+
51
+ return marshalled
52
+
53
+ # Otherwise,
54
+ # iterate over the unmarshalled attributes and marshall them
55
+ for key, value in unmarshalled.items():
56
+ child_key = f"{parent_key}.{key}" if parent_key else key
57
+
58
+ if isinstance(value, dict):
59
+ dict_key = child_key
60
+
61
+ marshalled.update(
62
+ _marshal(
63
+ value,
64
+ parent_key=dict_key,
65
+ depth=depth + 1,
66
+ max_depth=max_depth,
67
+ )
68
+ )
69
+ elif isinstance(value, list):
70
+ if max_depth is not None and depth + 1 >= max_depth:
71
+ marshalled[child_key] = value
72
+ # MISSING ENCODING TO JSON IF NOT PRIMITIVE
73
+ else:
74
+ for i, item in enumerate(value):
75
+ list_key = f"{child_key}.{i}"
76
+
77
+ if isinstance(item, (dict, list)):
78
+ marshalled.update(
79
+ _marshal(
80
+ item,
81
+ parent_key=list_key,
82
+ depth=depth + 1,
83
+ max_depth=max_depth,
84
+ )
85
+ )
86
+ else:
87
+ marshalled[list_key] = item
88
+ # MISSING ENCODING TO JSON IF NOT PRIMITIVE
89
+ else:
90
+ marshalled[child_key] = value
91
+ # MISSING ENCODING TO JSON IF NOT PRIMITIVE
92
+
93
+ return marshalled
94
+
95
+
96
+ def _encode_key(
97
+ namespace: Optional[str] = None,
98
+ key: str = "",
99
+ ) -> str:
100
+ if namespace is None:
101
+ return key
102
+
103
+ return f"ag.{namespace}.{key}"
104
+
105
+
106
+ def _make_serializable(value: Any) -> Any:
107
+ """
108
+ Transform complex nested structures into JSON-serializable form.
109
+ Handles Pydantic models, nested dictionaries and lists recursively.
110
+ """
111
+ if value is None or isinstance(value, (str, int, float, bool, bytes)):
112
+ return value
113
+
114
+ # Handle Pydantic objects (prioritize v2 over v1 API)
115
+ if hasattr(value, "model_dump"): # Pydantic v2
116
+ return value.model_dump()
117
+ elif hasattr(value, "dict"): # Pydantic v1
118
+ return value.dict()
119
+
120
+ if isinstance(value, dict):
121
+ try:
122
+ # Test serialization without modifying - optimizes for already-serializable dicts
123
+ dumps(
124
+ value
125
+ ) # If serialization fails, we'll catch the exception and process deeply
126
+ return value # Avoid unnecessary recursion for serializable dicts
127
+ except TypeError:
128
+ return {k: _make_serializable(v) for k, v in value.items()}
129
+ elif isinstance(value, list):
130
+ try:
131
+ # Test serialization without modifying - optimizes for already-serializable lists
132
+ dumps(
133
+ value
134
+ ) # If serialization fails, we'll catch the exception and process deeply
135
+ return value # Avoid unnecessary recursion for serializable lists
136
+ except TypeError:
137
+ return [_make_serializable(item) for item in value]
138
+
139
+ return repr(value)
140
+
141
+
142
+ def _encode_value(value: Any) -> Optional[Attribute]:
143
+ """
144
+ Encode values for tracing, ensuring proper JSON serialization.
145
+ Adds the @ag.type=json: prefix only to appropriate values.
146
+ """
147
+ if value is None:
148
+ return None
149
+
150
+ if isinstance(value, (str, int, float, bool, bytes)):
151
+ return value
152
+
153
+ try:
154
+ if (
155
+ isinstance(value, (dict, list))
156
+ or hasattr(value, "model_dump")
157
+ or hasattr(value, "dict")
158
+ ):
159
+ serializable_value = _make_serializable(value)
160
+ return "@ag.type=json:" + dumps(serializable_value)
161
+ except TypeError:
162
+ pass
163
+
164
+ return repr(value)
165
+
166
+
167
+ def serialize(
168
+ *,
169
+ namespace: str,
170
+ attributes: Dict[str, Any],
171
+ max_depth: Optional[int] = None,
172
+ ) -> Dict[str, str]:
173
+ if not isinstance(attributes, dict):
174
+ return {}
175
+
176
+ _attributes = {
177
+ k: v
178
+ for k, v in {
179
+ _encode_key(namespace, key): _encode_value(value)
180
+ for key, value in _marshal(attributes, max_depth=max_depth).items()
181
+ }.items()
182
+ if v is not None
183
+ }
184
+
185
+ return _attributes
@@ -0,0 +1,49 @@
1
+ from enum import Enum
2
+ from re import fullmatch
3
+
4
+ from opentelemetry.trace import SpanKind
5
+
6
+
7
+ class Reference(str, Enum):
8
+ #
9
+ VARIANT_ID = "variant.id"
10
+ VARIANT_SLUG = "variant.slug"
11
+ VARIANT_VERSION = "variant.version"
12
+ #
13
+ ENVIRONMENT_ID = "environment.id"
14
+ ENVIRONMENT_SLUG = "environment.slug"
15
+ ENVIRONMENT_VERSION = "environment.version"
16
+ #
17
+ APPLICATION_ID = "application.id"
18
+ APPLICATION_SLUG = "application.slug"
19
+ #
20
+
21
+
22
+ _PATTERN = r"[A-Za-z0-9._-]+"
23
+
24
+
25
+ def is_valid_attribute_key(
26
+ string: str,
27
+ ):
28
+ return bool(fullmatch(_PATTERN, string))
29
+
30
+
31
+ def parse_span_kind(type: str) -> SpanKind:
32
+ kind = SpanKind.INTERNAL
33
+ if type in [
34
+ "agent",
35
+ "chain",
36
+ "workflow",
37
+ ]:
38
+ kind = SpanKind.SERVER
39
+ elif type in [
40
+ "tool",
41
+ "embedding",
42
+ "query",
43
+ "completion",
44
+ "chat",
45
+ "rerank",
46
+ ]:
47
+ kind = SpanKind.CLIENT
48
+
49
+ return kind
@@ -0,0 +1,130 @@
1
+ from typing import Sequence, Dict, List, Optional
2
+
3
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
4
+ from opentelemetry.sdk.trace.export import (
5
+ ConsoleSpanExporter,
6
+ SpanExporter,
7
+ SpanExportResult,
8
+ ReadableSpan,
9
+ )
10
+
11
+ from agenta.sdk.utils.logging import get_module_logger
12
+ from agenta.sdk.utils.exceptions import suppress
13
+ from agenta.sdk.utils.cache import TTLLRUCache
14
+ from agenta.sdk.contexts.tracing import (
15
+ otlp_context_manager,
16
+ otlp_context,
17
+ OTLPContext,
18
+ )
19
+
20
+
21
+ log = get_module_logger(__name__)
22
+
23
+
24
+ class InlineTraceExporter(SpanExporter):
25
+ def __init__(
26
+ self,
27
+ registry: Dict[str, List[ReadableSpan]],
28
+ ):
29
+ self._shutdown = False
30
+ self._registry = registry
31
+
32
+ def export(
33
+ self,
34
+ spans: Sequence[ReadableSpan],
35
+ ) -> SpanExportResult:
36
+ if self._shutdown:
37
+ return
38
+
39
+ with suppress():
40
+ for span in spans:
41
+ trace_id = span.get_span_context().trace_id
42
+
43
+ if trace_id not in self._registry:
44
+ self._registry[trace_id] = []
45
+
46
+ self._registry[trace_id].append(span)
47
+
48
+ def shutdown(self) -> None:
49
+ self._shutdown = True
50
+
51
+ def force_flush(self, timeout_millis: int = 30000) -> bool:
52
+ return True
53
+
54
+ def is_ready(
55
+ self,
56
+ trace_id: int,
57
+ ) -> bool:
58
+ is_ready = trace_id in self._registry
59
+ return is_ready
60
+
61
+ def fetch(
62
+ self,
63
+ trace_id: int,
64
+ ) -> List[ReadableSpan]:
65
+ trace = self._registry.get(trace_id, [])
66
+
67
+ if trace_id in self._registry:
68
+ del self._registry[trace_id]
69
+
70
+ return trace
71
+
72
+
73
+ class OTLPExporter(OTLPSpanExporter):
74
+ _MAX_RETRY_TIMEOUT = 2
75
+
76
+ def __init__(
77
+ self,
78
+ *args,
79
+ credentials: Optional[TTLLRUCache] = None,
80
+ **kwargs,
81
+ ):
82
+ super().__init__(*args, **kwargs)
83
+
84
+ self.credentials = credentials
85
+
86
+ def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
87
+ grouped_spans: Dict[str, List[str]] = {}
88
+
89
+ for span in spans:
90
+ trace_id = span.get_span_context().trace_id
91
+
92
+ credentials = None
93
+ if self.credentials:
94
+ credentials = self.credentials.get(trace_id)
95
+
96
+ if credentials not in grouped_spans:
97
+ grouped_spans[credentials] = []
98
+
99
+ grouped_spans[credentials].append(span)
100
+
101
+ serialized_spans = []
102
+
103
+ for credentials, _spans in grouped_spans.items():
104
+ with otlp_context_manager(
105
+ context=OTLPContext(
106
+ credentials=credentials,
107
+ )
108
+ ):
109
+ serialized_spans.append(super().export(_spans))
110
+
111
+ if all(serialized_spans):
112
+ return SpanExportResult.SUCCESS
113
+ else:
114
+ return SpanExportResult.FAILURE
115
+
116
+ def _export(self, serialized_data: bytes, timeout_sec: Optional[float] = None):
117
+ credentials = otlp_context.get().credentials
118
+
119
+ if credentials:
120
+ self._session.headers.update({"Authorization": credentials})
121
+
122
+ with suppress():
123
+ if timeout_sec is not None:
124
+ return super()._export(serialized_data, timeout_sec)
125
+ else:
126
+ return super()._export(serialized_data)
127
+
128
+
129
+ ConsoleExporter = ConsoleSpanExporter
130
+ InlineExporter = InlineTraceExporter