UNPKG

@fusedashlabs/ui9000-workspace

Version:

UI9000 workspace MCP. One tool, show_workspace, draws a chart from your table. Set TYPESAFE_API_KEY to let Jev choose the chart.

166 lines (165 loc) • 7.09 kB
/** * Two Jev calls, then one outcome: draw or miss. * A miss leaves the engine draw in place. A clear class does the same when its chart cannot be drawn. */ import { canDraw, chartFamiliesFor, chartFitsProfile, chartNamedByWords, resolveChartName, } from '../catalog/discuss-chart.js'; import { chartLabel, chartRole } from '../catalog/chart-roles.js'; import { intentFrom } from '../spec/intent.js'; import { acceptReading, classChoiceRequest, markChoiceRequest, offeredChartIds, } from './chart-choice.js'; export async function resolveJev(input) { if (!input.ask) return { kind: 'skip' }; const families = chartFamiliesFor(input.profile, input.classified, intentFrom(input.intent)); if (families.length === 0) return { kind: 'miss' }; const columns = (input.classified ?? []).map((column) => ({ name: column.column.name, role: column.role, })); const askedForChart = Boolean(input.requestedChart?.trim()); const named = askedForChart ? resolveChartName(input.requestedChart ?? '') : undefined; let classResponse; try { classResponse = await input.ask(classChoiceRequest({ utterance: input.utterance, columns, profile: input.profile, intent: input.intent, askNamedChart: !input.requestedChart, })); } catch { return { kind: 'miss' }; } const offered = families.map((family) => family.main); const reading = acceptReading(classResponse, offered); if (!input.requestedChart && reading.namedChart) { const local = chartNamedByWords(input.utterance); const family = families.find((item) => item.main === reading.top); if (local && canDraw(local, input.profile, input.catalogIds) && chartFitsProfile(local, input.profile)) { return { kind: 'draw', chartId: local, by: 'named', why: reading.clear && family && inFamily(local, family) ? `You asked for ${chartLabel(local)}. Jev selected the same class, ${family.question} so this chart stays.` : `You asked for ${chartLabel(local)}, so this chart is drawn.`, }; } } if (!reading.clear || !reading.top) { return hesitate(input, families, named, reading.top, reading.runnerUp); } const family = families.find((item) => item.main === reading.top); if (!family) return { kind: 'miss' }; const runner = families.find((item) => item.main === reading.runnerUp); if (named && inFamily(named, family) && canDraw(named, input.profile, input.catalogIds)) { return { kind: 'draw', chartId: named, by: 'named', why: `You asked for ${chartLabel(named)}. Jev selected the same class, ${family.question} so this chart stays.`, }; } const picked = await pickMark(input, columns, family); const chartId = picked.id; const why = drawWhy(family, runner, chartId, picked.runnerUp, picked.fromMark); if (named && canDraw(named, input.profile, input.catalogIds) && chartFitsProfile(named, input.profile)) { return { kind: 'draw', chartId: named, by: 'named', why: `You asked for ${chartLabel(named)}, so this chart is drawn.`, }; } if (canDraw(chartId, input.profile, input.catalogIds) && chartFitsProfile(chartId, input.profile)) { return { kind: 'draw', chartId, by: 'jev', why }; } return { kind: 'miss' }; } async function pickMark(input, columns, family) { if (family.alternatives.length === 0 || !input.ask) { return { id: family.main, runnerUp: null, fromMark: false }; } const request = markChoiceRequest({ utterance: input.utterance, columns, profile: input.profile, intent: input.intent, family, }); try { const response = await input.ask(request); const reading = acceptReading(response, offeredChartIds(request)); if (reading.clear && reading.top && canDraw(reading.top, input.profile, input.catalogIds) && chartFitsProfile(reading.top, input.profile)) { return { id: reading.top, runnerUp: reading.runnerUp, fromMark: reading.top !== family.main }; } } catch { // A clear class stays. The mark falls back to its main. } return { id: family.main, runnerUp: null, fromMark: false }; } function hesitate(input, families, named, top, runnerUp) { const first = families.find((family) => family.main === top); const second = families.find((family) => family.main === runnerUp); if (!first || !second) return { kind: 'miss' }; if (named && canDraw(named, input.profile, input.catalogIds) && (inFamily(named, first) || inFamily(named, second))) { return { kind: 'draw', chartId: named, by: 'named', why: `You asked for ${chartLabel(named)}. Jev hesitated between ${chartLabel(first.main)} and ${chartLabel(second.main)}, and this chart is one of those classes, so it stays.`, }; } const lead = [first, second].find((family) => canDraw(family.main, input.profile, input.catalogIds)); if (named && canDraw(named, input.profile, input.catalogIds) && chartFitsProfile(named, input.profile)) { return { kind: 'draw', chartId: named, by: 'named', why: `You asked for ${chartLabel(named)}. Jev hesitated between ${chartLabel(first.main)} and ${chartLabel(second.main)}, and this chart is drawn.`, }; } if (lead) { return { kind: 'draw', chartId: lead.main, by: 'jev', why: `Jev hesitated between ${chartLabel(first.main)} and ${chartLabel(second.main)}, and drew ${chartLabel(lead.main)}, because ${because(lead.question)}`, }; } return { kind: 'miss' }; } function drawWhy(family, runner, chartId, markRunner, fromMark) { const over = runner ? ` over ${chartLabel(runner.main)}` : ''; const classBit = `Jev selected ${chartLabel(family.main)}${over}, because ${because(family.question)}`; if (!fromMark) return classBit; const markOver = markRunner ? ` over ${chartLabel(markRunner)}` : ''; return `${classBit} Inside it, Jev selected ${chartLabel(chartId)}${markOver}, because ${markBecause(family, chartId)}`; } function markBecause(family, chartId) { const alt = family.alternatives.find((item) => item.id === chartId); if (alt) return because(alt.when); return because(chartRole(chartId)?.role ?? family.question); } function because(sentence) { const trimmed = sentence.trim().replace(/[.\s]+$/, ''); return trimmed.charAt(0).toLowerCase() + trimmed.slice(1); } function inFamily(id, family) { return family.main === id || family.alternatives.some((alt) => alt.id === id); }