airflow.providers.common.ai.batch.state¶
Run-stable idempotency state for @task.llm_batch, persisted outside XCom.
XCom is cleared at the start of every retry attempt (unlike a deferral
resume), so a batch_id pushed to XCom on submit is gone by the time a
retry’s execute() runs – the recovery this module exists for cannot rely
on XCom at all. State lives as a small JSON file on the same
ObjectStoragePath as the batch’s results, at
{result_path}/_airflow_batch_state/{key}.json, following the durable-cache
pattern in durable/storage.py.
Two invariants that must not be relaxed:
The identity key never includes
try_number– that is the entire point of “run-stable”: a retry must compute the same key as the attempt before it, so it can find and re-attach to that attempt’s in-flight batch.The input fingerprint does include the output schema (see
compute_fingerprint()) – changingoutput_typewithout changing the prompts must still be treated as a different batch, since the schema is part of what gets sent to the provider.
Attributes¶
Classes¶
The on-disk record at |
Functions¶
|
Return the run-stable identity key for one task instance (excluding |
|
Return the short form of an identity key, used as the |
|
Hash the output schema material on its own. |
|
Hash everything that determines the request content and which account it is billed to. |
|
Phase A: record intent to submit, before any network call. |
|
Phase B: overwrite the intent record with the full record, once the provider has accepted the batch. |
|
Return the recorded state for |
|
Delete the recorded state for |
Module Contents¶
- airflow.providers.common.ai.batch.state.compute_identity_key(*, dag_id, task_id, run_id, map_index)[source]¶
Return the run-stable identity key for one task instance (excluding
try_number).Uses
\x00rather than_to join the components – a plain_-joined string collides (Dagetl+ taskload_dataand Dagetl_load+ taskdataboth yieldetl_load_data), which would let one task instance read or overwrite another’s batch state. Mirrorsdurable/storage.py’sDurableStorageidentity hash verbatim, for the same reason.
- airflow.providers.common.ai.batch.state.key16(key)[source]¶
Return the short form of an identity key, used as the
custom_idprefix and result filename stem.16 hex characters is 64 bits of the full SHA-256 digest – collision risk across the (at most tens of thousands of requests in) a single batch, or across the handful of concurrent task instances writing under the same
result_path, is astronomically below the odds of a provider-side outage; a shorter, more manageable id is worth that trade for something humans read in filenames and provider dashboards.
- airflow.providers.common.ai.batch.state.compute_output_schema_digest(output_schema)[source]¶
Hash the output schema material on its own.
Stored alongside the combined input fingerprint so a stale-state error message can say which part changed (prompts vs. the
output_typeschema) instead of only “something changed”.
- airflow.providers.common.ai.batch.state.compute_fingerprint(*, requests, llm_conn_id, model_id, system_prompt, max_tokens, request_params, output_schema)[source]¶
Hash everything that determines the request content and which account it is billed to.
output_schemais caller-supplied (the literal string"str"for an unstructured batch, or anOutputSpec.json_schemadict otherwise) – this module does not know how to derive a schema fromoutput_typeitself; that logic lives inbatch/output_schema.py, which this module does not import.Schema is included deliberately: it is sent to the provider as part of every request body (OpenAI’s
response_format, Anthropic’stools[0].input_schema), so a change to it – an added field, an edited description – is a change to the request content, not just to how the response gets parsed. Without this, clearing a task after editing its Pydanticoutput_typewould silently re-attach to a batch whose results were produced under the old schema.llm_conn_idis included for a different but equally serious reason: twopydanticaiconnections can point at two different accounts (or even two different providers’ API keys entirely). Without it, switchingllm_conn_idand rerunning would silently re-attach to – and return the results of – a batch submitted under a completely different account than the one the current run is configured to use.
- class airflow.providers.common.ai.batch.state.BatchStateRecord[source]¶
The on-disk record at
{result_path}/_airflow_batch_state/{key}.json.Written in two phases (see
write_intent()/write_submitted()) so a crash between “submit request sent” and “submit response received” leaves a trace the next attempt can act on (seefind_orphaned_batch()).Re-attach eligibility is decided by comparing
input_fingerprint, neveroutput_type_ref– the ref is a human-readable label only and can stay identical while the schema underneath it changes.
- airflow.providers.common.ai.batch.state.write_intent(result_path, *, key, input_fingerprint, output_schema_digest, intent_at)[source]¶
Phase A: record intent to submit, before any network call.
batch_idisnull; this record exists so a crash between “submit sent” and “submit response received” leaves a trace. Overwritten bywrite_submitted()once the provider responds.intent_atfeeds orphan recovery’snot_beforecheck.
- airflow.providers.common.ai.batch.state.write_submitted(result_path, *, key, input_fingerprint, output_schema_digest, intent_at, adapter, llm_conn_id, model_id, output_type_ref, batch_id, provider_input_ref, request_count, submitted_at)[source]¶
Phase B: overwrite the intent record with the full record, once the provider has accepted the batch.
- airflow.providers.common.ai.batch.state.read_state(result_path, key)[source]¶
Return the recorded state for
key, orNoneif no record exists.Only a genuine not-found reads as “no recorded batch”. Any other failure (a transient object-storage error, a corrupt file, a record written by a different schema version) raises
LLMBatchStateReadErrorinstead of degrading toNone, because “could not read the state” and “there is no state” are different facts and conflating them turns an I/O blip into a duplicate, billable submission.
- airflow.providers.common.ai.batch.state.delete_state(result_path, key)[source]¶
Delete the recorded state for
keyso the next attempt submits a fresh batch.Only two outcomes call this: a provider-side
failed(nothing ran) and a cancellation that lost requests. Every path where the batch is still alive or its results are still worth re-landing leaves the file in place.