Skip to content

Commit 9c5646b

Browse files
committed
Rehydrate remote prompt parameters
1 parent 3462f3f commit 9c5646b

2 files changed

Lines changed: 80 additions & 3 deletions

File tree

py/src/braintrust/parameters.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,28 @@ def validate_json_schema(parameters: dict[str, Any], schema: ParametersSchema) -
246246
return candidate
247247

248248

249+
def _rehydrate_remote_parameters(
250+
parameters: dict[str, Any],
251+
schema: ParametersSchema,
252+
) -> ValidatedParameters:
253+
properties = schema.get("properties")
254+
if not isinstance(properties, dict):
255+
return parameters
256+
257+
result: ValidatedParameters = dict(parameters)
258+
for name, property_schema in properties.items():
259+
if not isinstance(property_schema, dict) or name not in result:
260+
continue
261+
262+
if property_schema.get("x-bt-type") == "prompt":
263+
prompt_data = result[name]
264+
if not isinstance(prompt_data, dict):
265+
raise ValueError(f"Invalid parameter '{name}': prompt value must be an object")
266+
result[name] = _create_prompt(name, prompt_data)
267+
268+
return result
269+
270+
249271
def _validate_local_parameters(
250272
parameters: dict[str, Any],
251273
parameter_schema: EvalParameters,
@@ -334,7 +356,8 @@ def validate_parameters(
334356
if isinstance(parameter_schema, RemoteEvalParameters):
335357
merged = dict(parameter_schema.data)
336358
merged.update(parameters)
337-
return validate_json_schema(merged, parameter_schema.schema)
359+
validated = validate_json_schema(merged, parameter_schema.schema)
360+
return _rehydrate_remote_parameters(validated, parameter_schema.schema)
338361

339362
return _validate_local_parameters(parameters, parameter_schema)
340363

py/src/braintrust/test_parameters.py

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def test_validate_remote_parameters_merges_saved_data_and_runtime_overrides():
9090
}
9191

9292

93-
def test_validate_remote_parameters_keeps_prompt_values_as_dicts():
93+
def test_validate_remote_parameters_rehydrates_prompt_values():
9494
parameters = RemoteEvalParameters(
9595
id="params-123",
9696
project_id="project-123",
@@ -122,7 +122,61 @@ def test_validate_remote_parameters_keeps_prompt_values_as_dicts():
122122

123123
result = validate_parameters({}, parameters)
124124

125-
assert isinstance(result["main"], dict)
125+
assert hasattr(result["main"], "build")
126+
built = result["main"].build(input="test input")
127+
assert built["messages"] == [{"role": "user", "content": "test input"}]
128+
assert built["model"] == "gpt-5-mini"
129+
130+
131+
def test_validate_remote_parameters_allows_prompt_overrides():
132+
parameters = RemoteEvalParameters(
133+
id="params-123",
134+
project_id="project-123",
135+
name="Saved parameters",
136+
slug="saved-parameters",
137+
version="v1",
138+
schema={
139+
"type": "object",
140+
"properties": {
141+
"main": {
142+
"type": "object",
143+
"x-bt-type": "prompt",
144+
},
145+
},
146+
"additionalProperties": True,
147+
},
148+
data={
149+
"main": {
150+
"prompt": {
151+
"type": "chat",
152+
"messages": [{"role": "user", "content": "{{input}}"}],
153+
},
154+
"options": {
155+
"model": "gpt-5-mini",
156+
},
157+
},
158+
},
159+
)
160+
161+
result = validate_parameters(
162+
{
163+
"main": {
164+
"prompt": {
165+
"type": "chat",
166+
"messages": [{"role": "user", "content": "override {{input}}"}],
167+
},
168+
"options": {
169+
"model": "gpt-5-nano",
170+
},
171+
}
172+
},
173+
parameters,
174+
)
175+
176+
assert hasattr(result["main"], "build")
177+
built = result["main"].build(input="test input")
178+
assert built["messages"] == [{"role": "user", "content": "override test input"}]
179+
assert built["model"] == "gpt-5-nano"
126180

127181

128182
@pytest.mark.skipif(not HAS_PYDANTIC, reason="pydantic not installed")

0 commit comments

Comments
 (0)