31 lines
1.4 KiB
JavaScript
31 lines
1.4 KiB
JavaScript
import { MODEL_ID } from '#import-recipes/extraction/MODEL_ID.js'
|
|
import { RECIPE_SCHEMA } from '#import-recipes/extraction/RECIPE_SCHEMA.js'
|
|
import { buildExtractionPrompt } from '#import-recipes/extraction/buildExtractionPrompt.js'
|
|
import { readRecipeResponse } from '#import-recipes/extraction/readRecipeResponse.js'
|
|
|
|
const MAX_TOKENS = 16000
|
|
|
|
// The only call to the model in this tool. It never throws: a failure comes back
|
|
// as { ok: false, reason } so the CLI can log it and carry on with the next file.
|
|
//
|
|
// `output_config.format` pins the answer to RECIPE_SCHEMA - no prefill, no
|
|
// "answer with JSON only" wishful thinking. Thinking is adaptive, which is the
|
|
// default on this model; it is stated because splitting an ingredient list
|
|
// without inventing numbers is exactly the kind of thing worth thinking about.
|
|
export async function extractRecipe({ client, fileName, paragraphs }) {
|
|
try {
|
|
const response = await client.messages.create({
|
|
model: MODEL_ID,
|
|
max_tokens: MAX_TOKENS,
|
|
thinking: { type: 'adaptive' },
|
|
output_config: { format: { type: 'json_schema', schema: RECIPE_SCHEMA } },
|
|
messages: [
|
|
{ role: 'user', content: buildExtractionPrompt({ fileName, paragraphs }) }
|
|
]
|
|
})
|
|
const result = readRecipeResponse(response)
|
|
return result.ok ? { ok: true, recipe: result.recipe, usage: response.usage } : result
|
|
} catch (error) {
|
|
return { ok: false, reason: error.message }
|
|
}
|
|
}
|