fix(application-form-fill): 新附件先汇总再填表
This commit is contained in:
@@ -10,7 +10,7 @@ from django.utils import timezone
|
|||||||
from .file_summary.skills.attachment_reader import AttachmentReaderSkill
|
from .file_summary.skills.attachment_reader import AttachmentReaderSkill
|
||||||
from .file_summary.workflow import create_file_summary_batch, start_file_summary_workflow
|
from .file_summary.workflow import create_file_summary_batch, start_file_summary_workflow
|
||||||
from .llm import LLMConfigurationError, LLMRequestError, generate_reply, stream_reply
|
from .llm import LLMConfigurationError, LLMRequestError, generate_reply, stream_reply
|
||||||
from .models import Conversation, FileAttachment, FileSummaryBatch, Message
|
from .models import Conversation, FileAttachment, FileSummaryBatch, FileSummaryBatchAttachment, Message
|
||||||
from .application_form_fill.workflow import (
|
from .application_form_fill.workflow import (
|
||||||
create_application_form_fill_batch,
|
create_application_form_fill_batch,
|
||||||
find_latest_successful_summary_batch as find_latest_successful_form_fill_summary_batch,
|
find_latest_successful_summary_batch as find_latest_successful_form_fill_summary_batch,
|
||||||
@@ -231,6 +231,8 @@ def stream_message(conversation: Conversation, content: str):
|
|||||||
|
|
||||||
if route.starts_application_form_fill:
|
if route.starts_application_form_fill:
|
||||||
source_summary_batch = find_latest_successful_form_fill_summary_batch(conversation)
|
source_summary_batch = find_latest_successful_form_fill_summary_batch(conversation)
|
||||||
|
if source_summary_batch and not _summary_covers_active_attachments(conversation, source_summary_batch):
|
||||||
|
source_summary_batch = None
|
||||||
if not source_summary_batch:
|
if not source_summary_batch:
|
||||||
if not _has_active_attachments(conversation):
|
if not _has_active_attachments(conversation):
|
||||||
reply_content = "请先在当前对话右侧上传需要填表的产品资料或压缩包,我会先自动汇总再继续生成申报模板。"
|
reply_content = "请先在当前对话右侧上传需要填表的产品资料或压缩包,我会先自动汇总再继续生成申报模板。"
|
||||||
@@ -480,6 +482,20 @@ def _has_active_attachments(conversation: Conversation) -> bool:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _summary_covers_active_attachments(conversation: Conversation, batch: FileSummaryBatch) -> bool:
|
||||||
|
active_ids = set(
|
||||||
|
FileAttachment.objects.filter(conversation=conversation, is_active=True)
|
||||||
|
.exclude(upload_status=FileAttachment.UploadStatus.DELETED)
|
||||||
|
.values_list("id", flat=True)
|
||||||
|
)
|
||||||
|
if not active_ids:
|
||||||
|
return True
|
||||||
|
bound_ids = set(
|
||||||
|
FileSummaryBatchAttachment.objects.filter(batch=batch).values_list("attachment_id", flat=True)
|
||||||
|
)
|
||||||
|
return active_ids.issubset(bound_ids)
|
||||||
|
|
||||||
|
|
||||||
def _format_attachment_reader_reply(attachments: list[dict[str, object]], message: str) -> str:
|
def _format_attachment_reader_reply(attachments: list[dict[str, object]], message: str) -> str:
|
||||||
if not attachments:
|
if not attachments:
|
||||||
return message or "当前对话没有可读取的附件。"
|
return message or "当前对话没有可读取的附件。"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from review_agent.models import (
|
|||||||
Conversation,
|
Conversation,
|
||||||
FileAttachment,
|
FileAttachment,
|
||||||
FileSummaryBatch,
|
FileSummaryBatch,
|
||||||
|
FileSummaryBatchAttachment,
|
||||||
Message,
|
Message,
|
||||||
WorkflowEvent,
|
WorkflowEvent,
|
||||||
WorkflowNodeRun,
|
WorkflowNodeRun,
|
||||||
@@ -270,3 +271,63 @@ def test_stream_message_auto_runs_summary_before_application_form_fill(
|
|||||||
assert "汇总完成后继续自动填表" in joined
|
assert "汇总完成后继续自动填表" in joined
|
||||||
assert FileSummaryBatch.objects.filter(conversation=conversation, status=FileSummaryBatch.Status.SUCCESS).exists()
|
assert FileSummaryBatch.objects.filter(conversation=conversation, status=FileSummaryBatch.Status.SUCCESS).exists()
|
||||||
assert ApplicationFormFillBatch.objects.filter(conversation=conversation).exists()
|
assert ApplicationFormFillBatch.objects.filter(conversation=conversation).exists()
|
||||||
|
|
||||||
|
|
||||||
|
def test_stream_message_reruns_summary_when_new_attachment_not_in_latest_batch(
|
||||||
|
monkeypatch, settings, tmp_path, django_user_model
|
||||||
|
):
|
||||||
|
settings.MEDIA_ROOT = tmp_path
|
||||||
|
settings.APPLICATION_FORM_FILL_ASYNC = False
|
||||||
|
user = django_user_model.objects.create_user(username="owner", password="pass")
|
||||||
|
conversation = Conversation.objects.create(user=user, title="会话")
|
||||||
|
old_path = tmp_path / "old.txt"
|
||||||
|
old_path.write_text("旧资料", encoding="utf-8")
|
||||||
|
old_attachment = FileAttachment.objects.create(
|
||||||
|
conversation=conversation,
|
||||||
|
user=user,
|
||||||
|
original_name="旧资料.txt",
|
||||||
|
storage_path=str(old_path),
|
||||||
|
file_size=old_path.stat().st_size,
|
||||||
|
is_active=True,
|
||||||
|
)
|
||||||
|
old_summary = FileSummaryBatch.objects.create(
|
||||||
|
conversation=conversation,
|
||||||
|
user=user,
|
||||||
|
batch_no="FS-OLD",
|
||||||
|
status=FileSummaryBatch.Status.SUCCESS,
|
||||||
|
)
|
||||||
|
FileSummaryBatchAttachment.objects.create(batch=old_summary, attachment=old_attachment)
|
||||||
|
new_path = tmp_path / "ifu.txt"
|
||||||
|
new_path.write_text("【产品名称】\n新型冠状病毒2019-nCoV核酸检测试剂盒(荧光PCR法)", encoding="utf-8")
|
||||||
|
FileAttachment.objects.create(
|
||||||
|
conversation=conversation,
|
||||||
|
user=user,
|
||||||
|
original_name="目标产品说明书.docx",
|
||||||
|
storage_path=str(new_path),
|
||||||
|
file_size=new_path.stat().st_size,
|
||||||
|
is_active=True,
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"review_agent.services.route_message_intent",
|
||||||
|
lambda conversation, content: SkillRoute(
|
||||||
|
action="application_form_fill",
|
||||||
|
workflow_type="application_form_fill",
|
||||||
|
confidence=0.9,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def finish_summary(batch, async_run=True):
|
||||||
|
batch.status = FileSummaryBatch.Status.SUCCESS
|
||||||
|
batch.save(update_fields=["status"])
|
||||||
|
|
||||||
|
monkeypatch.setattr("review_agent.services.start_file_summary_workflow", finish_summary)
|
||||||
|
|
||||||
|
frames = list(stream_message(conversation, "请基于当前对话最近成功汇总的产品资料,自动提取产品关键信息并填入申报文件模板"))
|
||||||
|
joined = "".join(frames)
|
||||||
|
|
||||||
|
assert '"workflow_type": "file_summary"' in joined
|
||||||
|
assert "汇总完成后继续自动填表" in joined
|
||||||
|
latest_summary = FileSummaryBatch.objects.order_by("-id").first()
|
||||||
|
form_batch = ApplicationFormFillBatch.objects.get(conversation=conversation)
|
||||||
|
assert latest_summary != old_summary
|
||||||
|
assert form_batch.source_summary_batch == latest_summary
|
||||||
|
|||||||
Reference in New Issue
Block a user