This is an automated email from the ASF dual-hosted git repository.
kaxil pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 24ff00e7d0b common-ai: Honour serialize_output=True on
LLMFileAnalysisOperator (#67858)
24ff00e7d0b is described below
commit 24ff00e7d0b57abd03f3226f61e59217313973cb
Author: Kaxil Naik <[email protected]>
AuthorDate: Tue Jun 2 09:01:28 2026 +0100
common-ai: Honour serialize_output=True on LLMFileAnalysisOperator (#67858)
---
.../common/ai/operators/llm_file_analysis.py | 9 +++++++
.../common/ai/operators/test_llm_file_analysis.py | 28 ++++++++++++++++++++++
2 files changed, 37 insertions(+)
diff --git
a/providers/common/ai/src/airflow/providers/common/ai/operators/llm_file_analysis.py
b/providers/common/ai/src/airflow/providers/common/ai/operators/llm_file_analysis.py
index 1b2bd9a912a..0c9d1df1bdd 100644
---
a/providers/common/ai/src/airflow/providers/common/ai/operators/llm_file_analysis.py
+++
b/providers/common/ai/src/airflow/providers/common/ai/operators/llm_file_analysis.py
@@ -21,6 +21,8 @@ from __future__ import annotations
from collections.abc import Sequence
from typing import TYPE_CHECKING, Any
+from pydantic import BaseModel
+
from airflow.providers.common.ai.operators.llm import LLMOperator
from airflow.providers.common.ai.utils.file_analysis import
build_file_analysis_request
from airflow.providers.common.ai.utils.logging import log_run_summary
@@ -139,6 +141,13 @@ class LLMFileAnalysisOperator(LLMOperator):
if self.require_approval:
self.defer_for_approval(context, output) # type: ignore[misc]
+ if self._serialize_model_output and isinstance(output, BaseModel):
+ # Honour ``serialize_output=True`` and the older-Airflow fallback,
+ # matching ``LLMOperator.execute``. ``LLMFileAnalysisOperator``
+ # overrides ``execute`` rather than calling ``super().execute()``,
+ # so the dump step has to be repeated here.
+ output = output.model_dump()
+
return output
def _build_system_prompt(self) -> str:
diff --git
a/providers/common/ai/tests/unit/common/ai/operators/test_llm_file_analysis.py
b/providers/common/ai/tests/unit/common/ai/operators/test_llm_file_analysis.py
index bea72048a2a..7b32e27451d 100644
---
a/providers/common/ai/tests/unit/common/ai/operators/test_llm_file_analysis.py
+++
b/providers/common/ai/tests/unit/common/ai/operators/test_llm_file_analysis.py
@@ -146,6 +146,34 @@ class TestLLMFileAnalysisOperator:
assert isinstance(result, Summary)
assert result.findings == ["error spike"]
+ @patch("airflow.providers.common.ai.operators.llm.PydanticAIHook",
autospec=True)
+ @patch(
+
"airflow.providers.common.ai.operators.llm_file_analysis.build_file_analysis_request",
autospec=True
+ )
+ def test_execute_serialize_output_returns_dict(self, mock_build_request,
mock_hook_cls):
+ """serialize_output=True dumps the BaseModel to a dict on the wire."""
+ mock_build_request.return_value = FileAnalysisRequest(
+ user_content="prepared prompt",
+ resolved_paths=["/tmp/app.log"],
+ total_size_bytes=10,
+ )
+ mock_agent = MagicMock(spec=["run_sync"])
+ mock_agent.run_sync.return_value =
_make_mock_run_result(Summary(findings=["error spike"]))
+ mock_hook_cls.get_hook.return_value.create_agent.return_value =
mock_agent
+
+ op = LLMFileAnalysisOperator(
+ task_id="test",
+ prompt="Summarize the file",
+ llm_conn_id="my_llm",
+ file_path="/tmp/app.log",
+ output_type=Summary,
+ serialize_output=True,
+ )
+ result = op.execute(context={})
+
+ assert result == {"findings": ["error spike"]}
+ assert not isinstance(result, Summary)
+
@patch(
"airflow.providers.common.ai.operators.llm_file_analysis.build_file_analysis_request",
autospec=True
)