diff --git a/messages/de.json b/messages/de.json index 102a530a2..7d8918b71 100644 --- a/messages/de.json +++ b/messages/de.json @@ -11779,6 +11779,7 @@ "errorPdfNeedsVision": "Zum Scannen von PDFs wird ein Claude-Vision-Anbieter benötigt.", "errorFileType": "Dieser Dateityp kann nicht gelesen werden.", "errorNotIndexed": "Lies das Dokument zuerst aus, dann extrahiere.", + "errorOutputLimit": "Der KI-Anbieter hat sein Ausgabelimit erreicht, bevor er einen einzigen Wert geliefert hat. Versuche ein Modell, das längere Antworten erlaubt.", "errorLocalOcrDisabled": "Aktiviere zuerst die lokale Texterkennung in den KI-Einstellungen.", "errorProvider": "Dafür ist kein KI-Anbieter eingerichtet.", "errorTextImageOnly": "Lokale Texterkennung liest nur Bilder, nicht diese Datei.", @@ -11839,6 +11840,7 @@ "extracting": "Liest aus…", "extractHint": "Nutzt den bereits ausgelesenen Text dieses Dokuments. Ausgelesene Werte warten auf deine Prüfung; nichts wird automatisch gespeichert.", "extractEmpty": "Aus diesem Dokument konnten keine Werte ausgelesen werden.", + "extractIncomplete": "Ein Teil des Dokuments konnte nicht vollständig gelesen werden, deshalb können Werte fehlen. Vergleiche die Liste mit dem Dokument.", "failUnitRequired": "Ein Zahlenwert braucht eine Einheit, bevor er gespeichert werden kann.", "failUnitMismatch": "Die angegebene Einheit passt nicht zur gespeicherten Einheit dieses Markers. Passe sie an und schließe die Prüfung erneut ab.", "failGeneric": "Dieser Wert konnte nicht gespeichert werden.", diff --git a/messages/en.json b/messages/en.json index da5a32d20..eafaa47f1 100644 --- a/messages/en.json +++ b/messages/en.json @@ -11779,6 +11779,7 @@ "errorPdfNeedsVision": "Scanning PDFs needs a Claude vision provider.", "errorFileType": "This file type can't be read.", "errorNotIndexed": "Read the document first, then extract.", + "errorOutputLimit": "The AI provider reached its output limit before it returned a single value. Try a model that allows longer answers.", "errorLocalOcrDisabled": "Turn on local OCR in the AI settings first.", "errorProvider": "No AI provider is set up for this.", "errorTextImageOnly": "Local OCR reads images only, not this file.", @@ -11839,6 +11840,7 @@ "extracting": "Extracting…", "extractHint": "Uses the text already read from this document. Extracted values wait for your review; nothing is saved automatically.", "extractEmpty": "No values could be extracted from this document.", + "extractIncomplete": "Part of the document could not be read in full, so some values may be missing. Check the list against the document.", "failUnitRequired": "A numeric value needs a unit before it can be saved.", "failUnitMismatch": "The stated unit does not match the saved unit for this marker. Adjust it, then finish the review again.", "failGeneric": "This value could not be saved.", diff --git a/messages/es.json b/messages/es.json index 4e55bf123..b42bb66a5 100644 --- a/messages/es.json +++ b/messages/es.json @@ -11779,6 +11779,7 @@ "errorPdfNeedsVision": "Escanear PDF requiere un proveedor de visión Claude.", "errorFileType": "Este tipo de archivo no se puede leer.", "errorNotIndexed": "Primero lee el documento y luego extrae.", + "errorOutputLimit": "El proveedor de IA alcanzó su límite de salida antes de devolver un solo valor. Prueba con un modelo que permita respuestas más largas.", "errorLocalOcrDisabled": "Activa primero el OCR local en los ajustes de IA.", "errorProvider": "No hay ningún proveedor de IA configurado para esto.", "errorTextImageOnly": "El OCR local solo lee imágenes, no este archivo.", @@ -11839,6 +11840,7 @@ "extracting": "Extrayendo…", "extractHint": "Usa el texto ya leído de este documento. Los valores extraídos esperan tu revisión; nada se guarda automáticamente.", "extractEmpty": "No se pudieron extraer valores de este documento.", + "extractIncomplete": "Una parte del documento no se pudo leer por completo, así que pueden faltar valores. Compara la lista con el documento.", "failUnitRequired": "Un valor numérico necesita una unidad antes de poder guardarse.", "failUnitMismatch": "La unidad indicada no coincide con la unidad guardada de este marcador. Ajústala y termina la revisión de nuevo.", "failGeneric": "Este valor no se pudo guardar.", diff --git a/messages/fr.json b/messages/fr.json index 0d1b503e9..b6e43ded2 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -11779,6 +11779,7 @@ "errorPdfNeedsVision": "La lecture des PDF nécessite un fournisseur de vision Claude.", "errorFileType": "Ce type de fichier ne peut pas être lu.", "errorNotIndexed": "Lisez d’abord le document, puis extrayez.", + "errorOutputLimit": "Le fournisseur d’IA a atteint sa limite de sortie avant de renvoyer la moindre valeur. Essayez un modèle qui accepte des réponses plus longues.", "errorLocalOcrDisabled": "Activez d'abord l'OCR local dans les paramètres d'IA.", "errorProvider": "Aucun fournisseur d'IA n'est configuré pour cela.", "errorTextImageOnly": "L'OCR local ne lit que les images, pas ce fichier.", @@ -11839,6 +11840,7 @@ "extracting": "Extraction…", "extractHint": "Utilise le texte déjà lu de ce document. Les valeurs extraites attendent votre vérification ; rien n’est enregistré automatiquement.", "extractEmpty": "Aucune valeur n’a pu être extraite de ce document.", + "extractIncomplete": "Une partie du document n’a pas pu être lue entièrement, des valeurs peuvent donc manquer. Comparez la liste avec le document.", "failUnitRequired": "Une valeur numérique a besoin d’une unité avant de pouvoir être enregistrée.", "failUnitMismatch": "L’unité indiquée ne correspond pas à l’unité enregistrée pour ce marqueur. Ajustez-la, puis terminez à nouveau la vérification.", "failGeneric": "Cette valeur n’a pas pu être enregistrée.", diff --git a/messages/it.json b/messages/it.json index 8067e9aa9..d8ef6d7a3 100644 --- a/messages/it.json +++ b/messages/it.json @@ -11779,6 +11779,7 @@ "errorPdfNeedsVision": "La scansione dei PDF richiede un provider di visione Claude.", "errorFileType": "Questo tipo di file non può essere letto.", "errorNotIndexed": "Leggi prima il documento, poi estrai.", + "errorOutputLimit": "Il fornitore di IA ha raggiunto il suo limite di output prima di restituire un solo valore. Prova con un modello che consente risposte più lunghe.", "errorLocalOcrDisabled": "Attiva prima l'OCR locale nelle impostazioni IA.", "errorProvider": "Nessun provider IA è configurato per questo.", "errorTextImageOnly": "L'OCR locale legge solo immagini, non questo file.", @@ -11839,6 +11840,7 @@ "extracting": "Estrazione…", "extractHint": "Usa il testo già letto da questo documento. I valori estratti attendono la tua revisione; nulla viene salvato automaticamente.", "extractEmpty": "Non è stato possibile estrarre valori da questo documento.", + "extractIncomplete": "Una parte del documento non è stata letta per intero, quindi possono mancare dei valori. Confronta l'elenco con il documento.", "failUnitRequired": "Un valore numerico ha bisogno di un’unità prima di poter essere salvato.", "failUnitMismatch": "L’unità indicata non corrisponde all’unità salvata per questo marcatore. Correggila e concludi di nuovo la revisione.", "failGeneric": "Questo valore non è stato salvato.", diff --git a/messages/ko.json b/messages/ko.json index 842d0ab87..4c2a81deb 100644 --- a/messages/ko.json +++ b/messages/ko.json @@ -11779,6 +11779,7 @@ "errorPdfNeedsVision": "PDF를 읽으려면 Claude 비전 제공자가 필요해요.", "errorFileType": "이 파일 형식은 읽을 수 없어요.", "errorNotIndexed": "문서를 먼저 읽은 뒤에 추출해 주세요.", + "errorOutputLimit": "AI 제공자가 값을 하나도 돌려주기 전에 출력 한도에 도달했어요. 더 긴 답변을 허용하는 모델로 다시 시도해 주세요.", "errorLocalOcrDisabled": "AI 설정에서 로컬 OCR을 먼저 켜 주세요.", "errorProvider": "이 작업에 쓸 AI 제공자가 설정돼 있지 않아요.", "errorTextImageOnly": "로컬 OCR은 이미지만 읽어요. 이 파일은 읽을 수 없어요.", @@ -11839,6 +11840,7 @@ "extracting": "추출 중…", "extractHint": "이 문서에서 이미 읽은 텍스트를 사용해요. 추출한 값은 검토를 기다리며, 자동으로 저장되지 않아요.", "extractEmpty": "이 문서에서 추출할 수 있는 값이 없었어요.", + "extractIncomplete": "문서 일부를 끝까지 읽지 못해서 빠진 값이 있을 수 있어요. 목록을 문서와 비교해 주세요.", "failUnitRequired": "숫자 값을 저장하려면 단위가 필요해요.", "failUnitMismatch": "기재된 단위가 이 지표에 저장된 단위와 달라요. 수정한 뒤 검토를 다시 완료해 주세요.", "failGeneric": "이 값을 저장하지 못했어요.", diff --git a/messages/nl.json b/messages/nl.json index 44163c4a2..6afe594a6 100644 --- a/messages/nl.json +++ b/messages/nl.json @@ -11779,6 +11779,7 @@ "errorPdfNeedsVision": "Voor het scannen van PDF's is een Claude-provider met beeldherkenning nodig.", "errorFileType": "Dit bestandstype kan niet worden gelezen.", "errorNotIndexed": "Laat het document eerst lezen en extraheer daarna.", + "errorOutputLimit": "De AI-provider bereikte zijn uitvoerlimiet voordat hij één waarde teruggaf. Probeer een model dat langere antwoorden toestaat.", "errorLocalOcrDisabled": "Zet eerst lokale OCR aan in de AI-instellingen.", "errorProvider": "Hiervoor is geen AI-provider ingesteld.", "errorTextImageOnly": "Lokale OCR leest alleen afbeeldingen, niet dit bestand.", @@ -11839,6 +11840,7 @@ "extracting": "Extraheren…", "extractHint": "Gebruikt de tekst die al uit dit document is gelezen. Geëxtraheerde waarden wachten op jouw controle; er wordt niets automatisch opgeslagen.", "extractEmpty": "Er konden geen waarden uit dit document worden geëxtraheerd.", + "extractIncomplete": "Een deel van het document kon niet volledig worden gelezen, dus er kunnen waarden ontbreken. Vergelijk de lijst met het document.", "failUnitRequired": "Een numerieke waarde heeft een eenheid nodig voordat ze kan worden opgeslagen.", "failUnitMismatch": "De opgegeven eenheid komt niet overeen met de opgeslagen eenheid voor deze biomarker. Pas de eenheid aan en rond de controle daarna opnieuw af.", "failGeneric": "Deze waarde kon niet worden opgeslagen.", diff --git a/messages/pl.json b/messages/pl.json index 5b215e72f..d4685f56a 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -11779,6 +11779,7 @@ "errorPdfNeedsVision": "Skanowanie plików PDF wymaga dostawcy wizji Claude.", "errorFileType": "Tego typu pliku nie można odczytać.", "errorNotIndexed": "Najpierw odczytaj dokument, potem wyodrębnij.", + "errorOutputLimit": "Dostawca AI osiągnął limit odpowiedzi, zanim zwrócił choć jedną wartość. Spróbuj modelu, który pozwala na dłuższe odpowiedzi.", "errorLocalOcrDisabled": "Najpierw włącz lokalne OCR w ustawieniach AI.", "errorProvider": "Nie skonfigurowano dostawcy AI do tego celu.", "errorTextImageOnly": "Lokalne OCR odczytuje tylko obrazy, nie ten plik.", @@ -11839,6 +11840,7 @@ "extracting": "Odczytywanie…", "extractHint": "Korzysta z tekstu już odczytanego z tego dokumentu. Odczytane wartości czekają na Twoją weryfikację; nic nie zapisuje się automatycznie.", "extractEmpty": "Z tego dokumentu nie udało się odczytać żadnych wartości.", + "extractIncomplete": "Części dokumentu nie udało się odczytać w całości, więc może brakować niektórych wartości. Porównaj listę z dokumentem.", "failUnitRequired": "Wartość liczbowa potrzebuje jednostki, zanim będzie można ją zapisać.", "failUnitMismatch": "Podana jednostka nie zgadza się z zapisaną jednostką tego wskaźnika. Popraw ją i zakończ przegląd ponownie.", "failGeneric": "Nie udało się zapisać tej wartości.", diff --git a/messages/pt-BR.json b/messages/pt-BR.json index 60977cca7..5f3533071 100644 --- a/messages/pt-BR.json +++ b/messages/pt-BR.json @@ -11779,6 +11779,7 @@ "errorPdfNeedsVision": "Digitalizar PDFs precisa de um provedor de visão Claude.", "errorFileType": "Este tipo de arquivo não pode ser lido.", "errorNotIndexed": "Leia o documento primeiro, depois extraia.", + "errorOutputLimit": "O provedor de IA atingiu o limite de saída antes de devolver um único valor. Tente um modelo que permita respostas mais longas.", "errorLocalOcrDisabled": "Ative o OCR local nas configurações de IA primeiro.", "errorProvider": "Nenhum provedor de IA está configurado para isso.", "errorTextImageOnly": "O OCR local lê apenas imagens, não este arquivo.", @@ -11839,6 +11840,7 @@ "extracting": "Extraindo…", "extractHint": "Usa o texto já lido deste documento. Valores extraídos aguardam sua revisão; nada é salvo automaticamente.", "extractEmpty": "Nenhum valor pôde ser extraído deste documento.", + "extractIncomplete": "Parte do documento não pôde ser lida por completo, então podem faltar valores. Confira a lista com o documento.", "failUnitRequired": "Um valor numérico precisa de uma unidade antes de poder ser salvo.", "failUnitMismatch": "A unidade informada não corresponde à unidade salva para este marcador. Ajuste-a e conclua a revisão novamente.", "failGeneric": "Este valor não pôde ser salvo.", diff --git a/src/components/documents/__tests__/document-ai-error-key.test.ts b/src/components/documents/__tests__/document-ai-error-key.test.ts index 190dbd7ae..cc14b09c3 100644 --- a/src/components/documents/__tests__/document-ai-error-key.test.ts +++ b/src/components/documents/__tests__/document-ai-error-key.test.ts @@ -40,4 +40,10 @@ describe("documentAiErrorKey", () => { "documents.assist.errorGeneric", ); }); + + it("says the output limit stopped the read, not that the copy was unreadable", () => { + expect( + documentAiErrorKey(refusal("documents.inbound.outputLimit", 422)), + ).toBe("documents.assist.errorOutputLimit"); + }); }); diff --git a/src/components/documents/document-facts-review.tsx b/src/components/documents/document-facts-review.tsx index 692e2b58a..8af50c678 100644 --- a/src/components/documents/document-facts-review.tsx +++ b/src/components/documents/document-facts-review.tsx @@ -40,6 +40,7 @@ import { Label } from "@/components/ui/label"; import { MarkerMatchField } from "@/components/labs/marker-match-field"; import type { BiomarkerListResponse } from "@/components/labs/types"; import { apiGet } from "@/lib/api/api-fetch"; +import type { DocumentExtractRunResult } from "@/lib/documents/ai-runs/types"; import { useTranslations } from "@/lib/i18n/context"; import { queryKeys } from "@/lib/query-keys"; import { cn } from "@/lib/utils"; @@ -473,6 +474,25 @@ function FactRow({ * or nothing. Owner-only: staging and confirming write to the caller's own * record, so a delegate never sees these controls. */ +/** + * What the person is told when a stored extract finishes. The staged facts + * arrive with the document refetch the hook starts; the review opens onto + * them. A long document read in parts may have had a part cut at the + * provider's output limit or not reached, and then the review is not the + * whole document. + */ +function announceExtract( + result: DocumentExtractRunResult, + t: (key: string) => string, + openReview: () => void, +): void { + if (result.factsStaged > 0) openReview(); + else toast.info(t("documents.review.extractEmpty")); + if (result.incomplete) { + toast.warning(t("documents.review.extractIncomplete")); + } +} + export function DocumentFactsSection({ doc, canManage, @@ -680,13 +700,7 @@ export function DocumentFactsSection({ onSuccess: (result) => { setFailures({}); setIncluded({}); - // The staged facts arrive with the document refetch the - // hook starts; the review opens onto them. - if (result.factsStaged > 0) { - setOpen(true); - } else { - toast.info(t("documents.review.extractEmpty")); - } + announceExtract(result, t, () => setOpen(true)); }, onError: (error) => showErrorToast(aiErrorText(error), { cause: error }), diff --git a/src/components/documents/use-document-assist.ts b/src/components/documents/use-document-assist.ts index 0c26ecdc0..c47fb646f 100644 --- a/src/components/documents/use-document-assist.ts +++ b/src/components/documents/use-document-assist.ts @@ -222,6 +222,8 @@ export function documentAiErrorKey(err: unknown): string { return "documents.assist.errorProvider"; case "documents.inbound.notIndexed": return "documents.assist.errorNotIndexed"; + case "documents.inbound.outputLimit": + return "documents.assist.errorOutputLimit"; case "aiRuns.workerUnavailable": return "aiRuns.errorWorkerUnavailable"; case "aiRuns.timedOut":