Skip to content

Commit 944998d

Browse files
author
Dylan Huang
committed
save
1 parent f17169e commit 944998d

7 files changed

Lines changed: 64 additions & 53 deletions

File tree

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
"python.testing.autoTestDiscoverOnSaveEnabled": true,
66
"python.defaultInterpreterPath": "./.venv/bin/python",
77
"python.testing.cwd": "${workspaceFolder}",
8-
"editor.defaultFormatter": "ms-python.black-formatter"
8+
"editor.defaultFormatter": "ms-python.black-formatter",
9+
"cursorpyright.analysis.typeCheckingMode": "recommended"
910
}

eval_protocol/pytest/default_pydantic_ai_rollout_processor.py

Lines changed: 28 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
UserPromptPart,
2626
)
2727
from pydantic_ai.providers.openai import OpenAIProvider
28-
from typing_extensions import Callable
28+
from typing import Callable, Union
2929

3030
logger = logging.getLogger(__name__)
3131

@@ -34,7 +34,7 @@ class PydanticAgentRolloutProcessor(RolloutProcessor):
3434
"""Rollout processor for Pydantic AI agents. Mainly converts
3535
EvaluationRow.messages to and from Pydantic AI ModelMessage format."""
3636

37-
def __init__(self, setup_agent: Callable[..., Agent], usage_limits: UsageLimits = None):
37+
def __init__(self, setup_agent: Union[Callable[..., Agent], Agent], usage_limits: UsageLimits = None):
3838
# dummy model used for its helper functions for processing messages
3939
self.util = OpenAIModel("dummy-model", provider=OpenAIProvider(api_key="dummy"))
4040
self.setup_agent = setup_agent
@@ -58,63 +58,47 @@ def _map_litellm_to_pydantic_ai(
5858
"azure_ai": "azure",
5959
}
6060
provider = model_name.split("/")[0]
61+
model_name = model_name.removeprefix(f"{provider}/")
6162
if provider in mapping:
6263
provider = mapping[provider]
63-
return provider # type: ignore
64+
return provider, model_name
65+
66+
def _map_litellm_to_pydantic_ai_model(self, model_name: str) -> Union[OpenAIModel, GoogleModel, AnthropicModel]:
67+
if model_name.startswith("anthropic/"):
68+
return AnthropicModel(
69+
model_name.removeprefix("anthropic/"),
70+
)
71+
elif model_name.startswith("google/"):
72+
return GoogleModel(
73+
model_name.removeprefix("google/"),
74+
)
75+
elif model_name.startswith("gemini/"):
76+
return GoogleModel(
77+
model_name.removeprefix("gemini/"),
78+
)
79+
provider, model_name = self._map_litellm_to_pydantic_ai(model_name)
80+
return OpenAIModel(
81+
model_name,
82+
provider=provider,
83+
)
6484

6585
def __call__(self, rows: List[EvaluationRow], config: RolloutProcessorConfig) -> List[asyncio.Task[EvaluationRow]]:
6686
"""Create agent rollout tasks and return them for external handling."""
6787

6888
max_concurrent = getattr(config, "max_concurrent_rollouts", 8) or 8
6989
semaphore = asyncio.Semaphore(max_concurrent)
7090

71-
# validate that the "agent" field is present with a valid Pydantic AI Agent instance in the completion_params dict
72-
if "agent" not in config.kwargs:
73-
raise ValueError("kwargs must contain an 'agent' field with a valid Pydantic AI Agent instance")
74-
if not isinstance(config.kwargs["agent"], Agent) and not isinstance(
75-
config.kwargs["agent"], types.FunctionType
76-
):
77-
raise ValueError(
78-
"kwargs['agent'] must be a valid Pydantic AI Agent instance or a function that returns an Agent"
79-
)
80-
81-
if isinstance(config.kwargs["agent"], types.FunctionType):
82-
setup_agent = config.kwargs["agent"]
83-
if not isinstance(config.completion_params["model"], dict):
84-
raise ValueError(
85-
"completion_params['model'] must be a dict mapping agent argument names to model config dicts (with 'model' and 'provider' keys)"
86-
)
91+
if isinstance(self.setup_agent, types.FunctionType):
8792
kwargs: dict[str, OpenAIModel | GoogleModel | AnthropicModel] = {}
8893
for agent, model_config in config.completion_params["model"].items():
8994
if "model" not in model_config:
9095
raise ValueError(f"model_config for agent {agent} must contain a 'model' key")
91-
model_name = model_config["model"]
92-
if model_name.startswith("anthropic/"):
93-
kwargs[agent] = AnthropicModel(
94-
model_name.removeprefix("anthropic/"),
95-
)
96-
elif model_name.startswith("google/"):
97-
kwargs[agent] = GoogleModel(
98-
model_name.removeprefix("google/"),
99-
)
100-
elif model_name.startswith("gemini/"):
101-
kwargs[agent] = GoogleModel(
102-
model_name.removeprefix("gemini/"),
103-
)
104-
else:
105-
provider = self._map_litellm_to_pydantic_ai(model_name)
106-
kwargs[agent] = OpenAIModel(
107-
model_name.removeprefix(f"{provider}/"),
108-
provider=provider,
109-
)
110-
agent = setup_agent(**kwargs)
96+
kwargs[agent] = self._map_litellm_to_pydantic_ai_model(model_config["model"])
97+
agent = self.setup_agent(**kwargs)
11198
model = None
11299
else:
113-
agent = config.kwargs["agent"]
114-
model = OpenAIModel(
115-
config.completion_params["model"],
116-
provider=config.completion_params["provider"],
117-
)
100+
agent = self.setup_agent
101+
model = self._map_litellm_to_pydantic_ai_model(config.completion_params["model"])
118102

119103
async def process_row(row: EvaluationRow) -> EvaluationRow:
120104
"""Process a single row with agent rollout."""

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ tau2 = { git = "https://github.com/sierra-research/tau2-bench.git" }
174174

175175
[dependency-groups]
176176
dev = [
177+
"basedpyright>=1.31.3",
177178
"fastapi[standard]>=0.116.1",
178179
"fastmcp>=2.10.6",
179180
"haikus==0.3.8",

tests/chinook/test_pydantic_chinook.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@
3232
}
3333
},
3434
],
35-
rollout_processor=PydanticAgentRolloutProcessor(),
36-
rollout_processor_kwargs={"agent": setup_agent},
35+
rollout_processor=PydanticAgentRolloutProcessor(setup_agent),
3736
mode="pointwise",
3837
)
3938
async def test_simple_query(row: EvaluationRow) -> EvaluationRow:
@@ -96,8 +95,7 @@ class Response(BaseModel):
9695
}
9796
},
9897
],
99-
rollout_processor=PydanticAgentRolloutProcessor(),
100-
rollout_processor_kwargs={"agent": setup_agent},
98+
rollout_processor=PydanticAgentRolloutProcessor(setup_agent),
10199
mode="pointwise",
102100
)
103101
async def test_complex_queries(row: EvaluationRow) -> EvaluationRow:

tests/pytest/test_pydantic_agent.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,9 @@
1414
@evaluation_test(
1515
input_messages=[Message(role="user", content="Hello, how are you?")],
1616
completion_params=[
17-
{"model": "accounts/fireworks/models/gpt-oss-120b", "provider": "fireworks"},
17+
{"model": "fireworks_ai/accounts/fireworks/models/gpt-oss-120b"},
1818
],
19-
rollout_processor=PydanticAgentRolloutProcessor(),
20-
rollout_processor_kwargs={"agent": agent},
19+
rollout_processor=PydanticAgentRolloutProcessor(agent),
2120
mode="pointwise",
2221
)
2322
async def test_pydantic_agent(row: EvaluationRow) -> EvaluationRow:

tests/pytest/test_pydantic_multi_agent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ async def joke_factory(ctx: RunContext[None], count: int) -> list[str]:
5959
},
6060
},
6161
],
62-
rollout_processor=PydanticAgentRolloutProcessor.__init__(
62+
rollout_processor=PydanticAgentRolloutProcessor(
6363
setup_agent, UsageLimits(request_limit=5, total_tokens_limit=1000)
6464
),
6565
mode="pointwise",

uv.lock

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)