mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-02 21:58:11 +00:00
fix: handle BaseModel input in convert_to_model
This commit is contained in:
@@ -1110,7 +1110,7 @@ Follow these guidelines:
|
||||
)
|
||||
|
||||
def _export_output(
|
||||
self, result: str
|
||||
self, result: str | BaseModel
|
||||
) -> tuple[BaseModel | None, dict[str, Any] | None]:
|
||||
pydantic_output: BaseModel | None = None
|
||||
json_output: dict[str, Any] | None = None
|
||||
|
||||
@@ -153,16 +153,18 @@ class Converter(OutputConverter):
|
||||
|
||||
|
||||
def convert_to_model(
|
||||
result: str,
|
||||
result: str | BaseModel,
|
||||
output_pydantic: type[BaseModel] | None,
|
||||
output_json: type[BaseModel] | None,
|
||||
agent: Agent | BaseAgent | None = None,
|
||||
converter_cls: type[Converter] | None = None,
|
||||
) -> dict[str, Any] | BaseModel | str:
|
||||
"""Convert a result string to a Pydantic model or JSON.
|
||||
"""Convert a result to a Pydantic model or JSON.
|
||||
|
||||
Args:
|
||||
result: The result string to convert.
|
||||
result: The result to convert. Usually a JSON string, but a Pydantic
|
||||
instance is also accepted when an upstream caller already produced
|
||||
a structured object.
|
||||
output_pydantic: The Pydantic model class to convert to.
|
||||
output_json: The Pydantic model class to convert to JSON.
|
||||
agent: The agent instance.
|
||||
@@ -175,6 +177,11 @@ def convert_to_model(
|
||||
if model is None:
|
||||
return result
|
||||
|
||||
if isinstance(result, BaseModel):
|
||||
if isinstance(result, model):
|
||||
return result.model_dump() if output_json else result
|
||||
result = result.model_dump_json()
|
||||
|
||||
if converter_cls:
|
||||
return convert_with_instructions(
|
||||
result=result,
|
||||
|
||||
@@ -690,6 +690,27 @@ def test_multiple_guardrails_with_pydantic_output():
|
||||
assert parsed["processed"] is True
|
||||
|
||||
|
||||
def test_export_output_accepts_pydantic_input():
|
||||
"""Regression test for #5458: _export_output must not crash with TypeError
|
||||
when called with a Pydantic instance (e.g. when an upstream caller passes
|
||||
an already-converted model from a context task)."""
|
||||
from pydantic import BaseModel
|
||||
|
||||
class StructuredResult(BaseModel):
|
||||
value: str
|
||||
|
||||
task = create_smart_task(
|
||||
description="Test pydantic export",
|
||||
expected_output="Structured output",
|
||||
output_pydantic=StructuredResult,
|
||||
)
|
||||
|
||||
instance = StructuredResult(value="ok")
|
||||
pydantic_output, json_output = task._export_output(instance)
|
||||
assert pydantic_output is instance
|
||||
assert json_output is None
|
||||
|
||||
|
||||
def test_guardrails_vs_single_guardrail_mutual_exclusion():
|
||||
"""Test that guardrails list nullifies single guardrail."""
|
||||
|
||||
|
||||
@@ -87,6 +87,31 @@ def test_convert_to_model_with_no_model() -> None:
|
||||
assert output == "Plain text"
|
||||
|
||||
|
||||
def test_convert_to_model_with_basemodel_input_matching_pydantic() -> None:
|
||||
instance = SimpleModel(name="John", age=30)
|
||||
output = convert_to_model(instance, SimpleModel, None, None)
|
||||
assert output is instance
|
||||
|
||||
|
||||
def test_convert_to_model_with_basemodel_input_matching_json() -> None:
|
||||
instance = SimpleModel(name="John", age=30)
|
||||
output = convert_to_model(instance, None, SimpleModel, None)
|
||||
assert output == {"name": "John", "age": 30}
|
||||
|
||||
|
||||
def test_convert_to_model_with_basemodel_input_different_class() -> None:
|
||||
class OtherModel(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
extra: str = "default"
|
||||
|
||||
instance = OtherModel(name="John", age=30, extra="ignored")
|
||||
output = convert_to_model(instance, SimpleModel, None, None)
|
||||
assert isinstance(output, SimpleModel)
|
||||
assert output.name == "John"
|
||||
assert output.age == 30
|
||||
|
||||
|
||||
def test_convert_to_model_with_special_characters() -> None:
|
||||
json_string_test = """
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user