Commit dce41c59 by ccran

feat: add full text segment id support;

parent e047146d
...@@ -35,6 +35,7 @@ MAX_SINGLE_CHUNK_SIZE = 5000 ...@@ -35,6 +35,7 @@ MAX_SINGLE_CHUNK_SIZE = 5000
MERGE_RULE_PROMPT = False MERGE_RULE_PROMPT = False
META_KEY = "META" META_KEY = "META"
DEFAULT_RULESET_ID = "通用" DEFAULT_RULESET_ID = "通用"
FULL_TEXT_SEGMENT_ID = -1
## 规则集ID列表,需与rules.xlsx中的sheet名称保持一致!!! ## 规则集ID列表,需与rules.xlsx中的sheet名称保持一致!!!
ALL_RULESET_IDS = [ ALL_RULESET_IDS = [
"通用", "通用",
......
...@@ -18,6 +18,7 @@ from core.config import ( ...@@ -18,6 +18,7 @@ from core.config import (
doc_support_formats, doc_support_formats,
pdf_support_formats, pdf_support_formats,
MERGE_RULE_PROMPT, MERGE_RULE_PROMPT,
FULL_TEXT_SEGMENT_ID,
use_lufa, use_lufa,
max_model_len, max_model_len,
debug_mode, debug_mode,
...@@ -266,11 +267,18 @@ def summarize_facts(payload: SegmentSummaryRequest) -> SegmentSummaryResponse: ...@@ -266,11 +267,18 @@ def summarize_facts(payload: SegmentSummaryRequest) -> SegmentSummaryResponse:
class SegmentReviewRequest(BaseModel): class SegmentReviewRequest(BaseModel):
conversation_id: str conversation_id: str
segment_id: int segment_id: int = Field(
default=FULL_TEXT_SEGMENT_ID,
description="分段ID;为 FULL_TEXT_SEGMENT_ID 时使用 segment_text 审查全文",
)
segment_text: Optional[str] = Field(
default=None,
description="segment_id 为 FULL_TEXT_SEGMENT_ID 时用于审查的全文文本",
)
party_role: Optional[str] = "" party_role: Optional[str] = ""
ruleset_id: Optional[str] = "通用" ruleset_id: Optional[str] = "通用"
routed_rule_titles: Optional[List[str]] = None routed_rule_titles: Optional[List[str]] = None
file_ext: str file_ext: Optional[str] = None
context_memories: Optional[List[Dict]] = None context_memories: Optional[List[Dict]] = None
route_by: Literal["rule", "summary"] = Field( route_by: Literal["rule", "summary"] = Field(
default="rule", description="路由依据:rule=审查规则项,summary=摘要项" default="rule", description="路由依据:rule=审查规则项,summary=摘要项"
...@@ -294,9 +302,27 @@ class SegmentRuleRouterResponse(BaseModel): ...@@ -294,9 +302,27 @@ class SegmentRuleRouterResponse(BaseModel):
routed_rules: List[Dict] routed_rules: List[Dict]
@app.post("/segments/review/findings", response_model=SegmentReviewResponse) def _resolve_review_segment(payload: SegmentReviewRequest):
def review_segment(payload: SegmentReviewRequest) -> SegmentReviewResponse: if payload.segment_id == FULL_TEXT_SEGMENT_ID:
store = get_cached_memory(payload.conversation_id) segment_text = (payload.segment_text or "").strip()
if not segment_text:
raise HTTPException(
status_code=400,
detail=f"segment_text cannot be empty when segment_id is {FULL_TEXT_SEGMENT_ID}",
)
return FULL_TEXT_SEGMENT_ID, segment_text, None
if payload.segment_id < FULL_TEXT_SEGMENT_ID:
raise HTTPException(
status_code=400,
detail=f"segment_id must be {FULL_TEXT_SEGMENT_ID} or greater",
)
if not payload.file_ext:
raise HTTPException(
status_code=400,
detail=f"file_ext is required when segment_id is not {FULL_TEXT_SEGMENT_ID}",
)
try: try:
doc_obj, _ = get_cached_doc_tool(payload.conversation_id, payload.file_ext) doc_obj, _ = get_cached_doc_tool(payload.conversation_id, payload.file_ext)
except Exception as exc: except Exception as exc:
...@@ -312,6 +338,13 @@ def review_segment(payload: SegmentReviewRequest) -> SegmentReviewResponse: ...@@ -312,6 +338,13 @@ def review_segment(payload: SegmentReviewRequest) -> SegmentReviewResponse:
status_code=404, status_code=404,
detail=f"Segment text not found for id {payload.segment_id}: {exc}. Please parse document first.", detail=f"Segment text not found for id {payload.segment_id}: {exc}. Please parse document first.",
) )
return segment_idx, segment_text, doc_obj
@app.post("/segments/review/findings", response_model=SegmentReviewResponse)
def review_segment(payload: SegmentReviewRequest) -> SegmentReviewResponse:
store = get_cached_memory(payload.conversation_id)
segment_idx, segment_text, _ = _resolve_review_segment(payload)
ruleset_id = payload.ruleset_id or rules_reference_tool.default_ruleset_id ruleset_id = payload.ruleset_id or rules_reference_tool.default_ruleset_id
rules = rules_reference_tool.run( rules = rules_reference_tool.run(
...@@ -366,21 +399,7 @@ def review_segment(payload: SegmentReviewRequest) -> SegmentReviewResponse: ...@@ -366,21 +399,7 @@ def review_segment(payload: SegmentReviewRequest) -> SegmentReviewResponse:
@app.post("/segments/review/rule-router", response_model=SegmentRuleRouterResponse) @app.post("/segments/review/rule-router", response_model=SegmentRuleRouterResponse)
def route_segment_rules(payload: SegmentReviewRequest) -> SegmentRuleRouterResponse: def route_segment_rules(payload: SegmentReviewRequest) -> SegmentRuleRouterResponse:
try: segment_idx, segment_text, doc_obj = _resolve_review_segment(payload)
doc_obj, _ = get_cached_doc_tool(payload.conversation_id, payload.file_ext)
except Exception as exc:
raise HTTPException(
status_code=400, detail=f"Document tool not available: {exc}"
)
segment_idx = payload.segment_id - 1
try:
segment_text = doc_obj.get_chunk_item(segment_idx)
except Exception as exc:
raise HTTPException(
status_code=404,
detail=f"Segment text not found for id {payload.segment_id}: {exc}. Please parse document first.",
)
ruleset_id = payload.ruleset_id or rules_reference_tool.default_ruleset_id ruleset_id = payload.ruleset_id or rules_reference_tool.default_ruleset_id
rules = rules_reference_tool.run(ruleset_id=ruleset_id).get("rules", []) rules = rules_reference_tool.run(ruleset_id=ruleset_id).get("rules", [])
......
...@@ -4,7 +4,7 @@ import re ...@@ -4,7 +4,7 @@ import re
from thefuzz import fuzz from thefuzz import fuzz
from utils.doc_util import DocBase from utils.doc_util import DocBase
from utils.common_util import adjust_single_chunk_size from utils.common_util import adjust_single_chunk_size
from core.config import use_lufa from core.config import FULL_TEXT_SEGMENT_ID, use_lufa
import os import os
...@@ -553,6 +553,22 @@ class SpireWordDoc(DocBase): ...@@ -553,6 +553,22 @@ class SpireWordDoc(DocBase):
for loc in chunk_locations for loc in chunk_locations
] ]
def get_all_sub_chunks(self):
self._ensure_loaded()
sub_chunks = []
for chunk_id in range(len(self._chunk_list)):
sub_chunks.extend(self.get_sub_chunks(chunk_id))
return sub_chunks
def _get_comment_sub_chunks(self, chunk_id):
self._ensure_loaded()
if chunk_id == FULL_TEXT_SEGMENT_ID:
return self.get_all_sub_chunks()
if isinstance(chunk_id, int) and 0 <= chunk_id < self.get_chunk_num():
return self.get_sub_chunks(chunk_id)
logger.error(f"invalid chunk_id for comment: {chunk_id}")
return []
def format_comment_author(self, comment): def format_comment_author(self, comment):
return "{}|{}".format(str(comment["id"]), comment["key_points"]) return "{}|{}".format(str(comment["id"]), comment["key_points"])
...@@ -820,14 +836,15 @@ class SpireWordDoc(DocBase): ...@@ -820,14 +836,15 @@ class SpireWordDoc(DocBase):
for comment in comments: for comment in comments:
if comment.get("result") != "不合格": if comment.get("result") != "不合格":
continue continue
# update chunk_id comment_chunk_id = comment.get("chunk_id")
comment_chunk_id = comment.get("chunk_id", -1) # 优先使用 comment 中的 chunk_id;-1 表示全文;缺失或无效时回退到入参 chunk_id。
# 优先使用comments里提供的chunk_id,如果没有或无效则使用外部传入的chunk_id,如果都没有则异常处理 if isinstance(comment_chunk_id, int) and (
sub_chunks = ( comment_chunk_id == FULL_TEXT_SEGMENT_ID
self.get_sub_chunks(comment_chunk_id) or 0 <= comment_chunk_id < self.get_chunk_num()
if comment_chunk_id != -1 and comment_chunk_id < self.get_chunk_num() ):
else self.get_sub_chunks(chunk_id) sub_chunks = self._get_comment_sub_chunks(comment_chunk_id)
) else:
sub_chunks = self._get_comment_sub_chunks(chunk_id)
author = self.format_comment_author(comment) author = self.format_comment_author(comment)
suggest = comment.get("suggest", "") suggest = comment.get("suggest", "")
original_text = (comment.get("original_text") or "").strip() original_text = (comment.get("original_text") or "").strip()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment