kaxil commented on code in PR #74694:
URL: https://github.com/apache/airflow/pull/74694#discussion_r4268546945


##########
providers/common/ai/docs/changelog.rst:
##########
@@ -25,6 +25,12 @@
 Changelog
 ---------
 
+.. note::
+  ``LoggingToolset`` is no longer part of the public API and is no longer 
exported from

Review Comment:
   The `airflow.providers.common.ai.toolsets` re-export came from #74449 and 
never shipped (0.11.0rc1's `toolsets/__init__.py` doesn't have it). The path 
users actually have is 
`airflow.providers.common.ai.toolsets.logging.LoggingToolset`, which is in 
0.10.0 and now raises `ImportError`. Could the note name that path and give a 
replacement for the removed "use it directly with any pydantic-ai `Agent`" 
example, e.g. `Agent(..., capabilities=[ToolLoggingCapability()])`?



##########
providers/common/ai/docs/stability.rst:
##########
@@ -99,6 +102,110 @@ Pydantic AI toolsets, but the Pydantic AI class they 
inherit from can change.
        timeout under the default ``on_approval_timeout="fail"``, fails the 
task, except on
        ``@task.llm_branch``, where it skips the downstream tasks unless
        ``fail_on_reject=True``.
+   * - ``decision_policy`` on ``@task.llm`` and ``@task.llm_branch``, and
+       ``min_confidence`` on
+       :class:`~airflow.providers.common.ai.policies.decision.BranchOption`
+     - Without a ``min_confidence`` the operator behaves as it does without a 
policy. With
+       one, it returns (or branches on) the answer itself only when the least 
confident
+       field that reported a confidence is at or above the bar. Under the bar,
+       ``on_uncertain="review"`` (the default; needs Airflow 3.1+) opens the 
same human
+       review as ``require_approval``, and ``"fail"`` fails the task with
+       ``LowConfidenceError``. A model that reports no confidence counts as 
under the bar.
+       ``require_approval=True`` always asks a person. On ``@task.llm_branch`` 
a
+       ``BranchOption.min_confidence`` replaces the policy's bar for that 
branch, and with
+       ``allow_multiple_branches=True`` the strictest bar among the picked 
branches applies.
+       The operator pushes a ``decision`` XCom recording the confidence and 
the bar that
+       applied. ``@task.llm_sql``, ``@task.llm_file_analysis`` and
+       ``@task.llm_schema_compare`` reject a policy with a bar when they are 
created.
+   * - Per-tool approval on ``AgentOperator`` (``tool_approval_timeout``,
+       ``on_tool_approval_timeout`` and ``tool_approval_assigned_users``)
+     - Needs Airflow 3.3 or later; on earlier versions a tool marked for 
approval fails the
+       task. A call to a tool marked with Pydantic AI's approval API pauses 
the task before
+       the tool runs and asks on the Required Actions page, showing the tool 
name and its
+       arguments with secrets masked. **Approve** runs the call; **Reject** 
tells the agent
+       the call was denied, with the reviewer's reason, and the run carries 
on. A task
+       instance asks at most once per Dag run, retries and clears included; a 
second

Review Comment:
   Main keeps per-tool approval experimental because of this sentence. The only 
reason `_pause_for_tool_approval` gives for the guard is that a repeat request 
keeps the first one's subject and body. Since #73235 (in 3.4.0b1) a repeat 
request replaces them, and #74688 refuses an answer to the replaced request, so 
lifting the once-per-run limit is on the way. Promising 
`ToolApprovalAlreadyRequestedError` as stable would make that a breaking 
change. I'd keep this row experimental, or at least name the once-per-run limit 
as not covered.



##########
providers/common/ai/docs/stability.rst:
##########
@@ -99,6 +102,110 @@ Pydantic AI toolsets, but the Pydantic AI class they 
inherit from can change.
        timeout under the default ``on_approval_timeout="fail"``, fails the 
task, except on
        ``@task.llm_branch``, where it skips the downstream tasks unless
        ``fail_on_reject=True``.
+   * - ``decision_policy`` on ``@task.llm`` and ``@task.llm_branch``, and
+       ``min_confidence`` on
+       :class:`~airflow.providers.common.ai.policies.decision.BranchOption`
+     - Without a ``min_confidence`` the operator behaves as it does without a 
policy. With
+       one, it returns (or branches on) the answer itself only when the least 
confident
+       field that reported a confidence is at or above the bar. Under the bar,
+       ``on_uncertain="review"`` (the default; needs Airflow 3.1+) opens the 
same human
+       review as ``require_approval``, and ``"fail"`` fails the task with
+       ``LowConfidenceError``. A model that reports no confidence counts as 
under the bar.
+       ``require_approval=True`` always asks a person. On ``@task.llm_branch`` 
a
+       ``BranchOption.min_confidence`` replaces the policy's bar for that 
branch, and with
+       ``allow_multiple_branches=True`` the strictest bar among the picked 
branches applies.
+       The operator pushes a ``decision`` XCom recording the confidence and 
the bar that
+       applied. ``@task.llm_sql``, ``@task.llm_file_analysis`` and
+       ``@task.llm_schema_compare`` reject a policy with a bar when they are 
created.
+   * - Per-tool approval on ``AgentOperator`` (``tool_approval_timeout``,
+       ``on_tool_approval_timeout`` and ``tool_approval_assigned_users``)
+     - Needs Airflow 3.3 or later; on earlier versions a tool marked for 
approval fails the
+       task. A call to a tool marked with Pydantic AI's approval API pauses 
the task before
+       the tool runs and asks on the Required Actions page, showing the tool 
name and its
+       arguments with secrets masked. **Approve** runs the call; **Reject** 
tells the agent
+       the call was denied, with the reviewer's reason, and the run carries 
on. A task
+       instance asks at most once per Dag run, retries and clears included; a 
second
+       request fails the task with ``ToolApprovalAlreadyRequestedError``.
+       ``tool_approval_timeout`` (default ``None``, wait for as long as it 
takes) bounds the
+       pause. ``on_tool_approval_timeout`` is ``"fail"`` (default) or 
``"deny"``, which needs
+       a timeout; a timeout never approves. ``tool_approval_assigned_users`` 
limits who may
+       decide. Beside ``durable=True``, ``enable_hitl_review=True``, a 
``CodeMode``
+       capability or a ``SandboxToolset`` that provisions its own sandbox, a 
tool marked for
+       approval fails the task, except one called from code mode's 
``run_code``, which does
+       not run and is reported to the model.
+   * - 
:class:`~airflow.providers.common.ai.toolsets.object_storage.ObjectStorageToolset`
+     - Exposes ``list_files``, ``get_file_info`` and ``read_file``, each named
+       ``<tool_prefix>_<name>`` when ``tool_prefix`` is set, rooted at 
``path`` and read-only.
+       A path that is absolute, carries a scheme or climbs out of the root is 
refused. A
+       ``list_files`` result holds at most ``max_files`` entries. 
``read_file`` returns a
+       text file a window of lines at a time, at most ``max_output_bytes`` 
bytes, with the
+       offset to continue from, and a Parquet or Avro file as its row count, 
schema and first
+       rows. It refuses images, PDFs and other binary files, and any file 
larger than
+       ``max_read_bytes``.
+   * - 
:class:`~airflow.providers.common.ai.managed_agents.base.BaseManagedAgentHook`,
+       
:class:`~airflow.providers.common.ai.toolsets.managed_agent.ManagedAgentToolset`
 and
+       
:class:`~airflow.providers.common.ai.toolsets.managed_agent.BaseManagedAgentToolset`
+     - A vendor hook that adopts ``BaseManagedAgentHook`` implements 
``resolve_agent``,
+       ``get_agent_capabilities`` and ``invoke_agent``, and 
``hook.agent(name)`` binds one
+       agent; the Amazon ``BedrockAgentCoreHook`` and the Google 
``AgentEngineHook`` do so.
+       ``ManagedAgentToolset`` exposes a managed agent as one tool named 
``tool_name`` that
+       takes a single ``prompt`` and gives the model the answer's text. A 
rejection the model
+       can fix by rephrasing is returned to it, up to ``max_retries`` times 
(default ``1``);
+       a terminal or transient error fails the call and is left to the task's 
retry
+       settings. With ``durable=True`` a completed call is replayed from the 
cache only when
+       ``replayable=True``. A subclass of ``BaseManagedAgentToolset`` 
implements
+       ``agent_ref`` and ``invoke_sync`` (or ``invoke``).
+   * - ``@task.llm_sql`` and
+       
:class:`~airflow.providers.common.ai.operators.llm_sql.LLMSQLQueryOperator`
+     - Returns the SQL the model generates from the prompt and never runs it. 
Schema
+       context is ``schema_context`` when given, otherwise the tables in 
``table_names``
+       introspected through ``db_conn_id``. With ``validate_sql=True`` (the 
default) the
+       task fails unless the result is a single statement of a type in 
``allowed_sql_types``
+       (by default ``SELECT``, ``UNION``, ``INTERSECT`` and ``EXCEPT``). A 
reviewer's edit
+       under ``require_approval`` is validated again against 
``allowed_sql_types``, whether
+       or not ``validate_sql`` is set. ``datasource_config`` is not covered: 
it reads object
+       storage through DataFusion and is experimental; see below.
+   * - ``@task.llm_file_analysis`` and
+       
:class:`~airflow.providers.common.ai.operators.llm_file_analysis.LLMFileAnalysisOperator`
+     - Reads ``file_path``, a file or a prefix on object storage or a local 
path, and sends
+       its content to the model in one call. At most ``max_files`` files from 
a prefix are
+       included and the rest are noted in the prompt; the task fails when a 
file exceeds
+       ``max_file_size_bytes`` or the files together exceed 
``max_total_size_bytes``.
+       Text sent to the model is cut to ``max_text_chars``, and CSV, Parquet 
and Avro inputs
+       are sampled to ``sample_rows`` rows. PNG, JPG and PDF files are 
attached only with
+       ``multi_modal=True``. The output follows the rules of ``@task.llm``.
+   * - ``@task.llm_schema_compare`` and
+       
:class:`~airflow.providers.common.ai.operators.llm_schema_compare.LLMSchemaCompareOperator`
+     - Compares at least two schemas, from ``table_names`` on each of 
``db_conn_ids`` or from
+       ``data_sources``, and returns a ``SchemaCompareResult`` as a dict. It 
takes no
+       ``output_type``. ``context_strategy="basic"`` sends column names and 
types;
+       ``"full"`` (the default) adds primary keys, foreign keys and indexes. A
+       ``system_prompt`` replaces the default schema-comparison guidance, not 
the fixed
+       preamble and the schemas that surround it. ``data_sources`` is not 
covered: it reads a
+       source whose connection is not a ``DbApiHook`` through DataFusion and 
is experimental;
+       see below.
+   * - :class:`~airflow.providers.common.ai.hooks.langchain.LangChainHook` and
+       
:func:`~airflow.providers.common.ai.toolsets.langchain_bridge.airflow_toolset_to_langchain_tools`
+     - ``get_chat_model()`` and ``get_embedding_model()`` return LangChain 
models for
+       identifiers in ``provider:name`` form, from ``llm_model`` or the 
connection's
+       ``model`` and ``embed_model`` extras. The connection's password is 
passed as
+       ``api_key`` and its host as ``base_url``. ``embed_conn_id`` falls back 
to
+       ``llm_conn_id``. The bridge returns one LangChain ``StructuredTool`` 
per tool of the
+       toolset, with the tool's argument schema, and masks secrets in what a 
tool returns.
+       An argument that fails validation, a ``ModelRetry`` (up to the tool's
+       ``max_retries``) or a ``ToolFailed`` reaches the model as an error 
result. Once the
+       retries are used up, and for any other exception, the call raises and 
the run fails;
+       the exception class, ``ToolCallError``, is in the experimental 
``tools`` package.
+   * - Tool-call logging: ``enable_tool_logging`` on ``AgentOperator`` and
+       
:class:`~airflow.providers.common.ai.toolsets.logging.ToolLoggingCapability`
+     - With ``enable_tool_logging=True`` (the default), each call to a tool 
supplied through
+       ``toolsets=``, ``agent_params["tools"]`` or a capability logs its name 
and duration at
+       INFO inside a ``::group::`` block and its arguments at DEBUG. A call 
the run pauses
+       for approval, or that asks for a retry, deferral or skip, is logged at 
INFO and
+       re-raised; any other exception is logged at ERROR with a traceback and 
re-raised.
+       Output tools, provider-native tools and tools another capability adds 
through its own
+       wrapper are not logged. When ``capabilities`` already holds a
+       ``ToolLoggingCapability``, the operator adds none and its ``logger`` is 
used.
 
 Experimental features

Review Comment:
   This conflicts with #74679, which merged to main this morning (`git 
merge-tree` against main conflicts here and in `toolsets/index.rst`), and the 
two PRs classify six features the opposite way. This PR makes per-tool 
approval, `DecisionPolicy` / `BranchOption.min_confidence`, managed agent 
toolsets and the LangChain hook and bridge stable, while main keeps them 
experimental. Main has `@task.llm_batch` and the framework-neutral `tools` 
interface stable, while this PR lists them as experimental. 
`frameworks/index.rst` also merges cleanly into "the framework-neutral tool 
interface under them is stable", which would contradict the `tools` row here. 
Can you rebase so we can settle those six rows before going further?



##########
providers/common/ai/docs/frameworks/index.rst:
##########
@@ -103,9 +103,12 @@ Tested versions
      - Pins OpenTelemetry at 1.42.1 or lower, below the version in Airflow's 
constraints
        file. Airflow itself accepts 1.42.1.
 
-These are the versions the adapter tests have been run against. CI does not 
run those
-tests: its environment has ``mcp`` 2.2 and OpenTelemetry 1.44, which Strands 
and ADK
-exclude. They run in an environment with the framework installed.
+These are the versions the adapter tests have been run against. The main test 
matrix
+skips those tests: its environment has ``mcp`` 2.2 and OpenTelemetry 1.44, 
which Strands
+and ADK exclude. A separate CI job installs one framework at a time and runs 
them, on
+amd64 only, on the ``main`` branch only, and only when ``common.ai`` Python 
source or

Review Comment:
   Not quite "only when": `run_agent_framework_tests` goes through 
`_should_be_run`, which also returns `True` whenever selective checks decide 
full tests are needed (canary and push runs, the `full tests needed` label, 
environment file changes). The same sentence is in `stability.rst` around line 
280.



##########
providers/common/ai/src/airflow/providers/common/ai/operators/agent.py:
##########
@@ -277,7 +277,7 @@ class AgentOperator(CancellableAgentRunMixin, BaseOperator, 
HITLReviewMixin, Usa
         in the serialized Dag, and cannot be combined with this argument (the
         task fails when it runs).
     :param enable_tool_logging: When ``True`` (default), wraps the agent's
-        assembled function toolset in a ``LoggingToolset`` that logs tool calls
+        assembled function toolset in a ``ToolLoggingCapability`` that logs 
tool calls

Review Comment:
   The toolset isn't wrapped in a `ToolLoggingCapability`. The operator appends 
the capability, and its `get_wrapper_toolset` does the wrapping. Maybe "adds a 
`ToolLoggingCapability`, which wraps the agent's assembled function toolset", 
plus the rule from the stable row that a `ToolLoggingCapability` already in 
`capabilities` is used instead of adding a second one.



##########
providers/common/ai/docs/decision_models.rst:
##########
@@ -20,8 +20,9 @@ Decision models
 
 .. note::
 
-    Experimental: this can change or be removed in a minor release of this 
provider.
-    See :ref:`howto/stability`.
+    Experimental: using a decision model with ``ClassifierRetryPolicy`` can 
change or be

Review Comment:
   `ClassifierRetryPolicy` is experimental as a whole (its class docstring 
still says so), not only when used with a decision model. It also has its own 
`min_confidence`, so "`min_confidence` ... are stable" reads as covering it. 
Maybe "`ClassifierRetryPolicy` is experimental. `DecisionPolicy` and 
`BranchOption.min_confidence` are stable."



##########
providers/common/ai/docs/stability.rst:
##########
@@ -99,6 +102,110 @@ Pydantic AI toolsets, but the Pydantic AI class they 
inherit from can change.
        timeout under the default ``on_approval_timeout="fail"``, fails the 
task, except on
        ``@task.llm_branch``, where it skips the downstream tasks unless
        ``fail_on_reject=True``.
+   * - ``decision_policy`` on ``@task.llm`` and ``@task.llm_branch``, and
+       ``min_confidence`` on
+       :class:`~airflow.providers.common.ai.policies.decision.BranchOption`
+     - Without a ``min_confidence`` the operator behaves as it does without a 
policy. With
+       one, it returns (or branches on) the answer itself only when the least 
confident
+       field that reported a confidence is at or above the bar. Under the bar,
+       ``on_uncertain="review"`` (the default; needs Airflow 3.1+) opens the 
same human
+       review as ``require_approval``, and ``"fail"`` fails the task with
+       ``LowConfidenceError``. A model that reports no confidence counts as 
under the bar.
+       ``require_approval=True`` always asks a person. On ``@task.llm_branch`` 
a
+       ``BranchOption.min_confidence`` replaces the policy's bar for that 
branch, and with
+       ``allow_multiple_branches=True`` the strictest bar among the picked 
branches applies.
+       The operator pushes a ``decision`` XCom recording the confidence and 
the bar that
+       applied. ``@task.llm_sql``, ``@task.llm_file_analysis`` and
+       ``@task.llm_schema_compare`` reject a policy with a bar when they are 
created.
+   * - Per-tool approval on ``AgentOperator`` (``tool_approval_timeout``,
+       ``on_tool_approval_timeout`` and ``tool_approval_assigned_users``)
+     - Needs Airflow 3.3 or later; on earlier versions a tool marked for 
approval fails the
+       task. A call to a tool marked with Pydantic AI's approval API pauses 
the task before
+       the tool runs and asks on the Required Actions page, showing the tool 
name and its
+       arguments with secrets masked. **Approve** runs the call; **Reject** 
tells the agent
+       the call was denied, with the reviewer's reason, and the run carries 
on. A task
+       instance asks at most once per Dag run, retries and clears included; a 
second
+       request fails the task with ``ToolApprovalAlreadyRequestedError``.
+       ``tool_approval_timeout`` (default ``None``, wait for as long as it 
takes) bounds the
+       pause. ``on_tool_approval_timeout`` is ``"fail"`` (default) or 
``"deny"``, which needs
+       a timeout; a timeout never approves. ``tool_approval_assigned_users`` 
limits who may
+       decide. Beside ``durable=True``, ``enable_hitl_review=True``, a 
``CodeMode``
+       capability or a ``SandboxToolset`` that provisions its own sandbox, a 
tool marked for
+       approval fails the task, except one called from code mode's 
``run_code``, which does
+       not run and is reported to the model.
+   * - 
:class:`~airflow.providers.common.ai.toolsets.object_storage.ObjectStorageToolset`
+     - Exposes ``list_files``, ``get_file_info`` and ``read_file``, each named
+       ``<tool_prefix>_<name>`` when ``tool_prefix`` is set, rooted at 
``path`` and read-only.
+       A path that is absolute, carries a scheme or climbs out of the root is 
refused. A
+       ``list_files`` result holds at most ``max_files`` entries. 
``read_file`` returns a
+       text file a window of lines at a time, at most ``max_output_bytes`` 
bytes, with the
+       offset to continue from, and a Parquet or Avro file as its row count, 
schema and first
+       rows. It refuses images, PDFs and other binary files, and any file 
larger than
+       ``max_read_bytes``.
+   * - 
:class:`~airflow.providers.common.ai.managed_agents.base.BaseManagedAgentHook`,
+       
:class:`~airflow.providers.common.ai.toolsets.managed_agent.ManagedAgentToolset`
 and
+       
:class:`~airflow.providers.common.ai.toolsets.managed_agent.BaseManagedAgentToolset`
+     - A vendor hook that adopts ``BaseManagedAgentHook`` implements 
``resolve_agent``,
+       ``get_agent_capabilities`` and ``invoke_agent``, and 
``hook.agent(name)`` binds one
+       agent; the Amazon ``BedrockAgentCoreHook`` and the Google 
``AgentEngineHook`` do so.
+       ``ManagedAgentToolset`` exposes a managed agent as one tool named 
``tool_name`` that
+       takes a single ``prompt`` and gives the model the answer's text. A 
rejection the model
+       can fix by rephrasing is returned to it, up to ``max_retries`` times 
(default ``1``);
+       a terminal or transient error fails the call and is left to the task's 
retry
+       settings. With ``durable=True`` a completed call is replayed from the 
cache only when
+       ``replayable=True``. A subclass of ``BaseManagedAgentToolset`` 
implements
+       ``agent_ref`` and ``invoke_sync`` (or ``invoke``).
+   * - ``@task.llm_sql`` and
+       
:class:`~airflow.providers.common.ai.operators.llm_sql.LLMSQLQueryOperator`
+     - Returns the SQL the model generates from the prompt and never runs it. 
Schema
+       context is ``schema_context`` when given, otherwise the tables in 
``table_names``
+       introspected through ``db_conn_id``. With ``validate_sql=True`` (the 
default) the
+       task fails unless the result is a single statement of a type in 
``allowed_sql_types``
+       (by default ``SELECT``, ``UNION``, ``INTERSECT`` and ``EXCEPT``). A 
reviewer's edit
+       under ``require_approval`` is validated again against 
``allowed_sql_types``, whether
+       or not ``validate_sql`` is set. ``datasource_config`` is not covered: 
it reads object
+       storage through DataFusion and is experimental; see below.
+   * - ``@task.llm_file_analysis`` and
+       
:class:`~airflow.providers.common.ai.operators.llm_file_analysis.LLMFileAnalysisOperator`
+     - Reads ``file_path``, a file or a prefix on object storage or a local 
path, and sends
+       its content to the model in one call. At most ``max_files`` files from 
a prefix are
+       included and the rest are noted in the prompt; the task fails when a 
file exceeds
+       ``max_file_size_bytes`` or the files together exceed 
``max_total_size_bytes``.
+       Text sent to the model is cut to ``max_text_chars``, and CSV, Parquet 
and Avro inputs
+       are sampled to ``sample_rows`` rows. PNG, JPG and PDF files are 
attached only with
+       ``multi_modal=True``. The output follows the rules of ``@task.llm``.
+   * - ``@task.llm_schema_compare`` and
+       
:class:`~airflow.providers.common.ai.operators.llm_schema_compare.LLMSchemaCompareOperator`
+     - Compares at least two schemas, from ``table_names`` on each of 
``db_conn_ids`` or from
+       ``data_sources``, and returns a ``SchemaCompareResult`` as a dict. It 
takes no
+       ``output_type``. ``context_strategy="basic"`` sends column names and 
types;
+       ``"full"`` (the default) adds primary keys, foreign keys and indexes. A
+       ``system_prompt`` replaces the default schema-comparison guidance, not 
the fixed
+       preamble and the schemas that surround it. ``data_sources`` is not 
covered: it reads a
+       source whose connection is not a ``DbApiHook`` through DataFusion and 
is experimental;
+       see below.
+   * - :class:`~airflow.providers.common.ai.hooks.langchain.LangChainHook` and
+       
:func:`~airflow.providers.common.ai.toolsets.langchain_bridge.airflow_toolset_to_langchain_tools`
+     - ``get_chat_model()`` and ``get_embedding_model()`` return LangChain 
models for
+       identifiers in ``provider:name`` form, from ``llm_model`` or the 
connection's
+       ``model`` and ``embed_model`` extras. The connection's password is 
passed as
+       ``api_key`` and its host as ``base_url``. ``embed_conn_id`` falls back 
to
+       ``llm_conn_id``. The bridge returns one LangChain ``StructuredTool`` 
per tool of the
+       toolset, with the tool's argument schema, and masks secrets in what a 
tool returns.
+       An argument that fails validation, a ``ModelRetry`` (up to the tool's
+       ``max_retries``) or a ``ToolFailed`` reaches the model as an error 
result. Once the
+       retries are used up, and for any other exception, the call raises and 
the run fails;
+       the exception class, ``ToolCallError``, is in the experimental 
``tools`` package.
+   * - Tool-call logging: ``enable_tool_logging`` on ``AgentOperator`` and
+       
:class:`~airflow.providers.common.ai.toolsets.logging.ToolLoggingCapability`
+     - With ``enable_tool_logging=True`` (the default), each call to a tool 
supplied through
+       ``toolsets=``, ``agent_params["tools"]`` or a capability logs its name 
and duration at
+       INFO inside a ``::group::`` block and its arguments at DEBUG. A call 
the run pauses
+       for approval, or that asks for a retry, deferral or skip, is logged at 
INFO and
+       re-raised; any other exception is logged at ERROR with a traceback and 
re-raised.

Review Comment:
   This puts `ToolFailed` under "any other exception is logged at ERROR", but 
`_LoggingToolset.call_tool` logs it at INFO alongside `ModelRetry`, and 
`ObjectStorageToolset` raises it for every refused path. `toolsets/logging.rst` 
already says "report a failed tool result"; could this row list it too?



##########
providers/common/ai/docs/stability.rst:
##########
@@ -113,77 +220,69 @@ Everything this provider ships that is not in the table 
above is experimental.
      - Why it is experimental
    * - 
:class:`~airflow.providers.common.ai.policies.retry.ClassifierRetryPolicy`
        (:doc:`decision_models`)
-     - The confidence threshold, the fallback order and the behaviour when the 
classifier
-       is unavailable are still settling.
-   * - :class:`~airflow.providers.common.ai.policies.decision.DecisionPolicy`, 
and
-       ``min_confidence`` on
-       :class:`~airflow.providers.common.ai.policies.decision.BranchOption`
-       (:doc:`approval_gates`)
-     - New. The threshold semantics and the recorded decision may change as 
they are
-       used.
+     - It reads a decision model, and the ``typesafe:`` and ``system-one:`` 
prefixes need
+       ``pydantic-ai-slim`` 2.45 and 2.53, newer than the 2.33 floor of the 
provider's
+       other extras.
    * - ``@task.llm_batch`` and batch adapters (:doc:`operators/llm_batch`)
-     - Submission, re-attachment on retry, cancellation and handling of 
partial results
-       are still settling, and ``BatchAdapter`` has no implementation outside 
this
-       provider yet.
+     - ``BatchAdapter`` has two implementations, OpenAI and Anthropic, both in 
this
+       provider and none outside it.
    * - ``durable=True`` on ``AgentOperator`` (:doc:`durable_execution`)
-     - Which tool results are replayed on retry will change, so that a tool 
can declare
-       whether its result may be reused.
-   * - Per-tool approval on ``AgentOperator`` (``tool_approval_timeout``,
-       ``on_tool_approval_timeout`` and ``tool_approval_assigned_users``;
-       :doc:`tool_approval`)
-     - New, and needs Airflow 3.3. How a paused run resumes may change.
-   * - Code mode (:doc:`code_mode`), the Agent Skills toolset
-       (:doc:`toolsets/skills`) and the ``shields`` extra (used in 
:doc:`capabilities`)
-     - Thin integrations of packages outside this provider whose APIs are still
-       changing: ``pydantic-ai-harness``, ``pydantic-ai-skills`` and
-       ``pydantic-ai-shields``.
+     - Only ``ManagedAgentToolset`` can declare, through ``replayable``, that 
its results

Review Comment:
   It isn't only `ManagedAgentToolset`: `caching_toolset.py` reads `replayable` 
with `getattr` from the innermost toolset of any tool, and 
`BaseManagedAgentToolset` defaults it to `False`. Main's reason for this row 
("which tool results are replayed on retry will change") avoids the problem, so 
keeping it when resolving the conflict may be simplest.



##########
providers/common/ai/docs/stability.rst:
##########
@@ -113,77 +220,69 @@ Everything this provider ships that is not in the table 
above is experimental.
      - Why it is experimental
    * - 
:class:`~airflow.providers.common.ai.policies.retry.ClassifierRetryPolicy`
        (:doc:`decision_models`)
-     - The confidence threshold, the fallback order and the behaviour when the 
classifier
-       is unavailable are still settling.
-   * - :class:`~airflow.providers.common.ai.policies.decision.DecisionPolicy`, 
and
-       ``min_confidence`` on
-       :class:`~airflow.providers.common.ai.policies.decision.BranchOption`
-       (:doc:`approval_gates`)
-     - New. The threshold semantics and the recorded decision may change as 
they are
-       used.
+     - It reads a decision model, and the ``typesafe:`` and ``system-one:`` 
prefixes need
+       ``pydantic-ai-slim`` 2.45 and 2.53, newer than the 2.33 floor of the 
provider's
+       other extras.
    * - ``@task.llm_batch`` and batch adapters (:doc:`operators/llm_batch`)
-     - Submission, re-attachment on retry, cancellation and handling of 
partial results
-       are still settling, and ``BatchAdapter`` has no implementation outside 
this
-       provider yet.
+     - ``BatchAdapter`` has two implementations, OpenAI and Anthropic, both in 
this
+       provider and none outside it.
    * - ``durable=True`` on ``AgentOperator`` (:doc:`durable_execution`)
-     - Which tool results are replayed on retry will change, so that a tool 
can declare
-       whether its result may be reused.
-   * - Per-tool approval on ``AgentOperator`` (``tool_approval_timeout``,
-       ``on_tool_approval_timeout`` and ``tool_approval_assigned_users``;
-       :doc:`tool_approval`)
-     - New, and needs Airflow 3.3. How a paused run resumes may change.
-   * - Code mode (:doc:`code_mode`), the Agent Skills toolset
-       (:doc:`toolsets/skills`) and the ``shields`` extra (used in 
:doc:`capabilities`)
-     - Thin integrations of packages outside this provider whose APIs are still
-       changing: ``pydantic-ai-harness``, ``pydantic-ai-skills`` and
-       ``pydantic-ai-shields``.
+     - Only ``ManagedAgentToolset`` can declare, through ``replayable``, that 
its results
+       must not be replayed. For every other tool a cached result is replayed 
on retry, and
+       a tool has no way to declare that its result must not be reused.
+   * - Code mode (:doc:`code_mode`)
+     - It depends on ``pydantic-ai-harness``, which is pre-1.0; the 
``code-mode`` extra asks
+       for 0.24.0 or later.
+   * - The Agent Skills toolset (:doc:`toolsets/skills`)
+     - The ``skills`` extra is built on ``pydantic-ai-skills``. Native 
progressive
+       disclosure is tracked upstream in
+       `pydantic/pydantic-ai#5230 
<https://github.com/pydantic/pydantic-ai/issues/5230>`__,
+       and the extra is to be revisited once that lands.
+   * - The ``shields`` extra (used in :doc:`capabilities`)
+     - It depends on ``pydantic-ai-shields``, which is pre-1.0 (the extra asks 
for 0.3.4 or
+       later), and this provider has no test of it.
    * - :class:`~airflow.providers.common.ai.toolsets.sandbox.SandboxToolset` 
and its
        backends (:doc:`sandbox/index`)
-     - The sandbox runtime belongs to the backend; ownership and cleanup 
across worker
-       failures are still being designed.
-   * - LangChain and LlamaIndex hooks and the LangChain tool bridge
-       (:doc:`hooks/langchain`, :doc:`toolsets/langchain`, 
:doc:`hooks/llamaindex`)
-     - Each follows the API of a framework that changes often.
+     - A worker killed outright skips teardown. The ``sbx`` backend has no 
server-side
+       lifetime, so its microVM and workspace survive and nothing reclaims them
+       (:doc:`sandbox/backends`).
+   * - The LlamaIndex hook (:doc:`hooks/llamaindex`)
+     - It depends on ``llama-index-core``, which is pre-1.0; the 
``llamaindex`` extra asks
+       for 0.14.5 or later.
    * - The retrieval operators: ``DocumentLoaderOperator``,
        ``LlamaIndexEmbeddingOperator`` and ``LlamaIndexRetrievalOperator``
        (:doc:`rag_pipelines`)
-     - New. The shape of the pipeline, from loading documents through 
embedding to
-       retrieval, has not settled.
+     - The two LlamaIndex operators depend on ``llama-index-core``, which is 
pre-1.0.
+       ``DocumentLoaderOperator`` needs no AI framework; whether it belongs in 
this provider's
+       stable surface is an open decision, not a technical reason.
    * - 
:class:`~airflow.providers.common.ai.toolsets.datafusion.DataFusionToolset`
        (:doc:`toolsets/datafusion`)
      - Runs on the DataFusion engine of ``common.sql``, which pins 
``datafusion`` below 52
-       and follows its API.
-   * - Managed agent toolsets (:doc:`toolsets/managed_agent`)
-     - A contract for vendor providers that no vendor implements yet.
+       because the DataFusion 52 crate is not supported with ``iceberg-core``.
+   * - ``datasource_config`` on ``@task.llm_sql`` and ``data_sources`` on
+       ``@task.llm_schema_compare`` (:doc:`operators/llm_sql`,
+       :doc:`operators/llm_schema_compare`)
+     - They read object storage, or any source whose connection is not a 
``DbApiHook``,
+       through the DataFusion engine of ``common.sql``, which pins
+       ``datafusion`` below 52 because the DataFusion 52 crate is not 
supported with
+       ``iceberg-core``.
    * - OpenTelemetry spans (``[common.ai] otel_export_enabled`` and
        ``capture_content``; :doc:`observability`)
-     - Span names and attributes come from Pydantic AI's instrumentation and 
the
-       OpenTelemetry GenAI conventions, which are still in development.
-   * - :class:`~airflow.providers.common.ai.toolsets.logging.LoggingToolset` 
used directly
-       (:doc:`toolsets/logging`)
-     - The wrapper ``AgentOperator`` applies for ``enable_tool_logging``; its 
constructor
-       may change.
-   * - ``@task.llm_schema_compare``, ``@task.llm_file_analysis`` and 
``@task.llm_sql``
-       (:doc:`operators/llm_schema_compare`, 
:doc:`operators/llm_file_analysis`,
-       :doc:`operators/llm_sql`)
-     - Each adds its own input handling to ``@task.llm``: schema 
introspection, file
-       sampling, or SQL validation. Their options are still settling.
+     - Span names and attributes come from Pydantic AI's instrumentation, 
which follows the
+       OpenTelemetry GenAI semantic conventions. This provider only turns it 
on, so it does

Review Comment:
   The provider does control part of this: `observability.py` pins 
`_SEMCONV_VERSION = 5` so a change in pydantic-ai's default doesn't shift the 
span format between provider releases. The reason that still holds is main's, 
that the GenAI semantic conventions are still in development.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to