"""Chat-related web schemas.""" from __future__ import annotations from typing import Any try: from pydantic import BaseModel, Field except ModuleNotFoundError: # pragma: no cover - fallback for skeleton-only environments class BaseModel: """Very small fallback shim used only so imports work without pydantic.""" def __init__(self, **kwargs: Any) -> None: annotations = getattr(self.__class__, "__annotations__", {}) for name in annotations: default = getattr(self.__class__, name, None) if name in kwargs: value = kwargs[name] else: value = default setattr(self, name, value) def model_dump(self, *, exclude_none: bool = False) -> dict[str, Any]: data = dict(self.__dict__) if exclude_none: data = {key: value for key, value in data.items() if value is not None} return data def Field(default: Any = None, **kwargs: Any) -> Any: default_factory = kwargs.get("default_factory") if default_factory is not None: return default_factory() return default class WebProviderTarget(BaseModel): """Web-facing provider target shape. 先保持和 runtime 里的 `ProviderTarget` 接近,但只暴露 Web 当前需要的字段。 后面如果 provider 层扩字段,再由这里显式补齐。 """ provider: str | None = None model: str | None = None api_key: str | None = None api_base: str | None = None extra_headers: dict[str, str] | None = None class WebChatRequest(BaseModel): """最小正式 chat 请求结构。""" message: str = Field(min_length=1) session_id: str | None = None user_id: str | None = None title: str | None = None execution_context: str | None = None model: str | None = None provider_name: str | None = None embedding_model: str | None = None temperature: float | None = None max_tokens: int | None = None thinking_enabled: bool | None = None max_tool_iterations: int | None = None fallback_target: WebProviderTarget | None = None auxiliary_target: WebProviderTarget | None = None embedding_target: WebProviderTarget | None = None reply_to_scheduled_run_id: str | None = None scheduled_reply_intent: str | None = None class WebChatResponse(BaseModel): """最小正式 chat 响应结构。""" session_id: str run_id: str output_text: str finish_reason: str tool_iterations: int provider_name: str | None = None model: str | None = None usage: dict[str, Any] = Field(default_factory=dict) task_id: str | None = None task_status: str | None = None evidence_status: str | None = None acceptance_state: str | None = None validation_result: dict[str, Any] | None = None class WebChatAcceptanceRequest(BaseModel): """User acceptance on the latest assistant result in chat.""" session_id: str run_id: str acceptance_type: str comment: str | None = None class WebChatAcceptanceResponse(BaseModel): """Acceptance recording result.""" session_id: str run_id: str task_id: str task_status: str acceptance_type: str feedback_type: str learning_candidates: list[dict[str, Any]] = Field(default_factory=list) class WebChatFeedbackRequest(BaseModel): """Backward-compatible feedback payload.""" session_id: str run_id: str feedback_type: str comment: str | None = None class WebChatFeedbackResponse(WebChatAcceptanceResponse): """Backward-compatible feedback response.""" class WebProviderConfigRequest(BaseModel): """Provider config update from the status page.""" enabled: bool = True model: str | None = None api_key: str | None = None api_base: str | None = None request_timeout_seconds: float | None = None class WebProviderConfigResponse(BaseModel): """Provider config update result.""" ok: bool provider: str enabled: bool class WebChannelConfigRequest(BaseModel): """Channel config update from the settings page.""" enabled: bool = False kind: str mode: str account_id: str | None = None display_name: str | None = None config: dict[str, Any] = Field(default_factory=dict) secrets: dict[str, str | None] = Field(default_factory=dict) class WebChannelConfigResponse(BaseModel): """Channel config update result.""" ok: bool channel_id: str restart_required: bool channel: dict[str, Any] class WebChannelConnectionCreateRequest(BaseModel): """Create a channel connection from the setup UI.""" kind: str mode: str display_name: str | None = Field(default=None, alias="displayName") owner_user_id: str | None = Field(default=None, alias="ownerUserId") auth_type: str = Field(default="token", alias="authType") account_id: str | None = Field(default=None, alias="accountId") config: dict[str, Any] = Field(default_factory=dict) secrets: dict[str, str | None] = Field(default_factory=dict) class WebChannelConnectionResponse(BaseModel): """Channel connection response with redacted credentials.""" connection: dict[str, Any] credentials: dict[str, str] = Field(default_factory=dict) class WebChannelConnectionUpdateRequest(BaseModel): """Update editable channel connection setup fields.""" display_name: str | None = Field(default=None, alias="displayName") account_id: str | None = Field(default=None, alias="accountId") config: dict[str, Any] | None = None secrets: dict[str, str | None] | None = None class WebChannelValidationResponse(BaseModel): """Connector validation response.""" ok: bool status: str account_id: str | None = None display_name: str | None = None error: str | None = None metadata: dict[str, Any] = Field(default_factory=dict) connection: dict[str, Any] class WebConnectorBridgeEventRequest(BaseModel): """Inbound connector bridge event from the external sidecar.""" event_id: str = Field(alias="eventId") timestamp: str delivery_attempt: int = Field(default=1, alias="deliveryAttempt") connection_id: str = Field(alias="connectionId") channel_id: str = Field(alias="channelId") kind: str account_id: str = Field(alias="accountId") peer_id: str = Field(alias="peerId") peer_type: str = Field(default="unknown", alias="peerType") user_id: str | None = Field(default=None, alias="userId") thread_id: str | None = Field(default=None, alias="threadId") message_id: str = Field(alias="messageId") message_type: str = Field(default="text", alias="messageType") content: str metadata: dict[str, Any] = Field(default_factory=dict) class WebConnectorBridgeEventResponse(BaseModel): """Connector bridge event accept/dedupe response.""" accepted: bool duplicate: bool = False pending: bool = False retry_after_seconds: int | None = Field(default=None, alias="retryAfterSeconds") class WebConnectorSessionCreateRequest(BaseModel): """Start a connector-managed onboarding session.""" kind: str display_name: str | None = Field(default=None, alias="displayName") owner_user_id: str | None = Field(default=None, alias="ownerUserId") options: dict[str, Any] = Field(default_factory=dict) class WebConnectorSessionResponse(BaseModel): """Connector session view plus optional connection view.""" session: dict[str, Any] connection: dict[str, Any] | None = None class WebAgentConfigRequest(BaseModel): """Agent runtime defaults update from the settings page.""" max_tokens: int | None = None temperature: float max_tool_iterations: int class WebAgentConfigResponse(BaseModel): """Agent runtime defaults update result.""" ok: bool class WebStatusResponse(BaseModel): """Web 宿主层状态响应。""" status: str running: bool mode: str class WebErrorResponse(BaseModel): """统一错误响应结构。""" detail: str