Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/agno/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Each script calls `braintrust.auto_instrument()` before importing `agno`, so all
| `async_simple_agent_stream.py` | one agent, async + streamed |
| `team_agent.py` | research + advisor team, sync |
| `async_team_agent.py` | research + advisor team, async + streamed |
| `accuracy_eval.py` | `AccuracyEval` over one agent, scored in Braintrust |
| `eval_suite.py` | an `agno.eval` suite; each `Case` becomes an experiment row |

## Run

Expand Down
33 changes: 33 additions & 0 deletions examples/agno/accuracy_eval.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import braintrust


braintrust.auto_instrument()

# An eval run logs to whatever is current. Swap init_logger for
# braintrust.init(project=..., experiment=...) to score it as an experiment row instead.
braintrust.init_logger(project="agno-evals-project")

from agno.agent import Agent
from agno.eval.accuracy import AccuracyEval
from agno.models.openai import OpenAIChat
from agno.tools.yfinance import YFinanceTools


agent = Agent(
name="Stock Price Agent",
model=OpenAIChat(id="gpt-4o-mini"),
tools=[YFinanceTools()],
instructions="You are a stock price agent. Answer with the ticker and nothing else.",
)

evaluation = AccuracyEval(
name="Ticker Lookup",
model=OpenAIChat(id="gpt-4o-mini"),
agent=agent,
input="Which ticker does Figma trade under?",
expected_output="FIG",
num_iterations=2,
)

result = evaluation.run(print_summary=True)
print(f"average score: {result.avg_score}/10")
51 changes: 51 additions & 0 deletions examples/agno/eval_suite.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# agno.eval lazy-imports its submodules through a module-level __getattr__, which
# static analysis cannot see.
# pylint: disable=no-name-in-module

import sys

import braintrust


braintrust.auto_instrument()

# A suite run opens a Braintrust experiment of its own, so each Case lands as a
# scored experiment row. Pass eval_experiments=False to setup_agno() (or set
# BRAINTRUST_AGNO_EVAL_EXPERIMENTS=false) to keep suite runs in logs instead.
braintrust.init_logger(project="agno-evals-project")

from agno.agent import Agent
from agno.eval import Case, cli
from agno.models.openai import OpenAIChat
from agno.tools.yfinance import YFinanceTools


agent = Agent(
id="stock-agent",
name="Stock Price Agent",
model=OpenAIChat(id="gpt-4o-mini"),
tools=[YFinanceTools()],
instructions="Use your tools for any market data question.",
)

CASES = (
Case(
name="looks_up_current_price",
agent=agent,
input="What is the current price of FIG?",
tags=("smoke",),
criteria="Reports a current share price for Figma.",
expected_tool_calls=("get_current_stock_price",),
),
Case(
name="explains_pe_ratio",
agent=agent,
input="Explain the P/E ratio in one sentence.",
criteria="Explains that the P/E ratio compares share price to earnings per share.",
),
)

if __name__ == "__main__":
# python eval_suite.py --tag smoke # run a tagged subset
# python eval_suite.py --list # list cases without running them
sys.exit(cli(CASES))
31 changes: 26 additions & 5 deletions py/src/braintrust/integrations/agno/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@

import logging

from braintrust.logger import NOOP_SPAN, current_span, init_logger
from braintrust.logger import NOOP_SPAN, current_experiment, current_span, init_logger

from .eval_experiments import configure as _configure_eval_experiments
from .integration import AgnoIntegration
from .patchers import (
wrap_accuracy_eval,
wrap_agent,
wrap_agent_as_judge_eval,
wrap_eval_suite,
wrap_function_call,
wrap_model,
wrap_performance_eval,
wrap_reliability_eval,
wrap_team,
wrap_workflow,
)
Expand All @@ -19,9 +25,14 @@
__all__ = [
"AgnoIntegration",
"setup_agno",
"wrap_accuracy_eval",
"wrap_agent",
"wrap_agent_as_judge_eval",
"wrap_eval_suite",
"wrap_function_call",
"wrap_model",
"wrap_performance_eval",
"wrap_reliability_eval",
"wrap_team",
"wrap_workflow",
]
Expand All @@ -31,20 +42,30 @@ def setup_agno(
api_key: str | None = None,
project_id: str | None = None,
project_name: str | None = None,
eval_experiments: bool | None = None,
) -> bool:
"""
Setup Braintrust integration with Agno. Will automatically patch Agno agents, models, and function calls for tracing.
Setup Braintrust integration with Agno. Will automatically patch Agno agents, models,
function calls, and evals (``agno.eval``) for tracing.

Args:
api_key: Braintrust API key (optional, can use env var BRAINTRUST_API_KEY)
project_id: Braintrust project ID (optional)
project_name: Braintrust project name (optional, can use env var BRAINTRUST_PROJECT)
project_name: Braintrust project name (optional; defaults to the Global project)
eval_experiments: Whether an eval suite run should open a Braintrust experiment,
so its cases land as experiment rows rather than logs. Defaults to the
BRAINTRUST_AGNO_EVAL_EXPERIMENTS env var, which itself defaults to true.
Individual evals (AccuracyEval and friends) always log to whatever is
current, so pass eval_experiments=False to keep suite runs in logs too.

Returns:
True if setup was successful, False otherwise
"""
span = current_span()
if span == NOOP_SPAN:
_configure_eval_experiments(eval_experiments)

# An experiment opened by the caller is the destination for eval rows, so don't
# install a logger that would only shadow it for non-eval tracing.
if current_span() == NOOP_SPAN and current_experiment() is None:
init_logger(project=project_name, api_key=api_key, project_id=project_id)

return AgnoIntegration.setup()
Loading