1
1
from __future__ import annotations
2
2
3
+ from collections .abc import Sequence
3
4
from textwrap import dedent
4
5
from typing import Any
5
6
6
7
from pydantic import BaseModel , Field
7
8
from pydantic_core import to_json
8
9
9
10
from pydantic_ai import Agent , models
11
+ from pydantic_ai .messages import MultiModalContentTypes , UserContent
10
12
from pydantic_ai .settings import ModelSettings
11
13
12
14
__all__ = (
@@ -62,16 +64,7 @@ async def judge_output(
62
64
If the model is not specified, a default model is used. The default model starts as 'openai:gpt-4o',
63
65
but this can be changed using the `set_default_judge_model` function.
64
66
"""
65
- user_prompt = dedent (
66
- f"""
67
- <Output>
68
- { _stringify (output )}
69
- </Output>
70
- <Rubric>
71
- { rubric }
72
- </Rubric>
73
- """
74
- )
67
+ user_prompt = _build_prompt (output = output , rubric = rubric )
75
68
return (
76
69
await _judge_output_agent .run (user_prompt , model = model or _default_model , model_settings = model_settings )
77
70
).output
@@ -112,19 +105,8 @@ async def judge_input_output(
112
105
If the model is not specified, a default model is used. The default model starts as 'openai:gpt-4o',
113
106
but this can be changed using the `set_default_judge_model` function.
114
107
"""
115
- user_prompt = dedent (
116
- f"""
117
- <Input>
118
- { _stringify (inputs )}
119
- </Input>
120
- <Output>
121
- { _stringify (output )}
122
- </Output>
123
- <Rubric>
124
- { rubric }
125
- </Rubric>
126
- """
127
- )
108
+ user_prompt = _build_prompt (inputs = inputs , output = output , rubric = rubric )
109
+
128
110
return (
129
111
await _judge_input_output_agent .run (user_prompt , model = model or _default_model , model_settings = model_settings )
130
112
).output
@@ -168,22 +150,7 @@ async def judge_input_output_expected(
168
150
If the model is not specified, a default model is used. The default model starts as 'openai:gpt-4o',
169
151
but this can be changed using the `set_default_judge_model` function.
170
152
"""
171
- user_prompt = dedent (
172
- f"""
173
- <Input>
174
- { _stringify (inputs )}
175
- </Input>
176
- <ExpectedOutput>
177
- { _stringify (expected_output )}
178
- </ExpectedOutput>
179
- <Output>
180
- { _stringify (output )}
181
- </Output>
182
- <Rubric>
183
- { rubric }
184
- </Rubric>
185
- """
186
- )
153
+ user_prompt = _build_prompt (inputs = inputs , output = output , rubric = rubric , expected_output = expected_output )
187
154
188
155
return (
189
156
await _judge_input_output_expected_agent .run (
@@ -227,19 +194,7 @@ async def judge_output_expected(
227
194
If the model is not specified, a default model is used. The default model starts as 'openai:gpt-4o',
228
195
but this can be changed using the `set_default_judge_model` function.
229
196
"""
230
- user_prompt = dedent (
231
- f"""
232
- <ExpectedOutput>
233
- { _stringify (expected_output )}
234
- </ExpectedOutput>
235
- <Output>
236
- { _stringify (output )}
237
- </Output>
238
- <Rubric>
239
- { rubric }
240
- </Rubric>
241
- """
242
- )
197
+ user_prompt = _build_prompt (output = output , rubric = rubric , expected_output = expected_output )
243
198
return (
244
199
await _judge_output_expected_agent .run (
245
200
user_prompt , model = model or _default_model , model_settings = model_settings
@@ -265,3 +220,41 @@ def _stringify(value: Any) -> str:
265
220
return to_json (value ).decode ()
266
221
except Exception :
267
222
return repr (value )
223
+
224
+
225
+ def _build_prompt (
226
+ output : Any ,
227
+ rubric : str ,
228
+ inputs : Any | None = None ,
229
+ expected_output : Any | None = None ,
230
+ ) -> str | Sequence [str | UserContent ]:
231
+ """Build a prompt that includes input, output, and rubric."""
232
+ sections : list [str | UserContent ] = []
233
+
234
+ if inputs is not None :
235
+ if isinstance (inputs , str ):
236
+ sections .append (f'<Input>\n { inputs } \n </Input>' )
237
+ else :
238
+ sections .append ('<Input>\n ' )
239
+ if isinstance (inputs , Sequence ):
240
+ for item in inputs : # type: ignore
241
+ if isinstance (item , (str , MultiModalContentTypes )):
242
+ sections .append (item )
243
+ else :
244
+ sections .append (_stringify (item ))
245
+ elif isinstance (inputs , MultiModalContentTypes ):
246
+ sections .append (inputs )
247
+ else :
248
+ sections .append (_stringify (inputs ))
249
+ sections .append ('</Input>' )
250
+
251
+ sections .append (f'<Output>\n { _stringify (output )} \n </Output>' )
252
+ sections .append (f'<Rubric>\n { rubric } \n </Rubric>' )
253
+
254
+ if expected_output is not None :
255
+ sections .append (f'<ExpectedOutput>\n { _stringify (expected_output )} \n </ExpectedOutput>' )
256
+
257
+ if inputs is None or isinstance (inputs , str ):
258
+ return '\n \n ' .join (sections ) # type: ignore[arg-type]
259
+ else :
260
+ return sections
0 commit comments