mirror of
https://github.com/langgenius/dify.git
synced 2026-11-11 13:51:25 +08:00
Observe Builder commands and native tests on one session stream. Reconnect to the retained Run without submitting another test. Use Stop and Resume for task control during native tests and keep approval details available. Allow retained local sandbox bindings to be checked without reading restricted ancestor directories. Refs: ENG-1448
893 lines
29 KiB
Python
893 lines
29 KiB
Python
"""Pydantic request and response schemas for the Console Dify Builder API."""
|
|
|
|
from datetime import datetime
|
|
from typing import Annotated, Any, Literal
|
|
from uuid import UUID
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field, RootModel, StrictBool, StrictInt, StringConstraints, model_validator
|
|
|
|
from core.dify_builder.contract import (
|
|
ActionKind,
|
|
AssistantTurnItem,
|
|
BuilderErrorCode,
|
|
Decision,
|
|
DecisionItem,
|
|
ExecutionActivityState,
|
|
ExecutionProgressStatus,
|
|
FormCard,
|
|
InteractionResponseItem,
|
|
NoticeItem,
|
|
Phase,
|
|
PlanCard,
|
|
PreflightContextCard,
|
|
RecoveryRef,
|
|
ResourceSelectCard,
|
|
RunContextCard,
|
|
RunStatus,
|
|
SessionModel,
|
|
TestResultCard,
|
|
UserItem,
|
|
)
|
|
from core.dify_builder.models import TaskSummary
|
|
from core.dify_builder.real_call_consent import ConsentState
|
|
from fields.base import ResponseModel
|
|
from fields.workflow_stream_fields import WorkflowStreamPayload
|
|
from runtime_contracts.builder.presentation import BuilderText
|
|
from services.dify_builder.native_testing.public_contracts import NativeTestInputBinding, NativeTestPublicReceipt
|
|
|
|
NonEmptyString = Annotated[str, StringConstraints(strip_whitespace=True, min_length=1)]
|
|
ClientTurnId = Annotated[str, StringConstraints(strip_whitespace=True, min_length=1, max_length=128)]
|
|
# Real-call consent identifiers are bound byte-exactly by the consent owner: no trimming or coercion.
|
|
RealCallId = Annotated[str, StringConstraints(min_length=1, max_length=128)]
|
|
ScopeDigest = Annotated[str, StringConstraints(pattern=r"^[a-f0-9]{64}$")]
|
|
IdempotencyKey = Annotated[str, StringConstraints(min_length=1, max_length=128)]
|
|
ReasonCode = Annotated[str, StringConstraints(pattern=r"^[a-z][a-z0-9_]{0,127}$")]
|
|
|
|
|
|
class DifyBuilderPayload(BaseModel):
|
|
model_config = ConfigDict(extra="forbid", populate_by_name=True)
|
|
|
|
|
|
class DifyBuilderAttachmentPayload(DifyBuilderPayload):
|
|
"""An opaque native upload reference; authority is reconstructed server-side."""
|
|
|
|
upload_file_id: UUID
|
|
|
|
|
|
class DifyBuilderMaterialPayload(DifyBuilderPayload):
|
|
attachments: list[DifyBuilderAttachmentPayload] = Field(default_factory=list, max_length=5)
|
|
|
|
|
|
class DifyBuilderGoalPayload(DifyBuilderMaterialPayload):
|
|
goal_text: Annotated[str, StringConstraints(strip_whitespace=True, max_length=20000)] = ""
|
|
|
|
@model_validator(mode="after")
|
|
def require_goal_or_material(self) -> "DifyBuilderGoalPayload":
|
|
if not self.goal_text and not self.attachments:
|
|
raise ValueError("goal_text or attachments are required")
|
|
return self
|
|
|
|
|
|
class DifyBuilderCreateBuildSessionPayload(DifyBuilderGoalPayload):
|
|
app_id: NonEmptyString
|
|
scenario: Literal["build"]
|
|
# True when the app was created from this same prompt and still carries the
|
|
# name cut from it, so Builder may rewrite it once it understands the goal.
|
|
# False is create-from-blank: the user named the app, Builder inherits it.
|
|
derive_app_name: bool = False
|
|
model_config_data: SessionModel | None = Field(default=None, alias="model_config")
|
|
|
|
|
|
class DifyBuilderCreateEditSessionPayload(DifyBuilderGoalPayload):
|
|
app_id: NonEmptyString
|
|
scenario: Literal["edit"]
|
|
model_config_data: SessionModel | None = Field(default=None, alias="model_config")
|
|
|
|
|
|
class DifyBuilderCreateFixSessionPayload(DifyBuilderMaterialPayload):
|
|
app_id: NonEmptyString
|
|
scenario: Literal["fix"]
|
|
failed_run_id: NonEmptyString
|
|
session_id: NonEmptyString | None = None
|
|
client_action_id: ClientTurnId | None = None
|
|
model_config_data: SessionModel | None = Field(default=None, alias="model_config")
|
|
|
|
|
|
class DifyBuilderChecklistErrorPayload(DifyBuilderPayload):
|
|
node_id: str
|
|
node_type: str
|
|
title: str
|
|
messages: list[str]
|
|
unconnected: bool
|
|
plugin_missing: bool
|
|
|
|
|
|
class DifyBuilderCreateChecklistFixSessionPayload(DifyBuilderMaterialPayload):
|
|
app_id: NonEmptyString
|
|
scenario: Literal["fix"]
|
|
checklist_errors: Annotated[list[DifyBuilderChecklistErrorPayload], Field(min_length=1)]
|
|
failed_run_id: NonEmptyString | None = None
|
|
session_id: NonEmptyString | None = None
|
|
client_action_id: ClientTurnId | None = None
|
|
model_config_data: SessionModel | None = Field(default=None, alias="model_config")
|
|
|
|
|
|
class DifyBuilderCreateSessionPayload(
|
|
RootModel[
|
|
DifyBuilderCreateBuildSessionPayload
|
|
| DifyBuilderCreateEditSessionPayload
|
|
| DifyBuilderCreateFixSessionPayload
|
|
| DifyBuilderCreateChecklistFixSessionPayload
|
|
]
|
|
):
|
|
"""Create a Build, Edit, failed-run Fix, or checklist Fix session."""
|
|
|
|
|
|
class DifyBuilderReceiptFieldSnapshot(DifyBuilderPayload):
|
|
key: NonEmptyString
|
|
label: str
|
|
display_value: str
|
|
|
|
|
|
class DifyBuilderReceiptSnapshot(DifyBuilderPayload):
|
|
"""Localized display only; submitted values remain in action.payload."""
|
|
|
|
interaction_kind: Literal["choice", "form", "resource"]
|
|
reply_language: Annotated[str, StringConstraints(min_length=1, max_length=35)]
|
|
# Display metadata must not impose limits absent from action.payload.
|
|
question: str
|
|
answer: str = ""
|
|
fields: list[DifyBuilderReceiptFieldSnapshot] = Field(default_factory=list)
|
|
|
|
|
|
class DifyBuilderTaskControlPayload(DifyBuilderPayload):
|
|
"""Control the current task with a durable client replay identity."""
|
|
|
|
base_version: StrictInt
|
|
client_action_id: ClientTurnId
|
|
base_app_revision: str = ""
|
|
|
|
|
|
class DifyBuilderConversationLifecyclePayload(DifyBuilderPayload):
|
|
"""Lifecycle control uses Session CAS, never a graph revision."""
|
|
|
|
base_version: StrictInt = Field(ge=1)
|
|
client_action_id: ClientTurnId
|
|
expected_task_id: str = Field(default="", max_length=255)
|
|
|
|
|
|
class DifyBuilderClearConversationPayload(DifyBuilderConversationLifecyclePayload):
|
|
confirmed: StrictBool
|
|
|
|
@model_validator(mode="after")
|
|
def require_confirmation(self) -> "DifyBuilderClearConversationPayload":
|
|
if self.confirmed is not True:
|
|
raise ValueError("Explicit clear confirmation is required")
|
|
return self
|
|
|
|
|
|
class DifyBuilderFixIntentPayload(DifyBuilderPayload):
|
|
base_version: StrictInt = Field(ge=1)
|
|
client_action_id: ClientTurnId
|
|
failed_run_id: NonEmptyString | None = None
|
|
checklist_errors: list[DifyBuilderChecklistErrorPayload] = Field(default_factory=list, max_length=100)
|
|
model_config_data: SessionModel | None = Field(default=None, alias="model_config")
|
|
|
|
@model_validator(mode="after")
|
|
def require_one_source(self) -> "DifyBuilderFixIntentPayload":
|
|
if bool(self.failed_run_id) == bool(self.checklist_errors):
|
|
raise ValueError("Exactly one failed run or checklist source is required")
|
|
return self
|
|
|
|
|
|
class DifyBuilderFixControlQuery(DifyBuilderPayload):
|
|
operation: Literal["fix", "dismiss_fix", "stop_and_switch_fix"]
|
|
client_action_id: ClientTurnId
|
|
base_version: int = Field(ge=1)
|
|
fix_intent_id: NonEmptyString | None = None
|
|
expected_task_id: NonEmptyString | None = None
|
|
|
|
@model_validator(mode="after")
|
|
def require_decision_identity(self) -> "DifyBuilderFixControlQuery":
|
|
if self.operation != "fix" and (not self.fix_intent_id or not self.expected_task_id):
|
|
raise ValueError("Fix decision identity is required")
|
|
return self
|
|
|
|
|
|
class DifyBuilderFixControlResponse(ResponseModel):
|
|
session_id: str
|
|
operation: Literal["fix", "dismiss_fix", "stop_and_switch_fix"]
|
|
client_action_id: str
|
|
base_version: int
|
|
fix_intent_id: str
|
|
target_task_id: str
|
|
status: str
|
|
acknowledged: bool
|
|
|
|
|
|
class DifyBuilderConversationLifecycleResponse(ResponseModel):
|
|
session_id: str
|
|
version: int
|
|
last_command_id: str
|
|
task_id: str
|
|
task_status: Literal["abandoned"]
|
|
conversation_cleared: bool
|
|
conversation_last_seq: int
|
|
settlement: Literal["settled", "pending", "unknown"]
|
|
|
|
|
|
class DifyBuilderSubmitActionPayload(DifyBuilderPayload):
|
|
action_id: NonEmptyString
|
|
client_action_id: ClientTurnId | None = None
|
|
receipt_snapshot: DifyBuilderReceiptSnapshot | None = None
|
|
payload: dict[str, Any] = Field(
|
|
default_factory=dict,
|
|
description=(
|
|
"Existing action payload. Native controls: confirm_test_operation/deny_test_operation take "
|
|
"request_id,pending_operation_id,pending_version,scope_digest; resume_test takes request_id,"
|
|
"expected_generation; cancel_test takes request_id. Copy payload from the current server action."
|
|
),
|
|
)
|
|
base_version: StrictInt
|
|
base_app_revision: NonEmptyString
|
|
|
|
|
|
class DifyBuilderSubmitMessagePayload(DifyBuilderMaterialPayload):
|
|
text: Annotated[str, StringConstraints(strip_whitespace=True, max_length=20000)] = ""
|
|
base_version: StrictInt
|
|
client_turn_id: ClientTurnId
|
|
|
|
@model_validator(mode="after")
|
|
def require_text_or_material(self) -> "DifyBuilderSubmitMessagePayload":
|
|
if not self.text and not self.attachments:
|
|
raise ValueError("text or attachments are required")
|
|
return self
|
|
|
|
|
|
class DifyBuilderConversationListQuery(DifyBuilderPayload):
|
|
before_seq: int | None = Field(default=None, ge=0, description="Load groups before this sequence")
|
|
after_seq: int | None = Field(default=None, ge=-1, description="Load groups after this sequence")
|
|
limit: int = Field(default=20, ge=1, le=100, description="Number of conversation groups to return")
|
|
|
|
@model_validator(mode="after")
|
|
def validate_cursor(self) -> "DifyBuilderConversationListQuery":
|
|
if self.before_seq is not None and self.after_seq is not None:
|
|
raise ValueError("before_seq and after_seq are mutually exclusive")
|
|
return self
|
|
|
|
|
|
# Real-call consent requests name what a person reviews or approves. They never
|
|
# carry Tool parameters, credentials, graph, execution context or guards: the
|
|
# consent owner reconstructs all of those server-side from authoritative rows.
|
|
|
|
|
|
class DifyBuilderPrepareRealCallPayload(DifyBuilderPayload):
|
|
node_id: RealCallId
|
|
test_input_id: RealCallId
|
|
credential_id: RealCallId
|
|
at_version: StrictInt = Field(ge=1)
|
|
base_app_revision: RealCallId
|
|
idempotency_key: IdempotencyKey
|
|
|
|
|
|
class DifyBuilderConfirmRealCallPayload(DifyBuilderPayload):
|
|
decision: Literal["confirm", "decline"]
|
|
scope_digest: ScopeDigest
|
|
at_version: StrictInt = Field(ge=1)
|
|
idempotency_key: IdempotencyKey
|
|
|
|
|
|
class DifyBuilderExecuteRealCallPayload(DifyBuilderPayload):
|
|
scope_digest: ScopeDigest
|
|
at_version: StrictInt = Field(ge=1)
|
|
idempotency_key: IdempotencyKey
|
|
|
|
|
|
class DifyBuilderConversationItemBase(ResponseModel):
|
|
seq: int
|
|
at_version: int
|
|
|
|
|
|
class DifyBuilderUserConversationItemResponse(DifyBuilderConversationItemBase):
|
|
kind: Literal["user"]
|
|
payload: UserItem
|
|
|
|
|
|
class DifyBuilderDecisionConversationItemResponse(DifyBuilderConversationItemBase):
|
|
kind: Literal["decision"]
|
|
payload: DecisionItem
|
|
|
|
|
|
class DifyBuilderInteractionResponseConversationItemResponse(DifyBuilderConversationItemBase):
|
|
kind: Literal["interaction_response"]
|
|
payload: InteractionResponseItem
|
|
|
|
|
|
class DifyBuilderNoticeConversationItemResponse(DifyBuilderConversationItemBase):
|
|
kind: Literal["notice"]
|
|
payload: NoticeItem
|
|
|
|
|
|
class DifyBuilderRunContextConversationItemResponse(DifyBuilderConversationItemBase):
|
|
kind: Literal["run_context"]
|
|
payload: RunContextCard
|
|
|
|
|
|
class DifyBuilderPreflightContextConversationItemResponse(DifyBuilderConversationItemBase):
|
|
kind: Literal["preflight_context"]
|
|
payload: PreflightContextCard
|
|
|
|
|
|
class DifyBuilderAssistantTurnConversationItemResponse(DifyBuilderConversationItemBase):
|
|
kind: Literal["assistant_turn"]
|
|
payload: AssistantTurnItem
|
|
|
|
|
|
class DifyBuilderPlanConversationItemResponse(DifyBuilderConversationItemBase):
|
|
kind: Literal["plan"]
|
|
payload: PlanCard
|
|
|
|
|
|
class DifyBuilderFormConversationItemResponse(DifyBuilderConversationItemBase):
|
|
kind: Literal["form"]
|
|
payload: FormCard
|
|
|
|
|
|
class DifyBuilderResourceSelectConversationItemResponse(DifyBuilderConversationItemBase):
|
|
kind: Literal["resource_select"]
|
|
payload: ResourceSelectCard
|
|
|
|
|
|
class DifyBuilderTestResultConversationItemResponse(DifyBuilderConversationItemBase):
|
|
kind: Literal["test_result"]
|
|
payload: TestResultCard
|
|
|
|
|
|
DifyBuilderConversationItem = Annotated[
|
|
DifyBuilderUserConversationItemResponse
|
|
| DifyBuilderDecisionConversationItemResponse
|
|
| DifyBuilderInteractionResponseConversationItemResponse
|
|
| DifyBuilderNoticeConversationItemResponse
|
|
| DifyBuilderRunContextConversationItemResponse
|
|
| DifyBuilderPreflightContextConversationItemResponse
|
|
| DifyBuilderAssistantTurnConversationItemResponse
|
|
| DifyBuilderPlanConversationItemResponse
|
|
| DifyBuilderFormConversationItemResponse
|
|
| DifyBuilderResourceSelectConversationItemResponse
|
|
| DifyBuilderTestResultConversationItemResponse,
|
|
Field(discriminator="kind"),
|
|
]
|
|
|
|
# Complete assistant presentation arrives as a committed item. Native streamed
|
|
# assistant text is restricted to agent_message by the event validator.
|
|
DifyBuilderAppendedConversationItem = Annotated[
|
|
DifyBuilderUserConversationItemResponse
|
|
| DifyBuilderDecisionConversationItemResponse
|
|
| DifyBuilderInteractionResponseConversationItemResponse
|
|
| DifyBuilderNoticeConversationItemResponse
|
|
| DifyBuilderRunContextConversationItemResponse
|
|
| DifyBuilderPreflightContextConversationItemResponse
|
|
| DifyBuilderAssistantTurnConversationItemResponse
|
|
| DifyBuilderPlanConversationItemResponse
|
|
| DifyBuilderFormConversationItemResponse
|
|
| DifyBuilderResourceSelectConversationItemResponse
|
|
| DifyBuilderTestResultConversationItemResponse,
|
|
Field(discriminator="kind"),
|
|
]
|
|
|
|
|
|
class DifyBuilderActiveInteractionResponse(ResponseModel):
|
|
action_id: str
|
|
card_seq: int
|
|
valid_at_version: int
|
|
|
|
|
|
class DifyBuilderActionResponse(ResponseModel):
|
|
payload: dict[str, Any] = Field(default_factory=dict)
|
|
id: str
|
|
label: BuilderText
|
|
kind: ActionKind
|
|
|
|
|
|
class DifyBuilderAppRevisionResponse(ResponseModel):
|
|
current: str
|
|
conflicted: bool
|
|
|
|
|
|
class DifyBuilderAttachmentStateResponse(ResponseModel):
|
|
upload_file_id: UUID
|
|
name: str
|
|
extension: str
|
|
size: int
|
|
status: Literal["pending", "consumed", "truncated", "rejected"]
|
|
digest: str = ""
|
|
reason: str = ""
|
|
|
|
|
|
class DifyBuilderMaterialReceiptResponse(ResponseModel):
|
|
id: UUID
|
|
seq: int
|
|
app_id: str
|
|
session_id: str
|
|
task_id: str
|
|
generation: int
|
|
client_turn_id: str
|
|
status: Literal["pending", "consumed", "truncated", "rejected", "superseded"]
|
|
consumed_version: int | None
|
|
test_input_candidate: bool
|
|
attachments: list[DifyBuilderAttachmentStateResponse] = Field(max_length=5)
|
|
|
|
|
|
class DifyBuilderSessionStateResponse(ResponseModel):
|
|
attachments: list[DifyBuilderMaterialReceiptResponse] = Field(default_factory=list, max_length=100)
|
|
test_execution: NativeTestPublicReceipt | None = None
|
|
diagnostics: list[dict[str, Any]] = Field(default_factory=list, max_length=33)
|
|
session_id: str
|
|
reply_language: str = Field(
|
|
description="Detected user-input language (BCP-47); empty until a user language is detected."
|
|
)
|
|
version: int
|
|
canvas_read_only: bool
|
|
run_status: RunStatus
|
|
command_pending: bool = False
|
|
interrupted: bool
|
|
conversation_last_seq: int
|
|
phase: Phase
|
|
actions: list[DifyBuilderActionResponse] = Field(default_factory=list)
|
|
decision: Decision | None = None
|
|
active_interaction: DifyBuilderActiveInteractionResponse | None = None
|
|
recovery: RecoveryRef | None = None
|
|
model: SessionModel | None = None
|
|
app_revision: DifyBuilderAppRevisionResponse | None = None
|
|
task_id: str = ""
|
|
task_status: Literal["active", "paused", "completed", "abandoned"] = "active"
|
|
task_summaries: list[TaskSummary] = Field(default_factory=list, max_length=8)
|
|
conversation_cleared: bool = False
|
|
lifecycle_settlement: Literal["settled", "pending", "unknown"] | None = Field(
|
|
default=None,
|
|
description=(
|
|
"Known execution state of the current clear/abandon control, not physical cleanup proof. "
|
|
"Null means no applicable control; missing applicable evidence is unknown. "
|
|
"Clear retains its durable acknowledgment state without inferring later cleanup."
|
|
),
|
|
)
|
|
pending_fix_context: RunContextCard | PreflightContextCard | None = None
|
|
|
|
|
|
class DifyBuilderSessionViewResponse(DifyBuilderSessionStateResponse):
|
|
last_command_id: str = ""
|
|
|
|
|
|
class DifyBuilderCreatedSessionResponse(ResponseModel):
|
|
session: DifyBuilderSessionViewResponse
|
|
requires_start: bool
|
|
|
|
|
|
class DifyBuilderCommandSubmissionResponse(ResponseModel):
|
|
command_id: str
|
|
pending: bool
|
|
session: DifyBuilderSessionViewResponse | None = None
|
|
|
|
|
|
class DifyBuilderFixIntentResponse(ResponseModel):
|
|
fix_intent_id: str
|
|
status: str
|
|
session: DifyBuilderSessionViewResponse
|
|
|
|
|
|
class DifyBuilderConversationPageResponse(ResponseModel):
|
|
data: list[DifyBuilderConversationItem]
|
|
has_more: bool
|
|
first_seq: int | None
|
|
last_seq: int | None
|
|
|
|
|
|
class DifyBuilderCommandFinishedEventData(DifyBuilderSessionStateResponse):
|
|
command_id: str
|
|
post_canvas_action_id: str | None = None
|
|
|
|
|
|
class DifyBuilderCommandStartedEventData(ResponseModel):
|
|
session_id: str
|
|
command_id: str
|
|
version: int
|
|
phase: Phase
|
|
run_status: RunStatus
|
|
|
|
|
|
class DifyBuilderCommandStartedEventResponse(ResponseModel):
|
|
event: Literal["command_started"]
|
|
data: DifyBuilderCommandStartedEventData
|
|
|
|
|
|
class DifyBuilderWorkflowEventData(ResponseModel):
|
|
session_id: str
|
|
operation_id: str
|
|
at_version: int
|
|
revision: int
|
|
payload: WorkflowStreamPayload
|
|
|
|
|
|
class DifyBuilderWorkflowEventResponse(ResponseModel):
|
|
event: Literal["workflow"]
|
|
data: DifyBuilderWorkflowEventData
|
|
|
|
|
|
class DifyBuilderCanvasEventBase(ResponseModel):
|
|
session_id: str
|
|
operation_id: str
|
|
at_version: int
|
|
revision: int
|
|
|
|
|
|
class DifyBuilderGraphChangedEventData(DifyBuilderCanvasEventBase):
|
|
event: Literal["graph_changed"]
|
|
cause: Literal["apply", "restore"]
|
|
|
|
|
|
class DifyBuilderEditTargetsChangedEventData(DifyBuilderCanvasEventBase):
|
|
event: Literal["edit_targets_changed"]
|
|
node_ids: list[str]
|
|
|
|
|
|
class DifyBuilderPublishedEventData(DifyBuilderCanvasEventBase):
|
|
event: Literal["publish_workflow"]
|
|
|
|
|
|
DifyBuilderCanvasEventData = Annotated[
|
|
DifyBuilderGraphChangedEventData | DifyBuilderEditTargetsChangedEventData | DifyBuilderPublishedEventData,
|
|
Field(discriminator="event"),
|
|
]
|
|
|
|
|
|
class DifyBuilderCanvasEventResponse(ResponseModel):
|
|
event: Literal["canvas"]
|
|
data: DifyBuilderCanvasEventData
|
|
|
|
|
|
class DifyBuilderExecutionActivityResponse(ResponseModel):
|
|
id: str
|
|
label: BuilderText
|
|
state: ExecutionActivityState
|
|
parent_id: str | None = None
|
|
|
|
|
|
class DifyBuilderExecutionProgressResponse(ResponseModel):
|
|
status: ExecutionProgressStatus
|
|
activities: list[DifyBuilderExecutionActivityResponse] = Field(default_factory=list)
|
|
|
|
|
|
class DifyBuilderAgentMessageEventData(ResponseModel):
|
|
session_id: str
|
|
command_id: str
|
|
operation_id: str
|
|
turn_id: str
|
|
delta: str
|
|
seq: int
|
|
at_version: int
|
|
revision: int
|
|
done: bool
|
|
text_bytes: int
|
|
execution: DifyBuilderExecutionProgressResponse | None = None
|
|
cards: list[str] = Field(default_factory=list)
|
|
|
|
|
|
class DifyBuilderAgentMessageEventResponse(ResponseModel):
|
|
event: Literal["agent_message"]
|
|
data: DifyBuilderAgentMessageEventData
|
|
|
|
|
|
class DifyBuilderConversationItemAppendedEventData(ResponseModel):
|
|
session_id: str
|
|
command_id: str
|
|
item: DifyBuilderAppendedConversationItem
|
|
|
|
@model_validator(mode="after")
|
|
def complete_assistant_only(self):
|
|
if self.item.kind == "assistant_turn" and self.item.payload.reply_mode != "complete":
|
|
raise ValueError("streamed assistant turns use agent_message")
|
|
return self
|
|
|
|
|
|
class DifyBuilderConversationItemAppendedEventResponse(ResponseModel):
|
|
event: Literal["conversation_item_appended"]
|
|
data: DifyBuilderConversationItemAppendedEventData
|
|
|
|
|
|
class DifyBuilderReasoningEventData(ResponseModel):
|
|
session_id: str
|
|
operation_id: str
|
|
at_version: int
|
|
revision: int
|
|
delta: str
|
|
|
|
|
|
class DifyBuilderReasoningEventResponse(ResponseModel):
|
|
event: Literal["reasoning"]
|
|
data: DifyBuilderReasoningEventData
|
|
|
|
|
|
class DifyBuilderProgressEventData(ResponseModel):
|
|
session_id: str
|
|
operation_id: str
|
|
at_version: int
|
|
revision: int
|
|
status: ExecutionProgressStatus
|
|
activity: DifyBuilderExecutionActivityResponse | None
|
|
reply_language: str = ""
|
|
|
|
|
|
class DifyBuilderProgressEventResponse(ResponseModel):
|
|
event: Literal["progress"]
|
|
data: DifyBuilderProgressEventData
|
|
|
|
|
|
class DifyBuilderProgressSnapshotEventData(ResponseModel):
|
|
"""Complete observed activity state before subsequent live progress deltas."""
|
|
|
|
session_id: str
|
|
operation_id: str
|
|
at_version: int
|
|
revision: int
|
|
reply_language: str
|
|
execution: DifyBuilderExecutionProgressResponse
|
|
|
|
|
|
class DifyBuilderProgressSnapshotEventResponse(ResponseModel):
|
|
event: Literal["progress_snapshot"]
|
|
data: DifyBuilderProgressSnapshotEventData
|
|
|
|
|
|
class DifyBuilderCommandFinishedEventResponse(ResponseModel):
|
|
event: Literal["command_finished"]
|
|
data: DifyBuilderCommandFinishedEventData
|
|
|
|
|
|
class DifyBuilderSessionReadyEventResponse(ResponseModel):
|
|
event: Literal["session_ready"]
|
|
data: DifyBuilderSessionViewResponse
|
|
|
|
|
|
class DifyBuilderSessionStateEventResponse(ResponseModel):
|
|
event: Literal["session_state"]
|
|
data: DifyBuilderSessionViewResponse
|
|
|
|
|
|
class DifyBuilderErrorEventData(ResponseModel):
|
|
session_id: str | None = None
|
|
command_id: str | None = None
|
|
code: str | None = None
|
|
message: str
|
|
reason_code: ReasonCode | None = None
|
|
recoverable: bool | None = None
|
|
|
|
|
|
class DifyBuilderErrorEventResponse(ResponseModel):
|
|
event: Literal["error"]
|
|
data: DifyBuilderErrorEventData
|
|
|
|
|
|
class DifyBuilderNativeExecutionEventData(ResponseModel):
|
|
session_id: str
|
|
cursor: ScopeDigest
|
|
receipt: NativeTestPublicReceipt
|
|
|
|
|
|
class DifyBuilderNativeExecutionEventResponse(ResponseModel):
|
|
event: Literal["native_execution"]
|
|
data: DifyBuilderNativeExecutionEventData
|
|
|
|
|
|
class DifyBuilderStreamEventResponse(
|
|
RootModel[
|
|
Annotated[
|
|
DifyBuilderCommandStartedEventResponse
|
|
| DifyBuilderWorkflowEventResponse
|
|
| DifyBuilderCanvasEventResponse
|
|
| DifyBuilderAgentMessageEventResponse
|
|
| DifyBuilderConversationItemAppendedEventResponse
|
|
| DifyBuilderReasoningEventResponse
|
|
| DifyBuilderProgressEventResponse
|
|
| DifyBuilderProgressSnapshotEventResponse
|
|
| DifyBuilderCommandFinishedEventResponse
|
|
| DifyBuilderSessionReadyEventResponse
|
|
| DifyBuilderSessionStateEventResponse
|
|
| DifyBuilderErrorEventResponse
|
|
| DifyBuilderNativeExecutionEventResponse,
|
|
Field(discriminator="event"),
|
|
]
|
|
]
|
|
):
|
|
"""One JSON object carried by an SSE ``data:`` frame."""
|
|
|
|
|
|
class DifyBuilderErrorResponse(ResponseModel):
|
|
reason_code: ReasonCode | None = None
|
|
code: BuilderErrorCode
|
|
message: str | None = None
|
|
recoverable: bool | None = None
|
|
|
|
|
|
class DifyBuilderRealCallDisclosureResponse(ResponseModel):
|
|
"""The approved public disclosure of one real Tool call; no credential revision or secret."""
|
|
|
|
action: Literal["get_message"]
|
|
credential_id: str
|
|
credential_alias: str
|
|
credential_type: str
|
|
identity_basis: Literal["workspace_credential"] = Field(
|
|
description="The alias names a workspace credential, not a verified remote account identity"
|
|
)
|
|
remote_account_status: Literal["unknown"]
|
|
message_id: str
|
|
request_method: Literal["GET"]
|
|
request_url: str
|
|
response_format: Literal["full"]
|
|
include_body: bool
|
|
include_attachments: bool
|
|
oauth_refresh: Literal["if_needed"] = Field(
|
|
description=(
|
|
"One OAuth token refresh request is made only if the stored access token must be refreshed "
|
|
"before the call; otherwise no refresh request is sent"
|
|
)
|
|
)
|
|
oauth_refresh_method: Literal["POST"]
|
|
oauth_refresh_url: Literal["https://oauth2.googleapis.com/token"]
|
|
native_attempt_limit: Literal[1]
|
|
refresh_limit: Literal[1]
|
|
automatic_replay: Literal[False]
|
|
quota_usage: Literal["possible"]
|
|
monetary_estimate: Literal["unknown"]
|
|
output_retention: Literal["native_run_retention"]
|
|
data_description: Literal["gmail_message_headers_snippet_and_selected_body_or_attachment_metadata"]
|
|
expires_at: datetime
|
|
prior_uncertain_consent_id: str | None = None
|
|
|
|
|
|
class DifyBuilderRealCallConsentResponse(ResponseModel):
|
|
"""Current or saved receipt of one real-call consent."""
|
|
|
|
# The discriminator is always emitted, so document it as required.
|
|
model_config = ConfigDict(json_schema_serialization_defaults_required=True)
|
|
|
|
result: Literal["receipt"] = "receipt"
|
|
consent_id: str
|
|
scope_digest: str
|
|
state: ConsentState = Field(
|
|
description=(
|
|
"Consent lifecycle state. 'unknown' is final: a claimed call whose real effect could not be "
|
|
"confirmed. It is bounded by the disclosed limits of one native attempt and at most one token "
|
|
"refresh, is never replayed automatically, and any further real call needs a new human-approved "
|
|
"consent"
|
|
)
|
|
)
|
|
at_version: int
|
|
disclosure: DifyBuilderRealCallDisclosureResponse
|
|
command_id: str | None
|
|
execution_request_id: str | None
|
|
native_run_id: str | None
|
|
native_node_execution_id: str | None
|
|
reason_code: ReasonCode | None = Field(description="Fixed server reason code; never provider text")
|
|
refresh_claimed: bool
|
|
business_claimed: bool
|
|
can_retry: bool = Field(
|
|
description="Whether a new consent may be prepared; always false once a launch is reserved or claimed"
|
|
)
|
|
|
|
|
|
DifyBuilderRealCallRefusalCode = Literal[
|
|
"needs_setup", "needs_permission", "unsupported_action", "unavailable_metadata", "conflict"
|
|
]
|
|
DifyBuilderRealCallRefusalReason = Literal[
|
|
# needs_setup
|
|
"needs_setup",
|
|
"credential_required",
|
|
"credential_binding_mismatch",
|
|
"credential_provider_mismatch",
|
|
"unsupported_test_input",
|
|
# needs_permission
|
|
"credential_reference_unavailable",
|
|
"credential_policy_refused",
|
|
# unsupported_action
|
|
"unsupported_action",
|
|
"unsupported_tool_node",
|
|
"unsupported_tool_parameters",
|
|
"unsupported_tool_configuration",
|
|
"unsafe_message_id",
|
|
"unsupported_session_state",
|
|
"unsupported_graph",
|
|
"unsupported_workflow",
|
|
"unsupported_node_retry",
|
|
"unsupported_default_output",
|
|
"unsupported_selector",
|
|
"unsupported_node_implementation",
|
|
"unsupported_node_configuration",
|
|
"invalid_node_configuration",
|
|
"unsupported_start_input",
|
|
"unsupported_input_value",
|
|
"unsupported_features",
|
|
"unsupported_variables",
|
|
"unsupported_external_tracing",
|
|
# unavailable_metadata
|
|
"installed_tool_unavailable",
|
|
"invalid_graph_metadata",
|
|
"session_state_unavailable",
|
|
"real_call_unavailable",
|
|
# conflict
|
|
"stale_session_version",
|
|
"stale_app_revision",
|
|
"stale_consent_version",
|
|
"stale_execution_revision",
|
|
"test_input_binding_mismatch",
|
|
"consent_scope_mismatch",
|
|
"consent_scope_changed",
|
|
"consent_invalidated",
|
|
"consent_expired",
|
|
"consent_not_prepared",
|
|
"consent_not_confirmed",
|
|
"consent_already_claimed",
|
|
"consent_decision_conflict",
|
|
"idempotency_conflict",
|
|
"execution_already_launched",
|
|
"command_lineage_changed",
|
|
"session_busy",
|
|
"session_conflict",
|
|
]
|
|
|
|
|
|
class DifyBuilderRealCallRefusalResponse(ResponseModel):
|
|
"""Sanitized refusal of a real-call consent operation; carries fixed codes only."""
|
|
|
|
model_config = ConfigDict(json_schema_serialization_defaults_required=True)
|
|
|
|
result: Literal["refusal"] = "refusal"
|
|
code: DifyBuilderRealCallRefusalCode
|
|
reason_code: DifyBuilderRealCallRefusalReason
|
|
|
|
|
|
class DifyBuilderRealCallPermissionRefusalResponse(ResponseModel):
|
|
"""Execution permission refused after ownership was established; nothing foreign is disclosed."""
|
|
|
|
model_config = ConfigDict(json_schema_serialization_defaults_required=True)
|
|
|
|
result: Literal["refusal"] = "refusal"
|
|
code: Literal["needs_permission"]
|
|
reason_code: Literal["execution_permission_denied"]
|
|
|
|
|
|
class DifyBuilderFeatureUnavailableResponse(ResponseModel):
|
|
"""Builder is not enabled for the workspace (the shared ``dify_builder_required`` gate)."""
|
|
|
|
code: Literal["feature_unavailable"]
|
|
|
|
|
|
class DifyBuilderConsoleForbiddenResponse(ResponseModel):
|
|
"""Console permission-guard denial as serialized by the shared console HTTP error handler."""
|
|
|
|
code: Literal["forbidden"]
|
|
message: str
|
|
status: Literal[403]
|
|
|
|
|
|
class DifyBuilderRealCallForbiddenResponse(
|
|
RootModel[
|
|
Annotated[
|
|
DifyBuilderRealCallPermissionRefusalResponse
|
|
| DifyBuilderFeatureUnavailableResponse
|
|
| DifyBuilderConsoleForbiddenResponse,
|
|
Field(discriminator="code"),
|
|
]
|
|
]
|
|
):
|
|
"""Every HTTP 403 body a real-call consent route can return."""
|
|
|
|
|
|
class DifyBuilderNativeTestResponse(ResponseModel):
|
|
"""Strict public native receipt; domain DTO also owns generated schemas."""
|
|
|
|
data: NativeTestPublicReceipt
|
|
|
|
|
|
class DifyBuilderNativeTestInputResponse(ResponseModel):
|
|
data: NativeTestInputBinding
|