Skip to content

Commit a43712d

Browse files
committed
Refactor CrewAI instrumentation to enhance attribute management and update version to 0.36.0. Removed third-party dependencies and improved error handling in agent and task processing.
1 parent 921f0ec commit a43712d

16 files changed

Lines changed: 556 additions & 1554 deletions

File tree

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
"""AgentOps CrewAI instrumentation"""
1+
"""OpenTelemetry CrewAI instrumentation"""
22

33
from agentops.instrumentation.crewai.version import __version__
44
from agentops.instrumentation.crewai.instrumentation import CrewAIInstrumentor
55

6-
__all__ = ["CrewAIInstrumentor", "__version__"]
6+
__all__ = ["CrewAIInstrumentor", "__version__"]

agentops/instrumentation/crewai/crewai_span_attributes.py

Lines changed: 109 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from agentops.semconv.agent import AgentAttributes
1010
from agentops.semconv.tool import ToolAttributes
1111
from agentops.semconv.message import MessageAttributes
12-
from agentops.instrumentation.common.attributes import AttributeMap
1312

1413
# Initialize logger for logging potential issues and operations
1514
logger = logging.getLogger(__name__)
@@ -200,106 +199,132 @@ def _process_task(self):
200199
for i, tool in enumerate(tools):
201200
self._set_attribute(MessageAttributes.TOOL_CALL_NAME.format(i=i), tool.get("name", ""))
202201
self._set_attribute(MessageAttributes.TOOL_CALL_DESCRIPTION.format(i=i), tool.get("description", ""))
203-
except:
204-
logger.warning(f"Failed to parse tools json: {task['tools']}")
202+
except (json.JSONDecodeError, TypeError):
203+
logger.warning(f"Failed to parse tools for task: {task.get('id', 'unknown')}")
205204

206205
def _process_llm(self):
207206
"""Process an LLM instance."""
207+
llm = {}
208208
self._set_attribute(SpanAttributes.AGENTOPS_SPAN_KIND, "llm")
209209

210-
# Parse model parameters
211-
model_name = getattr(self.instance, "model", None) or getattr(self.instance, "model_name", None) or ""
212-
temp = getattr(self.instance, "temperature", None)
213-
max_tokens = getattr(self.instance, "max_tokens", None)
214-
215-
self._set_attribute(SpanAttributes.LLM_REQUEST_MODEL, model_name)
216-
if temp is not None:
217-
self._set_attribute(SpanAttributes.LLM_REQUEST_TEMPERATURE, str(temp))
218-
if max_tokens is not None:
219-
self._set_attribute(SpanAttributes.LLM_REQUEST_MAX_TOKENS, str(max_tokens))
220-
221-
# Add provider info based on class attributes or parent class
222-
if hasattr(self.instance, 'provider'):
223-
provider = self.instance.provider
224-
self._set_attribute(SpanAttributes.LLM_PROVIDER, provider)
225-
226-
# Set additional LLM attributes
227210
for key, value in self.instance.__dict__.items():
228211
if value is None:
229212
continue
230-
self._set_attribute(f"crewai.llm.{key}", str(value))
213+
llm[key] = str(value)
231214

232-
def _parse_agents(self, agents):
233-
"""Process a list of agents for a crew instance."""
234-
# Track agents in an array
235-
for i, agent in enumerate(agents):
236-
if not agent:
237-
continue
238-
239-
agent_data = self._extract_agent_data(agent)
240-
241-
# Set span attributes for each agent
242-
agent_prefix = f"crewai.crew.agent.{i}."
243-
for key, value in agent_data.items():
244-
self._set_attribute(f"{agent_prefix}{key}", value)
245-
246-
# Process tools if available
247-
if hasattr(agent, "tools") and agent.tools:
248-
parsed_tools = _parse_tools(agent.tools)
249-
for j, tool in enumerate(parsed_tools):
250-
tool_prefix = f"{agent_prefix}tool.{j}."
251-
for tool_key, tool_value in tool.items():
252-
self._set_attribute(f"{tool_prefix}{tool_key}", str(tool_value))
215+
model_name = llm.get('model_name', '') or llm.get('model', '')
216+
self._set_attribute(SpanAttributes.LLM_REQUEST_MODEL, model_name)
217+
self._set_attribute(SpanAttributes.LLM_REQUEST_TEMPERATURE, llm.get('temperature', ''))
218+
self._set_attribute(SpanAttributes.LLM_REQUEST_MAX_TOKENS, llm.get('max_tokens', ''))
219+
self._set_attribute(SpanAttributes.LLM_REQUEST_TOP_P, llm.get('top_p', ''))
220+
221+
if 'frequency_penalty' in llm:
222+
self._set_attribute(SpanAttributes.LLM_REQUEST_FREQUENCY_PENALTY, llm.get('frequency_penalty', ''))
223+
if 'presence_penalty' in llm:
224+
self._set_attribute(SpanAttributes.LLM_REQUEST_PRESENCE_PENALTY, llm.get('presence_penalty', ''))
225+
if 'streaming' in llm:
226+
self._set_attribute(SpanAttributes.LLM_REQUEST_STREAMING, llm.get('streaming', ''))
227+
228+
if 'api_key' in llm:
229+
self._set_attribute("gen_ai.request.api_key_present", "true")
253230

254-
# Process LLM if available
255-
if hasattr(agent, "llm") and agent.llm:
256-
llm = agent.llm
257-
if hasattr(llm, "model") and llm.model:
258-
self._set_attribute(f"{agent_prefix}llm.model", str(llm.model))
259-
elif hasattr(llm, "model_name") and llm.model_name:
260-
self._set_attribute(f"{agent_prefix}llm.model", str(llm.model_name))
231+
if 'base_url' in llm:
232+
self._set_attribute(SpanAttributes.LLM_OPENAI_API_BASE, llm.get('base_url', ''))
233+
234+
if 'api_version' in llm:
235+
self._set_attribute(SpanAttributes.LLM_OPENAI_API_VERSION, llm.get('api_version', ''))
261236

262-
def _parse_llms(self, llms):
263-
"""Process a dictionary of LLMs for a crew instance."""
264-
if not llms or not isinstance(llms, dict):
237+
def _parse_agents(self, agents):
238+
"""Parse agents into a list of dictionaries."""
239+
if not agents:
240+
logger.debug("CrewAI: No agents to parse")
265241
return
242+
243+
agent_count = len(agents)
244+
logger.debug(f"CrewAI: Parsing {agent_count} agents")
245+
246+
# Pre-process all agents to collect their data first
247+
agent_data_list = []
266248

267-
# Track LLMs in an array
268-
for i, (role, llm) in enumerate(llms.items()):
269-
if not llm:
249+
for idx, agent in enumerate(agents):
250+
if agent is None:
251+
logger.debug(f"CrewAI: Agent at index {idx} is None, skipping")
252+
agent_data_list.append(None)
270253
continue
271254

272-
# Set basic LLM information
273-
llm_prefix = f"crewai.crew.llm.{i}."
274-
self._set_attribute(f"{llm_prefix}role", str(role))
275-
276-
# Extract model information
277-
if hasattr(llm, "model") and llm.model:
278-
self._set_attribute(f"{llm_prefix}model", str(llm.model))
279-
elif hasattr(llm, "model_name") and llm.model_name:
280-
self._set_attribute(f"{llm_prefix}model", str(llm.model_name))
255+
logger.debug(f"CrewAI: Processing agent at index {idx}")
256+
try:
257+
agent_data = self._extract_agent_data(agent)
258+
agent_data_list.append(agent_data)
259+
except Exception as e:
260+
logger.error(f"CrewAI: Error extracting data for agent at index {idx}: {str(e)}")
261+
agent_data_list.append(None)
262+
263+
# Now set all attributes at once for each agent
264+
for idx, agent_data in enumerate(agent_data_list):
265+
if agent_data is None:
266+
continue
281267

282-
# Extract other important LLM parameters
283-
for key in ["temperature", "max_tokens", "top_p"]:
284-
if hasattr(llm, key) and getattr(llm, key) is not None:
285-
self._set_attribute(f"{llm_prefix}{key}", str(getattr(llm, key)))
268+
for key, value in agent_data.items():
269+
if key == "tools" and isinstance(value, list):
270+
for tool_idx, tool in enumerate(value):
271+
for tool_key, tool_value in tool.items():
272+
self._set_attribute(f"crewai.agents.{idx}.tools.{tool_idx}.{tool_key}", str(tool_value))
273+
else:
274+
self._set_attribute(f"crewai.agents.{idx}.{key}", value)
275+
276+
def _parse_llms(self, llms):
277+
"""Parse LLMs into a list of dictionaries."""
278+
for idx, llm in enumerate(llms):
279+
if llm is not None:
280+
model_name = getattr(llm, "model", None) or getattr(llm, "model_name", None) or ""
281+
llm_data = {
282+
"model": model_name,
283+
"temperature": llm.temperature,
284+
"max_tokens": llm.max_tokens,
285+
"max_completion_tokens": llm.max_completion_tokens,
286+
"top_p": llm.top_p,
287+
"n": llm.n,
288+
"seed": llm.seed,
289+
"base_url": llm.base_url,
290+
"api_version": llm.api_version,
291+
}
292+
293+
self._set_attribute(f"{SpanAttributes.LLM_REQUEST_MODEL}.{idx}", model_name)
294+
if hasattr(llm, "temperature"):
295+
self._set_attribute(f"{SpanAttributes.LLM_REQUEST_TEMPERATURE}.{idx}", str(llm.temperature))
296+
if hasattr(llm, "max_tokens"):
297+
self._set_attribute(f"{SpanAttributes.LLM_REQUEST_MAX_TOKENS}.{idx}", str(llm.max_tokens))
298+
if hasattr(llm, "top_p"):
299+
self._set_attribute(f"{SpanAttributes.LLM_REQUEST_TOP_P}.{idx}", str(llm.top_p))
300+
301+
for key, value in llm_data.items():
302+
if value is not None:
303+
self._set_attribute(f"crewai.llms.{idx}.{key}", str(value))
286304

287305
def _extract_agent_data(self, agent):
288-
"""Extract relevant data from an agent instance."""
289-
agent_data = {}
290-
291-
# Extract basic agent information
292-
for key in ["id", "role", "name", "goal", "backstory"]:
293-
if hasattr(agent, key) and getattr(agent, key):
294-
agent_data[key] = str(getattr(agent, key))
295-
296-
# Extract configuration settings
297-
for key in ["allow_delegation", "allow_code_execution", "max_iter", "max_rpm", "verbose"]:
298-
if hasattr(agent, key) and getattr(agent, key) is not None:
299-
agent_data[key] = str(getattr(agent, key))
300-
301-
return agent_data
306+
"""Extract data from an agent."""
307+
model = getattr(agent.llm, "model", None) or getattr(agent.llm, "model_name", None) or ""
308+
309+
tools_list = []
310+
if hasattr(agent, "tools") and agent.tools:
311+
tools_list = _parse_tools(agent.tools)
312+
313+
return {
314+
"id": str(agent.id),
315+
"role": agent.role,
316+
"goal": agent.goal,
317+
"backstory": agent.backstory,
318+
"cache": agent.cache,
319+
"config": agent.config,
320+
"verbose": agent.verbose,
321+
"allow_delegation": agent.allow_delegation,
322+
"tools": tools_list,
323+
"max_iter": agent.max_iter,
324+
"llm": str(model),
325+
}
302326

303327
def _set_attribute(self, key, value):
304-
"""Set an attribute on the span with validation."""
305-
set_span_attribute(self.span, key, value)
328+
"""Set an attribute on the span."""
329+
if value is not None and value != "":
330+
set_span_attribute(self.span, key, value)

0 commit comments

Comments
 (0)