Remove support for structured output

This commit is contained in:
Yiorgis Gozadinos 2026-03-24 14:04:57 +02:00
parent 83ea6347b6
commit aa9fdf051d
No known key found for this signature in database
7 changed files with 11 additions and 69 deletions

View file

@ -11,6 +11,7 @@
- **LLMJudge**: Custom evaluator now accepts `ModelConfig` instead of a model name string - **LLMJudge**: Custom evaluator now accepts `ModelConfig` instead of a model name string
- **Docling upgrade**: docling-core ≥2.70.2 (schema 1.10.0), docling ≥2.81.0. Adds field data model support for structured form/KV content, wide table chunking fixes, and rich table cell hang fix - **Docling upgrade**: docling-core ≥2.70.2 (schema 1.10.0), docling ≥2.81.0. Adds field data model support for structured form/KV content, wide table chunking fixes, and rich table cell hang fix
- **pydantic-ai ≥1.70.0**: Bumped minimum version. Removed `structured_output_type` helper — all supported providers now handle native structured output, so agents pass result types directly
## [0.34.1] - 2026-03-16 ## [0.34.1] - 2026-03-16

View file

@ -13,7 +13,7 @@ from haiku.rag.config import Config
from haiku.rag.config.models import AppConfig, ModelConfig from haiku.rag.config.models import AppConfig, ModelConfig
from haiku.rag.store.models import SearchResult from haiku.rag.store.models import SearchResult
from haiku.rag.tools.search import create_search_toolset from haiku.rag.tools.search import create_search_toolset
from haiku.rag.utils import get_model, structured_output_type from haiku.rag.utils import get_model
@dataclass @dataclass
@ -66,7 +66,7 @@ class QuestionAnswerAgent:
agent: Agent[_QARunDeps, RawSearchAnswer] = Agent( # ty: ignore[invalid-assignment] agent: Agent[_QARunDeps, RawSearchAnswer] = Agent( # ty: ignore[invalid-assignment]
model=model, model=model,
deps_type=_QARunDeps, deps_type=_QARunDeps,
output_type=structured_output_type(RawSearchAnswer, model), output_type=RawSearchAnswer,
instructions=system_prompt, instructions=system_prompt,
toolsets=[search_toolset], toolsets=[search_toolset],
retries=3, retries=3,

View file

@ -19,7 +19,7 @@ from haiku.rag.agents.research.prompts import (
from haiku.rag.agents.research.state import ResearchDeps, ResearchState from haiku.rag.agents.research.state import ResearchDeps, ResearchState
from haiku.rag.config import Config from haiku.rag.config import Config
from haiku.rag.config.models import AppConfig from haiku.rag.config.models import AppConfig
from haiku.rag.utils import build_prompt, get_model, structured_output_type from haiku.rag.utils import build_prompt, get_model
def format_context_for_prompt(context: ResearchContext) -> str: def format_context_for_prompt(context: ResearchContext) -> str:
@ -68,7 +68,7 @@ async def _iterative_plan_logic(
model = get_model(model_config, config) model = get_model(model_config, config)
plan_agent: Agent[ResearchDependencies, IterativePlanResult] = Agent( # type: ignore[assignment] plan_agent: Agent[ResearchDependencies, IterativePlanResult] = Agent( # type: ignore[assignment]
model=model, model=model,
output_type=structured_output_type(IterativePlanResult, model), output_type=IterativePlanResult,
instructions=effective_prompt, instructions=effective_prompt,
retries=3, retries=3,
deps_type=ResearchDependencies, deps_type=ResearchDependencies,
@ -118,7 +118,7 @@ async def _search_one_step_logic(
model = get_model(model_config, config) model = get_model(model_config, config)
agent: Agent[ResearchDependencies, RawSearchAnswer] = Agent( # type: ignore[assignment] agent: Agent[ResearchDependencies, RawSearchAnswer] = Agent( # type: ignore[assignment]
model=model, model=model,
output_type=structured_output_type(RawSearchAnswer, model), output_type=RawSearchAnswer,
instructions=search_prompt, instructions=search_prompt,
retries=3, retries=3,
deps_type=ResearchDependencies, deps_type=ResearchDependencies,
@ -220,7 +220,7 @@ def build_research_graph(
model = get_model(model_config, config) model = get_model(model_config, config)
agent: Agent[ResearchDependencies, ResearchReport] = Agent( # type: ignore[assignment] agent: Agent[ResearchDependencies, ResearchReport] = Agent( # type: ignore[assignment]
model=model, model=model,
output_type=structured_output_type(ResearchReport, model), output_type=ResearchReport,
instructions=synthesis_prompt, instructions=synthesis_prompt,
retries=3, retries=3,
deps_type=ResearchDependencies, deps_type=ResearchDependencies,

View file

@ -4,7 +4,7 @@ from haiku.rag.agents.rlm.dependencies import RLMDeps
from haiku.rag.agents.rlm.models import CodeExecution, RLMResult from haiku.rag.agents.rlm.models import CodeExecution, RLMResult
from haiku.rag.agents.rlm.prompts import RLM_SYSTEM_PROMPT from haiku.rag.agents.rlm.prompts import RLM_SYSTEM_PROMPT
from haiku.rag.config.models import AppConfig from haiku.rag.config.models import AppConfig
from haiku.rag.utils import get_model, structured_output_type from haiku.rag.utils import get_model
def create_rlm_agent(config: AppConfig) -> Agent[RLMDeps, RLMResult]: def create_rlm_agent(config: AppConfig) -> Agent[RLMDeps, RLMResult]:
@ -25,7 +25,7 @@ def create_rlm_agent(config: AppConfig) -> Agent[RLMDeps, RLMResult]:
agent: Agent[RLMDeps, RLMResult] = Agent( # type: ignore[invalid-assignment] agent: Agent[RLMDeps, RLMResult] = Agent( # type: ignore[invalid-assignment]
model, model,
deps_type=RLMDeps, deps_type=RLMDeps,
output_type=structured_output_type(RLMResult, model), output_type=RLMResult,
instructions=RLM_SYSTEM_PROMPT, instructions=RLM_SYSTEM_PROMPT,
retries=3, retries=3,
) )

View file

@ -318,20 +318,6 @@ def get_model(
return f"{provider}:{model}" return f"{provider}:{model}"
def structured_output_type(
result_type: type,
model: Any,
max_retries: int = 3,
) -> Any:
"""Return a NativeOutput or ToolOutput wrapper based on model capability."""
from pydantic_ai.models import Model
from pydantic_ai.output import NativeOutput, ToolOutput
if isinstance(model, Model) and model.profile.supports_json_schema_output:
return NativeOutput(result_type)
return ToolOutput(result_type, max_retries=max_retries)
def format_bytes(num_bytes: int) -> str: def format_bytes(num_bytes: int) -> str:
"""Format bytes as human-readable string.""" """Format bytes as human-readable string."""
size = float(num_bytes) size = float(num_bytes)

View file

@ -15,24 +15,11 @@ def vcr_cassette_dir():
class TestCreateRLMAgent: class TestCreateRLMAgent:
def test_creates_agent_native_output_when_supported(self): def test_creates_agent(self):
from pydantic_ai.output import NativeOutput
agent = create_rlm_agent(Config) agent = create_rlm_agent(Config)
assert isinstance(agent, Agent) assert isinstance(agent, Agent)
assert agent.deps_type is RLMDeps assert agent.deps_type is RLMDeps
assert isinstance(agent.output_type, NativeOutput) assert agent.output_type is RLMResult
assert agent.output_type.outputs is RLMResult
def test_creates_agent_native_output_for_ollama(self):
from pydantic_ai.output import NativeOutput
config = AppConfig()
config.rlm.model.name = "qwen3"
agent = create_rlm_agent(config)
assert isinstance(agent, Agent)
assert isinstance(agent.output_type, NativeOutput)
assert agent.output_type.outputs is RLMResult
def test_agent_has_execute_code_tool(self): def test_agent_has_execute_code_tool(self):
agent = create_rlm_agent(Config) agent = create_rlm_agent(Config)

View file

@ -139,38 +139,6 @@ Emoji test: 🚀 ✅ 📝"""
assert "🚀" in result_markdown assert "🚀" in result_markdown
def test_structured_output_type_native():
from pydantic_ai.output import NativeOutput
from haiku.rag.utils import structured_output_type
model = get_model(ModelConfig(provider="openai", name="gpt-4o"))
result = structured_output_type(str, model)
assert isinstance(result, NativeOutput)
assert result.outputs is str
def test_structured_output_type_ollama_native():
from pydantic_ai.output import NativeOutput
from haiku.rag.utils import structured_output_type
model = get_model(ModelConfig(provider="ollama", name="qwen3"))
result = structured_output_type(str, model)
assert isinstance(result, NativeOutput)
assert result.outputs is str
def test_structured_output_type_string_model():
from pydantic_ai.output import ToolOutput
from haiku.rag.utils import structured_output_type
result = structured_output_type(str, "unknown:model")
assert isinstance(result, ToolOutput)
assert result.output is str
def test_get_model_ollama(): def test_get_model_ollama():
"""Test get_model returns OpenAIChatModel for Ollama.""" """Test get_model returns OpenAIChatModel for Ollama."""
model_config = ModelConfig(provider="ollama", name="llama3") model_config = ModelConfig(provider="ollama", name="llama3")