feat: Human Input Node (#32060)

The frontend and backend implementation for the human input node.

Co-authored-by: twwu <twwu@dify.ai>
Co-authored-by: JzoNg <jzongcode@gmail.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
Co-authored-by: zhsama <torvalds@linux.do>
This commit is contained in:
QuantumGhost
2026-02-09 14:57:23 +08:00
committed by GitHub
parent 56e3a55023
commit a1fc280102
474 changed files with 32667 additions and 2050 deletions

View File

@@ -20,7 +20,7 @@ depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('account_trial_app_records',
sa.Column('id', models.types.StringUUID(), server_default=sa.text('uuid_generate_v4()'), nullable=False),
sa.Column('id', models.types.StringUUID(), nullable=False),
sa.Column('account_id', models.types.StringUUID(), nullable=False),
sa.Column('app_id', models.types.StringUUID(), nullable=False),
sa.Column('count', sa.Integer(), nullable=False),
@@ -33,17 +33,17 @@ def upgrade():
batch_op.create_index('account_trial_app_record_app_id_idx', ['app_id'], unique=False)
op.create_table('exporle_banners',
sa.Column('id', models.types.StringUUID(), server_default=sa.text('uuid_generate_v4()'), nullable=False),
sa.Column('id', models.types.StringUUID(), nullable=False),
sa.Column('content', sa.JSON(), nullable=False),
sa.Column('link', sa.String(length=255), nullable=False),
sa.Column('sort', sa.Integer(), nullable=False),
sa.Column('status', sa.String(length=255), server_default=sa.text("'enabled'::character varying"), nullable=False),
sa.Column('status', sa.String(length=255), server_default=sa.text("'enabled'"), nullable=False),
sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False),
sa.Column('language', sa.String(length=255), server_default=sa.text("'en-US'::character varying"), nullable=False),
sa.Column('language', sa.String(length=255), server_default=sa.text("'en-US'"), nullable=False),
sa.PrimaryKeyConstraint('id', name='exporler_banner_pkey')
)
op.create_table('trial_apps',
sa.Column('id', models.types.StringUUID(), server_default=sa.text('uuid_generate_v4()'), nullable=False),
sa.Column('id', models.types.StringUUID(), nullable=False),
sa.Column('app_id', models.types.StringUUID(), nullable=False),
sa.Column('tenant_id', models.types.StringUUID(), nullable=False),
sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False),

View File

@@ -0,0 +1,99 @@
"""Add human input related db models
Revision ID: e8c3b3c46151
Revises: 788d3099ae3a
Create Date: 2026-01-29 14:15:23.081903
"""
from alembic import op
import models as models
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "e8c3b3c46151"
down_revision = "788d3099ae3a"
branch_labels = None
depends_on = None
def upgrade():
op.create_table(
"execution_extra_contents",
sa.Column("id", models.types.StringUUID(), nullable=False),
sa.Column("created_at", sa.DateTime(), server_default=sa.text("CURRENT_TIMESTAMP"), nullable=False),
sa.Column("updated_at", sa.DateTime(), server_default=sa.text("CURRENT_TIMESTAMP"), nullable=False),
sa.Column("type", sa.String(length=30), nullable=False),
sa.Column("workflow_run_id", models.types.StringUUID(), nullable=False),
sa.Column("message_id", models.types.StringUUID(), nullable=True),
sa.Column("form_id", models.types.StringUUID(), nullable=True),
sa.PrimaryKeyConstraint("id", name=op.f("execution_extra_contents_pkey")),
)
with op.batch_alter_table("execution_extra_contents", schema=None) as batch_op:
batch_op.create_index(batch_op.f("execution_extra_contents_message_id_idx"), ["message_id"], unique=False)
batch_op.create_index(
batch_op.f("execution_extra_contents_workflow_run_id_idx"), ["workflow_run_id"], unique=False
)
op.create_table(
"human_input_form_deliveries",
sa.Column("id", models.types.StringUUID(), nullable=False),
sa.Column("created_at", sa.DateTime(), server_default=sa.text("CURRENT_TIMESTAMP"), nullable=False),
sa.Column("updated_at", sa.DateTime(), server_default=sa.text("CURRENT_TIMESTAMP"), nullable=False),
sa.Column("form_id", models.types.StringUUID(), nullable=False),
sa.Column("delivery_method_type", sa.String(length=20), nullable=False),
sa.Column("delivery_config_id", models.types.StringUUID(), nullable=True),
sa.Column("channel_payload", sa.Text(), nullable=False),
sa.PrimaryKeyConstraint("id", name=op.f("human_input_form_deliveries_pkey")),
)
op.create_table(
"human_input_form_recipients",
sa.Column("id", models.types.StringUUID(), nullable=False),
sa.Column("created_at", sa.DateTime(), server_default=sa.text("CURRENT_TIMESTAMP"), nullable=False),
sa.Column("updated_at", sa.DateTime(), server_default=sa.text("CURRENT_TIMESTAMP"), nullable=False),
sa.Column("form_id", models.types.StringUUID(), nullable=False),
sa.Column("delivery_id", models.types.StringUUID(), nullable=False),
sa.Column("recipient_type", sa.String(length=20), nullable=False),
sa.Column("recipient_payload", sa.Text(), nullable=False),
sa.Column("access_token", sa.VARCHAR(length=32), nullable=False),
sa.PrimaryKeyConstraint("id", name=op.f("human_input_form_recipients_pkey")),
)
with op.batch_alter_table('human_input_form_recipients', schema=None) as batch_op:
batch_op.create_unique_constraint(batch_op.f('human_input_form_recipients_access_token_key'), ['access_token'])
op.create_table(
"human_input_forms",
sa.Column("id", models.types.StringUUID(), nullable=False),
sa.Column("created_at", sa.DateTime(), server_default=sa.text("CURRENT_TIMESTAMP"), nullable=False),
sa.Column("updated_at", sa.DateTime(), server_default=sa.text("CURRENT_TIMESTAMP"), nullable=False),
sa.Column("tenant_id", models.types.StringUUID(), nullable=False),
sa.Column("app_id", models.types.StringUUID(), nullable=False),
sa.Column("workflow_run_id", models.types.StringUUID(), nullable=True),
sa.Column("form_kind", sa.String(length=20), nullable=False),
sa.Column("node_id", sa.String(length=60), nullable=False),
sa.Column("form_definition", sa.Text(), nullable=False),
sa.Column("rendered_content", sa.Text(), nullable=False),
sa.Column("status", sa.String(length=20), nullable=False),
sa.Column("expiration_time", sa.DateTime(), nullable=False),
sa.Column("selected_action_id", sa.String(length=200), nullable=True),
sa.Column("submitted_data", sa.Text(), nullable=True),
sa.Column("submitted_at", sa.DateTime(), nullable=True),
sa.Column("submission_user_id", models.types.StringUUID(), nullable=True),
sa.Column("submission_end_user_id", models.types.StringUUID(), nullable=True),
sa.Column("completed_by_recipient_id", models.types.StringUUID(), nullable=True),
sa.PrimaryKeyConstraint("id", name=op.f("human_input_forms_pkey")),
)
def downgrade():
op.drop_table("human_input_forms")
op.drop_table("human_input_form_recipients")
op.drop_table("human_input_form_deliveries")
op.drop_table("execution_extra_contents")

View File

@@ -0,0 +1,34 @@
"""drop server_default for app trail related tables
Revision ID: c3df22613c99
Revises: e8c3b3c46151
Create Date: 2026-02-09 09:50:46.181969
"""
from alembic import op
import models as models
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'c3df22613c99'
down_revision = 'e8c3b3c46151'
branch_labels = None
depends_on = None
def upgrade():
op.alter_column("account_trial_app_records", "id", server_default=None)
op.alter_column("exporle_banners", "id", server_default=None)
op.alter_column("trial_apps", "id", server_default=None)
def downgrade():
# This migration is primarily for schema consistence
# between database and model definitions.
#
# The original
# DROP SERVER default is idemponent.
# Besides, the original migration has been updated to
# reflect the
pass