File size: 3,911 Bytes
06671aa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
from __future__ import annotations

from typing import Any, Literal, Optional

from pydantic import BaseModel, Field


RiskLevel = Literal["critical", "high", "medium", "low", "info"]
ApplyStatus = Literal["applied", "not_applied", "error"]
AnchorMode = Literal["paragraph", "exact"]


class PlaybookSummary(BaseModel):
    id: str
    name: str
    description: str
    source_path: str
    updated_at: Optional[str] = None
    is_editable: bool = False


class PlaybookDetail(PlaybookSummary):
    yaml_text: str


class ParagraphText(BaseModel):
    id: str
    text: str
    native_para_id: str | None = None


class BodyTextInvariant(BaseModel):
    paragraphs: list[ParagraphText]
    text_hash: str


class ReviewFinding(BaseModel):
    id: str
    model_block_id: str = ""
    comment_key: str = ""
    risk_level: RiskLevel
    clause_type: str
    issue: str
    comment: str
    review_reasoning: dict
    target_text: str = ""
    anchor_mode: AnchorMode = "paragraph"
    start_paragraph_id: str
    end_paragraph_id: str
    start_anchor: str
    end_anchor: str
    before_anchor: str
    after_anchor: str
    confidence: float = Field(ge=0, le=1)
    stable_id: str = ""
    native_para_id: str | None = None
    text_hash: str = ""
    xml_path: str = ""
    op_type: Literal["COMMENT", "REWRITE_PARAGRAPH", "INSERT", "DELETE_PARAGRAPH"] = "COMMENT"
    new_text: Optional[str] = None
    insert_text: Optional[str] = None
    position: Optional[Literal["before", "after"]] = None
    rewrite_mode: Optional[Literal["paragraph", "span"]] = None
    old_span: Optional[str] = None
    new_span: Optional[str] = None
    bookmark_name: Optional[str] = None
    section_heading: Optional[str] = None


class ReviewResponse(BaseModel):
    schema_version: Literal["word_review_v1"] = "word_review_v1"
    workflow_id: str
    findings: list[ReviewFinding]
    model: Optional[str] = None
    usage: Optional[dict[str, Any]] = None


ResolveStrategy = Literal[
    "exact", "whitespace_normalized", "anchor_normalized",
    "sentence_fallback", "paragraph_fallback", "editable_subspan",
    "paragraph_anchor", "not_resolved",
]


class ApplyResult(BaseModel):
    id: str
    status: ApplyStatus
    message: str
    paragraph_id: Optional[str] = None
    match_count: int = 0
    failure_code: Optional[str] = None
    model_block_id: Optional[str] = None
    writer_paragraph_id: Optional[str] = None
    native_para_id: Optional[str] = None
    stable_id: Optional[str] = None
    xml_path: Optional[str] = None
    comment_key: Optional[str] = None
    expected_text_hash: Optional[str] = None
    actual_text_hash: Optional[str] = None
    original_target_text: Optional[str] = None
    resolved_target_text: Optional[str] = None
    resolve_strategy: Optional[ResolveStrategy] = None
    resolve_confidence: Optional[float] = None


class RunSummary(BaseModel):
    run_id: str
    created_at: str
    created_short: str
    document_name: str
    commented_docx_name: Optional[str] = None
    playbook_id: str
    playbook_name: str
    status: str
    finding_count: int
    applied_count: int
    model: Optional[str] = None


class RunDetail(RunSummary):
    document_text: str
    playbook_text: str
    result: ReviewResponse
    apply_report: list[ApplyResult]
    usage: Optional[dict[str, Any]] = None
    commented_docx_available: bool = False


class ChatMessage(BaseModel):
    id: str
    role: Literal["user", "assistant"]
    content: str
    created_at: str
    document_name: Optional[str] = None


class ChatConversationSummary(BaseModel):
    conversation_id: str
    title: str
    created_at: str
    updated_at: str


class ChatConversation(BaseModel):
    conversation_id: str
    title: str
    created_at: str
    updated_at: str
    messages: list[ChatMessage] = Field(default_factory=list)


class ChatConversationDetail(ChatConversation):
    owner_username: str