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 134b7332319 Add support for TypeSafe Jev classifier models (#73363)
134b7332319 is described below

commit 134b7332319d78f81facc66b085f764e84aa5bcd
Author: Kaxil Naik <[email protected]>
AuthorDate: Mon Sep 21 00:31:55 2026 +0100

    Add support for TypeSafe Jev classifier models (#73363)
    
    A classifier model answers typed questions -- pick one of these options, a
    bounded score, a yes/no probability -- and refuses anything that would need
    prose. pydantic-ai 2.45.0 added ``TypeSafeModel`` for TypeSafe's Jev, and 
its
    provider takes exactly the ``api_key`` and ``base_url`` that
    ``PydanticAIHook`` already passes, so no provider code changes: setting
    ``model_id`` or the connection's Model field is the whole integration.
    
    Adds the opt-in ``typesafe`` extra, a guide covering when a classifier 
model is
    the right choice and where it fits among the existing operators, and an 
example
    Dag with a branch and a confidence-gated classification.
    
    The extra pulls ``typesafe-sdk``, which is pre-1.0, so it stays opt-in 
rather
    than a base dependency. The temporary per-package ``exclude-newer`` pins are
    needed because 2.45.0 was published inside the rolling four-day window.
---
 docs/spelling_wordlist.txt                         |   1 +
 providers/common/ai/README.rst                     |   1 +
 providers/common/ai/docs/classifier_models.rst     | 189 +++++++++++++++
 providers/common/ai/docs/examples.rst              |   4 +
 providers/common/ai/docs/index.rst                 |  10 +-
 providers/common/ai/pyproject.toml                 |  10 +
 .../ai/example_dags/example_classifier_model.py    | 134 +++++++++++
 pyproject.toml                                     |   8 +
 uv.lock                                            | 259 +++++++++++----------
 9 files changed, 494 insertions(+), 122 deletions(-)

diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index f719c597bf7..67308f30971 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -918,6 +918,7 @@ jdk
 jenkins
 JenkinsRequest
 Jetson
+Jev
 Jiajie
 Jinja
 jinja
diff --git a/providers/common/ai/README.rst b/providers/common/ai/README.rst
index d799a5a0d31..b7b02d9ddb2 100644
--- a/providers/common/ai/README.rst
+++ b/providers/common/ai/README.rst
@@ -90,6 +90,7 @@ Extra           Dependencies
 ``google``      ``pydantic-ai-slim[google]>=2.23.0``
 ``openai``      ``pydantic-ai-slim[openai]>=2.23.0``
 ``mcp``         ``pydantic-ai-slim[mcp]>=2.23.0``
+``typesafe``    ``typesafe-sdk>=0.6.0``
 ``code-mode``   ``pydantic-ai-harness[codemode]>=0.3.0``
 ``shields``     ``pydantic-ai-shields>=0.3.4``
 ``skills``      ``apache-airflow-providers-git>=0.4.0``, 
``pydantic-ai-skills>=1.2.0``
diff --git a/providers/common/ai/docs/classifier_models.rst 
b/providers/common/ai/docs/classifier_models.rst
new file mode 100644
index 00000000000..f53e36f6b2a
--- /dev/null
+++ b/providers/common/ai/docs/classifier_models.rst
@@ -0,0 +1,189 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+ ..   http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+Classifier models
+=================
+
+Every other model in this provider writes text. A classifier model does not: 
you give it
+some text and a typed question, and it answers with a value from a set you 
named in
+advance, plus a confidence. Ask it for a string and the request is refused 
before it
+leaves your process.
+
+TypeSafe's Jev is the one pydantic-ai supports, as the ``typesafe:`` provider. 
Nothing in
+this provider is specific to it -- it arrives through the same
+:class:`~airflow.providers.common.ai.hooks.pydantic_ai.PydanticAIHook` as 
every other
+model, so a model id is the whole integration.
+
+Setup
+-----
+
+1. Install the extra:
+
+   .. code-block:: bash
+
+       pip install 'apache-airflow-providers-common-ai[typesafe]'
+
+   The extra installs the TypeSafe SDK; the model adapter itself is part of 
pydantic-ai
+   from 2.45.0, so make sure ``pydantic-ai-slim>=2.45.0`` is installed as 
well. The
+   provider does not raise its own floor to that release yet, because 
pydantic-ai's
+   ``openai`` extra needs openai 3.x while other Airflow providers still pin 
openai 2.x.
+
+2. Create a connection (``Admin > Connections``):
+
+   - **Connection Id**: ``jev_default``
+   - **Connection Type**: ``Pydantic AI``
+   - **Password**: your TypeSafe API key
+   - **Extra**: ``{"model": "typesafe:jev-1.13.0"}``
+
+Leave **Host** empty unless you are pointing at a proxy; the provider defaults 
to
+TypeSafe's own endpoint.
+
+Pin the version rather than using ``jev-latest``. A threshold you tuned 
against one
+release is not guaranteed to mean the same thing after the next one, and 
``jev-latest``
+moves under you.
+
+When a classifier model is the right choice
+-------------------------------------------
+
+All four of these have to hold.
+
+**The answer is a label, a number, or a pick, not prose.** A ``bool``, a 
``Literal`` or
+``Enum`` of strings, a bounded ``float``, a whole-number rubric, or a list of 
picks. A
+``str`` field is refused, so anything that writes a summary, a query, a 
migration, or a
+reply to a person is out.
+
+**You can name the options up front, and there are at most 255.** Downstream 
task ids,
+error categories, severity levels, environments, a repository list. If the set 
is open, or
+is discovered at run time and could grow past the cap, this is the wrong tool. 
The cap is
+per question, so tools attached to an agent get their own 255, with the output 
type as one
+option in that question.
+
+**The decision is on a path where latency or cost is the constraint.** One 
decision per
+Dag run rarely justifies changing models. One per row, per file, or per 
retrieved document
+does, and so does a branch a scheduler is waiting on.
+
+**You want a number to branch on, not a sentence to trust.** A pick, a rubric, 
and a
+yes/no each come back with a confidence, so "act automatically above 0.8, ask 
a human below
+it" becomes something you can write down. A bounded ``float`` is the 
exception: there the
+probability *is* the answer, so nothing separate is reported and you gate on 
the value
+itself. If you would not do anything different with a confidence of 0.55 than 
with 0.95,
+that is a sign a general-purpose model is fine here.
+
+And one case where the answer is neither: if a deterministic rule already 
sorts the input
+correctly, use the rule. A classifier model is cheap, not free, and a rule you 
can read is
+worth more than a probability you have to calibrate.
+
+Where it fits in this provider
+------------------------------
+
+.. list-table::
+   :header-rows: 1
+   :widths: 30 15 55
+
+   * - Surface
+     - Fits
+     - Why
+   * - 
:class:`~airflow.providers.common.ai.operators.llm_branch.LLMBranchOperator`
+     - Yes, with a caveat
+     - The downstream task ids are already presented to the model as a 
constrained set of
+       choices, which is exactly the shape a classifier model answers. Setting
+       ``model_id`` is the only change, as long as there are two or more 
downstream tasks
+       -- a one-option pick is refused. The other caveat is below: the 
operator cannot see
+       the confidence, so it will branch on an unsure answer without telling 
you.
+   * - :class:`~airflow.providers.common.ai.operators.llm.LLMOperator` /
+       :class:`~airflow.providers.common.ai.operators.agent.AgentOperator` 
with a typed
+       ``output_type``
+     - Yes
+     - A ``Literal``, ``Enum``, ``bool`` or bounded number works. Describe the 
field, which
+       becomes the question, and describe each option, which is what tells 
them apart. An
+       option with no description is read from its name alone.
+   * - :doc:`LLMRetryPolicy <retry_policies>`
+     - No
+     - Three of ``ErrorClassification``'s four fields are shapes a classifier 
model
+       refuses: ``category`` and ``reasoning`` are bare ``str``, and
+       ``suggested_delay_seconds`` is an unbounded ``int``. Only 
``should_retry`` fits.
+       Making the class answerable would need a ``Literal`` category, a rubric 
delay, and
+       no explanation at all.
+   * - Agents with toolsets
+     - Partly
+     - Which tool the text calls for is itself a pick, so a classifier model 
can make it.
+       What it cannot write is a tool's arguments. A tool taking none it calls 
itself; one
+       taking arguments raises ``ToolCallProposed`` after the request, which 
is a
+       ``ModelAPIError`` rather than a refusal, so ``FallbackModel`` hands 
those requests to
+       a text model behind it and only they cost a full call.
+
+Reading the confidence
+----------------------
+
+The confidence lives in ``provider_details`` on the model response, which no 
operator
+exposes on XCom by default -- ``AgentOperator`` carries it inside the 
``message_history``
+transcript when that is enabled. It is reported per output field, and a bare 
output type is
+reported under ``"response"``. A bounded ``float`` field reports none at all, 
because there
+the probability is the answer.
+
+This matters most for branching, because a branch is taken inside the operator 
before any
+downstream task can look at it. Given two repair paths that both fit the 
evidence, a
+classifier model splits its probability between them, and the branch is then 
decided by
+whichever side of a near-even split came out ahead. The confidence reports 
that faithfully;
+it is just not somewhere Airflow can act on yet, so a near-tie routes on a 
margin the
+operator cannot show you. Treat ``LLMBranchOperator`` on a classifier model as 
suited to
+branches whose options are genuinely distinct, and put anything closer than 
that behind a
+task that reads the confidence itself:
+
+.. code-block:: python
+
+    from airflow.providers.common.ai.hooks.pydantic_ai import PydanticAIHook
+    from airflow.providers.common.compat.sdk import task
+    from typing import Literal
+
+
+    @task
+    def triage(log_line: str) -> dict:
+        agent = PydanticAIHook(llm_conn_id="jev_default").create_agent(
+            output_type=Literal["transient", "resource", "permanent"],
+            instructions="Classify why this Airflow task failed.",
+        )
+        result = agent.run_sync(log_line)
+        details = result.response.provider_details or {}
+        confidence = (details.get("confidence") or {}).get("response")
+        return {"category": result.output, "confidence": confidence}
+
+Then branch on the returned confidence in a downstream task, so an unsure 
answer escalates
+instead of acting. Use a higher bar for acting automatically than for flagging 
something
+for review: collapsing both into one number is the easier thing to tune and 
the wrong
+shape for the decision.
+
+Worked example
+--------------
+
+`example_classifier_model.py 
<https://github.com/apache/airflow/blob/providers-common-ai/|version|/providers/common/ai/src/airflow/providers/common/ai/example_dags/example_classifier_model.py>`__
+has both halves: a branch whose only classifier-specific line is ``model_id``, 
and a
+classification that escalates when the confidence is low.
+
+What it answers badly
+---------------------
+
+Read `pydantic-ai's model page 
<https://pydantic.dev/docs/ai/models/typesafe/>`__ before you
+trust a number from one of these models. Two of its failure modes matter more 
than the
+rest in a Dag:
+
+- **The text is treated as data, not as hostile.** An injected instruction, a 
misleading
+  framing, or an argument for its own answer can move the result. A guard 
built on a
+  classifier model belongs alongside deterministic checks, not instead of them.
+- **Option order is part of what the model sees.** Reordering a ``Literal``'s 
members can
+  change the answer, so a threshold measured against one ordering is measured 
against
+  that ordering only.
diff --git a/providers/common/ai/docs/examples.rst 
b/providers/common/ai/docs/examples.rst
index 536e3f1b46a..f45a49d627a 100644
--- a/providers/common/ai/docs/examples.rst
+++ b/providers/common/ai/docs/examples.rst
@@ -144,6 +144,10 @@ Reliability
      - Failing over to another vendor inside one task attempt, and drilling 
the chain
        without waiting for an outage. Source:
        `example_llm_fallback.py 
<https://github.com/apache/airflow/blob/providers-common-ai/|version|/providers/common/ai/src/airflow/providers/common/ai/example_dags/example_llm_fallback.py>`__.
+   * - :doc:`classifier_models`
+     - Routing a failure with a model that answers typed questions instead of 
writing
+       text, and escalating when its confidence is low. Source:
+       `example_classifier_model.py 
<https://github.com/apache/airflow/blob/providers-common-ai/|version|/providers/common/ai/src/airflow/providers/common/ai/example_dags/example_classifier_model.py>`__.
 
 .. toctree::
     :hidden:
diff --git a/providers/common/ai/docs/index.rst 
b/providers/common/ai/docs/index.rst
index 329cba65e79..29b6c9e211e 100644
--- a/providers/common/ai/docs/index.rst
+++ b/providers/common/ai/docs/index.rst
@@ -117,10 +117,12 @@ Choosing extras
 
 The provider's extras split into a few groups:
 
-* **Model providers** — ``openai``, ``anthropic``, ``google``, ``bedrock``: 
pick the one
-  matching your ``llm_conn_id`` connection. Each extra name mirrors the 
identically named
+* **Model providers** — ``openai``, ``anthropic``, ``google``, ``bedrock``, 
``typesafe``:
+  pick the one matching your ``llm_conn_id`` connection. ``typesafe`` differs 
from the rest
+  in kind: it installs a classifier model that answers typed questions and 
cannot write
+  text (see :doc:`classifier_models`). Each extra name mirrors the identically 
named
   ``pydantic-ai-slim`` optional dependency group; pydantic-ai supports more 
model providers
-  than these four, each under its own extra name, so check the
+  than these, each under its own extra name, so check the
   `pydantic-ai install docs <https://ai.pydantic.dev/install/#slim-install>`__ 
for the full list.
 * **Agent tooling** — ``mcp``, ``skills``, ``code-mode``, ``shields``: MCP 
servers, Agent
   Skills, code-mode tool execution, and shield capabilities (input/output 
guards, tool
@@ -163,6 +165,7 @@ See the Optional dependencies table below for the exact 
package each extra insta
     Provider fallback <provider_fallback>
     Retry Policies <retry_policies>
     Self-hosted models <self_hosted_models>
+    Classifier models <classifier_models>
     HITL Review <hitl_review>
     Observability <observability>
 
@@ -273,6 +276,7 @@ Extra           Dependencies
 ``bedrock``     ``pydantic-ai-slim[bedrock]>=2.23.0``
 ``google``      ``pydantic-ai-slim[google]>=2.23.0``
 ``openai``      ``pydantic-ai-slim[openai]>=2.23.0``
+``typesafe``    ``typesafe-sdk>=0.6.0``
 ``mcp``         ``pydantic-ai-slim[mcp]>=2.23.0``
 ``code-mode``   ``pydantic-ai-harness[codemode]>=0.3.0``
 ``shields``     ``pydantic-ai-shields>=0.3.4``
diff --git a/providers/common/ai/pyproject.toml 
b/providers/common/ai/pyproject.toml
index a27011e7f69..ef66ea5b0ee 100644
--- a/providers/common/ai/pyproject.toml
+++ b/providers/common/ai/pyproject.toml
@@ -82,6 +82,16 @@ dependencies = [
 "bedrock" = ["pydantic-ai-slim[bedrock]>=2.23.0"]
 "google" = ["pydantic-ai-slim[google]>=2.23.0"]
 "openai" = ["pydantic-ai-slim[openai]>=2.23.0"]
+# TypeSafe Jev: answers typed questions (pick one of N, a bounded score, a 
yes/no
+# probability) and cannot emit text, so it fits a branch or a classification 
but not a
+# summary. It arrives through pydantic-ai's own ``typesafe:`` provider, so the 
hook needs
+# no changes -- setting ``model_id`` or the connection's Model field is the 
integration.
+# The adapter itself ships inside pydantic-ai-slim from 2.45.0 
(pydantic/pydantic-ai#8450);
+# this extra only adds the pre-1.0 typesafe-sdk it talks to, which is why it 
is opt-in. It
+# deliberately does not raise the pydantic-ai-slim floor: from 2.31 the 
``openai`` extra
+# needs openai>=3.8 while llama-index-llms-openai and the Google provider's 
litellm need
+# openai<3, so a higher floor makes the workspace unsatisfiable. The guide 
says 2.45+.
+"typesafe" = ["typesafe-sdk>=0.6.0"]
 "mcp" = ["pydantic-ai-slim[mcp]>=2.23.0"]
 # Code mode: collapse tool calls into a single `run_code` tool that the model
 # drives by writing Python, executed in the Monty sandbox (pydantic-monty).
diff --git 
a/providers/common/ai/src/airflow/providers/common/ai/example_dags/example_classifier_model.py
 
b/providers/common/ai/src/airflow/providers/common/ai/example_dags/example_classifier_model.py
new file mode 100644
index 00000000000..c108a3cacae
--- /dev/null
+++ 
b/providers/common/ai/src/airflow/providers/common/ai/example_dags/example_classifier_model.py
@@ -0,0 +1,134 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""
+Example DAG using a classifier model to route an incident.
+
+A classifier model answers typed questions and cannot write text, so it suits 
a branch
+(pick one of these task ids) and a classification (pick one of these labels), 
and nothing
+that produces prose. See the "Classifier models" guide in this provider's 
documentation.
+
+Prerequisites:
+  - ``pip install 'apache-airflow-providers-common-ai[typesafe]'``
+  - Connection ``jev_default`` with ``conn_type='pydanticai'``, 
``password=<API key>``,
+    ``extra='{"model": "typesafe:jev-1.13.0"}'``
+"""
+
+from __future__ import annotations
+
+from typing import Literal
+
+from airflow.providers.common.ai.hooks.pydantic_ai import PydanticAIHook
+from airflow.providers.common.ai.operators.llm_branch import LLMBranchOperator
+from airflow.providers.common.compat.sdk import dag, task
+
+# Clear-cut: the error names the missing permission, so one remediation fits 
and the others
+# do not. Branch options a classifier model can separate are the ones to give 
it.
+PERMISSION_FAILURE = (
+    "Task export_report failed: botocore.exceptions.ClientError: An error 
occurred "
+    "(AccessDenied) when calling the PutObject operation: the task role "
+    "airflow-worker lacks s3:PutObject on arn:aws:s3:::reports-prod/daily/."
+)
+
+# Answerable but not decisively: a classifier model lands on ``resource`` here 
with roughly
+# two thirds of the probability, which is enough to be worth recording and not 
enough to act
+# on unattended. That is the case the confidence gate below exists for.
+UNDERDETERMINED_INCIDENT = (
+    "Task load_orders failed: psycopg2.OperationalError: connection refused to 
"
+    "warehouse.internal:5432. Two other Dags writing to the same warehouse 
failed in the "
+    "last ten minutes. A Terraform apply touching the database security group 
merged "
+    "four hours ago."
+)
+
+# Acting automatically deserves a higher bar than flagging for review, so the 
two use
+# different thresholds rather than one shared number.
+ACT_ABOVE = 0.8
+REVIEW_ABOVE = 0.5
+
+
+# [START howto_classifier_model_branch]
+@dag(tags=["example", "classifier"])
+def example_classifier_model_branch():
+    """Route the incident. The model id is the only thing that makes this a 
classifier."""
+    route = LLMBranchOperator(
+        task_id="route_failure",
+        prompt=PERMISSION_FAILURE,
+        llm_conn_id="jev_default",
+        model_id="typesafe:jev-1.13.0",
+        system_prompt="Pick the remediation that addresses the cause, not the 
symptom.",
+    )
+
+    @task
+    def grant_bucket_write():
+        return "Adding s3:PutObject for the task role"
+
+    @task
+    def restore_deleted_bucket():
+        return "Recreating the destination bucket"
+
+    @task
+    def wait_and_retry():
+        return "Treating this as transient"
+
+    route >> [grant_bucket_write(), restore_deleted_bucket(), wait_and_retry()]
+
+
+# [END howto_classifier_model_branch]
+
+example_classifier_model_branch()
+
+
+# [START howto_classifier_model_confidence]
+@dag(tags=["example", "classifier"])
+def example_classifier_model_confidence():
+    """Classify a failure and escalate when the model says it does not know.
+
+    The branch Dag above cannot do this: ``LLMBranchOperator`` takes the 
branch inside the
+    operator, before any task can read the confidence.
+    """
+
+    @task
+    def classify(log_line: str) -> dict:
+        agent = PydanticAIHook(llm_conn_id="jev_default").create_agent(
+            output_type=Literal["transient", "resource", "permanent"],
+            instructions="Classify why this Airflow task failed.",
+        )
+        result = agent.run_sync(log_line)
+        # Confidence is reported per output field; a bare output type lands 
under
+        # "response". A bounded ``float`` output would report none at all -- 
there the
+        # probability is the answer -- so this ``or 0.0`` would read as no 
confidence
+        # rather than as a missing one. No operator surfaces this on XCom by 
default.
+        details = result.response.provider_details or {}
+        return {
+            "category": result.output,
+            "confidence": (details.get("confidence") or {}).get("response"),
+        }
+
+    @task
+    def act(classification: dict) -> str:
+        confidence = classification["confidence"] or 0.0
+        if confidence >= ACT_ABOVE:
+            return f"Remediating {classification['category']} automatically"
+        if confidence >= REVIEW_ABOVE:
+            return f"Filing {classification['category']} for review"
+        return "Paging a human: the classification was a coin flip"
+
+    act(classify(UNDERDETERMINED_INCIDENT))
+
+
+# [END howto_classifier_model_confidence]
+
+example_classifier_model_confidence()
diff --git a/pyproject.toml b/pyproject.toml
index 59c0b15f2dc..14af9aa3600 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1602,6 +1602,12 @@ pydantic-ai-skills = "2026-07-16T00:00:00Z"
 # past that release so 1.164.0 resolves while any later release stays 
quarantined.
 # Remove once the rolling window advances past 2026-08-12.
 google-cloud-aiplatform = "2026-08-13T00:00:00Z"
+# Temporary: typesafe-sdk>=0.6.0 (required by the 
apache-airflow-providers-common-ai[typesafe]
+# extra) first shipped on 2026-09-18, inside the "4 days" exclude-newer 
window. This pins the
+# per-package cutoff just past that release so it resolves while any later 
release stays
+# quarantined.
+# Remove once the rolling window advances past 2026-09-18.
+typesafe-sdk = "2026-09-19T00:00:00Z"
 
 [tool.uv.pip]
 # Synchroonize with scripts/ci/prek/upgrade_important_versions.py
@@ -1756,6 +1762,8 @@ apache-airflow-task-sdk-integration-tests = false
 pydantic-ai-skills = "2026-07-16T00:00:00Z"
 # Temporary: see the google-cloud-aiplatform note in 
`[tool.uv.exclude-newer-package]`.
 google-cloud-aiplatform = "2026-08-13T00:00:00Z"
+# Temporary: see the typesafe-sdk note in `[tool.uv.exclude-newer-package]`.
+typesafe-sdk = "2026-09-19T00:00:00Z"
 
 [tool.uv.sources]
 # These names must match the names as defined in the pyproject.toml of the 
workspace items,
diff --git a/uv.lock b/uv.lock
index 157eb691173..07a45a3b29f 100644
--- a/uv.lock
+++ b/uv.lock
@@ -29,147 +29,148 @@ exclude-newer = "0001-01-01T00:00:00Z" # This has no 
effect and is included for
 exclude-newer-span = "P4D"
 
 [options.exclude-newer-package]
-apache-airflow-providers-informatica = false
-apache-airflow-providers-clickhousedb = false
-apache-airflow-providers-amazon = false
-apache-airflow-providers-elasticsearch = false
-apache-airflow-providers-duckdb = false
+apache-airflow = false
+apache-airflow-breeze = false
+apache-airflow-core = false
+apache-airflow-ctl = false
+apache-airflow-ctl-tests = false
+apache-airflow-dev = false
+apache-airflow-devel-common = false
+apache-airflow-docker-stack = false
 apache-airflow-docker-tests = false
-apache-airflow-providers-microsoft-winrm = false
-apache-airflow-providers = false
-apache-airflow-providers-fab = false
-apache-airflow-providers-openlineage = false
-apache-airflow-providers-sftp = false
 apache-airflow-e2e-tests = false
-apache-airflow-shared-logging = false
-apache-airflow-providers-common-dataquality = false
+apache-airflow-helm-chart = false
+apache-airflow-kubernetes-tests = false
+apache-airflow-mypy = false
+apache-airflow-providers = false
+apache-airflow-providers-airbyte = false
+apache-airflow-providers-akeyless = false
+apache-airflow-providers-alibaba = false
+apache-airflow-providers-amazon = false
+apache-airflow-providers-anthropic = false
+apache-airflow-providers-apache-arrow = false
+apache-airflow-providers-apache-beam = false
+apache-airflow-providers-apache-cassandra = false
 apache-airflow-providers-apache-drill = false
-apache-airflow-providers-pgvector = false
-apache-airflow-providers-imap = false
-apache-airflow-providers-qdrant = false
-apache-airflow-providers-edge3 = false
-apache-airflow-providers-neo4j = false
-apache-airflow-providers-discord = false
-apache-airflow-providers-opensearch = false
-apache-airflow-providers-samba = false
-apache-airflow-providers-arangodb = false
+apache-airflow-providers-apache-druid = false
+apache-airflow-providers-apache-flink = false
+apache-airflow-providers-apache-hdfs = false
+apache-airflow-providers-apache-hive = false
+apache-airflow-providers-apache-iceberg = false
+apache-airflow-providers-apache-impala = false
+apache-airflow-providers-apache-kafka = false
+apache-airflow-providers-apache-kylin = false
+apache-airflow-providers-apache-livy = false
+apache-airflow-providers-apache-pig = false
+apache-airflow-providers-apache-pinot = false
 apache-airflow-providers-apache-spark = false
-apache-airflow-providers-ftp = false
-apache-airflow-providers-jenkins = false
-apache-airflow-shared-listeners = false
-apache-airflow-shared-providers-discovery = false
-apache-airflow-providers-telegram = false
+apache-airflow-providers-apache-tinkerpop = false
+apache-airflow-providers-apprise = false
+apache-airflow-providers-arangodb = false
+apache-airflow-providers-asana = false
+apache-airflow-providers-atlassian-jira = false
 apache-airflow-providers-celery = false
-apache-airflow-providers-docker = false
-apache-airflow-providers-sendgrid = false
+apache-airflow-providers-clickhousedb = false
+apache-airflow-providers-cloudant = false
+apache-airflow-providers-cncf-kubernetes = false
+apache-airflow-providers-cohere = false
 apache-airflow-providers-common-ai = false
-apache-airflow = false
-apache-airflow-shared-observability = false
-apache-airflow-dev = false
+apache-airflow-providers-common-compat = false
+apache-airflow-providers-common-dataquality = false
+apache-airflow-providers-common-io = false
+apache-airflow-providers-common-messaging = false
+apache-airflow-providers-common-sql = false
+apache-airflow-providers-databricks = false
+apache-airflow-providers-datadog = false
 apache-airflow-providers-dbt-cloud = false
+apache-airflow-providers-dingding = false
+apache-airflow-providers-discord = false
+apache-airflow-providers-docker = false
+apache-airflow-providers-duckdb = false
+apache-airflow-providers-edge3 = false
+apache-airflow-providers-elasticsearch = false
+apache-airflow-providers-exasol = false
+apache-airflow-providers-fab = false
+apache-airflow-providers-facebook = false
+apache-airflow-providers-ftp = false
+apache-airflow-providers-git = false
+apache-airflow-providers-github = false
+apache-airflow-providers-google = false
+apache-airflow-providers-grpc = false
+apache-airflow-providers-hashicorp = false
+apache-airflow-providers-http = false
+apache-airflow-providers-ibm-db2 = false
+apache-airflow-providers-ibm-mq = false
+apache-airflow-providers-imap = false
+apache-airflow-providers-influxdb = false
+apache-airflow-providers-informatica = false
+apache-airflow-providers-jdbc = false
+apache-airflow-providers-jenkins = false
+apache-airflow-providers-keycloak = false
+apache-airflow-providers-microsoft-azure = false
+apache-airflow-providers-microsoft-mssql = false
+apache-airflow-providers-microsoft-psrp = false
+apache-airflow-providers-microsoft-winrm = false
+apache-airflow-providers-mongo = false
+apache-airflow-providers-mysql = false
+apache-airflow-providers-neo4j = false
+apache-airflow-providers-odbc = false
+apache-airflow-providers-openai = false
 apache-airflow-providers-openfaas = false
-apache-airflow-devel-common = false
-apache-airflow-providers-apache-cassandra = false
-apache-airflow-providers-asana = false
+apache-airflow-providers-openlineage = false
+apache-airflow-providers-opensearch = false
+apache-airflow-providers-opsgenie = false
 apache-airflow-providers-oracle = false
-apache-airflow-providers-mysql = false
-apache-airflow-providers-alibaba = false
-apache-airflow-providers-microsoft-mssql = false
-apache-airflow-providers-teradata = false
-apache-airflow-providers-jdbc = false
-apache-airflow-helm-chart = false
-apache-airflow-providers-anthropic = false
-apache-airflow-providers-common-io = false
-apache-airflow-providers-cohere = false
+apache-airflow-providers-pagerduty = false
+apache-airflow-providers-papermill = false
+apache-airflow-providers-pgvector = false
 apache-airflow-providers-pinecone = false
-apache-airflow-providers-segment = false
+apache-airflow-providers-postgres = false
+apache-airflow-providers-presto = false
+apache-airflow-providers-qdrant = false
 apache-airflow-providers-redis = false
-apache-airflow-shared-dagnode = false
-apache-airflow-providers-apache-pinot = false
-apache-airflow-providers-weaviate = false
-google-cloud-aiplatform = "2026-08-13T00:00:00Z"
-apache-airflow-providers-akeyless = false
 apache-airflow-providers-salesforce = false
+apache-airflow-providers-samba = false
+apache-airflow-providers-segment = false
+apache-airflow-providers-sendgrid = false
+apache-airflow-providers-sftp = false
+apache-airflow-providers-singularity = false
+apache-airflow-providers-slack = false
+apache-airflow-providers-smtp = false
+apache-airflow-providers-snowflake = false
+apache-airflow-providers-sqlite = false
 apache-airflow-providers-ssh = false
-apache-airflow-providers-papermill = false
-apache-airflow-providers-google = false
-pydantic-ai-skills = "2026-07-16T00:00:00Z"
-apache-airflow-providers-apache-beam = false
-apache-airflow-providers-microsoft-psrp = false
+apache-airflow-providers-standard = false
+apache-airflow-providers-tableau = false
+apache-airflow-providers-telegram = false
+apache-airflow-providers-teradata = false
+apache-airflow-providers-trino = false
 apache-airflow-providers-vertica = false
-apache-airflow-providers-apache-hdfs = false
-apache-airflow-shared-template-rendering = false
-apache-airflow-mypy = false
-apache-airflow-providers-http = false
-apache-airflow-providers-slack = false
 apache-airflow-providers-vespa = false
-apache-airflow-providers-databricks = false
-apache-airflow-shared-state = false
-apache-airflow-docker-stack = false
-apache-airflow-providers-sqlite = false
-apache-airflow-providers-ibm-mq = false
-apache-airflow-shared-module-loading = false
+apache-airflow-providers-weaviate = false
 apache-airflow-providers-yandex = false
-apache-airflow-shared-serialization = false
-apache-airflow-scripts = false
-apache-airflow-providers-exasol = false
-apache-airflow-providers-mongo = false
-apache-airflow-providers-apache-arrow = false
-apache-airflow-providers-apprise = false
-apache-airflow-providers-apache-impala = false
-apache-airflow-ctl = false
-apache-airflow-providers-github = false
-apache-airflow-providers-snowflake = false
+apache-airflow-providers-ydb = false
 apache-airflow-providers-zendesk = false
-apache-airflow-providers-presto = false
-apache-airflow-providers-airbyte = false
-apache-airflow-providers-apache-hive = false
-apache-airflow-kubernetes-tests = false
-apache-airflow-providers-grpc = false
-apache-airflow-providers-apache-druid = false
-apache-airflow-providers-apache-flink = false
-apache-airflow-providers-cncf-kubernetes = false
-apache-airflow-providers-apache-pig = false
-apache-airflow-providers-apache-tinkerpop = false
-apache-airflow-shared-timezones = false
-apache-airflow-providers-apache-iceberg = false
-apache-airflow-breeze = false
-apache-airflow-providers-opsgenie = false
-apache-airflow-providers-apache-livy = false
-apache-airflow-core = false
-apache-airflow-providers-hashicorp = false
-apache-airflow-providers-pagerduty = false
-apache-airflow-providers-datadog = false
-apache-airflow-providers-apache-kafka = false
-apache-airflow-providers-influxdb = false
-apache-airflow-providers-keycloak = false
-apache-airflow-providers-trino = false
-apache-airflow-providers-common-messaging = false
-apache-airflow-providers-standard = false
-apache-airflow-providers-singularity = false
-apache-airflow-providers-common-compat = false
-apache-airflow-ctl-tests = false
-apache-airflow-providers-tableau = false
-apache-airflow-providers-ibm-db2 = false
-apache-airflow-providers-common-sql = false
+apache-airflow-scripts = false
 apache-airflow-shared-configuration = false
-apache-airflow-providers-facebook = false
-apache-airflow-providers-ydb = false
-apache-airflow-providers-microsoft-azure = false
+apache-airflow-shared-dagnode = false
+apache-airflow-shared-listeners = false
+apache-airflow-shared-logging = false
+apache-airflow-shared-module-loading = false
+apache-airflow-shared-observability = false
 apache-airflow-shared-plugins-manager = false
+apache-airflow-shared-providers-discovery = false
 apache-airflow-shared-secrets-backend = false
 apache-airflow-shared-secrets-masker = false
-apache-airflow-providers-git = false
+apache-airflow-shared-serialization = false
+apache-airflow-shared-state = false
+apache-airflow-shared-template-rendering = false
+apache-airflow-shared-timezones = false
 apache-airflow-task-sdk = false
-apache-airflow-providers-atlassian-jira = false
-apache-airflow-providers-odbc = false
-apache-airflow-providers-postgres = false
-apache-airflow-providers-openai = false
 apache-airflow-task-sdk-integration-tests = false
-apache-airflow-providers-smtp = false
-apache-airflow-providers-dingding = false
-apache-airflow-providers-cloudant = false
-apache-airflow-providers-apache-kylin = false
+google-cloud-aiplatform = "2026-08-13T00:00:00Z"
+pydantic-ai-skills = "2026-07-16T00:00:00Z"
+typesafe-sdk = "2026-09-19T00:00:00Z"
 
 [manifest]
 members = [
@@ -4656,6 +4657,9 @@ sql = [
     { name = "apache-airflow-providers-common-sql" },
     { name = "sqlglot" },
 ]
+typesafe = [
+    { name = "typesafe-sdk" },
+]
 
 [package.dev-dependencies]
 dev = [
@@ -4708,8 +4712,9 @@ requires-dist = [
     { name = "pypdf", marker = "extra == 'pdf'", specifier = ">=4.0.0" },
     { name = "python-docx", marker = "extra == 'docx'", specifier = ">=1.0.0" 
},
     { name = "sqlglot", marker = "extra == 'sql'", specifier = ">=30.0.0" },
+    { name = "typesafe-sdk", marker = "extra == 'typesafe'", specifier = 
">=0.6.0" },
 ]
-provides-extras = ["anthropic", "bedrock", "google", "openai", "mcp", 
"code-mode", "shields", "skills", "avro", "parquet", "sql", "common-sql", 
"langchain", "llamaindex", "pdf", "docx", "git"]
+provides-extras = ["anthropic", "bedrock", "google", "openai", "typesafe", 
"mcp", "code-mode", "shields", "skills", "avro", "parquet", "sql", 
"common-sql", "langchain", "llamaindex", "pdf", "docx", "git"]
 
 [package.metadata.requires-dev]
 dev = [
@@ -25424,6 +25429,22 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/91/25/489751806bf5c95e4007f8e17409199c54d31e49ffbea07c5729b1286c8e/types_toml-0.10.8.20260518-py3-none-any.whl";,
 hash = 
"sha256:0e564ab05f6fde62a315b3b5a9b6624fda569399795d30a37e64705a70459303", size 
= 9669, upload-time = "2026-05-18T06:02:15.86Z" },
 ]
 
+[[package]]
+name = "typesafe-sdk"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple"; }
+dependencies = [
+    { name = "httpx2" },
+    { name = "pydantic" },
+    { name = "pydantic-core" },
+    { name = "tenacity" },
+    { name = "typing-extensions" },
+]
+sdist = { url = 
"https://files.pythonhosted.org/packages/28/e2/ac317772d4d5cfabf3cecdeacd83cc838523e240bb4e1cf256837e6005bb/typesafe_sdk-0.7.0.tar.gz";,
 hash = 
"sha256:930d42fd73cfed6f25bccae488ce0f30f6043eaedd8682ef59734e79ab94a876", size 
= 22235, upload-time = "2026-09-18T09:12:30.846Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/d3/2a/16f4163e6d8827ef80b2ed7a400c80688e1ebf571c10f143f792bdafc654/typesafe_sdk-0.7.0-py3-none-any.whl";,
 hash = 
"sha256:c6d2257c4b04b8d4d81cff2b55489b4188795fd44fb40b9cef26ff243b2d1080", size 
= 35352, upload-time = "2026-09-18T09:12:29.625Z" },
+]
+
 [[package]]
 name = "typing-extensions"
 version = "4.16.0"

Reply via email to