Skip to content

Commit fd74efb

Browse files
authored
Merge pull request #310 from Scale3-Labs/development
Release: Enrich CrewAI trace attributes
2 parents 14e1b6f + a05a48d commit fd74efb

File tree

6 files changed

+115
-95
lines changed

6 files changed

+115
-95
lines changed

src/examples/crewai_example/simple_agent/agents.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,5 @@ def create_poet_agent(self):
2828
goal="""Create a poem that captures the essence of a given theme or emotion""",
2929
allow_delegation=False,
3030
verbose=True,
31-
llm=self.ollama,
31+
llm=self.open_ai,
3232
)

src/examples/crewai_example/simple_agent/main.py

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
from crewai import Crew
2-
from textwrap import dedent
32
from .agents import PoetryAgents
43
from .tasks import PoetryTasks
54
from langtrace_python_sdk import langtrace
65
from dotenv import load_dotenv
7-
import agentops
86

97
load_dotenv()
10-
agentops.init()
11-
langtrace.init(write_spans_to_console=False, batch=False)
8+
langtrace.init()
129

1310

1411
class PoetryCrew:
@@ -29,18 +26,11 @@ def run(self):
2926

3027

3128
# This is the main function that you will use to run your custom crew.
29+
# You can run this file using `python -m src.examples.crewai_example.simple_agent.main`
3230
if __name__ == "__main__":
3331
print("## Welcome to Poetry Crew")
3432
print("-------------------------------")
35-
topic = input(
36-
dedent(
37-
"""
38-
What topic do you want to write a poem on?
39-
"""
40-
)
41-
)
42-
43-
poetry_crew = PoetryCrew(topic=topic)
33+
poetry_crew = PoetryCrew(topic="cold")
4434
result = poetry_crew.run()
4535
print("\n\n########################")
4636
print("## Here is you poem")

src/examples/crewai_example/simple_agent/tasks.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ def create_poem(self, agent, topic):
1818
),
1919
expected_output="A creative and expressive poem that captures the essence of the given topic.",
2020
agent=agent,
21+
output_file="poem.txt",
2122
)

src/examples/crewai_example/trip_planner/main.py

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from crewai import Crew
2-
from textwrap import dedent
32
from .agents import TravelAgents
43
from .tasks import TravelTasks
54
from langtrace_python_sdk import langtrace
@@ -59,36 +58,8 @@ def run(self):
5958
if __name__ == "__main__":
6059
print("## Welcome to Trip Planner Crew")
6160
print("-------------------------------")
62-
origin = input(
63-
dedent(
64-
"""
65-
From where will you be traveling from?
66-
"""
67-
)
68-
)
69-
cities = input(
70-
dedent(
71-
"""
72-
What are the cities options you are interested in visiting?
73-
"""
74-
)
75-
)
76-
date_range = input(
77-
dedent(
78-
"""
79-
What is the date range you are interested in traveling?
80-
"""
81-
)
82-
)
83-
interests = input(
84-
dedent(
85-
"""
86-
What are some of your high level interests and hobbies?
87-
"""
88-
)
89-
)
9061

91-
trip_crew = TripCrew(origin, cities, date_range, interests)
62+
trip_crew = TripCrew("cairo", "marsa alam", "sep", "scuba diving")
9263
result = trip_crew.run()
9364
print("\n\n########################")
9465
print("## Here is you Trip Plan")

src/langtrace_python_sdk/instrumentation/crewai/instrumentation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def _instrument(self, **kwargs):
4646
)
4747
_W(
4848
"crewai.task",
49-
"Task.execute",
49+
"Task.execute_sync",
5050
patch_crew("Task.execute", version, tracer),
5151
)
5252
except Exception as e:

src/langtrace_python_sdk/instrumentation/crewai/patch.py

Lines changed: 108 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22
from importlib_metadata import version as v
33
from langtrace_python_sdk.constants import LANGTRACE_SDK_NAME
44
from langtrace_python_sdk.utils import set_span_attribute
5-
from langtrace_python_sdk.utils.llm import get_span_name
6-
from langtrace_python_sdk.utils.silently_fail import silently_fail
5+
from langtrace_python_sdk.utils.llm import get_span_name, set_span_attributes
76
from langtrace_python_sdk.constants.instrumentation.common import (
87
LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
98
SERVICE_PROVIDERS,
109
)
1110
from opentelemetry import baggage
1211
from langtrace.trace_attributes import FrameworkSpanAttributes
13-
from opentelemetry.trace import SpanKind
12+
from opentelemetry.trace import SpanKind, Span, Tracer
1413
from opentelemetry.trace.status import Status, StatusCode
1514

1615

@@ -33,8 +32,8 @@
3332
"share_crew": "bool",
3433
"step_callback": "object",
3534
"task_callback": "object",
36-
"prompt_file": "object",
37-
"output_log_file": "object",
35+
"prompt_file": "str",
36+
"output_log_file": "bool",
3837
}
3938

4039
task_properties = {
@@ -90,9 +89,8 @@
9089
}
9190

9291

93-
def patch_crew(operation_name, version, tracer):
92+
def patch_crew(operation_name, version, tracer: Tracer):
9493
def traced_method(wrapped, instance, args, kwargs):
95-
9694
service_provider = SERVICE_PROVIDERS["CREWAI"]
9795
extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
9896
span_attributes = {
@@ -104,52 +102,15 @@ def traced_method(wrapped, instance, args, kwargs):
104102
**(extra_attributes if extra_attributes is not None else {}),
105103
}
106104

107-
crew_config = {}
108-
for key, value in instance.__dict__.items():
109-
if instance.__class__.__name__ == "Crew":
110-
if key in crew_properties and value is not None:
111-
if crew_properties[key] == "json":
112-
crew_config[key] = json.dumps(value)
113-
elif crew_properties[key] == "object":
114-
crew_config[key] = str(value)
115-
else:
116-
crew_config[key] = value
117-
elif instance.__class__.__name__ == "Agent":
118-
if key in agent_properties and value is not None:
119-
if agent_properties[key] == "json":
120-
crew_config[key] = json.dumps(value)
121-
elif agent_properties[key] == "object":
122-
crew_config[key] = str(value)
123-
else:
124-
crew_config[key] = value
125-
elif instance.__class__.__name__ == "Task":
126-
if key in task_properties and value is not None:
127-
if task_properties[key] == "json":
128-
crew_config[key] = json.dumps(value)
129-
elif task_properties[key] == "object":
130-
crew_config[key] = str(value)
131-
else:
132-
crew_config[key] = value
133-
if crew_config:
134-
if instance.__class__.__name__ == "Crew":
135-
if "inputs" in kwargs and kwargs["inputs"]:
136-
crew_config["inputs"] = json.dumps(kwargs["inputs"])
137-
span_attributes["crewai.crew.config"] = json.dumps(crew_config)
138-
elif instance.__class__.__name__ == "Agent":
139-
if "context" in kwargs and kwargs["context"]:
140-
crew_config["context"] = json.dumps(kwargs["context"])
141-
span_attributes["crewai.agent.config"] = json.dumps(crew_config)
142-
elif instance.__class__.__name__ == "Task":
143-
span_attributes["crewai.task.config"] = json.dumps(crew_config)
144-
145105
attributes = FrameworkSpanAttributes(**span_attributes)
146106

147107
with tracer.start_as_current_span(
148108
get_span_name(operation_name), kind=SpanKind.CLIENT
149109
) as span:
150-
_set_input_attributes(span, kwargs, attributes)
151110

152111
try:
112+
set_span_attributes(span, attributes)
113+
CrewAISpanAttributes(span=span, instance=instance)
153114
result = wrapped(*args, **kwargs)
154115
if result:
155116
span.set_status(Status(StatusCode.OK))
@@ -158,6 +119,7 @@ def traced_method(wrapped, instance, args, kwargs):
158119
return result
159120

160121
except Exception as err:
122+
print("Error", err)
161123
# Record the exception in the span
162124
span.record_exception(err)
163125

@@ -170,7 +132,103 @@ def traced_method(wrapped, instance, args, kwargs):
170132
return traced_method
171133

172134

173-
@silently_fail
174-
def _set_input_attributes(span, kwargs, attributes):
175-
for field, value in attributes.model_dump(by_alias=True).items():
176-
set_span_attribute(span, field, value)
135+
class CrewAISpanAttributes:
136+
span: Span
137+
crew: dict
138+
139+
def __init__(self, span: Span, instance) -> None:
140+
self.span = span
141+
self.instance = instance
142+
self.crew = {
143+
"tasks": [],
144+
"agents": [],
145+
}
146+
147+
self.run()
148+
149+
def run(self):
150+
instance_name = self.instance.__class__.__name__
151+
if instance_name == "Crew":
152+
self.set_crew_attributes()
153+
set_span_attribute(self.span, "crewai.crew.config", json.dumps(self.crew))
154+
155+
elif instance_name == "Agent":
156+
agent = self.set_agent_attributes()
157+
# for key, value in agent.items():
158+
# set_span_attribute(self.span, key, value)
159+
set_span_attribute(self.span, "crewai.agent.config", json.dumps(agent))
160+
elif instance_name == "Task":
161+
task = self.set_task_attributes()
162+
# uncomment if you want to spread attributes for the UI instead of dumping the whole object
163+
# for key, value in task.items():
164+
# set_span_attribute(self.span, key, value)
165+
set_span_attribute(self.span, "crewai.task.config", json.dumps(task))
166+
167+
def set_crew_attributes(self):
168+
for key, value in self.instance.__dict__.items():
169+
if key == "tasks":
170+
self._parse_tasks(value)
171+
172+
elif key == "agents":
173+
self._parse_agents(value)
174+
else:
175+
self.crew[key] = str(value)
176+
177+
def set_agent_attributes(self):
178+
agent = {}
179+
for key, value in self.instance.__dict__.items():
180+
if value is None:
181+
continue
182+
agent[key] = str(value)
183+
184+
return agent
185+
186+
def set_task_attributes(self):
187+
task = {}
188+
for key, value in self.instance.__dict__.items():
189+
if value is None:
190+
continue
191+
192+
if key == "agent":
193+
task[key] = value.role
194+
else:
195+
task[key] = str(value)
196+
return task
197+
198+
def _parse_agents(self, agents):
199+
for agent in agents:
200+
model = None
201+
if agent.llm is not None:
202+
if hasattr(agent.llm, "model"):
203+
model = agent.llm.model
204+
elif hasattr(agent.llm, "model_name"):
205+
model = agent.llm.model_name
206+
self.crew["agents"].append(
207+
{
208+
"id": str(agent.id),
209+
"role": agent.role,
210+
"goal": agent.goal,
211+
"backstory": agent.backstory,
212+
"cache": agent.cache,
213+
"config": agent.config,
214+
"verbose": agent.verbose,
215+
"allow_delegation": agent.allow_delegation,
216+
"tools": agent.tools,
217+
"max_iter": agent.max_iter,
218+
"llm": str(model if model is not None else ""),
219+
}
220+
)
221+
222+
def _parse_tasks(self, tasks):
223+
for task in tasks:
224+
self.crew["tasks"].append(
225+
{
226+
"agent": task.agent.role,
227+
"description": task.description,
228+
"async_execution": task.async_execution,
229+
"expected_output": task.expected_output,
230+
"human_input": task.human_input,
231+
"tools": task.tools,
232+
"output_file": task.output_file,
233+
}
234+
)

0 commit comments

Comments
 (0)