feat: 添加视觉设计语言系统并重构认证页面UI
- 新增 visual_design_language.md 设计规范文档 - 新增 auth 设计 tokens (authBackground, authCard, authInput, feedback 系列等) - 重构登录/注册/验证码/重置密码页面为新设计系统 - 新增 AuthHeroHeader, AuthSurfaceCard, AuthSection, AuthField, PasswordField 组件 - 重构 AppBanner 和 Toast 支持多类型配置 (info/success/warning/error) - 后端 AgentScope: 重整 schemas/prompts/tools 作用域, 新增协议文档 - 更新 AGENTS.md 集成视觉设计语言约束
This commit is contained in:
@@ -1,32 +1,47 @@
|
||||
"""Centralized shared schemas for cross-module contracts."""
|
||||
|
||||
from schemas.inbox.messages import InboxMessageStatus, InboxMessageType
|
||||
from schemas.schedule.items import (
|
||||
AttachmentType,
|
||||
from schemas.inbox.messages import (
|
||||
CalendarContent,
|
||||
CalendarDeleteContent,
|
||||
CalendarInviteContent,
|
||||
CalendarUpdateContent,
|
||||
FriendshipContent,
|
||||
InboxMessageContent,
|
||||
InboxMessageStatus,
|
||||
InboxMessageType,
|
||||
parse_calendar_content,
|
||||
)
|
||||
from schemas.invite_codes import InviteCodeRewardConfig
|
||||
from schemas.memories import MemoryContent
|
||||
from schemas.messages import AgentChatMessageMetadata
|
||||
from schemas.schedule.items import (
|
||||
AttachmentType,
|
||||
ScheduleItemMetadata,
|
||||
ScheduleItemMetadataAttachment,
|
||||
ScheduleItemSourceType,
|
||||
ScheduleItemStatus,
|
||||
parse_calendar_content,
|
||||
)
|
||||
from schemas.sessions import SessionStateSnapshot
|
||||
from schemas.user.context import UserContext
|
||||
|
||||
__all__ = [
|
||||
"AgentChatMessageMetadata",
|
||||
"AttachmentType",
|
||||
"CalendarContent",
|
||||
"CalendarDeleteContent",
|
||||
"CalendarInviteContent",
|
||||
"CalendarUpdateContent",
|
||||
"FriendshipContent",
|
||||
"InboxMessageContent",
|
||||
"InboxMessageStatus",
|
||||
"InboxMessageType",
|
||||
"InviteCodeRewardConfig",
|
||||
"MemoryContent",
|
||||
"ScheduleItemMetadata",
|
||||
"ScheduleItemMetadataAttachment",
|
||||
"ScheduleItemSourceType",
|
||||
"ScheduleItemStatus",
|
||||
"SessionStateSnapshot",
|
||||
"UserContext",
|
||||
"parse_calendar_content",
|
||||
]
|
||||
|
||||
@@ -1,54 +1,52 @@
|
||||
from schemas.agent.agui_input import (
|
||||
extract_latest_tool_result,
|
||||
extract_latest_user_content,
|
||||
extract_latest_user_payload,
|
||||
extract_latest_user_text,
|
||||
parse_run_input,
|
||||
validate_run_request_messages_contract,
|
||||
)
|
||||
from schemas.agent.agent_runtime import (
|
||||
AcceptedTaskResponse,
|
||||
AgUiWireEvent,
|
||||
HistorySnapshotResponse,
|
||||
InternalRuntimeEvent,
|
||||
ResumeCommand,
|
||||
RunCommand,
|
||||
TaskAccepted,
|
||||
TaskAcceptedResponse,
|
||||
)
|
||||
from schemas.agent.runtime_models import (
|
||||
ResultType,
|
||||
RouterAgentOutput,
|
||||
RouterUiDecision,
|
||||
RunStatus,
|
||||
ToolAgentOutput,
|
||||
UiHintsPayload,
|
||||
ToolStatus,
|
||||
UiMode,
|
||||
WorkerAgentOutputLite,
|
||||
WorkerAgentOutputRich,
|
||||
WorkerAgentOutput,
|
||||
resolve_worker_output_model,
|
||||
)
|
||||
from schemas.agent.execution import (
|
||||
ExecutionBatchOutput,
|
||||
ExecutionTaskOutput,
|
||||
from schemas.agent.ui_hints import (
|
||||
UiHintAction,
|
||||
UiHintBlock,
|
||||
UiHintsPayload,
|
||||
)
|
||||
from schemas.agent.intent import IntentOutput, IntentTask
|
||||
from schemas.agent.report import ReportOutput
|
||||
from schemas.agent.runtime import RuntimeOutput
|
||||
from schemas.agent.config import SystemAgentLLMConfig
|
||||
from schemas.agent.system_agent import AgentType, SystemAgentLLMConfig
|
||||
|
||||
__all__ = [
|
||||
"AgUiWireEvent",
|
||||
"AcceptedTaskResponse",
|
||||
"ExecutionBatchOutput",
|
||||
"ExecutionTaskOutput",
|
||||
"HistorySnapshotResponse",
|
||||
"IntentOutput",
|
||||
"IntentTask",
|
||||
"InternalRuntimeEvent",
|
||||
"AgentType",
|
||||
"ResultType",
|
||||
"RouterAgentOutput",
|
||||
"RouterUiDecision",
|
||||
"RunStatus",
|
||||
"SystemAgentLLMConfig",
|
||||
"ToolAgentOutput",
|
||||
"ToolStatus",
|
||||
"UiMode",
|
||||
"UiHintAction",
|
||||
"UiHintBlock",
|
||||
"UiHintsPayload",
|
||||
"WorkerAgentOutputLite",
|
||||
"WorkerAgentOutputRich",
|
||||
"WorkerAgentOutput",
|
||||
"resolve_worker_output_model",
|
||||
"extract_latest_tool_result",
|
||||
"extract_latest_user_content",
|
||||
"extract_latest_user_payload",
|
||||
"extract_latest_user_text",
|
||||
"parse_run_input",
|
||||
"validate_run_request_messages_contract",
|
||||
"extract_latest_tool_result",
|
||||
"SystemAgentLLMConfig",
|
||||
"ReportOutput",
|
||||
"ResumeCommand",
|
||||
"RouterAgentOutput",
|
||||
"RuntimeOutput",
|
||||
"RunCommand",
|
||||
"TaskAccepted",
|
||||
"TaskAcceptedResponse",
|
||||
"ToolAgentOutput",
|
||||
"UiHintsPayload",
|
||||
"WorkerAgentOutput",
|
||||
]
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, ClassVar, Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class _AliasModel(BaseModel):
|
||||
model_config: ClassVar[ConfigDict] = ConfigDict(
|
||||
populate_by_name=True, serialize_by_alias=True, extra="forbid"
|
||||
)
|
||||
|
||||
|
||||
class AcceptedTaskResponse(_AliasModel):
|
||||
task_id: str = Field(alias="taskId", min_length=1)
|
||||
thread_id: str = Field(alias="threadId", min_length=1)
|
||||
run_id: str = Field(alias="runId", min_length=1)
|
||||
created: bool
|
||||
|
||||
|
||||
class RunCommand(_AliasModel):
|
||||
thread_id: str = Field(alias="threadId", min_length=1)
|
||||
run_id: str = Field(alias="runId", min_length=1)
|
||||
state: dict[str, Any] | None = None
|
||||
messages: list[dict[str, Any]] = Field(default_factory=list)
|
||||
tools: list[dict[str, Any]] = Field(default_factory=list)
|
||||
context: dict[str, Any] | list[dict[str, Any]] = Field(default_factory=list)
|
||||
parent_run_id: str | None = Field(default=None, alias="parentRunId")
|
||||
forwarded_props: dict[str, Any] = Field(
|
||||
default_factory=dict, alias="forwardedProps"
|
||||
)
|
||||
|
||||
|
||||
class ResumeCommand(RunCommand):
|
||||
pass
|
||||
|
||||
|
||||
# Backward compatibility alias during migration.
|
||||
TaskAcceptedResponse = AcceptedTaskResponse
|
||||
TaskAccepted = AcceptedTaskResponse
|
||||
|
||||
|
||||
class InternalRuntimeEvent(_AliasModel):
|
||||
type: str = Field(min_length=1)
|
||||
thread_id: str | None = Field(default=None, alias="threadId")
|
||||
run_id: str | None = Field(default=None, alias="runId")
|
||||
data: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class AgUiWireEvent(_AliasModel):
|
||||
type: str = Field(min_length=1)
|
||||
thread_id: str | None = Field(default=None, alias="threadId")
|
||||
run_id: str | None = Field(default=None, alias="runId")
|
||||
payload: Any = None
|
||||
|
||||
|
||||
class HistorySnapshot(_AliasModel):
|
||||
scope: Literal["history_day"] = "history_day"
|
||||
thread_id: str | None = Field(default=None, alias="threadId")
|
||||
day: str | None = None
|
||||
has_more: bool = Field(default=False, alias="hasMore")
|
||||
messages: list[dict[str, Any]] = Field(default_factory=list)
|
||||
|
||||
|
||||
class HistorySnapshotResponse(_AliasModel):
|
||||
type: Literal["STATE_SNAPSHOT"] = "STATE_SNAPSHOT"
|
||||
thread_id: str | None = Field(default=None, alias="threadId")
|
||||
run_id: str | None = Field(default=None, alias="runId")
|
||||
snapshot: HistorySnapshot
|
||||
@@ -1,28 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Literal
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class ExecutionToolCall(BaseModel):
|
||||
tool_name: str = Field(min_length=1)
|
||||
args: dict[str, Any] = Field(default_factory=dict)
|
||||
result: Any | None = None
|
||||
error: str | None = None
|
||||
|
||||
|
||||
class ExecutionTaskOutput(BaseModel):
|
||||
task_id: str = Field(min_length=1)
|
||||
status: Literal["SUCCESS", "PARTIAL", "FAILED"]
|
||||
execution_summary: str = Field(min_length=1)
|
||||
execution_data: dict[str, Any] = Field(default_factory=dict)
|
||||
user_feedback_needs: list[str] = Field(default_factory=list)
|
||||
response_metadata: dict[str, Any] = Field(default_factory=dict)
|
||||
tool_calls: list[ExecutionToolCall] = Field(default_factory=list)
|
||||
|
||||
|
||||
class ExecutionBatchOutput(BaseModel):
|
||||
task_results: list[ExecutionTaskOutput] = Field(default_factory=list)
|
||||
overall_status: Literal["SUCCESS", "PARTIAL", "FAILED"]
|
||||
aggregate_summary: str = Field(min_length=1)
|
||||
@@ -1,33 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
|
||||
|
||||
class IntentTask(BaseModel):
|
||||
task_id: str = Field(min_length=1)
|
||||
title: str = Field(min_length=1)
|
||||
objective: str = Field(min_length=1)
|
||||
|
||||
|
||||
class IntentOutput(BaseModel):
|
||||
route: Literal["DIRECT_RESPONSE", "TASK_EXECUTION"]
|
||||
intent_summary: str = Field(min_length=1)
|
||||
direct_response: str | None = None
|
||||
tasks: list[IntentTask] = Field(default_factory=list)
|
||||
complexity: Literal["simple", "complex"]
|
||||
response_metadata: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def validate_route(self) -> "IntentOutput":
|
||||
if self.route == "DIRECT_RESPONSE":
|
||||
if not self.direct_response:
|
||||
raise ValueError("direct_response is required for DIRECT_RESPONSE")
|
||||
if self.tasks:
|
||||
raise ValueError("tasks must be empty for DIRECT_RESPONSE")
|
||||
if self.route == "TASK_EXECUTION":
|
||||
if not self.tasks:
|
||||
raise ValueError("tasks is required for TASK_EXECUTION")
|
||||
return self
|
||||
@@ -1,10 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class ReportOutput(BaseModel):
|
||||
assistant_text: str = Field(min_length=1)
|
||||
response_metadata: dict[str, Any] = Field(default_factory=dict)
|
||||
@@ -1,13 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from schemas.agent.execution import ExecutionBatchOutput
|
||||
from schemas.agent.intent import IntentOutput
|
||||
from schemas.agent.report import ReportOutput
|
||||
|
||||
|
||||
class RuntimeOutput(BaseModel):
|
||||
intent: IntentOutput
|
||||
execution: ExecutionBatchOutput | None = None
|
||||
report: ReportOutput
|
||||
@@ -1,10 +1,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
from typing import Annotated, Any, Literal
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from schemas.agent.ui_hints import UiHintsPayload
|
||||
|
||||
|
||||
class TaskType(str, Enum):
|
||||
KNOWLEDGE = "knowledge"
|
||||
@@ -43,15 +45,43 @@ class ResultType(str, Enum):
|
||||
class TaskTyping(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
primary: TaskType
|
||||
secondary: list[TaskType] = Field(default_factory=list)
|
||||
primary: TaskType = Field(
|
||||
...,
|
||||
description=(
|
||||
"Primary task category. Choose the single category that best "
|
||||
"represents the core user intent."
|
||||
),
|
||||
examples=["planning"],
|
||||
)
|
||||
secondary: list[TaskType] = Field(
|
||||
default_factory=list,
|
||||
description=(
|
||||
"Secondary task categories. Keep only strongly relevant supporting "
|
||||
"categories, up to 3."
|
||||
),
|
||||
examples=[["scheduling", "action_execution"]],
|
||||
)
|
||||
|
||||
|
||||
class ResultTyping(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
primary: ResultType
|
||||
secondary: list[ResultType] = Field(default_factory=list)
|
||||
primary: ResultType = Field(
|
||||
...,
|
||||
description=(
|
||||
"Primary output type. It should match the execution mode and user "
|
||||
"expectation; avoid unknown whenever possible."
|
||||
),
|
||||
examples=["action_plan"],
|
||||
)
|
||||
secondary: list[ResultType] = Field(
|
||||
default_factory=list,
|
||||
description=(
|
||||
"Secondary output types. Use for compatible alternative response "
|
||||
"shapes, up to 3."
|
||||
),
|
||||
examples=[["todo_list", "summary"]],
|
||||
)
|
||||
|
||||
|
||||
class ExecutionMode(str, Enum):
|
||||
@@ -60,6 +90,11 @@ class ExecutionMode(str, Enum):
|
||||
MULTISTEP = "multistep"
|
||||
|
||||
|
||||
class UiMode(str, Enum):
|
||||
NONE = "none"
|
||||
RICH = "rich"
|
||||
|
||||
|
||||
class RunStatus(str, Enum):
|
||||
SUCCESS = "success"
|
||||
PARTIAL_SUCCESS = "partial_success"
|
||||
@@ -72,344 +107,277 @@ class ToolStatus(str, Enum):
|
||||
PARTIAL = "partial"
|
||||
|
||||
|
||||
class UiHintStatus(str, Enum):
|
||||
INFO = "info"
|
||||
SUCCESS = "success"
|
||||
WARNING = "warning"
|
||||
ERROR = "error"
|
||||
PENDING = "pending"
|
||||
|
||||
|
||||
class UiHintActionStyle(str, Enum):
|
||||
PRIMARY = "primary"
|
||||
SECONDARY = "secondary"
|
||||
GHOST = "ghost"
|
||||
DANGER = "danger"
|
||||
|
||||
|
||||
class UiHintTextFormat(str, Enum):
|
||||
PLAIN = "plain"
|
||||
MARKDOWN = "markdown"
|
||||
|
||||
|
||||
class UiHintContainerDirection(str, Enum):
|
||||
VERTICAL = "vertical"
|
||||
HORIZONTAL = "horizontal"
|
||||
|
||||
|
||||
class UiHintKvLayout(str, Enum):
|
||||
VERTICAL = "vertical"
|
||||
HORIZONTAL = "horizontal"
|
||||
GRID = "grid"
|
||||
|
||||
|
||||
class UiHintOperationType(str, Enum):
|
||||
CREATE = "create"
|
||||
UPDATE = "update"
|
||||
DELETE = "delete"
|
||||
EXECUTE = "execute"
|
||||
|
||||
|
||||
class UiHintOperationResult(str, Enum):
|
||||
SUCCESS = "success"
|
||||
FAILURE = "failure"
|
||||
PARTIAL = "partial"
|
||||
|
||||
|
||||
class KeyEntity(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
name: str
|
||||
type: str
|
||||
value: str | None = None
|
||||
name: str = Field(
|
||||
...,
|
||||
description="Entity name, such as meeting/contact/location/project.",
|
||||
)
|
||||
type: str = Field(
|
||||
...,
|
||||
description="Entity type label, such as person/date/location/task.",
|
||||
)
|
||||
value: str | None = Field(
|
||||
default=None,
|
||||
description="Normalized entity value. Keep null if normalization is uncertain.",
|
||||
)
|
||||
|
||||
|
||||
class ConstraintItem(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
key: str
|
||||
value: str
|
||||
required: bool = True
|
||||
key: str = Field(
|
||||
...,
|
||||
description="Constraint key, such as deadline/budget/channel/privacy.",
|
||||
)
|
||||
value: str = Field(
|
||||
...,
|
||||
description="Constraint value in concise natural language or normalized form.",
|
||||
)
|
||||
required: bool = Field(
|
||||
default=True,
|
||||
description=(
|
||||
"Whether this constraint is mandatory. True means execution cannot "
|
||||
"proceed if violated."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class NormalizedTaskInput(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
user_text: str = Field(..., description="归一化后的核心用户请求")
|
||||
user_text: str = Field(
|
||||
...,
|
||||
description="Normalized core user request text.",
|
||||
examples=["Reschedule tomorrow's 9am standup to 3pm and notify attendees."],
|
||||
)
|
||||
multimodal_summary: list[str] = Field(
|
||||
default_factory=list,
|
||||
description="Router 从图片/附件提炼出的要点",
|
||||
description="Key points extracted by router from images or attachments.",
|
||||
examples=[["Screenshot shows a calendar conflict at 09:00."]],
|
||||
)
|
||||
|
||||
|
||||
class RouterUiDecision(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
ui_mode: UiMode = Field(
|
||||
...,
|
||||
description=(
|
||||
"UI rendering mode decision for downstream worker schema selection. "
|
||||
"Use 'none' when plain text response is sufficient; use 'rich' "
|
||||
"when structured UI hints are beneficial."
|
||||
),
|
||||
examples=["none", "rich"],
|
||||
)
|
||||
ui_decision_reason: str = Field(
|
||||
...,
|
||||
description=(
|
||||
"Brief reason for UI mode decision, focused on user intent and "
|
||||
"information complexity."
|
||||
),
|
||||
examples=[
|
||||
"User asked a simple factual question; plain text is sufficient.",
|
||||
"User needs actionable options and status blocks; rich UI helps scanning.",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
class RouterAgentOutput(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
normalized_task_input: NormalizedTaskInput
|
||||
key_entities: list[KeyEntity] = Field(default_factory=list)
|
||||
constraints: list[ConstraintItem] = Field(default_factory=list)
|
||||
task_typing: TaskTyping
|
||||
execution_mode: ExecutionMode
|
||||
result_typing: ResultTyping
|
||||
normalized_task_input: NormalizedTaskInput = Field(
|
||||
...,
|
||||
description=(
|
||||
"Normalized task input for routing. Preserve user intent faithfully "
|
||||
"without adding or dropping critical semantics."
|
||||
),
|
||||
examples=[
|
||||
{
|
||||
"user_text": "Reschedule tomorrow's 9am standup to 3pm and notify attendees.",
|
||||
"multimodal_summary": ["Calendar screenshot indicates 09:00 conflict."],
|
||||
}
|
||||
],
|
||||
)
|
||||
key_entities: list[KeyEntity] = Field(
|
||||
default_factory=list,
|
||||
description=(
|
||||
"Key entities directly relevant to task execution. Return an empty "
|
||||
"list when confidence is low."
|
||||
),
|
||||
examples=[
|
||||
[
|
||||
{"name": "standup", "type": "event", "value": "team-standup"},
|
||||
{
|
||||
"name": "tomorrow 9am",
|
||||
"type": "datetime",
|
||||
"value": "2026-03-14T09:00:00+08:00",
|
||||
},
|
||||
{
|
||||
"name": "3pm",
|
||||
"type": "datetime",
|
||||
"value": "2026-03-14T15:00:00+08:00",
|
||||
},
|
||||
]
|
||||
],
|
||||
)
|
||||
constraints: list[ConstraintItem] = Field(
|
||||
default_factory=list,
|
||||
description=(
|
||||
"Execution constraints, including explicit constraints and "
|
||||
"high-confidence inferred constraints."
|
||||
),
|
||||
examples=[
|
||||
[
|
||||
{"key": "must_notify_attendees", "value": "true", "required": True},
|
||||
{"key": "timezone", "value": "Asia/Shanghai", "required": True},
|
||||
]
|
||||
],
|
||||
)
|
||||
task_typing: TaskTyping = Field(
|
||||
...,
|
||||
description=(
|
||||
"Task typing result used by downstream agents for strategy and "
|
||||
"capability boundaries."
|
||||
),
|
||||
examples=[{"primary": "scheduling", "secondary": ["communication_drafting"]}],
|
||||
)
|
||||
execution_mode: ExecutionMode = Field(
|
||||
...,
|
||||
description=(
|
||||
"Recommended execution mode: onestep/tool_assisted/multistep. It "
|
||||
"must be feasible under current context and capabilities."
|
||||
),
|
||||
examples=["tool_assisted"],
|
||||
)
|
||||
result_typing: ResultTyping = Field(
|
||||
...,
|
||||
description=(
|
||||
"Expected result typing used to constrain downstream output "
|
||||
"structure and expression."
|
||||
),
|
||||
examples=[
|
||||
{
|
||||
"primary": "execution_report",
|
||||
"secondary": ["summary", "options_with_recommendation"],
|
||||
}
|
||||
],
|
||||
)
|
||||
ui: RouterUiDecision = Field(
|
||||
...,
|
||||
description=(
|
||||
"Router decision on whether downstream worker should use rich UI "
|
||||
"schema or lightweight text-only schema."
|
||||
),
|
||||
examples=[
|
||||
{
|
||||
"ui_mode": "rich",
|
||||
"ui_decision_reason": "The request includes multiple actionable outcomes and benefits from structured blocks.",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
class ErrorInfo(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
code: str
|
||||
message: str
|
||||
retryable: bool = False
|
||||
details: dict[str, Any] | None = None
|
||||
|
||||
|
||||
class UiHintConfirm(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
title: str | None = None
|
||||
message: str | None = None
|
||||
confirm_label: str | None = Field(default=None, alias="confirmLabel")
|
||||
cancel_label: str | None = Field(default=None, alias="cancelLabel")
|
||||
|
||||
|
||||
class UiHintActionNavigation(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
type: Literal["navigation"]
|
||||
path: str
|
||||
params: dict[str, Any] | None = None
|
||||
|
||||
|
||||
class UiHintActionUrl(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
type: Literal["url"]
|
||||
url: str
|
||||
target: Literal["_self", "_blank"] | None = None
|
||||
|
||||
|
||||
class UiHintActionEvent(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
type: Literal["event"]
|
||||
event: str
|
||||
payload: dict[str, Any] | None = None
|
||||
|
||||
|
||||
class UiHintActionTool(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
type: Literal["tool"]
|
||||
tool_id: str = Field(alias="toolId")
|
||||
params: dict[str, Any] | None = None
|
||||
|
||||
|
||||
class UiHintActionCopy(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
type: Literal["copy"]
|
||||
content: str
|
||||
success_message: str | None = Field(default=None, alias="successMessage")
|
||||
|
||||
|
||||
class UiHintActionPayload(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
type: Literal["payload"]
|
||||
payload: dict[str, Any]
|
||||
submit_to: str | None = Field(default=None, alias="submitTo")
|
||||
|
||||
|
||||
UiHintActionTarget = Annotated[
|
||||
(
|
||||
UiHintActionNavigation
|
||||
| UiHintActionUrl
|
||||
| UiHintActionEvent
|
||||
| UiHintActionTool
|
||||
| UiHintActionCopy
|
||||
| UiHintActionPayload
|
||||
),
|
||||
Field(discriminator="type"),
|
||||
]
|
||||
|
||||
|
||||
class UiHintAction(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
id: str | None = None
|
||||
label: str
|
||||
style: UiHintActionStyle | None = None
|
||||
disabled: bool = False
|
||||
action: UiHintActionTarget
|
||||
confirm: UiHintConfirm | None = None
|
||||
|
||||
|
||||
class UiHintIcon(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
source: Literal["icon", "emoji", "url"]
|
||||
value: str
|
||||
color: str | None = None
|
||||
size: int | None = None
|
||||
|
||||
|
||||
class UiHintBadge(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
label: str
|
||||
variant: Literal["default", "success", "warning", "error", "info"] = "default"
|
||||
|
||||
|
||||
class UiHintKeyValuePair(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
key: str
|
||||
label: str | None = None
|
||||
value: str | int | bool | None = None
|
||||
copyable: bool = False
|
||||
|
||||
|
||||
class UiHintListItem(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
id: str | None = None
|
||||
title: str
|
||||
subtitle: str | None = None
|
||||
description: str | None = None
|
||||
icon: UiHintIcon | None = None
|
||||
badge: UiHintBadge | None = None
|
||||
metadata: dict[str, Any] = Field(default_factory=dict)
|
||||
actions: list[UiHintAction] = Field(default_factory=list)
|
||||
|
||||
|
||||
class UiHintPagination(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
page: int
|
||||
page_size: int = Field(alias="pageSize")
|
||||
total: int
|
||||
has_more: bool = Field(alias="hasMore")
|
||||
|
||||
|
||||
class UiHintBaseBlock(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
id: str | None = None
|
||||
title: str | None = None
|
||||
description: str | None = None
|
||||
status: UiHintStatus | None = None
|
||||
actions: list[UiHintAction] = Field(default_factory=list)
|
||||
|
||||
|
||||
class UiHintTextBlock(UiHintBaseBlock):
|
||||
kind: Literal["text"]
|
||||
content: str
|
||||
format: UiHintTextFormat = UiHintTextFormat.PLAIN
|
||||
|
||||
|
||||
class UiHintCardBlock(UiHintBaseBlock):
|
||||
kind: Literal["card"]
|
||||
children: list["UiHintBlock"] = Field(default_factory=list)
|
||||
|
||||
|
||||
class UiHintKvBlock(UiHintBaseBlock):
|
||||
kind: Literal["kv"]
|
||||
pairs: list[UiHintKeyValuePair] = Field(default_factory=list)
|
||||
layout: UiHintKvLayout = UiHintKvLayout.VERTICAL
|
||||
|
||||
|
||||
class UiHintListBlock(UiHintBaseBlock):
|
||||
kind: Literal["list"]
|
||||
items: list[UiHintListItem] = Field(default_factory=list)
|
||||
pagination: UiHintPagination | None = None
|
||||
empty_text: str | None = Field(default=None, alias="emptyText")
|
||||
|
||||
|
||||
class UiHintOperationBlock(UiHintBaseBlock):
|
||||
kind: Literal["operation"]
|
||||
operation: UiHintOperationType
|
||||
result: UiHintOperationResult
|
||||
message: str | None = None
|
||||
affected_count: int | None = Field(default=None, alias="affectedCount")
|
||||
details: dict[str, Any] | None = None
|
||||
|
||||
|
||||
class UiHintErrorBlock(UiHintBaseBlock):
|
||||
kind: Literal["error"]
|
||||
error_code: str = Field(alias="errorCode")
|
||||
message: str
|
||||
retryable: bool = False
|
||||
details: str | None = None
|
||||
suggestions: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class UiHintContainerBlock(UiHintBaseBlock):
|
||||
kind: Literal["container"]
|
||||
direction: UiHintContainerDirection = UiHintContainerDirection.VERTICAL
|
||||
gap: int | None = None
|
||||
children: list["UiHintBlock"] = Field(default_factory=list)
|
||||
|
||||
|
||||
class UiHintCustomBlock(UiHintBaseBlock):
|
||||
kind: Literal["custom"]
|
||||
renderer_key: str = Field(alias="rendererKey")
|
||||
payload: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
UiHintBlock = Annotated[
|
||||
(
|
||||
UiHintTextBlock
|
||||
| UiHintCardBlock
|
||||
| UiHintKvBlock
|
||||
| UiHintListBlock
|
||||
| UiHintOperationBlock
|
||||
| UiHintErrorBlock
|
||||
| UiHintContainerBlock
|
||||
| UiHintCustomBlock
|
||||
),
|
||||
Field(discriminator="kind"),
|
||||
]
|
||||
|
||||
|
||||
class UiHintsPayload(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
version: str = "1.0"
|
||||
status: UiHintStatus = UiHintStatus.INFO
|
||||
title: str | None = None
|
||||
description: str | None = None
|
||||
blocks: list[UiHintBlock] = Field(default_factory=list)
|
||||
actions: list[UiHintAction] = Field(default_factory=list)
|
||||
meta: dict[str, Any] = Field(default_factory=dict)
|
||||
code: str = Field(
|
||||
...,
|
||||
description="Stable error code for programmatic handling and analytics.",
|
||||
)
|
||||
message: str = Field(
|
||||
...,
|
||||
description="Human-readable error message for user or upstream agent.",
|
||||
)
|
||||
retryable: bool = Field(
|
||||
default=False,
|
||||
description="Whether retrying can likely resolve this error.",
|
||||
)
|
||||
details: dict[str, Any] | None = Field(
|
||||
default=None,
|
||||
description="Diagnostic details. Must not contain sensitive data or secrets.",
|
||||
)
|
||||
|
||||
|
||||
class ToolAgentOutput(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
tool_name: str
|
||||
tool_call_id: str
|
||||
tool_call_args: dict[str, Any] | None = None
|
||||
status: ToolStatus
|
||||
result_summary: str
|
||||
ui_hints: UiHintsPayload | None = None
|
||||
error: ErrorInfo | None = None
|
||||
tool_name: str = Field(..., description="Invoked tool name.")
|
||||
tool_call_id: str = Field(
|
||||
..., description="Tool call identifier for this invocation."
|
||||
)
|
||||
tool_call_args: dict[str, Any] | None = Field(
|
||||
default=None,
|
||||
description="Snapshot of tool call arguments for traceability and debugging.",
|
||||
)
|
||||
status: ToolStatus = Field(..., description="Tool execution status.")
|
||||
result_summary: str = Field(
|
||||
...,
|
||||
description="Concise tool result summary with key facts and without verbose logs.",
|
||||
)
|
||||
ui_hints: UiHintsPayload | None = Field(
|
||||
default=None,
|
||||
description="Optional UI semantic hints translated into ui_schema by ui_compiler.",
|
||||
)
|
||||
error: ErrorInfo | None = Field(
|
||||
default=None, description="Tool execution error details."
|
||||
)
|
||||
|
||||
|
||||
class WorkerAgentOutput(BaseModel):
|
||||
class WorkerAgentOutputLite(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
status: RunStatus = RunStatus.SUCCESS
|
||||
answer: str = Field(..., description="完整正文")
|
||||
key_points: list[str] = Field(
|
||||
default_factory=list, description="关键点,建议 0~5 条"
|
||||
status: RunStatus = Field(
|
||||
default=RunStatus.SUCCESS,
|
||||
description="Worker execution status: success/partial_success/failed.",
|
||||
examples=["success"],
|
||||
)
|
||||
answer: str = Field(
|
||||
...,
|
||||
description=(
|
||||
"Primary user-facing response text. Lead with conclusion, then "
|
||||
"include only necessary details."
|
||||
),
|
||||
examples=[
|
||||
"Done. I moved the standup to 3:00 PM tomorrow and prepared attendee notifications."
|
||||
],
|
||||
)
|
||||
key_points: list[str] = Field(
|
||||
default_factory=list,
|
||||
description="Key point summary, recommended 0-5 items, one sentence each.",
|
||||
examples=[["Original slot conflicted at 09:00.", "New slot set to 15:00."]],
|
||||
)
|
||||
result_type: ResultType = Field(
|
||||
default=ResultType.UNKNOWN,
|
||||
description="Structured result type of this response. Avoid unknown whenever possible.",
|
||||
examples=["execution_report"],
|
||||
)
|
||||
result_type: ResultType = ResultType.UNKNOWN
|
||||
suggested_actions: list[str] = Field(
|
||||
default_factory=list,
|
||||
description="后续建议行动,0~3条",
|
||||
description="Suggested next actions, 0-3 items, actionable and relevant.",
|
||||
examples=[["Review attendee RSVP status after notifications are sent."]],
|
||||
)
|
||||
error: ErrorInfo | None = Field(
|
||||
default=None,
|
||||
description="Error information for failed or partially failed runs; null on success.",
|
||||
)
|
||||
ui_hints: UiHintsPayload | None = None
|
||||
error: ErrorInfo | None = None
|
||||
|
||||
|
||||
UiHintCardBlock.model_rebuild()
|
||||
UiHintContainerBlock.model_rebuild()
|
||||
class WorkerAgentOutputRich(WorkerAgentOutputLite):
|
||||
ui_hints: UiHintsPayload | None = Field(
|
||||
default=None,
|
||||
description=(
|
||||
"Optional expressive UI semantic annotations. Focus on information "
|
||||
"and interaction intent, not concrete visual styling instructions."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
WorkerAgentOutput = WorkerAgentOutputRich
|
||||
|
||||
|
||||
def resolve_worker_output_model(ui_mode: UiMode) -> type[WorkerAgentOutputLite]:
|
||||
if ui_mode == UiMode.RICH:
|
||||
return WorkerAgentOutputRich
|
||||
return WorkerAgentOutputLite
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class AgentType(str, Enum):
|
||||
ROUTER = "router"
|
||||
WORKER = "worker"
|
||||
|
||||
|
||||
class SystemAgentLLMConfig(BaseModel):
|
||||
temperature: float | None = Field(default=None, ge=0.0, le=2.0)
|
||||
max_tokens: int | None = Field(default=None, ge=1)
|
||||
@@ -0,0 +1,545 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
from typing import Annotated, Any, Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class UiHintStatus(str, Enum):
|
||||
INFO = "info"
|
||||
SUCCESS = "success"
|
||||
WARNING = "warning"
|
||||
ERROR = "error"
|
||||
PENDING = "pending"
|
||||
|
||||
|
||||
class UiHintActionStyle(str, Enum):
|
||||
PRIMARY = "primary"
|
||||
SECONDARY = "secondary"
|
||||
GHOST = "ghost"
|
||||
DANGER = "danger"
|
||||
|
||||
|
||||
class UiHintTextFormat(str, Enum):
|
||||
PLAIN = "plain"
|
||||
MARKDOWN = "markdown"
|
||||
|
||||
|
||||
class UiHintContainerDirection(str, Enum):
|
||||
VERTICAL = "vertical"
|
||||
HORIZONTAL = "horizontal"
|
||||
|
||||
|
||||
class UiHintKvLayout(str, Enum):
|
||||
VERTICAL = "vertical"
|
||||
HORIZONTAL = "horizontal"
|
||||
GRID = "grid"
|
||||
|
||||
|
||||
class UiHintOperationType(str, Enum):
|
||||
CREATE = "create"
|
||||
UPDATE = "update"
|
||||
DELETE = "delete"
|
||||
EXECUTE = "execute"
|
||||
|
||||
|
||||
class UiHintOperationResult(str, Enum):
|
||||
SUCCESS = "success"
|
||||
FAILURE = "failure"
|
||||
PARTIAL = "partial"
|
||||
|
||||
|
||||
class UiHintConfirm(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
title: str | None = Field(
|
||||
default=None,
|
||||
description="Optional confirmation dialog title.",
|
||||
)
|
||||
message: str | None = Field(
|
||||
default=None,
|
||||
description="Optional confirmation message shown before action execution.",
|
||||
)
|
||||
confirm_label: str | None = Field(
|
||||
default=None,
|
||||
alias="confirmLabel",
|
||||
description="Optional confirm button label, e.g. 'Delete'.",
|
||||
)
|
||||
cancel_label: str | None = Field(
|
||||
default=None,
|
||||
alias="cancelLabel",
|
||||
description="Optional cancel button label, e.g. 'Cancel'.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintActionNavigation(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
type: Literal["navigation"]
|
||||
path: str = Field(
|
||||
...,
|
||||
description="Internal route path to navigate to.",
|
||||
)
|
||||
params: dict[str, Any] | None = Field(
|
||||
default=None,
|
||||
description="Optional route params for internal navigation.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintActionUrl(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
type: Literal["url"]
|
||||
url: str = Field(..., description="External URL to open.")
|
||||
target: Literal["_self", "_blank"] | None = Field(
|
||||
default=None,
|
||||
description="Optional browser target for URL action.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintActionEvent(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
type: Literal["event"]
|
||||
event: str = Field(
|
||||
...,
|
||||
description="Frontend domain event name, e.g. 'chat.retry'.",
|
||||
)
|
||||
payload: dict[str, Any] | None = Field(
|
||||
default=None,
|
||||
description="Optional event payload for frontend event handling.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintActionTool(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
type: Literal["tool"]
|
||||
tool_id: str = Field(
|
||||
alias="toolId",
|
||||
description="Tool identifier used to trigger another tool execution.",
|
||||
)
|
||||
params: dict[str, Any] | None = Field(
|
||||
default=None,
|
||||
description="Optional parameters for tool re-execution.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintActionCopy(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
type: Literal["copy"]
|
||||
content: str = Field(..., description="Text content to copy to clipboard.")
|
||||
success_message: str | None = Field(
|
||||
default=None,
|
||||
alias="successMessage",
|
||||
description="Optional user-facing success message after copy.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintActionPayload(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
type: Literal["payload"]
|
||||
payload: dict[str, Any] = Field(
|
||||
...,
|
||||
description="Structured payload to submit to frontend or gateway.",
|
||||
)
|
||||
submit_to: str | None = Field(
|
||||
default=None,
|
||||
alias="submitTo",
|
||||
description="Optional submit target path or endpoint key.",
|
||||
)
|
||||
|
||||
|
||||
UiHintActionTarget = Annotated[
|
||||
(
|
||||
UiHintActionNavigation
|
||||
| UiHintActionUrl
|
||||
| UiHintActionEvent
|
||||
| UiHintActionTool
|
||||
| UiHintActionCopy
|
||||
| UiHintActionPayload
|
||||
),
|
||||
Field(discriminator="type"),
|
||||
]
|
||||
|
||||
|
||||
class UiHintAction(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra="forbid",
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"id": "action-open-calendar",
|
||||
"label": "Open calendar",
|
||||
"style": "primary",
|
||||
"action": {"type": "navigation", "path": "/calendar"},
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
id: str | None = Field(
|
||||
default=None,
|
||||
description="Optional stable action id for tracking and targeting.",
|
||||
)
|
||||
label: str = Field(
|
||||
...,
|
||||
description="User-facing action label shown on button/link.",
|
||||
)
|
||||
style: UiHintActionStyle | None = Field(
|
||||
default=None,
|
||||
description="Optional semantic button style.",
|
||||
)
|
||||
disabled: bool = Field(
|
||||
default=False,
|
||||
description="Whether this action should be rendered as disabled.",
|
||||
)
|
||||
action: UiHintActionTarget = Field(
|
||||
...,
|
||||
description="Executable action target definition.",
|
||||
)
|
||||
confirm: UiHintConfirm | None = Field(
|
||||
default=None,
|
||||
description="Optional confirmation requirement before execution.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintIcon(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
source: Literal["icon", "emoji", "url"] = Field(
|
||||
...,
|
||||
description="Icon source type.",
|
||||
)
|
||||
value: str = Field(
|
||||
...,
|
||||
description="Icon identifier, emoji text, or image URL based on source.",
|
||||
)
|
||||
color: str | None = Field(
|
||||
default=None,
|
||||
description="Optional semantic color hint. Do not encode pixel-level style rules.",
|
||||
)
|
||||
size: int | None = Field(
|
||||
default=None,
|
||||
description="Optional icon size hint in abstract UI units.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintBadge(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
label: str = Field(..., description="Badge text label.")
|
||||
variant: Literal["default", "success", "warning", "error", "info"] = Field(
|
||||
default="default",
|
||||
description="Semantic badge variant.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintKeyValuePair(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
key: str = Field(..., description="Stable key identifier for this pair.")
|
||||
label: str | None = Field(
|
||||
default=None,
|
||||
description="Optional user-facing label. Fallback to key when missing.",
|
||||
)
|
||||
value: str | int | bool | None = Field(
|
||||
default=None,
|
||||
description="Scalar value for this key-value pair.",
|
||||
)
|
||||
copyable: bool = Field(
|
||||
default=False,
|
||||
description="Whether frontend may offer copy interaction for this value.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintListItem(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
id: str | None = Field(
|
||||
default=None,
|
||||
description="Optional stable list item id.",
|
||||
)
|
||||
title: str = Field(..., description="Primary list item title.")
|
||||
subtitle: str | None = Field(
|
||||
default=None,
|
||||
description="Optional short secondary text.",
|
||||
)
|
||||
description: str | None = Field(
|
||||
default=None,
|
||||
description="Optional detailed description for this item.",
|
||||
)
|
||||
icon: UiHintIcon | None = Field(
|
||||
default=None,
|
||||
description="Optional semantic icon metadata.",
|
||||
)
|
||||
badge: UiHintBadge | None = Field(
|
||||
default=None,
|
||||
description="Optional semantic badge metadata.",
|
||||
)
|
||||
metadata: dict[str, Any] = Field(
|
||||
default_factory=dict,
|
||||
description="Optional non-visual metadata for analytics or interactions.",
|
||||
)
|
||||
actions: list[UiHintAction] = Field(
|
||||
default_factory=list,
|
||||
description="Optional per-item actions, recommended up to 3.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintPagination(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
page: int = Field(..., description="Current page number starting from 1.")
|
||||
page_size: int = Field(
|
||||
alias="pageSize",
|
||||
description="Page size used for this list page.",
|
||||
)
|
||||
total: int = Field(..., description="Total number of records.")
|
||||
has_more: bool = Field(
|
||||
alias="hasMore",
|
||||
description="Whether there are more pages after current page.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintBaseBlock(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
id: str | None = Field(
|
||||
default=None,
|
||||
description="Optional stable block id.",
|
||||
)
|
||||
title: str | None = Field(
|
||||
default=None,
|
||||
description="Optional block title.",
|
||||
)
|
||||
description: str | None = Field(
|
||||
default=None,
|
||||
description="Optional block description.",
|
||||
)
|
||||
status: UiHintStatus | None = Field(
|
||||
default=None,
|
||||
description="Optional semantic status for this block.",
|
||||
)
|
||||
actions: list[UiHintAction] = Field(
|
||||
default_factory=list,
|
||||
description="Optional block-level actions, recommended up to 3.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintTextBlock(UiHintBaseBlock):
|
||||
kind: Literal["text"]
|
||||
content: str = Field(
|
||||
...,
|
||||
description="Main text content to present.",
|
||||
)
|
||||
format: UiHintTextFormat = Field(
|
||||
default=UiHintTextFormat.PLAIN,
|
||||
description="Text format: plain or markdown.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintCardBlock(UiHintBaseBlock):
|
||||
kind: Literal["card"]
|
||||
children: list["UiHintBlock"] = Field(
|
||||
default_factory=list,
|
||||
description="Nested child blocks grouped under this card.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintKvBlock(UiHintBaseBlock):
|
||||
kind: Literal["kv"]
|
||||
pairs: list[UiHintKeyValuePair] = Field(
|
||||
default_factory=list,
|
||||
description="Key-value pairs to display.",
|
||||
)
|
||||
layout: UiHintKvLayout = Field(
|
||||
default=UiHintKvLayout.VERTICAL,
|
||||
description="Preferred semantic layout for key-value content.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintListBlock(UiHintBaseBlock):
|
||||
kind: Literal["list"]
|
||||
items: list[UiHintListItem] = Field(
|
||||
default_factory=list,
|
||||
description="List items to present.",
|
||||
)
|
||||
pagination: UiHintPagination | None = Field(
|
||||
default=None,
|
||||
description="Optional pagination metadata.",
|
||||
)
|
||||
empty_text: str | None = Field(
|
||||
default=None,
|
||||
alias="emptyText",
|
||||
description="Optional message shown when list items are empty.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintOperationBlock(UiHintBaseBlock):
|
||||
kind: Literal["operation"]
|
||||
operation: UiHintOperationType = Field(
|
||||
...,
|
||||
description="Operation category: create/update/delete/execute.",
|
||||
)
|
||||
result: UiHintOperationResult = Field(
|
||||
...,
|
||||
description="Operation result: success/failure/partial.",
|
||||
)
|
||||
message: str | None = Field(
|
||||
default=None,
|
||||
description="Optional operation summary message.",
|
||||
)
|
||||
affected_count: int | None = Field(
|
||||
default=None,
|
||||
alias="affectedCount",
|
||||
description="Optional affected record count.",
|
||||
)
|
||||
details: dict[str, Any] | None = Field(
|
||||
default=None,
|
||||
description="Optional machine-readable operation details.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintErrorBlock(UiHintBaseBlock):
|
||||
kind: Literal["error"]
|
||||
error_code: str = Field(
|
||||
alias="errorCode",
|
||||
description="Stable error code for categorization.",
|
||||
)
|
||||
message: str = Field(
|
||||
...,
|
||||
description="Human-readable error message.",
|
||||
)
|
||||
retryable: bool = Field(
|
||||
default=False,
|
||||
description="Whether retry is likely to succeed.",
|
||||
)
|
||||
details: str | None = Field(
|
||||
default=None,
|
||||
description="Optional plain-text diagnostic details.",
|
||||
)
|
||||
suggestions: list[str] = Field(
|
||||
default_factory=list,
|
||||
description="Optional actionable suggestions, recommended up to 3.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintContainerBlock(UiHintBaseBlock):
|
||||
kind: Literal["container"]
|
||||
direction: UiHintContainerDirection = Field(
|
||||
default=UiHintContainerDirection.VERTICAL,
|
||||
description="Child block layout direction.",
|
||||
)
|
||||
gap: int | None = Field(
|
||||
default=None,
|
||||
description="Optional semantic spacing hint between children.",
|
||||
)
|
||||
children: list["UiHintBlock"] = Field(
|
||||
default_factory=list,
|
||||
description="Nested child blocks in this container.",
|
||||
)
|
||||
|
||||
|
||||
class UiHintCustomBlock(UiHintBaseBlock):
|
||||
kind: Literal["custom"]
|
||||
renderer_key: str = Field(
|
||||
alias="rendererKey",
|
||||
description=(
|
||||
"Custom semantic renderer key. Use only when standard block kinds "
|
||||
"cannot represent the intent."
|
||||
),
|
||||
)
|
||||
payload: dict[str, Any] = Field(
|
||||
default_factory=dict,
|
||||
description="Structured custom payload consumed by the renderer.",
|
||||
)
|
||||
|
||||
|
||||
UiHintBlock = Annotated[
|
||||
(
|
||||
UiHintTextBlock
|
||||
| UiHintCardBlock
|
||||
| UiHintKvBlock
|
||||
| UiHintListBlock
|
||||
| UiHintOperationBlock
|
||||
| UiHintErrorBlock
|
||||
| UiHintContainerBlock
|
||||
| UiHintCustomBlock
|
||||
),
|
||||
Field(discriminator="kind"),
|
||||
]
|
||||
|
||||
|
||||
class UiHintsPayload(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra="forbid",
|
||||
json_schema_extra={
|
||||
"examples": [
|
||||
{
|
||||
"version": "1.0",
|
||||
"status": "info",
|
||||
"title": "Schedule update",
|
||||
"blocks": [
|
||||
{
|
||||
"kind": "text",
|
||||
"content": "Your meeting is moved to 3:00 PM.",
|
||||
"format": "plain",
|
||||
},
|
||||
{
|
||||
"kind": "list",
|
||||
"title": "Next steps",
|
||||
"items": [
|
||||
{"title": "Open calendar"},
|
||||
{"title": "Notify attendees"},
|
||||
],
|
||||
},
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"label": "Open calendar",
|
||||
"style": "primary",
|
||||
"action": {"type": "navigation", "path": "/calendar"},
|
||||
}
|
||||
],
|
||||
"meta": {"source": "worker"},
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
version: str = Field(
|
||||
default="1.0",
|
||||
description="Ui hints payload version.",
|
||||
)
|
||||
status: UiHintStatus = Field(
|
||||
default=UiHintStatus.INFO,
|
||||
description="Overall semantic status for the full ui_hints payload.",
|
||||
)
|
||||
title: str | None = Field(
|
||||
default=None,
|
||||
description="Optional top-level semantic title.",
|
||||
)
|
||||
description: str | None = Field(
|
||||
default=None,
|
||||
description="Optional top-level semantic description.",
|
||||
)
|
||||
blocks: list[UiHintBlock] = Field(
|
||||
default_factory=list,
|
||||
description="Main semantic content blocks.",
|
||||
)
|
||||
actions: list[UiHintAction] = Field(
|
||||
default_factory=list,
|
||||
description="Optional top-level actions, recommended up to 3.",
|
||||
)
|
||||
meta: dict[str, Any] = Field(
|
||||
default_factory=dict,
|
||||
description="Optional non-visual metadata for tracing and integration.",
|
||||
)
|
||||
|
||||
|
||||
UiHintCardBlock.model_rebuild()
|
||||
UiHintContainerBlock.model_rebuild()
|
||||
@@ -1,6 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from enum import Enum
|
||||
from typing import ClassVar, Literal, Union
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class InboxMessageType(str, Enum):
|
||||
@@ -15,3 +19,65 @@ class InboxMessageStatus(str, Enum):
|
||||
ACCEPTED = "accepted"
|
||||
REJECTED = "rejected"
|
||||
DISMISSED = "dismissed"
|
||||
|
||||
|
||||
class CalendarInviteContent(BaseModel):
|
||||
model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid")
|
||||
|
||||
type: Literal["invite"]
|
||||
permission: int = Field(..., description="权限: 1=view, 4=edit, 8=invite")
|
||||
action: Literal["pending"] = "pending"
|
||||
|
||||
|
||||
class CalendarUpdateContent(BaseModel):
|
||||
model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid")
|
||||
|
||||
type: Literal["update"]
|
||||
title: str = Field(..., description="事件标题")
|
||||
action: Literal["updated"] = "updated"
|
||||
|
||||
|
||||
class CalendarDeleteContent(BaseModel):
|
||||
model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid")
|
||||
|
||||
type: Literal["delete"]
|
||||
title: str = Field(..., description="事件标题")
|
||||
action: Literal["deleted"] = "deleted"
|
||||
|
||||
|
||||
class FriendshipContent(BaseModel):
|
||||
model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid")
|
||||
|
||||
type: Literal["request"]
|
||||
message: str | None = Field(None, description="好友申请消息")
|
||||
|
||||
|
||||
CalendarContent = Union[
|
||||
CalendarInviteContent,
|
||||
CalendarUpdateContent,
|
||||
CalendarDeleteContent,
|
||||
]
|
||||
|
||||
InboxMessageContent = Union[
|
||||
CalendarInviteContent,
|
||||
CalendarUpdateContent,
|
||||
CalendarDeleteContent,
|
||||
FriendshipContent,
|
||||
]
|
||||
|
||||
|
||||
def parse_calendar_content(content: str | None) -> CalendarContent | None:
|
||||
if not content:
|
||||
return None
|
||||
try:
|
||||
data = json.loads(content)
|
||||
content_type = data.get("type")
|
||||
if content_type == "invite":
|
||||
return CalendarInviteContent(**data)
|
||||
if content_type == "update":
|
||||
return CalendarUpdateContent(**data)
|
||||
if content_type == "delete":
|
||||
return CalendarDeleteContent(**data)
|
||||
raise ValueError(f"Unknown calendar content type: {content_type}")
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import ClassVar
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
class InviteCodeRewardConfig(BaseModel):
|
||||
model_config: ClassVar[ConfigDict] = ConfigDict(extra="allow")
|
||||
|
||||
pass
|
||||
@@ -0,0 +1,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import ClassVar
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
class MemoryContent(BaseModel):
|
||||
model_config: ClassVar[ConfigDict] = ConfigDict(extra="allow")
|
||||
|
||||
pass
|
||||
@@ -0,0 +1,3 @@
|
||||
from schemas.messages.chat_message import AgentChatMessageMetadata
|
||||
|
||||
__all__ = ["AgentChatMessageMetadata"]
|
||||
@@ -0,0 +1,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import ClassVar
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
class AgentChatMessageMetadata(BaseModel):
|
||||
model_config: ClassVar[ConfigDict] = ConfigDict(extra="allow")
|
||||
|
||||
run_id: str | None = None
|
||||
stage: str | None = None
|
||||
latency_ms: int | None = None
|
||||
message_id: str | None = None
|
||||
@@ -1,14 +1,16 @@
|
||||
from schemas.schedule.items import (
|
||||
AttachmentType,
|
||||
from schemas.inbox.messages import (
|
||||
CalendarContent,
|
||||
CalendarDeleteContent,
|
||||
CalendarInviteContent,
|
||||
CalendarUpdateContent,
|
||||
parse_calendar_content,
|
||||
)
|
||||
from schemas.schedule.items import (
|
||||
AttachmentType,
|
||||
ScheduleItemMetadata,
|
||||
ScheduleItemMetadataAttachment,
|
||||
ScheduleItemSourceType,
|
||||
ScheduleItemStatus,
|
||||
parse_calendar_content,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from enum import Enum
|
||||
from typing import ClassVar, Literal, Union
|
||||
from typing import ClassVar, Literal
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
@@ -46,51 +45,3 @@ class ScheduleItemSourceType(str, Enum):
|
||||
MANUAL = "manual"
|
||||
IMPORTED = "imported"
|
||||
AGENT_GENERATED = "agent_generated"
|
||||
|
||||
|
||||
class CalendarInviteContent(BaseModel):
|
||||
model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid")
|
||||
|
||||
type: Literal["invite"]
|
||||
permission: int = Field(..., description="权限: 1=view, 4=edit, 8=invite")
|
||||
action: Literal["pending"] = "pending"
|
||||
|
||||
|
||||
class CalendarUpdateContent(BaseModel):
|
||||
model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid")
|
||||
|
||||
type: Literal["update"]
|
||||
title: str = Field(..., description="事件标题")
|
||||
action: Literal["updated"] = "updated"
|
||||
|
||||
|
||||
class CalendarDeleteContent(BaseModel):
|
||||
model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid")
|
||||
|
||||
type: Literal["delete"]
|
||||
title: str = Field(..., description="事件标题")
|
||||
action: Literal["deleted"] = "deleted"
|
||||
|
||||
|
||||
CalendarContent = Union[
|
||||
CalendarInviteContent,
|
||||
CalendarUpdateContent,
|
||||
CalendarDeleteContent,
|
||||
]
|
||||
|
||||
|
||||
def parse_calendar_content(content: str | None) -> CalendarContent | None:
|
||||
if not content:
|
||||
return None
|
||||
try:
|
||||
data = json.loads(content)
|
||||
content_type = data.get("type")
|
||||
if content_type == "invite":
|
||||
return CalendarInviteContent(**data)
|
||||
if content_type == "update":
|
||||
return CalendarUpdateContent(**data)
|
||||
if content_type == "delete":
|
||||
return CalendarDeleteContent(**data)
|
||||
raise ValueError(f"Unknown calendar content type: {content_type}")
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from schemas.sessions.chat_session import SessionStateSnapshot
|
||||
|
||||
__all__ = ["SessionStateSnapshot"]
|
||||
@@ -0,0 +1,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import ClassVar
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
class SessionStateSnapshot(BaseModel):
|
||||
model_config: ClassVar[ConfigDict] = ConfigDict(extra="allow")
|
||||
|
||||
pass
|
||||
@@ -4,7 +4,7 @@ import re
|
||||
from typing import Literal
|
||||
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
||||
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
||||
|
||||
_BCP47_PATTERN = re.compile(r"^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$")
|
||||
_COUNTRY_PATTERN = re.compile(r"^[A-Z]{2}$")
|
||||
@@ -62,8 +62,11 @@ def upgrade_to_latest(settings: ProfileSettingsUnion) -> ProfileSettingsV1:
|
||||
|
||||
|
||||
class UserContext(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: str
|
||||
username: str
|
||||
email: str | None = None
|
||||
avatar_url: str | None = None
|
||||
bio: str | None = None
|
||||
settings: ProfileSettingsUnion | None = None
|
||||
|
||||
Reference in New Issue
Block a user