diff --git a/airflow-core/docs/img/airflow_erd.sha256 b/airflow-core/docs/img/airflow_erd.sha256
index be504c724ae4e..45c4ecd626588 100644
--- a/airflow-core/docs/img/airflow_erd.sha256
+++ b/airflow-core/docs/img/airflow_erd.sha256
@@ -1 +1 @@
-6e452af5ca150404e6bd7553fb6a9adfcd7081ca60dfd09efea0f9aae7460cc6
\ No newline at end of file
+88337ee3cd515161de57099c2582d4b26d22666a30387fa32aec89e86d7beeb3
\ No newline at end of file
diff --git a/airflow-core/docs/img/airflow_erd.svg b/airflow-core/docs/img/airflow_erd.svg
index 3b5cb89169434..5cb8260c69f52 100644
--- a/airflow-core/docs/img/airflow_erd.svg
+++ b/airflow-core/docs/img/airflow_erd.svg
@@ -12,214 +12,214 @@
log
-
-log
-
-id
-
- [INTEGER]
- NOT NULL
-
-dag_id
-
- [VARCHAR(250)]
-
-dttm
-
- [TIMESTAMP]
-
-event
-
- [VARCHAR(60)]
-
-extra
-
- [TEXT]
-
-logical_date
-
- [TIMESTAMP]
-
-map_index
-
- [INTEGER]
-
-owner
-
- [VARCHAR(500)]
-
-owner_display_name
-
- [VARCHAR(500)]
-
-run_id
-
- [VARCHAR(250)]
-
-task_id
-
- [VARCHAR(250)]
-
-try_number
-
- [INTEGER]
+
+log
+
+id
+
+ [INTEGER]
+ NOT NULL
+
+dag_id
+
+ [VARCHAR(250)]
+
+dttm
+
+ [TIMESTAMP]
+
+event
+
+ [VARCHAR(60)]
+
+extra
+
+ [TEXT]
+
+logical_date
+
+ [TIMESTAMP]
+
+map_index
+
+ [INTEGER]
+
+owner
+
+ [VARCHAR(500)]
+
+owner_display_name
+
+ [VARCHAR(500)]
+
+run_id
+
+ [VARCHAR(250)]
+
+task_id
+
+ [VARCHAR(250)]
+
+try_number
+
+ [INTEGER]
dag_priority_parsing_request
-
-dag_priority_parsing_request
-
-id
-
- [VARCHAR(32)]
- NOT NULL
-
-bundle_name
-
- [VARCHAR(250)]
- NOT NULL
-
-relative_fileloc
-
- [VARCHAR(2000)]
- NOT NULL
+
+dag_priority_parsing_request
+
+id
+
+ [VARCHAR(32)]
+ NOT NULL
+
+bundle_name
+
+ [VARCHAR(250)]
+ NOT NULL
+
+relative_fileloc
+
+ [VARCHAR(2000)]
+ NOT NULL
job
-
-job
-
-id
-
- [INTEGER]
- NOT NULL
-
-dag_id
-
- [VARCHAR(250)]
-
-end_date
-
- [TIMESTAMP]
-
-executor_class
-
- [VARCHAR(500)]
-
-hostname
-
- [VARCHAR(500)]
-
-job_type
-
- [VARCHAR(30)]
-
-latest_heartbeat
-
- [TIMESTAMP]
-
-start_date
-
- [TIMESTAMP]
-
-state
-
- [VARCHAR(20)]
-
-unixname
-
- [VARCHAR(1000)]
+
+job
+
+id
+
+ [INTEGER]
+ NOT NULL
+
+dag_id
+
+ [VARCHAR(250)]
+
+end_date
+
+ [TIMESTAMP]
+
+executor_class
+
+ [VARCHAR(500)]
+
+hostname
+
+ [VARCHAR(500)]
+
+job_type
+
+ [VARCHAR(30)]
+
+latest_heartbeat
+
+ [TIMESTAMP]
+
+start_date
+
+ [TIMESTAMP]
+
+state
+
+ [VARCHAR(20)]
+
+unixname
+
+ [VARCHAR(1000)]
callback_request
-
-callback_request
-
-id
-
- [INTEGER]
- NOT NULL
-
-callback_data
-
- [JSONB]
- NOT NULL
-
-callback_type
-
- [VARCHAR(20)]
- NOT NULL
-
-created_at
-
- [TIMESTAMP]
- NOT NULL
-
-priority_weight
-
- [INTEGER]
- NOT NULL
+
+callback_request
+
+id
+
+ [INTEGER]
+ NOT NULL
+
+callback_data
+
+ [JSONB]
+ NOT NULL
+
+callback_type
+
+ [VARCHAR(20)]
+ NOT NULL
+
+created_at
+
+ [TIMESTAMP]
+ NOT NULL
+
+priority_weight
+
+ [INTEGER]
+ NOT NULL
import_error
-
-import_error
-
-id
-
- [INTEGER]
- NOT NULL
-
-bundle_name
-
- [VARCHAR(250)]
-
-filename
-
- [VARCHAR(1024)]
-
-stacktrace
-
- [TEXT]
-
-timestamp
-
- [TIMESTAMP]
+
+import_error
+
+id
+
+ [INTEGER]
+ NOT NULL
+
+bundle_name
+
+ [VARCHAR(250)]
+
+filename
+
+ [VARCHAR(1024)]
+
+stacktrace
+
+ [TEXT]
+
+timestamp
+
+ [TIMESTAMP]
dag_bundle
-
-dag_bundle
-
-name
-
- [VARCHAR(250)]
- NOT NULL
-
-active
-
- [BOOLEAN]
-
-last_refreshed
-
- [TIMESTAMP]
-
-signed_url_template
-
- [VARCHAR(200)]
-
-template_params
-
- [JSON]
-
-version
-
- [VARCHAR(200)]
+
+dag_bundle
+
+name
+
+ [VARCHAR(250)]
+ NOT NULL
+
+active
+
+ [BOOLEAN]
+
+last_refreshed
+
+ [TIMESTAMP]
+
+signed_url_template
+
+ [VARCHAR(200)]
+
+template_params
+
+ [JSON]
+
+version
+
+ [VARCHAR(200)]
@@ -240,9 +240,9 @@
dag_bundle--dag_bundle_team
-
-0..N
-1
+
+0..N
+1
@@ -358,9 +358,9 @@
dag_bundle--dag
-
-0..N
-1
+
+0..N
+1
@@ -744,170 +744,170 @@
team
-
-team
-
-id
-
- [UUID]
- NOT NULL
-
-name
-
- [VARCHAR(50)]
- NOT NULL
+
+team
+
+id
+
+ [UUID]
+ NOT NULL
+
+name
+
+ [VARCHAR(50)]
+ NOT NULL
team--dag_bundle_team
-
-0..N
-1
+
+0..N
+1
-
+
-variable
-
-variable
-
-id
-
- [INTEGER]
- NOT NULL
-
-description
-
- [TEXT]
-
-is_encrypted
-
- [BOOLEAN]
-
-key
-
- [VARCHAR(250)]
-
-team_id
-
- [UUID]
-
-val
-
- [TEXT]
+connection
+
+connection
+
+id
+
+ [INTEGER]
+ NOT NULL
+
+conn_id
+
+ [VARCHAR(250)]
+ NOT NULL
+
+conn_type
+
+ [VARCHAR(500)]
+ NOT NULL
+
+description
+
+ [TEXT]
+
+extra
+
+ [TEXT]
+
+host
+
+ [VARCHAR(500)]
+
+is_encrypted
+
+ [BOOLEAN]
+
+is_extra_encrypted
+
+ [BOOLEAN]
+
+login
+
+ [TEXT]
+
+password
+
+ [TEXT]
+
+port
+
+ [INTEGER]
+
+schema
+
+ [VARCHAR(500)]
+
+team_id
+
+ [UUID]
-
+
-team--variable
-
-0..N
-{0,1}
+team--connection
+
+0..N
+{0,1}
-
+
-connection
-
-connection
-
-id
-
- [INTEGER]
- NOT NULL
-
-conn_id
-
- [VARCHAR(250)]
- NOT NULL
-
-conn_type
-
- [VARCHAR(500)]
- NOT NULL
-
-description
-
- [TEXT]
-
-extra
-
- [TEXT]
-
-host
-
- [VARCHAR(500)]
-
-is_encrypted
-
- [BOOLEAN]
-
-is_extra_encrypted
-
- [BOOLEAN]
-
-login
-
- [TEXT]
-
-password
-
- [TEXT]
-
-port
-
- [INTEGER]
-
-schema
-
- [VARCHAR(500)]
-
-team_id
-
- [UUID]
+variable
+
+variable
+
+id
+
+ [INTEGER]
+ NOT NULL
+
+description
+
+ [TEXT]
+
+is_encrypted
+
+ [BOOLEAN]
+
+key
+
+ [VARCHAR(250)]
+
+team_id
+
+ [UUID]
+
+val
+
+ [TEXT]
-
+
-team--connection
-
-0..N
-{0,1}
+team--variable
+
+0..N
+{0,1}
slot_pool
-
-slot_pool
-
-id
-
- [INTEGER]
- NOT NULL
-
-description
-
- [TEXT]
-
-include_deferred
-
- [BOOLEAN]
- NOT NULL
-
-pool
-
- [VARCHAR(256)]
-
-slots
-
- [INTEGER]
-
-team_id
-
- [UUID]
+
+slot_pool
+
+id
+
+ [INTEGER]
+ NOT NULL
+
+description
+
+ [TEXT]
+
+include_deferred
+
+ [BOOLEAN]
+ NOT NULL
+
+pool
+
+ [VARCHAR(256)]
+
+slots
+
+ [INTEGER]
+
+team_id
+
+ [UUID]
team--slot_pool
-
-0..N
-{0,1}
+
+0..N
+{0,1}
@@ -1087,16 +1087,16 @@
asset--dag_schedule_asset_reference
-
-0..N
-1
+
+0..N
+1
asset--task_outlet_asset_reference
-
-0..N
-1
+
+0..N
+1
@@ -1108,9 +1108,9 @@
asset--asset_dag_run_queue
-
-0..N
-1
+
+0..N
+1
@@ -1433,68 +1433,72 @@
hitl_detail
-
-hitl_detail
-
-ti_id
-
- [UUID]
- NOT NULL
-
-body
-
- [TEXT]
-
-chosen_options
-
- [JSON]
-
-defaults
-
- [JSON]
-
-multiple
-
- [BOOLEAN]
-
-options
-
- [JSON]
- NOT NULL
-
-params
-
- [JSON]
- NOT NULL
-
-params_input
-
- [JSON]
- NOT NULL
-
-respondents
-
- [JSON]
-
-response_at
-
- [TIMESTAMP]
-
-subject
-
- [TEXT]
- NOT NULL
-
-user_id
-
- [VARCHAR(128)]
+
+hitl_detail
+
+ti_id
+
+ [UUID]
+ NOT NULL
+
+body
+
+ [TEXT]
+
+chosen_options
+
+ [JSON]
+
+defaults
+
+ [JSON]
+
+multiple
+
+ [BOOLEAN]
+
+options
+
+ [JSON]
+ NOT NULL
+
+params
+
+ [JSON]
+ NOT NULL
+
+params_input
+
+ [JSON]
+ NOT NULL
+
+responded_user_id
+
+ [VARCHAR(128)]
+
+responded_user_name
+
+ [VARCHAR(128)]
+
+respondents
+
+ [JSON]
+
+response_at
+
+ [TIMESTAMP]
+
+subject
+
+ [TEXT]
+ NOT NULL
task_instance--hitl_detail
-
-1
-1
+
+1
+1
@@ -2414,13 +2418,13 @@
alembic_version
-
-alembic_version
-
-version_num
-
- [VARCHAR(32)]
- NOT NULL
+
+alembic_version
+
+version_num
+
+ [VARCHAR(32)]
+ NOT NULL
diff --git a/airflow-core/src/airflow/api_fastapi/common/parameters.py b/airflow-core/src/airflow/api_fastapi/common/parameters.py
index 5dcecc6bb5c80..2351a5c0ea541 100644
--- a/airflow-core/src/airflow/api_fastapi/common/parameters.py
+++ b/airflow-core/src/airflow/api_fastapi/common/parameters.py
@@ -957,15 +957,27 @@ def _optional_boolean(value: bool | None) -> bool | None:
)
),
]
-QueryHITLDetailUserIdFilter = Annotated[
+QueryHITLDetailRespondedUserIdFilter = Annotated[
FilterParam[list[str]],
Depends(
filter_param_factory(
- HITLDetail.user_id,
+ HITLDetail.responded_user_id,
list[str],
FilterOptionEnum.ANY_EQUAL,
default_factory=list,
- filter_name="user_id",
+ filter_name="responded_user_id",
+ )
+ ),
+]
+QueryHITLDetailRespondedUserNameFilter = Annotated[
+ FilterParam[list[str]],
+ Depends(
+ filter_param_factory(
+ HITLDetail.responded_user_name,
+ list[str],
+ FilterOptionEnum.ANY_EQUAL,
+ default_factory=list,
+ filter_name="responded_user_name",
)
),
]
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/hitl.py b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/hitl.py
index 09d74944c3aa7..ea0f045539ee6 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/hitl.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/hitl.py
@@ -37,7 +37,8 @@ class UpdateHITLDetailPayload(BaseModel):
class HITLDetailResponse(BaseModel):
"""Response of updating a Human-in-the-loop detail."""
- user_id: str
+ responded_user_id: str
+ responded_user_name: str
response_at: datetime
chosen_options: list[str] = Field(min_length=1)
params_input: Mapping = Field(default_factory=dict)
@@ -58,7 +59,8 @@ class HITLDetail(BaseModel):
respondents: list[str] | None = None
# Response Content Detail
- user_id: str | None = None
+ responded_user_id: str | None = None
+ responded_user_name: str | None = None
response_at: datetime | None = None
chosen_options: list[str] | None = None
params_input: dict[str, Any] = Field(default_factory=dict)
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
index 81f0a2acb2a8c..67e93c40c5461 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
+++ b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
@@ -8179,14 +8179,22 @@ paths:
- type: boolean
- type: 'null'
title: Response Received
- - name: user_id
+ - name: responded_user_id
in: query
required: false
schema:
type: array
items:
type: string
- title: User Id
+ title: Responded User Id
+ - name: responded_user_name
+ in: query
+ required: false
+ schema:
+ type: array
+ items:
+ type: string
+ title: Responded User Name
- name: subject_search
in: query
required: false
@@ -10822,11 +10830,16 @@ components:
type: array
- type: 'null'
title: Respondents
- user_id:
+ responded_user_id:
anyOf:
- type: string
- type: 'null'
- title: User Id
+ title: Responded User Id
+ responded_user_name:
+ anyOf:
+ - type: string
+ - type: 'null'
+ title: Responded User Name
response_at:
anyOf:
- type: string
@@ -10873,9 +10886,12 @@ components:
description: Schema for a collection of Human-in-the-loop details.
HITLDetailResponse:
properties:
- user_id:
+ responded_user_id:
+ type: string
+ title: Responded User Id
+ responded_user_name:
type: string
- title: User Id
+ title: Responded User Name
response_at:
type: string
format: date-time
@@ -10892,7 +10908,8 @@ components:
title: Params Input
type: object
required:
- - user_id
+ - responded_user_id
+ - responded_user_name
- response_at
- chosen_options
title: HITLDetailResponse
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py
index 082a04f727c79..ce2774a6826d1 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py
@@ -31,11 +31,12 @@
QueryHITLDetailDagIdFilter,
QueryHITLDetailDagIdPatternSearch,
QueryHITLDetailDagRunIdFilter,
+ QueryHITLDetailRespondedUserIdFilter,
+ QueryHITLDetailRespondedUserNameFilter,
QueryHITLDetailResponseReceivedFilter,
QueryHITLDetailSubjectSearch,
QueryHITLDetailTaskIdFilter,
QueryHITLDetailTaskIdPatternSearch,
- QueryHITLDetailUserIdFilter,
QueryLimit,
QueryOffset,
QueryTIStateFilter,
@@ -121,19 +122,21 @@ def _update_hitl_detail(
),
)
+ user_id = user.get_id()
+ user_name = user.get_name()
if hitl_detail_model.respondents:
- user_id = user.get_id()
if isinstance(user_id, int):
# FabAuthManager (ab_user) store user id as integer, but common interface is string type
user_id = str(user_id)
if user_id not in hitl_detail_model.respondents:
- log.error("User=%s is not a respondent for the task", user_id)
+ log.error("User=%s (id=%s) is not a respondent for the task", user_name, user_id)
raise HTTPException(
status.HTTP_403_FORBIDDEN,
- f"User={user_id} is not a respondent for the task.",
+ f"User={user_name} (id={user_id}) is not a respondent for the task.",
)
- hitl_detail_model.user_id = user.get_id()
+ hitl_detail_model.responded_user_id = user_id
+ hitl_detail_model.responded_user_name = user_name
hitl_detail_model.response_at = timezone.utcnow()
hitl_detail_model.chosen_options = update_hitl_detail_payload.chosen_options
hitl_detail_model.params_input = update_hitl_detail_payload.params_input
@@ -269,7 +272,8 @@ def get_hitl_details(
ti_state: QueryTIStateFilter,
# hitl detail related filter
response_received: QueryHITLDetailResponseReceivedFilter,
- user_id: QueryHITLDetailUserIdFilter,
+ responded_user_id: QueryHITLDetailRespondedUserIdFilter,
+ responded_user_name: QueryHITLDetailRespondedUserNameFilter,
subject_patten: QueryHITLDetailSubjectSearch,
body_patten: QueryHITLDetailBodySearch,
) -> HITLDetailCollection:
@@ -292,7 +296,8 @@ def get_hitl_details(
ti_state,
# hitl detail related filter
response_received,
- user_id,
+ responded_user_id,
+ responded_user_name,
subject_patten,
body_patten,
],
diff --git a/airflow-core/src/airflow/api_fastapi/execution_api/datamodels/hitl.py b/airflow-core/src/airflow/api_fastapi/execution_api/datamodels/hitl.py
index 0a51c314a9f98..4162d2cf7717c 100644
--- a/airflow-core/src/airflow/api_fastapi/execution_api/datamodels/hitl.py
+++ b/airflow-core/src/airflow/api_fastapi/execution_api/datamodels/hitl.py
@@ -51,7 +51,8 @@ class HITLDetailResponse(BaseModel):
"""Schema for the response part of a Human-in-the-loop detail for a specific task instance."""
response_received: bool
- user_id: str | None
+ responded_user_name: str | None
+ responded_user_id: str | None
response_at: datetime | None
# It's empty if the user has not yet responded.
chosen_options: list[str] | None
@@ -62,7 +63,8 @@ def from_hitl_detail_orm(cls, hitl_detail: HITLDetail) -> HITLDetailResponse:
return HITLDetailResponse(
response_received=hitl_detail.response_received,
response_at=hitl_detail.response_at,
- user_id=hitl_detail.user_id,
+ responded_user_id=hitl_detail.responded_user_id,
+ responded_user_name=hitl_detail.responded_user_name,
chosen_options=hitl_detail.chosen_options,
params_input=hitl_detail.params_input or {},
)
diff --git a/airflow-core/src/airflow/api_fastapi/execution_api/routes/hitl.py b/airflow-core/src/airflow/api_fastapi/execution_api/routes/hitl.py
index d0a0e33278c59..c9efbda78f04a 100644
--- a/airflow-core/src/airflow/api_fastapi/execution_api/routes/hitl.py
+++ b/airflow-core/src/airflow/api_fastapi/execution_api/routes/hitl.py
@@ -77,7 +77,7 @@ def upsert_hitl_detail(
elif hitl_detail_model.response_received:
# Cleanup the response part of HITLDetail as we only store one response for one task instance.
# It normally happens after retry, we keep only the latest response.
- hitl_detail_model.user_id = None
+ hitl_detail_model.responded_by = None
hitl_detail_model.response_at = None
hitl_detail_model.chosen_options = None
hitl_detail_model.params_input = {}
@@ -116,7 +116,8 @@ def update_hitl_detail(
f"Human-in-the-loop detail for Task Instance with id {ti_id_str} already exists.",
)
- hitl_detail_model.user_id = "Fallback to defaults"
+ hitl_detail_model.responded_user_id = HITLDetail.DEFAULT_USER_NAME
+ hitl_detail_model.responded_user_name = HITLDetail.DEFAULT_USER_NAME
hitl_detail_model.response_at = datetime.now(timezone.utc)
hitl_detail_model.chosen_options = payload.chosen_options
hitl_detail_model.params_input = payload.params_input
diff --git a/airflow-core/src/airflow/migrations/versions/0076_3_1_0_add_human_in_the_loop_response.py b/airflow-core/src/airflow/migrations/versions/0076_3_1_0_add_human_in_the_loop_response.py
index 2bffd734109c4..3e0ede0183ee1 100644
--- a/airflow-core/src/airflow/migrations/versions/0076_3_1_0_add_human_in_the_loop_response.py
+++ b/airflow-core/src/airflow/migrations/versions/0076_3_1_0_add_human_in_the_loop_response.py
@@ -61,7 +61,8 @@ def upgrade():
Column("params", sqlalchemy_jsonfield.JSONField(json=json), nullable=False, default={}),
Column("respondents", sqlalchemy_jsonfield.JSONField(json=json), nullable=True),
Column("response_at", UtcDateTime, nullable=True),
- Column("user_id", String(128), nullable=True),
+ Column("responded_user_id", String(128), nullable=True),
+ Column("responded_user_name", String(128), nullable=True),
Column("chosen_options", sqlalchemy_jsonfield.JSONField(json=json), nullable=True),
Column("params_input", sqlalchemy_jsonfield.JSONField(json=json), nullable=False, default={}),
ForeignKeyConstraint(
diff --git a/airflow-core/src/airflow/models/hitl.py b/airflow-core/src/airflow/models/hitl.py
index 068441d6d2e1d..448939e2c6f61 100644
--- a/airflow-core/src/airflow/models/hitl.py
+++ b/airflow-core/src/airflow/models/hitl.py
@@ -48,7 +48,8 @@ class HITLDetail(Base):
# Response Content Detail
response_at = Column(UtcDateTime, nullable=True)
- user_id = Column(String(128), nullable=True)
+ responded_user_id = Column(String(128), nullable=True)
+ responded_user_name = Column(String(128), nullable=True)
chosen_options = Column(
sqlalchemy_jsonfield.JSONField(json=json),
nullable=True,
@@ -78,3 +79,5 @@ def response_received(self) -> bool:
@response_received.expression # type: ignore[no-redef]
def response_received(cls):
return cls.response_at.is_not(None)
+
+ DEFAULT_USER_NAME = "Fallback to defaults"
diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
index ba486a353aac5..9ceb20e3948fe 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
@@ -713,7 +713,7 @@ export const UseHumanInTheLoopServiceGetHitlDetailKeyFn = ({ dagId, dagRunId, ma
export type HumanInTheLoopServiceGetHitlDetailsDefaultResponse = Awaited>;
export type HumanInTheLoopServiceGetHitlDetailsQueryResult = UseQueryResult;
export const useHumanInTheLoopServiceGetHitlDetailsKey = "HumanInTheLoopServiceGetHitlDetails";
-export const UseHumanInTheLoopServiceGetHitlDetailsKeyFn = ({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskId, taskIdPattern, userId }: {
+export const UseHumanInTheLoopServiceGetHitlDetailsKeyFn = ({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, respondedUserId, respondedUserName, responseReceived, state, subjectSearch, taskId, taskIdPattern }: {
bodySearch?: string;
dagId?: string;
dagIdPattern?: string;
@@ -721,13 +721,14 @@ export const UseHumanInTheLoopServiceGetHitlDetailsKeyFn = ({ bodySearch, dagId,
limit?: number;
offset?: number;
orderBy?: string[];
+ respondedUserId?: string[];
+ respondedUserName?: string[];
responseReceived?: boolean;
state?: string[];
subjectSearch?: string;
taskId?: string;
taskIdPattern?: string;
- userId?: string[];
-} = {}, queryKey?: Array) => [useHumanInTheLoopServiceGetHitlDetailsKey, ...(queryKey ?? [{ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskId, taskIdPattern, userId }])];
+} = {}, queryKey?: Array) => [useHumanInTheLoopServiceGetHitlDetailsKey, ...(queryKey ?? [{ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, respondedUserId, respondedUserName, responseReceived, state, subjectSearch, taskId, taskIdPattern }])];
export type MonitorServiceGetHealthDefaultResponse = Awaited>;
export type MonitorServiceGetHealthQueryResult = UseQueryResult;
export const useMonitorServiceGetHealthKey = "MonitorServiceGetHealth";
diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
index 2f44048e710b8..01e19b91afee7 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
@@ -1363,13 +1363,14 @@ export const ensureUseHumanInTheLoopServiceGetHitlDetailData = (queryClient: Que
* @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
* @param data.state
* @param data.responseReceived
-* @param data.userId
+* @param data.respondedUserId
+* @param data.respondedUserName
* @param data.subjectSearch SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
* @param data.bodySearch SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
* @returns HITLDetailCollection Successful Response
* @throws ApiError
*/
-export const ensureUseHumanInTheLoopServiceGetHitlDetailsData = (queryClient: QueryClient, { bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskId, taskIdPattern, userId }: {
+export const ensureUseHumanInTheLoopServiceGetHitlDetailsData = (queryClient: QueryClient, { bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, respondedUserId, respondedUserName, responseReceived, state, subjectSearch, taskId, taskIdPattern }: {
bodySearch?: string;
dagId?: string;
dagIdPattern?: string;
@@ -1377,13 +1378,14 @@ export const ensureUseHumanInTheLoopServiceGetHitlDetailsData = (queryClient: Qu
limit?: number;
offset?: number;
orderBy?: string[];
+ respondedUserId?: string[];
+ respondedUserName?: string[];
responseReceived?: boolean;
state?: string[];
subjectSearch?: string;
taskId?: string;
taskIdPattern?: string;
- userId?: string[];
-} = {}) => queryClient.ensureQueryData({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailsKeyFn({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskId, taskIdPattern, userId }), queryFn: () => HumanInTheLoopService.getHitlDetails({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskId, taskIdPattern, userId }) });
+} = {}) => queryClient.ensureQueryData({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailsKeyFn({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, respondedUserId, respondedUserName, responseReceived, state, subjectSearch, taskId, taskIdPattern }), queryFn: () => HumanInTheLoopService.getHitlDetails({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, respondedUserId, respondedUserName, responseReceived, state, subjectSearch, taskId, taskIdPattern }) });
/**
* Get Health
* @returns HealthInfoResponse Successful Response
diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
index d91da2d7f0732..d2bfe420b190b 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
@@ -1363,13 +1363,14 @@ export const prefetchUseHumanInTheLoopServiceGetHitlDetail = (queryClient: Query
* @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
* @param data.state
* @param data.responseReceived
-* @param data.userId
+* @param data.respondedUserId
+* @param data.respondedUserName
* @param data.subjectSearch SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
* @param data.bodySearch SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
* @returns HITLDetailCollection Successful Response
* @throws ApiError
*/
-export const prefetchUseHumanInTheLoopServiceGetHitlDetails = (queryClient: QueryClient, { bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskId, taskIdPattern, userId }: {
+export const prefetchUseHumanInTheLoopServiceGetHitlDetails = (queryClient: QueryClient, { bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, respondedUserId, respondedUserName, responseReceived, state, subjectSearch, taskId, taskIdPattern }: {
bodySearch?: string;
dagId?: string;
dagIdPattern?: string;
@@ -1377,13 +1378,14 @@ export const prefetchUseHumanInTheLoopServiceGetHitlDetails = (queryClient: Quer
limit?: number;
offset?: number;
orderBy?: string[];
+ respondedUserId?: string[];
+ respondedUserName?: string[];
responseReceived?: boolean;
state?: string[];
subjectSearch?: string;
taskId?: string;
taskIdPattern?: string;
- userId?: string[];
-} = {}) => queryClient.prefetchQuery({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailsKeyFn({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskId, taskIdPattern, userId }), queryFn: () => HumanInTheLoopService.getHitlDetails({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskId, taskIdPattern, userId }) });
+} = {}) => queryClient.prefetchQuery({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailsKeyFn({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, respondedUserId, respondedUserName, responseReceived, state, subjectSearch, taskId, taskIdPattern }), queryFn: () => HumanInTheLoopService.getHitlDetails({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, respondedUserId, respondedUserName, responseReceived, state, subjectSearch, taskId, taskIdPattern }) });
/**
* Get Health
* @returns HealthInfoResponse Successful Response
diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
index be2271f380feb..5afeeb81a7e5f 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
@@ -1363,13 +1363,14 @@ export const useHumanInTheLoopServiceGetHitlDetail = = unknown[]>({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskId, taskIdPattern, userId }: {
+export const useHumanInTheLoopServiceGetHitlDetails = = unknown[]>({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, respondedUserId, respondedUserName, responseReceived, state, subjectSearch, taskId, taskIdPattern }: {
bodySearch?: string;
dagId?: string;
dagIdPattern?: string;
@@ -1377,13 +1378,14 @@ export const useHumanInTheLoopServiceGetHitlDetails = , "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailsKeyFn({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskId, taskIdPattern, userId }, queryKey), queryFn: () => HumanInTheLoopService.getHitlDetails({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskId, taskIdPattern, userId }) as TData, ...options });
+} = {}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailsKeyFn({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, respondedUserId, respondedUserName, responseReceived, state, subjectSearch, taskId, taskIdPattern }, queryKey), queryFn: () => HumanInTheLoopService.getHitlDetails({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, respondedUserId, respondedUserName, responseReceived, state, subjectSearch, taskId, taskIdPattern }) as TData, ...options });
/**
* Get Health
* @returns HealthInfoResponse Successful Response
diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
index 59612c368987b..0c363423c2d2a 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
@@ -1363,13 +1363,14 @@ export const useHumanInTheLoopServiceGetHitlDetailSuspense = = unknown[]>({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskId, taskIdPattern, userId }: {
+export const useHumanInTheLoopServiceGetHitlDetailsSuspense = = unknown[]>({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, respondedUserId, respondedUserName, responseReceived, state, subjectSearch, taskId, taskIdPattern }: {
bodySearch?: string;
dagId?: string;
dagIdPattern?: string;
@@ -1377,13 +1378,14 @@ export const useHumanInTheLoopServiceGetHitlDetailsSuspense = , "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailsKeyFn({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskId, taskIdPattern, userId }, queryKey), queryFn: () => HumanInTheLoopService.getHitlDetails({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskId, taskIdPattern, userId }) as TData, ...options });
+} = {}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailsKeyFn({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, respondedUserId, respondedUserName, responseReceived, state, subjectSearch, taskId, taskIdPattern }, queryKey), queryFn: () => HumanInTheLoopService.getHitlDetails({ bodySearch, dagId, dagIdPattern, dagRunId, limit, offset, orderBy, respondedUserId, respondedUserName, responseReceived, state, subjectSearch, taskId, taskIdPattern }) as TData, ...options });
/**
* Get Health
* @returns HealthInfoResponse Successful Response
diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
index 033cfa654a9d6..96c41495cea6a 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
@@ -3563,7 +3563,7 @@ export const $HITLDetail = {
],
title: 'Respondents'
},
- user_id: {
+ responded_user_id: {
anyOf: [
{
type: 'string'
@@ -3572,7 +3572,18 @@ export const $HITLDetail = {
type: 'null'
}
],
- title: 'User Id'
+ title: 'Responded User Id'
+ },
+ responded_user_name: {
+ anyOf: [
+ {
+ type: 'string'
+ },
+ {
+ type: 'null'
+ }
+ ],
+ title: 'Responded User Name'
},
response_at: {
anyOf: [
@@ -3639,9 +3650,13 @@ export const $HITLDetailCollection = {
export const $HITLDetailResponse = {
properties: {
- user_id: {
+ responded_user_id: {
+ type: 'string',
+ title: 'Responded User Id'
+ },
+ responded_user_name: {
type: 'string',
- title: 'User Id'
+ title: 'Responded User Name'
},
response_at: {
type: 'string',
@@ -3663,7 +3678,7 @@ export const $HITLDetailResponse = {
}
},
type: 'object',
- required: ['user_id', 'response_at', 'chosen_options'],
+ required: ['responded_user_id', 'responded_user_name', 'response_at', 'chosen_options'],
title: 'HITLDetailResponse',
description: 'Response of updating a Human-in-the-loop detail.'
} as const;
diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts
index f04bbe72a787f..ccf2fae7cb99f 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts
@@ -3615,7 +3615,8 @@ export class HumanInTheLoopService {
* @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
* @param data.state
* @param data.responseReceived
- * @param data.userId
+ * @param data.respondedUserId
+ * @param data.respondedUserName
* @param data.subjectSearch SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
* @param data.bodySearch SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
* @returns HITLDetailCollection Successful Response
@@ -3636,7 +3637,8 @@ export class HumanInTheLoopService {
task_id_pattern: data.taskIdPattern,
state: data.state,
response_received: data.responseReceived,
- user_id: data.userId,
+ responded_user_id: data.respondedUserId,
+ responded_user_name: data.respondedUserName,
subject_search: data.subjectSearch,
body_search: data.bodySearch
},
diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
index ea0b228d78694..68d79d3f4d922 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
@@ -939,7 +939,8 @@ export type HITLDetail = {
[key: string]: unknown;
};
respondents?: Array<(string)> | null;
- user_id?: string | null;
+ responded_user_id?: string | null;
+ responded_user_name?: string | null;
response_at?: string | null;
chosen_options?: Array<(string)> | null;
params_input?: {
@@ -960,7 +961,8 @@ export type HITLDetailCollection = {
* Response of updating a Human-in-the-loop detail.
*/
export type HITLDetailResponse = {
- user_id: string;
+ responded_user_id: string;
+ responded_user_name: string;
response_at: string;
chosen_options: Array<(string)>;
params_input?: {
@@ -3084,6 +3086,8 @@ export type GetHitlDetailsData = {
limit?: number;
offset?: number;
orderBy?: Array<(string)>;
+ respondedUserId?: Array<(string)>;
+ respondedUserName?: Array<(string)>;
responseReceived?: boolean | null;
state?: Array<(string)>;
/**
@@ -3095,7 +3099,6 @@ export type GetHitlDetailsData = {
* SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
*/
taskIdPattern?: string | null;
- userId?: Array<(string)>;
};
export type GetHitlDetailsResponse = HITLDetailCollection;
diff --git a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_hitl.py b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_hitl.py
index 25fc0e327385f..55749f251895e 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_hitl.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_hitl.py
@@ -171,7 +171,8 @@ def sample_hitl_details(sample_tis: list[TaskInstance], session: Session) -> lis
response_at=utcnow(),
chosen_options=[str(i)],
params_input={"input": i},
- user_id="test",
+ responded_user_id="test",
+ responded_user_name="test",
)
for i, ti in enumerate(sample_tis[5:])
]
@@ -211,7 +212,8 @@ def expected_sample_hitl_detail_dict(sample_ti: TaskInstance) -> dict[str, Any]:
"chosen_options": None,
"response_received": False,
"subject": "This is subject",
- "user_id": None,
+ "responded_user_id": None,
+ "responded_user_name": None,
"task_instance": {
"dag_display_name": DAG_ID,
"dag_id": DAG_ID,
@@ -317,7 +319,8 @@ def test_should_respond_200_with_existing_response(
assert response.json() == {
"params_input": {"input_1": 2},
"chosen_options": ["Approve"],
- "user_id": "test",
+ "responded_user_id": "test",
+ "responded_user_name": "test",
"response_at": "2025-07-03T00:00:00Z",
}
@@ -346,7 +349,8 @@ def test_should_respond_200_to_respondent_user(
assert response.json() == {
"params_input": {"input_1": 2},
"chosen_options": ["Approve"],
- "user_id": "test",
+ "responded_user_id": "test",
+ "responded_user_name": "test",
"response_at": "2025-07-03T00:00:00Z",
}
@@ -447,7 +451,8 @@ def test_should_respond_409(
expected_response = {
"params_input": {"input_1": 2},
"chosen_options": ["Approve"],
- "user_id": "test",
+ "responded_user_id": "test",
+ "responded_user_name": "test",
"response_at": "2025-07-03T00:00:00Z",
}
assert response.status_code == 200
@@ -572,7 +577,8 @@ def test_should_respond_200_with_existing_response(
({"body_search": "this is"}, 8),
({"response_received": False}, 5),
({"response_received": True}, 3),
- ({"user_id": ["test"]}, 3),
+ ({"responded_user_id": ["test"]}, 3),
+ ({"responded_user_name": ["test"]}, 3),
],
ids=[
"dag_id_pattern_hitl_dag",
@@ -587,7 +593,8 @@ def test_should_respond_200_with_existing_response(
"body",
"response_not_received",
"response_received",
- "user_id",
+ "responded_user_id",
+ "responded_user_name",
],
)
def test_should_respond_200_with_existing_response_and_query(
diff --git a/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_hitl.py b/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_hitl.py
index 5a4322fadaa8b..085fc7ae6a8b3 100644
--- a/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_hitl.py
+++ b/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_hitl.py
@@ -50,7 +50,8 @@
expected_empty_hitl_detail_response_part: dict[str, Any] = {
"response_at": None,
"chosen_options": None,
- "user_id": None,
+ "responded_user_id": None,
+ "responded_user_name": None,
"params_input": {},
"response_received": False,
}
@@ -93,7 +94,8 @@ def expected_sample_hitl_detail_dict(sample_ti: TaskInstance) -> dict[str, Any]:
"params_input": {"input_1": 2},
"response_at": convert_to_utc(datetime(2025, 7, 3, 0, 0, 0)),
"chosen_options": ["Reject"],
- "user_id": "Fallback to defaults",
+ "responded_user_id": "Fallback to defaults",
+ "responded_user_name": "Fallback to defaults",
},
},
],
@@ -170,7 +172,8 @@ def test_update_hitl_detail(client: Client, sample_ti: TaskInstance) -> None:
"response_at": "2025-07-03T00:00:00Z",
"chosen_options": ["Reject"],
"response_received": True,
- "user_id": "Fallback to defaults",
+ "responded_user_id": "Fallback to defaults",
+ "responded_user_name": "Fallback to defaults",
}
diff --git a/airflow-ctl/src/airflowctl/api/datamodels/generated.py b/airflow-ctl/src/airflowctl/api/datamodels/generated.py
index 1fe8689c3c889..5d4db60952716 100644
--- a/airflow-ctl/src/airflowctl/api/datamodels/generated.py
+++ b/airflow-ctl/src/airflowctl/api/datamodels/generated.py
@@ -530,7 +530,8 @@ class HITLDetailResponse(BaseModel):
Response of updating a Human-in-the-loop detail.
"""
- user_id: Annotated[str, Field(title="User Id")]
+ responded_user_id: Annotated[str, Field(title="Responded User Id")]
+ responded_user_name: Annotated[str, Field(title="Responded User Name")]
response_at: Annotated[datetime, Field(title="Response At")]
chosen_options: Annotated[list[str], Field(min_length=1, title="Chosen Options")]
params_input: Annotated[dict[str, Any] | None, Field(title="Params Input")] = None
@@ -1826,7 +1827,8 @@ class HITLDetail(BaseModel):
multiple: Annotated[bool | None, Field(title="Multiple")] = False
params: Annotated[dict[str, Any] | None, Field(title="Params")] = None
respondents: Annotated[list[str] | None, Field(title="Respondents")] = None
- user_id: Annotated[str | None, Field(title="User Id")] = None
+ responded_user_id: Annotated[str | None, Field(title="Responded User Id")] = None
+ responded_user_name: Annotated[str | None, Field(title="Responded User Name")] = None
response_at: Annotated[datetime | None, Field(title="Response At")] = None
chosen_options: Annotated[list[str] | None, Field(title="Chosen Options")] = None
params_input: Annotated[dict[str, Any] | None, Field(title="Params Input")] = None
diff --git a/providers/standard/src/airflow/providers/standard/triggers/hitl.py b/providers/standard/src/airflow/providers/standard/triggers/hitl.py
index 82ee829ac8321..36adcbb2a4a57 100644
--- a/providers/standard/src/airflow/providers/standard/triggers/hitl.py
+++ b/providers/standard/src/airflow/providers/standard/triggers/hitl.py
@@ -127,7 +127,11 @@ async def run(self) -> AsyncIterator[TriggerEvent]:
resp = await sync_to_async(get_hitl_detail_content_detail)(ti_id=self.ti_id)
if resp.response_received and resp.chosen_options:
self.log.info(
- "[HITL] user=%s options=%s at %s", resp.user_id, resp.chosen_options, resp.response_at
+ "[HITL] responded_by=%s (id=%s) options=%s at %s",
+ resp.responded_user_name,
+ resp.responded_user_id,
+ resp.chosen_options,
+ resp.response_at,
)
yield TriggerEvent(
HITLTriggerEventSuccessPayload(
diff --git a/providers/standard/tests/unit/standard/operators/test_hitl.py b/providers/standard/tests/unit/standard/operators/test_hitl.py
index ca0b0ae9f4f9a..18646ab118dd6 100644
--- a/providers/standard/tests/unit/standard/operators/test_hitl.py
+++ b/providers/standard/tests/unit/standard/operators/test_hitl.py
@@ -183,7 +183,8 @@ def test_execute(self, dag_maker: DagMaker, session: Session) -> None:
assert hitl_detail_model.params == {"input_1": 1}
assert hitl_detail_model.respondents == ["test"]
assert hitl_detail_model.response_at is None
- assert hitl_detail_model.user_id is None
+ assert hitl_detail_model.responded_user_id is None
+ assert hitl_detail_model.responded_user_name is None
assert hitl_detail_model.chosen_options is None
assert hitl_detail_model.params_input == {}
diff --git a/providers/standard/tests/unit/standard/triggers/test_hitl.py b/providers/standard/tests/unit/standard/triggers/test_hitl.py
index fe7900d3fbdf9..e4652ecd2ba2c 100644
--- a/providers/standard/tests/unit/standard/triggers/test_hitl.py
+++ b/providers/standard/tests/unit/standard/triggers/test_hitl.py
@@ -79,7 +79,8 @@ async def test_run_failed_due_to_timeout(self, mock_update, mock_supervisor_comm
)
mock_supervisor_comms.send.return_value = HITLDetailResponse(
response_received=False,
- user_id=None,
+ responded_user_id=None,
+ responded_user_name=None,
response_at=None,
chosen_options=None,
params_input={},
@@ -109,7 +110,8 @@ async def test_run_fallback_to_default_due_to_timeout(self, mock_update, mock_lo
)
mock_supervisor_comms.send.return_value = HITLDetailResponse(
response_received=False,
- user_id=None,
+ responded_user_id=None,
+ responded_user_name=None,
response_at=None,
chosen_options=None,
params_input={},
@@ -143,7 +145,8 @@ async def test_run(self, mock_update, mock_log, mock_supervisor_comms, time_mach
)
mock_supervisor_comms.send.return_value = HITLDetailResponse(
response_received=True,
- user_id="test",
+ responded_user_id="test",
+ responded_user_name="test",
response_at=utcnow(),
chosen_options=["3"],
params_input={"input": 50},
@@ -162,7 +165,8 @@ async def test_run(self, mock_update, mock_log, mock_supervisor_comms, time_mach
)
assert mock_log.info.call_args == mock.call(
- "[HITL] user=%s options=%s at %s",
+ "[HITL] responded_by=%s (id=%s) options=%s at %s",
+ "test",
"test",
["3"],
datetime(2025, 7, 29, 2, 0, 0, tzinfo=utc),
diff --git a/task-sdk/src/airflow/sdk/api/datamodels/_generated.py b/task-sdk/src/airflow/sdk/api/datamodels/_generated.py
index 3eb44c2f06f12..cd861bf07d124 100644
--- a/task-sdk/src/airflow/sdk/api/datamodels/_generated.py
+++ b/task-sdk/src/airflow/sdk/api/datamodels/_generated.py
@@ -175,7 +175,8 @@ class HITLDetailResponse(BaseModel):
"""
response_received: Annotated[bool, Field(title="Response Received")]
- user_id: Annotated[str | None, Field(title="User Id")] = None
+ responded_user_name: Annotated[str | None, Field(title="Responded User Name")] = None
+ responded_user_id: Annotated[str | None, Field(title="Responded User Id")] = None
response_at: Annotated[AwareDatetime | None, Field(title="Response At")] = None
chosen_options: Annotated[list[str] | None, Field(title="Chosen Options")] = None
params_input: Annotated[dict[str, Any] | None, Field(title="Params Input")] = None
diff --git a/task-sdk/tests/task_sdk/api/test_client.py b/task-sdk/tests/task_sdk/api/test_client.py
index 966ac729d0aff..986ab5fe5e8bf 100644
--- a/task-sdk/tests/task_sdk/api/test_client.py
+++ b/task-sdk/tests/task_sdk/api/test_client.py
@@ -1302,7 +1302,8 @@ def handle_request(request: httpx.Request) -> httpx.Response:
json={
"chosen_options": ["Approval"],
"params_input": {},
- "user_id": "admin",
+ "responded_user_id": "admin",
+ "responded_user_name": "admin",
"response_received": True,
"response_at": "2025-07-03T00:00:00Z",
},
@@ -1319,7 +1320,8 @@ def handle_request(request: httpx.Request) -> httpx.Response:
assert result.response_received is True
assert result.chosen_options == ["Approval"]
assert result.params_input == {}
- assert result.user_id == "admin"
+ assert result.responded_user_id == "admin"
+ assert result.responded_user_name == "admin"
assert result.response_at == timezone.datetime(2025, 7, 3, 0, 0, 0)
def test_get_detail_response(self, time_machine: TimeMachineFixture) -> None:
@@ -1333,7 +1335,8 @@ def handle_request(request: httpx.Request) -> httpx.Response:
json={
"chosen_options": ["Approval"],
"params_input": {},
- "user_id": "admin",
+ "responded_user_id": "admin",
+ "responded_user_name": "admin",
"response_received": True,
"response_at": "2025-07-03T00:00:00Z",
},
@@ -1346,5 +1349,6 @@ def handle_request(request: httpx.Request) -> httpx.Response:
assert result.response_received is True
assert result.chosen_options == ["Approval"]
assert result.params_input == {}
- assert result.user_id == "admin"
+ assert result.responded_user_id == "admin"
+ assert result.responded_user_name == "admin"
assert result.response_at == timezone.datetime(2025, 7, 3, 0, 0, 0)
diff --git a/task-sdk/tests/task_sdk/execution_time/test_hitl.py b/task-sdk/tests/task_sdk/execution_time/test_hitl.py
index 5ad6733eb089f..a5ed016f44de8 100644
--- a/task-sdk/tests/task_sdk/execution_time/test_hitl.py
+++ b/task-sdk/tests/task_sdk/execution_time/test_hitl.py
@@ -62,7 +62,8 @@ def test_update_hitl_detail_response(mock_supervisor_comms) -> None:
response_received=True,
chosen_options=["Approve"],
response_at=timestamp,
- user_id="admin",
+ responded_user_id="admin",
+ responded_user_name="admin",
params_input={"input_1": 1},
)
resp = update_hitl_detail_response(
@@ -74,7 +75,8 @@ def test_update_hitl_detail_response(mock_supervisor_comms) -> None:
response_received=True,
chosen_options=["Approve"],
response_at=timestamp,
- user_id="admin",
+ responded_user_id="admin",
+ responded_user_name="admin",
params_input={"input_1": 1},
)
@@ -84,7 +86,8 @@ def test_get_hitl_detail_content_detail(mock_supervisor_comms) -> None:
response_received=False,
chosen_options=None,
response_at=None,
- user_id=None,
+ responded_user_id=None,
+ responded_user_name=None,
params_input={},
)
resp = get_hitl_detail_content_detail(TI_ID)
@@ -92,6 +95,7 @@ def test_get_hitl_detail_content_detail(mock_supervisor_comms) -> None:
response_received=False,
chosen_options=None,
response_at=None,
- user_id=None,
+ responded_user_id=None,
+ responded_user_name=None,
params_input={},
)