@precisa-saude/fhir-ocr-utils 0.25.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -55,4 +55,263 @@ declare function findBiomarkersInText(ocrText: string): AnchorResult;
55
55
  */
56
56
  declare function getMatchedCodes(result: AnchorResult): string[];
57
57
 
58
- export { type AnchorMatch, type AnchorResult, CONFIDENCE_AMBIGUOUS, CONFIDENCE_NAME_ONLY, CONFIDENCE_VALUE_ADJACENT, findBiomarkersInText, getMatchedCodes };
58
+ /**
59
+ * Contrato de saída para extração de laudo por modelo.
60
+ *
61
+ * Este é o **contrato de interoperabilidade**, e não um prompt. Ele descreve a
62
+ * forma do JSON que qualquer modelo precisa devolver para o resto do toolkit
63
+ * conseguir conferir e converter o resultado. Não diz como pedir isso ao
64
+ * modelo, não traz instrução de comportamento e não depende de fornecedor:
65
+ * quem usa liga do jeito que a plataforma dele permitir (saída estruturada,
66
+ * gramática, tool use ou simples prompt com validação por cima).
67
+ *
68
+ * As descrições são deliberadamente neutras. Regra de comportamento ("nunca
69
+ * infira", "copie literalmente") é ajuste de prompt, muda de modelo para
70
+ * modelo e não pertence a um contrato público.
71
+ *
72
+ * As descrições são as únicas strings em inglês do pacote, e isso é
73
+ * deliberado: o schema é contrato de integração lido por quem consome de fora
74
+ * do Brasil, e uma descrição em pt-BR não ajuda ninguém em Colônia ou Madri.
75
+ * O resto da documentação segue a regra do ecossistema.
76
+ *
77
+ * O campo `sourceText` existe porque é o que torna a conferência possível:
78
+ * sem o trecho que originou o valor não dá para auditar a extração depois.
79
+ *
80
+ * Campo que aceita mais de um tipo usa `anyOf`, e não `type: [...]`. As duas
81
+ * formas são JSON Schema válido, mas decodificador restrito não engole a
82
+ * segunda: o LM Studio recusa a geração com `'type' must be a string`. Como o
83
+ * ponto do contrato é servir a qualquer modelo, vale a forma mais aceita.
84
+ */
85
+ declare const LAB_EXTRACTION_SCHEMA: {
86
+ readonly $id: "https://fhir-brasil.dev.br/schemas/lab-extraction.json";
87
+ readonly $schema: "https://json-schema.org/draft/2020-12/schema";
88
+ readonly additionalProperties: false;
89
+ readonly properties: {
90
+ readonly biomarkers: {
91
+ readonly description: "The measurements read from the report.";
92
+ readonly items: {
93
+ readonly additionalProperties: false;
94
+ readonly properties: {
95
+ readonly confidence: {
96
+ readonly description: "Confidence in this reading, from 0 to 1.";
97
+ readonly maximum: 1;
98
+ readonly minimum: 0;
99
+ readonly type: "number";
100
+ };
101
+ readonly loinc: {
102
+ readonly anyOf: readonly [{
103
+ readonly type: "string";
104
+ }, {
105
+ readonly type: "null";
106
+ }];
107
+ readonly description: "A LOINC code from the allowed list, or null when none of them applies.";
108
+ };
109
+ readonly name: {
110
+ readonly description: "The measurement name as the report prints it.";
111
+ readonly type: "string";
112
+ };
113
+ readonly referenceMax: {
114
+ readonly anyOf: readonly [{
115
+ readonly type: "number";
116
+ }, {
117
+ readonly type: "null";
118
+ }];
119
+ readonly description: "Upper bound of the range printed on the report, or null.";
120
+ };
121
+ readonly referenceMin: {
122
+ readonly anyOf: readonly [{
123
+ readonly type: "number";
124
+ }, {
125
+ readonly type: "null";
126
+ }];
127
+ readonly description: "Lower bound of the range printed on the report, or null.";
128
+ };
129
+ readonly sourceText: {
130
+ readonly description: "The snippet of the report carrying this measurement and its value.";
131
+ readonly type: "string";
132
+ };
133
+ readonly unit: {
134
+ readonly description: "Unit as the report prints it. Empty string when there is none.";
135
+ readonly type: "string";
136
+ };
137
+ readonly value: {
138
+ readonly anyOf: readonly [{
139
+ readonly type: "number";
140
+ }, {
141
+ readonly type: "string";
142
+ }];
143
+ readonly description: "Numeric value, or text for a qualitative result.";
144
+ };
145
+ };
146
+ readonly required: readonly ["name", "value", "unit", "sourceText", "confidence"];
147
+ readonly type: "object";
148
+ };
149
+ readonly type: "array";
150
+ };
151
+ };
152
+ readonly required: readonly ["biomarkers"];
153
+ readonly title: "Laboratory report extraction";
154
+ readonly type: "object";
155
+ };
156
+ /** Uma grandeza como o modelo devolve, antes de qualquer conferência. */
157
+ interface ExtractedBiomarker {
158
+ confidence: number;
159
+ loinc?: string | null;
160
+ name: string;
161
+ referenceMax?: number | null;
162
+ referenceMin?: number | null;
163
+ sourceText: string;
164
+ unit: string;
165
+ value: number | string;
166
+ }
167
+ /** O objeto inteiro que o modelo devolve. */
168
+ interface ExtractionPayload {
169
+ biomarkers: ExtractedBiomarker[];
170
+ }
171
+
172
+ /**
173
+ * Converte grandezas já conferidas no envelope que o `fhir-bio convert` come.
174
+ *
175
+ * O laudo e o paciente não vêm do modelo: o contrato de extração cobre só as
176
+ * grandezas. Os dois saem daqui com valores sintéticos e óbvios, na mesma
177
+ * linha do `fhir-rnds-sandbox`, para a demo rodar de ponta a ponta sem inventar
178
+ * identidade de ninguém. Quem integra de verdade troca os dois pelo que já tem.
179
+ */
180
+ interface LabResultEnvelope {
181
+ observations: {
182
+ biomarkerCode: string;
183
+ biomarkerName: string;
184
+ flag: 'H' | 'L' | '';
185
+ referenceMax?: number;
186
+ referenceMin?: number;
187
+ reportId: string;
188
+ unit: string;
189
+ value: number | string;
190
+ }[];
191
+ profile: {
192
+ name: string;
193
+ userId: string;
194
+ };
195
+ report: {
196
+ collectionDate: string;
197
+ createdAt: string;
198
+ overallStatus: 'ANORMAL' | 'NORMAL';
199
+ processingStatus: 'complete';
200
+ reportId: string;
201
+ userId: string;
202
+ };
203
+ }
204
+ interface ToLabResultOptions {
205
+ collectionDate?: string;
206
+ reportId?: string;
207
+ userId?: string;
208
+ }
209
+ declare function extractionToLabResult(biomarkers: ExtractedBiomarker[], options?: ToLabResultOptions): LabResultEnvelope;
210
+
211
+ /**
212
+ * Conferência da saída do modelo contra o contrato e contra a ancoragem.
213
+ *
214
+ * São duas checagens, e as duas são determinísticas:
215
+ *
216
+ * 1. **Forma.** O objeto bate com `LAB_EXTRACTION_SCHEMA`. Modelo que devolve
217
+ * texto solto, campo faltando ou tipo errado é recusado aqui, o que deixa
218
+ * a qualidade do modelo virar problema de cobertura e nunca de correção.
219
+ * 2. **Ancoragem.** O código veio da lista que a varredura liberou. Código que
220
+ * o laudo não mencionou é descartado, que é a falha cara: um valor
221
+ * plausível pendurado num exame que não estava na página.
222
+ *
223
+ * A validação de citação, a correção de código contra nome impresso e a
224
+ * política de confiança não moram aqui.
225
+ *
226
+ * Sem dependência de runtime além do `@precisa-saude/fhir`: a checagem de
227
+ * forma é escrita à mão porque o schema é pequeno e o pacote não carrega
228
+ * validador de JSON Schema.
229
+ */
230
+ /** Por que uma grandeza foi recusada. */
231
+ type RejectionReason = 'not-anchored' | 'schema';
232
+ interface RejectedBiomarker {
233
+ /** Mensagem legível, já em pt-BR, dizendo o que falhou. */
234
+ detail: string;
235
+ /** O que o modelo devolveu, sem alteração, para o consumidor poder logar. */
236
+ raw: unknown;
237
+ reason: RejectionReason;
238
+ }
239
+ interface ExtractionValidationResult {
240
+ accepted: ExtractedBiomarker[];
241
+ /** Erros do objeto inteiro, quando nem dá para chegar nas grandezas. */
242
+ errors: string[];
243
+ rejected: RejectedBiomarker[];
244
+ /** `true` quando o objeto tem forma válida, mesmo que toda grandeza caia. */
245
+ valid: boolean;
246
+ }
247
+ interface ValidateExtractionOptions {
248
+ /**
249
+ * Resultado da ancoragem sobre o mesmo texto que foi ao modelo. Sem ele a
250
+ * checagem de ancoragem não roda e só a forma é conferida, que é um modo
251
+ * deliberadamente mais fraco: serve para inspecionar saída de modelo sem o
252
+ * laudo em mãos.
253
+ */
254
+ anchors?: AnchorResult;
255
+ }
256
+ /**
257
+ * Confere a saída de um modelo contra o contrato e, quando a ancoragem é
258
+ * fornecida, contra a lista de códigos que a varredura liberou.
259
+ */
260
+ declare function validateExtraction(raw: unknown, options?: ValidateExtractionOptions): ExtractionValidationResult;
261
+ /** Só a lista de grandezas aprovadas, para quem não quer o relatório inteiro. */
262
+ declare function acceptedBiomarkers(raw: unknown, options?: ValidateExtractionOptions): ExtractedBiomarker[];
263
+
264
+ /**
265
+ * Cliente mínimo para endpoint compatível com OpenAI.
266
+ *
267
+ * Isto é **conveniência, não contrato**. O contrato é o
268
+ * `LAB_EXTRACTION_SCHEMA`, e o toolkit funciona inteiro sem esta função: quem
269
+ * integra chama o próprio modelo do jeito que a plataforma dele permitir e
270
+ * entrega o JSON ao `validateExtraction`. Esta função existe para a demo rodar
271
+ * de uma ponta à outra sem um `curl` no meio.
272
+ *
273
+ * `/v1/chat/completions` é o que praticamente todo mundo fala: LM Studio,
274
+ * Ollama, llama.cpp, vLLM, OpenRouter, OpenAI, e a Anthropic pelo endpoint de
275
+ * compatibilidade. Por isso não há SDK de fornecedor aqui, e por isso o pacote
276
+ * continua sem dependência de runtime: `fetch` é do Node.
277
+ *
278
+ * A chave **nunca** entra por argumento de linha de comando, só por variável de
279
+ * ambiente: argumento fica no histórico do shell e na lista de processos.
280
+ */
281
+ interface ExtractOptions {
282
+ apiKey?: string;
283
+ baseUrl: string;
284
+ model: string;
285
+ /**
286
+ * Modo de saída estruturada. O padrão é negociar sozinho.
287
+ *
288
+ * Aqui é onde a compatibilidade quebra de verdade: o LM Studio recusa
289
+ * `json_object` com 400 e só aceita `json_schema`, a OpenAI aceita os dois,
290
+ * e servidor mais simples não conhece o campo. Como nenhum valor serve a
291
+ * todos, a primeira tentativa vai com `json_schema` e, se o servidor recusar,
292
+ * a segunda vai sem nada. Quem quiser fixar um modo passa ele aqui.
293
+ *
294
+ * Vale lembrar que isto mexe em **aproveitamento**, não em correção: saída
295
+ * malformada é recusada pela conferência de qualquer jeito.
296
+ */
297
+ responseFormat?: 'auto' | 'json_object' | 'json_schema' | 'none';
298
+ /** Milissegundos até desistir. Modelo local frio demora para carregar. */
299
+ timeoutMs?: number;
300
+ }
301
+ interface ExtractResult {
302
+ /** O JSON que o modelo devolveu, ainda sem conferência nenhuma. */
303
+ payload: unknown;
304
+ /** Texto cru da resposta, guardado para quando o parse falha. */
305
+ raw: string;
306
+ tookMs: number;
307
+ }
308
+ /**
309
+ * Manda o laudo e a lista ancorada ao modelo e devolve o que ele respondeu.
310
+ *
311
+ * Não confere nada: a saída vai para o `validateExtraction`, que é onde a
312
+ * ancoragem é cobrada. Separar os dois é proposital, porque é o que deixa
313
+ * trocar de modelo sem mexer na parte que garante o resultado.
314
+ */
315
+ declare function extractWithModel(text: string, options: ExtractOptions): Promise<ExtractResult>;
316
+
317
+ export { type AnchorMatch, type AnchorResult, CONFIDENCE_AMBIGUOUS, CONFIDENCE_NAME_ONLY, CONFIDENCE_VALUE_ADJACENT, type ExtractOptions, type ExtractResult, type ExtractedBiomarker, type ExtractionPayload, type ExtractionValidationResult, LAB_EXTRACTION_SCHEMA, type LabResultEnvelope, type RejectedBiomarker, type RejectionReason, type ToLabResultOptions, type ValidateExtractionOptions, acceptedBiomarkers, extractWithModel, extractionToLabResult, findBiomarkersInText, getMatchedCodes, validateExtraction };
package/dist/index.d.ts CHANGED
@@ -55,4 +55,263 @@ declare function findBiomarkersInText(ocrText: string): AnchorResult;
55
55
  */
56
56
  declare function getMatchedCodes(result: AnchorResult): string[];
57
57
 
58
- export { type AnchorMatch, type AnchorResult, CONFIDENCE_AMBIGUOUS, CONFIDENCE_NAME_ONLY, CONFIDENCE_VALUE_ADJACENT, findBiomarkersInText, getMatchedCodes };
58
+ /**
59
+ * Contrato de saída para extração de laudo por modelo.
60
+ *
61
+ * Este é o **contrato de interoperabilidade**, e não um prompt. Ele descreve a
62
+ * forma do JSON que qualquer modelo precisa devolver para o resto do toolkit
63
+ * conseguir conferir e converter o resultado. Não diz como pedir isso ao
64
+ * modelo, não traz instrução de comportamento e não depende de fornecedor:
65
+ * quem usa liga do jeito que a plataforma dele permitir (saída estruturada,
66
+ * gramática, tool use ou simples prompt com validação por cima).
67
+ *
68
+ * As descrições são deliberadamente neutras. Regra de comportamento ("nunca
69
+ * infira", "copie literalmente") é ajuste de prompt, muda de modelo para
70
+ * modelo e não pertence a um contrato público.
71
+ *
72
+ * As descrições são as únicas strings em inglês do pacote, e isso é
73
+ * deliberado: o schema é contrato de integração lido por quem consome de fora
74
+ * do Brasil, e uma descrição em pt-BR não ajuda ninguém em Colônia ou Madri.
75
+ * O resto da documentação segue a regra do ecossistema.
76
+ *
77
+ * O campo `sourceText` existe porque é o que torna a conferência possível:
78
+ * sem o trecho que originou o valor não dá para auditar a extração depois.
79
+ *
80
+ * Campo que aceita mais de um tipo usa `anyOf`, e não `type: [...]`. As duas
81
+ * formas são JSON Schema válido, mas decodificador restrito não engole a
82
+ * segunda: o LM Studio recusa a geração com `'type' must be a string`. Como o
83
+ * ponto do contrato é servir a qualquer modelo, vale a forma mais aceita.
84
+ */
85
+ declare const LAB_EXTRACTION_SCHEMA: {
86
+ readonly $id: "https://fhir-brasil.dev.br/schemas/lab-extraction.json";
87
+ readonly $schema: "https://json-schema.org/draft/2020-12/schema";
88
+ readonly additionalProperties: false;
89
+ readonly properties: {
90
+ readonly biomarkers: {
91
+ readonly description: "The measurements read from the report.";
92
+ readonly items: {
93
+ readonly additionalProperties: false;
94
+ readonly properties: {
95
+ readonly confidence: {
96
+ readonly description: "Confidence in this reading, from 0 to 1.";
97
+ readonly maximum: 1;
98
+ readonly minimum: 0;
99
+ readonly type: "number";
100
+ };
101
+ readonly loinc: {
102
+ readonly anyOf: readonly [{
103
+ readonly type: "string";
104
+ }, {
105
+ readonly type: "null";
106
+ }];
107
+ readonly description: "A LOINC code from the allowed list, or null when none of them applies.";
108
+ };
109
+ readonly name: {
110
+ readonly description: "The measurement name as the report prints it.";
111
+ readonly type: "string";
112
+ };
113
+ readonly referenceMax: {
114
+ readonly anyOf: readonly [{
115
+ readonly type: "number";
116
+ }, {
117
+ readonly type: "null";
118
+ }];
119
+ readonly description: "Upper bound of the range printed on the report, or null.";
120
+ };
121
+ readonly referenceMin: {
122
+ readonly anyOf: readonly [{
123
+ readonly type: "number";
124
+ }, {
125
+ readonly type: "null";
126
+ }];
127
+ readonly description: "Lower bound of the range printed on the report, or null.";
128
+ };
129
+ readonly sourceText: {
130
+ readonly description: "The snippet of the report carrying this measurement and its value.";
131
+ readonly type: "string";
132
+ };
133
+ readonly unit: {
134
+ readonly description: "Unit as the report prints it. Empty string when there is none.";
135
+ readonly type: "string";
136
+ };
137
+ readonly value: {
138
+ readonly anyOf: readonly [{
139
+ readonly type: "number";
140
+ }, {
141
+ readonly type: "string";
142
+ }];
143
+ readonly description: "Numeric value, or text for a qualitative result.";
144
+ };
145
+ };
146
+ readonly required: readonly ["name", "value", "unit", "sourceText", "confidence"];
147
+ readonly type: "object";
148
+ };
149
+ readonly type: "array";
150
+ };
151
+ };
152
+ readonly required: readonly ["biomarkers"];
153
+ readonly title: "Laboratory report extraction";
154
+ readonly type: "object";
155
+ };
156
+ /** Uma grandeza como o modelo devolve, antes de qualquer conferência. */
157
+ interface ExtractedBiomarker {
158
+ confidence: number;
159
+ loinc?: string | null;
160
+ name: string;
161
+ referenceMax?: number | null;
162
+ referenceMin?: number | null;
163
+ sourceText: string;
164
+ unit: string;
165
+ value: number | string;
166
+ }
167
+ /** O objeto inteiro que o modelo devolve. */
168
+ interface ExtractionPayload {
169
+ biomarkers: ExtractedBiomarker[];
170
+ }
171
+
172
+ /**
173
+ * Converte grandezas já conferidas no envelope que o `fhir-bio convert` come.
174
+ *
175
+ * O laudo e o paciente não vêm do modelo: o contrato de extração cobre só as
176
+ * grandezas. Os dois saem daqui com valores sintéticos e óbvios, na mesma
177
+ * linha do `fhir-rnds-sandbox`, para a demo rodar de ponta a ponta sem inventar
178
+ * identidade de ninguém. Quem integra de verdade troca os dois pelo que já tem.
179
+ */
180
+ interface LabResultEnvelope {
181
+ observations: {
182
+ biomarkerCode: string;
183
+ biomarkerName: string;
184
+ flag: 'H' | 'L' | '';
185
+ referenceMax?: number;
186
+ referenceMin?: number;
187
+ reportId: string;
188
+ unit: string;
189
+ value: number | string;
190
+ }[];
191
+ profile: {
192
+ name: string;
193
+ userId: string;
194
+ };
195
+ report: {
196
+ collectionDate: string;
197
+ createdAt: string;
198
+ overallStatus: 'ANORMAL' | 'NORMAL';
199
+ processingStatus: 'complete';
200
+ reportId: string;
201
+ userId: string;
202
+ };
203
+ }
204
+ interface ToLabResultOptions {
205
+ collectionDate?: string;
206
+ reportId?: string;
207
+ userId?: string;
208
+ }
209
+ declare function extractionToLabResult(biomarkers: ExtractedBiomarker[], options?: ToLabResultOptions): LabResultEnvelope;
210
+
211
+ /**
212
+ * Conferência da saída do modelo contra o contrato e contra a ancoragem.
213
+ *
214
+ * São duas checagens, e as duas são determinísticas:
215
+ *
216
+ * 1. **Forma.** O objeto bate com `LAB_EXTRACTION_SCHEMA`. Modelo que devolve
217
+ * texto solto, campo faltando ou tipo errado é recusado aqui, o que deixa
218
+ * a qualidade do modelo virar problema de cobertura e nunca de correção.
219
+ * 2. **Ancoragem.** O código veio da lista que a varredura liberou. Código que
220
+ * o laudo não mencionou é descartado, que é a falha cara: um valor
221
+ * plausível pendurado num exame que não estava na página.
222
+ *
223
+ * A validação de citação, a correção de código contra nome impresso e a
224
+ * política de confiança não moram aqui.
225
+ *
226
+ * Sem dependência de runtime além do `@precisa-saude/fhir`: a checagem de
227
+ * forma é escrita à mão porque o schema é pequeno e o pacote não carrega
228
+ * validador de JSON Schema.
229
+ */
230
+ /** Por que uma grandeza foi recusada. */
231
+ type RejectionReason = 'not-anchored' | 'schema';
232
+ interface RejectedBiomarker {
233
+ /** Mensagem legível, já em pt-BR, dizendo o que falhou. */
234
+ detail: string;
235
+ /** O que o modelo devolveu, sem alteração, para o consumidor poder logar. */
236
+ raw: unknown;
237
+ reason: RejectionReason;
238
+ }
239
+ interface ExtractionValidationResult {
240
+ accepted: ExtractedBiomarker[];
241
+ /** Erros do objeto inteiro, quando nem dá para chegar nas grandezas. */
242
+ errors: string[];
243
+ rejected: RejectedBiomarker[];
244
+ /** `true` quando o objeto tem forma válida, mesmo que toda grandeza caia. */
245
+ valid: boolean;
246
+ }
247
+ interface ValidateExtractionOptions {
248
+ /**
249
+ * Resultado da ancoragem sobre o mesmo texto que foi ao modelo. Sem ele a
250
+ * checagem de ancoragem não roda e só a forma é conferida, que é um modo
251
+ * deliberadamente mais fraco: serve para inspecionar saída de modelo sem o
252
+ * laudo em mãos.
253
+ */
254
+ anchors?: AnchorResult;
255
+ }
256
+ /**
257
+ * Confere a saída de um modelo contra o contrato e, quando a ancoragem é
258
+ * fornecida, contra a lista de códigos que a varredura liberou.
259
+ */
260
+ declare function validateExtraction(raw: unknown, options?: ValidateExtractionOptions): ExtractionValidationResult;
261
+ /** Só a lista de grandezas aprovadas, para quem não quer o relatório inteiro. */
262
+ declare function acceptedBiomarkers(raw: unknown, options?: ValidateExtractionOptions): ExtractedBiomarker[];
263
+
264
+ /**
265
+ * Cliente mínimo para endpoint compatível com OpenAI.
266
+ *
267
+ * Isto é **conveniência, não contrato**. O contrato é o
268
+ * `LAB_EXTRACTION_SCHEMA`, e o toolkit funciona inteiro sem esta função: quem
269
+ * integra chama o próprio modelo do jeito que a plataforma dele permitir e
270
+ * entrega o JSON ao `validateExtraction`. Esta função existe para a demo rodar
271
+ * de uma ponta à outra sem um `curl` no meio.
272
+ *
273
+ * `/v1/chat/completions` é o que praticamente todo mundo fala: LM Studio,
274
+ * Ollama, llama.cpp, vLLM, OpenRouter, OpenAI, e a Anthropic pelo endpoint de
275
+ * compatibilidade. Por isso não há SDK de fornecedor aqui, e por isso o pacote
276
+ * continua sem dependência de runtime: `fetch` é do Node.
277
+ *
278
+ * A chave **nunca** entra por argumento de linha de comando, só por variável de
279
+ * ambiente: argumento fica no histórico do shell e na lista de processos.
280
+ */
281
+ interface ExtractOptions {
282
+ apiKey?: string;
283
+ baseUrl: string;
284
+ model: string;
285
+ /**
286
+ * Modo de saída estruturada. O padrão é negociar sozinho.
287
+ *
288
+ * Aqui é onde a compatibilidade quebra de verdade: o LM Studio recusa
289
+ * `json_object` com 400 e só aceita `json_schema`, a OpenAI aceita os dois,
290
+ * e servidor mais simples não conhece o campo. Como nenhum valor serve a
291
+ * todos, a primeira tentativa vai com `json_schema` e, se o servidor recusar,
292
+ * a segunda vai sem nada. Quem quiser fixar um modo passa ele aqui.
293
+ *
294
+ * Vale lembrar que isto mexe em **aproveitamento**, não em correção: saída
295
+ * malformada é recusada pela conferência de qualquer jeito.
296
+ */
297
+ responseFormat?: 'auto' | 'json_object' | 'json_schema' | 'none';
298
+ /** Milissegundos até desistir. Modelo local frio demora para carregar. */
299
+ timeoutMs?: number;
300
+ }
301
+ interface ExtractResult {
302
+ /** O JSON que o modelo devolveu, ainda sem conferência nenhuma. */
303
+ payload: unknown;
304
+ /** Texto cru da resposta, guardado para quando o parse falha. */
305
+ raw: string;
306
+ tookMs: number;
307
+ }
308
+ /**
309
+ * Manda o laudo e a lista ancorada ao modelo e devolve o que ele respondeu.
310
+ *
311
+ * Não confere nada: a saída vai para o `validateExtraction`, que é onde a
312
+ * ancoragem é cobrada. Separar os dois é proposital, porque é o que deixa
313
+ * trocar de modelo sem mexer na parte que garante o resultado.
314
+ */
315
+ declare function extractWithModel(text: string, options: ExtractOptions): Promise<ExtractResult>;
316
+
317
+ export { type AnchorMatch, type AnchorResult, CONFIDENCE_AMBIGUOUS, CONFIDENCE_NAME_ONLY, CONFIDENCE_VALUE_ADJACENT, type ExtractOptions, type ExtractResult, type ExtractedBiomarker, type ExtractionPayload, type ExtractionValidationResult, LAB_EXTRACTION_SCHEMA, type LabResultEnvelope, type RejectedBiomarker, type RejectionReason, type ToLabResultOptions, type ValidateExtractionOptions, acceptedBiomarkers, extractWithModel, extractionToLabResult, findBiomarkersInText, getMatchedCodes, validateExtraction };