Airflow Summit 2026 is coming August 31 - September 2 in Austin, TX. Register now to secure your spot!

Source code for airflow.providers.common.ai.utils.tool_definition

# 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.
"""Version-tolerant helpers for building pydantic-ai ``ToolDefinition`` objects."""

from __future__ import annotations

import dataclasses
from typing import Any, Literal

from pydantic_ai.tools import ToolDefinition
from pydantic_core import SchemaValidator, core_schema

# ``ToolDefinition.return_schema`` is newer than the provider's pydantic-ai
# floor. Detect it once so callers can include the kwarg only when supported,
# rather than raising ``TypeError`` on older installs.
_SUPPORTS_RETURN_SCHEMA = any(f.name == "return_schema" for f in dataclasses.fields(ToolDefinition))


[docs] def return_schema_kwargs(schema: dict[str, Any]) -> dict[str, Any]: """ Return ``{"return_schema": schema}`` when pydantic-ai supports the field, else ``{}``. ``return_schema`` lets CodeMode (the Monty sandbox) render a typed function signature for a tool (``-> str``) instead of ``-> Any``, which helps the model write correct code. It has no effect outside code mode. :param schema: A JSON Schema fragment describing the tool's return value. """ if _SUPPORTS_RETURN_SCHEMA: return {"return_schema": schema} return {}
def _fragment_to_core_schema(fragment: dict[str, Any]) -> core_schema.CoreSchema: any_of = fragment.get("anyOf") if isinstance(any_of, list): choices: list[core_schema.CoreSchema | tuple[core_schema.CoreSchema, str]] = [ _fragment_to_core_schema(choice) for choice in any_of if isinstance(choice, dict) ] return core_schema.union_schema(choices) if choices else core_schema.any_schema() schema_type = fragment.get("type") if isinstance(schema_type, list): choices = [ _fragment_to_core_schema({**fragment, "type": item}) for item in schema_type if isinstance(item, str) ] return core_schema.union_schema(choices) if choices else core_schema.any_schema() match schema_type: case "string": return core_schema.str_schema() case "integer": return core_schema.int_schema() case "number": return core_schema.float_schema() case "boolean": return core_schema.bool_schema() case "null": return core_schema.none_schema() case "array": items = fragment.get("items") return core_schema.list_schema( _fragment_to_core_schema(items) if isinstance(items, dict) else None ) case "object": return _object_fragment_to_core_schema(fragment) case _: return core_schema.any_schema() def _object_fragment_to_core_schema(fragment: dict[str, Any]) -> core_schema.CoreSchema: """ Convert a JSON Schema ``object`` fragment to a core schema. A fragment with no ``properties`` key is an untyped object (e.g. from a ``dict[K, V]`` annotation): accept any dict rather than stripping its contents. When ``properties`` is present, build a typed-dict that validates each declared field recursively — nested objects are handled the same way arrays already recurse into ``items``. Undeclared keys follow native pydantic-ai: ``forbid`` for fixed signatures (so a mistyped field name becomes a bounded retry), ``allow`` only when the schema sets ``additionalProperties: true`` (methods that accept ``**kwargs``). """ if "properties" not in fragment: return core_schema.dict_schema() required = set(fragment.get("required", [])) fields = { name: core_schema.typed_dict_field(_fragment_to_core_schema(prop), required=name in required) for name, prop in fragment["properties"].items() } extra_behavior: Literal["allow", "forbid"] = ( "allow" if fragment.get("additionalProperties") is True else "forbid" ) return core_schema.typed_dict_schema(fields, extra_behavior=extra_behavior)
[docs] def build_args_validator(parameters_json_schema: dict[str, Any]) -> SchemaValidator: """Build an argument validator from the schema advertised to the model.""" return SchemaValidator(_object_fragment_to_core_schema(parameters_json_schema))

Was this entry helpful?