Skip to content

Commit ab9d7b1

Browse files
Release (#316)
* remove logs * remove requirements * Bump version * Squash * CrewAI enhancements and embedchain support (#313) * Minor fixes * example update * remove stream_usage * Add embed chain support * update README * fix * update version and deps --------- Co-authored-by: Ali Waleed <[email protected]> * CrewAI and Langchain enhancements (#315) * Cleanups and bugfixes * bump version * bump version --------- Co-authored-by: Ali Waleed <[email protected]>
1 parent 0d6f5ee commit ab9d7b1

File tree

9 files changed

+188
-140
lines changed

9 files changed

+188
-140
lines changed

src/examples/crewai_example/simple_agent/main.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ def run(self):
1818
tasks = PoetryTasks()
1919

2020
poetry_agent = agents.create_poet_agent()
21-
poetry_agent_2 = agents.poet_agent_2()
22-
poetry_agent_3 = agents.poet_agent_3()
21+
# poetry_agent_2 = agents.poet_agent_2()
22+
# poetry_agent_3 = agents.poet_agent_3()
2323

2424
create_poem = tasks.create_poem(poetry_agent, self.topic)
25-
create_poem_2 = tasks.create_poem(poetry_agent_2, self.topic)
26-
create_poem_3 = tasks.create_poem(poetry_agent_3, self.topic)
25+
# create_poem_2 = tasks.create_poem(poetry_agent_2, self.topic)
26+
# create_poem_3 = tasks.create_poem(poetry_agent_3, self.topic)
2727

2828
crew = Crew(agents=[poetry_agent], tasks=[create_poem], verbose=True, memory=True)
2929
res = crew.kickoff()

src/langtrace_python_sdk/instrumentation/crewai/instrumentation.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from wrapt import wrap_function_wrapper as _W
2020
from typing import Collection
2121
from importlib_metadata import version as v
22-
from .patch import patch_crew
22+
from .patch import patch_crew, patch_memory
2323

2424

2525
class CrewAIInstrumentation(BaseInstrumentor):
@@ -49,7 +49,23 @@ def _instrument(self, **kwargs):
4949
"Task.execute_sync",
5050
patch_crew("Task.execute", version, tracer),
5151
)
52-
except Exception as e:
52+
_W(
53+
"crewai.memory.storage.rag_storage",
54+
"RAGStorage.save",
55+
patch_memory("RAGStorage.save", version, tracer),
56+
)
57+
_W(
58+
"crewai.memory.storage.rag_storage",
59+
"RAGStorage.search",
60+
patch_memory("RAGStorage.search", version, tracer),
61+
)
62+
_W(
63+
"crewai.memory.storage.rag_storage",
64+
"RAGStorage.reset",
65+
patch_memory("RAGStorage.reset", version, tracer),
66+
)
67+
# pylint: disable=broad-except
68+
except Exception:
5369
pass
5470

5571
def _uninstrument(self, **kwargs):

src/langtrace_python_sdk/instrumentation/crewai/patch.py

Lines changed: 58 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -11,82 +11,56 @@
1111
from langtrace.trace_attributes import FrameworkSpanAttributes
1212
from opentelemetry.trace import SpanKind, Span, Tracer
1313
from opentelemetry.trace.status import Status, StatusCode
14+
from langtrace_python_sdk.utils.misc import serialize_args, serialize_kwargs
1415

1516

16-
crew_properties = {
17-
"tasks": "object",
18-
"agents": "object",
19-
"cache": "bool",
20-
"process": "object",
21-
"verbose": "bool",
22-
"memory": "bool",
23-
"embedder": "json",
24-
"full_output": "bool",
25-
"manager_llm": "object",
26-
"manager_agent": "object",
27-
"manager_callbacks": "object",
28-
"function_calling_llm": "object",
29-
"config": "json",
30-
"id": "object",
31-
"max_rpm": "int",
32-
"share_crew": "bool",
33-
"step_callback": "object",
34-
"task_callback": "object",
35-
"prompt_file": "str",
36-
"output_log_file": "bool",
37-
}
38-
39-
task_properties = {
40-
"id": "object",
41-
"used_tools": "int",
42-
"tools_errors": "int",
43-
"delegations": "int",
44-
"i18n": "object",
45-
"thread": "object",
46-
"prompt_context": "object",
47-
"description": "str",
48-
"expected_output": "str",
49-
"config": "object",
50-
"callback": "str",
51-
"agent": "object",
52-
"context": "object",
53-
"async_execution": "bool",
54-
"output_json": "object",
55-
"output_pydantic": "object",
56-
"output_file": "object",
57-
"output": "object",
58-
"tools": "object",
59-
"human_input": "bool",
60-
}
61-
62-
agent_properties = {
63-
"formatting_errors": "int",
64-
"id": "object",
65-
"role": "str",
66-
"goal": "str",
67-
"backstory": "str",
68-
"cache": "bool",
69-
"config": "object",
70-
"max_rpm": "int",
71-
"verbose": "bool",
72-
"allow_delegation": "bool",
73-
"tools": "object",
74-
"max_iter": "int",
75-
"max_execution_time": "object",
76-
"agent_executor": "object",
77-
"tools_handler": "object",
78-
"force_answer_max_iterations": "int",
79-
"crew": "object",
80-
"cache_handler": "object",
81-
"step_callback": "object",
82-
"i18n": "object",
83-
"llm": "object",
84-
"function_calling_llm": "object",
85-
"callbacks": "object",
86-
"system_template": "object",
87-
"prompt_template": "object",
88-
"response_template": "object",
89-
}
17+
def patch_memory(operation_name, version, tracer: Tracer):
18+
def traced_method(wrapped, instance, args, kwargs):
19+
service_provider = SERVICE_PROVIDERS["CREWAI"]
20+
extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
21+
span_attributes = {
22+
"langtrace.sdk.name": "langtrace-python-sdk",
23+
"langtrace.service.name": service_provider,
24+
"langtrace.service.type": "framework",
25+
"langtrace.service.version": version,
26+
"langtrace.version": v(LANGTRACE_SDK_NAME),
27+
**(extra_attributes if extra_attributes is not None else {}),
28+
}
29+
30+
inputs = {}
31+
if len(args) > 0:
32+
inputs["args"] = serialize_args(*args)
33+
if len(kwargs) > 0:
34+
inputs["kwargs"] = serialize_kwargs(**kwargs)
35+
span_attributes["crewai.memory.storage.rag_storage.inputs"] = json.dumps(inputs)
36+
37+
attributes = FrameworkSpanAttributes(**span_attributes)
38+
39+
with tracer.start_as_current_span(
40+
get_span_name(operation_name), kind=SpanKind.CLIENT
41+
) as span:
42+
43+
try:
44+
set_span_attributes(span, attributes)
45+
result = wrapped(*args, **kwargs)
46+
if result is not None and len(result) > 0:
47+
set_span_attribute(span, "crewai.memory.storage.rag_storage.outputs", str(result))
48+
if result:
49+
span.set_status(Status(StatusCode.OK))
50+
span.end()
51+
return result
52+
53+
except Exception as err:
54+
# Record the exception in the span
55+
span.record_exception(err)
56+
57+
# Set the span status to indicate an error
58+
span.set_status(Status(StatusCode.ERROR, str(err)))
59+
60+
# Reraise the exception to ensure it's not swallowed
61+
raise
62+
63+
return traced_method
9064

9165

9266
def patch_crew(operation_name, version, tracer: Tracer):
@@ -161,25 +135,26 @@ def run(self):
161135
instance_name = self.instance.__class__.__name__
162136
if instance_name == "Crew":
163137
self.set_crew_attributes()
164-
set_span_attribute(self.span, "crewai.crew.config", json.dumps(self.crew))
138+
for key, value in self.crew.items():
139+
key = f"crewai.crew.{key}"
140+
set_span_attribute(self.span, key, value)
165141

166142
elif instance_name == "Agent":
167143
agent = self.set_agent_attributes()
168-
# for key, value in agent.items():
169-
# set_span_attribute(self.span, key, value)
170-
set_span_attribute(self.span, "crewai.agent.config", json.dumps(agent))
144+
for key, value in agent.items():
145+
key = f"crewai.agent.{key}"
146+
set_span_attribute(self.span, key, value)
147+
171148
elif instance_name == "Task":
172149
task = self.set_task_attributes()
173-
# uncomment if you want to spread attributes for the UI instead of dumping the whole object
174-
# for key, value in task.items():
175-
# set_span_attribute(self.span, key, value)
176-
set_span_attribute(self.span, "crewai.task.config", json.dumps(task))
150+
for key, value in task.items():
151+
key = f"crewai.task.{key}"
152+
set_span_attribute(self.span, key, value)
177153

178154
def set_crew_attributes(self):
179155
for key, value in self.instance.__dict__.items():
180156
if key == "tasks":
181157
self._parse_tasks(value)
182-
183158
elif key == "agents":
184159
self._parse_agents(value)
185160
else:

src/langtrace_python_sdk/instrumentation/embedchain/patch.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ def traced_method(wrapped, instance, args, kwargs):
5252
**(extra_attributes if extra_attributes is not None else {}),
5353
}
5454

55+
if hasattr(instance, 'config') and isinstance(instance.config, object):
56+
config_dict = instance.config.__dict__
57+
if isinstance(config_dict, dict):
58+
span_attributes["embedchain.config"] = json.dumps(config_dict)
59+
5560
if len(args) > 0:
5661
span_attributes["embedchain.inputs"] = json.dumps(args)
5762

src/langtrace_python_sdk/instrumentation/langchain/instrumentation.py

Lines changed: 62 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
"""
1616

1717
import importlib.metadata
18-
import inspect
1918
import logging
2019
from typing import Collection
2120

@@ -28,46 +27,46 @@
2827
logging.basicConfig(level=logging.FATAL)
2928

3029

31-
def patch_module_classes(
32-
module_name, tracer, version, task, trace_output=True, trace_input=True
33-
):
34-
"""
35-
Generic function to patch all public methods of all classes in a given module.
36-
37-
Parameters:
38-
- module: The module object containing the classes to patch.
39-
- module_name: The name of the module, used in the prefix for `wrap_function_wrapper`.
40-
- tracer: The tracer object used in `generic_patch`.
41-
- version: The version parameter used in `generic_patch`.
42-
- task: The name used to identify the type of task in `generic_patch`.
43-
- exclude_private: Whether to exclude private methods (those starting with '_').
44-
- trace_output: Whether to trace the output of the patched methods.
45-
- trace_input: Whether to trace the input of the patched methods.
46-
"""
47-
# import the module
48-
module = importlib.import_module(module_name)
49-
# loop through all public classes in the module
50-
for name, obj in inspect.getmembers(
51-
module,
52-
lambda member: inspect.isclass(member) and member.__module__ == module.__name__,
53-
):
54-
# loop through all public methods of the class
55-
for method_name, _ in inspect.getmembers(obj, predicate=inspect.isfunction):
56-
# Skip private methods
57-
if method_name.startswith("_"):
58-
continue
59-
try:
60-
method_path = f"{name}.{method_name}"
61-
wrap_function_wrapper(
62-
module_name,
63-
method_path,
64-
generic_patch(
65-
method_path, task, tracer, version, trace_output, trace_input
66-
),
67-
)
68-
# pylint: disable=broad-except
69-
except Exception:
70-
pass
30+
# def patch_module_classes(
31+
# module_name, tracer, version, task, trace_output=True, trace_input=True
32+
# ):
33+
# """
34+
# Generic function to patch all public methods of all classes in a given module.
35+
36+
# Parameters:
37+
# - module: The module object containing the classes to patch.
38+
# - module_name: The name of the module, used in the prefix for `wrap_function_wrapper`.
39+
# - tracer: The tracer object used in `generic_patch`.
40+
# - version: The version parameter used in `generic_patch`.
41+
# - task: The name used to identify the type of task in `generic_patch`.
42+
# - exclude_private: Whether to exclude private methods (those starting with '_').
43+
# - trace_output: Whether to trace the output of the patched methods.
44+
# - trace_input: Whether to trace the input of the patched methods.
45+
# """
46+
# # import the module
47+
# module = importlib.import_module(module_name)
48+
# # loop through all public classes in the module
49+
# for name, obj in inspect.getmembers(
50+
# module,
51+
# lambda member: inspect.isclass(member) and member.__module__ == module.__name__,
52+
# ):
53+
# # loop through all public methods of the class
54+
# for method_name, _ in inspect.getmembers(obj, predicate=inspect.isfunction):
55+
# # Skip private methods
56+
# if method_name.startswith("_"):
57+
# continue
58+
# try:
59+
# method_path = f"{name}.{method_name}"
60+
# wrap_function_wrapper(
61+
# module_name,
62+
# method_path,
63+
# generic_patch(
64+
# method_path, task, tracer, version, trace_output, trace_input
65+
# ),
66+
# )
67+
# # pylint: disable=broad-except
68+
# except Exception:
69+
# pass
7170

7271

7372
class LangchainInstrumentation(BaseInstrumentor):
@@ -83,14 +82,28 @@ def _instrument(self, **kwargs):
8382
tracer = get_tracer(__name__, "", tracer_provider)
8483
version = importlib.metadata.version("langchain")
8584

86-
modules_to_patch = [
87-
("langchain.text_splitter", "split_text", True, True),
88-
]
89-
90-
for module_name, task, trace_output, trace_input in modules_to_patch:
91-
patch_module_classes(
92-
module_name, tracer, version, task, trace_output, trace_input
93-
)
85+
wrap_function_wrapper(
86+
"langchain.agents.agent",
87+
"RunnableAgent.plan",
88+
generic_patch(
89+
"RunnableAgent.plan", "plan", tracer, version, True, True
90+
),
91+
)
92+
93+
wrap_function_wrapper(
94+
"langchain.agents.agent",
95+
"RunnableAgent.aplan",
96+
generic_patch(
97+
"RunnableAgent.aplan", "plan", tracer, version, True, True
98+
),
99+
)
100+
101+
# modules_to_patch = []
102+
103+
# for module_name, task, trace_output, trace_input in modules_to_patch:
104+
# patch_module_classes(
105+
# module_name, tracer, version, task, trace_output, trace_input
106+
# )
94107

95108
def _uninstrument(self, **kwargs):
96109
pass

src/langtrace_python_sdk/instrumentation/langchain/patch.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
SERVICE_PROVIDERS,
3030
)
3131
from importlib_metadata import version as v
32+
from langtrace_python_sdk.utils.misc import serialize_args, serialize_kwargs
3233

3334

3435
def generic_patch(
@@ -52,8 +53,12 @@ def traced_method(wrapped, instance, args, kwargs):
5253
**(extra_attributes if extra_attributes is not None else {}),
5354
}
5455

56+
inputs = {}
5557
if len(args) > 0 and trace_input:
56-
span_attributes["langchain.inputs"] = to_json_string(args)
58+
inputs["args"] = serialize_args(*args)
59+
if len(kwargs) > 0 and trace_input:
60+
inputs["kwargs"] = serialize_kwargs(**kwargs)
61+
span_attributes["langchain.inputs"] = json.dumps(inputs)
5762

5863
attributes = FrameworkSpanAttributes(**span_attributes)
5964

src/langtrace_python_sdk/instrumentation/langchain_core/instrumentation.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ def _instrument(self, **kwargs):
114114
"format",
115115
"format_messages",
116116
"format_prompt",
117+
"transform",
118+
"stream",
117119
"__or__",
118120
"__init__",
119121
"__repr__",

0 commit comments

Comments
 (0)