diff --git a/src/routes/clinicalAssistant.js b/src/routes/clinicalAssistant.js index a97ae9a..316541f 100644 --- a/src/routes/clinicalAssistant.js +++ b/src/routes/clinicalAssistant.js @@ -496,6 +496,14 @@ async function multimodalSearch(query, opts) { }); } +async function indexedTopicSuggestions(limit) { + return callMcpTool('nc_indexed_topic_suggestions', { + limit: limit || 9, + sample_size: 900, + doc_type: 'file' + }); +} + async function callMcpTool(name, args) { var session = await mcpRequest({ jsonrpc: '2.0', @@ -1056,6 +1064,15 @@ async function getAvailableExamples() { var now = Date.now(); if (_exampleCache.expiresAt > now && _exampleCache.examples.length) return _exampleCache.examples; + var indexedExamples = await getIndexedTopicExamples().catch(function(e) { + console.warn('[clinical-assistant] indexed topic suggestions skipped:', e.message); + return []; + }); + if (indexedExamples.length >= 3) { + _exampleCache = { expiresAt: now + EXAMPLE_CACHE_MS, examples: indexedExamples }; + return indexedExamples; + } + var examples = []; var candidates = rotateExamples(EXAMPLE_CANDIDATES, now); for (var i = 0; i < candidates.length && examples.length < 9; i++) { @@ -1087,6 +1104,31 @@ async function getAvailableExamples() { return examples; } +async function getIndexedTopicExamples() { + var response = await indexedTopicSuggestions(9); + var data = response && (response.structuredContent || response.data || response); + if ((!data || !Array.isArray(data.suggestions)) && response && Array.isArray(response.content)) { + for (var i = 0; i < response.content.length; i++) { + var c = response.content[i]; + if (c && c.type === 'text' && c.text) { + try { + var parsed = JSON.parse(c.text); + if (parsed && Array.isArray(parsed.suggestions)) data = parsed; + } catch (e) {} + } + } + } + if (!data || !Array.isArray(data.suggestions)) return []; + return data.suggestions.slice(0, 9).map(function(item) { + return { + label: item.label || 'Indexed topic', + prompt: item.prompt || '', + sourceTitle: Array.isArray(item.sample_titles) ? item.sample_titles[0] : '', + category: item.category || '' + }; + }).filter(function(item) { return item.prompt; }); +} + function rotateExamples(items, seed) { var copy = items.slice(); var offset = Math.floor(seed / EXAMPLE_CACHE_MS) % copy.length; @@ -1109,15 +1151,15 @@ async function generateImage(prompt, model) { function imageSizeForPrompt(prompt) { var text = String(prompt || ''); - if (/\b(flow\s*chart|flowchart|algorithm|pathway|timeline|vertical|stepwise|decision\s*tree|age\s*group|0-21|22-28|29-60)\b/i.test(text)) return '1024x1792'; - if (/\b(table|matrix|comparison|wide|landscape|side-by-side)\b/i.test(text)) return '1792x1024'; + if (/\b(flow\s*chart|flowchart|algorithm|pathway|timeline|vertical|stepwise|decision\s*tree|age\s*group|0-21|22-28|29-60)\b/i.test(text)) return '1024x1536'; + if (/\b(table|matrix|comparison|wide|landscape|side-by-side)\b/i.test(text)) return '1536x1024'; return '1024x1024'; } function imagePromptForCanvas(prompt, size) { var guidance = ' Keep all text and boxes fully inside the canvas with generous margins. Use fewer words per box, large readable type, and avoid cropping at edges.'; - if (size === '1024x1792') guidance += ' Use a vertical portrait layout with top-to-bottom flow and ample spacing between decision nodes.'; - if (size === '1792x1024') guidance += ' Use a wide landscape layout with columns and ample horizontal spacing.'; + if (size === '1024x1536') guidance += ' Use a vertical portrait layout with top-to-bottom flow and ample spacing between decision nodes.'; + if (size === '1536x1024') guidance += ' Use a wide landscape layout with columns and ample horizontal spacing.'; return String(prompt || '').trim() + guidance; }