from typing import Any from pydantic import BaseModel, ConfigDict, Field class SessionConfig(BaseModel): model: str | None = Field(default=None, min_length=1) temperature: float | None = Field(default=None, ge=0, le=2) max_tokens: int | None = Field(default=None, ge=1) model_config = ConfigDict(extra="forbid") class ErrorEnvelope(BaseModel): code: str message: str details: dict[str, Any] | None = None model_config = ConfigDict(extra="forbid") class AskRequest(BaseModel): session_id: str = Field(min_length=1) question: str = Field(min_length=1, max_length=100000) context: str = Field(default="") session_config: SessionConfig | None = None metadata: dict[str, Any] | None = None model_config = ConfigDict(extra="forbid") class AskResponse(BaseModel): session_id: str answer: str skill_count: int = Field(ge=0) model_config = ConfigDict(extra="forbid") class SampleItem(BaseModel): question: str = Field(min_length=1) context: str = Field(default="") ground_truth: str | None = Field(default=None) metadata: dict[str, Any] | None = None model_config = ConfigDict(extra="forbid") class LearnSampleRequest(BaseModel): session_id: str = Field(min_length=1) samples: list[SampleItem] = Field(min_length=1, max_length=25) epochs: int = Field(default=1, ge=1, le=20) session_config: SessionConfig | None = None model_config = ConfigDict(extra="forbid") class LearnSampleResponse(BaseModel): session_id: str processed: int = Field(ge=0) failed: int = Field(default=0, ge=0) skill_count_before: int = Field(ge=0) skill_count_after: int = Field(ge=0) new_skill_count: int = Field(ge=0) model_config = ConfigDict(extra="forbid") class LearnFeedbackRequest(BaseModel): session_id: str = Field(min_length=1) question: str = Field(min_length=1) answer: str = Field(min_length=1) feedback: str = Field(min_length=1) context: str = Field(default="") ground_truth: str | None = Field(default=None) session_config: SessionConfig | None = None model_config = ConfigDict(extra="forbid") class LearnFeedbackResponse(BaseModel): session_id: str learned: bool skill_count_before: int = Field(ge=0) skill_count_after: int = Field(ge=0) new_skill_count: int = Field(ge=0) model_config = ConfigDict(extra="forbid") class SkillbookGetRequest(BaseModel): session_id: str = Field(min_length=1) limit: int = Field(default=20, ge=1, le=200) include_invalid: bool = Field(default=False) model_config = ConfigDict(extra="forbid") class SkillItem(BaseModel): id: str content: str topic: str | None = None helpful: int | None = None harmful: int | None = None neutral: int | None = None model_config = ConfigDict(extra="allow") class SkillbookGetResponse(BaseModel): session_id: str stats: dict[str, Any] skills: list[SkillItem] model_config = ConfigDict(extra="forbid") class SkillbookSaveRequest(BaseModel): session_id: str = Field(min_length=1) path: str = Field(min_length=1) model_config = ConfigDict(extra="forbid") class SkillbookSaveResponse(BaseModel): session_id: str path: str saved_skill_count: int = Field(ge=0) model_config = ConfigDict(extra="forbid") class SkillbookLoadRequest(BaseModel): session_id: str = Field(min_length=1) path: str = Field(min_length=1) model_config = ConfigDict(extra="forbid") class SkillbookLoadResponse(BaseModel): session_id: str path: str skill_count: int = Field(ge=0) model_config = ConfigDict(extra="forbid")