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.

Files changed (68) hide show
  1. agenta/__init__.py +3 -23
  2. agenta/cli/helper.py +1 -5
  3. agenta/client/backend/__init__.py +0 -14
  4. agenta/client/backend/apps/client.py +20 -28
  5. agenta/client/backend/client.py +2 -25
  6. agenta/client/backend/containers/client.py +1 -5
  7. agenta/client/backend/core/__init__.py +1 -2
  8. agenta/client/backend/core/client_wrapper.py +6 -6
  9. agenta/client/backend/core/file.py +11 -33
  10. agenta/client/backend/core/http_client.py +18 -24
  11. agenta/client/backend/core/pydantic_utilities.py +29 -144
  12. agenta/client/backend/core/request_options.py +0 -3
  13. agenta/client/backend/core/serialization.py +42 -139
  14. agenta/client/backend/evaluations/client.py +2 -7
  15. agenta/client/backend/evaluators/client.py +1 -349
  16. agenta/client/backend/observability/client.py +2 -11
  17. agenta/client/backend/testsets/client.py +10 -10
  18. agenta/client/backend/types/__init__.py +0 -14
  19. agenta/client/backend/types/app.py +0 -1
  20. agenta/client/backend/types/app_variant_response.py +1 -3
  21. agenta/client/backend/types/create_span.py +2 -3
  22. agenta/client/backend/types/environment_output.py +0 -1
  23. agenta/client/backend/types/environment_output_extended.py +0 -1
  24. agenta/client/backend/types/evaluation.py +2 -1
  25. agenta/client/backend/types/evaluator.py +0 -2
  26. agenta/client/backend/types/evaluator_config.py +0 -1
  27. agenta/client/backend/types/human_evaluation.py +2 -1
  28. agenta/client/backend/types/llm_tokens.py +2 -2
  29. agenta/client/backend/types/span.py +0 -1
  30. agenta/client/backend/types/span_detail.py +1 -7
  31. agenta/client/backend/types/test_set_output_response.py +2 -5
  32. agenta/client/backend/types/trace_detail.py +1 -7
  33. agenta/client/backend/types/with_pagination.py +2 -4
  34. agenta/client/backend/variants/client.py +273 -1566
  35. agenta/docker/docker-assets/Dockerfile.cloud.template +1 -1
  36. agenta/sdk/__init__.py +5 -21
  37. agenta/sdk/agenta_init.py +29 -34
  38. agenta/sdk/config_manager.py +205 -0
  39. agenta/sdk/context/routing.py +5 -6
  40. agenta/sdk/decorators/routing.py +146 -158
  41. agenta/sdk/decorators/tracing.py +239 -206
  42. agenta/sdk/litellm/litellm.py +36 -47
  43. agenta/sdk/tracing/attributes.py +47 -7
  44. agenta/sdk/tracing/context.py +2 -5
  45. agenta/sdk/tracing/conventions.py +19 -25
  46. agenta/sdk/tracing/exporters.py +5 -17
  47. agenta/sdk/tracing/inline.py +146 -92
  48. agenta/sdk/tracing/processors.py +13 -65
  49. agenta/sdk/tracing/spans.py +4 -16
  50. agenta/sdk/tracing/tracing.py +65 -124
  51. agenta/sdk/types.py +2 -61
  52. agenta/sdk/utils/exceptions.py +5 -38
  53. {agenta-0.27.0.dist-info → agenta-0.27.0a0.dist-info}/METADATA +1 -1
  54. {agenta-0.27.0.dist-info → agenta-0.27.0a0.dist-info}/RECORD +56 -67
  55. agenta/client/backend/types/config_dto.py +0 -32
  56. agenta/client/backend/types/config_response_model.py +0 -32
  57. agenta/client/backend/types/evaluator_mapping_output_interface.py +0 -21
  58. agenta/client/backend/types/evaluator_output_interface.py +0 -21
  59. agenta/client/backend/types/lifecycle_dto.py +0 -24
  60. agenta/client/backend/types/reference_dto.py +0 -23
  61. agenta/client/backend/types/reference_request_model.py +0 -23
  62. agenta/sdk/managers/__init__.py +0 -6
  63. agenta/sdk/managers/config.py +0 -318
  64. agenta/sdk/managers/deployment.py +0 -45
  65. agenta/sdk/managers/shared.py +0 -639
  66. agenta/sdk/managers/variant.py +0 -182
  67. {agenta-0.27.0.dist-info → agenta-0.27.0a0.dist-info}/WHEEL +0 -0
  68. {agenta-0.27.0.dist-info → agenta-0.27.0a0.dist-info}/entry_points.txt +0 -0
@@ -1,250 +1,283 @@
1
- from typing import Callable, Optional, Any, Dict, List, Union
1
+ import inspect
2
+ import traceback
2
3
  from functools import wraps
3
4
  from itertools import chain
4
- from inspect import iscoroutinefunction, getfullargspec
5
+ from typing import Callable, Optional, Any, Dict, List
6
+
7
+ import agenta as ag
8
+
5
9
 
6
10
  from agenta.sdk.utils.exceptions import suppress
11
+
7
12
  from agenta.sdk.context.tracing import tracing_context
8
13
  from agenta.sdk.tracing.conventions import parse_span_kind
9
14
 
10
- import agenta as ag
11
-
12
15
 
13
- class instrument: # pylint: disable=invalid-name
16
+ class instrument:
14
17
  DEFAULT_KEY = "__default__"
15
18
 
16
19
  def __init__(
17
20
  self,
18
- type: str = "task", # pylint: disable=redefined-builtin
21
+ kind: str = "task",
19
22
  config: Optional[Dict[str, Any]] = None,
20
23
  ignore_inputs: Optional[bool] = None,
21
24
  ignore_outputs: Optional[bool] = None,
22
25
  max_depth: Optional[int] = 2,
23
- # DEPRECATING
24
- kind: str = "task",
26
+ # DEPRECATED
25
27
  spankind: Optional[str] = "TASK",
26
28
  ) -> None:
27
- self.type = spankind or kind or type
28
- self.kind = None
29
+ self.kind = spankind if spankind is not None else kind
29
30
  self.config = config
30
31
  self.ignore_inputs = ignore_inputs
31
32
  self.ignore_outputs = ignore_outputs
32
33
  self.max_depth = max_depth
33
34
 
34
35
  def __call__(self, func: Callable[..., Any]):
35
- is_coroutine_function = iscoroutinefunction(func)
36
+ is_coroutine_function = inspect.iscoroutinefunction(func)
37
+
38
+ def parse(*args, **kwargs) -> Dict[str, Any]:
39
+ inputs = {
40
+ key: value
41
+ for key, value in chain(
42
+ zip(inspect.getfullargspec(func).args, args),
43
+ kwargs.items(),
44
+ )
45
+ }
46
+
47
+ return inputs
48
+
49
+ def redact(
50
+ io: Dict[str, Any], ignore: List[str] | bool = False
51
+ ) -> Dict[str, Any]:
52
+ """
53
+ Redact user-defined sensitive information from inputs and outputs as defined by the ignore list or boolean flag.
54
+
55
+ Example:
56
+ - ignore = ["password"] -> {"username": "admin", "password": "********"} -> {"username": "admin"}
57
+ - ignore = True -> {"username": "admin", "password": "********"} -> {}
58
+ - ignore = False -> {"username": "admin", "password": "********"} -> {"username": "admin", "password": "********"}
59
+ """
60
+ io = {
61
+ key: value
62
+ for key, value in io.items()
63
+ if key
64
+ not in (
65
+ ignore
66
+ if isinstance(ignore, list)
67
+ else io.keys()
68
+ if ignore is True
69
+ else []
70
+ )
71
+ }
72
+
73
+ return io
74
+
75
+ def patch(result: Any) -> Dict[str, Any]:
76
+ """
77
+ Patch the result to ensure that it is a dictionary, with a default key when necessary.
78
+
79
+ Example:
80
+ - result = "Hello, World!" -> {"__default__": "Hello, World!"}
81
+ - result = {"message": "Hello, World!", "cost": 0.0, "usage": {}} -> {"__default__": "Hello, World!"}
82
+ - result = {"message": "Hello, World!"} -> {"message": "Hello, World!"}
83
+ """
84
+ outputs = (
85
+ {instrument.DEFAULT_KEY: result}
86
+ if not isinstance(result, dict)
87
+ else (
88
+ {instrument.DEFAULT_KEY: result["message"]}
89
+ if all(key in result for key in ["message", "cost", "usage"])
90
+ else result
91
+ )
92
+ )
93
+
94
+ return outputs
36
95
 
37
96
  @wraps(func)
38
97
  async def async_wrapper(*args, **kwargs):
39
- async def _async_auto_instrumented(*args, **kwargs):
40
- self._parse_type_and_kind()
41
-
42
- with ag.tracer.start_as_current_span(func.__name__, kind=self.kind):
43
- self._pre_instrument(func, *args, **kwargs)
98
+ async def wrapped_func(*args, **kwargs):
99
+ if not ag.tracing.get_current_span().is_recording():
100
+ self.kind = "workflow"
101
+
102
+ kind = parse_span_kind(self.kind)
103
+
104
+ with ag.tracer.start_as_current_span(func.__name__, kind=kind):
105
+ span = ag.tracing.get_current_span()
106
+
107
+ with suppress():
108
+ span.set_attributes(
109
+ attributes={"node": self.kind},
110
+ namespace="type",
111
+ )
112
+
113
+ if span.parent is None:
114
+ rctx = tracing_context.get()
115
+
116
+ span.set_attributes(
117
+ attributes={"configuration": rctx.get("config", {})},
118
+ namespace="meta",
119
+ )
120
+ span.set_attributes(
121
+ attributes={"environment": rctx.get("environment", {})},
122
+ namespace="meta",
123
+ )
124
+ span.set_attributes(
125
+ attributes={"version": rctx.get("version", {})},
126
+ namespace="meta",
127
+ )
128
+ span.set_attributes(
129
+ attributes={"variant": rctx.get("variant", {})},
130
+ namespace="meta",
131
+ )
132
+
133
+ _inputs = redact(parse(*args, **kwargs), self.ignore_inputs)
134
+ span.set_attributes(
135
+ attributes={"inputs": _inputs},
136
+ namespace="data",
137
+ max_depth=self.max_depth,
138
+ )
44
139
 
45
140
  result = await func(*args, **kwargs)
46
141
 
47
- self._post_instrument(result)
142
+ with suppress():
143
+ cost = None
144
+ usage = {}
145
+
146
+ if isinstance(result, dict):
147
+ cost = result.get("cost", None)
148
+ usage = result.get("usage", {})
149
+
150
+ span.set_attributes(
151
+ attributes={"total": cost},
152
+ namespace="metrics.unit.costs",
153
+ )
154
+ span.set_attributes(
155
+ attributes=(
156
+ {
157
+ "prompt": usage.get("prompt_tokens", None),
158
+ "completion": usage.get("completion_tokens", None),
159
+ "total": usage.get("total_tokens", None),
160
+ }
161
+ ),
162
+ namespace="metrics.unit.tokens",
163
+ )
164
+
165
+ _outputs = redact(patch(result), self.ignore_outputs)
166
+ span.set_attributes(
167
+ attributes={"outputs": _outputs},
168
+ namespace="data",
169
+ max_depth=self.max_depth,
170
+ )
171
+
172
+ span.set_status("OK")
173
+
174
+ with suppress():
175
+ if hasattr(span, "parent") and span.parent is None:
176
+ tracing_context.set(
177
+ tracing_context.get()
178
+ | {
179
+ "root": {
180
+ "trace_id": span.get_span_context().trace_id,
181
+ "span_id": span.get_span_context().span_id,
182
+ }
183
+ }
184
+ )
48
185
 
49
186
  return result
50
187
 
51
- return await _async_auto_instrumented(*args, **kwargs)
188
+ return await wrapped_func(*args, **kwargs)
52
189
 
53
190
  @wraps(func)
54
191
  def sync_wrapper(*args, **kwargs):
55
- def _sync_auto_instrumented(*args, **kwargs):
56
- self._parse_type_and_kind()
57
-
58
- with ag.tracer.start_as_current_span(func.__name__, kind=self.kind):
59
- self._pre_instrument(func, *args, **kwargs)
192
+ def wrapped_func(*args, **kwargs):
193
+ if not ag.tracing.get_current_span().is_recording():
194
+ self.kind = "workflow"
195
+
196
+ kind = parse_span_kind(self.kind)
197
+
198
+ with ag.tracer.start_as_current_span(func.__name__, kind=kind):
199
+ span = ag.tracing.get_current_span()
200
+
201
+ with suppress():
202
+ span.set_attributes(
203
+ attributes={"node": self.kind},
204
+ namespace="type",
205
+ )
206
+
207
+ if span.parent is None:
208
+ rctx = tracing_context.get()
209
+
210
+ span.set_attributes(
211
+ attributes={"configuration": rctx.get("config", {})},
212
+ namespace="meta",
213
+ )
214
+ span.set_attributes(
215
+ attributes={"environment": rctx.get("environment", {})},
216
+ namespace="meta",
217
+ )
218
+ span.set_attributes(
219
+ attributes={"version": rctx.get("version", {})},
220
+ namespace="meta",
221
+ )
222
+ span.set_attributes(
223
+ attributes={"variant": rctx.get("variant", {})},
224
+ namespace="meta",
225
+ )
226
+
227
+ _inputs = redact(parse(*args, **kwargs), self.ignore_inputs)
228
+ span.set_attributes(
229
+ attributes={"inputs": _inputs},
230
+ namespace="data",
231
+ max_depth=self.max_depth,
232
+ )
60
233
 
61
234
  result = func(*args, **kwargs)
62
235
 
63
- self._post_instrument(result)
236
+ with suppress():
237
+ cost = None
238
+ usage = {}
239
+ if isinstance(result, dict):
240
+ cost = result.get("cost", None)
241
+ usage = result.get("usage", {})
242
+
243
+ span.set_attributes(
244
+ attributes={"total": cost},
245
+ namespace="metrics.unit.costs",
246
+ )
247
+ span.set_attributes(
248
+ attributes=(
249
+ {
250
+ "prompt": usage.get("prompt_tokens", None),
251
+ "completion": usage.get("completion_tokens", None),
252
+ "total": usage.get("total_tokens", None),
253
+ }
254
+ ),
255
+ namespace="metrics.unit.tokens",
256
+ )
257
+
258
+ _outputs = redact(patch(result), self.ignore_outputs)
259
+ span.set_attributes(
260
+ attributes={"outputs": _outputs},
261
+ namespace="data",
262
+ max_depth=self.max_depth,
263
+ )
264
+
265
+ span.set_status("OK")
266
+
267
+ with suppress():
268
+ if hasattr(span, "parent") and span.parent is None:
269
+ tracing_context.set(
270
+ tracing_context.get()
271
+ | {
272
+ "root": {
273
+ "trace_id": span.get_span_context().trace_id,
274
+ "span_id": span.get_span_context().span_id,
275
+ }
276
+ }
277
+ )
64
278
 
65
279
  return result
66
280
 
67
- return _sync_auto_instrumented(*args, **kwargs)
281
+ return wrapped_func(*args, **kwargs)
68
282
 
69
283
  return async_wrapper if is_coroutine_function else sync_wrapper
70
-
71
- def _parse_type_and_kind(self):
72
- if not ag.tracing.get_current_span().is_recording():
73
- self.type = "workflow"
74
-
75
- self.kind = parse_span_kind(self.type)
76
-
77
- def _pre_instrument(
78
- self,
79
- func,
80
- *args,
81
- **kwargs,
82
- ):
83
- span = ag.tracing.get_current_span()
84
-
85
- with suppress():
86
- span.set_attributes(
87
- attributes={"node": self.type},
88
- namespace="type",
89
- )
90
-
91
- if span.parent is None:
92
- rctx = tracing_context.get()
93
-
94
- span.set_attributes(
95
- attributes={"configuration": rctx.get("config", {})},
96
- namespace="meta",
97
- )
98
- span.set_attributes(
99
- attributes={"environment": rctx.get("environment", {})},
100
- namespace="meta",
101
- )
102
- span.set_attributes(
103
- attributes={"version": rctx.get("version", {})},
104
- namespace="meta",
105
- )
106
- span.set_attributes(
107
- attributes={"variant": rctx.get("variant", {})},
108
- namespace="meta",
109
- )
110
-
111
- _inputs = self._redact(
112
- self._parse(
113
- func,
114
- *args,
115
- **kwargs,
116
- ),
117
- self.ignore_inputs,
118
- )
119
- span.set_attributes(
120
- attributes={"inputs": _inputs},
121
- namespace="data",
122
- max_depth=self.max_depth,
123
- )
124
-
125
- def _post_instrument(
126
- self,
127
- result,
128
- ):
129
- span = ag.tracing.get_current_span()
130
- with suppress():
131
- cost = None
132
- usage = {}
133
-
134
- if isinstance(result, dict):
135
- cost = result.get("cost", None)
136
- usage = result.get("usage", {})
137
-
138
- if isinstance(usage, (int, float)):
139
- usage = {"total_tokens": usage}
140
-
141
- span.set_attributes(
142
- attributes={"total": cost},
143
- namespace="metrics.unit.costs",
144
- )
145
- span.set_attributes(
146
- attributes=(
147
- {
148
- "prompt": usage.get("prompt_tokens", None),
149
- "completion": usage.get("completion_tokens", None),
150
- "total": usage.get("total_tokens", None),
151
- }
152
- ),
153
- namespace="metrics.unit.tokens",
154
- )
155
-
156
- _outputs = self._redact(self._patch(result), self.ignore_outputs)
157
- span.set_attributes(
158
- attributes={"outputs": _outputs},
159
- namespace="data",
160
- max_depth=self.max_depth,
161
- )
162
-
163
- span.set_status("OK")
164
-
165
- with suppress():
166
- if hasattr(span, "parent") and span.parent is None:
167
- tracing_context.set(
168
- tracing_context.get()
169
- | {
170
- "root": {
171
- "trace_id": span.get_span_context().trace_id,
172
- "span_id": span.get_span_context().span_id,
173
- }
174
- }
175
- )
176
-
177
- def _parse(
178
- self,
179
- func,
180
- *args,
181
- **kwargs,
182
- ) -> Dict[str, Any]:
183
- inputs = {
184
- key: value
185
- for key, value in chain(
186
- zip(getfullargspec(func).args, args),
187
- kwargs.items(),
188
- )
189
- }
190
-
191
- return inputs
192
-
193
- def _redact(
194
- self,
195
- io: Dict[str, Any],
196
- ignore: Union[List[str], bool] = False,
197
- ) -> Dict[str, Any]:
198
- """
199
- Redact user-defined sensitive information
200
- from inputs and outputs as defined by the ignore list or boolean flag.
201
-
202
- Example:
203
- - ignore = ["password"] -> {"username": "admin", "password": "********"}
204
- -> {"username": "admin"}
205
- - ignore = True -> {"username": "admin", "password": "********"}
206
- -> {}
207
- - ignore = False -> {"username": "admin", "password": "********"}
208
- -> {"username": "admin", "password": "********"}
209
- """
210
- io = {
211
- key: value
212
- for key, value in io.items()
213
- if key
214
- not in (
215
- ignore
216
- if isinstance(ignore, list)
217
- else io.keys()
218
- if ignore is True
219
- else []
220
- )
221
- }
222
-
223
- return io
224
-
225
- def _patch(
226
- self,
227
- result: Any,
228
- ) -> Dict[str, Any]:
229
- """
230
- Patch the result to ensure that it is a dictionary, with a default key when necessary.
231
-
232
- Example:
233
- - result = "Hello, World!"
234
- -> {"__default__": "Hello, World!"}
235
- - result = {"message": "Hello, World!", "cost": 0.0, "usage": {}}
236
- -> {"__default__": "Hello, World!"}
237
- - result = {"message": "Hello, World!"}
238
- -> {"message": "Hello, World!"}
239
- """
240
- outputs = (
241
- {instrument.DEFAULT_KEY: result}
242
- if not isinstance(result, dict)
243
- else (
244
- {instrument.DEFAULT_KEY: result["message"]}
245
- if all(key in result for key in ["message", "cost", "usage"])
246
- else result
247
- )
248
- )
249
-
250
- return outputs
@@ -1,15 +1,16 @@
1
- from opentelemetry.trace import SpanKind
2
-
3
1
  import agenta as ag
4
2
 
3
+ from opentelemetry.trace import SpanKind
4
+
5
5
  from agenta.sdk.tracing.spans import CustomSpan
6
- from agenta.sdk.utils.exceptions import suppress # TODO: use it !
6
+ from agenta.sdk.utils.exceptions import suppress
7
7
  from agenta.sdk.utils.logging import log
8
8
 
9
9
 
10
10
  def litellm_handler():
11
11
  try:
12
- from litellm.integrations.custom_logger import ( # pylint: disable=import-outside-toplevel
12
+ from litellm.utils import ModelResponse
13
+ from litellm.integrations.custom_logger import (
13
14
  CustomLogger as LitellmCustomLogger,
14
15
  )
15
16
  except ImportError as exc:
@@ -17,23 +18,18 @@ def litellm_handler():
17
18
  "The litellm SDK is not installed. Please install it using `pip install litellm`."
18
19
  ) from exc
19
20
  except Exception as exc:
20
- raise Exception( # pylint: disable=broad-exception-raised
21
- f"Unexpected error occurred when importing litellm: {exc}"
21
+ raise Exception(
22
+ "Unexpected error occurred when importing litellm: {}".format(exc)
22
23
  ) from exc
23
24
 
24
25
  class LitellmHandler(LitellmCustomLogger):
25
- """
26
- This handler is responsible for instrumenting certain events,
27
- when using litellm to call LLMs.
26
+ """This handler is responsible for instrumenting certain events when using litellm to call LLMs.
28
27
 
29
28
  Args:
30
- LitellmCustomLogger (object): custom logger that allows us
31
- to override the events to capture.
29
+ LitellmCustomLogger (object): custom logger that allows us to override the events to capture.
32
30
  """
33
31
 
34
32
  def __init__(self):
35
- super().__init__()
36
-
37
33
  self.span = None
38
34
 
39
35
  def log_pre_api_call(
@@ -42,7 +38,7 @@ def litellm_handler():
42
38
  messages,
43
39
  kwargs,
44
40
  ):
45
- type = ( # pylint: disable=redefined-builtin
41
+ type = (
46
42
  "chat"
47
43
  if kwargs.get("call_type") in ["completion", "acompletion"]
48
44
  else "embedding"
@@ -63,8 +59,10 @@ def litellm_handler():
63
59
  log.error("LiteLLM callback error: span not found.")
64
60
  return
65
61
 
62
+ log.info(f"log_pre_api_call({hex(self.span.context.span_id)[2:]})")
63
+
66
64
  self.span.set_attributes(
67
- attributes={"inputs": {"prompt": kwargs["messages"]}},
65
+ attributes={"inputs": {"messages": kwargs["messages"]}},
68
66
  namespace="data",
69
67
  )
70
68
 
@@ -89,14 +87,12 @@ def litellm_handler():
89
87
  log.error("LiteLLM callback error: span not found.")
90
88
  return
91
89
 
92
- result = kwargs.get("complete_streaming_response")
93
-
94
- outputs = (
95
- {"__default__": result} if not isinstance(result, dict) else result
96
- )
90
+ # log.info(f"log_stream({hex(self.span.context.span_id)[2:]})")
97
91
 
98
92
  self.span.set_attributes(
99
- attributes={"outputs": outputs},
93
+ attributes={
94
+ "output": {"__default__": kwargs.get("complete_streaming_response")}
95
+ },
100
96
  namespace="data",
101
97
  )
102
98
 
@@ -131,20 +127,14 @@ def litellm_handler():
131
127
  log.error("LiteLLM callback error: span not found.")
132
128
  return
133
129
 
134
- try:
135
- result = []
136
- for choice in response_obj.choices:
137
- message = choice.message.__dict__
138
- result.append(message)
130
+ # log.info(f"log_success({hex(self.span.context.span_id)[2:]})")
139
131
 
140
- outputs = {"completion": result}
141
- self.span.set_attributes(
142
- attributes={"outputs": outputs},
143
- namespace="data",
144
- )
145
-
146
- except Exception as e:
147
- pass
132
+ self.span.set_attributes(
133
+ attributes={
134
+ "output": {"__default__": response_obj.choices[0].message.content}
135
+ },
136
+ namespace="data",
137
+ )
148
138
 
149
139
  self.span.set_attributes(
150
140
  attributes={"total": kwargs.get("response_cost")},
@@ -177,6 +167,8 @@ def litellm_handler():
177
167
  log.error("LiteLLM callback error: span not found.")
178
168
  return
179
169
 
170
+ # log.info(f"log_failure({hex(self.span.context.span_id)[2:]})")
171
+
180
172
  self.span.record_exception(kwargs["exception"])
181
173
 
182
174
  self.span.set_status(status="ERROR")
@@ -194,14 +186,12 @@ def litellm_handler():
194
186
  log.error("LiteLLM callback error: span not found.")
195
187
  return
196
188
 
197
- result = kwargs.get("complete_streaming_response")
198
-
199
- outputs = (
200
- {"__default__": result} if not isinstance(result, dict) else result
201
- )
189
+ # log.info(f"async_log_stream({hex(self.span.context.span_id)[2:]})")
202
190
 
203
191
  self.span.set_attributes(
204
- attributes={"outputs": outputs},
192
+ attributes={
193
+ "output": {"__default__": kwargs.get("complete_streaming_response")}
194
+ },
205
195
  namespace="data",
206
196
  )
207
197
 
@@ -236,15 +226,12 @@ def litellm_handler():
236
226
  log.error("LiteLLM callback error: span not found.")
237
227
  return
238
228
 
239
- # result = kwargs.get("complete_streaming_response")
240
- result = response_obj.choices[0].message.content
241
-
242
- outputs = (
243
- {"__default__": result} if not isinstance(result, dict) else result
244
- )
229
+ log.info(f"async_log_success({hex(self.span.context.span_id)[2:]})")
245
230
 
246
231
  self.span.set_attributes(
247
- attributes={"outputs": outputs},
232
+ attributes={
233
+ "output": {"__default__": kwargs.get("complete_streaming_response")}
234
+ },
248
235
  namespace="data",
249
236
  )
250
237
 
@@ -279,6 +266,8 @@ def litellm_handler():
279
266
  log.error("LiteLLM callback error: span not found.")
280
267
  return
281
268
 
269
+ # log.info(f"async_log_failure({hex(self.span.context.span_id)[2:]})")
270
+
282
271
  self.span.record_exception(kwargs["exception"])
283
272
 
284
273
  self.span.set_status(status="ERROR")