fix(agno): route tools to metadata, materialize media attachments, propagate exception objects#582
Merged
Conversation
Align the Agno integration with `.agents/skills/sdk-integrations/SKILL.md`. - Pass `Exception` instances to `span.log(error=...)` instead of `str(e)`, matching the anthropic pattern and giving Braintrust a chance to extract structured error info. - Route `tools`, `tool_choice`, `functions`, and `tool_call_limit` into `metadata` rather than `input` via a new `_split_model_call()` helper. Fixes the pre-existing `tool_chocie` typo by construction. - Guard `FunctionCall._build_entrypoint_args()` and attribute reads on `instance.function.*` so extraction failures no longer escape into the user's tool exec path. - Materialize inline `Image` / `Audio` / `Video` / `File` bytes as Braintrust `Attachment`s using `_materialize_attachment` from `integrations/utils.py`. Applied to model input `messages`, non-streaming model output, and both streaming aggregators. Images land under `image_url.url`, other media under `file.file_data` with `filename` preserved, remote `url`s pass through verbatim. - Fix an off-by-check bug in the existing `get_args_kwargs` helper (would IndexError when kwargs-only, then removed since it's now unused). - Extend `test_agno_simple_agent_execution` with a negative assertion that tool-related keys never leak into `input`. - Add `test_agno_agent_tools_metadata_placement` (tools) and `test_agno_agent_image_input_materializes_attachment` (vision) as cassette-backed positive tests. Both call `_skip_if_cassette_missing()` so CI stays green until the cassettes are recorded. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`_prepare_model_input` and `_prepare_model_output` used to call `_try_to_dict()` on every model turn — a wasted `.model_dump()` on non-media calls, since Braintrust's log-time serializer already handles dataclasses and Pydantic models. Now they check for inline `content` bytes or `filepath` first and pass the raw SDK object through when no materialization is needed. Only calls that actually carry binary media pay for the conversion. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Abhijeet Prasad (AbhiPrasad)
approved these changes
Jul 16, 2026
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
Align the Agno integration with
.agents/skills/sdk-integrations/SKILL.md. Audit turned up six issues; this PR fixes five (a metric-key concern was retracted as widely-used).span.log(error=e)instead ofspan.log(error=str(e))across all 15 wrappers, matching the anthropic pattern.tools,tool_choice,functions,tool_call_limitroute intometadatavia a new_split_model_call()helper (fixes the pre-existingtool_chocietypo by construction).FunctionCall._build_entrypoint_args()andinstance.function.*reads are now guarded so extraction failures don't escape into the user's tool exec.Image/Audio/Video/Filebytes get materialized to BraintrustAttachments (via_materialize_attachmentinintegrations/utils.py), applied to inputmessages, non-streaming model output, and both streaming aggregators. Images useimage_url.url; other media usefile.file_data; remoteurls pass through verbatim.get_args_kwargs(superseded by_split_model_call).Test plan
cd py && CI=1 BRAINTRUST_TEST_PACKAGE_VERSION=latest OPENAI_API_KEY=sk-test python -m pytest src/braintrust/integrations/agno/— 34 passed, 3 skipped (2 new cassettes pending, 1 pre-existing importorskip).test_agno_simple_agent_execution(existing cassette) with a negative assertion that tool fields never appear ininput._skip_if_cassette_missing()keeps CI green in the meantime:nox -s "test_agno(latest)" -- --vcr-record=all \ -k "test_agno_agent_tools_metadata_placement or test_agno_agent_image_input_materializes_attachment"cd py && make lintNotes
Model.response(tools=...)is{name, description, parameters, ...}— not yet wrapped in the OpenAI{"type": "function", "function": {...}}envelope. Placement is now correct (metadata, not input); full OpenAI-shape normalization is a follow-up.RunOutput) can also carry media — worth revisiting once the model-side fix has some soak time.🤖 Generated with Claude Code
Created by abhijeet
Slack thread