[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-astronomer-migrating-ai-sdk-to-common-ai":3,"mdc-mbcko1-key":56,"related-org-astronomer-migrating-ai-sdk-to-common-ai":4027,"related-repo-astronomer-migrating-ai-sdk-to-common-ai":4193},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":28,"repoUrl":29,"updatedAt":30,"license":31,"forks":32,"topics":33,"repo":51,"sourceUrl":54,"mdContent":55},"migrating-ai-sdk-to-common-ai","migrate Airflow projects to official AI provider","Migrates Airflow projects from airflow-ai-sdk to apache-airflow-providers-common-ai 0.4.0+. Use when replacing airflow-ai-sdk with the official Airflow AI provider - migrating LLM decorators (@task.llm, @task.agent, @task.llm_branch, @task.embed), switching from model strings\u002Fobjects to connection-based LLM configuration, updating imports from airflow_ai_sdk to the new provider, or upgrading an existing common-ai 0.1.x setup to 0.4.x (multimodal prompts, toolsets, embedding operators); also when common-ai provider, AIP-99, a pydanticai connection or migrating away from airflow-ai-sdk come up.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"astronomer","Astronomer","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fastronomer.png",[12,16,19,22,25],{"name":13,"slug":14,"type":15},"Airflow","airflow","tag",{"name":17,"slug":18,"type":15},"LLM","llm",{"name":20,"slug":21,"type":15},"Data Engineering","data-engineering",{"name":23,"slug":24,"type":15},"Data Pipeline","data-pipeline",{"name":26,"slug":27,"type":15},"Migration","migration",412,"https:\u002F\u002Fgithub.com\u002Fastronomer\u002Fagents","2026-04-30T05:38:54.464262",null,55,[34,35,36,37,14,38,39,40,41,21,42,43,18,44,45,46,47,48,49,50],"agentic-workflow","agents","ai","ai-agents","apache-airflow","claude","cursor","dag","data-pipelines","dbt","mcp","orchestrator","skills","workflow-automation","workflow-management","workflow-orchestration","workflows",{"repoUrl":29,"stars":28,"forks":32,"topics":52,"description":53},[34,35,36,37,14,38,39,40,41,21,42,43,18,44,45,46,47,48,49,50],"AI agent tooling for data engineering workflows.","https:\u002F\u002Fgithub.com\u002Fastronomer\u002Fagents\u002Ftree\u002FHEAD\u002Fskills\u002Fmigrating-ai-sdk-to-common-ai","---\nname: migrating-ai-sdk-to-common-ai\ndescription: Migrates Airflow projects from airflow-ai-sdk to apache-airflow-providers-common-ai 0.4.0+. Use when replacing airflow-ai-sdk with the official Airflow AI provider - migrating LLM decorators (@task.llm, @task.agent, @task.llm_branch, @task.embed), switching from model strings\u002Fobjects to connection-based LLM configuration, updating imports from airflow_ai_sdk to the new provider, or upgrading an existing common-ai 0.1.x setup to 0.4.x (multimodal prompts, toolsets, embedding operators); also when common-ai provider, AIP-99, a pydanticai connection or migrating away from airflow-ai-sdk come up.\n---\n\n# Migrate airflow-ai-sdk to apache-airflow-providers-common-ai\n\nThis skill migrates Airflow projects from `airflow-ai-sdk` to `apache-airflow-providers-common-ai` (target **0.4.0+**), the official Airflow AI provider built on PydanticAI. It also covers upgrading projects already on common-ai 0.1.x, since several capabilities (multimodal prompts, `toolsets`, embedding operators, structured-output XCom behavior) changed between 0.1.0 and 0.4.0.\n\n> **CRITICAL**: The new provider requires **Airflow 3.0+** and (for 0.4.0) **pydantic-ai-slim >= 1.71.0**. The API surface has changed: LLM configuration moves from code (model strings\u002Fobjects) to Airflow connections (`pydanticai` type). There is no `@task.embed` in the new provider; embeddings move to the LlamaIndex integration or a plain `@task` (see Step 3).\n\n## Before starting\n\nUse the Grep tool with the pattern below to inventory everything that needs to migrate:\n\n```\nairflow_ai_sdk|airflow-ai-sdk|ai_sdk|@task\\.llm|@task\\.agent|@task\\.llm_branch|@task\\.embed\n```\n\nFrom the results, capture:\n\n1. All files importing `airflow-ai-sdk` \u002F `airflow_ai_sdk`\n2. Which decorators are in use: `@task.llm`, `@task.agent`, `@task.llm_branch`, `@task.embed`\n3. The model configuration pattern (string names like `\"gpt-5\"`, or `OpenAIModel(...)` objects)\n4. Any `airflow_ai_sdk.BaseModel` subclasses used as `output_type`\n\nUse this inventory to drive the steps below.\n\n---\n\n## Step 1: Update requirements.txt\n\n**Remove:**\n```\nairflow-ai-sdk[openai]\n# or any variant: airflow-ai-sdk[openai]==0.1.7, airflow-ai-sdk[anthropic], etc.\n```\n\n**Add:**\n```\napache-airflow-providers-common-ai[openai]>=0.4.0\n```\n\nUse the latest available 0.x version unless the user has pinned a specific one. Available extras (0.4.0): `[openai]`, `[anthropic]`, `[google]`, `[bedrock]`, `[llamaindex]`, `[langchain]`, `[mcp]`, plus file-format extras (`[pdf]`, `[docx]`, `[parquet]`, `[avro]`) for `DocumentLoaderOperator` and `[sql]`\u002F`[common-sql]` for the SQL operators. There are no `[groq]`\u002F`[mistral]` extras; for those providers install the matching `pydantic-ai-slim` extra yourself.\n\nAdd `[llamaindex]` if the project migrates `@task.embed` to the `LlamaIndexEmbeddingOperator` (recommended, see Step 3). In that case `sentence-transformers` and `torch` can usually be **removed**, which shrinks the image considerably. Keep them only if the project stays on local sentence-transformers embeddings via plain `@task`.\n\n---\n\n## Step 2: Create PydanticAI connection\n\nThe new provider uses an Airflow connection instead of model strings or objects in code.\n\n**Connection type:** `pydanticai`\n**Default connection ID:** `pydanticai_default`\n\n### Via environment variable (.env)\n\n```bash\nAIRFLOW_CONN_PYDANTICAI_DEFAULT='{\n    \"conn_type\": \"pydanticai\",\n    \"password\": \"\u003Capi-key>\",\n    \"extra\": {\n        \"model\": \"\u003Cprovider>:\u003Cmodel-name>\"\n    }\n}'\n```\n\n### Model format\n\nThe model field uses `provider:model` format:\n\n| Provider | Example model value |\n|----------|-------------------|\n| OpenAI | `openai:gpt-5` |\n| Anthropic | `anthropic:claude-sonnet-4-20250514` |\n| Google | `google:gemini-2.5-pro` |\n| Groq | `groq:llama-3.3-70b-versatile` |\n| Mistral | `mistral:mistral-large-latest` |\n| Bedrock | `bedrock:us.anthropic.claude-sonnet-4-20250514-v1:0` |\n\n### Custom endpoints (Ollama, vLLM, Snowflake Cortex, etc.)\n\nSet `host` to the base URL:\n```bash\nAIRFLOW_CONN_PYDANTICAI_CORTEX='{\n    \"conn_type\": \"pydanticai\",\n    \"password\": \"\u003Capi-key>\",\n    \"host\": \"https:\u002F\u002Fmy-endpoint.com\u002Fv1\",\n    \"extra\": {\n        \"model\": \"openai:\u003Cmodel-name>\"\n    }\n}'\n```\n\nUse the `openai:` prefix for any OpenAI-compatible API, regardless of the actual provider.\n\n### Connection ID convention\n\nThe env var name determines the connection ID:\n- `AIRFLOW_CONN_PYDANTICAI_DEFAULT` creates `pydanticai_default`\n- `AIRFLOW_CONN_PYDANTICAI_CORTEX` creates `pydanticai_cortex`\n\n### Model resolution priority\n\n1. `model_id` parameter on the decorator\u002Foperator (highest)\n2. `model` in connection's extra JSON (fallback)\n\n### Other connection types (0.4.0)\n\nBesides `pydanticai`, the provider registers vendor-specific connection types: `pydanticai-azure` (Azure OpenAI: host = endpoint, extra `api_version`), `pydanticai-bedrock` (AWS credentials\u002Fregion in extra), and `pydanticai-vertex` (GCP project\u002Flocation in extra). The LlamaIndex and LangChain hooks read API key\u002Fhost\u002Fextra from whatever connection ID they are given, so a single `pydanticai_default` connection can serve LLM calls **and** embeddings: one API key entry for the whole project.\n\n---\n\n## Step 3: Migrate decorators\n\n### @task.llm\n\n```python\n# BEFORE (airflow-ai-sdk)\nimport airflow_ai_sdk as ai_sdk\n\nclass MyOutput(ai_sdk.BaseModel):\n    field: str\n\n@task.llm(\n    model=\"gpt-5\",                    # or model=OpenAIModel(...)\n    system_prompt=\"You are helpful.\",\n    output_type=MyOutput,\n)\ndef my_task(text: str) -> str:\n    return text\n\n# AFTER (apache-airflow-providers-common-ai)\nfrom pydantic import BaseModel\n\nclass MyOutput(BaseModel):\n    field: str\n\n@task.llm(\n    llm_conn_id=\"pydanticai_default\",  # Airflow connection ID\n    system_prompt=\"You are helpful.\",\n    output_type=MyOutput,\n)\ndef my_task(text: str) -> str:\n    return text\n```\n\n**Parameter mapping:**\n\n| airflow-ai-sdk | common-ai provider | Notes |\n|----------------|-------------------|-------|\n| `model=\"gpt-5\"` | `llm_conn_id=\"pydanticai_default\"` | Model specified in connection |\n| `model=OpenAIModel(...)` | `llm_conn_id=\"pydanticai_default\"` | Model + endpoint in connection |\n| `system_prompt=\"...\"` | `system_prompt=\"...\"` | Unchanged |\n| `output_type=MyModel` | `output_type=MyModel` | Unchanged |\n| `result_type=MyModel` | `output_type=MyModel` | `result_type` was already deprecated |\n| (not available) | `model_id=\"openai:gpt-5\"` | Override connection's model |\n| (not available) | `require_approval=True` | Built-in HITL review |\n| (not available) | `agent_params={...}` | Extra kwargs for pydantic-ai Agent |\n| (not available) | `serialize_output=True` | Force dict shape for BaseModel output |\n\n**Multimodal prompts (0.4.0+):** the translation function may return a `Sequence[UserContent]` instead of a string, e.g. for vision:\n\n```python\n@task.llm(llm_conn_id=\"pydanticai_default\", system_prompt=\"...\", output_type=ReviewAnalysis)\ndef analyze(text: str, image_path: str | None = None):\n    if image_path:\n        with open(image_path, \"rb\") as f:\n            return [text, BinaryContent(data=f.read(), media_type=\"image\u002Fjpeg\")]\n    return text\n```\n\nThis matches the old airflow-ai-sdk vision pattern, so vision code migrates unchanged. Note: common-ai **0.1.x only accepted strings** — if a project disabled vision to migrate to 0.1.0, re-enable it when bumping to 0.4.0. Non-string prompts are incompatible with `require_approval=True` \u002F `enable_hitl_review=True` (both render the prompt as text).\n\n**Structured output via XCom (0.4.0 behavior change):** with `output_type=\u003CBaseModel subclass>`, the model **instance** flows through XCom on Airflow cores whose task SDK has `SUPPORTS_OPERATOR_DESERIALIZATION_WALKER` (attribute access downstream); on older cores (including Astro Runtime 3.2 task SDK 1.2.x) the provider automatically dumps to a **dict** (subscript access). Check which shape arrives at runtime before choosing attribute vs dict access downstream, or set `serialize_output=True` to force the dict shape everywhere. The `output_type` class must be defined at **module scope** (nested classes cannot be deserialized from XCom).\n\n### @task.llm_branch\n\n```python\n# BEFORE\n@task.llm_branch(\n    model=\"gpt-5\",\n    system_prompt=\"Choose a team...\",\n    allow_multiple_branches=False,\n)\ndef route(text: str) -> str:\n    return text\n\n# AFTER\n@task.llm_branch(\n    llm_conn_id=\"pydanticai_default\",\n    system_prompt=\"Choose a team...\",\n    allow_multiple_branches=False,    # same parameter, unchanged\n)\ndef route(text: str) -> str:\n    return text\n```\n\nOnly change: `model=` becomes `llm_conn_id=`.\n\n### @task.agent\n\nThis has the biggest API change. The Agent is no longer pre-built in user code.\n\n```python\n# BEFORE (airflow-ai-sdk) - Agent built at module level\nfrom pydantic_ai import Agent\n\nmy_agent = Agent(\n    \"gpt-5\",\n    system_prompt=\"You are a research assistant.\",\n    tools=[search_tool, lookup_tool],\n)\n\n@task.agent(agent=my_agent)\ndef research(question: str) -> str:\n    return question\n\n# AFTER (common-ai provider) - No Agent object, config via parameters\nfrom pydantic_ai.toolsets import FunctionToolset\n\n@task.agent(\n    llm_conn_id=\"pydanticai_default\",\n    system_prompt=\"You are a research assistant.\",\n    toolsets=[FunctionToolset(tools=[search_tool, lookup_tool])],\n)\ndef research(question: str) -> str:\n    return question\n```\n\n**Parameter mapping:**\n\n| airflow-ai-sdk | common-ai provider | Notes |\n|----------------|-------------------|-------|\n| `agent=Agent(model, ...)` | `llm_conn_id=\"...\"` | Model from connection |\n| Agent's `system_prompt` | `system_prompt=\"...\"` | Now a decorator param |\n| Agent's `tools=[...]` | `toolsets=[FunctionToolset(tools=[...])]` | Preferred: gets automatic tool-call logging |\n| Agent's `tools=[...]` | `agent_params={\"tools\": [...]}` | Also works, but no tool-call logging |\n| Agent's `output_type` | `output_type=MyModel` | Now a decorator param |\n| (not available) | `durable=True` | Step-level caching (needs `[common.ai] durable_cache_path`) |\n| (not available) | `enable_hitl_review=True` | Iterative human review loop (see below) |\n\n**Key insight:** Everything that was configured on the `Agent()` constructor now goes into either a top-level decorator parameter or `agent_params`. The `agent_params` dict is passed directly to pydantic-ai's `Agent` constructor. Prefer `toolsets` over `agent_params[\"tools\"]`: the operator wraps each toolset in a `LoggingToolset`, so every tool call appears in the task log with timing.\n\n**enable_hitl_review behavior:** the task generates a first draft, then **blocks** until a human acts. The reviewer uses the **HITL Review** tab\u002Fextra link on the task instance (chat UI from the provider's auto-registered `hitl_review` plugin) to request changes (agent regenerates with the feedback in its message history) or approve. Constraints: requires a string prompt, incompatible with `durable=True`, and the final (possibly regenerated) output is what flows to XCom. Warn users that the Dag run waits indefinitely at this task unless `hitl_timeout` is set. For headless testing, the plugin exposes REST endpoints under `\u002Fhitl-review`: `GET \u002Fsessions\u002Ffind`, `POST \u002Fsessions\u002Ffeedback`, `POST \u002Fsessions\u002Fapprove`, `POST \u002Fsessions\u002Freject` (query params `dag_id`, `task_id`, `run_id`, `map_index`).\n\n### @task.embed (NO EQUIVALENT — three replacement options)\n\nThe new provider does NOT include an embed decorator. Pick the replacement based on what the project needs:\n\n**Option A (recommended): `LlamaIndexEmbeddingOperator`** (0.4.0, `[llamaindex]` extra). Connection-based, one task embeds the whole document list, and with `persist_dir` the resulting vector index is persisted for retrieval (pairs with `LlamaIndexRetrievalOperator`):\n\n```python\nfrom airflow.providers.common.ai.operators.llamaindex_embedding import LlamaIndexEmbeddingOperator\n\n_embeddings = LlamaIndexEmbeddingOperator(\n    task_id=\"create_embeddings\",\n    documents=[{\"text\": \"...\", \"metadata\": {\"id\": 1}}, ...],  # templated, accepts XComArg\n    llm_conn_id=\"pydanticai_default\",   # reuses the same connection (API key only)\n    embed_model=\"text-embedding-3-small\",\n    persist_dir=f\"{AIRFLOW_HOME}\u002Finclude\u002Fmy_index\",  # optional; local path or s3:\u002F\u002F, gs:\u002F\u002F, ...\n)\n```\n\nThe operator returns `{\"chunks\": [{\"text\", \"metadata\", \"vector\"}], ...}`. Put a stable key into each document's `metadata` — it round-trips through chunking, so vectors can be mapped back to source records.\n\n**Option B: `LlamaIndexHook` for raw vectors** (no operator, no persisted index). Shortest path when vectors go straight to a database:\n\n```python\n@task\ndef create_embeddings(rows):\n    from airflow.providers.common.ai.hooks.llamaindex import LlamaIndexHook\n    embed_model = LlamaIndexHook(\n        llm_conn_id=\"pydanticai_default\",\n        embed_model=\"text-embedding-3-small\",\n    ).get_embedding_model()\n    vectors = embed_model.get_text_embedding_batch([r[\"text\"] for r in rows])\n    return list(zip([r[\"id\"] for r in rows], vectors))\n```\n\n**Option C: plain `@task` with sentence-transformers** (keeps the old local\u002Foffline behavior, no API cost; requires keeping `sentence-transformers` + `torch` in requirements):\n\n```python\n@task\ndef embed_texts(texts: list[str]) -> list[list[float]]:\n    from sentence_transformers import SentenceTransformer\n    model = SentenceTransformer(\"all-MiniLM-L6-v2\")\n    return model.encode(texts, normalize_embeddings=True).tolist()\n```\n\nNote on dimensions: switching from `all-MiniLM-L6-v2` (384) to `text-embedding-3-small` (1536) changes vector size — existing stored embeddings must be regenerated, and fixed-size vector columns (e.g. pgvector `vector(384)`) need a schema change. Embed all texts in one task\u002Fbatch call rather than `.expand()` per text: batching is one API round-trip and avoids per-task model loading.\n\n---\n\n## Step 4: Update imports\n\n| Old import | New import |\n|-----------|-----------|\n| `import airflow_ai_sdk as ai_sdk` | Remove entirely |\n| `from airflow_ai_sdk import BaseModel` | `from pydantic import BaseModel` |\n| `from airflow_ai_sdk.models.base import BaseModel` | `from pydantic import BaseModel` |\n| `class Foo(ai_sdk.BaseModel):` | `class Foo(BaseModel):` |\n| `from pydantic_ai import Agent` | Remove if Agent was only used for `@task.agent` |\n| `from pydantic_ai.models.openai import OpenAIModel` | Remove (model config in connection now) |\n| (new) | `from pydantic_ai.toolsets import FunctionToolset` for `@task.agent` toolsets |\n\nThe `@task.llm`, `@task.agent`, `@task.llm_branch` decorators are auto-registered by the provider. No explicit import needed beyond `from airflow.sdk import task`.\n\n`pydantic_ai` imports for non-decorator usage (e.g., `BinaryContent` for multimodal) are still valid since the new provider depends on `pydantic-ai-slim` (>= 1.71.0 for provider 0.4.0).\n\n---\n\n## Step 5: Update connections.yaml (if used for local testing)\n\n```yaml\npydanticai_default:\n  conn_type: pydanticai\n  password: \u003Capi-key>\n  extra:\n    model: \"openai:gpt-5\"\n```\n\nFor custom endpoints:\n```yaml\npydanticai_cortex:\n  conn_type: pydanticai\n  password: \u003Capi-key>\n  host: https:\u002F\u002Fmy-endpoint.com\u002Fv1\n  extra:\n    model: \"openai:llama3.1-8b\"\n```\n\n---\n\n## Step 6: Clean up env vars\n\nThe new provider reads model config from the `pydanticai` connection, so env vars that previously fed the model in code are usually redundant. Before removing any of them, grep the project (and any sibling scripts\u002Fservices) to confirm nothing else still references them:\n\n```\nOPENAI_API_KEY|OPENAI_BASE_URL|ANTHROPIC_API_KEY|GOOGLE_API_KEY\n```\n\nCandidates for removal **only if no other code references them**:\n- `OPENAI_API_KEY` (now in the pydanticai connection's password field)\n- `OPENAI_BASE_URL` (now in the connection's host field)\n- Custom model name vars (now in the connection's extra.model)\n\nIf anything outside the migrated DAGs still uses them (other DAGs not yet migrated, helper scripts, non-Airflow services sharing the `.env`), leave them in place.\n\n**Keep** `AIRFLOW_CONN_*` env vars for all connections.\n\n---\n\n## Step 7: Verify\n\nAfter migration, grep the codebase to confirm no stale references remain:\n\n```\nairflow_ai_sdk|airflow-ai-sdk|ai_sdk\\.BaseModel|from pydantic_ai import Agent|from pydantic_ai.models\n```\n\nVerify:\n- [ ] No imports from `airflow_ai_sdk`\n- [ ] No `Agent()` objects created for `@task.agent` (unless used outside decorators)\n- [ ] No `model=` parameter on LLM decorators (should be `llm_conn_id=`)\n- [ ] All `@task.embed` replaced (LlamaIndex operator\u002Fhook or plain `@task`); stored embeddings regenerated if the model\u002Fdimensions changed\n- [ ] Vision translation functions return `[text, BinaryContent(...)]` again if they were string-only-restricted under common-ai 0.1.x\n- [ ] Downstream consumers of `output_type=BaseModel` results use the XCom shape that actually arrives (dict on older cores, instance on newer; `serialize_output=True` pins it)\n- [ ] `pydanticai` connection configured in `.env` or connections.yaml\n- [ ] `requirements.txt` has `apache-airflow-providers-common-ai[...]` instead of `airflow-ai-sdk[...]`; `torch`\u002F`sentence-transformers` removed if no longer used\n- [ ] Run the Dags end-to-end: tasks with `enable_hitl_review=True` or `require_approval=True` wait for human input, so the test plan must include acting on them (UI tab or `\u002Fhitl-review` REST)\n\n---\n\n## Quick reference: New features in common-ai provider\n\nThese features are available after migration but have no airflow-ai-sdk equivalent:\n\n| Feature | Parameter \u002F API | Since | Description |\n|---------|-----------------|-------|-------------|\n| HITL approval | `require_approval=True` on `@task.llm` | 0.1.0 | Pause for human review before returning |\n| HITL review loop | `enable_hitl_review=True` on `@task.agent` | 0.1.0 | Iterative review with regeneration (chat UI via `hitl_review` plugin) |\n| Durable execution | `durable=True` on `@task.agent` | 0.1.0 | Step-level caching for resilience |\n| Tool logging | `enable_tool_logging=True` on `@task.agent` | 0.1.0 | INFO-level tool call logs (default: on; requires `toolsets`) |\n| Model override | `model_id=\"openai:gpt-5\"` | 0.1.0 | Override connection's model per-task |\n| File analysis | `@task.llm_file_analysis` | 0.1.0 | Analyze files\u002Fimages via ObjectStoragePath |\n| NL-to-SQL | `@task.llm_sql` | 0.1.0 | Generate SQL from natural language |\n| Multimodal prompts | Translation function returns `Sequence[UserContent]` | 0.4.0 | Vision and other binary content in `@task.llm` \u002F `@task.agent` \u002F `@task.llm_branch` |\n| Pydantic instance via XCom | `output_type=BaseModel` (with `serialize_output` opt-out) | 0.4.0 | Instance flows through XCom on capable cores; dict fallback otherwise |\n| Embeddings | `LlamaIndexEmbeddingOperator` (+ `persist_dir`) | 0.4.0 | Connection-based embeddings + persisted vector index |\n| Retrieval | `LlamaIndexRetrievalOperator` | 0.4.0 | Top-k similarity search over a persisted index |\n",{"data":57,"body":58},{"name":4,"description":6},{"type":59,"children":60},"root",[61,70,109,161,168,173,185,190,285,290,294,300,308,317,325,334,465,521,524,530,535,562,569,666,672,685,815,821,834,917,930,936,941,975,981,1006,1012,1070,1073,1079,1084,1318,1326,1568,1586,1640,1666,1727,1732,1867,1887,1892,1897,2078,2085,2288,2352,2465,2471,2476,2514,2591,2612,2630,2709,2740,2786,2823,2826,2832,2998,3029,3055,3058,3064,3159,3164,3264,3267,3273,3285,3294,3305,3335,3348,3365,3368,3374,3379,3388,3393,3620,3623,3629,3634,4021],{"type":62,"tag":63,"props":64,"children":66},"element","h1",{"id":65},"migrate-airflow-ai-sdk-to-apache-airflow-providers-common-ai",[67],{"type":68,"value":69},"text","Migrate airflow-ai-sdk to apache-airflow-providers-common-ai",{"type":62,"tag":71,"props":72,"children":73},"p",{},[74,76,83,85,91,93,99,101,107],{"type":68,"value":75},"This skill migrates Airflow projects from ",{"type":62,"tag":77,"props":78,"children":80},"code",{"className":79},[],[81],{"type":68,"value":82},"airflow-ai-sdk",{"type":68,"value":84}," to ",{"type":62,"tag":77,"props":86,"children":88},{"className":87},[],[89],{"type":68,"value":90},"apache-airflow-providers-common-ai",{"type":68,"value":92}," (target ",{"type":62,"tag":94,"props":95,"children":96},"strong",{},[97],{"type":68,"value":98},"0.4.0+",{"type":68,"value":100},"), the official Airflow AI provider built on PydanticAI. It also covers upgrading projects already on common-ai 0.1.x, since several capabilities (multimodal prompts, ",{"type":62,"tag":77,"props":102,"children":104},{"className":103},[],[105],{"type":68,"value":106},"toolsets",{"type":68,"value":108},", embedding operators, structured-output XCom behavior) changed between 0.1.0 and 0.4.0.",{"type":62,"tag":110,"props":111,"children":112},"blockquote",{},[113],{"type":62,"tag":71,"props":114,"children":115},{},[116,121,123,128,130,135,137,143,145,151,153,159],{"type":62,"tag":94,"props":117,"children":118},{},[119],{"type":68,"value":120},"CRITICAL",{"type":68,"value":122},": The new provider requires ",{"type":62,"tag":94,"props":124,"children":125},{},[126],{"type":68,"value":127},"Airflow 3.0+",{"type":68,"value":129}," and (for 0.4.0) ",{"type":62,"tag":94,"props":131,"children":132},{},[133],{"type":68,"value":134},"pydantic-ai-slim >= 1.71.0",{"type":68,"value":136},". The API surface has changed: LLM configuration moves from code (model strings\u002Fobjects) to Airflow connections (",{"type":62,"tag":77,"props":138,"children":140},{"className":139},[],[141],{"type":68,"value":142},"pydanticai",{"type":68,"value":144}," type). There is no ",{"type":62,"tag":77,"props":146,"children":148},{"className":147},[],[149],{"type":68,"value":150},"@task.embed",{"type":68,"value":152}," in the new provider; embeddings move to the LlamaIndex integration or a plain ",{"type":62,"tag":77,"props":154,"children":156},{"className":155},[],[157],{"type":68,"value":158},"@task",{"type":68,"value":160}," (see Step 3).",{"type":62,"tag":162,"props":163,"children":165},"h2",{"id":164},"before-starting",[166],{"type":68,"value":167},"Before starting",{"type":62,"tag":71,"props":169,"children":170},{},[171],{"type":68,"value":172},"Use the Grep tool with the pattern below to inventory everything that needs to migrate:",{"type":62,"tag":174,"props":175,"children":179},"pre",{"className":176,"code":178,"language":68},[177],"language-text","airflow_ai_sdk|airflow-ai-sdk|ai_sdk|@task\\.llm|@task\\.agent|@task\\.llm_branch|@task\\.embed\n",[180],{"type":62,"tag":77,"props":181,"children":183},{"__ignoreMap":182},"",[184],{"type":68,"value":178},{"type":62,"tag":71,"props":186,"children":187},{},[188],{"type":68,"value":189},"From the results, capture:",{"type":62,"tag":191,"props":192,"children":193},"ol",{},[194,213,245,266],{"type":62,"tag":195,"props":196,"children":197},"li",{},[198,200,205,207],{"type":68,"value":199},"All files importing ",{"type":62,"tag":77,"props":201,"children":203},{"className":202},[],[204],{"type":68,"value":82},{"type":68,"value":206}," \u002F ",{"type":62,"tag":77,"props":208,"children":210},{"className":209},[],[211],{"type":68,"value":212},"airflow_ai_sdk",{"type":62,"tag":195,"props":214,"children":215},{},[216,218,224,226,232,233,239,240],{"type":68,"value":217},"Which decorators are in use: ",{"type":62,"tag":77,"props":219,"children":221},{"className":220},[],[222],{"type":68,"value":223},"@task.llm",{"type":68,"value":225},", ",{"type":62,"tag":77,"props":227,"children":229},{"className":228},[],[230],{"type":68,"value":231},"@task.agent",{"type":68,"value":225},{"type":62,"tag":77,"props":234,"children":236},{"className":235},[],[237],{"type":68,"value":238},"@task.llm_branch",{"type":68,"value":225},{"type":62,"tag":77,"props":241,"children":243},{"className":242},[],[244],{"type":68,"value":150},{"type":62,"tag":195,"props":246,"children":247},{},[248,250,256,258,264],{"type":68,"value":249},"The model configuration pattern (string names like ",{"type":62,"tag":77,"props":251,"children":253},{"className":252},[],[254],{"type":68,"value":255},"\"gpt-5\"",{"type":68,"value":257},", or ",{"type":62,"tag":77,"props":259,"children":261},{"className":260},[],[262],{"type":68,"value":263},"OpenAIModel(...)",{"type":68,"value":265}," objects)",{"type":62,"tag":195,"props":267,"children":268},{},[269,271,277,279],{"type":68,"value":270},"Any ",{"type":62,"tag":77,"props":272,"children":274},{"className":273},[],[275],{"type":68,"value":276},"airflow_ai_sdk.BaseModel",{"type":68,"value":278}," subclasses used as ",{"type":62,"tag":77,"props":280,"children":282},{"className":281},[],[283],{"type":68,"value":284},"output_type",{"type":62,"tag":71,"props":286,"children":287},{},[288],{"type":68,"value":289},"Use this inventory to drive the steps below.",{"type":62,"tag":291,"props":292,"children":293},"hr",{},[],{"type":62,"tag":162,"props":295,"children":297},{"id":296},"step-1-update-requirementstxt",[298],{"type":68,"value":299},"Step 1: Update requirements.txt",{"type":62,"tag":71,"props":301,"children":302},{},[303],{"type":62,"tag":94,"props":304,"children":305},{},[306],{"type":68,"value":307},"Remove:",{"type":62,"tag":174,"props":309,"children":312},{"className":310,"code":311,"language":68},[177],"airflow-ai-sdk[openai]\n# or any variant: airflow-ai-sdk[openai]==0.1.7, airflow-ai-sdk[anthropic], etc.\n",[313],{"type":62,"tag":77,"props":314,"children":315},{"__ignoreMap":182},[316],{"type":68,"value":311},{"type":62,"tag":71,"props":318,"children":319},{},[320],{"type":62,"tag":94,"props":321,"children":322},{},[323],{"type":68,"value":324},"Add:",{"type":62,"tag":174,"props":326,"children":329},{"className":327,"code":328,"language":68},[177],"apache-airflow-providers-common-ai[openai]>=0.4.0\n",[330],{"type":62,"tag":77,"props":331,"children":332},{"__ignoreMap":182},[333],{"type":68,"value":328},{"type":62,"tag":71,"props":335,"children":336},{},[337,339,345,346,352,353,359,360,366,367,373,374,380,381,387,389,395,396,402,403,409,410,416,418,424,426,432,434,440,442,448,449,455,457,463],{"type":68,"value":338},"Use the latest available 0.x version unless the user has pinned a specific one. Available extras (0.4.0): ",{"type":62,"tag":77,"props":340,"children":342},{"className":341},[],[343],{"type":68,"value":344},"[openai]",{"type":68,"value":225},{"type":62,"tag":77,"props":347,"children":349},{"className":348},[],[350],{"type":68,"value":351},"[anthropic]",{"type":68,"value":225},{"type":62,"tag":77,"props":354,"children":356},{"className":355},[],[357],{"type":68,"value":358},"[google]",{"type":68,"value":225},{"type":62,"tag":77,"props":361,"children":363},{"className":362},[],[364],{"type":68,"value":365},"[bedrock]",{"type":68,"value":225},{"type":62,"tag":77,"props":368,"children":370},{"className":369},[],[371],{"type":68,"value":372},"[llamaindex]",{"type":68,"value":225},{"type":62,"tag":77,"props":375,"children":377},{"className":376},[],[378],{"type":68,"value":379},"[langchain]",{"type":68,"value":225},{"type":62,"tag":77,"props":382,"children":384},{"className":383},[],[385],{"type":68,"value":386},"[mcp]",{"type":68,"value":388},", plus file-format extras (",{"type":62,"tag":77,"props":390,"children":392},{"className":391},[],[393],{"type":68,"value":394},"[pdf]",{"type":68,"value":225},{"type":62,"tag":77,"props":397,"children":399},{"className":398},[],[400],{"type":68,"value":401},"[docx]",{"type":68,"value":225},{"type":62,"tag":77,"props":404,"children":406},{"className":405},[],[407],{"type":68,"value":408},"[parquet]",{"type":68,"value":225},{"type":62,"tag":77,"props":411,"children":413},{"className":412},[],[414],{"type":68,"value":415},"[avro]",{"type":68,"value":417},") for ",{"type":62,"tag":77,"props":419,"children":421},{"className":420},[],[422],{"type":68,"value":423},"DocumentLoaderOperator",{"type":68,"value":425}," and ",{"type":62,"tag":77,"props":427,"children":429},{"className":428},[],[430],{"type":68,"value":431},"[sql]",{"type":68,"value":433},"\u002F",{"type":62,"tag":77,"props":435,"children":437},{"className":436},[],[438],{"type":68,"value":439},"[common-sql]",{"type":68,"value":441}," for the SQL operators. There are no ",{"type":62,"tag":77,"props":443,"children":445},{"className":444},[],[446],{"type":68,"value":447},"[groq]",{"type":68,"value":433},{"type":62,"tag":77,"props":450,"children":452},{"className":451},[],[453],{"type":68,"value":454},"[mistral]",{"type":68,"value":456}," extras; for those providers install the matching ",{"type":62,"tag":77,"props":458,"children":460},{"className":459},[],[461],{"type":68,"value":462},"pydantic-ai-slim",{"type":68,"value":464}," extra yourself.",{"type":62,"tag":71,"props":466,"children":467},{},[468,470,475,477,482,484,490,492,498,499,505,507,512,514,519],{"type":68,"value":469},"Add ",{"type":62,"tag":77,"props":471,"children":473},{"className":472},[],[474],{"type":68,"value":372},{"type":68,"value":476}," if the project migrates ",{"type":62,"tag":77,"props":478,"children":480},{"className":479},[],[481],{"type":68,"value":150},{"type":68,"value":483}," to the ",{"type":62,"tag":77,"props":485,"children":487},{"className":486},[],[488],{"type":68,"value":489},"LlamaIndexEmbeddingOperator",{"type":68,"value":491}," (recommended, see Step 3). In that case ",{"type":62,"tag":77,"props":493,"children":495},{"className":494},[],[496],{"type":68,"value":497},"sentence-transformers",{"type":68,"value":425},{"type":62,"tag":77,"props":500,"children":502},{"className":501},[],[503],{"type":68,"value":504},"torch",{"type":68,"value":506}," can usually be ",{"type":62,"tag":94,"props":508,"children":509},{},[510],{"type":68,"value":511},"removed",{"type":68,"value":513},", which shrinks the image considerably. Keep them only if the project stays on local sentence-transformers embeddings via plain ",{"type":62,"tag":77,"props":515,"children":517},{"className":516},[],[518],{"type":68,"value":158},{"type":68,"value":520},".",{"type":62,"tag":291,"props":522,"children":523},{},[],{"type":62,"tag":162,"props":525,"children":527},{"id":526},"step-2-create-pydanticai-connection",[528],{"type":68,"value":529},"Step 2: Create PydanticAI connection",{"type":62,"tag":71,"props":531,"children":532},{},[533],{"type":68,"value":534},"The new provider uses an Airflow connection instead of model strings or objects in code.",{"type":62,"tag":71,"props":536,"children":537},{},[538,543,545,550,555,556],{"type":62,"tag":94,"props":539,"children":540},{},[541],{"type":68,"value":542},"Connection type:",{"type":68,"value":544}," ",{"type":62,"tag":77,"props":546,"children":548},{"className":547},[],[549],{"type":68,"value":142},{"type":62,"tag":94,"props":551,"children":552},{},[553],{"type":68,"value":554},"Default connection ID:",{"type":68,"value":544},{"type":62,"tag":77,"props":557,"children":559},{"className":558},[],[560],{"type":68,"value":561},"pydanticai_default",{"type":62,"tag":563,"props":564,"children":566},"h3",{"id":565},"via-environment-variable-env",[567],{"type":68,"value":568},"Via environment variable (.env)",{"type":62,"tag":174,"props":570,"children":574},{"className":571,"code":572,"language":573,"meta":182,"style":182},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","AIRFLOW_CONN_PYDANTICAI_DEFAULT='{\n    \"conn_type\": \"pydanticai\",\n    \"password\": \"\u003Capi-key>\",\n    \"extra\": {\n        \"model\": \"\u003Cprovider>:\u003Cmodel-name>\"\n    }\n}'\n","bash",[575],{"type":62,"tag":77,"props":576,"children":577},{"__ignoreMap":182},[578,607,616,625,634,643,652],{"type":62,"tag":579,"props":580,"children":583},"span",{"class":581,"line":582},"line",1,[584,590,596,601],{"type":62,"tag":579,"props":585,"children":587},{"style":586},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[588],{"type":68,"value":589},"AIRFLOW_CONN_PYDANTICAI_DEFAULT",{"type":62,"tag":579,"props":591,"children":593},{"style":592},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[594],{"type":68,"value":595},"=",{"type":62,"tag":579,"props":597,"children":598},{"style":592},[599],{"type":68,"value":600},"'",{"type":62,"tag":579,"props":602,"children":604},{"style":603},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[605],{"type":68,"value":606},"{\n",{"type":62,"tag":579,"props":608,"children":610},{"class":581,"line":609},2,[611],{"type":62,"tag":579,"props":612,"children":613},{"style":603},[614],{"type":68,"value":615},"    \"conn_type\": \"pydanticai\",\n",{"type":62,"tag":579,"props":617,"children":619},{"class":581,"line":618},3,[620],{"type":62,"tag":579,"props":621,"children":622},{"style":603},[623],{"type":68,"value":624},"    \"password\": \"\u003Capi-key>\",\n",{"type":62,"tag":579,"props":626,"children":628},{"class":581,"line":627},4,[629],{"type":62,"tag":579,"props":630,"children":631},{"style":603},[632],{"type":68,"value":633},"    \"extra\": {\n",{"type":62,"tag":579,"props":635,"children":637},{"class":581,"line":636},5,[638],{"type":62,"tag":579,"props":639,"children":640},{"style":603},[641],{"type":68,"value":642},"        \"model\": \"\u003Cprovider>:\u003Cmodel-name>\"\n",{"type":62,"tag":579,"props":644,"children":646},{"class":581,"line":645},6,[647],{"type":62,"tag":579,"props":648,"children":649},{"style":603},[650],{"type":68,"value":651},"    }\n",{"type":62,"tag":579,"props":653,"children":655},{"class":581,"line":654},7,[656,661],{"type":62,"tag":579,"props":657,"children":658},{"style":603},[659],{"type":68,"value":660},"}",{"type":62,"tag":579,"props":662,"children":663},{"style":592},[664],{"type":68,"value":665},"'\n",{"type":62,"tag":563,"props":667,"children":669},{"id":668},"model-format",[670],{"type":68,"value":671},"Model format",{"type":62,"tag":71,"props":673,"children":674},{},[675,677,683],{"type":68,"value":676},"The model field uses ",{"type":62,"tag":77,"props":678,"children":680},{"className":679},[],[681],{"type":68,"value":682},"provider:model",{"type":68,"value":684}," format:",{"type":62,"tag":686,"props":687,"children":688},"table",{},[689,708],{"type":62,"tag":690,"props":691,"children":692},"thead",{},[693],{"type":62,"tag":694,"props":695,"children":696},"tr",{},[697,703],{"type":62,"tag":698,"props":699,"children":700},"th",{},[701],{"type":68,"value":702},"Provider",{"type":62,"tag":698,"props":704,"children":705},{},[706],{"type":68,"value":707},"Example model value",{"type":62,"tag":709,"props":710,"children":711},"tbody",{},[712,730,747,764,781,798],{"type":62,"tag":694,"props":713,"children":714},{},[715,721],{"type":62,"tag":716,"props":717,"children":718},"td",{},[719],{"type":68,"value":720},"OpenAI",{"type":62,"tag":716,"props":722,"children":723},{},[724],{"type":62,"tag":77,"props":725,"children":727},{"className":726},[],[728],{"type":68,"value":729},"openai:gpt-5",{"type":62,"tag":694,"props":731,"children":732},{},[733,738],{"type":62,"tag":716,"props":734,"children":735},{},[736],{"type":68,"value":737},"Anthropic",{"type":62,"tag":716,"props":739,"children":740},{},[741],{"type":62,"tag":77,"props":742,"children":744},{"className":743},[],[745],{"type":68,"value":746},"anthropic:claude-sonnet-4-20250514",{"type":62,"tag":694,"props":748,"children":749},{},[750,755],{"type":62,"tag":716,"props":751,"children":752},{},[753],{"type":68,"value":754},"Google",{"type":62,"tag":716,"props":756,"children":757},{},[758],{"type":62,"tag":77,"props":759,"children":761},{"className":760},[],[762],{"type":68,"value":763},"google:gemini-2.5-pro",{"type":62,"tag":694,"props":765,"children":766},{},[767,772],{"type":62,"tag":716,"props":768,"children":769},{},[770],{"type":68,"value":771},"Groq",{"type":62,"tag":716,"props":773,"children":774},{},[775],{"type":62,"tag":77,"props":776,"children":778},{"className":777},[],[779],{"type":68,"value":780},"groq:llama-3.3-70b-versatile",{"type":62,"tag":694,"props":782,"children":783},{},[784,789],{"type":62,"tag":716,"props":785,"children":786},{},[787],{"type":68,"value":788},"Mistral",{"type":62,"tag":716,"props":790,"children":791},{},[792],{"type":62,"tag":77,"props":793,"children":795},{"className":794},[],[796],{"type":68,"value":797},"mistral:mistral-large-latest",{"type":62,"tag":694,"props":799,"children":800},{},[801,806],{"type":62,"tag":716,"props":802,"children":803},{},[804],{"type":68,"value":805},"Bedrock",{"type":62,"tag":716,"props":807,"children":808},{},[809],{"type":62,"tag":77,"props":810,"children":812},{"className":811},[],[813],{"type":68,"value":814},"bedrock:us.anthropic.claude-sonnet-4-20250514-v1:0",{"type":62,"tag":563,"props":816,"children":818},{"id":817},"custom-endpoints-ollama-vllm-snowflake-cortex-etc",[819],{"type":68,"value":820},"Custom endpoints (Ollama, vLLM, Snowflake Cortex, etc.)",{"type":62,"tag":71,"props":822,"children":823},{},[824,826,832],{"type":68,"value":825},"Set ",{"type":62,"tag":77,"props":827,"children":829},{"className":828},[],[830],{"type":68,"value":831},"host",{"type":68,"value":833}," to the base URL:",{"type":62,"tag":174,"props":835,"children":837},{"className":571,"code":836,"language":573,"meta":182,"style":182},"AIRFLOW_CONN_PYDANTICAI_CORTEX='{\n    \"conn_type\": \"pydanticai\",\n    \"password\": \"\u003Capi-key>\",\n    \"host\": \"https:\u002F\u002Fmy-endpoint.com\u002Fv1\",\n    \"extra\": {\n        \"model\": \"openai:\u003Cmodel-name>\"\n    }\n}'\n",[838],{"type":62,"tag":77,"props":839,"children":840},{"__ignoreMap":182},[841,861,868,875,883,890,898,905],{"type":62,"tag":579,"props":842,"children":843},{"class":581,"line":582},[844,849,853,857],{"type":62,"tag":579,"props":845,"children":846},{"style":586},[847],{"type":68,"value":848},"AIRFLOW_CONN_PYDANTICAI_CORTEX",{"type":62,"tag":579,"props":850,"children":851},{"style":592},[852],{"type":68,"value":595},{"type":62,"tag":579,"props":854,"children":855},{"style":592},[856],{"type":68,"value":600},{"type":62,"tag":579,"props":858,"children":859},{"style":603},[860],{"type":68,"value":606},{"type":62,"tag":579,"props":862,"children":863},{"class":581,"line":609},[864],{"type":62,"tag":579,"props":865,"children":866},{"style":603},[867],{"type":68,"value":615},{"type":62,"tag":579,"props":869,"children":870},{"class":581,"line":618},[871],{"type":62,"tag":579,"props":872,"children":873},{"style":603},[874],{"type":68,"value":624},{"type":62,"tag":579,"props":876,"children":877},{"class":581,"line":627},[878],{"type":62,"tag":579,"props":879,"children":880},{"style":603},[881],{"type":68,"value":882},"    \"host\": \"https:\u002F\u002Fmy-endpoint.com\u002Fv1\",\n",{"type":62,"tag":579,"props":884,"children":885},{"class":581,"line":636},[886],{"type":62,"tag":579,"props":887,"children":888},{"style":603},[889],{"type":68,"value":633},{"type":62,"tag":579,"props":891,"children":892},{"class":581,"line":645},[893],{"type":62,"tag":579,"props":894,"children":895},{"style":603},[896],{"type":68,"value":897},"        \"model\": \"openai:\u003Cmodel-name>\"\n",{"type":62,"tag":579,"props":899,"children":900},{"class":581,"line":654},[901],{"type":62,"tag":579,"props":902,"children":903},{"style":603},[904],{"type":68,"value":651},{"type":62,"tag":579,"props":906,"children":908},{"class":581,"line":907},8,[909,913],{"type":62,"tag":579,"props":910,"children":911},{"style":603},[912],{"type":68,"value":660},{"type":62,"tag":579,"props":914,"children":915},{"style":592},[916],{"type":68,"value":665},{"type":62,"tag":71,"props":918,"children":919},{},[920,922,928],{"type":68,"value":921},"Use the ",{"type":62,"tag":77,"props":923,"children":925},{"className":924},[],[926],{"type":68,"value":927},"openai:",{"type":68,"value":929}," prefix for any OpenAI-compatible API, regardless of the actual provider.",{"type":62,"tag":563,"props":931,"children":933},{"id":932},"connection-id-convention",[934],{"type":68,"value":935},"Connection ID convention",{"type":62,"tag":71,"props":937,"children":938},{},[939],{"type":68,"value":940},"The env var name determines the connection ID:",{"type":62,"tag":942,"props":943,"children":944},"ul",{},[945,960],{"type":62,"tag":195,"props":946,"children":947},{},[948,953,955],{"type":62,"tag":77,"props":949,"children":951},{"className":950},[],[952],{"type":68,"value":589},{"type":68,"value":954}," creates ",{"type":62,"tag":77,"props":956,"children":958},{"className":957},[],[959],{"type":68,"value":561},{"type":62,"tag":195,"props":961,"children":962},{},[963,968,969],{"type":62,"tag":77,"props":964,"children":966},{"className":965},[],[967],{"type":68,"value":848},{"type":68,"value":954},{"type":62,"tag":77,"props":970,"children":972},{"className":971},[],[973],{"type":68,"value":974},"pydanticai_cortex",{"type":62,"tag":563,"props":976,"children":978},{"id":977},"model-resolution-priority",[979],{"type":68,"value":980},"Model resolution priority",{"type":62,"tag":191,"props":982,"children":983},{},[984,995],{"type":62,"tag":195,"props":985,"children":986},{},[987,993],{"type":62,"tag":77,"props":988,"children":990},{"className":989},[],[991],{"type":68,"value":992},"model_id",{"type":68,"value":994}," parameter on the decorator\u002Foperator (highest)",{"type":62,"tag":195,"props":996,"children":997},{},[998,1004],{"type":62,"tag":77,"props":999,"children":1001},{"className":1000},[],[1002],{"type":68,"value":1003},"model",{"type":68,"value":1005}," in connection's extra JSON (fallback)",{"type":62,"tag":563,"props":1007,"children":1009},{"id":1008},"other-connection-types-040",[1010],{"type":68,"value":1011},"Other connection types (0.4.0)",{"type":62,"tag":71,"props":1013,"children":1014},{},[1015,1017,1022,1024,1030,1032,1038,1040,1046,1048,1054,1056,1061,1063,1068],{"type":68,"value":1016},"Besides ",{"type":62,"tag":77,"props":1018,"children":1020},{"className":1019},[],[1021],{"type":68,"value":142},{"type":68,"value":1023},", the provider registers vendor-specific connection types: ",{"type":62,"tag":77,"props":1025,"children":1027},{"className":1026},[],[1028],{"type":68,"value":1029},"pydanticai-azure",{"type":68,"value":1031}," (Azure OpenAI: host = endpoint, extra ",{"type":62,"tag":77,"props":1033,"children":1035},{"className":1034},[],[1036],{"type":68,"value":1037},"api_version",{"type":68,"value":1039},"), ",{"type":62,"tag":77,"props":1041,"children":1043},{"className":1042},[],[1044],{"type":68,"value":1045},"pydanticai-bedrock",{"type":68,"value":1047}," (AWS credentials\u002Fregion in extra), and ",{"type":62,"tag":77,"props":1049,"children":1051},{"className":1050},[],[1052],{"type":68,"value":1053},"pydanticai-vertex",{"type":68,"value":1055}," (GCP project\u002Flocation in extra). The LlamaIndex and LangChain hooks read API key\u002Fhost\u002Fextra from whatever connection ID they are given, so a single ",{"type":62,"tag":77,"props":1057,"children":1059},{"className":1058},[],[1060],{"type":68,"value":561},{"type":68,"value":1062}," connection can serve LLM calls ",{"type":62,"tag":94,"props":1064,"children":1065},{},[1066],{"type":68,"value":1067},"and",{"type":68,"value":1069}," embeddings: one API key entry for the whole project.",{"type":62,"tag":291,"props":1071,"children":1072},{},[],{"type":62,"tag":162,"props":1074,"children":1076},{"id":1075},"step-3-migrate-decorators",[1077],{"type":68,"value":1078},"Step 3: Migrate decorators",{"type":62,"tag":563,"props":1080,"children":1082},{"id":1081},"taskllm",[1083],{"type":68,"value":223},{"type":62,"tag":174,"props":1085,"children":1089},{"className":1086,"code":1087,"language":1088,"meta":182,"style":182},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# BEFORE (airflow-ai-sdk)\nimport airflow_ai_sdk as ai_sdk\n\nclass MyOutput(ai_sdk.BaseModel):\n    field: str\n\n@task.llm(\n    model=\"gpt-5\",                    # or model=OpenAIModel(...)\n    system_prompt=\"You are helpful.\",\n    output_type=MyOutput,\n)\ndef my_task(text: str) -> str:\n    return text\n\n# AFTER (apache-airflow-providers-common-ai)\nfrom pydantic import BaseModel\n\nclass MyOutput(BaseModel):\n    field: str\n\n@task.llm(\n    llm_conn_id=\"pydanticai_default\",  # Airflow connection ID\n    system_prompt=\"You are helpful.\",\n    output_type=MyOutput,\n)\ndef my_task(text: str) -> str:\n    return text\n","python",[1090],{"type":62,"tag":77,"props":1091,"children":1092},{"__ignoreMap":182},[1093,1101,1109,1118,1126,1134,1141,1149,1157,1166,1175,1184,1193,1202,1210,1219,1228,1236,1245,1253,1261,1269,1278,1286,1294,1302,1310],{"type":62,"tag":579,"props":1094,"children":1095},{"class":581,"line":582},[1096],{"type":62,"tag":579,"props":1097,"children":1098},{},[1099],{"type":68,"value":1100},"# BEFORE (airflow-ai-sdk)\n",{"type":62,"tag":579,"props":1102,"children":1103},{"class":581,"line":609},[1104],{"type":62,"tag":579,"props":1105,"children":1106},{},[1107],{"type":68,"value":1108},"import airflow_ai_sdk as ai_sdk\n",{"type":62,"tag":579,"props":1110,"children":1111},{"class":581,"line":618},[1112],{"type":62,"tag":579,"props":1113,"children":1115},{"emptyLinePlaceholder":1114},true,[1116],{"type":68,"value":1117},"\n",{"type":62,"tag":579,"props":1119,"children":1120},{"class":581,"line":627},[1121],{"type":62,"tag":579,"props":1122,"children":1123},{},[1124],{"type":68,"value":1125},"class MyOutput(ai_sdk.BaseModel):\n",{"type":62,"tag":579,"props":1127,"children":1128},{"class":581,"line":636},[1129],{"type":62,"tag":579,"props":1130,"children":1131},{},[1132],{"type":68,"value":1133},"    field: str\n",{"type":62,"tag":579,"props":1135,"children":1136},{"class":581,"line":645},[1137],{"type":62,"tag":579,"props":1138,"children":1139},{"emptyLinePlaceholder":1114},[1140],{"type":68,"value":1117},{"type":62,"tag":579,"props":1142,"children":1143},{"class":581,"line":654},[1144],{"type":62,"tag":579,"props":1145,"children":1146},{},[1147],{"type":68,"value":1148},"@task.llm(\n",{"type":62,"tag":579,"props":1150,"children":1151},{"class":581,"line":907},[1152],{"type":62,"tag":579,"props":1153,"children":1154},{},[1155],{"type":68,"value":1156},"    model=\"gpt-5\",                    # or model=OpenAIModel(...)\n",{"type":62,"tag":579,"props":1158,"children":1160},{"class":581,"line":1159},9,[1161],{"type":62,"tag":579,"props":1162,"children":1163},{},[1164],{"type":68,"value":1165},"    system_prompt=\"You are helpful.\",\n",{"type":62,"tag":579,"props":1167,"children":1169},{"class":581,"line":1168},10,[1170],{"type":62,"tag":579,"props":1171,"children":1172},{},[1173],{"type":68,"value":1174},"    output_type=MyOutput,\n",{"type":62,"tag":579,"props":1176,"children":1178},{"class":581,"line":1177},11,[1179],{"type":62,"tag":579,"props":1180,"children":1181},{},[1182],{"type":68,"value":1183},")\n",{"type":62,"tag":579,"props":1185,"children":1187},{"class":581,"line":1186},12,[1188],{"type":62,"tag":579,"props":1189,"children":1190},{},[1191],{"type":68,"value":1192},"def my_task(text: str) -> str:\n",{"type":62,"tag":579,"props":1194,"children":1196},{"class":581,"line":1195},13,[1197],{"type":62,"tag":579,"props":1198,"children":1199},{},[1200],{"type":68,"value":1201},"    return text\n",{"type":62,"tag":579,"props":1203,"children":1205},{"class":581,"line":1204},14,[1206],{"type":62,"tag":579,"props":1207,"children":1208},{"emptyLinePlaceholder":1114},[1209],{"type":68,"value":1117},{"type":62,"tag":579,"props":1211,"children":1213},{"class":581,"line":1212},15,[1214],{"type":62,"tag":579,"props":1215,"children":1216},{},[1217],{"type":68,"value":1218},"# AFTER (apache-airflow-providers-common-ai)\n",{"type":62,"tag":579,"props":1220,"children":1222},{"class":581,"line":1221},16,[1223],{"type":62,"tag":579,"props":1224,"children":1225},{},[1226],{"type":68,"value":1227},"from pydantic import BaseModel\n",{"type":62,"tag":579,"props":1229,"children":1231},{"class":581,"line":1230},17,[1232],{"type":62,"tag":579,"props":1233,"children":1234},{"emptyLinePlaceholder":1114},[1235],{"type":68,"value":1117},{"type":62,"tag":579,"props":1237,"children":1239},{"class":581,"line":1238},18,[1240],{"type":62,"tag":579,"props":1241,"children":1242},{},[1243],{"type":68,"value":1244},"class MyOutput(BaseModel):\n",{"type":62,"tag":579,"props":1246,"children":1248},{"class":581,"line":1247},19,[1249],{"type":62,"tag":579,"props":1250,"children":1251},{},[1252],{"type":68,"value":1133},{"type":62,"tag":579,"props":1254,"children":1256},{"class":581,"line":1255},20,[1257],{"type":62,"tag":579,"props":1258,"children":1259},{"emptyLinePlaceholder":1114},[1260],{"type":68,"value":1117},{"type":62,"tag":579,"props":1262,"children":1264},{"class":581,"line":1263},21,[1265],{"type":62,"tag":579,"props":1266,"children":1267},{},[1268],{"type":68,"value":1148},{"type":62,"tag":579,"props":1270,"children":1272},{"class":581,"line":1271},22,[1273],{"type":62,"tag":579,"props":1274,"children":1275},{},[1276],{"type":68,"value":1277},"    llm_conn_id=\"pydanticai_default\",  # Airflow connection ID\n",{"type":62,"tag":579,"props":1279,"children":1281},{"class":581,"line":1280},23,[1282],{"type":62,"tag":579,"props":1283,"children":1284},{},[1285],{"type":68,"value":1165},{"type":62,"tag":579,"props":1287,"children":1289},{"class":581,"line":1288},24,[1290],{"type":62,"tag":579,"props":1291,"children":1292},{},[1293],{"type":68,"value":1174},{"type":62,"tag":579,"props":1295,"children":1297},{"class":581,"line":1296},25,[1298],{"type":62,"tag":579,"props":1299,"children":1300},{},[1301],{"type":68,"value":1183},{"type":62,"tag":579,"props":1303,"children":1305},{"class":581,"line":1304},26,[1306],{"type":62,"tag":579,"props":1307,"children":1308},{},[1309],{"type":68,"value":1192},{"type":62,"tag":579,"props":1311,"children":1313},{"class":581,"line":1312},27,[1314],{"type":62,"tag":579,"props":1315,"children":1316},{},[1317],{"type":68,"value":1201},{"type":62,"tag":71,"props":1319,"children":1320},{},[1321],{"type":62,"tag":94,"props":1322,"children":1323},{},[1324],{"type":68,"value":1325},"Parameter mapping:",{"type":62,"tag":686,"props":1327,"children":1328},{},[1329,1349],{"type":62,"tag":690,"props":1330,"children":1331},{},[1332],{"type":62,"tag":694,"props":1333,"children":1334},{},[1335,1339,1344],{"type":62,"tag":698,"props":1336,"children":1337},{},[1338],{"type":68,"value":82},{"type":62,"tag":698,"props":1340,"children":1341},{},[1342],{"type":68,"value":1343},"common-ai provider",{"type":62,"tag":698,"props":1345,"children":1346},{},[1347],{"type":68,"value":1348},"Notes",{"type":62,"tag":709,"props":1350,"children":1351},{},[1352,1378,1403,1428,1452,1483,1505,1526,1547],{"type":62,"tag":694,"props":1353,"children":1354},{},[1355,1364,1373],{"type":62,"tag":716,"props":1356,"children":1357},{},[1358],{"type":62,"tag":77,"props":1359,"children":1361},{"className":1360},[],[1362],{"type":68,"value":1363},"model=\"gpt-5\"",{"type":62,"tag":716,"props":1365,"children":1366},{},[1367],{"type":62,"tag":77,"props":1368,"children":1370},{"className":1369},[],[1371],{"type":68,"value":1372},"llm_conn_id=\"pydanticai_default\"",{"type":62,"tag":716,"props":1374,"children":1375},{},[1376],{"type":68,"value":1377},"Model specified in connection",{"type":62,"tag":694,"props":1379,"children":1380},{},[1381,1390,1398],{"type":62,"tag":716,"props":1382,"children":1383},{},[1384],{"type":62,"tag":77,"props":1385,"children":1387},{"className":1386},[],[1388],{"type":68,"value":1389},"model=OpenAIModel(...)",{"type":62,"tag":716,"props":1391,"children":1392},{},[1393],{"type":62,"tag":77,"props":1394,"children":1396},{"className":1395},[],[1397],{"type":68,"value":1372},{"type":62,"tag":716,"props":1399,"children":1400},{},[1401],{"type":68,"value":1402},"Model + endpoint in connection",{"type":62,"tag":694,"props":1404,"children":1405},{},[1406,1415,1423],{"type":62,"tag":716,"props":1407,"children":1408},{},[1409],{"type":62,"tag":77,"props":1410,"children":1412},{"className":1411},[],[1413],{"type":68,"value":1414},"system_prompt=\"...\"",{"type":62,"tag":716,"props":1416,"children":1417},{},[1418],{"type":62,"tag":77,"props":1419,"children":1421},{"className":1420},[],[1422],{"type":68,"value":1414},{"type":62,"tag":716,"props":1424,"children":1425},{},[1426],{"type":68,"value":1427},"Unchanged",{"type":62,"tag":694,"props":1429,"children":1430},{},[1431,1440,1448],{"type":62,"tag":716,"props":1432,"children":1433},{},[1434],{"type":62,"tag":77,"props":1435,"children":1437},{"className":1436},[],[1438],{"type":68,"value":1439},"output_type=MyModel",{"type":62,"tag":716,"props":1441,"children":1442},{},[1443],{"type":62,"tag":77,"props":1444,"children":1446},{"className":1445},[],[1447],{"type":68,"value":1439},{"type":62,"tag":716,"props":1449,"children":1450},{},[1451],{"type":68,"value":1427},{"type":62,"tag":694,"props":1453,"children":1454},{},[1455,1464,1472],{"type":62,"tag":716,"props":1456,"children":1457},{},[1458],{"type":62,"tag":77,"props":1459,"children":1461},{"className":1460},[],[1462],{"type":68,"value":1463},"result_type=MyModel",{"type":62,"tag":716,"props":1465,"children":1466},{},[1467],{"type":62,"tag":77,"props":1468,"children":1470},{"className":1469},[],[1471],{"type":68,"value":1439},{"type":62,"tag":716,"props":1473,"children":1474},{},[1475,1481],{"type":62,"tag":77,"props":1476,"children":1478},{"className":1477},[],[1479],{"type":68,"value":1480},"result_type",{"type":68,"value":1482}," was already deprecated",{"type":62,"tag":694,"props":1484,"children":1485},{},[1486,1491,1500],{"type":62,"tag":716,"props":1487,"children":1488},{},[1489],{"type":68,"value":1490},"(not available)",{"type":62,"tag":716,"props":1492,"children":1493},{},[1494],{"type":62,"tag":77,"props":1495,"children":1497},{"className":1496},[],[1498],{"type":68,"value":1499},"model_id=\"openai:gpt-5\"",{"type":62,"tag":716,"props":1501,"children":1502},{},[1503],{"type":68,"value":1504},"Override connection's model",{"type":62,"tag":694,"props":1506,"children":1507},{},[1508,1512,1521],{"type":62,"tag":716,"props":1509,"children":1510},{},[1511],{"type":68,"value":1490},{"type":62,"tag":716,"props":1513,"children":1514},{},[1515],{"type":62,"tag":77,"props":1516,"children":1518},{"className":1517},[],[1519],{"type":68,"value":1520},"require_approval=True",{"type":62,"tag":716,"props":1522,"children":1523},{},[1524],{"type":68,"value":1525},"Built-in HITL review",{"type":62,"tag":694,"props":1527,"children":1528},{},[1529,1533,1542],{"type":62,"tag":716,"props":1530,"children":1531},{},[1532],{"type":68,"value":1490},{"type":62,"tag":716,"props":1534,"children":1535},{},[1536],{"type":62,"tag":77,"props":1537,"children":1539},{"className":1538},[],[1540],{"type":68,"value":1541},"agent_params={...}",{"type":62,"tag":716,"props":1543,"children":1544},{},[1545],{"type":68,"value":1546},"Extra kwargs for pydantic-ai Agent",{"type":62,"tag":694,"props":1548,"children":1549},{},[1550,1554,1563],{"type":62,"tag":716,"props":1551,"children":1552},{},[1553],{"type":68,"value":1490},{"type":62,"tag":716,"props":1555,"children":1556},{},[1557],{"type":62,"tag":77,"props":1558,"children":1560},{"className":1559},[],[1561],{"type":68,"value":1562},"serialize_output=True",{"type":62,"tag":716,"props":1564,"children":1565},{},[1566],{"type":68,"value":1567},"Force dict shape for BaseModel output",{"type":62,"tag":71,"props":1569,"children":1570},{},[1571,1576,1578,1584],{"type":62,"tag":94,"props":1572,"children":1573},{},[1574],{"type":68,"value":1575},"Multimodal prompts (0.4.0+):",{"type":68,"value":1577}," the translation function may return a ",{"type":62,"tag":77,"props":1579,"children":1581},{"className":1580},[],[1582],{"type":68,"value":1583},"Sequence[UserContent]",{"type":68,"value":1585}," instead of a string, e.g. for vision:",{"type":62,"tag":174,"props":1587,"children":1589},{"className":1086,"code":1588,"language":1088,"meta":182,"style":182},"@task.llm(llm_conn_id=\"pydanticai_default\", system_prompt=\"...\", output_type=ReviewAnalysis)\ndef analyze(text: str, image_path: str | None = None):\n    if image_path:\n        with open(image_path, \"rb\") as f:\n            return [text, BinaryContent(data=f.read(), media_type=\"image\u002Fjpeg\")]\n    return text\n",[1590],{"type":62,"tag":77,"props":1591,"children":1592},{"__ignoreMap":182},[1593,1601,1609,1617,1625,1633],{"type":62,"tag":579,"props":1594,"children":1595},{"class":581,"line":582},[1596],{"type":62,"tag":579,"props":1597,"children":1598},{},[1599],{"type":68,"value":1600},"@task.llm(llm_conn_id=\"pydanticai_default\", system_prompt=\"...\", output_type=ReviewAnalysis)\n",{"type":62,"tag":579,"props":1602,"children":1603},{"class":581,"line":609},[1604],{"type":62,"tag":579,"props":1605,"children":1606},{},[1607],{"type":68,"value":1608},"def analyze(text: str, image_path: str | None = None):\n",{"type":62,"tag":579,"props":1610,"children":1611},{"class":581,"line":618},[1612],{"type":62,"tag":579,"props":1613,"children":1614},{},[1615],{"type":68,"value":1616},"    if image_path:\n",{"type":62,"tag":579,"props":1618,"children":1619},{"class":581,"line":627},[1620],{"type":62,"tag":579,"props":1621,"children":1622},{},[1623],{"type":68,"value":1624},"        with open(image_path, \"rb\") as f:\n",{"type":62,"tag":579,"props":1626,"children":1627},{"class":581,"line":636},[1628],{"type":62,"tag":579,"props":1629,"children":1630},{},[1631],{"type":68,"value":1632},"            return [text, BinaryContent(data=f.read(), media_type=\"image\u002Fjpeg\")]\n",{"type":62,"tag":579,"props":1634,"children":1635},{"class":581,"line":645},[1636],{"type":62,"tag":579,"props":1637,"children":1638},{},[1639],{"type":68,"value":1201},{"type":62,"tag":71,"props":1641,"children":1642},{},[1643,1645,1650,1652,1657,1658,1664],{"type":68,"value":1644},"This matches the old airflow-ai-sdk vision pattern, so vision code migrates unchanged. Note: common-ai ",{"type":62,"tag":94,"props":1646,"children":1647},{},[1648],{"type":68,"value":1649},"0.1.x only accepted strings",{"type":68,"value":1651}," — if a project disabled vision to migrate to 0.1.0, re-enable it when bumping to 0.4.0. Non-string prompts are incompatible with ",{"type":62,"tag":77,"props":1653,"children":1655},{"className":1654},[],[1656],{"type":68,"value":1520},{"type":68,"value":206},{"type":62,"tag":77,"props":1659,"children":1661},{"className":1660},[],[1662],{"type":68,"value":1663},"enable_hitl_review=True",{"type":68,"value":1665}," (both render the prompt as text).",{"type":62,"tag":71,"props":1667,"children":1668},{},[1669,1674,1676,1682,1684,1689,1691,1697,1699,1704,1706,1711,1713,1718,1720,1725],{"type":62,"tag":94,"props":1670,"children":1671},{},[1672],{"type":68,"value":1673},"Structured output via XCom (0.4.0 behavior change):",{"type":68,"value":1675}," with ",{"type":62,"tag":77,"props":1677,"children":1679},{"className":1678},[],[1680],{"type":68,"value":1681},"output_type=\u003CBaseModel subclass>",{"type":68,"value":1683},", the model ",{"type":62,"tag":94,"props":1685,"children":1686},{},[1687],{"type":68,"value":1688},"instance",{"type":68,"value":1690}," flows through XCom on Airflow cores whose task SDK has ",{"type":62,"tag":77,"props":1692,"children":1694},{"className":1693},[],[1695],{"type":68,"value":1696},"SUPPORTS_OPERATOR_DESERIALIZATION_WALKER",{"type":68,"value":1698}," (attribute access downstream); on older cores (including Astro Runtime 3.2 task SDK 1.2.x) the provider automatically dumps to a ",{"type":62,"tag":94,"props":1700,"children":1701},{},[1702],{"type":68,"value":1703},"dict",{"type":68,"value":1705}," (subscript access). Check which shape arrives at runtime before choosing attribute vs dict access downstream, or set ",{"type":62,"tag":77,"props":1707,"children":1709},{"className":1708},[],[1710],{"type":68,"value":1562},{"type":68,"value":1712}," to force the dict shape everywhere. The ",{"type":62,"tag":77,"props":1714,"children":1716},{"className":1715},[],[1717],{"type":68,"value":284},{"type":68,"value":1719}," class must be defined at ",{"type":62,"tag":94,"props":1721,"children":1722},{},[1723],{"type":68,"value":1724},"module scope",{"type":68,"value":1726}," (nested classes cannot be deserialized from XCom).",{"type":62,"tag":563,"props":1728,"children":1730},{"id":1729},"taskllm_branch",[1731],{"type":68,"value":238},{"type":62,"tag":174,"props":1733,"children":1735},{"className":1086,"code":1734,"language":1088,"meta":182,"style":182},"# BEFORE\n@task.llm_branch(\n    model=\"gpt-5\",\n    system_prompt=\"Choose a team...\",\n    allow_multiple_branches=False,\n)\ndef route(text: str) -> str:\n    return text\n\n# AFTER\n@task.llm_branch(\n    llm_conn_id=\"pydanticai_default\",\n    system_prompt=\"Choose a team...\",\n    allow_multiple_branches=False,    # same parameter, unchanged\n)\ndef route(text: str) -> str:\n    return text\n",[1736],{"type":62,"tag":77,"props":1737,"children":1738},{"__ignoreMap":182},[1739,1747,1755,1763,1771,1779,1786,1794,1801,1808,1816,1823,1831,1838,1846,1853,1860],{"type":62,"tag":579,"props":1740,"children":1741},{"class":581,"line":582},[1742],{"type":62,"tag":579,"props":1743,"children":1744},{},[1745],{"type":68,"value":1746},"# BEFORE\n",{"type":62,"tag":579,"props":1748,"children":1749},{"class":581,"line":609},[1750],{"type":62,"tag":579,"props":1751,"children":1752},{},[1753],{"type":68,"value":1754},"@task.llm_branch(\n",{"type":62,"tag":579,"props":1756,"children":1757},{"class":581,"line":618},[1758],{"type":62,"tag":579,"props":1759,"children":1760},{},[1761],{"type":68,"value":1762},"    model=\"gpt-5\",\n",{"type":62,"tag":579,"props":1764,"children":1765},{"class":581,"line":627},[1766],{"type":62,"tag":579,"props":1767,"children":1768},{},[1769],{"type":68,"value":1770},"    system_prompt=\"Choose a team...\",\n",{"type":62,"tag":579,"props":1772,"children":1773},{"class":581,"line":636},[1774],{"type":62,"tag":579,"props":1775,"children":1776},{},[1777],{"type":68,"value":1778},"    allow_multiple_branches=False,\n",{"type":62,"tag":579,"props":1780,"children":1781},{"class":581,"line":645},[1782],{"type":62,"tag":579,"props":1783,"children":1784},{},[1785],{"type":68,"value":1183},{"type":62,"tag":579,"props":1787,"children":1788},{"class":581,"line":654},[1789],{"type":62,"tag":579,"props":1790,"children":1791},{},[1792],{"type":68,"value":1793},"def route(text: str) -> str:\n",{"type":62,"tag":579,"props":1795,"children":1796},{"class":581,"line":907},[1797],{"type":62,"tag":579,"props":1798,"children":1799},{},[1800],{"type":68,"value":1201},{"type":62,"tag":579,"props":1802,"children":1803},{"class":581,"line":1159},[1804],{"type":62,"tag":579,"props":1805,"children":1806},{"emptyLinePlaceholder":1114},[1807],{"type":68,"value":1117},{"type":62,"tag":579,"props":1809,"children":1810},{"class":581,"line":1168},[1811],{"type":62,"tag":579,"props":1812,"children":1813},{},[1814],{"type":68,"value":1815},"# AFTER\n",{"type":62,"tag":579,"props":1817,"children":1818},{"class":581,"line":1177},[1819],{"type":62,"tag":579,"props":1820,"children":1821},{},[1822],{"type":68,"value":1754},{"type":62,"tag":579,"props":1824,"children":1825},{"class":581,"line":1186},[1826],{"type":62,"tag":579,"props":1827,"children":1828},{},[1829],{"type":68,"value":1830},"    llm_conn_id=\"pydanticai_default\",\n",{"type":62,"tag":579,"props":1832,"children":1833},{"class":581,"line":1195},[1834],{"type":62,"tag":579,"props":1835,"children":1836},{},[1837],{"type":68,"value":1770},{"type":62,"tag":579,"props":1839,"children":1840},{"class":581,"line":1204},[1841],{"type":62,"tag":579,"props":1842,"children":1843},{},[1844],{"type":68,"value":1845},"    allow_multiple_branches=False,    # same parameter, unchanged\n",{"type":62,"tag":579,"props":1847,"children":1848},{"class":581,"line":1212},[1849],{"type":62,"tag":579,"props":1850,"children":1851},{},[1852],{"type":68,"value":1183},{"type":62,"tag":579,"props":1854,"children":1855},{"class":581,"line":1221},[1856],{"type":62,"tag":579,"props":1857,"children":1858},{},[1859],{"type":68,"value":1793},{"type":62,"tag":579,"props":1861,"children":1862},{"class":581,"line":1230},[1863],{"type":62,"tag":579,"props":1864,"children":1865},{},[1866],{"type":68,"value":1201},{"type":62,"tag":71,"props":1868,"children":1869},{},[1870,1872,1878,1880,1886],{"type":68,"value":1871},"Only change: ",{"type":62,"tag":77,"props":1873,"children":1875},{"className":1874},[],[1876],{"type":68,"value":1877},"model=",{"type":68,"value":1879}," becomes ",{"type":62,"tag":77,"props":1881,"children":1883},{"className":1882},[],[1884],{"type":68,"value":1885},"llm_conn_id=",{"type":68,"value":520},{"type":62,"tag":563,"props":1888,"children":1890},{"id":1889},"taskagent",[1891],{"type":68,"value":231},{"type":62,"tag":71,"props":1893,"children":1894},{},[1895],{"type":68,"value":1896},"This has the biggest API change. The Agent is no longer pre-built in user code.",{"type":62,"tag":174,"props":1898,"children":1900},{"className":1086,"code":1899,"language":1088,"meta":182,"style":182},"# BEFORE (airflow-ai-sdk) - Agent built at module level\nfrom pydantic_ai import Agent\n\nmy_agent = Agent(\n    \"gpt-5\",\n    system_prompt=\"You are a research assistant.\",\n    tools=[search_tool, lookup_tool],\n)\n\n@task.agent(agent=my_agent)\ndef research(question: str) -> str:\n    return question\n\n# AFTER (common-ai provider) - No Agent object, config via parameters\nfrom pydantic_ai.toolsets import FunctionToolset\n\n@task.agent(\n    llm_conn_id=\"pydanticai_default\",\n    system_prompt=\"You are a research assistant.\",\n    toolsets=[FunctionToolset(tools=[search_tool, lookup_tool])],\n)\ndef research(question: str) -> str:\n    return question\n",[1901],{"type":62,"tag":77,"props":1902,"children":1903},{"__ignoreMap":182},[1904,1912,1920,1927,1935,1943,1951,1959,1966,1973,1981,1989,1997,2004,2012,2020,2027,2035,2042,2049,2057,2064,2071],{"type":62,"tag":579,"props":1905,"children":1906},{"class":581,"line":582},[1907],{"type":62,"tag":579,"props":1908,"children":1909},{},[1910],{"type":68,"value":1911},"# BEFORE (airflow-ai-sdk) - Agent built at module level\n",{"type":62,"tag":579,"props":1913,"children":1914},{"class":581,"line":609},[1915],{"type":62,"tag":579,"props":1916,"children":1917},{},[1918],{"type":68,"value":1919},"from pydantic_ai import Agent\n",{"type":62,"tag":579,"props":1921,"children":1922},{"class":581,"line":618},[1923],{"type":62,"tag":579,"props":1924,"children":1925},{"emptyLinePlaceholder":1114},[1926],{"type":68,"value":1117},{"type":62,"tag":579,"props":1928,"children":1929},{"class":581,"line":627},[1930],{"type":62,"tag":579,"props":1931,"children":1932},{},[1933],{"type":68,"value":1934},"my_agent = Agent(\n",{"type":62,"tag":579,"props":1936,"children":1937},{"class":581,"line":636},[1938],{"type":62,"tag":579,"props":1939,"children":1940},{},[1941],{"type":68,"value":1942},"    \"gpt-5\",\n",{"type":62,"tag":579,"props":1944,"children":1945},{"class":581,"line":645},[1946],{"type":62,"tag":579,"props":1947,"children":1948},{},[1949],{"type":68,"value":1950},"    system_prompt=\"You are a research assistant.\",\n",{"type":62,"tag":579,"props":1952,"children":1953},{"class":581,"line":654},[1954],{"type":62,"tag":579,"props":1955,"children":1956},{},[1957],{"type":68,"value":1958},"    tools=[search_tool, lookup_tool],\n",{"type":62,"tag":579,"props":1960,"children":1961},{"class":581,"line":907},[1962],{"type":62,"tag":579,"props":1963,"children":1964},{},[1965],{"type":68,"value":1183},{"type":62,"tag":579,"props":1967,"children":1968},{"class":581,"line":1159},[1969],{"type":62,"tag":579,"props":1970,"children":1971},{"emptyLinePlaceholder":1114},[1972],{"type":68,"value":1117},{"type":62,"tag":579,"props":1974,"children":1975},{"class":581,"line":1168},[1976],{"type":62,"tag":579,"props":1977,"children":1978},{},[1979],{"type":68,"value":1980},"@task.agent(agent=my_agent)\n",{"type":62,"tag":579,"props":1982,"children":1983},{"class":581,"line":1177},[1984],{"type":62,"tag":579,"props":1985,"children":1986},{},[1987],{"type":68,"value":1988},"def research(question: str) -> str:\n",{"type":62,"tag":579,"props":1990,"children":1991},{"class":581,"line":1186},[1992],{"type":62,"tag":579,"props":1993,"children":1994},{},[1995],{"type":68,"value":1996},"    return question\n",{"type":62,"tag":579,"props":1998,"children":1999},{"class":581,"line":1195},[2000],{"type":62,"tag":579,"props":2001,"children":2002},{"emptyLinePlaceholder":1114},[2003],{"type":68,"value":1117},{"type":62,"tag":579,"props":2005,"children":2006},{"class":581,"line":1204},[2007],{"type":62,"tag":579,"props":2008,"children":2009},{},[2010],{"type":68,"value":2011},"# AFTER (common-ai provider) - No Agent object, config via parameters\n",{"type":62,"tag":579,"props":2013,"children":2014},{"class":581,"line":1212},[2015],{"type":62,"tag":579,"props":2016,"children":2017},{},[2018],{"type":68,"value":2019},"from pydantic_ai.toolsets import FunctionToolset\n",{"type":62,"tag":579,"props":2021,"children":2022},{"class":581,"line":1221},[2023],{"type":62,"tag":579,"props":2024,"children":2025},{"emptyLinePlaceholder":1114},[2026],{"type":68,"value":1117},{"type":62,"tag":579,"props":2028,"children":2029},{"class":581,"line":1230},[2030],{"type":62,"tag":579,"props":2031,"children":2032},{},[2033],{"type":68,"value":2034},"@task.agent(\n",{"type":62,"tag":579,"props":2036,"children":2037},{"class":581,"line":1238},[2038],{"type":62,"tag":579,"props":2039,"children":2040},{},[2041],{"type":68,"value":1830},{"type":62,"tag":579,"props":2043,"children":2044},{"class":581,"line":1247},[2045],{"type":62,"tag":579,"props":2046,"children":2047},{},[2048],{"type":68,"value":1950},{"type":62,"tag":579,"props":2050,"children":2051},{"class":581,"line":1255},[2052],{"type":62,"tag":579,"props":2053,"children":2054},{},[2055],{"type":68,"value":2056},"    toolsets=[FunctionToolset(tools=[search_tool, lookup_tool])],\n",{"type":62,"tag":579,"props":2058,"children":2059},{"class":581,"line":1263},[2060],{"type":62,"tag":579,"props":2061,"children":2062},{},[2063],{"type":68,"value":1183},{"type":62,"tag":579,"props":2065,"children":2066},{"class":581,"line":1271},[2067],{"type":62,"tag":579,"props":2068,"children":2069},{},[2070],{"type":68,"value":1988},{"type":62,"tag":579,"props":2072,"children":2073},{"class":581,"line":1280},[2074],{"type":62,"tag":579,"props":2075,"children":2076},{},[2077],{"type":68,"value":1996},{"type":62,"tag":71,"props":2079,"children":2080},{},[2081],{"type":62,"tag":94,"props":2082,"children":2083},{},[2084],{"type":68,"value":1325},{"type":62,"tag":686,"props":2086,"children":2087},{},[2088,2106],{"type":62,"tag":690,"props":2089,"children":2090},{},[2091],{"type":62,"tag":694,"props":2092,"children":2093},{},[2094,2098,2102],{"type":62,"tag":698,"props":2095,"children":2096},{},[2097],{"type":68,"value":82},{"type":62,"tag":698,"props":2099,"children":2100},{},[2101],{"type":68,"value":1343},{"type":62,"tag":698,"props":2103,"children":2104},{},[2105],{"type":68,"value":1348},{"type":62,"tag":709,"props":2107,"children":2108},{},[2109,2135,2162,2189,2215,2239,2268],{"type":62,"tag":694,"props":2110,"children":2111},{},[2112,2121,2130],{"type":62,"tag":716,"props":2113,"children":2114},{},[2115],{"type":62,"tag":77,"props":2116,"children":2118},{"className":2117},[],[2119],{"type":68,"value":2120},"agent=Agent(model, ...)",{"type":62,"tag":716,"props":2122,"children":2123},{},[2124],{"type":62,"tag":77,"props":2125,"children":2127},{"className":2126},[],[2128],{"type":68,"value":2129},"llm_conn_id=\"...\"",{"type":62,"tag":716,"props":2131,"children":2132},{},[2133],{"type":68,"value":2134},"Model from connection",{"type":62,"tag":694,"props":2136,"children":2137},{},[2138,2149,2157],{"type":62,"tag":716,"props":2139,"children":2140},{},[2141,2143],{"type":68,"value":2142},"Agent's ",{"type":62,"tag":77,"props":2144,"children":2146},{"className":2145},[],[2147],{"type":68,"value":2148},"system_prompt",{"type":62,"tag":716,"props":2150,"children":2151},{},[2152],{"type":62,"tag":77,"props":2153,"children":2155},{"className":2154},[],[2156],{"type":68,"value":1414},{"type":62,"tag":716,"props":2158,"children":2159},{},[2160],{"type":68,"value":2161},"Now a decorator param",{"type":62,"tag":694,"props":2163,"children":2164},{},[2165,2175,2184],{"type":62,"tag":716,"props":2166,"children":2167},{},[2168,2169],{"type":68,"value":2142},{"type":62,"tag":77,"props":2170,"children":2172},{"className":2171},[],[2173],{"type":68,"value":2174},"tools=[...]",{"type":62,"tag":716,"props":2176,"children":2177},{},[2178],{"type":62,"tag":77,"props":2179,"children":2181},{"className":2180},[],[2182],{"type":68,"value":2183},"toolsets=[FunctionToolset(tools=[...])]",{"type":62,"tag":716,"props":2185,"children":2186},{},[2187],{"type":68,"value":2188},"Preferred: gets automatic tool-call logging",{"type":62,"tag":694,"props":2190,"children":2191},{},[2192,2201,2210],{"type":62,"tag":716,"props":2193,"children":2194},{},[2195,2196],{"type":68,"value":2142},{"type":62,"tag":77,"props":2197,"children":2199},{"className":2198},[],[2200],{"type":68,"value":2174},{"type":62,"tag":716,"props":2202,"children":2203},{},[2204],{"type":62,"tag":77,"props":2205,"children":2207},{"className":2206},[],[2208],{"type":68,"value":2209},"agent_params={\"tools\": [...]}",{"type":62,"tag":716,"props":2211,"children":2212},{},[2213],{"type":68,"value":2214},"Also works, but no tool-call logging",{"type":62,"tag":694,"props":2216,"children":2217},{},[2218,2227,2235],{"type":62,"tag":716,"props":2219,"children":2220},{},[2221,2222],{"type":68,"value":2142},{"type":62,"tag":77,"props":2223,"children":2225},{"className":2224},[],[2226],{"type":68,"value":284},{"type":62,"tag":716,"props":2228,"children":2229},{},[2230],{"type":62,"tag":77,"props":2231,"children":2233},{"className":2232},[],[2234],{"type":68,"value":1439},{"type":62,"tag":716,"props":2236,"children":2237},{},[2238],{"type":68,"value":2161},{"type":62,"tag":694,"props":2240,"children":2241},{},[2242,2246,2255],{"type":62,"tag":716,"props":2243,"children":2244},{},[2245],{"type":68,"value":1490},{"type":62,"tag":716,"props":2247,"children":2248},{},[2249],{"type":62,"tag":77,"props":2250,"children":2252},{"className":2251},[],[2253],{"type":68,"value":2254},"durable=True",{"type":62,"tag":716,"props":2256,"children":2257},{},[2258,2260,2266],{"type":68,"value":2259},"Step-level caching (needs ",{"type":62,"tag":77,"props":2261,"children":2263},{"className":2262},[],[2264],{"type":68,"value":2265},"[common.ai] durable_cache_path",{"type":68,"value":2267},")",{"type":62,"tag":694,"props":2269,"children":2270},{},[2271,2275,2283],{"type":62,"tag":716,"props":2272,"children":2273},{},[2274],{"type":68,"value":1490},{"type":62,"tag":716,"props":2276,"children":2277},{},[2278],{"type":62,"tag":77,"props":2279,"children":2281},{"className":2280},[],[2282],{"type":68,"value":1663},{"type":62,"tag":716,"props":2284,"children":2285},{},[2286],{"type":68,"value":2287},"Iterative human review loop (see below)",{"type":62,"tag":71,"props":2289,"children":2290},{},[2291,2296,2298,2304,2306,2312,2314,2319,2321,2327,2329,2334,2336,2342,2344,2350],{"type":62,"tag":94,"props":2292,"children":2293},{},[2294],{"type":68,"value":2295},"Key insight:",{"type":68,"value":2297}," Everything that was configured on the ",{"type":62,"tag":77,"props":2299,"children":2301},{"className":2300},[],[2302],{"type":68,"value":2303},"Agent()",{"type":68,"value":2305}," constructor now goes into either a top-level decorator parameter or ",{"type":62,"tag":77,"props":2307,"children":2309},{"className":2308},[],[2310],{"type":68,"value":2311},"agent_params",{"type":68,"value":2313},". The ",{"type":62,"tag":77,"props":2315,"children":2317},{"className":2316},[],[2318],{"type":68,"value":2311},{"type":68,"value":2320}," dict is passed directly to pydantic-ai's ",{"type":62,"tag":77,"props":2322,"children":2324},{"className":2323},[],[2325],{"type":68,"value":2326},"Agent",{"type":68,"value":2328}," constructor. Prefer ",{"type":62,"tag":77,"props":2330,"children":2332},{"className":2331},[],[2333],{"type":68,"value":106},{"type":68,"value":2335}," over ",{"type":62,"tag":77,"props":2337,"children":2339},{"className":2338},[],[2340],{"type":68,"value":2341},"agent_params[\"tools\"]",{"type":68,"value":2343},": the operator wraps each toolset in a ",{"type":62,"tag":77,"props":2345,"children":2347},{"className":2346},[],[2348],{"type":68,"value":2349},"LoggingToolset",{"type":68,"value":2351},", so every tool call appears in the task log with timing.",{"type":62,"tag":71,"props":2353,"children":2354},{},[2355,2360,2362,2367,2369,2374,2376,2382,2384,2389,2391,2397,2399,2405,2407,2413,2414,2420,2421,2427,2428,2434,2436,2442,2443,2449,2450,2456,2457,2463],{"type":62,"tag":94,"props":2356,"children":2357},{},[2358],{"type":68,"value":2359},"enable_hitl_review behavior:",{"type":68,"value":2361}," the task generates a first draft, then ",{"type":62,"tag":94,"props":2363,"children":2364},{},[2365],{"type":68,"value":2366},"blocks",{"type":68,"value":2368}," until a human acts. The reviewer uses the ",{"type":62,"tag":94,"props":2370,"children":2371},{},[2372],{"type":68,"value":2373},"HITL Review",{"type":68,"value":2375}," tab\u002Fextra link on the task instance (chat UI from the provider's auto-registered ",{"type":62,"tag":77,"props":2377,"children":2379},{"className":2378},[],[2380],{"type":68,"value":2381},"hitl_review",{"type":68,"value":2383}," plugin) to request changes (agent regenerates with the feedback in its message history) or approve. Constraints: requires a string prompt, incompatible with ",{"type":62,"tag":77,"props":2385,"children":2387},{"className":2386},[],[2388],{"type":68,"value":2254},{"type":68,"value":2390},", and the final (possibly regenerated) output is what flows to XCom. Warn users that the Dag run waits indefinitely at this task unless ",{"type":62,"tag":77,"props":2392,"children":2394},{"className":2393},[],[2395],{"type":68,"value":2396},"hitl_timeout",{"type":68,"value":2398}," is set. For headless testing, the plugin exposes REST endpoints under ",{"type":62,"tag":77,"props":2400,"children":2402},{"className":2401},[],[2403],{"type":68,"value":2404},"\u002Fhitl-review",{"type":68,"value":2406},": ",{"type":62,"tag":77,"props":2408,"children":2410},{"className":2409},[],[2411],{"type":68,"value":2412},"GET \u002Fsessions\u002Ffind",{"type":68,"value":225},{"type":62,"tag":77,"props":2415,"children":2417},{"className":2416},[],[2418],{"type":68,"value":2419},"POST \u002Fsessions\u002Ffeedback",{"type":68,"value":225},{"type":62,"tag":77,"props":2422,"children":2424},{"className":2423},[],[2425],{"type":68,"value":2426},"POST \u002Fsessions\u002Fapprove",{"type":68,"value":225},{"type":62,"tag":77,"props":2429,"children":2431},{"className":2430},[],[2432],{"type":68,"value":2433},"POST \u002Fsessions\u002Freject",{"type":68,"value":2435}," (query params ",{"type":62,"tag":77,"props":2437,"children":2439},{"className":2438},[],[2440],{"type":68,"value":2441},"dag_id",{"type":68,"value":225},{"type":62,"tag":77,"props":2444,"children":2446},{"className":2445},[],[2447],{"type":68,"value":2448},"task_id",{"type":68,"value":225},{"type":62,"tag":77,"props":2451,"children":2453},{"className":2452},[],[2454],{"type":68,"value":2455},"run_id",{"type":68,"value":225},{"type":62,"tag":77,"props":2458,"children":2460},{"className":2459},[],[2461],{"type":68,"value":2462},"map_index",{"type":68,"value":2464},").",{"type":62,"tag":563,"props":2466,"children":2468},{"id":2467},"taskembed-no-equivalent-three-replacement-options",[2469],{"type":68,"value":2470},"@task.embed (NO EQUIVALENT — three replacement options)",{"type":62,"tag":71,"props":2472,"children":2473},{},[2474],{"type":68,"value":2475},"The new provider does NOT include an embed decorator. Pick the replacement based on what the project needs:",{"type":62,"tag":71,"props":2477,"children":2478},{},[2479,2489,2491,2496,2498,2504,2506,2512],{"type":62,"tag":94,"props":2480,"children":2481},{},[2482,2484],{"type":68,"value":2483},"Option A (recommended): ",{"type":62,"tag":77,"props":2485,"children":2487},{"className":2486},[],[2488],{"type":68,"value":489},{"type":68,"value":2490}," (0.4.0, ",{"type":62,"tag":77,"props":2492,"children":2494},{"className":2493},[],[2495],{"type":68,"value":372},{"type":68,"value":2497}," extra). Connection-based, one task embeds the whole document list, and with ",{"type":62,"tag":77,"props":2499,"children":2501},{"className":2500},[],[2502],{"type":68,"value":2503},"persist_dir",{"type":68,"value":2505}," the resulting vector index is persisted for retrieval (pairs with ",{"type":62,"tag":77,"props":2507,"children":2509},{"className":2508},[],[2510],{"type":68,"value":2511},"LlamaIndexRetrievalOperator",{"type":68,"value":2513},"):",{"type":62,"tag":174,"props":2515,"children":2517},{"className":1086,"code":2516,"language":1088,"meta":182,"style":182},"from airflow.providers.common.ai.operators.llamaindex_embedding import LlamaIndexEmbeddingOperator\n\n_embeddings = LlamaIndexEmbeddingOperator(\n    task_id=\"create_embeddings\",\n    documents=[{\"text\": \"...\", \"metadata\": {\"id\": 1}}, ...],  # templated, accepts XComArg\n    llm_conn_id=\"pydanticai_default\",   # reuses the same connection (API key only)\n    embed_model=\"text-embedding-3-small\",\n    persist_dir=f\"{AIRFLOW_HOME}\u002Finclude\u002Fmy_index\",  # optional; local path or s3:\u002F\u002F, gs:\u002F\u002F, ...\n)\n",[2518],{"type":62,"tag":77,"props":2519,"children":2520},{"__ignoreMap":182},[2521,2529,2536,2544,2552,2560,2568,2576,2584],{"type":62,"tag":579,"props":2522,"children":2523},{"class":581,"line":582},[2524],{"type":62,"tag":579,"props":2525,"children":2526},{},[2527],{"type":68,"value":2528},"from airflow.providers.common.ai.operators.llamaindex_embedding import LlamaIndexEmbeddingOperator\n",{"type":62,"tag":579,"props":2530,"children":2531},{"class":581,"line":609},[2532],{"type":62,"tag":579,"props":2533,"children":2534},{"emptyLinePlaceholder":1114},[2535],{"type":68,"value":1117},{"type":62,"tag":579,"props":2537,"children":2538},{"class":581,"line":618},[2539],{"type":62,"tag":579,"props":2540,"children":2541},{},[2542],{"type":68,"value":2543},"_embeddings = LlamaIndexEmbeddingOperator(\n",{"type":62,"tag":579,"props":2545,"children":2546},{"class":581,"line":627},[2547],{"type":62,"tag":579,"props":2548,"children":2549},{},[2550],{"type":68,"value":2551},"    task_id=\"create_embeddings\",\n",{"type":62,"tag":579,"props":2553,"children":2554},{"class":581,"line":636},[2555],{"type":62,"tag":579,"props":2556,"children":2557},{},[2558],{"type":68,"value":2559},"    documents=[{\"text\": \"...\", \"metadata\": {\"id\": 1}}, ...],  # templated, accepts XComArg\n",{"type":62,"tag":579,"props":2561,"children":2562},{"class":581,"line":645},[2563],{"type":62,"tag":579,"props":2564,"children":2565},{},[2566],{"type":68,"value":2567},"    llm_conn_id=\"pydanticai_default\",   # reuses the same connection (API key only)\n",{"type":62,"tag":579,"props":2569,"children":2570},{"class":581,"line":654},[2571],{"type":62,"tag":579,"props":2572,"children":2573},{},[2574],{"type":68,"value":2575},"    embed_model=\"text-embedding-3-small\",\n",{"type":62,"tag":579,"props":2577,"children":2578},{"class":581,"line":907},[2579],{"type":62,"tag":579,"props":2580,"children":2581},{},[2582],{"type":68,"value":2583},"    persist_dir=f\"{AIRFLOW_HOME}\u002Finclude\u002Fmy_index\",  # optional; local path or s3:\u002F\u002F, gs:\u002F\u002F, ...\n",{"type":62,"tag":579,"props":2585,"children":2586},{"class":581,"line":1159},[2587],{"type":62,"tag":579,"props":2588,"children":2589},{},[2590],{"type":68,"value":1183},{"type":62,"tag":71,"props":2592,"children":2593},{},[2594,2596,2602,2604,2610],{"type":68,"value":2595},"The operator returns ",{"type":62,"tag":77,"props":2597,"children":2599},{"className":2598},[],[2600],{"type":68,"value":2601},"{\"chunks\": [{\"text\", \"metadata\", \"vector\"}], ...}",{"type":68,"value":2603},". Put a stable key into each document's ",{"type":62,"tag":77,"props":2605,"children":2607},{"className":2606},[],[2608],{"type":68,"value":2609},"metadata",{"type":68,"value":2611}," — it round-trips through chunking, so vectors can be mapped back to source records.",{"type":62,"tag":71,"props":2613,"children":2614},{},[2615,2628],{"type":62,"tag":94,"props":2616,"children":2617},{},[2618,2620,2626],{"type":68,"value":2619},"Option B: ",{"type":62,"tag":77,"props":2621,"children":2623},{"className":2622},[],[2624],{"type":68,"value":2625},"LlamaIndexHook",{"type":68,"value":2627}," for raw vectors",{"type":68,"value":2629}," (no operator, no persisted index). Shortest path when vectors go straight to a database:",{"type":62,"tag":174,"props":2631,"children":2633},{"className":1086,"code":2632,"language":1088,"meta":182,"style":182},"@task\ndef create_embeddings(rows):\n    from airflow.providers.common.ai.hooks.llamaindex import LlamaIndexHook\n    embed_model = LlamaIndexHook(\n        llm_conn_id=\"pydanticai_default\",\n        embed_model=\"text-embedding-3-small\",\n    ).get_embedding_model()\n    vectors = embed_model.get_text_embedding_batch([r[\"text\"] for r in rows])\n    return list(zip([r[\"id\"] for r in rows], vectors))\n",[2634],{"type":62,"tag":77,"props":2635,"children":2636},{"__ignoreMap":182},[2637,2645,2653,2661,2669,2677,2685,2693,2701],{"type":62,"tag":579,"props":2638,"children":2639},{"class":581,"line":582},[2640],{"type":62,"tag":579,"props":2641,"children":2642},{},[2643],{"type":68,"value":2644},"@task\n",{"type":62,"tag":579,"props":2646,"children":2647},{"class":581,"line":609},[2648],{"type":62,"tag":579,"props":2649,"children":2650},{},[2651],{"type":68,"value":2652},"def create_embeddings(rows):\n",{"type":62,"tag":579,"props":2654,"children":2655},{"class":581,"line":618},[2656],{"type":62,"tag":579,"props":2657,"children":2658},{},[2659],{"type":68,"value":2660},"    from airflow.providers.common.ai.hooks.llamaindex import LlamaIndexHook\n",{"type":62,"tag":579,"props":2662,"children":2663},{"class":581,"line":627},[2664],{"type":62,"tag":579,"props":2665,"children":2666},{},[2667],{"type":68,"value":2668},"    embed_model = LlamaIndexHook(\n",{"type":62,"tag":579,"props":2670,"children":2671},{"class":581,"line":636},[2672],{"type":62,"tag":579,"props":2673,"children":2674},{},[2675],{"type":68,"value":2676},"        llm_conn_id=\"pydanticai_default\",\n",{"type":62,"tag":579,"props":2678,"children":2679},{"class":581,"line":645},[2680],{"type":62,"tag":579,"props":2681,"children":2682},{},[2683],{"type":68,"value":2684},"        embed_model=\"text-embedding-3-small\",\n",{"type":62,"tag":579,"props":2686,"children":2687},{"class":581,"line":654},[2688],{"type":62,"tag":579,"props":2689,"children":2690},{},[2691],{"type":68,"value":2692},"    ).get_embedding_model()\n",{"type":62,"tag":579,"props":2694,"children":2695},{"class":581,"line":907},[2696],{"type":62,"tag":579,"props":2697,"children":2698},{},[2699],{"type":68,"value":2700},"    vectors = embed_model.get_text_embedding_batch([r[\"text\"] for r in rows])\n",{"type":62,"tag":579,"props":2702,"children":2703},{"class":581,"line":1159},[2704],{"type":62,"tag":579,"props":2705,"children":2706},{},[2707],{"type":68,"value":2708},"    return list(zip([r[\"id\"] for r in rows], vectors))\n",{"type":62,"tag":71,"props":2710,"children":2711},{},[2712,2724,2726,2731,2733,2738],{"type":62,"tag":94,"props":2713,"children":2714},{},[2715,2717,2722],{"type":68,"value":2716},"Option C: plain ",{"type":62,"tag":77,"props":2718,"children":2720},{"className":2719},[],[2721],{"type":68,"value":158},{"type":68,"value":2723}," with sentence-transformers",{"type":68,"value":2725}," (keeps the old local\u002Foffline behavior, no API cost; requires keeping ",{"type":62,"tag":77,"props":2727,"children":2729},{"className":2728},[],[2730],{"type":68,"value":497},{"type":68,"value":2732}," + ",{"type":62,"tag":77,"props":2734,"children":2736},{"className":2735},[],[2737],{"type":68,"value":504},{"type":68,"value":2739}," in requirements):",{"type":62,"tag":174,"props":2741,"children":2743},{"className":1086,"code":2742,"language":1088,"meta":182,"style":182},"@task\ndef embed_texts(texts: list[str]) -> list[list[float]]:\n    from sentence_transformers import SentenceTransformer\n    model = SentenceTransformer(\"all-MiniLM-L6-v2\")\n    return model.encode(texts, normalize_embeddings=True).tolist()\n",[2744],{"type":62,"tag":77,"props":2745,"children":2746},{"__ignoreMap":182},[2747,2754,2762,2770,2778],{"type":62,"tag":579,"props":2748,"children":2749},{"class":581,"line":582},[2750],{"type":62,"tag":579,"props":2751,"children":2752},{},[2753],{"type":68,"value":2644},{"type":62,"tag":579,"props":2755,"children":2756},{"class":581,"line":609},[2757],{"type":62,"tag":579,"props":2758,"children":2759},{},[2760],{"type":68,"value":2761},"def embed_texts(texts: list[str]) -> list[list[float]]:\n",{"type":62,"tag":579,"props":2763,"children":2764},{"class":581,"line":618},[2765],{"type":62,"tag":579,"props":2766,"children":2767},{},[2768],{"type":68,"value":2769},"    from sentence_transformers import SentenceTransformer\n",{"type":62,"tag":579,"props":2771,"children":2772},{"class":581,"line":627},[2773],{"type":62,"tag":579,"props":2774,"children":2775},{},[2776],{"type":68,"value":2777},"    model = SentenceTransformer(\"all-MiniLM-L6-v2\")\n",{"type":62,"tag":579,"props":2779,"children":2780},{"class":581,"line":636},[2781],{"type":62,"tag":579,"props":2782,"children":2783},{},[2784],{"type":68,"value":2785},"    return model.encode(texts, normalize_embeddings=True).tolist()\n",{"type":62,"tag":71,"props":2787,"children":2788},{},[2789,2791,2797,2799,2805,2807,2813,2815,2821],{"type":68,"value":2790},"Note on dimensions: switching from ",{"type":62,"tag":77,"props":2792,"children":2794},{"className":2793},[],[2795],{"type":68,"value":2796},"all-MiniLM-L6-v2",{"type":68,"value":2798}," (384) to ",{"type":62,"tag":77,"props":2800,"children":2802},{"className":2801},[],[2803],{"type":68,"value":2804},"text-embedding-3-small",{"type":68,"value":2806}," (1536) changes vector size — existing stored embeddings must be regenerated, and fixed-size vector columns (e.g. pgvector ",{"type":62,"tag":77,"props":2808,"children":2810},{"className":2809},[],[2811],{"type":68,"value":2812},"vector(384)",{"type":68,"value":2814},") need a schema change. Embed all texts in one task\u002Fbatch call rather than ",{"type":62,"tag":77,"props":2816,"children":2818},{"className":2817},[],[2819],{"type":68,"value":2820},".expand()",{"type":68,"value":2822}," per text: batching is one API round-trip and avoids per-task model loading.",{"type":62,"tag":291,"props":2824,"children":2825},{},[],{"type":62,"tag":162,"props":2827,"children":2829},{"id":2828},"step-4-update-imports",[2830],{"type":68,"value":2831},"Step 4: Update imports",{"type":62,"tag":686,"props":2833,"children":2834},{},[2835,2851],{"type":62,"tag":690,"props":2836,"children":2837},{},[2838],{"type":62,"tag":694,"props":2839,"children":2840},{},[2841,2846],{"type":62,"tag":698,"props":2842,"children":2843},{},[2844],{"type":68,"value":2845},"Old import",{"type":62,"tag":698,"props":2847,"children":2848},{},[2849],{"type":68,"value":2850},"New import",{"type":62,"tag":709,"props":2852,"children":2853},{},[2854,2871,2892,2912,2933,2955,2972],{"type":62,"tag":694,"props":2855,"children":2856},{},[2857,2866],{"type":62,"tag":716,"props":2858,"children":2859},{},[2860],{"type":62,"tag":77,"props":2861,"children":2863},{"className":2862},[],[2864],{"type":68,"value":2865},"import airflow_ai_sdk as ai_sdk",{"type":62,"tag":716,"props":2867,"children":2868},{},[2869],{"type":68,"value":2870},"Remove entirely",{"type":62,"tag":694,"props":2872,"children":2873},{},[2874,2883],{"type":62,"tag":716,"props":2875,"children":2876},{},[2877],{"type":62,"tag":77,"props":2878,"children":2880},{"className":2879},[],[2881],{"type":68,"value":2882},"from airflow_ai_sdk import BaseModel",{"type":62,"tag":716,"props":2884,"children":2885},{},[2886],{"type":62,"tag":77,"props":2887,"children":2889},{"className":2888},[],[2890],{"type":68,"value":2891},"from pydantic import BaseModel",{"type":62,"tag":694,"props":2893,"children":2894},{},[2895,2904],{"type":62,"tag":716,"props":2896,"children":2897},{},[2898],{"type":62,"tag":77,"props":2899,"children":2901},{"className":2900},[],[2902],{"type":68,"value":2903},"from airflow_ai_sdk.models.base import BaseModel",{"type":62,"tag":716,"props":2905,"children":2906},{},[2907],{"type":62,"tag":77,"props":2908,"children":2910},{"className":2909},[],[2911],{"type":68,"value":2891},{"type":62,"tag":694,"props":2913,"children":2914},{},[2915,2924],{"type":62,"tag":716,"props":2916,"children":2917},{},[2918],{"type":62,"tag":77,"props":2919,"children":2921},{"className":2920},[],[2922],{"type":68,"value":2923},"class Foo(ai_sdk.BaseModel):",{"type":62,"tag":716,"props":2925,"children":2926},{},[2927],{"type":62,"tag":77,"props":2928,"children":2930},{"className":2929},[],[2931],{"type":68,"value":2932},"class Foo(BaseModel):",{"type":62,"tag":694,"props":2934,"children":2935},{},[2936,2945],{"type":62,"tag":716,"props":2937,"children":2938},{},[2939],{"type":62,"tag":77,"props":2940,"children":2942},{"className":2941},[],[2943],{"type":68,"value":2944},"from pydantic_ai import Agent",{"type":62,"tag":716,"props":2946,"children":2947},{},[2948,2950],{"type":68,"value":2949},"Remove if Agent was only used for ",{"type":62,"tag":77,"props":2951,"children":2953},{"className":2952},[],[2954],{"type":68,"value":231},{"type":62,"tag":694,"props":2956,"children":2957},{},[2958,2967],{"type":62,"tag":716,"props":2959,"children":2960},{},[2961],{"type":62,"tag":77,"props":2962,"children":2964},{"className":2963},[],[2965],{"type":68,"value":2966},"from pydantic_ai.models.openai import OpenAIModel",{"type":62,"tag":716,"props":2968,"children":2969},{},[2970],{"type":68,"value":2971},"Remove (model config in connection now)",{"type":62,"tag":694,"props":2973,"children":2974},{},[2975,2980],{"type":62,"tag":716,"props":2976,"children":2977},{},[2978],{"type":68,"value":2979},"(new)",{"type":62,"tag":716,"props":2981,"children":2982},{},[2983,2989,2991,2996],{"type":62,"tag":77,"props":2984,"children":2986},{"className":2985},[],[2987],{"type":68,"value":2988},"from pydantic_ai.toolsets import FunctionToolset",{"type":68,"value":2990}," for ",{"type":62,"tag":77,"props":2992,"children":2994},{"className":2993},[],[2995],{"type":68,"value":231},{"type":68,"value":2997}," toolsets",{"type":62,"tag":71,"props":2999,"children":3000},{},[3001,3003,3008,3009,3014,3015,3020,3022,3028],{"type":68,"value":3002},"The ",{"type":62,"tag":77,"props":3004,"children":3006},{"className":3005},[],[3007],{"type":68,"value":223},{"type":68,"value":225},{"type":62,"tag":77,"props":3010,"children":3012},{"className":3011},[],[3013],{"type":68,"value":231},{"type":68,"value":225},{"type":62,"tag":77,"props":3016,"children":3018},{"className":3017},[],[3019],{"type":68,"value":238},{"type":68,"value":3021}," decorators are auto-registered by the provider. No explicit import needed beyond ",{"type":62,"tag":77,"props":3023,"children":3025},{"className":3024},[],[3026],{"type":68,"value":3027},"from airflow.sdk import task",{"type":68,"value":520},{"type":62,"tag":71,"props":3030,"children":3031},{},[3032,3038,3040,3046,3048,3053],{"type":62,"tag":77,"props":3033,"children":3035},{"className":3034},[],[3036],{"type":68,"value":3037},"pydantic_ai",{"type":68,"value":3039}," imports for non-decorator usage (e.g., ",{"type":62,"tag":77,"props":3041,"children":3043},{"className":3042},[],[3044],{"type":68,"value":3045},"BinaryContent",{"type":68,"value":3047}," for multimodal) are still valid since the new provider depends on ",{"type":62,"tag":77,"props":3049,"children":3051},{"className":3050},[],[3052],{"type":68,"value":462},{"type":68,"value":3054}," (>= 1.71.0 for provider 0.4.0).",{"type":62,"tag":291,"props":3056,"children":3057},{},[],{"type":62,"tag":162,"props":3059,"children":3061},{"id":3060},"step-5-update-connectionsyaml-if-used-for-local-testing",[3062],{"type":68,"value":3063},"Step 5: Update connections.yaml (if used for local testing)",{"type":62,"tag":174,"props":3065,"children":3069},{"className":3066,"code":3067,"language":3068,"meta":182,"style":182},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","pydanticai_default:\n  conn_type: pydanticai\n  password: \u003Capi-key>\n  extra:\n    model: \"openai:gpt-5\"\n","yaml",[3070],{"type":62,"tag":77,"props":3071,"children":3072},{"__ignoreMap":182},[3073,3086,3104,3121,3133],{"type":62,"tag":579,"props":3074,"children":3075},{"class":581,"line":582},[3076,3081],{"type":62,"tag":579,"props":3077,"children":3079},{"style":3078},"--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178",[3080],{"type":68,"value":561},{"type":62,"tag":579,"props":3082,"children":3083},{"style":592},[3084],{"type":68,"value":3085},":\n",{"type":62,"tag":579,"props":3087,"children":3088},{"class":581,"line":609},[3089,3094,3099],{"type":62,"tag":579,"props":3090,"children":3091},{"style":3078},[3092],{"type":68,"value":3093},"  conn_type",{"type":62,"tag":579,"props":3095,"children":3096},{"style":592},[3097],{"type":68,"value":3098},":",{"type":62,"tag":579,"props":3100,"children":3101},{"style":603},[3102],{"type":68,"value":3103}," pydanticai\n",{"type":62,"tag":579,"props":3105,"children":3106},{"class":581,"line":618},[3107,3112,3116],{"type":62,"tag":579,"props":3108,"children":3109},{"style":3078},[3110],{"type":68,"value":3111},"  password",{"type":62,"tag":579,"props":3113,"children":3114},{"style":592},[3115],{"type":68,"value":3098},{"type":62,"tag":579,"props":3117,"children":3118},{"style":603},[3119],{"type":68,"value":3120}," \u003Capi-key>\n",{"type":62,"tag":579,"props":3122,"children":3123},{"class":581,"line":627},[3124,3129],{"type":62,"tag":579,"props":3125,"children":3126},{"style":3078},[3127],{"type":68,"value":3128},"  extra",{"type":62,"tag":579,"props":3130,"children":3131},{"style":592},[3132],{"type":68,"value":3085},{"type":62,"tag":579,"props":3134,"children":3135},{"class":581,"line":636},[3136,3141,3145,3150,3154],{"type":62,"tag":579,"props":3137,"children":3138},{"style":3078},[3139],{"type":68,"value":3140},"    model",{"type":62,"tag":579,"props":3142,"children":3143},{"style":592},[3144],{"type":68,"value":3098},{"type":62,"tag":579,"props":3146,"children":3147},{"style":592},[3148],{"type":68,"value":3149}," \"",{"type":62,"tag":579,"props":3151,"children":3152},{"style":603},[3153],{"type":68,"value":729},{"type":62,"tag":579,"props":3155,"children":3156},{"style":592},[3157],{"type":68,"value":3158},"\"\n",{"type":62,"tag":71,"props":3160,"children":3161},{},[3162],{"type":68,"value":3163},"For custom endpoints:",{"type":62,"tag":174,"props":3165,"children":3167},{"className":3066,"code":3166,"language":3068,"meta":182,"style":182},"pydanticai_cortex:\n  conn_type: pydanticai\n  password: \u003Capi-key>\n  host: https:\u002F\u002Fmy-endpoint.com\u002Fv1\n  extra:\n    model: \"openai:llama3.1-8b\"\n",[3168],{"type":62,"tag":77,"props":3169,"children":3170},{"__ignoreMap":182},[3171,3182,3197,3212,3229,3240],{"type":62,"tag":579,"props":3172,"children":3173},{"class":581,"line":582},[3174,3178],{"type":62,"tag":579,"props":3175,"children":3176},{"style":3078},[3177],{"type":68,"value":974},{"type":62,"tag":579,"props":3179,"children":3180},{"style":592},[3181],{"type":68,"value":3085},{"type":62,"tag":579,"props":3183,"children":3184},{"class":581,"line":609},[3185,3189,3193],{"type":62,"tag":579,"props":3186,"children":3187},{"style":3078},[3188],{"type":68,"value":3093},{"type":62,"tag":579,"props":3190,"children":3191},{"style":592},[3192],{"type":68,"value":3098},{"type":62,"tag":579,"props":3194,"children":3195},{"style":603},[3196],{"type":68,"value":3103},{"type":62,"tag":579,"props":3198,"children":3199},{"class":581,"line":618},[3200,3204,3208],{"type":62,"tag":579,"props":3201,"children":3202},{"style":3078},[3203],{"type":68,"value":3111},{"type":62,"tag":579,"props":3205,"children":3206},{"style":592},[3207],{"type":68,"value":3098},{"type":62,"tag":579,"props":3209,"children":3210},{"style":603},[3211],{"type":68,"value":3120},{"type":62,"tag":579,"props":3213,"children":3214},{"class":581,"line":627},[3215,3220,3224],{"type":62,"tag":579,"props":3216,"children":3217},{"style":3078},[3218],{"type":68,"value":3219},"  host",{"type":62,"tag":579,"props":3221,"children":3222},{"style":592},[3223],{"type":68,"value":3098},{"type":62,"tag":579,"props":3225,"children":3226},{"style":603},[3227],{"type":68,"value":3228}," https:\u002F\u002Fmy-endpoint.com\u002Fv1\n",{"type":62,"tag":579,"props":3230,"children":3231},{"class":581,"line":636},[3232,3236],{"type":62,"tag":579,"props":3233,"children":3234},{"style":3078},[3235],{"type":68,"value":3128},{"type":62,"tag":579,"props":3237,"children":3238},{"style":592},[3239],{"type":68,"value":3085},{"type":62,"tag":579,"props":3241,"children":3242},{"class":581,"line":645},[3243,3247,3251,3255,3260],{"type":62,"tag":579,"props":3244,"children":3245},{"style":3078},[3246],{"type":68,"value":3140},{"type":62,"tag":579,"props":3248,"children":3249},{"style":592},[3250],{"type":68,"value":3098},{"type":62,"tag":579,"props":3252,"children":3253},{"style":592},[3254],{"type":68,"value":3149},{"type":62,"tag":579,"props":3256,"children":3257},{"style":603},[3258],{"type":68,"value":3259},"openai:llama3.1-8b",{"type":62,"tag":579,"props":3261,"children":3262},{"style":592},[3263],{"type":68,"value":3158},{"type":62,"tag":291,"props":3265,"children":3266},{},[],{"type":62,"tag":162,"props":3268,"children":3270},{"id":3269},"step-6-clean-up-env-vars",[3271],{"type":68,"value":3272},"Step 6: Clean up env vars",{"type":62,"tag":71,"props":3274,"children":3275},{},[3276,3278,3283],{"type":68,"value":3277},"The new provider reads model config from the ",{"type":62,"tag":77,"props":3279,"children":3281},{"className":3280},[],[3282],{"type":68,"value":142},{"type":68,"value":3284}," connection, so env vars that previously fed the model in code are usually redundant. Before removing any of them, grep the project (and any sibling scripts\u002Fservices) to confirm nothing else still references them:",{"type":62,"tag":174,"props":3286,"children":3289},{"className":3287,"code":3288,"language":68},[177],"OPENAI_API_KEY|OPENAI_BASE_URL|ANTHROPIC_API_KEY|GOOGLE_API_KEY\n",[3290],{"type":62,"tag":77,"props":3291,"children":3292},{"__ignoreMap":182},[3293],{"type":68,"value":3288},{"type":62,"tag":71,"props":3295,"children":3296},{},[3297,3299,3304],{"type":68,"value":3298},"Candidates for removal ",{"type":62,"tag":94,"props":3300,"children":3301},{},[3302],{"type":68,"value":3303},"only if no other code references them",{"type":68,"value":3098},{"type":62,"tag":942,"props":3306,"children":3307},{},[3308,3319,3330],{"type":62,"tag":195,"props":3309,"children":3310},{},[3311,3317],{"type":62,"tag":77,"props":3312,"children":3314},{"className":3313},[],[3315],{"type":68,"value":3316},"OPENAI_API_KEY",{"type":68,"value":3318}," (now in the pydanticai connection's password field)",{"type":62,"tag":195,"props":3320,"children":3321},{},[3322,3328],{"type":62,"tag":77,"props":3323,"children":3325},{"className":3324},[],[3326],{"type":68,"value":3327},"OPENAI_BASE_URL",{"type":68,"value":3329}," (now in the connection's host field)",{"type":62,"tag":195,"props":3331,"children":3332},{},[3333],{"type":68,"value":3334},"Custom model name vars (now in the connection's extra.model)",{"type":62,"tag":71,"props":3336,"children":3337},{},[3338,3340,3346],{"type":68,"value":3339},"If anything outside the migrated DAGs still uses them (other DAGs not yet migrated, helper scripts, non-Airflow services sharing the ",{"type":62,"tag":77,"props":3341,"children":3343},{"className":3342},[],[3344],{"type":68,"value":3345},".env",{"type":68,"value":3347},"), leave them in place.",{"type":62,"tag":71,"props":3349,"children":3350},{},[3351,3356,3357,3363],{"type":62,"tag":94,"props":3352,"children":3353},{},[3354],{"type":68,"value":3355},"Keep",{"type":68,"value":544},{"type":62,"tag":77,"props":3358,"children":3360},{"className":3359},[],[3361],{"type":68,"value":3362},"AIRFLOW_CONN_*",{"type":68,"value":3364}," env vars for all connections.",{"type":62,"tag":291,"props":3366,"children":3367},{},[],{"type":62,"tag":162,"props":3369,"children":3371},{"id":3370},"step-7-verify",[3372],{"type":68,"value":3373},"Step 7: Verify",{"type":62,"tag":71,"props":3375,"children":3376},{},[3377],{"type":68,"value":3378},"After migration, grep the codebase to confirm no stale references remain:",{"type":62,"tag":174,"props":3380,"children":3383},{"className":3381,"code":3382,"language":68},[177],"airflow_ai_sdk|airflow-ai-sdk|ai_sdk\\.BaseModel|from pydantic_ai import Agent|from pydantic_ai.models\n",[3384],{"type":62,"tag":77,"props":3385,"children":3386},{"__ignoreMap":182},[3387],{"type":68,"value":3382},{"type":62,"tag":71,"props":3389,"children":3390},{},[3391],{"type":68,"value":3392},"Verify:",{"type":62,"tag":942,"props":3394,"children":3397},{"className":3395},[3396],"contains-task-list",[3398,3415,3438,3459,3482,3499,3523,3545,3590],{"type":62,"tag":195,"props":3399,"children":3402},{"className":3400},[3401],"task-list-item",[3403,3408,3410],{"type":62,"tag":3404,"props":3405,"children":3407},"input",{"disabled":1114,"type":3406},"checkbox",[],{"type":68,"value":3409}," No imports from ",{"type":62,"tag":77,"props":3411,"children":3413},{"className":3412},[],[3414],{"type":68,"value":212},{"type":62,"tag":195,"props":3416,"children":3418},{"className":3417},[3401],[3419,3422,3424,3429,3431,3436],{"type":62,"tag":3404,"props":3420,"children":3421},{"disabled":1114,"type":3406},[],{"type":68,"value":3423}," No ",{"type":62,"tag":77,"props":3425,"children":3427},{"className":3426},[],[3428],{"type":68,"value":2303},{"type":68,"value":3430}," objects created for ",{"type":62,"tag":77,"props":3432,"children":3434},{"className":3433},[],[3435],{"type":68,"value":231},{"type":68,"value":3437}," (unless used outside decorators)",{"type":62,"tag":195,"props":3439,"children":3441},{"className":3440},[3401],[3442,3445,3446,3451,3453,3458],{"type":62,"tag":3404,"props":3443,"children":3444},{"disabled":1114,"type":3406},[],{"type":68,"value":3423},{"type":62,"tag":77,"props":3447,"children":3449},{"className":3448},[],[3450],{"type":68,"value":1877},{"type":68,"value":3452}," parameter on LLM decorators (should be ",{"type":62,"tag":77,"props":3454,"children":3456},{"className":3455},[],[3457],{"type":68,"value":1885},{"type":68,"value":2267},{"type":62,"tag":195,"props":3460,"children":3462},{"className":3461},[3401],[3463,3466,3468,3473,3475,3480],{"type":62,"tag":3404,"props":3464,"children":3465},{"disabled":1114,"type":3406},[],{"type":68,"value":3467}," All ",{"type":62,"tag":77,"props":3469,"children":3471},{"className":3470},[],[3472],{"type":68,"value":150},{"type":68,"value":3474}," replaced (LlamaIndex operator\u002Fhook or plain ",{"type":62,"tag":77,"props":3476,"children":3478},{"className":3477},[],[3479],{"type":68,"value":158},{"type":68,"value":3481},"); stored embeddings regenerated if the model\u002Fdimensions changed",{"type":62,"tag":195,"props":3483,"children":3485},{"className":3484},[3401],[3486,3489,3491,3497],{"type":62,"tag":3404,"props":3487,"children":3488},{"disabled":1114,"type":3406},[],{"type":68,"value":3490}," Vision translation functions return ",{"type":62,"tag":77,"props":3492,"children":3494},{"className":3493},[],[3495],{"type":68,"value":3496},"[text, BinaryContent(...)]",{"type":68,"value":3498}," again if they were string-only-restricted under common-ai 0.1.x",{"type":62,"tag":195,"props":3500,"children":3502},{"className":3501},[3401],[3503,3506,3508,3514,3516,3521],{"type":62,"tag":3404,"props":3504,"children":3505},{"disabled":1114,"type":3406},[],{"type":68,"value":3507}," Downstream consumers of ",{"type":62,"tag":77,"props":3509,"children":3511},{"className":3510},[],[3512],{"type":68,"value":3513},"output_type=BaseModel",{"type":68,"value":3515}," results use the XCom shape that actually arrives (dict on older cores, instance on newer; ",{"type":62,"tag":77,"props":3517,"children":3519},{"className":3518},[],[3520],{"type":68,"value":1562},{"type":68,"value":3522}," pins it)",{"type":62,"tag":195,"props":3524,"children":3526},{"className":3525},[3401],[3527,3530,3531,3536,3538,3543],{"type":62,"tag":3404,"props":3528,"children":3529},{"disabled":1114,"type":3406},[],{"type":68,"value":544},{"type":62,"tag":77,"props":3532,"children":3534},{"className":3533},[],[3535],{"type":68,"value":142},{"type":68,"value":3537}," connection configured in ",{"type":62,"tag":77,"props":3539,"children":3541},{"className":3540},[],[3542],{"type":68,"value":3345},{"type":68,"value":3544}," or connections.yaml",{"type":62,"tag":195,"props":3546,"children":3548},{"className":3547},[3401],[3549,3552,3553,3559,3561,3567,3569,3575,3577,3582,3583,3588],{"type":62,"tag":3404,"props":3550,"children":3551},{"disabled":1114,"type":3406},[],{"type":68,"value":544},{"type":62,"tag":77,"props":3554,"children":3556},{"className":3555},[],[3557],{"type":68,"value":3558},"requirements.txt",{"type":68,"value":3560}," has ",{"type":62,"tag":77,"props":3562,"children":3564},{"className":3563},[],[3565],{"type":68,"value":3566},"apache-airflow-providers-common-ai[...]",{"type":68,"value":3568}," instead of ",{"type":62,"tag":77,"props":3570,"children":3572},{"className":3571},[],[3573],{"type":68,"value":3574},"airflow-ai-sdk[...]",{"type":68,"value":3576},"; ",{"type":62,"tag":77,"props":3578,"children":3580},{"className":3579},[],[3581],{"type":68,"value":504},{"type":68,"value":433},{"type":62,"tag":77,"props":3584,"children":3586},{"className":3585},[],[3587],{"type":68,"value":497},{"type":68,"value":3589}," removed if no longer used",{"type":62,"tag":195,"props":3591,"children":3593},{"className":3592},[3401],[3594,3597,3599,3604,3606,3611,3613,3618],{"type":62,"tag":3404,"props":3595,"children":3596},{"disabled":1114,"type":3406},[],{"type":68,"value":3598}," Run the Dags end-to-end: tasks with ",{"type":62,"tag":77,"props":3600,"children":3602},{"className":3601},[],[3603],{"type":68,"value":1663},{"type":68,"value":3605}," or ",{"type":62,"tag":77,"props":3607,"children":3609},{"className":3608},[],[3610],{"type":68,"value":1520},{"type":68,"value":3612}," wait for human input, so the test plan must include acting on them (UI tab or ",{"type":62,"tag":77,"props":3614,"children":3616},{"className":3615},[],[3617],{"type":68,"value":2404},{"type":68,"value":3619}," REST)",{"type":62,"tag":291,"props":3621,"children":3622},{},[],{"type":62,"tag":162,"props":3624,"children":3626},{"id":3625},"quick-reference-new-features-in-common-ai-provider",[3627],{"type":68,"value":3628},"Quick reference: New features in common-ai provider",{"type":62,"tag":71,"props":3630,"children":3631},{},[3632],{"type":68,"value":3633},"These features are available after migration but have no airflow-ai-sdk equivalent:",{"type":62,"tag":686,"props":3635,"children":3636},{},[3637,3663],{"type":62,"tag":690,"props":3638,"children":3639},{},[3640],{"type":62,"tag":694,"props":3641,"children":3642},{},[3643,3648,3653,3658],{"type":62,"tag":698,"props":3644,"children":3645},{},[3646],{"type":68,"value":3647},"Feature",{"type":62,"tag":698,"props":3649,"children":3650},{},[3651],{"type":68,"value":3652},"Parameter \u002F API",{"type":62,"tag":698,"props":3654,"children":3655},{},[3656],{"type":68,"value":3657},"Since",{"type":62,"tag":698,"props":3659,"children":3660},{},[3661],{"type":68,"value":3662},"Description",{"type":62,"tag":709,"props":3664,"children":3665},{},[3666,3699,3737,3768,3806,3831,3857,3883,3928,3963,3996],{"type":62,"tag":694,"props":3667,"children":3668},{},[3669,3674,3689,3694],{"type":62,"tag":716,"props":3670,"children":3671},{},[3672],{"type":68,"value":3673},"HITL approval",{"type":62,"tag":716,"props":3675,"children":3676},{},[3677,3682,3684],{"type":62,"tag":77,"props":3678,"children":3680},{"className":3679},[],[3681],{"type":68,"value":1520},{"type":68,"value":3683}," on ",{"type":62,"tag":77,"props":3685,"children":3687},{"className":3686},[],[3688],{"type":68,"value":223},{"type":62,"tag":716,"props":3690,"children":3691},{},[3692],{"type":68,"value":3693},"0.1.0",{"type":62,"tag":716,"props":3695,"children":3696},{},[3697],{"type":68,"value":3698},"Pause for human review before returning",{"type":62,"tag":694,"props":3700,"children":3701},{},[3702,3707,3721,3725],{"type":62,"tag":716,"props":3703,"children":3704},{},[3705],{"type":68,"value":3706},"HITL review loop",{"type":62,"tag":716,"props":3708,"children":3709},{},[3710,3715,3716],{"type":62,"tag":77,"props":3711,"children":3713},{"className":3712},[],[3714],{"type":68,"value":1663},{"type":68,"value":3683},{"type":62,"tag":77,"props":3717,"children":3719},{"className":3718},[],[3720],{"type":68,"value":231},{"type":62,"tag":716,"props":3722,"children":3723},{},[3724],{"type":68,"value":3693},{"type":62,"tag":716,"props":3726,"children":3727},{},[3728,3730,3735],{"type":68,"value":3729},"Iterative review with regeneration (chat UI via ",{"type":62,"tag":77,"props":3731,"children":3733},{"className":3732},[],[3734],{"type":68,"value":2381},{"type":68,"value":3736}," plugin)",{"type":62,"tag":694,"props":3738,"children":3739},{},[3740,3745,3759,3763],{"type":62,"tag":716,"props":3741,"children":3742},{},[3743],{"type":68,"value":3744},"Durable execution",{"type":62,"tag":716,"props":3746,"children":3747},{},[3748,3753,3754],{"type":62,"tag":77,"props":3749,"children":3751},{"className":3750},[],[3752],{"type":68,"value":2254},{"type":68,"value":3683},{"type":62,"tag":77,"props":3755,"children":3757},{"className":3756},[],[3758],{"type":68,"value":231},{"type":62,"tag":716,"props":3760,"children":3761},{},[3762],{"type":68,"value":3693},{"type":62,"tag":716,"props":3764,"children":3765},{},[3766],{"type":68,"value":3767},"Step-level caching for resilience",{"type":62,"tag":694,"props":3769,"children":3770},{},[3771,3776,3791,3795],{"type":62,"tag":716,"props":3772,"children":3773},{},[3774],{"type":68,"value":3775},"Tool logging",{"type":62,"tag":716,"props":3777,"children":3778},{},[3779,3785,3786],{"type":62,"tag":77,"props":3780,"children":3782},{"className":3781},[],[3783],{"type":68,"value":3784},"enable_tool_logging=True",{"type":68,"value":3683},{"type":62,"tag":77,"props":3787,"children":3789},{"className":3788},[],[3790],{"type":68,"value":231},{"type":62,"tag":716,"props":3792,"children":3793},{},[3794],{"type":68,"value":3693},{"type":62,"tag":716,"props":3796,"children":3797},{},[3798,3800,3805],{"type":68,"value":3799},"INFO-level tool call logs (default: on; requires ",{"type":62,"tag":77,"props":3801,"children":3803},{"className":3802},[],[3804],{"type":68,"value":106},{"type":68,"value":2267},{"type":62,"tag":694,"props":3807,"children":3808},{},[3809,3814,3822,3826],{"type":62,"tag":716,"props":3810,"children":3811},{},[3812],{"type":68,"value":3813},"Model override",{"type":62,"tag":716,"props":3815,"children":3816},{},[3817],{"type":62,"tag":77,"props":3818,"children":3820},{"className":3819},[],[3821],{"type":68,"value":1499},{"type":62,"tag":716,"props":3823,"children":3824},{},[3825],{"type":68,"value":3693},{"type":62,"tag":716,"props":3827,"children":3828},{},[3829],{"type":68,"value":3830},"Override connection's model per-task",{"type":62,"tag":694,"props":3832,"children":3833},{},[3834,3839,3848,3852],{"type":62,"tag":716,"props":3835,"children":3836},{},[3837],{"type":68,"value":3838},"File analysis",{"type":62,"tag":716,"props":3840,"children":3841},{},[3842],{"type":62,"tag":77,"props":3843,"children":3845},{"className":3844},[],[3846],{"type":68,"value":3847},"@task.llm_file_analysis",{"type":62,"tag":716,"props":3849,"children":3850},{},[3851],{"type":68,"value":3693},{"type":62,"tag":716,"props":3853,"children":3854},{},[3855],{"type":68,"value":3856},"Analyze files\u002Fimages via ObjectStoragePath",{"type":62,"tag":694,"props":3858,"children":3859},{},[3860,3865,3874,3878],{"type":62,"tag":716,"props":3861,"children":3862},{},[3863],{"type":68,"value":3864},"NL-to-SQL",{"type":62,"tag":716,"props":3866,"children":3867},{},[3868],{"type":62,"tag":77,"props":3869,"children":3871},{"className":3870},[],[3872],{"type":68,"value":3873},"@task.llm_sql",{"type":62,"tag":716,"props":3875,"children":3876},{},[3877],{"type":68,"value":3693},{"type":62,"tag":716,"props":3879,"children":3880},{},[3881],{"type":68,"value":3882},"Generate SQL from natural language",{"type":62,"tag":694,"props":3884,"children":3885},{},[3886,3891,3901,3906],{"type":62,"tag":716,"props":3887,"children":3888},{},[3889],{"type":68,"value":3890},"Multimodal prompts",{"type":62,"tag":716,"props":3892,"children":3893},{},[3894,3896],{"type":68,"value":3895},"Translation function returns ",{"type":62,"tag":77,"props":3897,"children":3899},{"className":3898},[],[3900],{"type":68,"value":1583},{"type":62,"tag":716,"props":3902,"children":3903},{},[3904],{"type":68,"value":3905},"0.4.0",{"type":62,"tag":716,"props":3907,"children":3908},{},[3909,3911,3916,3917,3922,3923],{"type":68,"value":3910},"Vision and other binary content in ",{"type":62,"tag":77,"props":3912,"children":3914},{"className":3913},[],[3915],{"type":68,"value":223},{"type":68,"value":206},{"type":62,"tag":77,"props":3918,"children":3920},{"className":3919},[],[3921],{"type":68,"value":231},{"type":68,"value":206},{"type":62,"tag":77,"props":3924,"children":3926},{"className":3925},[],[3927],{"type":68,"value":238},{"type":62,"tag":694,"props":3929,"children":3930},{},[3931,3936,3954,3958],{"type":62,"tag":716,"props":3932,"children":3933},{},[3934],{"type":68,"value":3935},"Pydantic instance via XCom",{"type":62,"tag":716,"props":3937,"children":3938},{},[3939,3944,3946,3952],{"type":62,"tag":77,"props":3940,"children":3942},{"className":3941},[],[3943],{"type":68,"value":3513},{"type":68,"value":3945}," (with ",{"type":62,"tag":77,"props":3947,"children":3949},{"className":3948},[],[3950],{"type":68,"value":3951},"serialize_output",{"type":68,"value":3953}," opt-out)",{"type":62,"tag":716,"props":3955,"children":3956},{},[3957],{"type":68,"value":3905},{"type":62,"tag":716,"props":3959,"children":3960},{},[3961],{"type":68,"value":3962},"Instance flows through XCom on capable cores; dict fallback otherwise",{"type":62,"tag":694,"props":3964,"children":3965},{},[3966,3971,3987,3991],{"type":62,"tag":716,"props":3967,"children":3968},{},[3969],{"type":68,"value":3970},"Embeddings",{"type":62,"tag":716,"props":3972,"children":3973},{},[3974,3979,3981,3986],{"type":62,"tag":77,"props":3975,"children":3977},{"className":3976},[],[3978],{"type":68,"value":489},{"type":68,"value":3980}," (+ ",{"type":62,"tag":77,"props":3982,"children":3984},{"className":3983},[],[3985],{"type":68,"value":2503},{"type":68,"value":2267},{"type":62,"tag":716,"props":3988,"children":3989},{},[3990],{"type":68,"value":3905},{"type":62,"tag":716,"props":3992,"children":3993},{},[3994],{"type":68,"value":3995},"Connection-based embeddings + persisted vector index",{"type":62,"tag":694,"props":3997,"children":3998},{},[3999,4004,4012,4016],{"type":62,"tag":716,"props":4000,"children":4001},{},[4002],{"type":68,"value":4003},"Retrieval",{"type":62,"tag":716,"props":4005,"children":4006},{},[4007],{"type":62,"tag":77,"props":4008,"children":4010},{"className":4009},[],[4011],{"type":68,"value":2511},{"type":62,"tag":716,"props":4013,"children":4014},{},[4015],{"type":68,"value":3905},{"type":62,"tag":716,"props":4017,"children":4018},{},[4019],{"type":68,"value":4020},"Top-k similarity search over a persisted index",{"type":62,"tag":4022,"props":4023,"children":4024},"style",{},[4025],{"type":68,"value":4026},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":4028,"total":4192},[4029,4043,4055,4071,4084,4101,4114,4127,4142,4156,4166,4179],{"slug":14,"name":14,"fn":4030,"description":4031,"org":4032,"tags":4033,"stars":28,"repoUrl":29,"updatedAt":4042},"manage and troubleshoot Airflow via CLI","Queries, manages, and troubleshoots Apache Airflow using the `af` CLI. Use when working with anything related to Airflow - a DAG, a DAG run, a task log, an import or parse error, a broken DAG, or any Airflow operation. Covers listing and triggering DAGs, retrying runs, reading task logs, diagnosing failures, debugging import and parse errors, checking connections, variables and pools, exploring the REST API, and monitoring health (for example \"trigger a pipeline\", \"retry a run\", \"list connections\", \"check Airflow health\", \"why did my DAG fail\"). This is the entrypoint that routes to sibling skills for authoring, testing, deploying, and migrating Airflow 2 to 3. Not for warehouse\u002FSQL analytics on Airflow metadata tables (use analyzing-data); for deep root-cause reports use debugging-dags or airflow-investigation.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4034,4035,4038,4039],{"name":13,"slug":14,"type":15},{"name":4036,"slug":4037,"type":15},"CLI","cli",{"name":23,"slug":24,"type":15},{"name":4040,"slug":4041,"type":15},"Debugging","debugging","2026-04-06T18:01:43.992997",{"slug":4044,"name":4044,"fn":4045,"description":4046,"org":4047,"tags":4048,"stars":28,"repoUrl":29,"updatedAt":4054},"airflow-hitl","add human-in-the-loop steps to Airflow DAGs","Builds human-in-the-loop (HITL) Airflow workflows - approval gates, form input, and human-driven branching. Use when a DAG needs a human in the loop - an approval or reject step, sign-off before a task runs, a decision or approval UI, branching on a human choice, or collecting form input mid-run; also on mentions of ApprovalOperator, HITLOperator, HITLBranchOperator, HITLEntryOperator, or HITLTrigger. Requires Airflow 3.1+. Not for AI\u002FLLM task calls (see migrating-ai-sdk-to-common-ai).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4049,4050,4053],{"name":13,"slug":14,"type":15},{"name":4051,"slug":4052,"type":15},"Approvals","approvals",{"name":23,"slug":24,"type":15},"2026-04-06T18:01:46.758548",{"slug":4056,"name":4056,"fn":4057,"description":4058,"org":4059,"tags":4060,"stars":28,"repoUrl":29,"updatedAt":4070},"airflow-plugins","build Airflow UI plugins","Builds Airflow 3.1+ plugins that embed FastAPI apps, custom UI pages, React components, middleware, macros, and operator links directly into the Airflow UI. Use when building anything custom inside Airflow 3.1+ that involves Python and a browser-facing interface - creating an Airflow plugin, adding a custom UI page or nav entry, building FastAPI-backed endpoints inside Airflow, serving static assets from a plugin, embedding a React app, adding middleware to the API server, creating custom operator extra links, or calling the Airflow REST API from inside a plugin; also when AirflowPlugin, fastapi_apps, external_views, react_apps, or plugin registration come up.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4061,4062,4065,4067],{"name":13,"slug":14,"type":15},{"name":4063,"slug":4064,"type":15},"Plugin Development","plugin-development",{"name":4066,"slug":1088,"type":15},"Python",{"name":4068,"slug":4069,"type":15},"UI Components","ui-components","2026-04-06T18:01:56.827891",{"slug":4072,"name":4072,"fn":4073,"description":4074,"org":4075,"tags":4076,"stars":28,"repoUrl":29,"updatedAt":4083},"airflow-state-store","persist Airflow task and asset state","Persists task and asset state across retries and DAG runs using Airflow 3.3's AIP-103 key\u002Fvalue stores (`task_state_store`, `asset_state_store`) and the crash-safe `ResumableJobMixin`. Use when the user asks about task state store, checkpointing in tasks, persisting state across retries, job IDs surviving worker crashes, watermarks, asset metadata, resumable tasks, crash-safe operators, or \"what's new in Airflow 3.3\". Also use proactively when reading a DAG that uses Variables or XCom for intra-task coordination state — flag the anti-pattern and recommend task_state_store or asset_state_store instead. Requires Airflow 3.3+.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4077,4078,4079,4080],{"name":13,"slug":14,"type":15},{"name":20,"slug":21,"type":15},{"name":23,"slug":24,"type":15},{"name":4081,"slug":4082,"type":15},"Operations","operations","2026-07-07T06:43:11.160671",{"slug":4085,"name":4085,"fn":4086,"description":4087,"org":4088,"tags":4089,"stars":28,"repoUrl":29,"updatedAt":4100},"analyzing-data","query data warehouses for business questions","Queries the data warehouse with SQL and answers business questions about data. Use when answering anything that needs warehouse data - counts, metrics, trends, aggregations, joins across tables, data lookups, or ad-hoc SQL analysis (for example \"who uses X\", \"how many Y\", \"show me Z\", \"find customers\", \"what is the count\").",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4090,4093,4096,4097],{"name":4091,"slug":4092,"type":15},"Analytics","analytics",{"name":4094,"slug":4095,"type":15},"Data Analysis","data-analysis",{"name":20,"slug":21,"type":15},{"name":4098,"slug":4099,"type":15},"SQL","sql","2026-04-06T18:01:49.599775",{"slug":4102,"name":4102,"fn":4103,"description":4104,"org":4105,"tags":4106,"stars":28,"repoUrl":29,"updatedAt":4113},"annotating-task-lineage","annotate Airflow tasks with data lineage","Annotate Airflow tasks with data lineage using inlets and outlets. Use when the user wants to add lineage metadata to tasks, specify input\u002Foutput datasets, or enable lineage tracking for operators without built-in OpenLineage extraction.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4107,4108,4109,4110],{"name":13,"slug":14,"type":15},{"name":20,"slug":21,"type":15},{"name":23,"slug":24,"type":15},{"name":4111,"slug":4112,"type":15},"Observability","observability","2026-04-06T18:02:03.487365",{"slug":4115,"name":4115,"fn":4116,"description":4117,"org":4118,"tags":4119,"stars":28,"repoUrl":29,"updatedAt":4126},"authoring-dags","author Airflow DAGs","Workflow and best practices for writing Apache Airflow DAGs. Use when creating a new DAG, write pipeline code, handling questions about DAG patterns and conventions or extending an existing DAG with a follow-up\u002Fdownstream task. ANY request shaped like 'add a DAG named X', 'write a pipeline', 'add a task that runs after Y', or 'extend the DAG'. For testing and debugging DAGs, see the testing-dags skill.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4120,4121,4122,4125],{"name":13,"slug":14,"type":15},{"name":23,"slug":24,"type":15},{"name":4123,"slug":4124,"type":15},"ETL","etl",{"name":4066,"slug":1088,"type":15},"2026-04-06T18:01:52.679888",{"slug":4128,"name":4128,"fn":4129,"description":4130,"org":4131,"tags":4132,"stars":28,"repoUrl":29,"updatedAt":4141},"authoring-go-sdk-tasks","implement Airflow tasks in Go","Writes Airflow task logic in Go using the Airflow Go SDK. Use when the user wants to implement Airflow tasks in Go, asks about `BundleProvider`\u002F`RegisterDags`, the `bundlev1` Registry\u002FDag interfaces, registering Go tasks (`AddTask`\u002F`AddTaskWithName`), dependency injection by parameter type (`context.Context`, `sdk.TIRunContext`, `*slog.Logger`, `sdk.Client`), or reading connections\u002Fvariables\u002FXComs from Go. This skill covers the Go-specific native API; the shared Python-stub pattern and conceptual model live in authoring-language-sdk-tasks. For building\u002Fpacking\u002Fshipping the bundle see deploying-go-sdk-bundles; for coordinator config see configuring-airflow-language-sdks.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4133,4134,4137,4138],{"name":13,"slug":14,"type":15},{"name":4135,"slug":4136,"type":15},"API Development","api-development",{"name":23,"slug":24,"type":15},{"name":4139,"slug":4140,"type":15},"Go","go","2026-07-11T05:39:13.552213",{"slug":4143,"name":4143,"fn":4144,"description":4145,"org":4146,"tags":4147,"stars":28,"repoUrl":29,"updatedAt":4155},"authoring-java-sdk-tasks","implement Airflow tasks in Java","Writes Airflow task logic in Java, Kotlin, or any JVM language using the Airflow Java SDK. Use when the user wants to implement Airflow tasks in Java\u002FJVM, asks about `@Builder.Dag`\u002F`@Builder.Task`\u002F`@Builder.XCom`, the `Task`\u002F`BundleBuilder` interfaces, reading connections\u002Fvariables\u002FXComs from Java, the JSON-to-Java type mapping, or logging from Java tasks. This skill covers the Java-specific native API; the shared Python-stub pattern and conceptual model live in authoring-language-sdk-tasks. For building\u002Fshipping the bundle see deploying-java-sdk-bundles; for coordinator config see configuring-airflow-language-sdks.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4148,4149,4152],{"name":23,"slug":24,"type":15},{"name":4150,"slug":4151,"type":15},"Engineering","engineering",{"name":4153,"slug":4154,"type":15},"Java","java","2026-07-18T05:48:13.374003",{"slug":4157,"name":4157,"fn":4158,"description":4159,"org":4160,"tags":4161,"stars":28,"repoUrl":29,"updatedAt":4165},"authoring-language-sdk-tasks","implement non-Python Airflow tasks","The language-neutral foundation for Airflow language SDKs — implement task logic in a non-Python language while the DAG stays in Python. Use when the user wants to run an Airflow task in another language (Java, Kotlin, Go, or other JVM\u002Fnative languages), asks how the Python `@task.stub` pairs with native task code, how task\u002FDAG IDs must match across the two sides, how data passes via XCom as JSON, or which language SDKs exist. This skill owns the shared Python-stub pattern and conceptual model; for a specific language's native API, build, and runtime, use that language's skill (e.g. authoring-java-sdk-tasks, authoring-go-sdk-tasks).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4162,4163,4164],{"name":13,"slug":14,"type":15},{"name":23,"slug":24,"type":15},{"name":4150,"slug":4151,"type":15},"2026-07-18T05:11:54.496539",{"slug":4167,"name":4167,"fn":4168,"description":4169,"org":4170,"tags":4171,"stars":28,"repoUrl":29,"updatedAt":4178},"blueprint","build reusable Airflow task group templates","Define reusable Airflow task group templates with Pydantic validation and compose DAGs from YAML. Use when creating blueprint templates, composing DAGs from YAML, validating configurations, or enabling no-code DAG authoring for non-engineers.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4172,4173,4174,4175],{"name":13,"slug":14,"type":15},{"name":23,"slug":24,"type":15},{"name":4123,"slug":4124,"type":15},{"name":4176,"slug":4177,"type":15},"Templates","templates","2026-04-06T18:01:45.361425",{"slug":4180,"name":4180,"fn":4181,"description":4182,"org":4183,"tags":4184,"stars":28,"repoUrl":29,"updatedAt":4191},"checking-freshness","check data freshness in warehouses","Quick data freshness check. Use when the user asks if data is up to date, when a table was last updated, if data is stale, or needs to verify data currency before using it.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4185,4186,4187,4190],{"name":13,"slug":14,"type":15},{"name":20,"slug":21,"type":15},{"name":4188,"slug":4189,"type":15},"Data Quality","data-quality",{"name":4123,"slug":4124,"type":15},"2026-04-06T18:02:02.138565",34,{"items":4194,"total":4192},[4195,4202,4208,4215,4222,4229,4236],{"slug":14,"name":14,"fn":4030,"description":4031,"org":4196,"tags":4197,"stars":28,"repoUrl":29,"updatedAt":4042},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4198,4199,4200,4201],{"name":13,"slug":14,"type":15},{"name":4036,"slug":4037,"type":15},{"name":23,"slug":24,"type":15},{"name":4040,"slug":4041,"type":15},{"slug":4044,"name":4044,"fn":4045,"description":4046,"org":4203,"tags":4204,"stars":28,"repoUrl":29,"updatedAt":4054},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4205,4206,4207],{"name":13,"slug":14,"type":15},{"name":4051,"slug":4052,"type":15},{"name":23,"slug":24,"type":15},{"slug":4056,"name":4056,"fn":4057,"description":4058,"org":4209,"tags":4210,"stars":28,"repoUrl":29,"updatedAt":4070},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4211,4212,4213,4214],{"name":13,"slug":14,"type":15},{"name":4063,"slug":4064,"type":15},{"name":4066,"slug":1088,"type":15},{"name":4068,"slug":4069,"type":15},{"slug":4072,"name":4072,"fn":4073,"description":4074,"org":4216,"tags":4217,"stars":28,"repoUrl":29,"updatedAt":4083},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4218,4219,4220,4221],{"name":13,"slug":14,"type":15},{"name":20,"slug":21,"type":15},{"name":23,"slug":24,"type":15},{"name":4081,"slug":4082,"type":15},{"slug":4085,"name":4085,"fn":4086,"description":4087,"org":4223,"tags":4224,"stars":28,"repoUrl":29,"updatedAt":4100},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4225,4226,4227,4228],{"name":4091,"slug":4092,"type":15},{"name":4094,"slug":4095,"type":15},{"name":20,"slug":21,"type":15},{"name":4098,"slug":4099,"type":15},{"slug":4102,"name":4102,"fn":4103,"description":4104,"org":4230,"tags":4231,"stars":28,"repoUrl":29,"updatedAt":4113},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4232,4233,4234,4235],{"name":13,"slug":14,"type":15},{"name":20,"slug":21,"type":15},{"name":23,"slug":24,"type":15},{"name":4111,"slug":4112,"type":15},{"slug":4115,"name":4115,"fn":4116,"description":4117,"org":4237,"tags":4238,"stars":28,"repoUrl":29,"updatedAt":4126},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[4239,4240,4241,4242],{"name":13,"slug":14,"type":15},{"name":23,"slug":24,"type":15},{"name":4123,"slug":4124,"type":15},{"name":4066,"slug":1088,"type":15}]