Fix ai-service Dockerfile: bake in drug_entities.json, override its path

This commit is contained in:
2026-08-10 10:35:13 +07:00
parent a4b8e1c4db
commit 60b4397032
51 changed files with 4302 additions and 2087 deletions
+36 -11
View File
@@ -20,7 +20,7 @@ import { cn } from "@duoc-thu/ui";
interface ChatPanelProps {
sessionId: string;
initialQuery?: string;
onCitationClick?: (citation: Citation, index: number) => void;
onCitationClick?: (citation: Citation, index: number, allCitations: Citation[]) => void;
onCitationsLoaded?: (citations: Citation[]) => void;
activeCitationIndex?: number | null;
className?: string;
@@ -63,6 +63,7 @@ export function ChatPanel({
const [error, setError] = useState<string | null>(null);
const messagesEndRef = useRef<HTMLDivElement>(null);
const abortControllerRef = useRef<AbortController | null>(null);
const initialQuerySentRef = useRef<string | undefined>(undefined);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
@@ -132,7 +133,15 @@ export function ChatPanel({
};
useEffect(() => {
if (initialQuery) {
// Guard against firing twice for the same query: React 18 Strict Mode
// (dev only) runs this effect setup twice on mount, and with no guard
// that sent every quick-prompt click as two identical live requests
// (found live 2026-08-07: duplicate "Chỉ định & Tác dụng không mong
// muốn của Aspirin" turns in the trace). The ref persists across the
// Strict Mode replay, so the second invocation for the same
// `initialQuery` is a no-op; a genuinely new query still sends once.
if (initialQuery && initialQuerySentRef.current !== initialQuery) {
initialQuerySentRef.current = initialQuery;
handleSendMessage(initialQuery);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -291,15 +300,31 @@ export function ChatPanel({
{messages.length === 0 ? (
renderEmptyState()
) : (
messages.map((msg) => (
<ChatBubble
key={msg.id}
message={msg}
onCitationClick={(citation, idx) => onCitationClick?.(citation, idx)}
activeCitationIndex={activeCitationIndex}
onRetry={() => handleSendMessage(msg.content)}
/>
))
messages.map((msg, msgIdx) => {
// Retry must resend the ORIGINAL user question, not this
// bubble's own text — for an assistant bubble, `msg.content` is
// the answer/error text itself, so resending it fed the error
// message back in as if it were the next question (found live
// 2026-08-07: a trace row where the query text WAS literally
// "Dịch vụ đang gặp sự cố tạm thời..."). Walk back to the
// nearest preceding user turn instead.
const retryQuery =
msg.role === "assistant"
? [...messages.slice(0, msgIdx)].reverse().find((m) => m.role === "user")?.content
: undefined;
return (
<ChatBubble
key={msg.id}
message={msg}
onCitationClick={(citation, idx, allCitations) =>
onCitationClick?.(citation, idx, allCitations)
}
activeCitationIndex={activeCitationIndex}
onRetry={retryQuery ? () => handleSendMessage(retryQuery) : undefined}
onQuickReply={(text) => handleSendMessage(text)}
/>
);
})
)}
{/* Loading Indicator */}