Forward instruction schemas to llm-connect

This commit is contained in:
2026-05-21 03:19:27 +02:00
parent 5c4f96e7aa
commit cf92f0d686
3 changed files with 81 additions and 8 deletions

View File

@@ -180,7 +180,10 @@ def _llm_run_config(instr: Any) -> dict[str, Any]:
value = getattr(instr, field, None) value = getattr(instr, field, None)
if value is not None: if value is not None:
config[field] = value config[field] = value
model_params = getattr(instr, "model_params", None) model_params = dict(getattr(instr, "model_params", None) or {})
schema = _load_output_schema(getattr(instr, "output_schema", ""))
if schema is not None:
model_params.setdefault("json_schema", schema)
if model_params: if model_params:
config["model_params"] = model_params config["model_params"] = model_params
return config return config
@@ -263,16 +266,33 @@ def _validate_against_schema(data: Any, schema_path: str) -> str | None:
if not schema_path: if not schema_path:
return None return None
try:
schema = _load_output_schema(schema_path)
except (OSError, json.JSONDecodeError, TypeError) as exc:
return f"could not read output schema: {exc}"
if schema is None:
return None
return _validate_schema_node(data, schema, "$")
def _load_output_schema(schema_path: str) -> dict[str, Any] | None:
"""Load a JSON schema file when present.
Missing schema files are intentionally tolerated for backward
compatibility with existing tests and definitions.
"""
if not schema_path:
return None
path = Path(schema_path) path = Path(schema_path)
if not path.exists(): if not path.exists():
return None return None
try: schema = json.loads(path.read_text(encoding="utf-8"))
schema = json.loads(path.read_text(encoding="utf-8")) if not isinstance(schema, dict):
except (OSError, json.JSONDecodeError) as exc: raise TypeError("output schema must be a JSON object")
return f"could not read output schema: {exc}" return schema
return _validate_schema_node(data, schema, "$")
def _validate_schema_node(data: Any, schema: dict[str, Any], path: str) -> str | None: def _validate_schema_node(data: Any, schema: dict[str, Any], path: str) -> str | None:

View File

@@ -275,6 +275,46 @@ def test_execute_instruction_forwards_llm_connect_run_config():
] ]
def test_execute_instruction_forwards_output_schema_to_llm_connect(tmp_path, monkeypatch):
schema_dir = tmp_path / "schemas"
schema_dir.mkdir()
schema_path = schema_dir / "daily-triage-report.json"
schema = {
"type": "object",
"required": ["summary", "recommendations"],
"properties": {
"summary": {"type": "string"},
"recommendations": {"type": "array", "items": {"type": "object"}},
},
}
schema_path.write_text(json.dumps(schema), encoding="utf-8")
monkeypatch.chdir(tmp_path)
llm = _CountingLLM([
json.dumps({"summary": "Review open work.", "recommendations": []})
])
instr = _instr(
id="daily-triage-report",
prompt="Report.",
trusted_fields=[],
output_schema="schemas/daily-triage-report.json",
model_params={"reasoning_effort": "medium"},
)
result = execute_instruction_with_audit(instr, _Event(), {}, llm)
assert result.output_validated is True
assert llm.calls == [
{
"model_name": "claude-sonnet-4-6",
"model_params": {
"reasoning_effort": "medium",
"json_schema": schema,
},
}
]
def test_execute_instruction_with_audit_accepts_report_payload(): def test_execute_instruction_with_audit_accepts_report_payload():
report_data = { report_data = {
"summary": "State Hub has loose ends.", "summary": "State Hub has loose ends.",

View File

@@ -157,5 +157,18 @@ async def test_evaluate_instructions_forwards_llm_connect_depth_config(monkeypat
"temperature": 0.2, "temperature": 0.2,
"max_tokens": 1200, "max_tokens": 1200,
"max_depth": 2, "max_depth": 2,
"model_params": {"reasoning_effort": "medium"}, "model_params": {
"reasoning_effort": "medium",
"json_schema": {
"type": "object",
"required": ["summary", "recommendations"],
"properties": {
"summary": {"type": "string"},
"recommendations": {
"type": "array",
"items": {"type": "object"},
},
},
},
},
} }