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:
qzl
2026-03-13 14:10:13 +08:00
parent fb3c649db7
commit a10a2db27a
100 changed files with 6333 additions and 4800 deletions
+19 -4
View File
@@ -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",
]
+36 -38
View File
@@ -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
-28
View File
@@ -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)
-33
View File
@@ -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
-10
View File
@@ -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)
-13
View File
@@ -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
+273 -305
View File
@@ -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)
+545
View File
@@ -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()
+66
View File
@@ -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
+11
View File
@@ -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
+3
View File
@@ -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
+5 -3
View File
@@ -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 -50
View File
@@ -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
+3
View File
@@ -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 -1
View File
@@ -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