Add production condition retrieval smoke test
This commit is contained in:
@@ -16,6 +16,11 @@ interface RagCitation {
|
||||
source_crop?: string | null;
|
||||
attachment?: string | null;
|
||||
evidence_text?: string | null;
|
||||
drug_id?: string | null;
|
||||
drug_name?: string | null;
|
||||
section_key?: string | null;
|
||||
section_title?: string | null;
|
||||
source_document?: string | null;
|
||||
}
|
||||
|
||||
interface RagResponse {
|
||||
@@ -33,6 +38,12 @@ interface RagResponse {
|
||||
claims: Array<{ text: string; source_ids: string[] }>;
|
||||
}>;
|
||||
answer_mode?: "concise" | "normal" | "detailed";
|
||||
/**
|
||||
* Fixed notice set by `rag/answer.py`, never written by the model. Optional
|
||||
* here only so an older ai-service build still parses; the fallback below
|
||||
* keeps the guarantee that a message always carries one.
|
||||
*/
|
||||
disclaimer?: string;
|
||||
answer_plan?: {
|
||||
verbosity: "concise" | "normal" | "detailed";
|
||||
layout: string;
|
||||
@@ -40,6 +51,14 @@ interface RagResponse {
|
||||
show_heading: boolean;
|
||||
needs_warning: boolean;
|
||||
} | null;
|
||||
candidate_assessments?: Array<{
|
||||
drug_id: string;
|
||||
drug_name: string;
|
||||
indication_supported: boolean;
|
||||
status: string;
|
||||
indication_source_ids: string[];
|
||||
safety_source_ids: string[];
|
||||
}>;
|
||||
}
|
||||
|
||||
function toAnswerBlocks(raw: NonNullable<RagResponse["blocks"]>): AnswerBlock[] {
|
||||
@@ -119,6 +138,8 @@ const REFUSALS: Record<string, string> = {
|
||||
"Hệ thống phát hiện một phần câu trả lời không có trích dẫn nguồn rõ ràng nên đã huỷ để tránh sai sót. Vui lòng thử lại.",
|
||||
unsupported_claim:
|
||||
"Dược thư có nội dung liên quan đến câu hỏi này, nhưng bước đối chiếu lại chưa xác nhận được câu trả lời khớp hoàn toàn với nguồn. Vui lòng thử lại.",
|
||||
unsupported_drug:
|
||||
"Câu trả lời vừa tạo có tên thuốc ngoài tập ứng viên được Dược thư hỗ trợ nên đã bị huỷ để tránh gợi ý không có bằng chứng.",
|
||||
incomplete_answer:
|
||||
"Câu trả lời vừa tạo đã bị huỷ vì bước đối chiếu phát hiện còn bỏ sót dữ kiện liên quan trong nguồn. Vui lòng thử lại để hệ thống tạo câu trả lời đầy đủ hơn.",
|
||||
// Kept as the fallback `answer.py` itself falls back to when, for some
|
||||
@@ -142,6 +163,14 @@ const REFUSALS: Record<string, string> = {
|
||||
const GENERIC_REFUSAL =
|
||||
"Hệ thống không thể xử lý câu hỏi này lúc này. Vui lòng thử lại.";
|
||||
|
||||
// Mirrors `DISCLAIMER` in `apps/ai-service/rag/answer.py`. ai-service is the
|
||||
// source of truth and normally supplies it; this exists so a version skew
|
||||
// between the two services cannot produce a medical message with no notice
|
||||
// attached. If the wording changes, change it there first.
|
||||
const FALLBACK_DISCLAIMER =
|
||||
"Nội dung được trích từ Dược thư Quốc gia Việt Nam 2018, phục vụ tra cứu " +
|
||||
"chuyên môn và không thay thế chỉ định của bác sĩ hoặc dược sĩ lâm sàng.";
|
||||
|
||||
// Chunk ids are always `{drug_id}__{section_key}__{part_index}` — drug_id and
|
||||
// section_key use single underscores internally, so splitting on the double
|
||||
// underscore reliably recovers both per citation. This must be derived per
|
||||
@@ -168,12 +197,16 @@ function toCitations(raw: RagCitation[]): Citation[] {
|
||||
return Array.from(byChunk.entries()).map(([chunkId, group]) => {
|
||||
const primary = group.find((g) => !g.attachment) ?? group[0];
|
||||
const attachmentRef = group.find((g) => g.attachment);
|
||||
const [drugSlug, sectionKey] = chunkId.split("__");
|
||||
const [drugSlug, chunkSectionKey] = chunkId.split("__");
|
||||
const sectionKey = primary.section_key ?? chunkSectionKey;
|
||||
const isQuarantined = Boolean(attachmentRef);
|
||||
return {
|
||||
chunkId,
|
||||
drugName: drugSlug ? drugSlug.replace(/_/g, " ").toUpperCase() : chunkId,
|
||||
drugName:
|
||||
primary.drug_name ??
|
||||
(drugSlug ? drugSlug.replace(/_/g, " ").toUpperCase() : chunkId),
|
||||
sectionType: sectionKey ?? "",
|
||||
sourceDocument: primary.source_document ?? "Dược thư Quốc gia Việt Nam 2018",
|
||||
sourcePageRange: [primary.printed_page_start, primary.printed_page_end],
|
||||
physicalPage: primary.physical_page,
|
||||
snippet: primary.evidence_text ?? "",
|
||||
@@ -309,6 +342,19 @@ export async function POST(request: Request) {
|
||||
: undefined,
|
||||
answerMode: rag.answer_mode,
|
||||
answerPlan: rag.answer_plan ? toAnswerPlan(rag.answer_plan) : undefined,
|
||||
candidateAssessments: rag.candidate_assessments?.map((item) => ({
|
||||
drugId: item.drug_id,
|
||||
drugName: item.drug_name,
|
||||
indicationSupported: item.indication_supported,
|
||||
status: item.status,
|
||||
indicationSourceIds: item.indication_source_ids,
|
||||
safetySourceIds: item.safety_source_ids,
|
||||
})),
|
||||
// Carried on every message, including abstains and clarifications: those
|
||||
// are clinical responses too. The local fallback covers an ai-service
|
||||
// that predates the field, so the guarantee does not depend on both
|
||||
// sides being deployed together.
|
||||
disclaimer: rag.disclaimer?.trim() || FALLBACK_DISCLAIMER,
|
||||
};
|
||||
|
||||
const responseHeaders = new Headers({
|
||||
|
||||
Reference in New Issue
Block a user