diff --git a/src/routes/myResources.js b/src/routes/myResources.js index ac7c99fd..f51aaa08 100644 --- a/src/routes/myResources.js +++ b/src/routes/myResources.js @@ -24,6 +24,13 @@ var resourceImages = require('../utils/resourceImages'); var deckSchema = require('../utils/deckSchema'); var deckBuild = require('../utils/deckBuild'); var deckReview = require('../utils/deckReview'); +// The diagnostics below are the record of what a generation or a modification +// actually did. console goes to the container's stdout, which is destroyed every +// time the container is recreated — so the one question these exist to answer, +// "did that modification change anything", became unanswerable after any deploy. +// logger writes to the dated file in the scribe-logs volume, and to Loki when it +// is configured, so the record outlives the container. +var logger = require('../utils/logger'); var webSearch = require('../utils/webSearch'); var pubmedSearch = require('../utils/pubmedSearch'); var documentExport = require('../utils/documentExport'); @@ -375,20 +382,21 @@ router.post('/my-resources/generate', async function (req, res) { // plain-markdown deck is a materially worse artifact to fall back to // after a single unlucky reply. var firstFailure = deckFailure(ai && ai.content); - console.warn('[my-resources] deck reply was not usable (' + firstFailure + '); asking once more'); + logger.warn('[my-resources] deck reply was not usable (' + firstFailure + '); asking once more', + { topic: topic, attempt: 1, model: ai && ai.model }); var retryGaps = []; ai = await callAI([{ role: 'user', content: prompt }], options); deck = deckBuild.parse(ai && ai.content, retryGaps); reportVocabularyGaps(retryGaps, topic); - if (deck) console.log('[my-resources] the second deck attempt parsed'); + if (deck) logger.info('[my-resources] the second deck attempt parsed', { topic: topic }); } if (deckMode && !deck) { // Twice is enough. Falling back to markdown beats saving nothing, and // beats saving the model's apology. deckFallback = deckFailure(ai && ai.content); - console.warn('[my-resources] deck reply was not usable twice (' + deckFallback + - '); retrying as markdown'); + logger.warn('[my-resources] deck reply was not usable twice (' + deckFallback + + '); retrying as markdown', { topic: topic, attempt: 2, model: ai && ai.model }); var plain = buildPrompt({ topic: topic, kind: kind, refinement: refinement, corpusContext: corpus.context, literature: sources.literature, webFindings: sources.webFindings, @@ -706,7 +714,8 @@ function logRefine(event) { } parts.push('instruction="' + String(event.instructions || '').slice(0, 70).replace(/\s+/g, ' ') + '"'); var line = parts.join(' '); - if (event.outcome !== 'applied' || event.unchanged) console.warn(line); else console.log(line); + if (event.outcome !== 'applied' || event.unchanged) logger.warn(line, event); + else logger.info(line, event); try { require('../utils/metrics').resourceRefines.inc({ path: event.path, @@ -727,7 +736,7 @@ function reportVocabularyGaps(gaps, topic) { try { require('../utils/metrics').deckVocabularyGaps.inc({ wanted: gap.wanted }); } catch (e) { /* metrics are never worth an error here */ } - console.warn('[deck-vocabulary] wanted "' + gap.wanted + '" (' + gap.detail + + logger.warn('[deck-vocabulary] wanted "' + gap.wanted + '" (' + gap.detail + ') while generating: ' + String(topic || '').slice(0, 80)); }); } @@ -753,7 +762,7 @@ async function collectFigures(ids, user, dir) { out.push(file); } catch (e) { // Still generating, failed, or deleted. The deck is fine without it. - console.warn('[my-resources] figure unavailable for export:', e.message); + logger.warn('[my-resources] figure unavailable for export', { error: e.message }); } } return out; diff --git a/test/my-resources-refine.test.js b/test/my-resources-refine.test.js index 57145b3d..fc21a554 100644 --- a/test/my-resources-refine.test.js +++ b/test/my-resources-refine.test.js @@ -68,6 +68,7 @@ function router(t, overrides = {}) { '../utils/documentExport': { FORMATS: {}, isSupported: () => false, filename: () => 'x', mimeFor: () => '', render: async () => ({}), renderDeck: async () => ({}) }, '../utils/generatedImages': { service: () => ({ get: async () => null, asset: async () => null }), workflows: ['my_resources'] }, '../utils/learningRetrieval': { retrieve: async () => ({ context: '', sources: [], reason: null }) }, + '../utils/logger': quiet, '../utils/metrics': { resourceRefines: { inc() {} }, resourceVocabularyGaps: { inc() {} } }, '../utils/pubmedSearch': { isAvailable: async () => false, search: async () => ({ results: [] }), formatForPrompt: () => '' }, '../utils/webSearch': { isAvailable: async () => false, search: async () => ({ results: [] }), formatForPrompt: () => '' },