agenta 0.27.0__py3-none-any.whl → 0.27.0a0__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/__init__.py +3 -23
- agenta/cli/helper.py +1 -5
- agenta/client/backend/__init__.py +0 -14
- agenta/client/backend/apps/client.py +20 -28
- agenta/client/backend/client.py +2 -25
- agenta/client/backend/containers/client.py +1 -5
- agenta/client/backend/core/__init__.py +1 -2
- agenta/client/backend/core/client_wrapper.py +6 -6
- agenta/client/backend/core/file.py +11 -33
- agenta/client/backend/core/http_client.py +18 -24
- agenta/client/backend/core/pydantic_utilities.py +29 -144
- agenta/client/backend/core/request_options.py +0 -3
- agenta/client/backend/core/serialization.py +42 -139
- agenta/client/backend/evaluations/client.py +2 -7
- agenta/client/backend/evaluators/client.py +1 -349
- agenta/client/backend/observability/client.py +2 -11
- agenta/client/backend/testsets/client.py +10 -10
- agenta/client/backend/types/__init__.py +0 -14
- agenta/client/backend/types/app.py +0 -1
- agenta/client/backend/types/app_variant_response.py +1 -3
- agenta/client/backend/types/create_span.py +2 -3
- agenta/client/backend/types/environment_output.py +0 -1
- agenta/client/backend/types/environment_output_extended.py +0 -1
- agenta/client/backend/types/evaluation.py +2 -1
- agenta/client/backend/types/evaluator.py +0 -2
- agenta/client/backend/types/evaluator_config.py +0 -1
- agenta/client/backend/types/human_evaluation.py +2 -1
- agenta/client/backend/types/llm_tokens.py +2 -2
- agenta/client/backend/types/span.py +0 -1
- agenta/client/backend/types/span_detail.py +1 -7
- agenta/client/backend/types/test_set_output_response.py +2 -5
- agenta/client/backend/types/trace_detail.py +1 -7
- agenta/client/backend/types/with_pagination.py +2 -4
- agenta/client/backend/variants/client.py +273 -1566
- agenta/docker/docker-assets/Dockerfile.cloud.template +1 -1
- agenta/sdk/__init__.py +5 -21
- agenta/sdk/agenta_init.py +29 -34
- agenta/sdk/config_manager.py +205 -0
- agenta/sdk/context/routing.py +5 -6
- agenta/sdk/decorators/routing.py +146 -158
- agenta/sdk/decorators/tracing.py +239 -206
- agenta/sdk/litellm/litellm.py +36 -47
- agenta/sdk/tracing/attributes.py +47 -7
- agenta/sdk/tracing/context.py +2 -5
- agenta/sdk/tracing/conventions.py +19 -25
- agenta/sdk/tracing/exporters.py +5 -17
- agenta/sdk/tracing/inline.py +146 -92
- agenta/sdk/tracing/processors.py +13 -65
- agenta/sdk/tracing/spans.py +4 -16
- agenta/sdk/tracing/tracing.py +65 -124
- agenta/sdk/types.py +2 -61
- agenta/sdk/utils/exceptions.py +5 -38
- {agenta-0.27.0.dist-info → agenta-0.27.0a0.dist-info}/METADATA +1 -1
- {agenta-0.27.0.dist-info → agenta-0.27.0a0.dist-info}/RECORD +56 -67
- agenta/client/backend/types/config_dto.py +0 -32
- agenta/client/backend/types/config_response_model.py +0 -32
- agenta/client/backend/types/evaluator_mapping_output_interface.py +0 -21
- agenta/client/backend/types/evaluator_output_interface.py +0 -21
- agenta/client/backend/types/lifecycle_dto.py +0 -24
- agenta/client/backend/types/reference_dto.py +0 -23
- agenta/client/backend/types/reference_request_model.py +0 -23
- agenta/sdk/managers/__init__.py +0 -6
- agenta/sdk/managers/config.py +0 -318
- agenta/sdk/managers/deployment.py +0 -45
- agenta/sdk/managers/shared.py +0 -639
- agenta/sdk/managers/variant.py +0 -182
- {agenta-0.27.0.dist-info → agenta-0.27.0a0.dist-info}/WHEEL +0 -0
- {agenta-0.27.0.dist-info → agenta-0.27.0a0.dist-info}/entry_points.txt +0 -0
agenta/sdk/tracing/attributes.py
CHANGED
|
@@ -93,19 +93,14 @@ def _marshal(
|
|
|
93
93
|
return marshalled
|
|
94
94
|
|
|
95
95
|
|
|
96
|
-
def _encode_key(
|
|
97
|
-
namespace: Optional[str] = None,
|
|
98
|
-
key: str = "",
|
|
99
|
-
) -> str:
|
|
96
|
+
def _encode_key(namespace: Optional[str] = None, key: str = "") -> str:
|
|
100
97
|
if namespace is None:
|
|
101
98
|
return key
|
|
102
99
|
|
|
103
100
|
return f"ag.{namespace}.{key}"
|
|
104
101
|
|
|
105
102
|
|
|
106
|
-
def _encode_value(
|
|
107
|
-
value: Any,
|
|
108
|
-
) -> Optional[Attribute]:
|
|
103
|
+
def _encode_value(value: Any) -> Optional[Attribute]:
|
|
109
104
|
if value is None:
|
|
110
105
|
return None
|
|
111
106
|
|
|
@@ -139,3 +134,48 @@ def serialize(
|
|
|
139
134
|
}
|
|
140
135
|
|
|
141
136
|
return _attributes
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
"""
|
|
140
|
+
def _decode_key(namespace: Optional[str] = None, key: str = "") -> str:
|
|
141
|
+
if namespace is None:
|
|
142
|
+
return key
|
|
143
|
+
|
|
144
|
+
return key.replace(f"ag.{namespace}.", "")
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
"""
|
|
148
|
+
def _decode_value(value: Attribute) -> Any:
|
|
149
|
+
if isinstance(value, (int, float, bool, bytes)):
|
|
150
|
+
return value
|
|
151
|
+
|
|
152
|
+
if isinstance(value, str):
|
|
153
|
+
if value == "@ag.type=none:":
|
|
154
|
+
return None
|
|
155
|
+
|
|
156
|
+
if value.startswith("@ag.type=json:"):
|
|
157
|
+
encoded = value[len("@ag.type=json:") :]
|
|
158
|
+
value = loads(encoded)
|
|
159
|
+
return value
|
|
160
|
+
|
|
161
|
+
return value
|
|
162
|
+
|
|
163
|
+
return value
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
"""
|
|
167
|
+
def deserialize(
|
|
168
|
+
*,
|
|
169
|
+
namespace: str,
|
|
170
|
+
attributes: Dict[str, Any],
|
|
171
|
+
max_depth: Optional[int] = None,
|
|
172
|
+
) -> Dict[str, Any]:
|
|
173
|
+
if not isinstance(attributes, dict):
|
|
174
|
+
return {}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
_decode_key(namespace, key): _decode_value(value)
|
|
178
|
+
for key, value in attributes
|
|
179
|
+
if key != _decode_key(namespace, key)
|
|
180
|
+
}
|
|
181
|
+
"""
|
agenta/sdk/tracing/context.py
CHANGED
|
@@ -15,10 +15,7 @@ def tracing_context_manager():
|
|
|
15
15
|
try:
|
|
16
16
|
yield
|
|
17
17
|
except Exception as e:
|
|
18
|
-
log.error("
|
|
19
|
-
log.error("
|
|
20
|
-
log.error("----------------------------------------------")
|
|
21
|
-
log.error(format_exc().strip("\n"))
|
|
22
|
-
log.error("----------------------------------------------")
|
|
18
|
+
log.error(f"Error with tracing context: {_tracing_context}")
|
|
19
|
+
log.error(f"Exception: {format_exc()}")
|
|
23
20
|
finally:
|
|
24
21
|
tracing_context.reset(token)
|
|
@@ -1,31 +1,25 @@
|
|
|
1
|
-
from
|
|
2
|
-
from re import fullmatch
|
|
1
|
+
from typing import Literal
|
|
3
2
|
|
|
4
3
|
from opentelemetry.trace import SpanKind
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def is_valid_attribute_key(
|
|
26
|
-
string: str,
|
|
27
|
-
):
|
|
28
|
-
return bool(fullmatch(_PATTERN, string))
|
|
5
|
+
Namespace = Literal[
|
|
6
|
+
"data.inputs",
|
|
7
|
+
"data.internals",
|
|
8
|
+
"data.outputs",
|
|
9
|
+
"metrics.scores",
|
|
10
|
+
"metrics.unit.costs",
|
|
11
|
+
"metrics.unit.tokens",
|
|
12
|
+
"meta.configuration",
|
|
13
|
+
"meta.version",
|
|
14
|
+
"tags",
|
|
15
|
+
"refs",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
Code = Literal[
|
|
19
|
+
"OK",
|
|
20
|
+
"UNSET",
|
|
21
|
+
"ERROR",
|
|
22
|
+
]
|
|
29
23
|
|
|
30
24
|
|
|
31
25
|
def parse_span_kind(type: str) -> SpanKind:
|
agenta/sdk/tracing/exporters.py
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
from typing import Sequence, Dict, List
|
|
2
2
|
|
|
3
|
-
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
4
3
|
from opentelemetry.sdk.trace.export import (
|
|
5
4
|
ConsoleSpanExporter,
|
|
6
5
|
SpanExporter,
|
|
7
6
|
SpanExportResult,
|
|
8
7
|
ReadableSpan,
|
|
9
8
|
)
|
|
9
|
+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
|
|
10
|
+
OTLPSpanExporter,
|
|
11
|
+
)
|
|
10
12
|
|
|
11
13
|
from agenta.sdk.utils.exceptions import suppress
|
|
12
14
|
|
|
@@ -38,28 +40,14 @@ class InlineTraceExporter(SpanExporter):
|
|
|
38
40
|
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
39
41
|
return True
|
|
40
42
|
|
|
41
|
-
def
|
|
42
|
-
self,
|
|
43
|
-
trace_id: int,
|
|
44
|
-
) -> bool:
|
|
45
|
-
is_ready = trace_id in self._registry
|
|
46
|
-
|
|
47
|
-
return is_ready
|
|
48
|
-
|
|
49
|
-
def fetch(
|
|
50
|
-
self,
|
|
51
|
-
trace_id: int,
|
|
52
|
-
) -> List[ReadableSpan]:
|
|
43
|
+
def fetch(self, trace_id: int) -> List[ReadableSpan]:
|
|
53
44
|
trace = self._registry.get(trace_id, [])
|
|
54
45
|
|
|
55
|
-
|
|
56
|
-
del self._registry[trace_id]
|
|
46
|
+
del self._registry[trace_id]
|
|
57
47
|
|
|
58
48
|
return trace
|
|
59
49
|
|
|
60
50
|
|
|
61
|
-
OTLPSpanExporter._MAX_RETRY_TIMEOUT = 2
|
|
62
|
-
|
|
63
51
|
ConsoleExporter = ConsoleSpanExporter
|
|
64
52
|
InlineExporter = InlineTraceExporter
|
|
65
53
|
OTLPExporter = OTLPSpanExporter
|
agenta/sdk/tracing/inline.py
CHANGED
|
@@ -11,11 +11,82 @@ from enum import Enum
|
|
|
11
11
|
from collections import OrderedDict
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
NOF_CHARS = 8
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _p_id(id):
|
|
18
|
+
return repr(str(id)[:NOF_CHARS])
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _p_osa(o):
|
|
22
|
+
elements = []
|
|
23
|
+
|
|
24
|
+
for i in OrderedDict(sorted(o.items())).items():
|
|
25
|
+
if not i[0].startswith("_"):
|
|
26
|
+
if i[1].__class__.__module__ != "builtins":
|
|
27
|
+
if repr(i[1]).startswith("<"):
|
|
28
|
+
elements.append(f"{i[0]}: {i[1].name}")
|
|
29
|
+
elif repr(i[1]).startswith("UUID("):
|
|
30
|
+
elements.append(f"{i[0]}: {_p_id(i[1])}")
|
|
31
|
+
else:
|
|
32
|
+
elements.append(f"{i[0]}: {i[1].__str__()}")
|
|
33
|
+
else:
|
|
34
|
+
if isinstance(i[1], list):
|
|
35
|
+
elements.append(
|
|
36
|
+
f"{i[0]}: [" + ", ".join([el.__str__() for el in i[1]]) + "]"
|
|
37
|
+
)
|
|
38
|
+
elif isinstance(i[1], dict):
|
|
39
|
+
elements.append(f"{i[0]}: {{{_p_osa(i[1])}}}")
|
|
40
|
+
else:
|
|
41
|
+
if i[1] is not None:
|
|
42
|
+
if i[0] == "slug":
|
|
43
|
+
elements.append(f"{i[0]}: {repr(i[1][:8])}")
|
|
44
|
+
else:
|
|
45
|
+
elements.append(f"{i[0]}: {repr(i[1])}")
|
|
46
|
+
|
|
47
|
+
return ", ".join(elements)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _p_ora(o, open="{", close="}", sep=": ", foo=repr):
|
|
51
|
+
if o.__class__.__module__ != "builtins":
|
|
52
|
+
if o.__class__.__name__ == "UUID":
|
|
53
|
+
return repr(o)
|
|
54
|
+
if isinstance(o, Enum):
|
|
55
|
+
return o
|
|
56
|
+
if isinstance(o, datetime):
|
|
57
|
+
return o.isoformat()
|
|
58
|
+
return f"{o.__class__.__name__}({_p_ora(o.__dict__, open='', close='', sep='=', foo=lambda x : x)})"
|
|
59
|
+
elif isinstance(o, list):
|
|
60
|
+
return f"[{', '.join([repr(el) for el in o])}]"
|
|
61
|
+
elif isinstance(o, dict):
|
|
62
|
+
o = OrderedDict(sorted(o.items()))
|
|
63
|
+
return f"{open}{', '.join([f'{foo(elk)}{sep}{_p_ora(elv)}' for elk, elv in o.items()])}{close}"
|
|
64
|
+
else:
|
|
65
|
+
if o is not None:
|
|
66
|
+
return repr(o)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _str(o):
|
|
70
|
+
return f"{{{_p_osa(o.__dict__)}}}"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _repr(o):
|
|
74
|
+
return _p_ora(o)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class DisplayBase(BaseModel):
|
|
78
|
+
def __str__(self):
|
|
79
|
+
return _str(self)
|
|
80
|
+
|
|
81
|
+
def __repr__(self):
|
|
82
|
+
return _repr(self)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class ProjectScopeDTO(DisplayBase):
|
|
15
86
|
project_id: UUID
|
|
16
87
|
|
|
17
88
|
|
|
18
|
-
class LifecycleDTO(
|
|
89
|
+
class LifecycleDTO(DisplayBase):
|
|
19
90
|
created_at: datetime
|
|
20
91
|
updated_at: Optional[datetime] = None
|
|
21
92
|
|
|
@@ -31,14 +102,14 @@ class LifecycleDTO(BaseModel):
|
|
|
31
102
|
### services.observability.dtos ###
|
|
32
103
|
### --------------------------- ###
|
|
33
104
|
|
|
34
|
-
from typing import List, Dict, Any, Union, Optional
|
|
105
|
+
from typing import List, Dict, Any, Union, Optional, Sequence
|
|
35
106
|
|
|
36
107
|
from enum import Enum
|
|
37
108
|
from datetime import datetime
|
|
38
109
|
from uuid import UUID
|
|
39
110
|
|
|
40
111
|
|
|
41
|
-
class TimeDTO(
|
|
112
|
+
class TimeDTO(DisplayBase):
|
|
42
113
|
start: datetime
|
|
43
114
|
end: datetime
|
|
44
115
|
span: int
|
|
@@ -50,7 +121,7 @@ class StatusCode(Enum):
|
|
|
50
121
|
ERROR = "ERROR"
|
|
51
122
|
|
|
52
123
|
|
|
53
|
-
class StatusDTO(
|
|
124
|
+
class StatusDTO(DisplayBase):
|
|
54
125
|
code: StatusCode
|
|
55
126
|
message: Optional[str] = None
|
|
56
127
|
stacktrace: Optional[str] = None
|
|
@@ -60,6 +131,14 @@ AttributeValueType = Any
|
|
|
60
131
|
Attributes = Dict[str, AttributeValueType]
|
|
61
132
|
|
|
62
133
|
|
|
134
|
+
class AttributesDTO(DisplayBase):
|
|
135
|
+
data: Optional[Attributes] = None
|
|
136
|
+
metrics: Optional[Attributes] = None
|
|
137
|
+
meta: Optional[Attributes] = None
|
|
138
|
+
tags: Optional[Attributes] = None
|
|
139
|
+
semconv: Optional[Attributes] = None
|
|
140
|
+
|
|
141
|
+
|
|
63
142
|
class TreeType(Enum):
|
|
64
143
|
# --- VARIANTS --- #
|
|
65
144
|
INVOCATION = "invocation"
|
|
@@ -84,16 +163,16 @@ class NodeType(Enum):
|
|
|
84
163
|
# --- VARIANTS --- #
|
|
85
164
|
|
|
86
165
|
|
|
87
|
-
class RootDTO(
|
|
166
|
+
class RootDTO(DisplayBase):
|
|
88
167
|
id: UUID
|
|
89
168
|
|
|
90
169
|
|
|
91
|
-
class TreeDTO(
|
|
170
|
+
class TreeDTO(DisplayBase):
|
|
92
171
|
id: UUID
|
|
93
172
|
type: Optional[TreeType] = None
|
|
94
173
|
|
|
95
174
|
|
|
96
|
-
class NodeDTO(
|
|
175
|
+
class NodeDTO(DisplayBase):
|
|
97
176
|
id: UUID
|
|
98
177
|
type: Optional[NodeType] = None
|
|
99
178
|
name: str
|
|
@@ -106,13 +185,13 @@ Tags = Dict[str, str]
|
|
|
106
185
|
Refs = Dict[str, str]
|
|
107
186
|
|
|
108
187
|
|
|
109
|
-
class LinkDTO(
|
|
188
|
+
class LinkDTO(DisplayBase):
|
|
110
189
|
type: str
|
|
111
190
|
id: UUID
|
|
112
191
|
tree_id: Optional[UUID] = None
|
|
113
192
|
|
|
114
193
|
|
|
115
|
-
class ParentDTO(
|
|
194
|
+
class ParentDTO(DisplayBase):
|
|
116
195
|
id: UUID
|
|
117
196
|
|
|
118
197
|
|
|
@@ -134,25 +213,25 @@ class OTelStatusCode(Enum):
|
|
|
134
213
|
STATUS_CODE_UNSET = "STATUS_CODE_UNSET"
|
|
135
214
|
|
|
136
215
|
|
|
137
|
-
class OTelContextDTO(
|
|
216
|
+
class OTelContextDTO(DisplayBase):
|
|
138
217
|
trace_id: str
|
|
139
218
|
span_id: str
|
|
140
219
|
|
|
141
220
|
|
|
142
|
-
class OTelEventDTO(
|
|
221
|
+
class OTelEventDTO(DisplayBase):
|
|
143
222
|
name: str
|
|
144
223
|
timestamp: datetime
|
|
145
224
|
|
|
146
225
|
attributes: Optional[Attributes] = None
|
|
147
226
|
|
|
148
227
|
|
|
149
|
-
class OTelLinkDTO(
|
|
228
|
+
class OTelLinkDTO(DisplayBase):
|
|
150
229
|
context: OTelContextDTO
|
|
151
230
|
|
|
152
231
|
attributes: Optional[Attributes] = None
|
|
153
232
|
|
|
154
233
|
|
|
155
|
-
class OTelExtraDTO(
|
|
234
|
+
class OTelExtraDTO(DisplayBase):
|
|
156
235
|
kind: Optional[str] = None
|
|
157
236
|
|
|
158
237
|
attributes: Optional[Attributes] = None
|
|
@@ -160,10 +239,10 @@ class OTelExtraDTO(BaseModel):
|
|
|
160
239
|
links: Optional[List[OTelLinkDTO]] = None
|
|
161
240
|
|
|
162
241
|
|
|
163
|
-
class SpanDTO(
|
|
164
|
-
scope:
|
|
242
|
+
class SpanDTO(DisplayBase):
|
|
243
|
+
scope: ProjectScopeDTO
|
|
165
244
|
|
|
166
|
-
lifecycle:
|
|
245
|
+
lifecycle: LifecycleDTO
|
|
167
246
|
|
|
168
247
|
root: RootDTO
|
|
169
248
|
tree: TreeDTO
|
|
@@ -187,7 +266,30 @@ class SpanDTO(BaseModel):
|
|
|
187
266
|
nodes: Optional[Dict[str, Union["SpanDTO", List["SpanDTO"]]]] = None
|
|
188
267
|
|
|
189
268
|
|
|
190
|
-
class
|
|
269
|
+
class SpanCreateDTO(DisplayBase):
|
|
270
|
+
scope: ProjectScopeDTO
|
|
271
|
+
|
|
272
|
+
root: RootDTO
|
|
273
|
+
tree: TreeDTO
|
|
274
|
+
node: NodeDTO
|
|
275
|
+
|
|
276
|
+
parent: Optional[ParentDTO] = None
|
|
277
|
+
|
|
278
|
+
time: TimeDTO
|
|
279
|
+
status: StatusDTO
|
|
280
|
+
|
|
281
|
+
data: Optional[Data] = None
|
|
282
|
+
metrics: Optional[Metrics] = None
|
|
283
|
+
meta: Optional[Metadata] = None
|
|
284
|
+
tags: Optional[Tags] = None
|
|
285
|
+
refs: Optional[Refs] = None
|
|
286
|
+
|
|
287
|
+
links: Optional[List[LinkDTO]] = None
|
|
288
|
+
|
|
289
|
+
otel: Optional[OTelExtraDTO] = None
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
class OTelSpanDTO(DisplayBase):
|
|
191
293
|
context: OTelContextDTO
|
|
192
294
|
|
|
193
295
|
name: str
|
|
@@ -219,20 +321,20 @@ from typing import List, Dict, OrderedDict
|
|
|
219
321
|
|
|
220
322
|
|
|
221
323
|
def parse_span_dtos_to_span_idx(
|
|
222
|
-
span_dtos: List[
|
|
223
|
-
) -> Dict[str,
|
|
324
|
+
span_dtos: List[SpanCreateDTO],
|
|
325
|
+
) -> Dict[str, SpanCreateDTO]:
|
|
224
326
|
span_idx = {span_dto.node.id: span_dto for span_dto in span_dtos}
|
|
225
327
|
|
|
226
328
|
return span_idx
|
|
227
329
|
|
|
228
330
|
|
|
229
331
|
def parse_span_idx_to_span_id_tree(
|
|
230
|
-
span_idx: Dict[str,
|
|
332
|
+
span_idx: Dict[str, SpanCreateDTO],
|
|
231
333
|
) -> OrderedDict:
|
|
232
334
|
span_id_tree = OrderedDict()
|
|
233
335
|
index = {}
|
|
234
336
|
|
|
235
|
-
def push(span_dto:
|
|
337
|
+
def push(span_dto: SpanCreateDTO) -> None:
|
|
236
338
|
if span_dto.parent is None:
|
|
237
339
|
span_id_tree[span_dto.node.id] = OrderedDict()
|
|
238
340
|
index[span_dto.node.id] = span_id_tree[span_dto.node.id]
|
|
@@ -248,15 +350,15 @@ def parse_span_idx_to_span_id_tree(
|
|
|
248
350
|
|
|
249
351
|
def cumulate_costs(
|
|
250
352
|
spans_id_tree: OrderedDict,
|
|
251
|
-
spans_idx: Dict[str,
|
|
353
|
+
spans_idx: Dict[str, SpanCreateDTO],
|
|
252
354
|
) -> None:
|
|
253
|
-
def _get_unit(span:
|
|
355
|
+
def _get_unit(span: SpanCreateDTO):
|
|
254
356
|
if span.metrics is not None:
|
|
255
357
|
return span.metrics.get("unit.costs.total", 0.0)
|
|
256
358
|
|
|
257
359
|
return 0.0
|
|
258
360
|
|
|
259
|
-
def _get_acc(span:
|
|
361
|
+
def _get_acc(span: SpanCreateDTO):
|
|
260
362
|
if span.metrics is not None:
|
|
261
363
|
return span.metrics.get("acc.costs.total", 0.0)
|
|
262
364
|
|
|
@@ -265,7 +367,7 @@ def cumulate_costs(
|
|
|
265
367
|
def _acc(a: float, b: float):
|
|
266
368
|
return a + b
|
|
267
369
|
|
|
268
|
-
def _set(span:
|
|
370
|
+
def _set(span: SpanCreateDTO, cost: float):
|
|
269
371
|
if span.metrics is None:
|
|
270
372
|
span.metrics = {}
|
|
271
373
|
|
|
@@ -279,7 +381,7 @@ def cumulate_tokens(
|
|
|
279
381
|
spans_id_tree: OrderedDict,
|
|
280
382
|
spans_idx: Dict[str, dict],
|
|
281
383
|
) -> None:
|
|
282
|
-
def _get_unit(span:
|
|
384
|
+
def _get_unit(span: SpanCreateDTO):
|
|
283
385
|
_tokens = {
|
|
284
386
|
"prompt": 0.0,
|
|
285
387
|
"completion": 0.0,
|
|
@@ -295,7 +397,7 @@ def cumulate_tokens(
|
|
|
295
397
|
|
|
296
398
|
return _tokens
|
|
297
399
|
|
|
298
|
-
def _get_acc(span:
|
|
400
|
+
def _get_acc(span: SpanCreateDTO):
|
|
299
401
|
_tokens = {
|
|
300
402
|
"prompt": 0.0,
|
|
301
403
|
"completion": 0.0,
|
|
@@ -318,7 +420,7 @@ def cumulate_tokens(
|
|
|
318
420
|
"total": a.get("total", 0.0) + b.get("total", 0.0),
|
|
319
421
|
}
|
|
320
422
|
|
|
321
|
-
def _set(span:
|
|
423
|
+
def _set(span: SpanCreateDTO, tokens: dict):
|
|
322
424
|
if span.metrics is None:
|
|
323
425
|
span.metrics = {}
|
|
324
426
|
|
|
@@ -340,7 +442,7 @@ def cumulate_tokens(
|
|
|
340
442
|
|
|
341
443
|
def _cumulate_tree_dfs(
|
|
342
444
|
spans_id_tree: OrderedDict,
|
|
343
|
-
spans_idx: Dict[str,
|
|
445
|
+
spans_idx: Dict[str, SpanCreateDTO],
|
|
344
446
|
get_unit_metric,
|
|
345
447
|
get_acc_metric,
|
|
346
448
|
accumulate_metric,
|
|
@@ -412,12 +514,9 @@ def _connect_tree_dfs(
|
|
|
412
514
|
### apis.fastapi.observability.opentelemetry.semconv ###
|
|
413
515
|
### ------------------------------------------------ ###
|
|
414
516
|
|
|
415
|
-
from json import loads
|
|
416
|
-
|
|
417
517
|
VERSION = "0.4.1"
|
|
418
518
|
|
|
419
519
|
V_0_4_1_ATTRIBUTES_EXACT = [
|
|
420
|
-
# OPENLLMETRY
|
|
421
520
|
("gen_ai.system", "ag.meta.system"),
|
|
422
521
|
("gen_ai.request.base_url", "ag.meta.request.base_url"),
|
|
423
522
|
("gen_ai.request.endpoint", "ag.meta.request.endpoint"),
|
|
@@ -442,35 +541,12 @@ V_0_4_1_ATTRIBUTES_EXACT = [
|
|
|
442
541
|
("db.vector.query.top_k", "ag.meta.request.top_k"),
|
|
443
542
|
("pinecone.query.top_k", "ag.meta.request.top_k"),
|
|
444
543
|
("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"),
|
|
454
544
|
]
|
|
455
545
|
V_0_4_1_ATTRIBUTES_PREFIX = [
|
|
456
|
-
# OPENLLMETRY
|
|
457
546
|
("gen_ai.prompt", "ag.data.inputs.prompt"),
|
|
458
547
|
("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
548
|
]
|
|
466
549
|
|
|
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"))),
|
|
471
|
-
]
|
|
472
|
-
|
|
473
|
-
|
|
474
550
|
V_0_4_1_MAPS = {
|
|
475
551
|
"attributes": {
|
|
476
552
|
"exact": {
|
|
@@ -481,9 +557,6 @@ V_0_4_1_MAPS = {
|
|
|
481
557
|
"from": {otel: agenta for otel, agenta in V_0_4_1_ATTRIBUTES_PREFIX[::-1]},
|
|
482
558
|
"to": {agenta: otel for otel, agenta in V_0_4_1_ATTRIBUTES_PREFIX[::-1]},
|
|
483
559
|
},
|
|
484
|
-
"dynamic": {
|
|
485
|
-
"from": {otel: agenta for otel, agenta in V_0_4_1_ATTRIBUTES_DYNAMIC[::-1]}
|
|
486
|
-
},
|
|
487
560
|
},
|
|
488
561
|
}
|
|
489
562
|
V_0_4_1_KEYS = {
|
|
@@ -496,9 +569,6 @@ V_0_4_1_KEYS = {
|
|
|
496
569
|
"from": list(V_0_4_1_MAPS["attributes"]["prefix"]["from"].keys()),
|
|
497
570
|
"to": list(V_0_4_1_MAPS["attributes"]["prefix"]["to"].keys()),
|
|
498
571
|
},
|
|
499
|
-
"dynamic": {
|
|
500
|
-
"from": list(V_0_4_1_MAPS["attributes"]["dynamic"]["from"].keys()),
|
|
501
|
-
},
|
|
502
572
|
},
|
|
503
573
|
}
|
|
504
574
|
|
|
@@ -512,7 +582,6 @@ KEYS = {
|
|
|
512
582
|
|
|
513
583
|
CODEX = {"maps": MAPS[VERSION], "keys": KEYS[VERSION]}
|
|
514
584
|
|
|
515
|
-
|
|
516
585
|
### ------------------------------------------------ ###
|
|
517
586
|
### apis.fastapi.observability.opentelemetry.semconv ###
|
|
518
587
|
########################################################
|
|
@@ -686,18 +755,6 @@ def _parse_from_semconv(
|
|
|
686
755
|
|
|
687
756
|
del attributes[old_key]
|
|
688
757
|
|
|
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
|
-
|
|
701
758
|
|
|
702
759
|
def _parse_from_links(
|
|
703
760
|
otel_span_dto: OTelSpanDTO,
|
|
@@ -800,8 +857,11 @@ def _parse_from_attributes(
|
|
|
800
857
|
|
|
801
858
|
|
|
802
859
|
def parse_from_otel_span_dto(
|
|
860
|
+
project_id: str,
|
|
803
861
|
otel_span_dto: OTelSpanDTO,
|
|
804
862
|
) -> SpanDTO:
|
|
863
|
+
scope = ProjectScopeDTO(project_id=UUID(project_id))
|
|
864
|
+
|
|
805
865
|
lifecyle = LifecycleDTO(
|
|
806
866
|
created_at=datetime.now(),
|
|
807
867
|
)
|
|
@@ -821,15 +881,11 @@ def parse_from_otel_span_dto(
|
|
|
821
881
|
|
|
822
882
|
node_id = UUID(tree_id.hex[16:] + otel_span_dto.context.span_id[2:])
|
|
823
883
|
|
|
824
|
-
node_type =
|
|
825
|
-
try:
|
|
826
|
-
node_type = NodeType(types.get("node", "").lower())
|
|
827
|
-
except: # pylint: disable=bare-except
|
|
828
|
-
pass
|
|
884
|
+
node_type: str = types.get("node")
|
|
829
885
|
|
|
830
886
|
node = NodeDTO(
|
|
831
887
|
id=node_id,
|
|
832
|
-
type=node_type,
|
|
888
|
+
type=node_type.lower() if node_type else None,
|
|
833
889
|
name=otel_span_dto.name,
|
|
834
890
|
)
|
|
835
891
|
|
|
@@ -877,6 +933,7 @@ def parse_from_otel_span_dto(
|
|
|
877
933
|
)
|
|
878
934
|
|
|
879
935
|
span_dto = SpanDTO(
|
|
936
|
+
scope=scope,
|
|
880
937
|
lifecycle=lifecyle,
|
|
881
938
|
root=root,
|
|
882
939
|
tree=tree,
|
|
@@ -947,6 +1004,7 @@ from opentelemetry.sdk.trace import ReadableSpan
|
|
|
947
1004
|
|
|
948
1005
|
|
|
949
1006
|
def parse_inline_trace(
|
|
1007
|
+
project_id: str,
|
|
950
1008
|
spans: Dict[str, ReadableSpan],
|
|
951
1009
|
):
|
|
952
1010
|
otel_span_dtos = _parse_readable_spans(spans)
|
|
@@ -955,7 +1013,8 @@ def parse_inline_trace(
|
|
|
955
1013
|
### apis.fastapi.observability.api.otlp_collect_traces() ###
|
|
956
1014
|
### ---------------------------------------------------- ###
|
|
957
1015
|
span_dtos = [
|
|
958
|
-
parse_from_otel_span_dto(otel_span_dto)
|
|
1016
|
+
parse_from_otel_span_dto(project_id, otel_span_dto)
|
|
1017
|
+
for otel_span_dto in otel_span_dtos
|
|
959
1018
|
]
|
|
960
1019
|
### ---------------------------------------------------- ###
|
|
961
1020
|
### apis.fastapi.observability.api.otlp_collect_traces() ###
|
|
@@ -973,7 +1032,7 @@ def parse_inline_trace(
|
|
|
973
1032
|
###############################################
|
|
974
1033
|
### services.observability.service.ingest() ###
|
|
975
1034
|
### --------------------------------------- ###
|
|
976
|
-
|
|
1035
|
+
calculate_cost(span_idx)
|
|
977
1036
|
cumulate_costs(span_id_tree, span_idx)
|
|
978
1037
|
cumulate_tokens(span_id_tree, span_idx)
|
|
979
1038
|
### --------------------------------------- ###
|
|
@@ -1195,7 +1254,7 @@ def _parse_to_legacy_span(span: SpanDTO) -> CreateSpan:
|
|
|
1195
1254
|
),
|
|
1196
1255
|
#
|
|
1197
1256
|
app_id=(
|
|
1198
|
-
span.refs.get("
|
|
1257
|
+
span.refs.get("application_id", "missing-app-id")
|
|
1199
1258
|
if span.refs
|
|
1200
1259
|
else "missing-app-id"
|
|
1201
1260
|
),
|
|
@@ -1212,7 +1271,7 @@ def _parse_to_legacy_span(span: SpanDTO) -> CreateSpan:
|
|
|
1212
1271
|
return legacy_span
|
|
1213
1272
|
|
|
1214
1273
|
|
|
1215
|
-
|
|
1274
|
+
PAYING_TYPES = [
|
|
1216
1275
|
"embedding",
|
|
1217
1276
|
"query",
|
|
1218
1277
|
"completion",
|
|
@@ -1221,14 +1280,9 @@ TYPES_WITH_COSTS = [
|
|
|
1221
1280
|
]
|
|
1222
1281
|
|
|
1223
1282
|
|
|
1224
|
-
def
|
|
1283
|
+
def calculate_cost(span_idx: Dict[str, SpanCreateDTO]):
|
|
1225
1284
|
for span in span_idx.values():
|
|
1226
|
-
if (
|
|
1227
|
-
span.node.type
|
|
1228
|
-
and span.node.type.name.lower() in TYPES_WITH_COSTS
|
|
1229
|
-
and span.meta
|
|
1230
|
-
and span.metrics
|
|
1231
|
-
):
|
|
1285
|
+
if span.node.type.name.lower() in PAYING_TYPES and span.meta and span.metrics:
|
|
1232
1286
|
try:
|
|
1233
1287
|
costs = cost_calculator.cost_per_token(
|
|
1234
1288
|
model=span.meta.get("response.model"),
|