zozo123 opened a new pull request, #76157:
URL: https://github.com/apache/airflow/pull/76157
Related: #74194 (part 2: the resumable standalone operator with deferrable
polling)
This builds on #74419 and #74517, which add `DatabricksGenieHook` (part 1).
It is stacked on #74517 because part 1 has not merged yet. The first four
commits on this branch are the part 1 commits plus one fixup that removes a
`common.ai` extra the rebase duplicated. **Only the last commit (`Add a
resumable Databricks Genie operator`) is new in this PR.** I will rebase once
part 1 merges. Please treat it as a draft until then.
### What it adds
- `DatabricksGenieOperator` (`operators/genie.py`): asks a Genie space a
question, either as a new conversation or as a follow-up in an existing
`conversation_id`. It returns the same bounded result that the hook produces.
It can also attach to an existing message through `conversation_id` +
`message_id`.
- `DatabricksGenieMessageTrigger` (`triggers/genie.py`): polls the message
status from the triggerer when `deferrable=True`. It only reads the status. The
worker fetches and bounds the result when it resumes.
- A how-to page (`docs/operators/genie_operator.rst`), a system test Dag
(`example_databricks_genie.py`), the `provider.yaml` / `get_provider_info.py`
registrations, and unit tests for both classes.
### Design
- **Resuming.** The operator uses `ResumableJobMixin`. On Airflow 3.3+ with
`durable=True`, it stores `{space_id, conversation_id, message_id,
fingerprint}` in the task state store. A retry does one of three things:
- reconnects to a message that is still running;
- returns the answer of a message that has completed;
- asks again if the message failed, was cancelled or expired.
- **Question fingerprint.** The fingerprint is a hash of the space, the
conversation and the question. If the templated question changes between tries,
the stored message is ignored, so an old answer is never returned for a new
question.
- **Unconfirmed submissions.** Sending the question is not idempotent. The
operator writes a short-lived marker before the POST and removes it once the
message IDs are stored.
- A definite 4xx rejection removes the marker, and the task can retry
normally.
- A timeout, connection error or 5xx fails the task with
`AirflowFailException` and keeps the marker, because Genie may have accepted
the question.
- The next try refuses to ask again. The user can attach to the message
with `conversation_id`/`message_id`, or opt in with `resubmit_unconfirmed=True`.
- **Deferral.** Before it defers, the worker reads the message once through
the hook's HTTPS-enforcing request path, so a message that has already finished
never defers. The trigger checks the connection scheme the same way and allows
plain HTTP only to loopback addresses. `timeout` bounds both the sync wait and
the deferral.
- **Before Airflow 3.3.** There is no task state store, so the operator
behaves like a normal non-resumable task: each try asks again.
- **Cancellation.** There is no `on_kill`, because the Genie conversation
API has no endpoint to cancel a message.
### Dependency on part 1 and limitations
- This uses hook internals from #74517: `_request(..., retry_read=False)`,
`_summarize_result`, `_truncate_result` and `_PENDING_STATUSES`. If part 1
exposes public primitives (for example `start_message` / `get_message` /
`summarize_message`), this operator can switch to them and get smaller. I
implemented against the interface as it stands in #74517 at `dba5ef1`.
- `breeze` could not run locally because no Docker was available, so the
`update-providers-build-files`, `check-provider-yaml-valid` and
`check-template-fields-valid` hooks were skipped. I edited `provider.yaml` and
`get_provider_info.py` by hand and am relying on CI for these checks.
### Testing
All of these passed locally on Python 3.13 with Airflow 3.3+, so the durable
tests ran:
- `uv run --project providers/databricks pytest
providers/databricks/tests/unit/databricks/{operators,triggers,hooks}/test_genie.py`:
104 passed.
- `uv run --project providers/databricks pytest
providers/databricks/tests/unit -n 8`: 1294 passed, 13 skipped.
- `prek run --stage pre-commit --files <changed files>`: everything passed
except the three Docker-only hooks above.
- `ruff format` / `ruff check` (0.17.0): clean.
- `mypy` on the new files: clean.
- `scripts/in_container/run_template_fields_check.py` on the operator:
passed.
---
##### Was generative AI tooling used to co-author this PR?
- [X] Yes (please specify the tool below)
Generated-by: Grok Bot following [the
guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions)
---
* Read the **[Pull Request
Guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#pull-request-guidelines)**
for more information. Note: commit author/co-author name and email in commits
become permanently public when merged.
* For fundamental code changes, an Airflow Improvement Proposal
([AIP](https://cwiki.apache.org/confluence/display/AIRFLOW/Airflow+Improvement+Proposals))
is needed.
* When adding dependency, check compliance with the [ASF 3rd Party License
Policy](https://www.apache.org/legal/resolved.html#category-x).
* For significant user-facing changes create newsfragment:
`{pr_number}.significant.rst`, in
[airflow-core/newsfragments](https://github.com/apache/airflow/tree/main/airflow-core/newsfragments).
You can add this file in a follow-up commit after the PR is created so you
know the PR number.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]