-
Notifications
You must be signed in to change notification settings - Fork 447
feat(llmobs): [MLOB-2662] [MLOB-3100] add agent manifest #13311
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
ncybul
wants to merge
69
commits into
main
Choose a base branch
from
nicole-cybul/agent-manifest-instrumentation
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,513
−33
Draft
Changes from all commits
Commits
Show all changes
69 commits
Select commit
Hold shift + click to select a range
4cc8e94
add more fields to agent manifest
ncybul c89efd0
support hosted tools
ncybul 47275fa
move agent manifest to metadata field
ncybul 59f4693
remove open ai agents metadata for agent spans
ncybul c05a832
fix tests
ncybul 43151aa
add release note
ncybul b663a6a
update handoff field for agent manifest
ncybul 6c15ea8
feat(llmobs): add pydantic_ai instrumentation
Kyle-Verhoog 62d1d5e
fix existing apm tests
ncybul c01df4f
remove llmobs tagging for now and add model and provider tags to apm …
ncybul 6ef188d
trace tool runs
ncybul b4b1009
trace agent.iter instead of agent run methods and add corresponding t…
ncybul aa95f54
add tests for tool usage
ncybul b59254f
Merge branch 'main' into kylev/pydantic
ncybul 5bf858c
move setting base tags into the pydantic integration
ncybul 1155207
remove unused imports and fixtures
ncybul b3d1e05
fix double patching issue and begin setting llmobs tags
ncybul 3281376
add double patching checks
ncybul 7d47b69
add patch tests
ncybul 5ebcccc
run black
ncybul 53a581c
ran ruff
ncybul 9300a5d
type fixes
ncybul b8137a6
update suitespec
ncybul cf35d57
add pydantic ai section in suitespec
ncybul d232be0
remove unused argument
ncybul 038061c
remove unneeded cassettes and reuse existing one
ncybul 0a36d5d
use standard system property to get model provider and parse it if ne…
ncybul 2fb2b8d
run black
ncybul 2439bd1
ruff fix
ncybul 611279a
add error handling and test with error
ncybul 07baefe
add release note for pydantic ai integration
ncybul 408f0a4
run black
ncybul 0cf8428
add docs file
ncybul 8acbe43
remove erroneous change to openai test
ncybul ebe09ca
ignore error stack for error test
ncybul f50ab44
run black
ncybul 2a19f0f
update registry
ncybul 0b8ddb6
Merge branch 'main' into kylev/pydantic
ncybul 44f1eed
Merge branch 'kylev/pydantic' into nicole-cybul/pydantic-llmobs-tracing
ncybul c2a10e4
add preliminary span links
ncybul 2cdaa69
create helper trace method
ncybul c57d55e
add agent span tagging
ncybul 2ba1394
implement tool span tagging
ncybul 8d20fb7
Merge branch 'main' into nicole-cybul/pydantic-llmobs-tracing
ncybul b0660eb
trace run_stream separately and add tests
ncybul e5cfe9d
add agent iter error test
ncybul 59c6895
add tests for structured tool and stream calls
ncybul efc65ec
add test for run stream error
ncybul aa20370
add test for stream text where delta is true
ncybul ff676ee
consolidate stream text tests
ncybul e9593f1
add span link test
ncybul 6096966
move calculate square tool to utils file
ncybul 1c7c70e
add assertions on tool span events
ncybul 9d5fbeb
add assertions for agent and tool metadata
ncybul b468cff
perform llmobs tagging for mid streaming errors
ncybul 66b94c8
run black
ncybul 6b17c96
ruff fixes
ncybul 28323e8
add release note
ncybul 3075c7a
type fixes
ncybul 6679b87
fix type annotation
ncybul 5a212c1
more type fixes
ncybul fbe9b1b
Merge branch 'main' into nicole-cybul/agent-manifest-instrumentation
ncybul 43fff38
fix patching for run single turn
ncybul 5ae2c26
Merge branch 'nicole-cybul/pydantic-llmobs-tracing' into nicole-cybul…
ncybul dd9f53f
add agent manifest tagging for pydantic ai
ncybul f7f6aae
remove metadata tagging for pydantic ai in favor of agent manifest
ncybul c378c4e
add agent manifest for crewai
ncybul f4fbda9
remove memory from crewai manifest
ncybul c8daba4
tag what is available for langgraph agent manifest
ncybul File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
from agents import Agent | ||
from agents import Handoff | ||
from agents import ( | ||
WebSearchTool, | ||
FileSearchTool, | ||
ComputerTool, | ||
) | ||
|
||
def create_agent_manifest(agent): | ||
manifest = {} | ||
manifest["framework"] = "OpenAI" | ||
manifest["model_provider"] = "openai" | ||
|
||
if hasattr(agent, "name"): | ||
manifest["name"] = agent.name | ||
if hasattr(agent, "instructions"): | ||
manifest["instructions"] = agent.instructions | ||
if hasattr(agent, "handoff_description"): | ||
manifest["handoff_description"] = agent.handoff_description | ||
if hasattr(agent, "model"): | ||
manifest["model"] = agent.model | ||
|
||
model_settings = extract_model_settings_from_agent(agent) | ||
if model_settings: | ||
manifest["model_settings"] = model_settings | ||
|
||
tools = extract_tools_from_agent(agent) | ||
if tools: | ||
manifest["tools"] = tools | ||
|
||
handoffs = extract_handoffs_from_agent(agent) | ||
if handoffs: | ||
manifest["handoffs"] = handoffs | ||
|
||
guardrails = extract_guardrails_from_agent(agent) | ||
if guardrails: | ||
manifest["guardrails"] = guardrails | ||
|
||
return manifest | ||
|
||
def extract_model_settings_from_agent(agent): | ||
if not hasattr(agent, "model_settings"): | ||
return None | ||
|
||
# convert model_settings to dict if it's not already | ||
model_settings = agent.model_settings | ||
if type(model_settings) != dict: | ||
if hasattr(model_settings, "__dict__"): | ||
model_settings = model_settings.__dict__ | ||
else: | ||
return None | ||
|
||
return make_json_compatible(model_settings) | ||
|
||
def extract_tools_from_agent(agent): | ||
if not hasattr(agent, "tools"): | ||
return None | ||
|
||
tools = [] | ||
for tool in agent.tools: | ||
tool_dict = {} | ||
if isinstance(tool, WebSearchTool): | ||
if hasattr(tool, "user_location"): | ||
tool_dict["user_location"] = tool.user_location | ||
if hasattr(tool, "search_context_size"): | ||
tool_dict["search_context_size"] = tool.search_context_size | ||
elif isinstance(tool, FileSearchTool): | ||
if hasattr(tool, "vector_store_ids"): | ||
tool_dict["vector_store_ids"] = tool.vector_store_ids | ||
if hasattr(tool, "max_num_results"): | ||
tool_dict["max_num_results"] = tool.max_num_results | ||
if hasattr(tool, "include_search_results"): | ||
tool_dict["include_search_results"] = tool.include_search_results | ||
elif isinstance(tool, ComputerTool): | ||
if hasattr(tool, "name"): | ||
tool_dict["name"] = tool.name | ||
else: | ||
if hasattr(tool, "name"): | ||
tool_dict["name"] = tool.name | ||
if hasattr(tool, "description"): | ||
tool_dict["description"] = tool.description | ||
if hasattr(tool, "strict_json_schema"): | ||
tool_dict["strict_json_schema"] = tool.strict_json_schema | ||
if hasattr(tool, "params_json_schema"): | ||
parameter_schema = tool.params_json_schema | ||
required_params = get_required_param_dict(parameter_schema.get("required", [])) | ||
parameters = {} | ||
for param, schema in parameter_schema.get("properties", {}).items(): | ||
param_dict = {} | ||
if "type" in schema: | ||
param_dict["type"] = schema["type"] | ||
if "title" in schema: | ||
param_dict["title"] = schema["title"] | ||
if param in required_params: | ||
param_dict["required"] = True | ||
parameters[param] = param_dict | ||
tool_dict["parameters"] = parameters | ||
Comment on lines
+78
to
+97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PydanticAI has a very similar schema for tool definitions. It may be helpful to pull this out into a shared helper function. |
||
tools.append(tool_dict) | ||
|
||
return tools | ||
|
||
def extract_handoffs_from_agent(agent): | ||
if not hasattr(agent, "handoffs"): | ||
return None | ||
|
||
handoffs = [] | ||
for handoff in agent.handoffs: | ||
handoff_dict = {} | ||
if isinstance(handoff, Agent): | ||
if hasattr(handoff, "handoff_description"): | ||
handoff_dict["handoff_description"] = handoff.handoff_description | ||
if hasattr(handoff, "name"): | ||
handoff_dict["agent_name"] = handoff.name | ||
elif isinstance(handoff, Handoff): | ||
if hasattr(handoff, "tool_name"): | ||
handoff_dict["tool_name"] = handoff.tool_name | ||
if hasattr(handoff, "tool_description"): | ||
handoff_dict["handoff_description"] = handoff.tool_description | ||
if hasattr(handoff, "agent_name"): | ||
handoff_dict["agent_name"] = handoff.agent_name | ||
if handoff_dict: | ||
handoffs.append(handoff_dict) | ||
|
||
return handoffs | ||
|
||
def extract_guardrails_from_agent(agent): | ||
guardrails = [] | ||
if hasattr(agent, "input_guardrails"): | ||
guardrails.extend([getattr(guardrail, "name", "") for guardrail in agent.input_guardrails]) | ||
if hasattr(agent, "output_guardrails"): | ||
guardrails.extend([getattr(guardrail, "name", "") for guardrail in agent.output_guardrails]) | ||
return guardrails | ||
|
||
def get_required_param_dict(required_params): | ||
return {param: True for param in required_params} | ||
|
||
def make_json_compatible(obj): | ||
if isinstance(obj, dict): | ||
return {str(k): make_json_compatible(v) for k, v in obj.items()} | ||
elif isinstance(obj, (list, tuple, set)): | ||
return [make_json_compatible(v) for v in obj] | ||
elif isinstance(obj, (int, float, str, bool)) or obj is None: | ||
return obj | ||
else: | ||
return str(obj) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this method may have been moved onto the
agents.run.AgentRunner
class. We may need to condition this patching based on the library version.