schemas.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. # -*- coding: utf-8 -*-
  2. """Schemas for the document chat module."""
  3. from typing import Any, Dict, List, Literal, Optional
  4. from pydantic import BaseModel, Field
  5. class SelectedSection(BaseModel):
  6. index: str = Field(default="", description="Section index, for example 2.1")
  7. title: str = Field(default="", description="Section title")
  8. content: str = Field(default="", description="Current section content from the editor")
  9. code: str = Field(default="", description="Section code")
  10. chapter_level_1: str = Field(default="", description="Optional primary chapter classification for retrieval")
  11. chapter_level_2: str = Field(default="", description="Optional secondary chapter classification for retrieval")
  12. class DocumentContext(BaseModel):
  13. before: str = Field(default="", description="Previous context snippet")
  14. after: str = Field(default="", description="Following context snippet")
  15. siblings: List[Dict[str, Any]] = Field(default_factory=list)
  16. references: List[Dict[str, Any]] = Field(default_factory=list)
  17. retrieval_filters: Dict[str, Any] = Field(default_factory=dict)
  18. class DocumentChatRequest(BaseModel):
  19. user_id: Optional[str] = None
  20. message: str = Field(..., min_length=1, description="User message")
  21. selected_section: Optional[SelectedSection] = Field(default=None, description="Selected section; null or empty for general questions")
  22. conversation_id: Optional[str] = None
  23. task_id: Optional[str] = None
  24. project_info: Dict[str, Any] = Field(default_factory=dict)
  25. document_context: DocumentContext = Field(default_factory=DocumentContext)
  26. conversation_history: List[Dict[str, Any]] = Field(default_factory=list)
  27. response_mode: Literal["json", "sse"] = "json"
  28. class Config:
  29. extra = "forbid"
  30. class IntentResult(BaseModel):
  31. intent: Literal["document_modify", "document_answer", "clarify", "unsupported"]
  32. confidence: float = Field(default=0.0, ge=0.0, le=1.0)
  33. skill_name: Optional[str] = None
  34. operation: str = ""
  35. target_scope: str = "selected_section"
  36. normalized_instruction: str = ""
  37. needs_clarification: bool = False
  38. clarification_question: str = ""
  39. reason: str = ""
  40. warnings: List[str] = Field(default_factory=list)
  41. class DocumentChatSkillInput(BaseModel):
  42. user_id: Optional[str] = None
  43. user_message: str
  44. selected_section: Optional[SelectedSection] = None
  45. intent_result: IntentResult
  46. conversation_id: Optional[str] = None
  47. task_id: Optional[str] = None
  48. project_info: Dict[str, Any] = Field(default_factory=dict)
  49. document_context: DocumentContext = Field(default_factory=DocumentContext)
  50. conversation_history: List[Dict[str, Any]] = Field(default_factory=list)
  51. class DocumentChatSkillOutput(BaseModel):
  52. skill_name: str
  53. response_type: Literal["answer", "proposal", "clarify", "unsupported", "general_answer", "error"]
  54. answer: Optional[str] = None
  55. old_content: Optional[str] = None
  56. proposed_content: Optional[str] = None
  57. change_summary: List[str] = Field(default_factory=list)
  58. references: List[Dict[str, Any]] = Field(default_factory=list)
  59. warnings: List[str] = Field(default_factory=list)
  60. class DiffItem(BaseModel):
  61. type: Literal["equal", "insert", "delete", "replace", "full_content"]
  62. old_text: str = ""
  63. new_text: str = ""
  64. class DiffResult(BaseModel):
  65. old_content_hash: str
  66. new_content_hash: str
  67. diff: List[DiffItem] = Field(default_factory=list)
  68. diff_granularity: Literal["line", "full_content"] = "line"
  69. class DocumentChatData(BaseModel):
  70. callback_task_id: str
  71. response_type: Literal["answer", "proposal", "clarify", "unsupported", "general_answer", "error"]
  72. intent_result: Optional[Dict[str, Any]] = None
  73. answer: Optional[str] = None
  74. proposed_content: Optional[str] = None
  75. old_content_hash: Optional[str] = None
  76. new_content_hash: Optional[str] = None
  77. diff: List[Dict[str, Any]] = Field(default_factory=list)
  78. diff_granularity: Optional[str] = None
  79. change_summary: List[str] = Field(default_factory=list)
  80. references: List[Dict[str, Any]] = Field(default_factory=list)
  81. retrieval_status: Optional[str] = None
  82. retrieval_metrics: Dict[str, Any] = Field(default_factory=dict)
  83. warnings: List[str] = Field(default_factory=list)
  84. selected_section: Dict[str, Any] = Field(default_factory=dict)
  85. error_message: Optional[str] = None
  86. class DocumentChatResponse(BaseModel):
  87. code: int
  88. message: str
  89. data: Optional[DocumentChatData] = None
  90. def model_to_dict(value: Any) -> Dict[str, Any]:
  91. """Return a dict for Pydantic v1/v2 models."""
  92. if value is None:
  93. return {}
  94. if isinstance(value, dict):
  95. return value
  96. if hasattr(value, "model_dump"):
  97. return value.model_dump()
  98. if hasattr(value, "dict"):
  99. return value.dict()
  100. return dict(value)