2026-04-03 14:44:46 +08:00
|
|
|
|
"""HTTP / OpenAPI 模型。"""
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
from typing import Any
|
|
|
|
|
|
|
|
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class RegressionSetCreate(BaseModel):
|
|
|
|
|
|
name: str
|
|
|
|
|
|
description: str | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class RegressionSetOut(BaseModel):
|
|
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
|
|
|
|
|
|
id: str
|
|
|
|
|
|
name: str
|
|
|
|
|
|
description: str | None
|
|
|
|
|
|
created_at: datetime
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CaseCreate(BaseModel):
|
|
|
|
|
|
title: str | None = None
|
|
|
|
|
|
user_utterances: list[str]
|
|
|
|
|
|
source_conversation_id: str | None = None
|
|
|
|
|
|
source_user_id: str | None = None
|
|
|
|
|
|
reference_memoir_markdown: str | None = None
|
|
|
|
|
|
is_protected: bool = False
|
|
|
|
|
|
meta: dict[str, Any] | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CaseOut(BaseModel):
|
|
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
|
|
|
|
|
|
id: str
|
|
|
|
|
|
regression_set_id: str
|
|
|
|
|
|
source_conversation_id: str | None
|
|
|
|
|
|
source_user_id: str | None
|
|
|
|
|
|
title: str | None
|
|
|
|
|
|
user_utterances: list[Any]
|
|
|
|
|
|
is_protected: bool
|
|
|
|
|
|
created_at: datetime
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VersionCreate(BaseModel):
|
|
|
|
|
|
name: str
|
|
|
|
|
|
runner_kind: str = "llm_chat_v1"
|
|
|
|
|
|
config_json: dict[str, Any] | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VersionOut(BaseModel):
|
|
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
|
|
|
|
|
|
id: str
|
|
|
|
|
|
name: str
|
|
|
|
|
|
runner_kind: str
|
|
|
|
|
|
config_json: dict[str, Any] | None
|
|
|
|
|
|
created_at: datetime
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ExperimentCreate(BaseModel):
|
|
|
|
|
|
name: str
|
|
|
|
|
|
regression_set_id: str
|
|
|
|
|
|
baseline_version_id: str
|
|
|
|
|
|
candidate_version_id: str
|
|
|
|
|
|
rubric_pack: str = "conversation_v1+memoir_v1"
|
|
|
|
|
|
composite_weights_json: dict[str, Any] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
description='默认 {"conversation":0.5,"memoir":0.5}',
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ExperimentOut(BaseModel):
|
|
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
|
|
|
|
|
|
id: str
|
|
|
|
|
|
name: str
|
|
|
|
|
|
regression_set_id: str
|
|
|
|
|
|
baseline_version_id: str
|
|
|
|
|
|
candidate_version_id: str
|
|
|
|
|
|
rubric_pack: str
|
|
|
|
|
|
status: str
|
|
|
|
|
|
error_message: str | None
|
|
|
|
|
|
created_at: datetime
|
|
|
|
|
|
completed_at: datetime | None
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-06 13:45:04 +08:00
|
|
|
|
class SessionDialogueMessageOut(BaseModel):
|
|
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
|
|
|
|
|
|
role: str
|
|
|
|
|
|
content: str
|
|
|
|
|
|
created_at: datetime | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SessionDialogueOut(BaseModel):
|
|
|
|
|
|
conversation_id: str
|
|
|
|
|
|
messages: list[SessionDialogueMessageOut]
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-03 14:44:46 +08:00
|
|
|
|
class SessionListItem(BaseModel):
|
|
|
|
|
|
id: str
|
|
|
|
|
|
user_id: str
|
2026-04-06 13:45:04 +08:00
|
|
|
|
user_phone: str | None = Field(default=None, description="users.phone,列表展示用")
|
2026-04-03 14:44:46 +08:00
|
|
|
|
started_at: datetime | None
|
2026-04-06 13:45:04 +08:00
|
|
|
|
last_message_at: datetime | None = None
|
2026-04-03 14:44:46 +08:00
|
|
|
|
conversation_stage: str | None
|
|
|
|
|
|
current_topic: str | None
|
|
|
|
|
|
status: str | None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SessionListResponse(BaseModel):
|
|
|
|
|
|
items: list[SessionListItem]
|
|
|
|
|
|
total: int
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SessionTranscriptOut(BaseModel):
|
|
|
|
|
|
conversation_id: str
|
|
|
|
|
|
user_id: str
|
|
|
|
|
|
user_utterances_from_segments: list[str]
|
|
|
|
|
|
user_utterances_from_messages: list[str]
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-06 13:45:04 +08:00
|
|
|
|
class UserExportFixtureTurnOut(BaseModel):
|
|
|
|
|
|
user: str
|
|
|
|
|
|
ai: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UserExportFixtureListOut(BaseModel):
|
|
|
|
|
|
items: list[str]
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-06 23:19:20 +08:00
|
|
|
|
class MemoirSectionBaselineOut(BaseModel):
|
|
|
|
|
|
title: str
|
|
|
|
|
|
body: str
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-06 13:45:04 +08:00
|
|
|
|
class UserExportFixtureDetailOut(BaseModel):
|
|
|
|
|
|
filename: str
|
|
|
|
|
|
turns: list[UserExportFixtureTurnOut]
|
2026-04-06 23:19:20 +08:00
|
|
|
|
source_user_id: str | None = None
|
|
|
|
|
|
memoir_sections: list[MemoirSectionBaselineOut] = Field(default_factory=list)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ReplayBootstrapBody(BaseModel):
|
|
|
|
|
|
user_id: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ReplayBootstrapOut(BaseModel):
|
|
|
|
|
|
conversation_id: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class EvalSandboxOut(BaseModel):
|
|
|
|
|
|
"""内部评测专用:一次性临时账号 + 空白会话,不落真实手机号业务。"""
|
|
|
|
|
|
|
|
|
|
|
|
user_id: str
|
|
|
|
|
|
conversation_id: str
|
|
|
|
|
|
phone: str
|
|
|
|
|
|
nickname: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ReplayConversationBody(BaseModel):
|
|
|
|
|
|
conversation_id: str
|
|
|
|
|
|
fixture_filename: str | None = None
|
|
|
|
|
|
user_utterances: list[str] | None = None
|
|
|
|
|
|
flush_memoir_after: bool = True
|
|
|
|
|
|
skip_tts: bool = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ReplayConversationOut(BaseModel):
|
|
|
|
|
|
conversation_id: str
|
|
|
|
|
|
turns_replayed: int
|
|
|
|
|
|
utterances_echo: list[str] = Field(default_factory=list)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ManualJudgeConversationBody(BaseModel):
|
|
|
|
|
|
conversation_id: str
|
|
|
|
|
|
"""与当前评测台选中的 MD 一致,供基准 transcript / 整体打分。"""
|
|
|
|
|
|
fixture_filename: str | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ManualJudgeConversationStreamBody(BaseModel):
|
|
|
|
|
|
conversation_id: str
|
|
|
|
|
|
fixture_filename: str | None = None
|
2026-04-07 17:15:01 +08:00
|
|
|
|
include_turn_judges: bool = False
|
|
|
|
|
|
"""对当前会话逐轮调用评审 LLM(在整体分之后)。"""
|
|
|
|
|
|
include_baseline_turn_judges: bool = False
|
|
|
|
|
|
"""对导出基线逐轮调用评审 LLM(需 fixture + 整体基线分成功)。"""
|
2026-04-06 23:19:20 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ManualJudgeConversationOut(BaseModel):
|
|
|
|
|
|
conversation_id: str
|
|
|
|
|
|
fixture_filename: str | None = None
|
|
|
|
|
|
baseline_transcript: str = ""
|
|
|
|
|
|
replay_transcript: str
|
|
|
|
|
|
baseline_judge: dict[str, Any] | None = None
|
|
|
|
|
|
replay_judge: dict[str, Any] | None = None
|
|
|
|
|
|
errors: list[str] = Field(default_factory=list)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ManualJudgeMemoirBody(BaseModel):
|
|
|
|
|
|
user_id: str
|
|
|
|
|
|
baseline_sections: list[MemoirSectionBaselineOut] | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ManualJudgeMemoirOut(BaseModel):
|
|
|
|
|
|
user_id: str
|
|
|
|
|
|
chapter_results: list[dict[str, Any]] = Field(default_factory=list)
|
|
|
|
|
|
story_results: list[dict[str, Any]] = Field(default_factory=list)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MemoirChapterSnapOut(BaseModel):
|
|
|
|
|
|
id: str
|
|
|
|
|
|
title: str
|
|
|
|
|
|
category: str | None = None
|
|
|
|
|
|
order_index: int | None = None
|
|
|
|
|
|
canonical_markdown: str | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MemoirStorySnapOut(BaseModel):
|
|
|
|
|
|
id: str
|
|
|
|
|
|
title: str
|
|
|
|
|
|
stage: str | None = None
|
|
|
|
|
|
canonical_markdown: str | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UserMemoirSnapshotOut(BaseModel):
|
|
|
|
|
|
user_id: str
|
|
|
|
|
|
chapters: list[MemoirChapterSnapOut]
|
|
|
|
|
|
stories: list[MemoirStorySnapOut]
|
2026-04-06 13:45:04 +08:00
|
|
|
|
|
|
|
|
|
|
|
2026-04-03 14:44:46 +08:00
|
|
|
|
class SnapshotFromConversationBody(BaseModel):
|
|
|
|
|
|
title: str | None = None
|
|
|
|
|
|
use_messages: bool = False
|
|
|
|
|
|
is_protected: bool = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ImportMarkdownBody(BaseModel):
|
|
|
|
|
|
markdown: str
|
|
|
|
|
|
title: str | None = None
|
|
|
|
|
|
is_protected: bool = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ImportJsonCaseBody(BaseModel):
|
|
|
|
|
|
regression_set_id: str
|
|
|
|
|
|
utterances: list[str] | None = None
|
|
|
|
|
|
raw_json: dict[str, Any] | list[Any] | None = Field(
|
|
|
|
|
|
default=None,
|
|
|
|
|
|
description="与 utterances 二选一:对象含 utterances 键或根数组",
|
|
|
|
|
|
)
|
|
|
|
|
|
title: str | None = None
|
|
|
|
|
|
is_protected: bool = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class RunTurnOut(BaseModel):
|
|
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
|
|
|
|
|
|
id: str
|
|
|
|
|
|
turn_index: int
|
|
|
|
|
|
user_utterance: str
|
|
|
|
|
|
assistant_reply: str | None
|
|
|
|
|
|
duration_ms: int | None
|
|
|
|
|
|
judge_scores_json: dict[str, Any] | None
|
|
|
|
|
|
judge_rationale: str | None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class EvalRunOut(BaseModel):
|
|
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
|
|
|
|
|
|
id: str
|
|
|
|
|
|
experiment_id: str
|
|
|
|
|
|
case_id: str
|
|
|
|
|
|
side: str
|
|
|
|
|
|
status: str
|
|
|
|
|
|
error_message: str | None
|
|
|
|
|
|
memoir_markdown: str | None
|
|
|
|
|
|
conversation_score_total: float | None
|
|
|
|
|
|
memoir_score_total: float | None
|
|
|
|
|
|
composite_score: float | None
|
2026-04-06 13:45:04 +08:00
|
|
|
|
judge_bundle_json: dict[str, Any] | None = None
|
2026-04-03 14:44:46 +08:00
|
|
|
|
turns: list[RunTurnOut] = []
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-06 13:45:04 +08:00
|
|
|
|
class SessionEvalRunItem(BaseModel):
|
|
|
|
|
|
experiment_name: str
|
|
|
|
|
|
run: EvalRunOut
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SessionEvalRunsOut(BaseModel):
|
|
|
|
|
|
conversation_id: str
|
|
|
|
|
|
items: list[SessionEvalRunItem]
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-03 14:44:46 +08:00
|
|
|
|
class GateVerdictOut(BaseModel):
|
|
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
|
|
|
|
|
|
passed: bool
|
|
|
|
|
|
mean_composite_delta: float | None
|
|
|
|
|
|
protected_regressions_json: list[dict[str, Any]] | None
|
|
|
|
|
|
details_json: dict[str, Any] | None
|
|
|
|
|
|
computed_at: datetime
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ExperimentDetailOut(BaseModel):
|
|
|
|
|
|
experiment: ExperimentOut
|
|
|
|
|
|
runs: list[EvalRunOut]
|
|
|
|
|
|
gate: GateVerdictOut | None
|