langtrace-python-sdk 3.6.3__py3-none-any.whl → 3.8.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,9 @@
1
+ import asyncio
2
+ from examples.phidata_example.agent import agent_run
3
+ from langtrace_python_sdk import langtrace
4
+
5
+ langtrace.init()
6
+
7
+ class PhiDataRunner:
8
+ def run(self):
9
+ agent_run()
@@ -0,0 +1,16 @@
1
+ from langtrace_python_sdk import langtrace
2
+ from phi.agent import Agent
3
+ from phi.model.openai import OpenAIChat
4
+ from phi.tools.duckduckgo import DuckDuckGo
5
+
6
+ langtrace.init()
7
+
8
+ def agent_run():
9
+ web_agent = Agent(
10
+ model=OpenAIChat(id="gpt-4o"),
11
+ tools=[DuckDuckGo()],
12
+ instructions=["Always include sources"],
13
+ show_tool_calls=True,
14
+ markdown=True,
15
+ )
16
+ web_agent.print_response("how do I get from lagos to nairobi, through kigali rwanda (staying a week) and how much would it cost on average?", stream=True)
@@ -41,6 +41,8 @@ SERVICE_PROVIDERS = {
41
41
  "CEREBRAS": "Cerebras",
42
42
  "MILVUS": "Milvus",
43
43
  "GRAPHLIT": "Graphlit",
44
+ "PHIDATA": "Phidata",
45
+ "AGNO": "Agno",
44
46
  }
45
47
 
46
48
  LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY = "langtrace_additional_attributes"
@@ -30,6 +30,8 @@ from .cerebras import CerebrasInstrumentation
30
30
  from .milvus import MilvusInstrumentation
31
31
  from .google_genai import GoogleGenaiInstrumentation
32
32
  from .graphlit import GraphlitInstrumentation
33
+ from .phidata import PhiDataInstrumentation
34
+ from .agno import AgnoInstrumentation
33
35
 
34
36
  __all__ = [
35
37
  "AnthropicInstrumentation",
@@ -61,4 +63,6 @@ __all__ = [
61
63
  "GoogleGenaiInstrumentation",
62
64
  "CrewaiToolsInstrumentation",
63
65
  "GraphlitInstrumentation",
66
+ "PhiDataInstrumentation",
67
+ "AgnoInstrumentation",
64
68
  ]
@@ -0,0 +1,5 @@
1
+ from .instrumentation import AgnoInstrumentation
2
+
3
+ __all__ = [
4
+ "AgnoInstrumentation",
5
+ ]
@@ -0,0 +1,80 @@
1
+ """
2
+ Copyright (c) 2024 Scale3 Labs
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ """
16
+
17
+ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
18
+ from opentelemetry.trace import get_tracer
19
+ from wrapt import wrap_function_wrapper as _W
20
+ from typing import Collection
21
+ from importlib_metadata import version as v
22
+ from .patch import patch_agent, patch_memory
23
+
24
+ class AgnoInstrumentation(BaseInstrumentor):
25
+ def instrumentation_dependencies(self) -> Collection[str]:
26
+ return ["agno >= 1.1.4"]
27
+
28
+ def _instrument(self, **kwargs):
29
+ tracer_provider = kwargs.get("tracer_provider")
30
+ tracer = get_tracer(__name__, "", tracer_provider)
31
+ version = v("agno")
32
+
33
+ try:
34
+ _W(
35
+ "agno.agent.agent",
36
+ "Agent.run",
37
+ patch_agent("Agent.run", version, tracer),
38
+ )
39
+ _W(
40
+ "agno.agent.agent",
41
+ "Agent.arun",
42
+ patch_agent("Agent.arun", version, tracer),
43
+ )
44
+ _W(
45
+ "agno.agent.agent",
46
+ "Agent._run",
47
+ patch_agent("Agent._run", version, tracer),
48
+ )
49
+ _W(
50
+ "agno.agent.agent",
51
+ "Agent._arun",
52
+ patch_agent("Agent._arun", version, tracer),
53
+ )
54
+
55
+ _W(
56
+ "agno.memory.agent",
57
+ "AgentMemory.update_memory",
58
+ patch_memory("AgentMemory.update_memory", version, tracer),
59
+ )
60
+ _W(
61
+ "agno.memory.agent",
62
+ "AgentMemory.aupdate_memory",
63
+ patch_memory("AgentMemory.aupdate_memory", version, tracer),
64
+ )
65
+ _W(
66
+ "agno.memory.agent",
67
+ "AgentMemory.update_summary",
68
+ patch_memory("AgentMemory.update_summary", version, tracer),
69
+ )
70
+ _W(
71
+ "agno.memory.agent",
72
+ "AgentMemory.aupdate_summary",
73
+ patch_memory("AgentMemory.aupdate_summary", version, tracer),
74
+ )
75
+
76
+ except Exception:
77
+ pass
78
+
79
+ def _uninstrument(self, **kwargs):
80
+ pass
@@ -0,0 +1,261 @@
1
+ import json
2
+ from importlib_metadata import version as v
3
+ from langtrace.trace_attributes import FrameworkSpanAttributes
4
+ from opentelemetry import baggage
5
+ from opentelemetry.trace import Span, SpanKind, Tracer
6
+ from opentelemetry.trace.status import Status, StatusCode
7
+ from typing import Dict, Any, Optional
8
+
9
+ from langtrace_python_sdk.constants import LANGTRACE_SDK_NAME
10
+ from langtrace_python_sdk.constants.instrumentation.common import (
11
+ LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
12
+ SERVICE_PROVIDERS,
13
+ )
14
+ from langtrace_python_sdk.utils import set_span_attribute
15
+ from langtrace_python_sdk.utils.llm import get_span_name, set_span_attributes
16
+ from langtrace_python_sdk.utils.misc import serialize_args, serialize_kwargs
17
+
18
+ def _extract_metrics(metrics: Dict[str, Any]) -> Dict[str, Any]:
19
+ """Helper function to extract and format metrics"""
20
+ formatted_metrics = {}
21
+
22
+ # Extract basic metrics
23
+ for key in ['time', 'time_to_first_token', 'input_tokens', 'output_tokens',
24
+ 'prompt_tokens', 'completion_tokens', 'total_tokens']:
25
+ if key in metrics:
26
+ formatted_metrics[key] = metrics[key]
27
+
28
+ # Extract nested metric details if present
29
+ if 'prompt_tokens_details' in metrics:
30
+ formatted_metrics['prompt_tokens_details'] = metrics['prompt_tokens_details']
31
+ if 'completion_tokens_details' in metrics:
32
+ formatted_metrics['completion_tokens_details'] = metrics['completion_tokens_details']
33
+ if 'tool_call_times' in metrics:
34
+ formatted_metrics['tool_call_times'] = metrics['tool_call_times']
35
+
36
+ return formatted_metrics
37
+
38
+ def patch_memory(operation_name, version, tracer: Tracer):
39
+ def traced_method(wrapped, instance, args, kwargs):
40
+ service_provider = SERVICE_PROVIDERS["AGNO"]
41
+ extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
42
+ span_attributes = {
43
+ "langtrace.sdk.name": "langtrace-python-sdk",
44
+ "langtrace.service.name": service_provider,
45
+ "langtrace.service.type": "framework",
46
+ "langtrace.service.version": version,
47
+ "langtrace.version": v(LANGTRACE_SDK_NAME),
48
+ **(extra_attributes if extra_attributes is not None else {}),
49
+ }
50
+
51
+ span_attributes.update({
52
+ "agno.memory.type": type(instance).__name__,
53
+ "agno.memory.create_session_summary": str(instance.create_session_summary),
54
+ "agno.memory.create_user_memories": str(instance.create_user_memories),
55
+ "agno.memory.retrieval": str(instance.retrieval)
56
+ })
57
+
58
+ inputs = {}
59
+ if len(args) > 0:
60
+ inputs["args"] = serialize_args(*args)
61
+ if len(kwargs) > 0:
62
+ inputs["kwargs"] = serialize_kwargs(**kwargs)
63
+ span_attributes["agno.memory.inputs"] = json.dumps(inputs)
64
+
65
+ attributes = FrameworkSpanAttributes(**span_attributes)
66
+
67
+ with tracer.start_as_current_span(
68
+ get_span_name(operation_name), kind=SpanKind.CLIENT
69
+ ) as span:
70
+ try:
71
+ set_span_attributes(span, attributes)
72
+ result = wrapped(*args, **kwargs)
73
+
74
+ if result is not None:
75
+ set_span_attribute(span, "agno.memory.output", str(result))
76
+
77
+ if instance.summary is not None:
78
+ set_span_attribute(span, "agno.memory.summary", str(instance.summary))
79
+ if instance.memories is not None:
80
+ set_span_attribute(span, "agno.memory.memories_count", str(len(instance.memories)))
81
+
82
+ span.set_status(Status(StatusCode.OK))
83
+ return result
84
+
85
+ except Exception as err:
86
+ span.record_exception(err)
87
+ span.set_status(Status(StatusCode.ERROR, str(err)))
88
+ raise
89
+
90
+ return traced_method
91
+
92
+ def patch_agent(operation_name, version, tracer: Tracer):
93
+ def traced_method(wrapped, instance, args, kwargs):
94
+ service_provider = SERVICE_PROVIDERS["AGNO"]
95
+ extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
96
+ span_attributes = {
97
+ "langtrace.sdk.name": "langtrace-python-sdk",
98
+ "langtrace.service.name": service_provider,
99
+ "langtrace.service.type": "framework",
100
+ "langtrace.service.version": version,
101
+ "langtrace.version": v(LANGTRACE_SDK_NAME),
102
+ **(extra_attributes if extra_attributes is not None else {}),
103
+ }
104
+
105
+ attributes = FrameworkSpanAttributes(**span_attributes)
106
+
107
+ with tracer.start_as_current_span(
108
+ get_span_name(operation_name), kind=SpanKind.CLIENT
109
+ ) as span:
110
+ try:
111
+ set_span_attributes(span, attributes)
112
+ AgnoSpanAttributes(span=span, instance=instance)
113
+ result_generator = wrapped(*args, **kwargs)
114
+
115
+ accumulated_content = ""
116
+ current_tool_call = None
117
+ response_metadata = None
118
+ seen_tool_calls = set()
119
+
120
+ try:
121
+ for response in result_generator:
122
+ if not hasattr(response, 'to_dict'):
123
+ yield response
124
+ continue
125
+
126
+ if not response_metadata:
127
+ response_metadata = {
128
+ "run_id": response.run_id,
129
+ "agent_id": response.agent_id,
130
+ "session_id": response.session_id,
131
+ "model": response.model,
132
+ "content_type": response.content_type,
133
+ }
134
+ for key, value in response_metadata.items():
135
+ if value is not None:
136
+ set_span_attribute(span, f"agno.agent.{key}", str(value))
137
+
138
+ if response.content:
139
+ accumulated_content += response.content
140
+ set_span_attribute(span, "agno.agent.response", accumulated_content)
141
+
142
+ if response.messages:
143
+ for msg in response.messages:
144
+ if msg.tool_calls:
145
+ for tool_call in msg.tool_calls:
146
+ tool_id = tool_call.get('id')
147
+ if tool_id and tool_id not in seen_tool_calls:
148
+ seen_tool_calls.add(tool_id)
149
+ tool_info = {
150
+ 'id': tool_id,
151
+ 'name': tool_call.get('function', {}).get('name'),
152
+ 'arguments': tool_call.get('function', {}).get('arguments'),
153
+ 'start_time': msg.created_at,
154
+ }
155
+ current_tool_call = tool_info
156
+ set_span_attribute(span, f"agno.agent.tool_call.{tool_id}", json.dumps(tool_info))
157
+
158
+ if msg.metrics:
159
+ metrics = _extract_metrics(msg.metrics)
160
+ role_prefix = f"agno.agent.metrics.{msg.role}"
161
+ for key, value in metrics.items():
162
+ set_span_attribute(span, f"{role_prefix}.{key}", str(value))
163
+
164
+ if response.tools:
165
+ for tool in response.tools:
166
+ tool_id = tool.get('tool_call_id')
167
+ if tool_id and current_tool_call and current_tool_call['id'] == tool_id:
168
+ tool_result = {
169
+ **current_tool_call,
170
+ 'result': tool.get('content'),
171
+ 'error': tool.get('tool_call_error'),
172
+ 'end_time': tool.get('created_at'),
173
+ 'metrics': tool.get('metrics'),
174
+ }
175
+ set_span_attribute(span, f"agno.agent.tool_call.{tool_id}", json.dumps(tool_result))
176
+ current_tool_call = None
177
+
178
+ yield response
179
+
180
+ except Exception as err:
181
+ span.record_exception(err)
182
+ span.set_status(Status(StatusCode.ERROR, str(err)))
183
+ raise
184
+ finally:
185
+ span.set_status(Status(StatusCode.OK))
186
+ if len(seen_tool_calls) > 0:
187
+ span.set_attribute("agno.agent.total_tool_calls", len(seen_tool_calls))
188
+ except Exception as err:
189
+ span.record_exception(err)
190
+ span.set_status(Status(StatusCode.ERROR, str(err)))
191
+ raise
192
+
193
+ return traced_method
194
+
195
+ class AgnoSpanAttributes:
196
+ span: Span
197
+ agent_data: dict
198
+
199
+ def __init__(self, span: Span, instance) -> None:
200
+ self.span = span
201
+ self.instance = instance
202
+ self.agent_data = {
203
+ "memory": {},
204
+ "model": {},
205
+ "tools": [],
206
+ }
207
+
208
+ self.run()
209
+
210
+ def run(self):
211
+ instance_attrs = {
212
+ "agent_id": self.instance.agent_id,
213
+ "session_id": self.instance.session_id,
214
+ "name": self.instance.name,
215
+ "markdown": self.instance.markdown,
216
+ "reasoning": self.instance.reasoning,
217
+ "add_references": self.instance.add_references,
218
+ "show_tool_calls": self.instance.show_tool_calls,
219
+ "stream": self.instance.stream,
220
+ "stream_intermediate_steps": self.instance.stream_intermediate_steps,
221
+ }
222
+
223
+ for key, value in instance_attrs.items():
224
+ if value is not None:
225
+ set_span_attribute(self.span, f"agno.agent.{key}", str(value))
226
+
227
+ if self.instance.model:
228
+ model_attrs = {
229
+ "id": self.instance.model.id,
230
+ "name": self.instance.model.name,
231
+ "provider": self.instance.model.provider,
232
+ "structured_outputs": self.instance.model.structured_outputs,
233
+ "supports_structured_outputs": self.instance.model.supports_structured_outputs,
234
+ }
235
+ for key, value in model_attrs.items():
236
+ if value is not None:
237
+ set_span_attribute(self.span, f"agno.agent.model.{key}", str(value))
238
+
239
+ if hasattr(self.instance.model, 'metrics') and self.instance.model.metrics:
240
+ metrics = _extract_metrics(self.instance.model.metrics)
241
+ set_span_attribute(self.span, "agno.agent.model.metrics", json.dumps(metrics))
242
+
243
+ if self.instance.tools:
244
+ tool_list = []
245
+ for tool in self.instance.tools:
246
+ if hasattr(tool, "name"):
247
+ tool_list.append(tool.name)
248
+ elif hasattr(tool, "__name__"):
249
+ tool_list.append(tool.__name__)
250
+ set_span_attribute(self.span, "agno.agent.tools", str(tool_list))
251
+
252
+ if self.instance.memory:
253
+ memory_attrs = {
254
+ "create_session_summary": self.instance.memory.create_session_summary,
255
+ "create_user_memories": self.instance.memory.create_user_memories,
256
+ "update_session_summary_after_run": self.instance.memory.update_session_summary_after_run,
257
+ "update_user_memories_after_run": self.instance.memory.update_user_memories_after_run,
258
+ }
259
+ for key, value in memory_attrs.items():
260
+ if value is not None:
261
+ set_span_attribute(self.span, f"agno.agent.memory.{key}", str(value))
@@ -0,0 +1,5 @@
1
+ from .instrumentation import PhiDataInstrumentation
2
+
3
+ __all__ = [
4
+ "PhiDataInstrumentation",
5
+ ]
@@ -0,0 +1,80 @@
1
+ """
2
+ Copyright (c) 2024 Scale3 Labs
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ """
16
+
17
+ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
18
+ from opentelemetry.trace import get_tracer
19
+ from wrapt import wrap_function_wrapper as _W
20
+ from typing import Collection
21
+ from importlib_metadata import version as v
22
+ from .patch import patch_agent, patch_memory
23
+
24
+ class PhiDataInstrumentation(BaseInstrumentor):
25
+ def instrumentation_dependencies(self) -> Collection[str]:
26
+ return ["phidata >= 2.7.10"] # Adjust version as needed
27
+
28
+ def _instrument(self, **kwargs):
29
+ tracer_provider = kwargs.get("tracer_provider")
30
+ tracer = get_tracer(__name__, "", tracer_provider)
31
+ version = v("phidata")
32
+
33
+ try:
34
+ _W(
35
+ "phi.agent.agent",
36
+ "Agent.run",
37
+ patch_agent("Agent.run", version, tracer),
38
+ )
39
+ _W(
40
+ "phi.agent.agent",
41
+ "Agent.arun",
42
+ patch_agent("Agent.arun", version, tracer),
43
+ )
44
+ _W(
45
+ "phi.agent.agent",
46
+ "Agent._run",
47
+ patch_agent("Agent._run", version, tracer),
48
+ )
49
+ _W(
50
+ "phi.agent.agent",
51
+ "Agent._arun",
52
+ patch_agent("Agent._arun", version, tracer),
53
+ )
54
+
55
+ _W(
56
+ "phi.memory.agent",
57
+ "AgentMemory.update_memory",
58
+ patch_memory("AgentMemory.update_memory", version, tracer),
59
+ )
60
+ _W(
61
+ "phi.memory.agent",
62
+ "AgentMemory.aupdate_memory",
63
+ patch_memory("AgentMemory.aupdate_memory", version, tracer),
64
+ )
65
+ _W(
66
+ "phi.memory.agent",
67
+ "AgentMemory.update_summary",
68
+ patch_memory("AgentMemory.update_summary", version, tracer),
69
+ )
70
+ _W(
71
+ "phi.memory.agent",
72
+ "AgentMemory.aupdate_summary",
73
+ patch_memory("AgentMemory.aupdate_summary", version, tracer),
74
+ )
75
+
76
+ except Exception:
77
+ pass
78
+
79
+ def _uninstrument(self, **kwargs):
80
+ pass
@@ -0,0 +1,262 @@
1
+ import json
2
+ from importlib_metadata import version as v
3
+ from langtrace.trace_attributes import FrameworkSpanAttributes
4
+ from opentelemetry import baggage
5
+ from opentelemetry.trace import Span, SpanKind, Tracer
6
+ from opentelemetry.trace.status import Status, StatusCode
7
+ from typing import Dict, Any, Optional
8
+
9
+ from langtrace_python_sdk.constants import LANGTRACE_SDK_NAME
10
+ from langtrace_python_sdk.constants.instrumentation.common import (
11
+ LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
12
+ SERVICE_PROVIDERS,
13
+ )
14
+ from langtrace_python_sdk.utils import set_span_attribute
15
+ from langtrace_python_sdk.utils.llm import get_span_name, set_span_attributes
16
+ from langtrace_python_sdk.utils.misc import serialize_args, serialize_kwargs
17
+
18
+ def _extract_metrics(metrics: Dict[str, Any]) -> Dict[str, Any]:
19
+ """Helper function to extract and format metrics"""
20
+ formatted_metrics = {}
21
+
22
+ # Extract basic metrics
23
+ for key in ['time', 'time_to_first_token', 'input_tokens', 'output_tokens',
24
+ 'prompt_tokens', 'completion_tokens', 'total_tokens']:
25
+ if key in metrics:
26
+ formatted_metrics[key] = metrics[key]
27
+
28
+ # Extract nested metric details if present
29
+ if 'prompt_tokens_details' in metrics:
30
+ formatted_metrics['prompt_tokens_details'] = metrics['prompt_tokens_details']
31
+ if 'completion_tokens_details' in metrics:
32
+ formatted_metrics['completion_tokens_details'] = metrics['completion_tokens_details']
33
+ if 'tool_call_times' in metrics:
34
+ formatted_metrics['tool_call_times'] = metrics['tool_call_times']
35
+
36
+ return formatted_metrics
37
+
38
+ def patch_memory(operation_name, version, tracer: Tracer):
39
+ def traced_method(wrapped, instance, args, kwargs):
40
+ service_provider = SERVICE_PROVIDERS["PHIDATA"]
41
+ extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
42
+ span_attributes = {
43
+ "langtrace.sdk.name": "langtrace-python-sdk",
44
+ "langtrace.service.name": service_provider,
45
+ "langtrace.service.type": "framework",
46
+ "langtrace.service.version": version,
47
+ "langtrace.version": v(LANGTRACE_SDK_NAME),
48
+ **(extra_attributes if extra_attributes is not None else {}),
49
+ }
50
+
51
+ span_attributes.update({
52
+ "phidata.memory.type": type(instance).__name__,
53
+ "phidata.memory.create_session_summary": str(instance.create_session_summary),
54
+ "phidata.memory.create_user_memories": str(instance.create_user_memories),
55
+ "phidata.memory.retrieval": str(instance.retrieval)
56
+ })
57
+
58
+ inputs = {}
59
+ if len(args) > 0:
60
+ inputs["args"] = serialize_args(*args)
61
+ if len(kwargs) > 0:
62
+ inputs["kwargs"] = serialize_kwargs(**kwargs)
63
+ span_attributes["phidata.memory.inputs"] = json.dumps(inputs)
64
+
65
+ attributes = FrameworkSpanAttributes(**span_attributes)
66
+
67
+ with tracer.start_as_current_span(
68
+ get_span_name(operation_name), kind=SpanKind.CLIENT
69
+ ) as span:
70
+ try:
71
+ set_span_attributes(span, attributes)
72
+ result = wrapped(*args, **kwargs)
73
+
74
+ if result is not None:
75
+ set_span_attribute(span, "phidata.memory.output", str(result))
76
+
77
+ if instance.summary is not None:
78
+ set_span_attribute(span, "phidata.memory.summary", str(instance.summary))
79
+ if instance.memories is not None:
80
+ set_span_attribute(span, "phidata.memory.memories_count", str(len(instance.memories)))
81
+
82
+ span.set_status(Status(StatusCode.OK))
83
+ return result
84
+
85
+ except Exception as err:
86
+ span.record_exception(err)
87
+ span.set_status(Status(StatusCode.ERROR, str(err)))
88
+ raise
89
+
90
+ return traced_method
91
+
92
+ def patch_agent(operation_name, version, tracer: Tracer):
93
+ def traced_method(wrapped, instance, args, kwargs):
94
+ service_provider = SERVICE_PROVIDERS["PHIDATA"]
95
+ extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
96
+ span_attributes = {
97
+ "langtrace.sdk.name": "langtrace-python-sdk",
98
+ "langtrace.service.name": service_provider,
99
+ "langtrace.service.type": "framework",
100
+ "langtrace.service.version": version,
101
+ "langtrace.version": v(LANGTRACE_SDK_NAME),
102
+ **(extra_attributes if extra_attributes is not None else {}),
103
+ }
104
+
105
+ attributes = FrameworkSpanAttributes(**span_attributes)
106
+
107
+ with tracer.start_as_current_span(
108
+ get_span_name(operation_name), kind=SpanKind.CLIENT
109
+ ) as span:
110
+ try:
111
+ set_span_attributes(span, attributes)
112
+ PhiDataSpanAttributes(span=span, instance=instance)
113
+ result_generator = wrapped(*args, **kwargs)
114
+
115
+ accumulated_content = ""
116
+ current_tool_call = None
117
+ response_metadata = None
118
+ seen_tool_calls = set()
119
+
120
+ try:
121
+ for response in result_generator:
122
+ if not hasattr(response, 'model_dump'):
123
+ yield response
124
+ continue
125
+
126
+ if not response_metadata:
127
+ response_metadata = {
128
+ "run_id": response.run_id,
129
+ "agent_id": response.agent_id,
130
+ "session_id": response.session_id,
131
+ "model": response.model,
132
+ "content_type": response.content_type,
133
+ }
134
+ for key, value in response_metadata.items():
135
+ if value is not None:
136
+ set_span_attribute(span, f"phidata.agent.{key}", str(value))
137
+
138
+ if response.content:
139
+ accumulated_content += response.content
140
+ set_span_attribute(span, "phidata.agent.response", accumulated_content)
141
+
142
+ if response.messages:
143
+ for msg in response.messages:
144
+ if msg.tool_calls:
145
+ for tool_call in msg.tool_calls:
146
+ tool_id = tool_call.get('id')
147
+ if tool_id and tool_id not in seen_tool_calls:
148
+ seen_tool_calls.add(tool_id)
149
+ tool_info = {
150
+ 'id': tool_id,
151
+ 'name': tool_call.get('function', {}).get('name'),
152
+ 'arguments': tool_call.get('function', {}).get('arguments'),
153
+ 'start_time': msg.created_at,
154
+ }
155
+ current_tool_call = tool_info
156
+ set_span_attribute(span, f"phidata.agent.tool_call.{tool_id}", json.dumps(tool_info))
157
+
158
+ if msg.metrics:
159
+ metrics = _extract_metrics(msg.metrics)
160
+ role_prefix = f"phidata.agent.metrics.{msg.role}"
161
+ for key, value in metrics.items():
162
+ set_span_attribute(span, f"{role_prefix}.{key}", str(value))
163
+
164
+ if response.tools:
165
+ for tool in response.tools:
166
+ tool_id = tool.get('tool_call_id')
167
+ if tool_id and current_tool_call and current_tool_call['id'] == tool_id:
168
+ tool_result = {
169
+ **current_tool_call,
170
+ 'result': tool.get('content'),
171
+ 'error': tool.get('tool_call_error'),
172
+ 'end_time': tool.get('created_at'),
173
+ 'metrics': tool.get('metrics'),
174
+ }
175
+ set_span_attribute(span, f"phidata.agent.tool_call.{tool_id}", json.dumps(tool_result))
176
+ current_tool_call = None
177
+
178
+ yield response
179
+
180
+ except Exception as err:
181
+ span.record_exception(err)
182
+ span.set_status(Status(StatusCode.ERROR, str(err)))
183
+ raise
184
+ finally:
185
+ span.set_status(Status(StatusCode.OK))
186
+ if len(seen_tool_calls) > 0:
187
+ span.set_attribute("phidata.agent.total_tool_calls", len(seen_tool_calls))
188
+
189
+ except Exception as err:
190
+ span.record_exception(err)
191
+ span.set_status(Status(StatusCode.ERROR, str(err)))
192
+ raise
193
+
194
+ return traced_method
195
+
196
+ class PhiDataSpanAttributes:
197
+ span: Span
198
+ agent_data: dict
199
+
200
+ def __init__(self, span: Span, instance) -> None:
201
+ self.span = span
202
+ self.instance = instance
203
+ self.agent_data = {
204
+ "memory": {},
205
+ "model": {},
206
+ "tools": [],
207
+ }
208
+
209
+ self.run()
210
+
211
+ def run(self):
212
+ instance_attrs = {
213
+ "agent_id": self.instance.agent_id,
214
+ "session_id": self.instance.session_id,
215
+ "name": self.instance.name,
216
+ "markdown": self.instance.markdown,
217
+ "reasoning": self.instance.reasoning,
218
+ "add_references": self.instance.add_references,
219
+ "show_tool_calls": self.instance.show_tool_calls,
220
+ "stream": self.instance.stream,
221
+ "stream_intermediate_steps": self.instance.stream_intermediate_steps,
222
+ }
223
+
224
+ for key, value in instance_attrs.items():
225
+ if value is not None:
226
+ set_span_attribute(self.span, f"phidata.agent.{key}", str(value))
227
+
228
+ if self.instance.model:
229
+ model_attrs = {
230
+ "id": self.instance.model.id,
231
+ "name": self.instance.model.name,
232
+ "provider": self.instance.model.provider,
233
+ "structured_outputs": self.instance.model.structured_outputs,
234
+ "supports_structured_outputs": self.instance.model.supports_structured_outputs,
235
+ }
236
+ for key, value in model_attrs.items():
237
+ if value is not None:
238
+ set_span_attribute(self.span, f"phidata.agent.model.{key}", str(value))
239
+
240
+ if hasattr(self.instance.model, 'metrics') and self.instance.model.metrics:
241
+ metrics = _extract_metrics(self.instance.model.metrics)
242
+ set_span_attribute(self.span, "phidata.agent.model.metrics", json.dumps(metrics))
243
+
244
+ if self.instance.tools:
245
+ tool_list = []
246
+ for tool in self.instance.tools:
247
+ if hasattr(tool, "name"):
248
+ tool_list.append(tool.name)
249
+ elif hasattr(tool, "__name__"):
250
+ tool_list.append(tool.__name__)
251
+ set_span_attribute(self.span, "phidata.agent.tools", str(tool_list))
252
+
253
+ if self.instance.memory:
254
+ memory_attrs = {
255
+ "create_session_summary": self.instance.memory.create_session_summary,
256
+ "create_user_memories": self.instance.memory.create_user_memories,
257
+ "update_session_summary_after_run": self.instance.memory.update_session_summary_after_run,
258
+ "update_user_memories_after_run": self.instance.memory.update_user_memories_after_run,
259
+ }
260
+ for key, value in memory_attrs.items():
261
+ if value is not None:
262
+ set_span_attribute(self.span, f"phidata.agent.memory.{key}", str(value))
@@ -74,6 +74,8 @@ from langtrace_python_sdk.instrumentation import (
74
74
  MilvusInstrumentation,
75
75
  GoogleGenaiInstrumentation,
76
76
  GraphlitInstrumentation,
77
+ PhiDataInstrumentation,
78
+ AgnoInstrumentation,
77
79
  )
78
80
  from opentelemetry.util.re import parse_env_headers
79
81
  from sentry_sdk.types import Event, Hint
@@ -323,6 +325,8 @@ def init(
323
325
  "google-generativeai": GeminiInstrumentation(),
324
326
  "google-genai": GoogleGenaiInstrumentation(),
325
327
  "graphlit-client": GraphlitInstrumentation(),
328
+ "phidata": PhiDataInstrumentation(),
329
+ "agno": AgnoInstrumentation(),
326
330
  "mistralai": MistralInstrumentation(),
327
331
  "boto3": AWSBedrockInstrumentation(),
328
332
  "autogen": AutogenInstrumentation(),
@@ -1 +1 @@
1
- __version__ = "3.6.3"
1
+ __version__ = "3.8.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langtrace-python-sdk
3
- Version: 3.6.3
3
+ Version: 3.8.0
4
4
  Summary: Python SDK for LangTrace
5
5
  Project-URL: Homepage, https://github.com/Scale3-Labs/langtrace-python-sdk
6
6
  Author-email: Scale3 Labs <engineering@scale3labs.com>
@@ -42,6 +42,7 @@ Requires-Dist: litellm==1.48.7; extra == 'dev'
42
42
  Requires-Dist: mistralai; extra == 'dev'
43
43
  Requires-Dist: ollama; extra == 'dev'
44
44
  Requires-Dist: openai==1.60.0; extra == 'dev'
45
+ Requires-Dist: phidata; extra == 'dev'
45
46
  Requires-Dist: pinecone; extra == 'dev'
46
47
  Requires-Dist: python-dotenv; extra == 'dev'
47
48
  Requires-Dist: qdrant-client; extra == 'dev'
@@ -136,6 +137,9 @@ Langtrace automatically captures traces from the following vendors:
136
137
  | Autogen | ❌ | ✅ |
137
138
  | HiveAgent | ❌ | ✅ |
138
139
  | Inspect AI | ❌ | ✅ |
140
+ | Graphlit | ❌ | ✅ |
141
+ | Phidata | ❌ | ✅ |
142
+ | Arch | ❌ | ✅ |
139
143
 
140
144
  ### Vector Databases
141
145
  | Database | TypeScript SDK | Python SDK |
@@ -102,6 +102,8 @@ examples/openai_example/resources/mask.png,sha256=mUE9Dfp-x8jI0Nh4WGr0P9pueUqEZf
102
102
  examples/otlp_example/otlp_basic.py,sha256=Ykbzu6EpO-V1wQsPePgC16eLFVym91r-ZR-SDj2mIT0,1346
103
103
  examples/otlp_example/otlp_with_langtrace.py,sha256=0x5UOrisqaOSkBP8nbaNqL83_gB-jNM0Uq5FXZ9B4Q0,1535
104
104
  examples/perplexity_example/basic.py,sha256=bp7n27gaugJkaFVyt8pjaEfi66lYcqP6eFFjPewUShY,668
105
+ examples/phidata_example/__init__.py,sha256=akqLiv3M71QXio4Ucc2gPcH-oY2I7-3WjTzVMDh1WC0,190
106
+ examples/phidata_example/agent.py,sha256=pHTE1Xj0Ppv61tjKXz9U_wlP6SXjAJeoGWNOnWe_cjQ,550
105
107
  examples/pinecone_example/__init__.py,sha256=_rvn7Ygt_QWMQoa5wB2GB0S9gZVrlJrPrEhXqU3hPKw,427
106
108
  examples/pinecone_example/basic.py,sha256=5MoHZMBxHMdC61oj-CP19gj9SxSvIcDrQL934JPZoQs,1549
107
109
  examples/qdrant_example/__init__.py,sha256=Ze9xEzW8FiHUO58YBa8JeHNOwcmo3dpYH77AkdyglKU,197
@@ -112,8 +114,8 @@ examples/vertexai_example/main.py,sha256=gndId5X5ksD-ycxnAWMdEqIDbLc3kz5Vt8vm4YP
112
114
  examples/weaviate_example/__init__.py,sha256=8JMDBsRSEV10HfTd-YC7xb4txBjD3la56snk-Bbg2Kw,618
113
115
  examples/weaviate_example/query_text.py,sha256=wPHQTc_58kPoKTZMygVjTj-2ZcdrIuaausJfMxNQnQc,127162
114
116
  langtrace_python_sdk/__init__.py,sha256=VZM6i71NR7pBQK6XvJWRelknuTYUhqwqE7PlicKa5Wg,1166
115
- langtrace_python_sdk/langtrace.py,sha256=cQHDnwAgqFrYcdv0NPvngUw1lhWXLLQ08f_-It1_fuE,14611
116
- langtrace_python_sdk/version.py,sha256=Y-FgC1VqJrp0YmwoM9m0nqI9YiRGdYqtfNFOz9j36SQ,22
117
+ langtrace_python_sdk/langtrace.py,sha256=k1Zp7AUZ6WKuDqbb7V3PGhHu7Vp8S8-cpevLu7QIdPM,14748
118
+ langtrace_python_sdk/version.py,sha256=edzbr-98v4uv9fXQrYJg9zw4sjVitC66PBVB8KQLB0I,22
117
119
  langtrace_python_sdk/constants/__init__.py,sha256=3CNYkWMdd1DrkGqzLUgNZXjdAlM6UFMlf_F-odAToyc,146
118
120
  langtrace_python_sdk/constants/exporter/langtrace_exporter.py,sha256=EVCrouYCpY98f0KSaKr4PzNxPULTZZO6dSA_crEOyJU,106
119
121
  langtrace_python_sdk/constants/instrumentation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -121,7 +123,7 @@ langtrace_python_sdk/constants/instrumentation/anthropic.py,sha256=YX3llt3zwDY6X
121
123
  langtrace_python_sdk/constants/instrumentation/aws_bedrock.py,sha256=QwKtO4NBarOZoGkt5cFCcpxAw3zvZxcMMWBbzPPGv-g,422
122
124
  langtrace_python_sdk/constants/instrumentation/chroma.py,sha256=hiPGYdHS0Yj4Kh3eaYBbuCAl_swqIygu80yFqkOgdak,955
123
125
  langtrace_python_sdk/constants/instrumentation/cohere.py,sha256=9yD133VdrYZ5BoJR4nJHlj67gHEImB9-KsD-NkzHW1I,1159
124
- langtrace_python_sdk/constants/instrumentation/common.py,sha256=8yPsGPo0q8PBXkST74mFKlmlg54c-iPSzc-dqeLigew,1252
126
+ langtrace_python_sdk/constants/instrumentation/common.py,sha256=ZW4_dM-2xWyf-Mw4rqpZtfg_a9KSGX_1lhR-TOq-A4Y,1298
125
127
  langtrace_python_sdk/constants/instrumentation/embedchain.py,sha256=HodCJvaFjILoOG50OwFObxfVxt_8VUaIAIqvgoN3tzo,278
126
128
  langtrace_python_sdk/constants/instrumentation/gemini.py,sha256=UAmfgg9FM7uNeOCdPfWlir6OIH-8BoxFGPRpdBd9ZZs,358
127
129
  langtrace_python_sdk/constants/instrumentation/groq.py,sha256=VFXmIl4aqGY_fS0PAmjPj_Qm7Tibxbx7Ur_e7rQpqXc,134
@@ -138,7 +140,10 @@ langtrace_python_sdk/constants/instrumentation/weaviate.py,sha256=gtv-JBxvNGClEM
138
140
  langtrace_python_sdk/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
139
141
  langtrace_python_sdk/extensions/langtrace_exporter.py,sha256=ckd8dMmY6h2oxE04p1JFLwUB5PSJX_Cy4eDFEM6aj4Y,6605
140
142
  langtrace_python_sdk/extensions/langtrace_filesystem.py,sha256=34fZutG28EJ66l67OvTGsydAH3ZpXgikdE7hVLqBpG4,7863
141
- langtrace_python_sdk/instrumentation/__init__.py,sha256=V7RS5u2L4wNBbaLpFlNBykj_sGVFBj63CDM2VYrlSFM,2394
143
+ langtrace_python_sdk/instrumentation/__init__.py,sha256=UcdRFsfst03uzSQt5LefGcPMqBGueaMKSX5PVFG6OwE,2533
144
+ langtrace_python_sdk/instrumentation/agno/__init__.py,sha256=95fn4oA-CHB0mxc6KnVB20KSbXGl_ZZr9n99EEaXzrY,91
145
+ langtrace_python_sdk/instrumentation/agno/instrumentation.py,sha256=CrwHzkzLpdo2m2nBHE69d8MIFMxZuvTsT1LTxqlTWV0,2664
146
+ langtrace_python_sdk/instrumentation/agno/patch.py,sha256=EOaz3n_FYx7qXtd_liSjvBqjvD5E1ze6FL1K51v8NBc,12060
142
147
  langtrace_python_sdk/instrumentation/anthropic/__init__.py,sha256=donrurJAGYlxrSRA3BIf76jGeUcAx9Tq8CVpah68S0Y,101
143
148
  langtrace_python_sdk/instrumentation/anthropic/instrumentation.py,sha256=ndXdruI0BG7n75rsuEpKjfzePxrZxg40gZ39ONmD_v4,1845
144
149
  langtrace_python_sdk/instrumentation/anthropic/patch.py,sha256=ztPN4VZujoxYOKhTbFnup7Ibms9NAzYCPAJY43NUgKw,4935
@@ -216,6 +221,9 @@ langtrace_python_sdk/instrumentation/openai/__init__.py,sha256=VPHRNCQEdkizIVP2d
216
221
  langtrace_python_sdk/instrumentation/openai/instrumentation.py,sha256=PZxI0qfoud1VsKGmJu49YDp0Z9z9TzCR8qxR3uznOMA,2810
217
222
  langtrace_python_sdk/instrumentation/openai/patch.py,sha256=mRE150sHGL2dq4oOKTYWek1vup7HuRW_sqP6nNOd3N8,27862
218
223
  langtrace_python_sdk/instrumentation/openai/types.py,sha256=aVkoa7tmAbYfyOhnyMrDaVjQuwhmRNLMthlNtKMtWX8,4311
224
+ langtrace_python_sdk/instrumentation/phidata/__init__.py,sha256=q2v6luvqp9gFf1AJX6YrBvuyMC_q6cEnB5syl2HNPlU,97
225
+ langtrace_python_sdk/instrumentation/phidata/instrumentation.py,sha256=E_u0WUtBukmWjKjkgtJgeCS6ab_2xhcamLOH9cERNCM,2694
226
+ langtrace_python_sdk/instrumentation/phidata/patch.py,sha256=-Jf_20wLLRGRM6sY3RreS-ocXjdq5m33-gxNtl_eUdQ,12133
219
227
  langtrace_python_sdk/instrumentation/pinecone/__init__.py,sha256=DzXyGh9_MGWveJvXULkFwdkf7PbG2s3bAWtT1Dmz7Ok,99
220
228
  langtrace_python_sdk/instrumentation/pinecone/instrumentation.py,sha256=2jhxGmXPrdSsrETRR_e58Rdc_uoKfRunvsCc660v6QA,1831
221
229
  langtrace_python_sdk/instrumentation/pinecone/patch.py,sha256=S5fPFYgZEIHAJm92YDm_R3n8SZOHb5sYbCsSFbLOcu0,5248
@@ -285,8 +293,8 @@ tests/pinecone/cassettes/test_query.yaml,sha256=b5v9G3ssUy00oG63PlFUR3JErF2Js-5A
285
293
  tests/pinecone/cassettes/test_upsert.yaml,sha256=neWmQ1v3d03V8WoLl8FoFeeCYImb8pxlJBWnFd_lITU,38607
286
294
  tests/qdrant/conftest.py,sha256=9n0uHxxIjWk9fbYc4bx-uP8lSAgLBVx-cV9UjnsyCHM,381
287
295
  tests/qdrant/test_qdrant.py,sha256=pzjAjVY2kmsmGfrI2Gs2xrolfuaNHz7l1fqGQCjp5_o,3353
288
- langtrace_python_sdk-3.6.3.dist-info/METADATA,sha256=FsMzDmiBYQnekanZmi3gnCHPVly7JbPx94kgh4W_MeY,15683
289
- langtrace_python_sdk-3.6.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
290
- langtrace_python_sdk-3.6.3.dist-info/entry_points.txt,sha256=1_b9-qvf2fE7uQNZcbUei9vLpFZBbbh9LrtGw95ssAo,70
291
- langtrace_python_sdk-3.6.3.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
292
- langtrace_python_sdk-3.6.3.dist-info/RECORD,,
296
+ langtrace_python_sdk-3.8.0.dist-info/METADATA,sha256=42XsIyqMHzukQVePvhG78-ITceSu6l6yXFWd3_C4zZ4,15792
297
+ langtrace_python_sdk-3.8.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
298
+ langtrace_python_sdk-3.8.0.dist-info/entry_points.txt,sha256=1_b9-qvf2fE7uQNZcbUei9vLpFZBbbh9LrtGw95ssAo,70
299
+ langtrace_python_sdk-3.8.0.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
300
+ langtrace_python_sdk-3.8.0.dist-info/RECORD,,