Skip to content

Add Databricks agent invocation operator and hook - #74386

Merged
kaxil merged 19 commits into
apache:mainfrom
AlejandroMorgante:add-databricks-agent-invoke
Oct 22, 2026
Merged

kaxil merged 19 commits into
apache:mainfrom
AlejandroMorgante:add-databricks-agent-invoke

Conversation

@AlejandroMorgante

@AlejandroMorgante AlejandroMorgante commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Add DatabricksAgentInvokeOperator and DatabricksAgentHook to invoke agents hosted with DurableAgentServer on Databricks Apps. Supports service principal OAuth, background submission, synchronous polling and deferrable waiting, preserving the full invocation response in XCom.

The hook implements BaseManagedAgentHook for Common AI and toolset use, with a default session per invocation. Request, state and error handling were checked against the runtime and templates in Databricks' databricks-ai-bridge repository, tag databricks-agentbricks-v0.4.0. Stored failures are distinguished from transient HTTP errors, and interruptions nested in the output are returned by the operator and rejected by Common AI.

Invocation IDs remain stable across retries and clears with unchanged input and session. Stored failures stop task retries and require a new ID to run again. GET timeouts use the minimum of the hook's HTTP timeout and the remaining budget; synchronous POST requests retain the full remaining budget. Deadline checks reject late results, but network activity and OAuth refresh can delay timeout reporting. Background hook methods accept the app URL per call.

Includes documentation, focused unit tests and a deterministic system-test fixture requiring no model endpoint.

Validation:

  • After the GET timeout and per-call URL fixes, 174 hook, operator and trigger tests passed in Breeze, plus five local timeout-budget probes.
  • After adding an async HTTP-options regression test and strengthening the decreasing polling-budget test, the affected modules passed again: 120 hook tests and 46 operator tests.
  • The system test was rerun successfully against a real Databricks workspace with OAuth, covering synchronous, Common AI and deferrable invocation, before the final test-only changes.
  • The Databricks documentation build passed after clarifying timeout guarantees. Ruff and commit hooks passed for the final test changes. Broader checks are left to CI.

Earlier validation included the full Databricks suite (1087 passed, 12 skipped), 142 agent tests with the minimum tenacity==8.3.0, five local checks against the Databricks 0.4.0 runtime with simulated OAuth, targeted mypy, static/manual checks, spellcheck and the dependency lower-bound checker. Those broader and minimum-dependency checks have not been repeated for the latest changes.

See the fixture README for deployment, configuration and cleanup. The fixture uses an in-memory store and does not test recovery across app restarts. With the app and configuration prepared:

SYSTEM_TESTS_ENV_ID=your-unique-id \
BREEZE_INIT_COMMAND='set -a; . /files/databricks-agent-system-test.env; set +a' \
breeze testing system-tests \
  --backend sqlite \
  --forward-credentials \
  --test-timeout 2400 \
  providers/databricks/tests/system/databricks/example_databricks_agent.py \
  -q

Was generative AI tooling used to co-author this PR?
  • Yes — Codex (GPT-6)

Generated-by: Codex (GPT-6) following the guidelines

@AlejandroMorgante

Copy link
Copy Markdown
Contributor Author

@SameerMesiah97 could you please help me with a review when you have some time? Thanks! 🙏

@AlejandroMorgante
AlejandroMorgante marked this pull request as draft October 9, 2026 19:31
@AlejandroMorgante
AlejandroMorgante marked this pull request as ready for review October 9, 2026 19:31
Comment thread providers/databricks/src/airflow/providers/databricks/hooks/agent.py Outdated
Comment thread providers/databricks/docs/operators/agent.rst
@AlejandroMorgante
AlejandroMorgante force-pushed the add-databricks-agent-invoke branch from 152283d to e13d7dd Compare October 9, 2026 23:25
Comment thread providers/databricks/src/airflow/providers/databricks/operators/agent.py Outdated
Comment thread providers/databricks/src/airflow/providers/databricks/hooks/agent.py Outdated
Comment thread providers/databricks/src/airflow/providers/databricks/hooks/agent.py Outdated
Comment thread providers/databricks/docs/operators/agent.rst Outdated
Comment thread providers/databricks/src/airflow/providers/databricks/hooks/agent.py Outdated
Comment thread providers/databricks/src/airflow/providers/databricks/operators/agent.py Outdated
Comment thread providers/databricks/tests/unit/databricks/operators/test_agent.py
Comment thread providers/databricks/tests/unit/databricks/hooks/test_agent.py Outdated
Comment thread providers/databricks/tests/system/databricks/example_databricks_agent.py Outdated
@AlejandroMorgante
AlejandroMorgante marked this pull request as draft October 10, 2026 15:25
@AlejandroMorgante
AlejandroMorgante force-pushed the add-databricks-agent-invoke branch from 7266b14 to 7f3b704 Compare October 10, 2026 15:28
@AlejandroMorgante

Copy link
Copy Markdown
Contributor Author

End-to-end validation

Validated commit 68fc3d9b60d8c6f24ef4e074d6bf052d09fd5ff2 against a real Databricks App running the deterministic agent fixture.

Both synchronous and deferrable invocations completed successfully. Verification tasks confirmed the response content, original input, and session ID. The deferrable task resumed successfully after the trigger fired.

Environment: Airflow 3.3.0, Python 3.12, PostgreSQL, and LocalExecutor. The test loaded the provider source from the PR commit. No model endpoint was used.

Successful Airflow run

Synchronous and deferrable tasks and their result checks succeeded

Synchronous invocation

Synchronous invocation submission, polling, and completion

Deferrable invocation

Deferral, successful trigger event, and invocation completion

Agent input and output

Original input and validated agent response

Databricks App

Deterministic agent running in Databricks

Databricks invocation logs

Accepted POST requests and successful GET requests for both invocations

The temporary OAuth secret was revoked and the Databricks App stopped after validation.

@AlejandroMorgante
AlejandroMorgante marked this pull request as ready for review October 11, 2026 01:05
Comment thread providers/databricks/src/airflow/providers/databricks/operators/agent.py Outdated
Comment thread providers/databricks/src/airflow/providers/databricks/hooks/agent.py Outdated
@AlejandroMorgante
AlejandroMorgante force-pushed the add-databricks-agent-invoke branch from 68fc3d9 to 5416249 Compare October 13, 2026 20:22
Comment thread providers/databricks/src/airflow/providers/databricks/operators/agent.py Outdated
Comment thread providers/databricks/pyproject.toml Outdated
Comment thread providers/databricks/docs/operators/agent.rst Outdated
Comment thread providers/databricks/docs/operators/agent.rst Outdated
@AlejandroMorgante
AlejandroMorgante force-pushed the add-databricks-agent-invoke branch 2 times, most recently from 975f4c8 to f3522fd Compare October 20, 2026 01:23
Comment thread providers/databricks/src/airflow/providers/databricks/hooks/agent.py Outdated
@AlejandroMorgante
AlejandroMorgante force-pushed the add-databricks-agent-invoke branch 2 times, most recently from 1983021 to 89fdc43 Compare October 22, 2026 02:58

@kaxil kaxil left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving, the earlier points are all sorted. None of these need to hold the merge:

  • In the text extraction at hooks/agent.py:253-267, one non-text block in the last assistant message hits the break, and text ends up as the JSON dump of the whole transcript. ChatDatabricks with use_responses_api=True puts reasoning and mcp_call blocks right next to the answer, so this can show up with ordinary agents. I'd skip those blocks and only fall back when no text is found.
  • agent.rst:155-157 should say a literal invocation_id has to be unique per Dag run. Otherwise the next run gets the stored answer back, or a 409 if the prompt changed.
  • hooks/agent.py:223-226 is only reachable because test_reraised_http_error_remains_transient sets retry_args["reraise"] itself; the constructor doesn't accept retry_args. I'd drop the branch and the test.
  • The operator fixture's submit reply has no status, so test_defers never hits the != "completed" check with a value the server actually sends (queued/active).
  • The hook tests sleep a real second per retry, since wait_exponential ignores retry_delay=0 on the first wait. Stubbing retry_args["sleep"] in the fixture covers the whole file.
  • caller isn't passed through, so every request goes out as operator/Unknown.

Also curious why this is limited to client-secret service principals. _get_token already gets workspace OAuth tokens for the federated modes from the same endpoint.

Allow Airflow tasks to invoke DurableAgentServer agents deployed on Databricks Apps and wait without occupying a worker.

Generated-by: Codex (GPT-6)
Keep the new operator documentation valid for Sphinx and recognize the official MLflow spelling.

Generated-by: Codex (GPT-6)
Keep the operator guide focused on user configuration and invocation, following the existing Databricks documentation conventions.

Generated-by: Codex (GPT-6)
A completed response must not turn an expired invocation wait into success. Recoverable async disconnects should not fail a task immediately, and polling failures need safe context for diagnosis.

Generated-by: Codex (GPT-6)
Common AI consumers need to consult Databricks agents through the same contract as Vertex AI and Bedrock. Existing background and deferrable workflows must remain usable without installing the optional Common AI provider.

Generated-by: Codex (GPT-6)
Replacement mocks should follow Airflow's testing conventions so they remain tied to the API they represent.

Generated-by: Codex (GPT-6)
Mocked responses must reflect the Databricks runtime contract so session,
interruption, failure and idempotency defects cannot hide behind passing
tests. Non-default settings and unconditional assertions make routing and
polling regressions observable.

This draft PR step intentionally exposes failures pending implementation
fixes in the following commits.

Generated-by: Codex (GPT-6)
Stored failures and template interruptions must not be treated as
transient errors or completed answers. Task retries need to respect
the server's idempotency rules, and toolset calls need a session.

Generated-by: Codex (GPT-6)
Conditional expectations made individual regression cases harder to assess during review.

Generated-by: Codex (GPT-6)
Make agent task progress traceable from submission through completion without exposing request or response contents.

Generated-by: Codex (GPT-6)
Completed background submissions omit the agent output, explicit request
budgets can exceed the default HTTP timeout, and netrc credentials must
not replace service principal authentication.

Generated-by: Codex (GPT-6)
Common AI consumers need the final assistant answer rather than a JSON
envelope containing intermediate tool calls and their results. The official
OpenAI and LangGraph templates serialize assistant messages differently.

Generated-by: Codex (GPT-6)
Known failed invocations must fail the task even when waiting is disabled.
Common AI users also need the supported dependency floor and guidance on
agent tools and timeouts to avoid unintended duplicate invocations.

Generated-by: Codex (GPT-6)
The OpenAI template permits SDK versions that reject Common AI text blocks.
String message content matches the documented Databricks input format and
is accepted by both OpenAI and LangGraph templates.

Generated-by: Codex (GPT-6)
Status requests should not consume the entire invocation budget, and connection-only hooks should support background invocations across apps.

Generated-by: Codex (GPT-6)
Network activity and OAuth refresh can delay deadline detection, so callers should not interpret the invocation budget as a strict wall-clock cutoff.

Generated-by: Codex (GPT-6)
Guard against losing async HTTP options or restarting the polling budget between status requests.

Generated-by: Codex (GPT-6)
Ensure alternate Tenacity error propagation preserves transient HTTP failures instead of treating them as terminal agent failures.

Generated-by: Codex (GPT-6)
Allow existing provider authentication methods and preserve useful response text when agents emit mixed content blocks.

Generated-by: Codex (GPT-6)
@AlejandroMorgante
AlejandroMorgante force-pushed the add-databricks-agent-invoke branch from a2c4a89 to 3340196 Compare October 22, 2026 22:35
@AlejandroMorgante

Copy link
Copy Markdown
Contributor Author

@kaxil

Thanks for the thorough review and all the suggestions!

Applied all the follow-ups: mixed-content text extraction, invocation ID documentation, removal of the reraise=True branch and test, realistic deferral states, mocked retry sleeps, caller propagation, and OAuth federation through the base hook.

Validated client-secret OAuth, AWS federation, a custom OIDC token provider, and Kubernetes federation using a local service-account token. The system test passed again after rebasing, covering synchronous, managed-agent, and deferrable execution. Pre-commit checks passed.

@kaxil
kaxil merged commit 7ac92c9 into apache:main Oct 22, 2026
99 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants