iobroker.assistant 1.4.5 → 1.4.7
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/README.md +16 -1
- package/admin/i18n/de.json +11 -2
- package/admin/i18n/en.json +11 -2
- package/admin/i18n/es.json +11 -2
- package/admin/i18n/fr.json +11 -2
- package/admin/i18n/it.json +11 -2
- package/admin/i18n/nl.json +11 -2
- package/admin/i18n/pl.json +11 -2
- package/admin/i18n/pt.json +11 -2
- package/admin/i18n/ru.json +11 -2
- package/admin/i18n/uk.json +11 -2
- package/admin/i18n/zh-cn.json +11 -2
- package/admin/jsonConfig.json +65 -2
- package/build/lib/ask.js +10 -0
- package/build/lib/ask.js.map +1 -1
- package/build/lib/journal.js.map +1 -1
- package/build/lib/llm.js +50 -12
- package/build/lib/llm.js.map +1 -1
- package/build/lib/media.js +226 -0
- package/build/lib/media.js.map +1 -0
- package/build/lib/piAgent.js +1 -0
- package/build/lib/piAgent.js.map +1 -1
- package/build/lib/voice/engines.js +3 -0
- package/build/lib/voice/engines.js.map +1 -1
- package/build/lib/voice/esphome.js +258 -5
- package/build/lib/voice/esphome.js.map +1 -1
- package/build/lib/voice/ffmpegStatic.js +137 -0
- package/build/lib/voice/ffmpegStatic.js.map +1 -0
- package/build/lib/voice/stt.js +35 -3
- package/build/lib/voice/stt.js.map +1 -1
- package/build/main.js +306 -32
- package/build/main.js.map +1 -1
- package/io-package.json +34 -29
- package/package.json +1 -1
package/build/lib/journal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"journal.js","sourceRoot":"","sources":["../../src/lib/journal.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Conversation journal: one entry per request the assistant answered — where it came from (chat, script,\n * Telegram, which satellite, which wake word), which tier answered it (routine, rule-based NLU, local\n * model, cloud model), which tools the model called and which state ids were read or written along the\n * way. The admin chat shows it live, so one can check what a satellite heard and what was done about it.\n *\n * Tools and state accesses happen deep inside the pipeline (NLU executor, LLM tool loop, routine\n * executor). Instead of threading an entry object through all of them, the entry rides along in an\n * {@link AsyncLocalStorage}: {@link Journal.run} opens it, and {@link Journal.note}/{@link Journal.tool}/\n * {@link Journal.stateAccess} write to whatever entry the current call chain belongs to — and do nothing\n * outside of one (a trigger writing a state is not a conversation).\n *\n * Besides the entry, every request keeps a **trace**: the steps of its processing with their timing and\n * whatever explains them — the NLU's parse, the full prompt and every round of the model, tool arguments\n * and results in full, speech recognition and synthesis. Traces are too big to ride along in `journal.last`\n * and in `journal.json`, so the chat fetches one when a request is opened ({@link Journal.trace}); each is\n * kept in a file of its own and only for the newest {@link MAX_TRACES} requests.\n */\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { promises as fs } from 'node:fs';\nimport * as path from 'node:path';\nimport type { VadReport } from './voice/vad';\n\n/** How a request reached us. */\nexport type JournalTransport = 'chat' | 'script' | 'state' | 'telegram' | 'esphome' | 'udp' | 'native' | 'wyoming';\n\n/** Which stage of the pipeline produced the answer. */\nexport type JournalTier =\n | 'stop' // stop word silenced a ringing timer/alarm\n | 'askReply' // the utterance answered an open `askUser` question\n | 'routine'\n | 'nlu'\n | 'localLlm'\n | 'cloud'\n | 'none'; // nothing local understood it and there is no cloud provider\n\n/** Tokens a model call used (summed over the rounds of a request in {@link JournalEntry.tokens}). */\nexport interface JournalTokens {\n input: number;\n output: number;\n /** Prompt tokens served from the provider's prompt cache. */\n cacheRead?: number;\n /** Prompt tokens written to the provider's prompt cache (Anthropic). */\n cacheWrite?: number;\n}\n\n/** One step of how a request was processed — what the debug view of the admin chat shows. */\nexport interface JournalStep {\n /** Start of the step in ms after the request started (negative for speech recognition, which comes first). */\n t: number;\n /** Duration, for steps that took time. */\n ms?: number;\n /** Pipeline stage: request, stt, context, stop, askReply, routine, youtube, nlu, localLlm, cloud, tool, state, answer, tts, error. */\n stage: string;\n title: string;\n level?: 'warn' | 'error';\n tokens?: JournalTokens;\n /** Whatever explains the step (prompts, model responses, parse results) — a JSON snapshot. */\n data?: unknown;\n}\n\n/** What a caller records; the journal adds the timing. */\nexport type JournalStepInput = Omit<JournalStep, 't'>;\n\n/** Hook for library code that should not know the journal (the LLM agent). */\nexport type TraceFn = (step: JournalStepInput) => void;\n\nexport interface JournalTool {\n name: string;\n args: Record<string, unknown>;\n ms: number;\n /** Result, JSON, shortened. */\n result?: string;\n error?: string;\n}\n\nexport interface JournalEntry {\n id: string;\n /** Start of the answer, ms. */\n ts: number;\n /** False while the answer is still being produced. */\n done: boolean;\n /** Time to produce the answer (without STT/TTS). */\n ms?: number;\n source: string;\n transport: JournalTransport;\n satellite?: string;\n room?: string;\n /** Wake word the satellite reported; '' = the mic was re-opened for a follow-up, absent = unknown. */\n wakeWord?: string;\n /** Speech recognition time, for voice requests. */\n sttMs?: number;\n /** Length of the recorded utterance. */\n audioMs?: number;\n /** Speech synthesis time of the reply (voice requests, filled in after the answer). */\n ttsMs?: number;\n /** Length of the spoken reply. */\n replyAudioMs?: number;\n question: string;\n answer?: string;\n error?: string;\n tier?: JournalTier;\n /** Model name, routine name or NLU actions — whatever explains the tier. */\n detail?: string;\n tools: JournalTool[];\n /** State ids read to produce the answer. */\n read: string[];\n /** States written to produce the answer. */\n written: { id: string; value: unknown }[];\n /** The reply asks back, so a satellite re-opened its mic. */\n followUp?: boolean;\n /** Tokens of all model calls, when a cloud model answered. */\n tokens?: JournalTokens;\n}\n\n/** What the caller knows about a request before the pipeline starts. */\nexport interface JournalOrigin {\n transport?: JournalTransport;\n satellite?: string;\n room?: string;\n wakeWord?: string;\n sttMs?: number;\n audioMs?: number;\n /** Voice activity detection of the utterance (ESPHome) — trace only. */\n vad?: VadReport;\n}\n\n/** What a transport reports after it synthesised the reply. */\nexport interface JournalSpeech {\n ms: number;\n audioMs: number;\n provider: string;\n fallback?: string;\n voice?: string;\n}\n\nexport interface JournalOptions {\n /** Retention in hours; 0 disables the journal entirely. */\n hours: number;\n /** File the journal is kept in across restarts ('' = memory only). */\n file: string;\n /** Called with every new or updated entry (drives the `journal.last` state the chat listens to). */\n onChange?: (entry: JournalEntry) => void;\n log: ioBroker.Logger;\n}\n\n/** Hard cap independent of the retention — a script asking every few seconds must not fill the disk. */\nconst MAX_ENTRIES = 3000;\nconst SAVE_DELAY_MS = 5000;\nconst RESULT_CHARS = 300;\n/** Requests whose trace is kept on disk. */\nexport const MAX_TRACES = 200;\n/** Traces held in memory (the newest ones — the ones somebody is about to open). */\nconst MEMORY_TRACES = 30;\n/** Steps per request; a tool loop gone wild must not grow one trace without end. */\nconst MAX_STEPS = 400;\n/** Longer strings are cut — enough for a system prompt with a few hundred devices. */\nconst STEP_STRING_CHARS = 100_000;\n/** A reply is matched to its synthesis only this long after the answer. */\nconst SPEECH_WINDOW_MS = 120_000;\n/** Entry ids as {@link Journal.run} makes them — checked before one becomes a file name. */\nconst ID_RE = /^[a-z0-9-]+$/;\n\n/** Directory the traces of a journal file are kept in. */\nexport function traceDirFor(file: string): string {\n return path.join(path.dirname(file), 'journal-trace');\n}\n\n/**\n * JSON snapshot of a step's data — taken when it is recorded, because the objects go on changing (the\n * model's message list grows, cache markers move). Long strings are cut and buffers summarised.\n */\nexport function snapshot(value: unknown): unknown {\n if (value === undefined) {\n return undefined;\n }\n try {\n const text = JSON.stringify(value, (_key, v: unknown) => {\n if (typeof v === 'string' && v.length > STEP_STRING_CHARS) {\n return `${v.slice(0, STEP_STRING_CHARS)}… [+${v.length - STEP_STRING_CHARS} chars]`;\n }\n if (typeof v === 'bigint') {\n return v.toString();\n }\n // Buffer.toJSON() has already run when the replacer sees it.\n const b = v as { type?: unknown; data?: unknown } | null;\n if (b && typeof b === 'object' && b.type === 'Buffer' && Array.isArray(b.data)) {\n return `<${b.data.length} bytes>`;\n }\n return v;\n });\n return text === undefined ? undefined : JSON.parse(text);\n } catch (e) {\n return `<not serializable: ${(e as Error).message}>`;\n }\n}\n\n/** Everything an error carries that helps — an API error's status and body, the stack. */\nexport function errorInfo(e: unknown): Record<string, unknown> {\n if (!(e instanceof Error)) {\n return { message: String(e) };\n }\n const info: Record<string, unknown> = { name: e.name, message: e.message };\n for (const key of ['status', 'code', 'type', 'param', 'error', 'request_id', 'requestID']) {\n const v = (e as unknown as Record<string, unknown>)[key];\n if (v !== undefined && v !== null) {\n info[key] = v;\n }\n }\n if (e.stack) {\n info.stack = e.stack;\n }\n return info;\n}\n\n/** Derive the transport from a request source when the caller did not say. */\nexport function transportForSource(source: string): JournalTransport {\n if (source === 'chat') {\n return 'chat';\n }\n if (source === '') {\n return 'state';\n }\n if (source === 'wyoming') {\n return 'wyoming';\n }\n if (source.startsWith('telegram:')) {\n return 'telegram';\n }\n return 'script';\n}\n\nexport class Journal {\n private entries: JournalEntry[] = [];\n private readonly store = new AsyncLocalStorage<JournalEntry>();\n private saveTimer: NodeJS.Timeout | null = null;\n private counter = 0;\n /** Steps of the newest requests; older ones are read back from their file. */\n private readonly traces = new Map<string, JournalStep[]>();\n /** Traces changed since they were last written. */\n private readonly dirty = new Set<string>();\n /** LLM tool calls in progress per request: a state access inside one belongs to that tool's step. */\n private readonly toolDepth = new WeakMap<JournalEntry, number>();\n private readonly traceDir: string;\n\n constructor(private readonly opts: JournalOptions) {\n this.traceDir = opts.file ? traceDirFor(opts.file) : '';\n }\n\n get enabled(): boolean {\n return this.opts.hours > 0;\n }\n\n /** Load the entries of the last run (silently starts empty when there are none). */\n async load(): Promise<void> {\n if (!this.enabled || !this.opts.file) {\n return;\n }\n try {\n const raw = JSON.parse(await fs.readFile(this.opts.file, 'utf8')) as unknown;\n if (Array.isArray(raw)) {\n // An entry still open at shutdown never got its answer.\n this.entries = (raw as JournalEntry[])\n .filter(e => e && typeof e.id === 'string' && typeof e.ts === 'number')\n .map(e => (e.done ? e : { ...e, done: true, error: e.error || 'interrupted' }));\n this.prune();\n }\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code !== 'ENOENT') {\n this.opts.log.warn(`Journal could not be read: ${(e as Error).message}`);\n }\n }\n }\n\n /** Entries newer than `since` (ms), oldest first. */\n list(since = 0): JournalEntry[] {\n this.prune();\n return this.entries.filter(e => e.ts > since);\n }\n\n clear(): void {\n this.entries = [];\n this.traces.clear();\n this.dirty.clear();\n this.scheduleSave();\n }\n\n /** The entry of a request (the chat asks for it together with its trace). */\n get(id: string): JournalEntry | undefined {\n return this.entries.find(e => e.id === id);\n }\n\n /** The processing steps of a request, or null when none were kept (too old, or from an older version). */\n async trace(id: string): Promise<JournalStep[] | null> {\n if (!ID_RE.test(id) || !this.get(id)) {\n return null;\n }\n const steps = this.traces.get(id);\n if (steps) {\n return steps;\n }\n if (!this.traceDir) {\n return null;\n }\n try {\n const raw = JSON.parse(await fs.readFile(path.join(this.traceDir, `${id}.json`), 'utf8')) as {\n steps?: unknown;\n };\n return Array.isArray(raw?.steps) ? (raw.steps as JournalStep[]) : null;\n } catch {\n return null;\n }\n }\n\n /**\n * Run `fn` as one journalled request. The entry is published once when the request starts (so the\n * chat shows the question while the answer is still being worked out) and once when it is finished.\n */\n async run(question: string, source: string, origin: JournalOrigin, fn: () => Promise<string>): Promise<string> {\n if (!this.enabled) {\n return fn();\n }\n const entry: JournalEntry = {\n id: `${Date.now().toString(36)}-${(this.counter++).toString(36)}`,\n ts: Date.now(),\n done: false,\n source,\n transport: origin.transport || transportForSource(source),\n question,\n tools: [],\n read: [],\n written: [],\n };\n for (const key of ['satellite', 'room', 'wakeWord', 'sttMs', 'audioMs'] as const) {\n if (origin[key] !== undefined && origin[key] !== '') {\n (entry as unknown as Record<string, unknown>)[key] = origin[key];\n }\n }\n // A satellite that re-opened its mic reports no wake word — say so instead of \"unknown\".\n if (origin.wakeWord === '') {\n entry.wakeWord = '';\n }\n this.entries.push(entry);\n this.traces.set(entry.id, []);\n this.addStep(entry, {\n stage: 'request',\n title: `Request from ${source || entry.transport} (${entry.transport})`,\n data: { question, source, origin },\n });\n this.publish(entry);\n try {\n const answer = await this.store.run(entry, fn);\n entry.answer = answer;\n return answer;\n } catch (e) {\n entry.error = (e as Error).message;\n this.addStep(entry, { stage: 'error', level: 'error', title: entry.error, data: errorInfo(e) });\n throw e;\n } finally {\n entry.done = true;\n entry.ms = Date.now() - entry.ts;\n this.dirty.add(entry.id);\n this.publish(entry);\n this.prune();\n this.scheduleSave();\n }\n }\n\n /** Add a step to the current request's trace (nothing outside of one). */\n step(input: JournalStepInput): void {\n const entry = this.store.getStore();\n if (entry) {\n this.addStep(entry, input);\n }\n }\n\n /**\n * Run an LLM tool as part of the current request: the call lands in the entry's tool list and, with the\n * full arguments and result, in the trace.\n */\n async toolCall<T>(name: string, args: Record<string, unknown>, fn: () => Promise<T>): Promise<T> {\n const entry = this.store.getStore();\n if (!entry) {\n return fn();\n }\n this.toolDepth.set(entry, (this.toolDepth.get(entry) || 0) + 1);\n const start = Date.now();\n try {\n const result = await fn();\n const ms = Date.now() - start;\n this.addStep(entry, { stage: 'tool', title: `Tool ${name}`, ms, data: { args, result } });\n this.tool(name, args, ms, result);\n return result;\n } catch (e) {\n const ms = Date.now() - start;\n const message = (e as Error).message;\n this.addStep(entry, {\n stage: 'tool',\n level: 'error',\n title: `Tool ${name} failed: ${message}`,\n ms,\n data: { args, error: errorInfo(e) },\n });\n this.tool(name, args, ms, undefined, message);\n throw e;\n } finally {\n this.toolDepth.set(entry, (this.toolDepth.get(entry) || 1) - 1);\n }\n }\n\n /**\n * Run a state call (MCP `get_states`/`set_state`/…) as part of the current request: the ids land in the\n * entry's read/written lists, and — unless an LLM tool made the call, whose step already shows it — the\n * call with its result in the trace.\n */\n async stateCall<T>(name: string, args: Record<string, unknown> | undefined, fn: () => Promise<T>): Promise<T> {\n const entry = this.store.getStore();\n if (!entry) {\n return fn();\n }\n this.stateAccess(name, args);\n if (this.toolDepth.get(entry)) {\n return fn();\n }\n const start = Date.now();\n try {\n const result = await fn();\n this.addStep(entry, { stage: 'state', title: name, ms: Date.now() - start, data: { args, result } });\n return result;\n } catch (e) {\n this.addStep(entry, {\n stage: 'state',\n level: 'error',\n title: `${name} failed: ${(e as Error).message}`,\n ms: Date.now() - start,\n data: { args, error: errorInfo(e) },\n });\n throw e;\n }\n }\n\n /**\n * A transport synthesised the reply to the latest request of `source`. That happens after the answer,\n * outside of the request, so the entry is found by its source.\n */\n spoken(source: string, speech: JournalSpeech): void {\n const now = Date.now();\n for (let i = this.entries.length - 1; i >= 0; i--) {\n const entry = this.entries[i];\n if (entry.source !== source) {\n continue;\n }\n if (!entry.done || entry.ttsMs !== undefined || now - entry.ts - (entry.ms || 0) > SPEECH_WINDOW_MS) {\n return;\n }\n entry.ttsMs = speech.ms;\n entry.replyAudioMs = Math.round(speech.audioMs);\n const voice = speech.voice ? ` (${speech.voice})` : '';\n this.addStep(entry, {\n stage: 'tts',\n title: `Speech synthesis: ${speech.provider}${voice}, ${(speech.audioMs / 1000).toFixed(1)} s of audio`,\n ms: speech.ms,\n data: speech,\n });\n this.dirty.add(entry.id);\n this.publish(entry);\n this.scheduleSave();\n return;\n }\n }\n\n private addStep(entry: JournalEntry, input: JournalStepInput): void {\n const steps = this.traces.get(entry.id);\n if (!steps || steps.length > MAX_STEPS) {\n return;\n }\n if (steps.length === MAX_STEPS) {\n steps.push({\n t: Date.now() - entry.ts,\n stage: 'error',\n level: 'warn',\n title: `More than ${MAX_STEPS} steps — the rest is not recorded`,\n });\n return;\n }\n const step: JournalStep = {\n t: Date.now() - entry.ts - (input.ms || 0),\n stage: input.stage,\n title: input.title,\n };\n if (input.ms !== undefined) {\n step.ms = input.ms;\n }\n if (input.level) {\n step.level = input.level;\n }\n if (input.tokens) {\n step.tokens = input.tokens;\n const sum = (entry.tokens ||= { input: 0, output: 0 });\n sum.input += input.tokens.input || 0;\n sum.output += input.tokens.output || 0;\n if (input.tokens.cacheRead) {\n sum.cacheRead = (sum.cacheRead || 0) + input.tokens.cacheRead;\n }\n if (input.tokens.cacheWrite) {\n sum.cacheWrite = (sum.cacheWrite || 0) + input.tokens.cacheWrite;\n }\n }\n if (input.data !== undefined) {\n step.data = snapshot(input.data);\n }\n steps.push(step);\n }\n\n /** Fill in fields of the current request's entry (tier, detail, followUp …). */\n note(fields: Partial<Pick<JournalEntry, 'tier' | 'detail' | 'followUp'>>): void {\n const entry = this.store.getStore();\n if (entry) {\n Object.assign(entry, fields);\n }\n }\n\n /** Record a tool call the model made for the current request. */\n tool(name: string, args: Record<string, unknown>, ms: number, result: unknown, error?: string): void {\n const entry = this.store.getStore();\n if (!entry) {\n return;\n }\n const item: JournalTool = { name, args: args || {}, ms };\n if (error) {\n item.error = error;\n } else {\n let text: string;\n try {\n text = typeof result === 'string' ? result : JSON.stringify(result);\n } catch {\n text = String(result);\n }\n item.result = text.length > RESULT_CHARS ? `${text.slice(0, RESULT_CHARS)}…` : text;\n }\n entry.tools.push(item);\n // Live view: show each tool call as it happens, a cloud round can take seconds.\n this.publish(entry);\n }\n\n /** Record which states a call reads or writes (MCP `get_states`/`set_state`/`set_states`, routines). */\n stateAccess(tool: string, args: Record<string, unknown> | undefined): void {\n const entry = this.store.getStore();\n if (!entry || !args) {\n return;\n }\n if (tool === 'get_states' && Array.isArray(args.ids)) {\n for (const id of args.ids) {\n if (typeof id === 'string' && !entry.read.includes(id)) {\n entry.read.push(id);\n }\n }\n } else if (tool === 'get_state' && typeof args.id === 'string') {\n if (!entry.read.includes(args.id)) {\n entry.read.push(args.id);\n }\n } else if (tool === 'set_state' && typeof args.id === 'string') {\n entry.written.push({ id: args.id, value: args.value });\n } else if (tool === 'set_states' && Array.isArray(args.states)) {\n for (const s of args.states as { id?: unknown; value?: unknown }[]) {\n if (typeof s?.id === 'string') {\n entry.written.push({ id: s.id, value: s.value });\n }\n }\n }\n }\n\n /** Write pending changes now (on unload). */\n async flush(): Promise<void> {\n if (this.saveTimer) {\n clearTimeout(this.saveTimer);\n this.saveTimer = null;\n }\n await this.save();\n }\n\n private publish(entry: JournalEntry): void {\n try {\n this.opts.onChange?.(entry);\n } catch {\n // a listener must never break the answer\n }\n }\n\n private prune(): void {\n const cutoff = Date.now() - this.opts.hours * 3600_000;\n let start = 0;\n while (start < this.entries.length && this.entries[start].ts < cutoff) {\n start++;\n }\n start = Math.max(start, this.entries.length - MAX_ENTRIES);\n if (start > 0) {\n for (const e of this.entries.slice(0, start)) {\n this.traces.delete(e.id);\n this.dirty.delete(e.id);\n }\n this.entries = this.entries.slice(start);\n }\n // Without a file the memory is all there is: keep as many as would be on disk.\n const keep = this.traceDir ? MEMORY_TRACES : MAX_TRACES;\n for (const id of this.traces.keys()) {\n if (this.traces.size <= keep) {\n break;\n }\n // Not written yet, or still being answered: needed until it is on disk.\n if ((!this.traceDir || !this.dirty.has(id)) && this.get(id)?.done !== false) {\n this.traces.delete(id);\n }\n }\n }\n\n private scheduleSave(): void {\n if (!this.opts.file || this.saveTimer) {\n return;\n }\n this.saveTimer = setTimeout(() => {\n this.saveTimer = null;\n void this.save();\n }, SAVE_DELAY_MS);\n this.saveTimer.unref?.();\n }\n\n private async save(): Promise<void> {\n if (!this.opts.file) {\n return;\n }\n try {\n await fs.mkdir(path.dirname(this.opts.file), { recursive: true });\n await fs.writeFile(this.opts.file, JSON.stringify(this.entries.filter(e => e.done)));\n } catch (e) {\n this.opts.log.warn(`Journal could not be saved: ${(e as Error).message}`);\n }\n await this.saveTraces();\n this.prune();\n }\n\n /** Write the traces that changed, and delete the files of requests that are no longer kept. */\n private async saveTraces(): Promise<void> {\n try {\n await fs.mkdir(this.traceDir, { recursive: true });\n for (const id of [...this.dirty]) {\n const steps = this.traces.get(id);\n this.dirty.delete(id);\n if (steps) {\n await fs.writeFile(path.join(this.traceDir, `${id}.json`), JSON.stringify({ id, steps }));\n }\n }\n const keep = new Set(this.entries.slice(-MAX_TRACES).map(e => e.id));\n for (const name of await fs.readdir(this.traceDir)) {\n if (name.endsWith('.json') && !keep.has(name.slice(0, -5))) {\n await fs.rm(path.join(this.traceDir, name), { force: true });\n }\n }\n } catch (e) {\n this.opts.log.warn(`Journal traces could not be saved: ${(e as Error).message}`);\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"journal.js","sourceRoot":"","sources":["../../src/lib/journal.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Conversation journal: one entry per request the assistant answered — where it came from (chat, script,\n * Telegram, which satellite, which wake word), which tier answered it (routine, rule-based NLU, local\n * model, cloud model), which tools the model called and which state ids were read or written along the\n * way. The admin chat shows it live, so one can check what a satellite heard and what was done about it.\n *\n * Tools and state accesses happen deep inside the pipeline (NLU executor, LLM tool loop, routine\n * executor). Instead of threading an entry object through all of them, the entry rides along in an\n * {@link AsyncLocalStorage}: {@link Journal.run} opens it, and {@link Journal.note}/{@link Journal.tool}/\n * {@link Journal.stateAccess} write to whatever entry the current call chain belongs to — and do nothing\n * outside of one (a trigger writing a state is not a conversation).\n *\n * Besides the entry, every request keeps a **trace**: the steps of its processing with their timing and\n * whatever explains them — the NLU's parse, the full prompt and every round of the model, tool arguments\n * and results in full, speech recognition and synthesis. Traces are too big to ride along in `journal.last`\n * and in `journal.json`, so the chat fetches one when a request is opened ({@link Journal.trace}); each is\n * kept in a file of its own and only for the newest {@link MAX_TRACES} requests.\n */\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { promises as fs } from 'node:fs';\nimport * as path from 'node:path';\nimport type { VadReport } from './voice/vad';\n\n/** How a request reached us. */\nexport type JournalTransport = 'chat' | 'script' | 'state' | 'telegram' | 'esphome' | 'udp' | 'native' | 'wyoming';\n\n/** Which stage of the pipeline produced the answer. */\nexport type JournalTier =\n | 'stop' // stop word silenced a ringing timer/alarm\n | 'askReply' // the utterance answered an open `askUser` question\n | 'routine'\n | 'nlu'\n | 'localLlm'\n | 'cloud'\n | 'none'; // nothing local understood it and there is no cloud provider\n\n/** Tokens a model call used (summed over the rounds of a request in {@link JournalEntry.tokens}). */\nexport interface JournalTokens {\n input: number;\n output: number;\n /** Prompt tokens served from the provider's prompt cache. */\n cacheRead?: number;\n /** Prompt tokens written to the provider's prompt cache (Anthropic). */\n cacheWrite?: number;\n}\n\n/** One step of how a request was processed — what the debug view of the admin chat shows. */\nexport interface JournalStep {\n /** Start of the step in ms after the request started (negative for speech recognition, which comes first). */\n t: number;\n /** Duration, for steps that took time. */\n ms?: number;\n /** Pipeline stage: request, stt, context, stop, askReply, routine, youtube, nlu, localLlm, cloud, tool, state, answer, tts, error. */\n stage: string;\n title: string;\n level?: 'warn' | 'error';\n tokens?: JournalTokens;\n /** Whatever explains the step (prompts, model responses, parse results) — a JSON snapshot. */\n data?: unknown;\n}\n\n/** What a caller records; the journal adds the timing. */\nexport type JournalStepInput = Omit<JournalStep, 't'>;\n\n/** Hook for library code that should not know the journal (the LLM agent). */\nexport type TraceFn = (step: JournalStepInput) => void;\n\nexport interface JournalTool {\n name: string;\n args: Record<string, unknown>;\n ms: number;\n /** Result, JSON, shortened. */\n result?: string;\n error?: string;\n}\n\nexport interface JournalEntry {\n id: string;\n /** Start of the answer, ms. */\n ts: number;\n /** False while the answer is still being produced. */\n done: boolean;\n /** Time to produce the answer (without STT/TTS). */\n ms?: number;\n source: string;\n transport: JournalTransport;\n satellite?: string;\n room?: string;\n /** Wake word the satellite reported; '' = the mic was re-opened for a follow-up, absent = unknown. */\n wakeWord?: string;\n /** Speech recognition time, for voice requests. */\n sttMs?: number;\n /** Length of the recorded utterance. */\n audioMs?: number;\n /** Speech synthesis time of the reply (voice requests, filled in after the answer). */\n ttsMs?: number;\n /** Length of the spoken reply. */\n replyAudioMs?: number;\n question: string;\n answer?: string;\n error?: string;\n tier?: JournalTier;\n /** Model name, routine name or NLU actions — whatever explains the tier. */\n detail?: string;\n tools: JournalTool[];\n /** State ids read to produce the answer. */\n read: string[];\n /** States written to produce the answer. */\n written: { id: string; value: unknown }[];\n /** The reply asks back, so a satellite re-opened its mic. */\n followUp?: boolean;\n /** Tokens of all model calls, when a cloud model answered. */\n tokens?: JournalTokens;\n}\n\n/** What the caller knows about a request before the pipeline starts. */\nexport interface JournalOrigin {\n transport?: JournalTransport;\n satellite?: string;\n room?: string;\n wakeWord?: string;\n sttMs?: number;\n audioMs?: number;\n /** Voice activity detection of the utterance (ESPHome) — trace only. */\n vad?: VadReport;\n /** Answered with the yes/no button of a satellite's screen instead of speech (ESPHome) — trace only. */\n button?: 'yes' | 'no';\n}\n\n/** What a transport reports after it synthesised the reply. */\nexport interface JournalSpeech {\n ms: number;\n audioMs: number;\n provider: string;\n fallback?: string;\n voice?: string;\n}\n\nexport interface JournalOptions {\n /** Retention in hours; 0 disables the journal entirely. */\n hours: number;\n /** File the journal is kept in across restarts ('' = memory only). */\n file: string;\n /** Called with every new or updated entry (drives the `journal.last` state the chat listens to). */\n onChange?: (entry: JournalEntry) => void;\n log: ioBroker.Logger;\n}\n\n/** Hard cap independent of the retention — a script asking every few seconds must not fill the disk. */\nconst MAX_ENTRIES = 3000;\nconst SAVE_DELAY_MS = 5000;\nconst RESULT_CHARS = 300;\n/** Requests whose trace is kept on disk. */\nexport const MAX_TRACES = 200;\n/** Traces held in memory (the newest ones — the ones somebody is about to open). */\nconst MEMORY_TRACES = 30;\n/** Steps per request; a tool loop gone wild must not grow one trace without end. */\nconst MAX_STEPS = 400;\n/** Longer strings are cut — enough for a system prompt with a few hundred devices. */\nconst STEP_STRING_CHARS = 100_000;\n/** A reply is matched to its synthesis only this long after the answer. */\nconst SPEECH_WINDOW_MS = 120_000;\n/** Entry ids as {@link Journal.run} makes them — checked before one becomes a file name. */\nconst ID_RE = /^[a-z0-9-]+$/;\n\n/** Directory the traces of a journal file are kept in. */\nexport function traceDirFor(file: string): string {\n return path.join(path.dirname(file), 'journal-trace');\n}\n\n/**\n * JSON snapshot of a step's data — taken when it is recorded, because the objects go on changing (the\n * model's message list grows, cache markers move). Long strings are cut and buffers summarised.\n */\nexport function snapshot(value: unknown): unknown {\n if (value === undefined) {\n return undefined;\n }\n try {\n const text = JSON.stringify(value, (_key, v: unknown) => {\n if (typeof v === 'string' && v.length > STEP_STRING_CHARS) {\n return `${v.slice(0, STEP_STRING_CHARS)}… [+${v.length - STEP_STRING_CHARS} chars]`;\n }\n if (typeof v === 'bigint') {\n return v.toString();\n }\n // Buffer.toJSON() has already run when the replacer sees it.\n const b = v as { type?: unknown; data?: unknown } | null;\n if (b && typeof b === 'object' && b.type === 'Buffer' && Array.isArray(b.data)) {\n return `<${b.data.length} bytes>`;\n }\n return v;\n });\n return text === undefined ? undefined : JSON.parse(text);\n } catch (e) {\n return `<not serializable: ${(e as Error).message}>`;\n }\n}\n\n/** Everything an error carries that helps — an API error's status and body, the stack. */\nexport function errorInfo(e: unknown): Record<string, unknown> {\n if (!(e instanceof Error)) {\n return { message: String(e) };\n }\n const info: Record<string, unknown> = { name: e.name, message: e.message };\n for (const key of ['status', 'code', 'type', 'param', 'error', 'request_id', 'requestID']) {\n const v = (e as unknown as Record<string, unknown>)[key];\n if (v !== undefined && v !== null) {\n info[key] = v;\n }\n }\n if (e.stack) {\n info.stack = e.stack;\n }\n return info;\n}\n\n/** Derive the transport from a request source when the caller did not say. */\nexport function transportForSource(source: string): JournalTransport {\n if (source === 'chat') {\n return 'chat';\n }\n if (source === '') {\n return 'state';\n }\n if (source === 'wyoming') {\n return 'wyoming';\n }\n if (source.startsWith('telegram:')) {\n return 'telegram';\n }\n return 'script';\n}\n\nexport class Journal {\n private entries: JournalEntry[] = [];\n private readonly store = new AsyncLocalStorage<JournalEntry>();\n private saveTimer: NodeJS.Timeout | null = null;\n private counter = 0;\n /** Steps of the newest requests; older ones are read back from their file. */\n private readonly traces = new Map<string, JournalStep[]>();\n /** Traces changed since they were last written. */\n private readonly dirty = new Set<string>();\n /** LLM tool calls in progress per request: a state access inside one belongs to that tool's step. */\n private readonly toolDepth = new WeakMap<JournalEntry, number>();\n private readonly traceDir: string;\n\n constructor(private readonly opts: JournalOptions) {\n this.traceDir = opts.file ? traceDirFor(opts.file) : '';\n }\n\n get enabled(): boolean {\n return this.opts.hours > 0;\n }\n\n /** Load the entries of the last run (silently starts empty when there are none). */\n async load(): Promise<void> {\n if (!this.enabled || !this.opts.file) {\n return;\n }\n try {\n const raw = JSON.parse(await fs.readFile(this.opts.file, 'utf8')) as unknown;\n if (Array.isArray(raw)) {\n // An entry still open at shutdown never got its answer.\n this.entries = (raw as JournalEntry[])\n .filter(e => e && typeof e.id === 'string' && typeof e.ts === 'number')\n .map(e => (e.done ? e : { ...e, done: true, error: e.error || 'interrupted' }));\n this.prune();\n }\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code !== 'ENOENT') {\n this.opts.log.warn(`Journal could not be read: ${(e as Error).message}`);\n }\n }\n }\n\n /** Entries newer than `since` (ms), oldest first. */\n list(since = 0): JournalEntry[] {\n this.prune();\n return this.entries.filter(e => e.ts > since);\n }\n\n clear(): void {\n this.entries = [];\n this.traces.clear();\n this.dirty.clear();\n this.scheduleSave();\n }\n\n /** The entry of a request (the chat asks for it together with its trace). */\n get(id: string): JournalEntry | undefined {\n return this.entries.find(e => e.id === id);\n }\n\n /** The processing steps of a request, or null when none were kept (too old, or from an older version). */\n async trace(id: string): Promise<JournalStep[] | null> {\n if (!ID_RE.test(id) || !this.get(id)) {\n return null;\n }\n const steps = this.traces.get(id);\n if (steps) {\n return steps;\n }\n if (!this.traceDir) {\n return null;\n }\n try {\n const raw = JSON.parse(await fs.readFile(path.join(this.traceDir, `${id}.json`), 'utf8')) as {\n steps?: unknown;\n };\n return Array.isArray(raw?.steps) ? (raw.steps as JournalStep[]) : null;\n } catch {\n return null;\n }\n }\n\n /**\n * Run `fn` as one journalled request. The entry is published once when the request starts (so the\n * chat shows the question while the answer is still being worked out) and once when it is finished.\n */\n async run(question: string, source: string, origin: JournalOrigin, fn: () => Promise<string>): Promise<string> {\n if (!this.enabled) {\n return fn();\n }\n const entry: JournalEntry = {\n id: `${Date.now().toString(36)}-${(this.counter++).toString(36)}`,\n ts: Date.now(),\n done: false,\n source,\n transport: origin.transport || transportForSource(source),\n question,\n tools: [],\n read: [],\n written: [],\n };\n for (const key of ['satellite', 'room', 'wakeWord', 'sttMs', 'audioMs'] as const) {\n if (origin[key] !== undefined && origin[key] !== '') {\n (entry as unknown as Record<string, unknown>)[key] = origin[key];\n }\n }\n // A satellite that re-opened its mic reports no wake word — say so instead of \"unknown\".\n if (origin.wakeWord === '') {\n entry.wakeWord = '';\n }\n this.entries.push(entry);\n this.traces.set(entry.id, []);\n this.addStep(entry, {\n stage: 'request',\n title: `Request from ${source || entry.transport} (${entry.transport})`,\n data: { question, source, origin },\n });\n this.publish(entry);\n try {\n const answer = await this.store.run(entry, fn);\n entry.answer = answer;\n return answer;\n } catch (e) {\n entry.error = (e as Error).message;\n this.addStep(entry, { stage: 'error', level: 'error', title: entry.error, data: errorInfo(e) });\n throw e;\n } finally {\n entry.done = true;\n entry.ms = Date.now() - entry.ts;\n this.dirty.add(entry.id);\n this.publish(entry);\n this.prune();\n this.scheduleSave();\n }\n }\n\n /** Add a step to the current request's trace (nothing outside of one). */\n step(input: JournalStepInput): void {\n const entry = this.store.getStore();\n if (entry) {\n this.addStep(entry, input);\n }\n }\n\n /**\n * Run an LLM tool as part of the current request: the call lands in the entry's tool list and, with the\n * full arguments and result, in the trace.\n */\n async toolCall<T>(name: string, args: Record<string, unknown>, fn: () => Promise<T>): Promise<T> {\n const entry = this.store.getStore();\n if (!entry) {\n return fn();\n }\n this.toolDepth.set(entry, (this.toolDepth.get(entry) || 0) + 1);\n const start = Date.now();\n try {\n const result = await fn();\n const ms = Date.now() - start;\n this.addStep(entry, { stage: 'tool', title: `Tool ${name}`, ms, data: { args, result } });\n this.tool(name, args, ms, result);\n return result;\n } catch (e) {\n const ms = Date.now() - start;\n const message = (e as Error).message;\n this.addStep(entry, {\n stage: 'tool',\n level: 'error',\n title: `Tool ${name} failed: ${message}`,\n ms,\n data: { args, error: errorInfo(e) },\n });\n this.tool(name, args, ms, undefined, message);\n throw e;\n } finally {\n this.toolDepth.set(entry, (this.toolDepth.get(entry) || 1) - 1);\n }\n }\n\n /**\n * Run a state call (MCP `get_states`/`set_state`/…) as part of the current request: the ids land in the\n * entry's read/written lists, and — unless an LLM tool made the call, whose step already shows it — the\n * call with its result in the trace.\n */\n async stateCall<T>(name: string, args: Record<string, unknown> | undefined, fn: () => Promise<T>): Promise<T> {\n const entry = this.store.getStore();\n if (!entry) {\n return fn();\n }\n this.stateAccess(name, args);\n if (this.toolDepth.get(entry)) {\n return fn();\n }\n const start = Date.now();\n try {\n const result = await fn();\n this.addStep(entry, { stage: 'state', title: name, ms: Date.now() - start, data: { args, result } });\n return result;\n } catch (e) {\n this.addStep(entry, {\n stage: 'state',\n level: 'error',\n title: `${name} failed: ${(e as Error).message}`,\n ms: Date.now() - start,\n data: { args, error: errorInfo(e) },\n });\n throw e;\n }\n }\n\n /**\n * A transport synthesised the reply to the latest request of `source`. That happens after the answer,\n * outside of the request, so the entry is found by its source.\n */\n spoken(source: string, speech: JournalSpeech): void {\n const now = Date.now();\n for (let i = this.entries.length - 1; i >= 0; i--) {\n const entry = this.entries[i];\n if (entry.source !== source) {\n continue;\n }\n if (!entry.done || entry.ttsMs !== undefined || now - entry.ts - (entry.ms || 0) > SPEECH_WINDOW_MS) {\n return;\n }\n entry.ttsMs = speech.ms;\n entry.replyAudioMs = Math.round(speech.audioMs);\n const voice = speech.voice ? ` (${speech.voice})` : '';\n this.addStep(entry, {\n stage: 'tts',\n title: `Speech synthesis: ${speech.provider}${voice}, ${(speech.audioMs / 1000).toFixed(1)} s of audio`,\n ms: speech.ms,\n data: speech,\n });\n this.dirty.add(entry.id);\n this.publish(entry);\n this.scheduleSave();\n return;\n }\n }\n\n private addStep(entry: JournalEntry, input: JournalStepInput): void {\n const steps = this.traces.get(entry.id);\n if (!steps || steps.length > MAX_STEPS) {\n return;\n }\n if (steps.length === MAX_STEPS) {\n steps.push({\n t: Date.now() - entry.ts,\n stage: 'error',\n level: 'warn',\n title: `More than ${MAX_STEPS} steps — the rest is not recorded`,\n });\n return;\n }\n const step: JournalStep = {\n t: Date.now() - entry.ts - (input.ms || 0),\n stage: input.stage,\n title: input.title,\n };\n if (input.ms !== undefined) {\n step.ms = input.ms;\n }\n if (input.level) {\n step.level = input.level;\n }\n if (input.tokens) {\n step.tokens = input.tokens;\n const sum = (entry.tokens ||= { input: 0, output: 0 });\n sum.input += input.tokens.input || 0;\n sum.output += input.tokens.output || 0;\n if (input.tokens.cacheRead) {\n sum.cacheRead = (sum.cacheRead || 0) + input.tokens.cacheRead;\n }\n if (input.tokens.cacheWrite) {\n sum.cacheWrite = (sum.cacheWrite || 0) + input.tokens.cacheWrite;\n }\n }\n if (input.data !== undefined) {\n step.data = snapshot(input.data);\n }\n steps.push(step);\n }\n\n /** Fill in fields of the current request's entry (tier, detail, followUp …). */\n note(fields: Partial<Pick<JournalEntry, 'tier' | 'detail' | 'followUp'>>): void {\n const entry = this.store.getStore();\n if (entry) {\n Object.assign(entry, fields);\n }\n }\n\n /** Record a tool call the model made for the current request. */\n tool(name: string, args: Record<string, unknown>, ms: number, result: unknown, error?: string): void {\n const entry = this.store.getStore();\n if (!entry) {\n return;\n }\n const item: JournalTool = { name, args: args || {}, ms };\n if (error) {\n item.error = error;\n } else {\n let text: string;\n try {\n text = typeof result === 'string' ? result : JSON.stringify(result);\n } catch {\n text = String(result);\n }\n item.result = text.length > RESULT_CHARS ? `${text.slice(0, RESULT_CHARS)}…` : text;\n }\n entry.tools.push(item);\n // Live view: show each tool call as it happens, a cloud round can take seconds.\n this.publish(entry);\n }\n\n /** Record which states a call reads or writes (MCP `get_states`/`set_state`/`set_states`, routines). */\n stateAccess(tool: string, args: Record<string, unknown> | undefined): void {\n const entry = this.store.getStore();\n if (!entry || !args) {\n return;\n }\n if (tool === 'get_states' && Array.isArray(args.ids)) {\n for (const id of args.ids) {\n if (typeof id === 'string' && !entry.read.includes(id)) {\n entry.read.push(id);\n }\n }\n } else if (tool === 'get_state' && typeof args.id === 'string') {\n if (!entry.read.includes(args.id)) {\n entry.read.push(args.id);\n }\n } else if (tool === 'set_state' && typeof args.id === 'string') {\n entry.written.push({ id: args.id, value: args.value });\n } else if (tool === 'set_states' && Array.isArray(args.states)) {\n for (const s of args.states as { id?: unknown; value?: unknown }[]) {\n if (typeof s?.id === 'string') {\n entry.written.push({ id: s.id, value: s.value });\n }\n }\n }\n }\n\n /** Write pending changes now (on unload). */\n async flush(): Promise<void> {\n if (this.saveTimer) {\n clearTimeout(this.saveTimer);\n this.saveTimer = null;\n }\n await this.save();\n }\n\n private publish(entry: JournalEntry): void {\n try {\n this.opts.onChange?.(entry);\n } catch {\n // a listener must never break the answer\n }\n }\n\n private prune(): void {\n const cutoff = Date.now() - this.opts.hours * 3600_000;\n let start = 0;\n while (start < this.entries.length && this.entries[start].ts < cutoff) {\n start++;\n }\n start = Math.max(start, this.entries.length - MAX_ENTRIES);\n if (start > 0) {\n for (const e of this.entries.slice(0, start)) {\n this.traces.delete(e.id);\n this.dirty.delete(e.id);\n }\n this.entries = this.entries.slice(start);\n }\n // Without a file the memory is all there is: keep as many as would be on disk.\n const keep = this.traceDir ? MEMORY_TRACES : MAX_TRACES;\n for (const id of this.traces.keys()) {\n if (this.traces.size <= keep) {\n break;\n }\n // Not written yet, or still being answered: needed until it is on disk.\n if ((!this.traceDir || !this.dirty.has(id)) && this.get(id)?.done !== false) {\n this.traces.delete(id);\n }\n }\n }\n\n private scheduleSave(): void {\n if (!this.opts.file || this.saveTimer) {\n return;\n }\n this.saveTimer = setTimeout(() => {\n this.saveTimer = null;\n void this.save();\n }, SAVE_DELAY_MS);\n this.saveTimer.unref?.();\n }\n\n private async save(): Promise<void> {\n if (!this.opts.file) {\n return;\n }\n try {\n await fs.mkdir(path.dirname(this.opts.file), { recursive: true });\n await fs.writeFile(this.opts.file, JSON.stringify(this.entries.filter(e => e.done)));\n } catch (e) {\n this.opts.log.warn(`Journal could not be saved: ${(e as Error).message}`);\n }\n await this.saveTraces();\n this.prune();\n }\n\n /** Write the traces that changed, and delete the files of requests that are no longer kept. */\n private async saveTraces(): Promise<void> {\n try {\n await fs.mkdir(this.traceDir, { recursive: true });\n for (const id of [...this.dirty]) {\n const steps = this.traces.get(id);\n this.dirty.delete(id);\n if (steps) {\n await fs.writeFile(path.join(this.traceDir, `${id}.json`), JSON.stringify({ id, steps }));\n }\n }\n const keep = new Set(this.entries.slice(-MAX_TRACES).map(e => e.id));\n for (const name of await fs.readdir(this.traceDir)) {\n if (name.endsWith('.json') && !keep.has(name.slice(0, -5))) {\n await fs.rm(path.join(this.traceDir, name), { force: true });\n }\n }\n } catch (e) {\n this.opts.log.warn(`Journal traces could not be saved: ${(e as Error).message}`);\n }\n }\n}\n"]}
|
package/build/lib/llm.js
CHANGED
|
@@ -6,9 +6,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.LlmAgent = exports.OPENROUTER_HEADERS = exports.PROVIDER_PRESETS = void 0;
|
|
7
7
|
exports.resolveProvider = resolveProvider;
|
|
8
8
|
exports.effectiveApiKey = effectiveApiKey;
|
|
9
|
+
exports.openAiUserContent = openAiUserContent;
|
|
10
|
+
exports.anthropicUserContent = anthropicUserContent;
|
|
9
11
|
const openai_1 = __importDefault(require("openai"));
|
|
10
12
|
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
11
13
|
const journal_1 = require("./journal");
|
|
14
|
+
const media_1 = require("./media");
|
|
12
15
|
/** Per-provider SDK, endpoint and default model. Gemini/DeepSeek expose OpenAI-compatible endpoints. */
|
|
13
16
|
exports.PROVIDER_PRESETS = {
|
|
14
17
|
openai: { sdk: 'openai', baseUrl: '', defaultModel: 'gpt-4o-mini' },
|
|
@@ -147,10 +150,11 @@ class LlmAgent {
|
|
|
147
150
|
/**
|
|
148
151
|
* `systemPrompt` overrides the configured one for this call (e.g. to inject a device list).
|
|
149
152
|
* `history` are prior turns (user/assistant) prepended so the model can resolve follow-ups.
|
|
153
|
+
* `images` go with the question (a photo from Telegram); the model must be able to see.
|
|
150
154
|
*/
|
|
151
|
-
async ask(question, systemPrompt, history = []) {
|
|
155
|
+
async ask(question, systemPrompt, history = [], images = []) {
|
|
152
156
|
await this.ensureModel();
|
|
153
|
-
this.log.debug(`llm ask [${this.provider}/${this.model}] (${history.length} history): ${question}`);
|
|
157
|
+
this.log.debug(`llm ask [${this.provider}/${this.model}] (${history.length} history${images.length ? `, ${images.length} picture(s)` : ''}): ${question}`);
|
|
154
158
|
const started = Date.now();
|
|
155
159
|
const sys = systemPrompt ?? this.systemPrompt;
|
|
156
160
|
this.trace?.({
|
|
@@ -165,11 +169,12 @@ class LlmAgent {
|
|
|
165
169
|
maxRounds: this.maxRounds,
|
|
166
170
|
system: sys,
|
|
167
171
|
tools: this.tools.map(t => t.name),
|
|
172
|
+
images: images.length ? images.map(i => i.mimeType) : undefined,
|
|
168
173
|
},
|
|
169
174
|
});
|
|
170
175
|
const answer = this.provider === 'anthropic'
|
|
171
|
-
? await this.askAnthropic(question, sys, history)
|
|
172
|
-
: await this.askOpenAI(question, sys, history);
|
|
176
|
+
? await this.askAnthropic(question, sys, history, images)
|
|
177
|
+
: await this.askOpenAI(question, sys, history, images);
|
|
173
178
|
this.log.debug(`llm answer (${Date.now() - started} ms): ${answer}`);
|
|
174
179
|
return answer;
|
|
175
180
|
}
|
|
@@ -181,7 +186,8 @@ class LlmAgent {
|
|
|
181
186
|
const max = this.log.level === 'silly' ? 8000 : 1500;
|
|
182
187
|
let text;
|
|
183
188
|
try {
|
|
184
|
-
|
|
189
|
+
// base64 of a picture would fill the whole excerpt
|
|
190
|
+
text = JSON.stringify((0, media_1.redactImages)(payload));
|
|
185
191
|
}
|
|
186
192
|
catch {
|
|
187
193
|
text = String(payload);
|
|
@@ -355,7 +361,7 @@ class LlmAgent {
|
|
|
355
361
|
level: 'error',
|
|
356
362
|
title: `Round ${round + 1} failed: ${e.message}`,
|
|
357
363
|
ms: Date.now() - start,
|
|
358
|
-
data: { sent, error: (0, journal_1.errorInfo)(e) },
|
|
364
|
+
data: { sent: (0, media_1.redactImages)(sent), error: (0, journal_1.errorInfo)(e) },
|
|
359
365
|
});
|
|
360
366
|
throw e;
|
|
361
367
|
}
|
|
@@ -380,7 +386,7 @@ class LlmAgent {
|
|
|
380
386
|
}
|
|
381
387
|
}
|
|
382
388
|
// ── OpenAI (Chat Completions + function tools) ──────────────────────────
|
|
383
|
-
async askOpenAI(question, systemPrompt, history = []) {
|
|
389
|
+
async askOpenAI(question, systemPrompt, history = [], images = []) {
|
|
384
390
|
const client = this.openai;
|
|
385
391
|
const tools = this.tools.map(t => ({
|
|
386
392
|
type: 'function',
|
|
@@ -393,7 +399,7 @@ class LlmAgent {
|
|
|
393
399
|
for (const t of history) {
|
|
394
400
|
messages.push({ role: t.role, content: t.content });
|
|
395
401
|
}
|
|
396
|
-
messages.push({ role: 'user', content: question });
|
|
402
|
+
messages.push({ role: 'user', content: openAiUserContent(question, images) });
|
|
397
403
|
// Messages the trace has shown already: the system prompt is part of the opening step.
|
|
398
404
|
let shown = systemPrompt ? 1 : 0;
|
|
399
405
|
for (let round = 0; round < this.maxRounds; round++) {
|
|
@@ -416,7 +422,7 @@ class LlmAgent {
|
|
|
416
422
|
ms,
|
|
417
423
|
tokens: tokensOf(resp.usage),
|
|
418
424
|
data: {
|
|
419
|
-
sent,
|
|
425
|
+
sent: (0, media_1.redactImages)(sent),
|
|
420
426
|
response: { finish_reason: resp.choices[0].finish_reason, message: msg },
|
|
421
427
|
model: resp.model,
|
|
422
428
|
usage: resp.usage,
|
|
@@ -468,7 +474,7 @@ class LlmAgent {
|
|
|
468
474
|
}
|
|
469
475
|
}
|
|
470
476
|
// ── Anthropic (Messages API + tool_use) ─────────────────────────────────
|
|
471
|
-
async askAnthropic(question, systemPrompt, history = []) {
|
|
477
|
+
async askAnthropic(question, systemPrompt, history = [], images = []) {
|
|
472
478
|
const client = this.anthropic;
|
|
473
479
|
const tools = this.tools.map(t => ({
|
|
474
480
|
name: t.name,
|
|
@@ -485,7 +491,7 @@ class LlmAgent {
|
|
|
485
491
|
: undefined;
|
|
486
492
|
const messages = [
|
|
487
493
|
...history.map(t => ({ role: t.role, content: t.content })),
|
|
488
|
-
{ role: 'user', content: question },
|
|
494
|
+
{ role: 'user', content: anthropicUserContent(question, images) },
|
|
489
495
|
];
|
|
490
496
|
// Messages the trace has shown already (the system prompt is part of the opening step).
|
|
491
497
|
let shown = 0;
|
|
@@ -511,7 +517,7 @@ class LlmAgent {
|
|
|
511
517
|
ms,
|
|
512
518
|
tokens: tokensOf(resp.usage),
|
|
513
519
|
data: {
|
|
514
|
-
sent,
|
|
520
|
+
sent: (0, media_1.redactImages)(sent),
|
|
515
521
|
response: { stop_reason: resp.stop_reason, content: resp.content },
|
|
516
522
|
model: resp.model,
|
|
517
523
|
usage: resp.usage,
|
|
@@ -537,4 +543,36 @@ class LlmAgent {
|
|
|
537
543
|
}
|
|
538
544
|
}
|
|
539
545
|
exports.LlmAgent = LlmAgent;
|
|
546
|
+
/**
|
|
547
|
+
* The user turn of the OpenAI Chat Completions API (also Gemini, OpenRouter …): the plain question, or text and
|
|
548
|
+
* pictures as data URLs.
|
|
549
|
+
*
|
|
550
|
+
* @param question the question
|
|
551
|
+
* @param images pictures that came with it
|
|
552
|
+
*/
|
|
553
|
+
function openAiUserContent(question, images = []) {
|
|
554
|
+
if (!images.length) {
|
|
555
|
+
return question;
|
|
556
|
+
}
|
|
557
|
+
return [
|
|
558
|
+
{ type: 'text', text: question },
|
|
559
|
+
...images.map(i => ({ type: 'image_url', image_url: { url: `data:${i.mimeType};base64,${i.data}` } })),
|
|
560
|
+
];
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* The user turn of the Anthropic Messages API: the plain question, or the pictures first and then the text (what
|
|
564
|
+
* Anthropic recommends).
|
|
565
|
+
*
|
|
566
|
+
* @param question the question
|
|
567
|
+
* @param images pictures that came with it
|
|
568
|
+
*/
|
|
569
|
+
function anthropicUserContent(question, images = []) {
|
|
570
|
+
if (!images.length) {
|
|
571
|
+
return question;
|
|
572
|
+
}
|
|
573
|
+
return [
|
|
574
|
+
...images.map(i => ({ type: 'image', source: { type: 'base64', media_type: i.mimeType, data: i.data } })),
|
|
575
|
+
{ type: 'text', text: question },
|
|
576
|
+
];
|
|
577
|
+
}
|
|
540
578
|
//# sourceMappingURL=llm.js.map
|
package/build/lib/llm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm.js","sourceRoot":"","sources":["../../src/lib/llm.ts"],"names":[],"mappings":"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","sourcesContent":["import OpenAI from 'openai';\nimport Anthropic from '@anthropic-ai/sdk';\nimport type { Tool } from './tools';\nimport { errorInfo, type JournalTokens, type TraceFn } from './journal';\n\n/** All selectable LLM providers. gemini/deepseek/openrouter/custom all use the OpenAI-compatible SDK path. */\nexport type Provider = 'openai' | 'anthropic' | 'gemini' | 'deepseek' | 'openrouter' | 'custom';\n\ninterface ProviderPreset {\n /** Which vendor SDK to use. */\n sdk: 'openai' | 'anthropic';\n /** Fixed API base URL ('' = SDK default; for 'custom' the user's baseUrl is used instead). */\n baseUrl: string;\n /** Default model when none is configured. */\n defaultModel: string;\n}\n\n/** Per-provider SDK, endpoint and default model. Gemini/DeepSeek expose OpenAI-compatible endpoints. */\nexport const PROVIDER_PRESETS: Record<Provider, ProviderPreset> = {\n openai: { sdk: 'openai', baseUrl: '', defaultModel: 'gpt-4o-mini' },\n anthropic: { sdk: 'anthropic', baseUrl: '', defaultModel: 'claude-sonnet-4-6' },\n gemini: {\n sdk: 'openai',\n baseUrl: 'https://generativelanguage.googleapis.com/v1beta/openai/',\n defaultModel: 'gemini-2.0-flash',\n },\n deepseek: { sdk: 'openai', baseUrl: 'https://api.deepseek.com', defaultModel: 'deepseek-chat' },\n // OpenRouter names a model with its vendor in front\n openrouter: { sdk: 'openai', baseUrl: 'https://openrouter.ai/api/v1', defaultModel: 'openai/gpt-4o-mini' },\n custom: { sdk: 'openai', baseUrl: '', defaultModel: '' },\n};\n\n/** OpenRouter shows the calling app in its statistics (same attribution as `@iobroker/ai-core`) */\nexport const OPENROUTER_HEADERS = { 'HTTP-Referer': 'https://www.iobroker.net', 'X-Title': 'ioBroker' };\n\n/** Resolve the effective SDK, base URL and default model for a provider (custom uses `configBaseUrl`). */\nexport function resolveProvider(\n provider: string | undefined,\n configBaseUrl?: string,\n): { sdk: 'openai' | 'anthropic'; baseUrl: string; defaultModel: string } {\n const preset = PROVIDER_PRESETS[(provider as Provider) || 'openai'] || PROVIDER_PRESETS.openai;\n return {\n sdk: preset.sdk,\n baseUrl: provider === 'custom' ? configBaseUrl || '' : preset.baseUrl,\n defaultModel: preset.defaultModel,\n };\n}\n\n/**\n * API key to use for `provider`: a local OpenAI-compatible server (the `custom` provider) usually has no\n * key at all, but the OpenAI SDK refuses to start without one — a placeholder goes into the\n * `Authorization` header, which such servers ignore. Empty for every other provider without a key.\n */\nexport function effectiveApiKey(provider: string | undefined, apiKey: string): string {\n return apiKey || (provider === 'custom' ? 'no-key' : '');\n}\n\nexport interface LlmAgentOptions {\n provider: Provider;\n apiKey: string;\n model: string;\n baseUrl?: string;\n systemPrompt?: string;\n maxTokens?: number;\n tools: Tool[];\n log: ioBroker.Logger;\n /** Records the prompt and every round of {@link LlmAgent.ask} (the request journal's debug view). */\n trace?: TraceFn;\n}\n\n/** Normalised token counts of an OpenAI-compatible or Anthropic `usage` block. */\nfunction tokensOf(usage: any): JournalTokens | undefined {\n if (!usage || typeof usage !== 'object') {\n return undefined;\n }\n if (usage.input_tokens !== undefined) {\n return {\n input: Number(usage.input_tokens) || 0,\n output: Number(usage.output_tokens) || 0,\n cacheRead: Number(usage.cache_read_input_tokens) || undefined,\n cacheWrite: Number(usage.cache_creation_input_tokens) || undefined,\n };\n }\n return {\n input: Number(usage.prompt_tokens) || 0,\n output: Number(usage.completion_tokens) || 0,\n cacheRead: Number(usage.prompt_tokens_details?.cached_tokens) || undefined,\n };\n}\n\n/** \"Round 2 → get_states, set_state\" or \"Round 3 → answer\" — the title of a round in the trace. */\nfunction roundTitle(round: number, toolNames: string[]): string {\n return `Round ${round + 1} → ${toolNames.length ? toolNames.join(', ') : 'answer'}`;\n}\n\n/**\n * Provider-agnostic LLM agent with tool-calling.\n *\n * `ask(question)` runs the model, executes any tool calls it requests, feeds the\n * results back, and repeats until the model returns a final text answer.\n *\n * The request/response plumbing of the vendor SDKs is intentionally loosely typed\n * (`any`) so the code stays robust across minor SDK type changes; the public API\n * (options, `ask`, tools) is fully typed.\n */\nexport class LlmAgent {\n public readonly provider: 'openai' | 'anthropic';\n /** Configured model; empty means \"ask the server\" (see {@link ensureModel}). */\n private configuredModel: string;\n private modelLookup: Promise<string> | null = null;\n\n private readonly systemPrompt: string;\n private readonly maxTokens: number;\n private readonly tools: Tool[];\n private readonly toolMap: Record<string, Tool>;\n private readonly log: ioBroker.Logger;\n private readonly maxRounds = 6; // safety cap against tool-call loops\n /** Configured provider (gemini, deepseek, custom … — {@link provider} is only the SDK). */\n private readonly providerName: string;\n private readonly baseUrl: string;\n private readonly trace?: TraceFn;\n\n private readonly openai?: OpenAI;\n private readonly anthropic?: Anthropic;\n\n constructor(opts: LlmAgentOptions) {\n this.provider = opts.provider === 'anthropic' ? 'anthropic' : 'openai';\n this.providerName = opts.provider || this.provider;\n this.baseUrl = opts.baseUrl || '';\n this.trace = opts.trace;\n this.configuredModel = (opts.model || '').trim();\n this.systemPrompt = opts.systemPrompt || '';\n this.maxTokens = opts.maxTokens || 1024;\n this.tools = opts.tools || [];\n this.log = opts.log;\n this.toolMap = Object.fromEntries(this.tools.map(t => [t.name, t]));\n\n if (this.provider === 'anthropic') {\n this.anthropic = new Anthropic({ apiKey: opts.apiKey, baseURL: opts.baseUrl || undefined });\n } else {\n this.openai = new OpenAI({\n apiKey: opts.apiKey,\n baseURL: opts.baseUrl || undefined,\n defaultHeaders: opts.provider === 'openrouter' ? OPENROUTER_HEADERS : undefined,\n });\n }\n }\n\n /** The model requests go to — empty until {@link ensureModel} has looked it up, if none was configured. */\n public get model(): string {\n return this.configuredModel;\n }\n\n /**\n * Make sure there is a model to send. A local OpenAI-compatible server (Ollama, LM Studio, llama.cpp\n * server, …) is usually set up with the `custom` provider and no model typed in — and sending\n * `\"model\": \"\"` gets a 400 from the server (\"unknown provider for model\" and the like) that says\n * nothing about the real cause. So an empty model is looked up once from the server's `/models` list\n * (with one model loaded, that is the one), and a server that lists nothing gets a clear error.\n */\n private async ensureModel(): Promise<string> {\n if (this.configuredModel) {\n return this.configuredModel;\n }\n this.modelLookup ||= this.listModels()\n .then(models => {\n if (!models.length) {\n throw new Error('no model configured and the server lists none');\n }\n this.configuredModel = models[0];\n this.log.info(\n `No model configured — using \"${models[0]}\" from the server${\n models.length > 1 ? ` (available: ${models.join(', ')})` : ''\n }. Select a model in the adapter settings to choose another.`,\n );\n return models[0];\n })\n .catch(e => {\n this.modelLookup = null; // let the next request try again (server may still be starting)\n throw new Error(\n `No model configured — select one in the adapter settings (lookup failed: ${(e as Error).message})`,\n );\n });\n return this.modelLookup;\n }\n\n /**\n * `systemPrompt` overrides the configured one for this call (e.g. to inject a device list).\n * `history` are prior turns (user/assistant) prepended so the model can resolve follow-ups.\n */\n public async ask(\n question: string,\n systemPrompt?: string,\n history: { role: 'user' | 'assistant'; content: string }[] = [],\n ): Promise<string> {\n await this.ensureModel();\n this.log.debug(`llm ask [${this.provider}/${this.model}] (${history.length} history): ${question}`);\n const started = Date.now();\n const sys = systemPrompt ?? this.systemPrompt;\n this.trace?.({\n stage: 'cloud',\n title: `${this.providerName} · ${this.model}: ${this.tools.length} tools, ${history.length} earlier turn(s)`,\n data: {\n provider: this.providerName,\n sdk: this.provider,\n model: this.model,\n baseUrl: this.baseUrl || undefined,\n maxTokens: this.maxTokens,\n maxRounds: this.maxRounds,\n system: sys,\n tools: this.tools.map(t => t.name),\n },\n });\n const answer =\n this.provider === 'anthropic'\n ? await this.askAnthropic(question, sys, history)\n : await this.askOpenAI(question, sys, history);\n this.log.debug(`llm answer (${Date.now() - started} ms): ${answer}`);\n return answer;\n }\n\n /** Compact, truncated JSON for debug logging of LLM requests/responses. */\n private dump(label: string, payload: unknown): void {\n if (this.log.level !== 'silly' && this.log.level !== 'debug') {\n return; // avoid stringifying large payloads when debug logging is off\n }\n const max = this.log.level === 'silly' ? 8000 : 1500;\n let text: string;\n try {\n text = JSON.stringify(payload);\n } catch {\n text = String(payload);\n }\n this.log.debug(`${label}: ${text.length > max ? `${text.slice(0, max)}…[${text.length}]` : text}`);\n }\n\n /**\n * One prompt in, one line out — no tools, no history, no system prompt. The small side jobs\n * (translate/rephrase/classify) all run through here.\n */\n private async complete(prompt: string, maxTokens: number): Promise<string> {\n await this.ensureModel();\n if (this.provider === 'anthropic') {\n const client = this.anthropic as Anthropic;\n const resp: any = await client.messages.create({\n model: this.model,\n max_tokens: maxTokens,\n messages: [{ role: 'user', content: prompt }],\n });\n return resp.content\n .filter((b: any) => b.type === 'text')\n .map((b: any) => b.text)\n .join('')\n .trim();\n }\n const client = this.openai as OpenAI;\n const resp: any = await client.chat.completions.create({\n model: this.model,\n max_tokens: maxTokens,\n messages: [{ role: 'user', content: prompt }],\n });\n return String(resp.choices[0].message.content || '').trim();\n }\n\n /** Translate a short device/room name into `targetLanguage` (single completion, no tools). */\n public async translate(text: string, targetLanguage: string): Promise<string> {\n const trimmed = (text || '').trim();\n if (!trimmed) {\n return '';\n }\n return this.complete(\n `Translate the following smart-home device or room name into ${targetLanguage}. ` +\n `Reply with ONLY the translation — no quotes, no punctuation, no explanation.\\n\\nName: ${trimmed}`,\n 64,\n );\n }\n\n /**\n * Reword a canned sentence (a trigger's announcement or question) so it sounds spoken rather than\n * templated, keeping every concrete detail. `persona` is the adapter's system prompt, so the reworded\n * line still sounds like the same assistant. Returns the input unchanged on any problem — a trigger\n * must still fire when the model is unavailable.\n */\n public async rephrase(text: string, language: string, persona = ''): Promise<string> {\n const trimmed = (text || '').trim();\n if (!trimmed) {\n return '';\n }\n try {\n const result = await this.complete(\n `${persona ? `${persona.trim()}\\n\\n` : ''}Reword the following sentence so it sounds natural and ` +\n `spoken${language ? `, in ${language}` : ''}. Keep every concrete detail (names, numbers, ` +\n `versions, times) and keep it one short sentence. If it is a question, keep it a question. ` +\n `Reply with ONLY the sentence.\\n\\nSentence: ${trimmed}`,\n 200,\n );\n return result || trimmed;\n } catch (e) {\n this.log.warn(`rephrase failed, using the original text: ${(e as Error).message}`);\n return trimmed;\n }\n }\n\n /**\n * Reword an ioBroker system notification into one spoken sentence, in the given `tone` (see\n * `notifications.toneFor`). Separate from {@link rephrase} because of the hints below: they are\n * specific to how ioBroker words its notifications, and each one comes from a real text that was\n * read out wrongly — version numbers mistaken for dates, origin prefixes spelled out letter by\n * letter, an empty `{}` announced as a problem. Returns the input unchanged on any failure.\n */\n public async rewordNotification(text: string, tone: string, language: string, persona = ''): Promise<string> {\n const trimmed = (text || '').trim();\n if (!trimmed) {\n return '';\n }\n try {\n const result = await this.complete(\n `${persona ? `${persona.trim()}\\n\\n` : ''}Reword the following system notification into one ` +\n `short, natural, spoken sentence${language ? `, in ${language}` : ''}. ` +\n `Keep every concrete detail such as adapter names and version numbers. ` +\n `Dates like M/D/YYYY or M/D/YYYY, H:MM:SS AM/PM are timestamps — say them as a date or ` +\n `a time, never as a version number. Technical prefixes such as ` +\n `\"system.host.XYZ: adapter.0:\" only say where it came from and need not be repeated. ` +\n `Empty values such as \"onedrive: {}\" mean there is nothing wrong there — only mention ` +\n `them if it matters. ${tone} Reply with ONLY the sentence.\\n\\nNotification: ${trimmed}`,\n 300,\n );\n return result || trimmed;\n } catch (e) {\n this.log.warn(`notification rewording failed, speaking the original: ${(e as Error).message}`);\n return trimmed;\n }\n }\n\n /**\n * Does `answer` express `category` (\"agreement\", \"Zustimmung\", \"refusal\", …)? This is the classifier\n * behind a trigger's response rules, where a spoken \"ja, mach das\" has to map onto one rule. Decided\n * by the model because the wording is open-ended; a failure answers `false`, so an unclassifiable\n * reply falls through to the trigger's fallback rule instead of acting on a guess.\n */\n public async classify(answer: string, category: string): Promise<boolean> {\n const text = (answer || '').trim();\n const want = (category || '').trim();\n if (!text || !want) {\n return false;\n }\n try {\n const verdict = await this.complete(\n `Does the following reply express \"${want}\"? Consider the meaning, in any language, ` +\n `including short or indirect phrasings. Answer with exactly YES or NO.\\n\\nReply: ${text}`,\n 8,\n );\n return /^\\s*(yes|ja|да)\\b/i.test(verdict);\n } catch (e) {\n this.log.warn(`classify failed ('${want}'): ${(e as Error).message}`);\n return false;\n }\n }\n\n /** Lightweight validation of the provider credentials (used by the settings Test button). */\n public async testConnection(): Promise<{ ok: boolean; error?: string }> {\n try {\n if (this.provider === 'anthropic') {\n const client = this.anthropic as Anthropic;\n await client.messages.create({\n model: await this.ensureModel(),\n max_tokens: 1,\n messages: [{ role: 'user', content: 'ping' }],\n });\n } else {\n const client = this.openai as OpenAI;\n if (this.providerName === 'openrouter') {\n // its model list is public and accepts any key; /key needs a valid one\n await client.get('/key');\n } else {\n await client.models.list();\n }\n }\n return { ok: true };\n } catch (e) {\n return { ok: false, error: (e as Error).message };\n }\n }\n\n /** List the provider's available model ids (for the settings autocomplete). */\n public async listModels(): Promise<string[]> {\n if (this.provider === 'anthropic') {\n const client = this.anthropic as Anthropic;\n const res = (await client.models.list()) as unknown as { data?: { id: string }[] };\n return (res.data || []).map(m => m.id);\n }\n const client = this.openai as OpenAI;\n const res = (await client.models.list()) as unknown as { data?: { id: string }[] };\n // OpenAI lists many non-chat models (embeddings, tts, whisper, …) — filter to chat-capable-ish.\n const EXCLUDE =\n /embedding|whisper|tts|audio|dall-e|moderation|image|realtime|transcribe|search|davinci|babbage|codex/i;\n return (res.data || [])\n .map(m => String(m.id))\n .filter(id => !EXCLUDE.test(id))\n .sort();\n }\n\n /**\n * One model request of the tool loop, timed. A failure goes into the trace together with what was sent\n * (status, API error body, stack) before it is rethrown.\n */\n private async timedRound(\n round: number,\n sent: unknown[],\n call: () => Promise<any>,\n ): Promise<{ resp: any; ms: number }> {\n const start = Date.now();\n try {\n return { resp: await call(), ms: Date.now() - start };\n } catch (e) {\n this.trace?.({\n stage: 'cloud',\n level: 'error',\n title: `Round ${round + 1} failed: ${(e as Error).message}`,\n ms: Date.now() - start,\n data: { sent, error: errorInfo(e) },\n });\n throw e;\n }\n }\n\n private tooManyRounds(): string {\n this.trace?.({ stage: 'cloud', level: 'warn', title: `Gave up after ${this.maxRounds} rounds of tool calls` });\n return 'Ich konnte die Anfrage nicht abschließen (zu viele Tool-Schritte).';\n }\n\n private async runTool(name: string, args: Record<string, unknown>): Promise<unknown> {\n const tool = this.toolMap[name];\n if (!tool) {\n return { error: `unknown tool: ${name}` };\n }\n try {\n const result = await tool.run(args || {});\n this.log.info(`tool ${name}(${JSON.stringify(args)}) → ${JSON.stringify(result).slice(0, 200)}`);\n return result;\n } catch (e) {\n this.log.warn(`tool ${name} failed: ${(e as Error).message}`);\n return { error: (e as Error).message };\n }\n }\n\n // ── OpenAI (Chat Completions + function tools) ──────────────────────────\n private async askOpenAI(\n question: string,\n systemPrompt: string,\n history: { role: 'user' | 'assistant'; content: string }[] = [],\n ): Promise<string> {\n const client = this.openai as OpenAI;\n const tools = this.tools.map(t => ({\n type: 'function' as const,\n function: { name: t.name, description: t.description, parameters: t.parameters },\n }));\n\n const messages: any[] = [];\n if (systemPrompt) {\n messages.push({ role: 'system', content: systemPrompt });\n }\n for (const t of history) {\n messages.push({ role: t.role, content: t.content });\n }\n messages.push({ role: 'user', content: question });\n // Messages the trace has shown already: the system prompt is part of the opening step.\n let shown = systemPrompt ? 1 : 0;\n\n for (let round = 0; round < this.maxRounds; round++) {\n const request = {\n model: this.model,\n messages,\n tools: tools.length ? tools : undefined,\n max_tokens: this.maxTokens,\n };\n this.dump(`llm → openai round ${round}`, request);\n const sent = messages.slice(shown);\n const { resp, ms } = await this.timedRound(round, sent, () => client.chat.completions.create(request));\n const msg = resp.choices[0].message;\n this.dump(`llm ← openai round ${round}`, { message: msg, usage: resp.usage });\n messages.push(msg);\n shown = messages.length;\n this.trace?.({\n stage: 'cloud',\n title: roundTitle(\n round,\n (msg.tool_calls || []).map((tc: any) => String(tc.function?.name)),\n ),\n ms,\n tokens: tokensOf(resp.usage),\n data: {\n sent,\n response: { finish_reason: resp.choices[0].finish_reason, message: msg },\n model: resp.model,\n usage: resp.usage,\n },\n });\n\n if (!msg.tool_calls || !msg.tool_calls.length) {\n return String(msg.content || '').trim();\n }\n\n for (const tc of msg.tool_calls) {\n let args: Record<string, unknown> = {};\n try {\n args = JSON.parse(tc.function.arguments || '{}');\n } catch {\n /* leave empty */\n }\n const result = await this.runTool(tc.function.name, args);\n messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify(result) });\n }\n }\n return this.tooManyRounds();\n }\n\n /**\n * Keep a single cache breakpoint on the last content block of the last message (Anthropic allows\n * ≤4 total; we also mark system + last tool). Strips prior message breakpoints so we never exceed it.\n */\n private markLastMessageForCache(messages: any[]): void {\n for (const m of messages) {\n if (Array.isArray(m.content)) {\n for (const b of m.content) {\n if (b && typeof b === 'object') {\n delete b.cache_control;\n }\n }\n }\n }\n const last = messages[messages.length - 1];\n if (!last) {\n return;\n }\n if (typeof last.content === 'string') {\n last.content = [{ type: 'text', text: last.content, cache_control: { type: 'ephemeral' } }];\n } else if (Array.isArray(last.content) && last.content.length) {\n const b = last.content[last.content.length - 1];\n if (b && typeof b === 'object') {\n b.cache_control = { type: 'ephemeral' };\n }\n }\n }\n\n // ── Anthropic (Messages API + tool_use) ─────────────────────────────────\n private async askAnthropic(\n question: string,\n systemPrompt: string,\n history: { role: 'user' | 'assistant'; content: string }[] = [],\n ): Promise<string> {\n const client = this.anthropic as Anthropic;\n const tools: any[] = this.tools.map(t => ({\n name: t.name,\n description: t.description,\n input_schema: t.parameters as Anthropic.Tool.InputSchema,\n }));\n // Prompt caching: mark the last tool → the static prefix (system + all tools) is cached and\n // read back on every later round/request (5-min TTL), cutting latency & cost on the big context.\n if (tools.length) {\n tools[tools.length - 1] = { ...tools[tools.length - 1], cache_control: { type: 'ephemeral' } };\n }\n const system: any = systemPrompt\n ? [{ type: 'text', text: systemPrompt, cache_control: { type: 'ephemeral' } }]\n : undefined;\n\n const messages: any[] = [\n ...history.map(t => ({ role: t.role, content: t.content })),\n { role: 'user', content: question },\n ];\n // Messages the trace has shown already (the system prompt is part of the opening step).\n let shown = 0;\n\n for (let round = 0; round < this.maxRounds; round++) {\n // Cache the growing conversation prefix too (e.g. the large list_devices result): move a\n // single cache breakpoint to the last content block of the last message each round.\n this.markLastMessageForCache(messages);\n const request = {\n model: this.model,\n max_tokens: this.maxTokens,\n system,\n tools: tools.length ? tools : undefined,\n messages,\n };\n this.dump(`llm → anthropic round ${round}`, request);\n const sent = messages.slice(shown);\n const { resp, ms } = await this.timedRound(round, sent, () => client.messages.create(request));\n this.dump(`llm ← anthropic round ${round}`, { content: resp.content, usage: resp.usage });\n\n const toolUses = resp.content.filter((b: any) => b.type === 'tool_use');\n this.trace?.({\n stage: 'cloud',\n title: roundTitle(\n round,\n toolUses.map((b: any) => String(b.name)),\n ),\n ms,\n tokens: tokensOf(resp.usage),\n data: {\n sent,\n response: { stop_reason: resp.stop_reason, content: resp.content },\n model: resp.model,\n usage: resp.usage,\n },\n });\n if (!toolUses.length) {\n return resp.content\n .filter((b: any) => b.type === 'text')\n .map((b: any) => b.text)\n .join('')\n .trim();\n }\n\n messages.push({ role: 'assistant', content: resp.content });\n shown = messages.length;\n const toolResults = [];\n for (const tu of toolUses) {\n const result = await this.runTool(tu.name, tu.input);\n toolResults.push({ type: 'tool_result', tool_use_id: tu.id, content: JSON.stringify(result) });\n }\n messages.push({ role: 'user', content: toolResults });\n }\n return this.tooManyRounds();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"llm.js","sourceRoot":"","sources":["../../src/lib/llm.ts"],"names":[],"mappings":"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","sourcesContent":["import OpenAI from 'openai';\nimport Anthropic from '@anthropic-ai/sdk';\nimport type { Tool } from './tools';\nimport { errorInfo, type JournalTokens, type TraceFn } from './journal';\nimport { redactImages, type ImageInput } from './media';\n\n/** All selectable LLM providers. gemini/deepseek/openrouter/custom all use the OpenAI-compatible SDK path. */\nexport type Provider = 'openai' | 'anthropic' | 'gemini' | 'deepseek' | 'openrouter' | 'custom';\n\ninterface ProviderPreset {\n /** Which vendor SDK to use. */\n sdk: 'openai' | 'anthropic';\n /** Fixed API base URL ('' = SDK default; for 'custom' the user's baseUrl is used instead). */\n baseUrl: string;\n /** Default model when none is configured. */\n defaultModel: string;\n}\n\n/** Per-provider SDK, endpoint and default model. Gemini/DeepSeek expose OpenAI-compatible endpoints. */\nexport const PROVIDER_PRESETS: Record<Provider, ProviderPreset> = {\n openai: { sdk: 'openai', baseUrl: '', defaultModel: 'gpt-4o-mini' },\n anthropic: { sdk: 'anthropic', baseUrl: '', defaultModel: 'claude-sonnet-4-6' },\n gemini: {\n sdk: 'openai',\n baseUrl: 'https://generativelanguage.googleapis.com/v1beta/openai/',\n defaultModel: 'gemini-2.0-flash',\n },\n deepseek: { sdk: 'openai', baseUrl: 'https://api.deepseek.com', defaultModel: 'deepseek-chat' },\n // OpenRouter names a model with its vendor in front\n openrouter: { sdk: 'openai', baseUrl: 'https://openrouter.ai/api/v1', defaultModel: 'openai/gpt-4o-mini' },\n custom: { sdk: 'openai', baseUrl: '', defaultModel: '' },\n};\n\n/** OpenRouter shows the calling app in its statistics (same attribution as `@iobroker/ai-core`) */\nexport const OPENROUTER_HEADERS = { 'HTTP-Referer': 'https://www.iobroker.net', 'X-Title': 'ioBroker' };\n\n/** Resolve the effective SDK, base URL and default model for a provider (custom uses `configBaseUrl`). */\nexport function resolveProvider(\n provider: string | undefined,\n configBaseUrl?: string,\n): { sdk: 'openai' | 'anthropic'; baseUrl: string; defaultModel: string } {\n const preset = PROVIDER_PRESETS[(provider as Provider) || 'openai'] || PROVIDER_PRESETS.openai;\n return {\n sdk: preset.sdk,\n baseUrl: provider === 'custom' ? configBaseUrl || '' : preset.baseUrl,\n defaultModel: preset.defaultModel,\n };\n}\n\n/**\n * API key to use for `provider`: a local OpenAI-compatible server (the `custom` provider) usually has no\n * key at all, but the OpenAI SDK refuses to start without one — a placeholder goes into the\n * `Authorization` header, which such servers ignore. Empty for every other provider without a key.\n */\nexport function effectiveApiKey(provider: string | undefined, apiKey: string): string {\n return apiKey || (provider === 'custom' ? 'no-key' : '');\n}\n\nexport interface LlmAgentOptions {\n provider: Provider;\n apiKey: string;\n model: string;\n baseUrl?: string;\n systemPrompt?: string;\n maxTokens?: number;\n tools: Tool[];\n log: ioBroker.Logger;\n /** Records the prompt and every round of {@link LlmAgent.ask} (the request journal's debug view). */\n trace?: TraceFn;\n}\n\n/** Normalised token counts of an OpenAI-compatible or Anthropic `usage` block. */\nfunction tokensOf(usage: any): JournalTokens | undefined {\n if (!usage || typeof usage !== 'object') {\n return undefined;\n }\n if (usage.input_tokens !== undefined) {\n return {\n input: Number(usage.input_tokens) || 0,\n output: Number(usage.output_tokens) || 0,\n cacheRead: Number(usage.cache_read_input_tokens) || undefined,\n cacheWrite: Number(usage.cache_creation_input_tokens) || undefined,\n };\n }\n return {\n input: Number(usage.prompt_tokens) || 0,\n output: Number(usage.completion_tokens) || 0,\n cacheRead: Number(usage.prompt_tokens_details?.cached_tokens) || undefined,\n };\n}\n\n/** \"Round 2 → get_states, set_state\" or \"Round 3 → answer\" — the title of a round in the trace. */\nfunction roundTitle(round: number, toolNames: string[]): string {\n return `Round ${round + 1} → ${toolNames.length ? toolNames.join(', ') : 'answer'}`;\n}\n\n/**\n * Provider-agnostic LLM agent with tool-calling.\n *\n * `ask(question)` runs the model, executes any tool calls it requests, feeds the\n * results back, and repeats until the model returns a final text answer.\n *\n * The request/response plumbing of the vendor SDKs is intentionally loosely typed\n * (`any`) so the code stays robust across minor SDK type changes; the public API\n * (options, `ask`, tools) is fully typed.\n */\nexport class LlmAgent {\n public readonly provider: 'openai' | 'anthropic';\n /** Configured model; empty means \"ask the server\" (see {@link ensureModel}). */\n private configuredModel: string;\n private modelLookup: Promise<string> | null = null;\n\n private readonly systemPrompt: string;\n private readonly maxTokens: number;\n private readonly tools: Tool[];\n private readonly toolMap: Record<string, Tool>;\n private readonly log: ioBroker.Logger;\n private readonly maxRounds = 6; // safety cap against tool-call loops\n /** Configured provider (gemini, deepseek, custom … — {@link provider} is only the SDK). */\n private readonly providerName: string;\n private readonly baseUrl: string;\n private readonly trace?: TraceFn;\n\n private readonly openai?: OpenAI;\n private readonly anthropic?: Anthropic;\n\n constructor(opts: LlmAgentOptions) {\n this.provider = opts.provider === 'anthropic' ? 'anthropic' : 'openai';\n this.providerName = opts.provider || this.provider;\n this.baseUrl = opts.baseUrl || '';\n this.trace = opts.trace;\n this.configuredModel = (opts.model || '').trim();\n this.systemPrompt = opts.systemPrompt || '';\n this.maxTokens = opts.maxTokens || 1024;\n this.tools = opts.tools || [];\n this.log = opts.log;\n this.toolMap = Object.fromEntries(this.tools.map(t => [t.name, t]));\n\n if (this.provider === 'anthropic') {\n this.anthropic = new Anthropic({ apiKey: opts.apiKey, baseURL: opts.baseUrl || undefined });\n } else {\n this.openai = new OpenAI({\n apiKey: opts.apiKey,\n baseURL: opts.baseUrl || undefined,\n defaultHeaders: opts.provider === 'openrouter' ? OPENROUTER_HEADERS : undefined,\n });\n }\n }\n\n /** The model requests go to — empty until {@link ensureModel} has looked it up, if none was configured. */\n public get model(): string {\n return this.configuredModel;\n }\n\n /**\n * Make sure there is a model to send. A local OpenAI-compatible server (Ollama, LM Studio, llama.cpp\n * server, …) is usually set up with the `custom` provider and no model typed in — and sending\n * `\"model\": \"\"` gets a 400 from the server (\"unknown provider for model\" and the like) that says\n * nothing about the real cause. So an empty model is looked up once from the server's `/models` list\n * (with one model loaded, that is the one), and a server that lists nothing gets a clear error.\n */\n private async ensureModel(): Promise<string> {\n if (this.configuredModel) {\n return this.configuredModel;\n }\n this.modelLookup ||= this.listModels()\n .then(models => {\n if (!models.length) {\n throw new Error('no model configured and the server lists none');\n }\n this.configuredModel = models[0];\n this.log.info(\n `No model configured — using \"${models[0]}\" from the server${\n models.length > 1 ? ` (available: ${models.join(', ')})` : ''\n }. Select a model in the adapter settings to choose another.`,\n );\n return models[0];\n })\n .catch(e => {\n this.modelLookup = null; // let the next request try again (server may still be starting)\n throw new Error(\n `No model configured — select one in the adapter settings (lookup failed: ${(e as Error).message})`,\n );\n });\n return this.modelLookup;\n }\n\n /**\n * `systemPrompt` overrides the configured one for this call (e.g. to inject a device list).\n * `history` are prior turns (user/assistant) prepended so the model can resolve follow-ups.\n * `images` go with the question (a photo from Telegram); the model must be able to see.\n */\n public async ask(\n question: string,\n systemPrompt?: string,\n history: { role: 'user' | 'assistant'; content: string }[] = [],\n images: ImageInput[] = [],\n ): Promise<string> {\n await this.ensureModel();\n this.log.debug(\n `llm ask [${this.provider}/${this.model}] (${history.length} history${images.length ? `, ${images.length} picture(s)` : ''}): ${question}`,\n );\n const started = Date.now();\n const sys = systemPrompt ?? this.systemPrompt;\n this.trace?.({\n stage: 'cloud',\n title: `${this.providerName} · ${this.model}: ${this.tools.length} tools, ${history.length} earlier turn(s)`,\n data: {\n provider: this.providerName,\n sdk: this.provider,\n model: this.model,\n baseUrl: this.baseUrl || undefined,\n maxTokens: this.maxTokens,\n maxRounds: this.maxRounds,\n system: sys,\n tools: this.tools.map(t => t.name),\n images: images.length ? images.map(i => i.mimeType) : undefined,\n },\n });\n const answer =\n this.provider === 'anthropic'\n ? await this.askAnthropic(question, sys, history, images)\n : await this.askOpenAI(question, sys, history, images);\n this.log.debug(`llm answer (${Date.now() - started} ms): ${answer}`);\n return answer;\n }\n\n /** Compact, truncated JSON for debug logging of LLM requests/responses. */\n private dump(label: string, payload: unknown): void {\n if (this.log.level !== 'silly' && this.log.level !== 'debug') {\n return; // avoid stringifying large payloads when debug logging is off\n }\n const max = this.log.level === 'silly' ? 8000 : 1500;\n let text: string;\n try {\n // base64 of a picture would fill the whole excerpt\n text = JSON.stringify(redactImages(payload));\n } catch {\n text = String(payload);\n }\n this.log.debug(`${label}: ${text.length > max ? `${text.slice(0, max)}…[${text.length}]` : text}`);\n }\n\n /**\n * One prompt in, one line out — no tools, no history, no system prompt. The small side jobs\n * (translate/rephrase/classify) all run through here.\n */\n private async complete(prompt: string, maxTokens: number): Promise<string> {\n await this.ensureModel();\n if (this.provider === 'anthropic') {\n const client = this.anthropic as Anthropic;\n const resp: any = await client.messages.create({\n model: this.model,\n max_tokens: maxTokens,\n messages: [{ role: 'user', content: prompt }],\n });\n return resp.content\n .filter((b: any) => b.type === 'text')\n .map((b: any) => b.text)\n .join('')\n .trim();\n }\n const client = this.openai as OpenAI;\n const resp: any = await client.chat.completions.create({\n model: this.model,\n max_tokens: maxTokens,\n messages: [{ role: 'user', content: prompt }],\n });\n return String(resp.choices[0].message.content || '').trim();\n }\n\n /** Translate a short device/room name into `targetLanguage` (single completion, no tools). */\n public async translate(text: string, targetLanguage: string): Promise<string> {\n const trimmed = (text || '').trim();\n if (!trimmed) {\n return '';\n }\n return this.complete(\n `Translate the following smart-home device or room name into ${targetLanguage}. ` +\n `Reply with ONLY the translation — no quotes, no punctuation, no explanation.\\n\\nName: ${trimmed}`,\n 64,\n );\n }\n\n /**\n * Reword a canned sentence (a trigger's announcement or question) so it sounds spoken rather than\n * templated, keeping every concrete detail. `persona` is the adapter's system prompt, so the reworded\n * line still sounds like the same assistant. Returns the input unchanged on any problem — a trigger\n * must still fire when the model is unavailable.\n */\n public async rephrase(text: string, language: string, persona = ''): Promise<string> {\n const trimmed = (text || '').trim();\n if (!trimmed) {\n return '';\n }\n try {\n const result = await this.complete(\n `${persona ? `${persona.trim()}\\n\\n` : ''}Reword the following sentence so it sounds natural and ` +\n `spoken${language ? `, in ${language}` : ''}. Keep every concrete detail (names, numbers, ` +\n `versions, times) and keep it one short sentence. If it is a question, keep it a question. ` +\n `Reply with ONLY the sentence.\\n\\nSentence: ${trimmed}`,\n 200,\n );\n return result || trimmed;\n } catch (e) {\n this.log.warn(`rephrase failed, using the original text: ${(e as Error).message}`);\n return trimmed;\n }\n }\n\n /**\n * Reword an ioBroker system notification into one spoken sentence, in the given `tone` (see\n * `notifications.toneFor`). Separate from {@link rephrase} because of the hints below: they are\n * specific to how ioBroker words its notifications, and each one comes from a real text that was\n * read out wrongly — version numbers mistaken for dates, origin prefixes spelled out letter by\n * letter, an empty `{}` announced as a problem. Returns the input unchanged on any failure.\n */\n public async rewordNotification(text: string, tone: string, language: string, persona = ''): Promise<string> {\n const trimmed = (text || '').trim();\n if (!trimmed) {\n return '';\n }\n try {\n const result = await this.complete(\n `${persona ? `${persona.trim()}\\n\\n` : ''}Reword the following system notification into one ` +\n `short, natural, spoken sentence${language ? `, in ${language}` : ''}. ` +\n `Keep every concrete detail such as adapter names and version numbers. ` +\n `Dates like M/D/YYYY or M/D/YYYY, H:MM:SS AM/PM are timestamps — say them as a date or ` +\n `a time, never as a version number. Technical prefixes such as ` +\n `\"system.host.XYZ: adapter.0:\" only say where it came from and need not be repeated. ` +\n `Empty values such as \"onedrive: {}\" mean there is nothing wrong there — only mention ` +\n `them if it matters. ${tone} Reply with ONLY the sentence.\\n\\nNotification: ${trimmed}`,\n 300,\n );\n return result || trimmed;\n } catch (e) {\n this.log.warn(`notification rewording failed, speaking the original: ${(e as Error).message}`);\n return trimmed;\n }\n }\n\n /**\n * Does `answer` express `category` (\"agreement\", \"Zustimmung\", \"refusal\", …)? This is the classifier\n * behind a trigger's response rules, where a spoken \"ja, mach das\" has to map onto one rule. Decided\n * by the model because the wording is open-ended; a failure answers `false`, so an unclassifiable\n * reply falls through to the trigger's fallback rule instead of acting on a guess.\n */\n public async classify(answer: string, category: string): Promise<boolean> {\n const text = (answer || '').trim();\n const want = (category || '').trim();\n if (!text || !want) {\n return false;\n }\n try {\n const verdict = await this.complete(\n `Does the following reply express \"${want}\"? Consider the meaning, in any language, ` +\n `including short or indirect phrasings. Answer with exactly YES or NO.\\n\\nReply: ${text}`,\n 8,\n );\n return /^\\s*(yes|ja|да)\\b/i.test(verdict);\n } catch (e) {\n this.log.warn(`classify failed ('${want}'): ${(e as Error).message}`);\n return false;\n }\n }\n\n /** Lightweight validation of the provider credentials (used by the settings Test button). */\n public async testConnection(): Promise<{ ok: boolean; error?: string }> {\n try {\n if (this.provider === 'anthropic') {\n const client = this.anthropic as Anthropic;\n await client.messages.create({\n model: await this.ensureModel(),\n max_tokens: 1,\n messages: [{ role: 'user', content: 'ping' }],\n });\n } else {\n const client = this.openai as OpenAI;\n if (this.providerName === 'openrouter') {\n // its model list is public and accepts any key; /key needs a valid one\n await client.get('/key');\n } else {\n await client.models.list();\n }\n }\n return { ok: true };\n } catch (e) {\n return { ok: false, error: (e as Error).message };\n }\n }\n\n /** List the provider's available model ids (for the settings autocomplete). */\n public async listModels(): Promise<string[]> {\n if (this.provider === 'anthropic') {\n const client = this.anthropic as Anthropic;\n const res = (await client.models.list()) as unknown as { data?: { id: string }[] };\n return (res.data || []).map(m => m.id);\n }\n const client = this.openai as OpenAI;\n const res = (await client.models.list()) as unknown as { data?: { id: string }[] };\n // OpenAI lists many non-chat models (embeddings, tts, whisper, …) — filter to chat-capable-ish.\n const EXCLUDE =\n /embedding|whisper|tts|audio|dall-e|moderation|image|realtime|transcribe|search|davinci|babbage|codex/i;\n return (res.data || [])\n .map(m => String(m.id))\n .filter(id => !EXCLUDE.test(id))\n .sort();\n }\n\n /**\n * One model request of the tool loop, timed. A failure goes into the trace together with what was sent\n * (status, API error body, stack) before it is rethrown.\n */\n private async timedRound(\n round: number,\n sent: unknown[],\n call: () => Promise<any>,\n ): Promise<{ resp: any; ms: number }> {\n const start = Date.now();\n try {\n return { resp: await call(), ms: Date.now() - start };\n } catch (e) {\n this.trace?.({\n stage: 'cloud',\n level: 'error',\n title: `Round ${round + 1} failed: ${(e as Error).message}`,\n ms: Date.now() - start,\n data: { sent: redactImages(sent), error: errorInfo(e) },\n });\n throw e;\n }\n }\n\n private tooManyRounds(): string {\n this.trace?.({ stage: 'cloud', level: 'warn', title: `Gave up after ${this.maxRounds} rounds of tool calls` });\n return 'Ich konnte die Anfrage nicht abschließen (zu viele Tool-Schritte).';\n }\n\n private async runTool(name: string, args: Record<string, unknown>): Promise<unknown> {\n const tool = this.toolMap[name];\n if (!tool) {\n return { error: `unknown tool: ${name}` };\n }\n try {\n const result = await tool.run(args || {});\n this.log.info(`tool ${name}(${JSON.stringify(args)}) → ${JSON.stringify(result).slice(0, 200)}`);\n return result;\n } catch (e) {\n this.log.warn(`tool ${name} failed: ${(e as Error).message}`);\n return { error: (e as Error).message };\n }\n }\n\n // ── OpenAI (Chat Completions + function tools) ──────────────────────────\n private async askOpenAI(\n question: string,\n systemPrompt: string,\n history: { role: 'user' | 'assistant'; content: string }[] = [],\n images: ImageInput[] = [],\n ): Promise<string> {\n const client = this.openai as OpenAI;\n const tools = this.tools.map(t => ({\n type: 'function' as const,\n function: { name: t.name, description: t.description, parameters: t.parameters },\n }));\n\n const messages: any[] = [];\n if (systemPrompt) {\n messages.push({ role: 'system', content: systemPrompt });\n }\n for (const t of history) {\n messages.push({ role: t.role, content: t.content });\n }\n messages.push({ role: 'user', content: openAiUserContent(question, images) });\n // Messages the trace has shown already: the system prompt is part of the opening step.\n let shown = systemPrompt ? 1 : 0;\n\n for (let round = 0; round < this.maxRounds; round++) {\n const request = {\n model: this.model,\n messages,\n tools: tools.length ? tools : undefined,\n max_tokens: this.maxTokens,\n };\n this.dump(`llm → openai round ${round}`, request);\n const sent = messages.slice(shown);\n const { resp, ms } = await this.timedRound(round, sent, () => client.chat.completions.create(request));\n const msg = resp.choices[0].message;\n this.dump(`llm ← openai round ${round}`, { message: msg, usage: resp.usage });\n messages.push(msg);\n shown = messages.length;\n this.trace?.({\n stage: 'cloud',\n title: roundTitle(\n round,\n (msg.tool_calls || []).map((tc: any) => String(tc.function?.name)),\n ),\n ms,\n tokens: tokensOf(resp.usage),\n data: {\n sent: redactImages(sent),\n response: { finish_reason: resp.choices[0].finish_reason, message: msg },\n model: resp.model,\n usage: resp.usage,\n },\n });\n\n if (!msg.tool_calls || !msg.tool_calls.length) {\n return String(msg.content || '').trim();\n }\n\n for (const tc of msg.tool_calls) {\n let args: Record<string, unknown> = {};\n try {\n args = JSON.parse(tc.function.arguments || '{}');\n } catch {\n /* leave empty */\n }\n const result = await this.runTool(tc.function.name, args);\n messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify(result) });\n }\n }\n return this.tooManyRounds();\n }\n\n /**\n * Keep a single cache breakpoint on the last content block of the last message (Anthropic allows\n * ≤4 total; we also mark system + last tool). Strips prior message breakpoints so we never exceed it.\n */\n private markLastMessageForCache(messages: any[]): void {\n for (const m of messages) {\n if (Array.isArray(m.content)) {\n for (const b of m.content) {\n if (b && typeof b === 'object') {\n delete b.cache_control;\n }\n }\n }\n }\n const last = messages[messages.length - 1];\n if (!last) {\n return;\n }\n if (typeof last.content === 'string') {\n last.content = [{ type: 'text', text: last.content, cache_control: { type: 'ephemeral' } }];\n } else if (Array.isArray(last.content) && last.content.length) {\n const b = last.content[last.content.length - 1];\n if (b && typeof b === 'object') {\n b.cache_control = { type: 'ephemeral' };\n }\n }\n }\n\n // ── Anthropic (Messages API + tool_use) ─────────────────────────────────\n private async askAnthropic(\n question: string,\n systemPrompt: string,\n history: { role: 'user' | 'assistant'; content: string }[] = [],\n images: ImageInput[] = [],\n ): Promise<string> {\n const client = this.anthropic as Anthropic;\n const tools: any[] = this.tools.map(t => ({\n name: t.name,\n description: t.description,\n input_schema: t.parameters as Anthropic.Tool.InputSchema,\n }));\n // Prompt caching: mark the last tool → the static prefix (system + all tools) is cached and\n // read back on every later round/request (5-min TTL), cutting latency & cost on the big context.\n if (tools.length) {\n tools[tools.length - 1] = { ...tools[tools.length - 1], cache_control: { type: 'ephemeral' } };\n }\n const system: any = systemPrompt\n ? [{ type: 'text', text: systemPrompt, cache_control: { type: 'ephemeral' } }]\n : undefined;\n\n const messages: any[] = [\n ...history.map(t => ({ role: t.role, content: t.content })),\n { role: 'user', content: anthropicUserContent(question, images) },\n ];\n // Messages the trace has shown already (the system prompt is part of the opening step).\n let shown = 0;\n\n for (let round = 0; round < this.maxRounds; round++) {\n // Cache the growing conversation prefix too (e.g. the large list_devices result): move a\n // single cache breakpoint to the last content block of the last message each round.\n this.markLastMessageForCache(messages);\n const request = {\n model: this.model,\n max_tokens: this.maxTokens,\n system,\n tools: tools.length ? tools : undefined,\n messages,\n };\n this.dump(`llm → anthropic round ${round}`, request);\n const sent = messages.slice(shown);\n const { resp, ms } = await this.timedRound(round, sent, () => client.messages.create(request));\n this.dump(`llm ← anthropic round ${round}`, { content: resp.content, usage: resp.usage });\n\n const toolUses = resp.content.filter((b: any) => b.type === 'tool_use');\n this.trace?.({\n stage: 'cloud',\n title: roundTitle(\n round,\n toolUses.map((b: any) => String(b.name)),\n ),\n ms,\n tokens: tokensOf(resp.usage),\n data: {\n sent: redactImages(sent),\n response: { stop_reason: resp.stop_reason, content: resp.content },\n model: resp.model,\n usage: resp.usage,\n },\n });\n if (!toolUses.length) {\n return resp.content\n .filter((b: any) => b.type === 'text')\n .map((b: any) => b.text)\n .join('')\n .trim();\n }\n\n messages.push({ role: 'assistant', content: resp.content });\n shown = messages.length;\n const toolResults = [];\n for (const tu of toolUses) {\n const result = await this.runTool(tu.name, tu.input);\n toolResults.push({ type: 'tool_result', tool_use_id: tu.id, content: JSON.stringify(result) });\n }\n messages.push({ role: 'user', content: toolResults });\n }\n return this.tooManyRounds();\n }\n}\n\n/**\n * The user turn of the OpenAI Chat Completions API (also Gemini, OpenRouter …): the plain question, or text and\n * pictures as data URLs.\n *\n * @param question the question\n * @param images pictures that came with it\n */\nexport function openAiUserContent(question: string, images: ImageInput[] = []): string | unknown[] {\n if (!images.length) {\n return question;\n }\n return [\n { type: 'text', text: question },\n ...images.map(i => ({ type: 'image_url', image_url: { url: `data:${i.mimeType};base64,${i.data}` } })),\n ];\n}\n\n/**\n * The user turn of the Anthropic Messages API: the plain question, or the pictures first and then the text (what\n * Anthropic recommends).\n *\n * @param question the question\n * @param images pictures that came with it\n */\nexport function anthropicUserContent(question: string, images: ImageInput[] = []): string | unknown[] {\n if (!images.length) {\n return question;\n }\n return [\n ...images.map(i => ({ type: 'image', source: { type: 'base64', media_type: i.mimeType, data: i.data } })),\n { type: 'text', text: question },\n ];\n}\n"]}
|