iobroker.assistant 1.4.6 → 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/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
- text = JSON.stringify(payload);
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
@@ -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"]}
@@ -0,0 +1,226 @@
1
+ "use strict";
2
+ /**
3
+ * Pictures and videos that come with a question (`ask` with `images` / `video`, e.g. from Telegram).
4
+ *
5
+ * Pictures go to the cloud model as they are (base64, the vendors take JPEG/PNG/GIF/WebP). Videos are not looked at
6
+ * yet: the sender keeps them in the ioBroker file storage and only says where, so a video never travels through the
7
+ * message bus as base64.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.MAX_IMAGE_BASE64 = exports.MAX_IMAGES = void 0;
11
+ exports.sniffImageType = sniffImageType;
12
+ exports.parseAskImages = parseAskImages;
13
+ exports.parseAskVideo = parseAskVideo;
14
+ exports.isVideoError = isVideoError;
15
+ exports.redactImages = redactImages;
16
+ exports.imageOnlyQuestion = imageOnlyQuestion;
17
+ exports.videoNotSupportedText = videoNotSupportedText;
18
+ exports.noVisionText = noVisionText;
19
+ exports.attachmentNote = attachmentNote;
20
+ exports.historyText = historyText;
21
+ /** At most this many pictures per question (an album of Telegram has up to 10) */
22
+ exports.MAX_IMAGES = 4;
23
+ /** Largest picture as base64 text: Anthropic refuses images over 5 MB */
24
+ exports.MAX_IMAGE_BASE64 = 5 * 1024 * 1024;
25
+ const IMAGE_TYPES = new Set(['image/jpeg', 'image/png', 'image/gif', 'image/webp']);
26
+ /**
27
+ * The type of a picture from its first bytes, `null` if it is none of the four the vendors take.
28
+ *
29
+ * @param buf the start of the file (16 bytes are enough)
30
+ */
31
+ function sniffImageType(buf) {
32
+ if (buf.length >= 3 && buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff) {
33
+ return 'image/jpeg';
34
+ }
35
+ if (buf.length >= 8 && buf.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))) {
36
+ return 'image/png';
37
+ }
38
+ if (buf.length >= 6 && /^GIF8[79]a$/.test(buf.subarray(0, 6).toString('latin1'))) {
39
+ return 'image/gif';
40
+ }
41
+ if (buf.length >= 12 && buf.toString('latin1', 0, 4) === 'RIFF' && buf.toString('latin1', 8, 12) === 'WEBP') {
42
+ return 'image/webp';
43
+ }
44
+ return null;
45
+ }
46
+ /**
47
+ * Check the `images` of an `ask`: base64 of JPEG/PNG/GIF/WebP, at most {@link MAX_IMAGES} and
48
+ * {@link MAX_IMAGE_BASE64} each. The type is taken from the content, not from what the sender says.
49
+ *
50
+ * @param raw the `images` field: a list of `{ data, mimeType? }` (one object or a bare base64 string work too)
51
+ * @returns the pictures, or the reason they are refused
52
+ */
53
+ function parseAskImages(raw) {
54
+ if (raw === undefined || raw === null || raw === '') {
55
+ return { images: [] };
56
+ }
57
+ const list = Array.isArray(raw) ? raw : [raw];
58
+ if (list.length > exports.MAX_IMAGES) {
59
+ return { images: [], error: `too many pictures (${list.length}, at most ${exports.MAX_IMAGES})` };
60
+ }
61
+ const images = [];
62
+ for (const item of list) {
63
+ const data = typeof item === 'string' ? item : item?.data;
64
+ if (typeof data !== 'string' || !data) {
65
+ return { images: [], error: 'a picture without data' };
66
+ }
67
+ // a data URL is fine as well
68
+ const base64 = data.replace(/^data:[^;,]*;base64,/, '').replace(/\s+/g, '');
69
+ if (base64.length > exports.MAX_IMAGE_BASE64) {
70
+ return {
71
+ images: [],
72
+ error: `picture too big (${Math.round((base64.length * 3) / 4 / 1024)} KB, at most ${(exports.MAX_IMAGE_BASE64 * 3) / 4 / 1024} KB)`,
73
+ };
74
+ }
75
+ const mimeType = sniffImageType(Buffer.from(base64.slice(0, 32), 'base64'));
76
+ if (!mimeType || !IMAGE_TYPES.has(mimeType)) {
77
+ return { images: [], error: 'not a JPEG, PNG, GIF or WebP picture' };
78
+ }
79
+ images.push({ data: base64, mimeType });
80
+ }
81
+ return { images };
82
+ }
83
+ /**
84
+ * Check the `video` of an `ask`: only a reference into the ioBroker file storage, never the content.
85
+ *
86
+ * @param raw the `video` field
87
+ * @returns the reference, `null` without one, or the reason it is refused (e.g. base64 content)
88
+ */
89
+ function parseAskVideo(raw) {
90
+ if (raw === undefined || raw === null || raw === '') {
91
+ return null;
92
+ }
93
+ if (typeof raw !== 'object' || Array.isArray(raw)) {
94
+ return { error: 'video must be { adapter, path } of the ioBroker file storage' };
95
+ }
96
+ const v = raw;
97
+ if ('data' in v) {
98
+ return { error: 'a video is not sent as content - store it and send { adapter, path }' };
99
+ }
100
+ const str = (x) => (typeof x === 'string' ? x.trim() : '');
101
+ const num = (x) => typeof x === 'number' && Number.isFinite(x) && x >= 0 ? x : undefined;
102
+ const ref = {
103
+ adapter: str(v.adapter),
104
+ path: str(v.path).replace(/^\/+/, ''),
105
+ mimeType: str(v.mimeType) || undefined,
106
+ durationSec: num(v.durationSec),
107
+ size: num(v.size),
108
+ error: str(v.error) || undefined,
109
+ };
110
+ if ((!ref.adapter || !ref.path) && !ref.error) {
111
+ return { error: 'video without adapter and path' };
112
+ }
113
+ return ref;
114
+ }
115
+ /** Whether {@link parseAskVideo} refused the video */
116
+ function isVideoError(v) {
117
+ return !!v && !('adapter' in v);
118
+ }
119
+ /**
120
+ * Copy of a request / message list with the pictures replaced by a short note — for the trace and the debug log,
121
+ * where hundreds of KB of base64 per round help nobody. Understands the OpenAI (`image_url` with a data URL) and the
122
+ * Anthropic form (`image` with a base64 `source`).
123
+ *
124
+ * @param value what to copy
125
+ */
126
+ function redactImages(value) {
127
+ const walk = (v) => {
128
+ if (Array.isArray(v)) {
129
+ return v.map(walk);
130
+ }
131
+ if (!v || typeof v !== 'object') {
132
+ return v;
133
+ }
134
+ const o = v;
135
+ if (o.type === 'image_url' && typeof o.image_url?.url === 'string' && o.image_url.url.startsWith('data:')) {
136
+ return { ...o, image_url: { ...o.image_url, url: imageNote(o.image_url.url) } };
137
+ }
138
+ if (o.type === 'image' && o.source?.type === 'base64' && typeof o.source.data === 'string') {
139
+ return { ...o, source: { ...o.source, data: imageNote(o.source.data, o.source.media_type) } };
140
+ }
141
+ const out = {};
142
+ for (const [k, x] of Object.entries(o)) {
143
+ out[k] = walk(x);
144
+ }
145
+ return out;
146
+ };
147
+ return walk(value);
148
+ }
149
+ function imageNote(data, mimeType) {
150
+ const m = /^data:([^;,]+)/.exec(data);
151
+ const base64 = data.replace(/^data:[^,]*,/, '');
152
+ return `<${mimeType || m?.[1] || 'image'}, ${Math.round((base64.length * 3) / 4 / 1024)} KB>`;
153
+ }
154
+ /**
155
+ * The question for pictures that came without one (a photo without caption).
156
+ *
157
+ * @param lang language of the answer
158
+ * @param count number of pictures
159
+ */
160
+ function imageOnlyQuestion(lang, count = 1) {
161
+ if (lang === 'ru') {
162
+ return count > 1 ? 'Что на этих картинках?' : 'Что на этой картинке?';
163
+ }
164
+ if (lang === 'de') {
165
+ return count > 1 ? 'Was ist auf diesen Bildern?' : 'Was ist auf diesem Bild?';
166
+ }
167
+ return count > 1 ? 'What is in these pictures?' : 'What is in this picture?';
168
+ }
169
+ /**
170
+ * The answer to a video that came without a question.
171
+ *
172
+ * @param lang language of the answer
173
+ */
174
+ function videoNotSupportedText(lang) {
175
+ if (lang === 'ru') {
176
+ return 'Видео я пока смотреть не умею.';
177
+ }
178
+ if (lang === 'de') {
179
+ return 'Videos kann ich mir noch nicht ansehen.';
180
+ }
181
+ return "I can't watch videos yet.";
182
+ }
183
+ /**
184
+ * The answer to pictures when there is no cloud model to look at them.
185
+ *
186
+ * @param lang language of the answer
187
+ */
188
+ function noVisionText(lang) {
189
+ if (lang === 'ru') {
190
+ return 'Картинки я могу посмотреть только с облачной моделью.';
191
+ }
192
+ if (lang === 'de') {
193
+ return 'Bilder kann ich mir nur mit einem Cloud-Modell ansehen.';
194
+ }
195
+ return 'I can only look at pictures with a cloud model.';
196
+ }
197
+ /**
198
+ * Note for the model in the user turn: what came along besides the text. A model that does not get the pictures
199
+ * (an older pi-agent) at least knows that there were some.
200
+ *
201
+ * @param images number of pictures attached
202
+ * @param video whether a video came along
203
+ */
204
+ function attachmentNote(images, video) {
205
+ const parts = [];
206
+ if (images) {
207
+ parts.push(images > 1 ? `${images} pictures are attached.` : 'A picture is attached.');
208
+ }
209
+ if (video) {
210
+ parts.push('The user also sent a video, which you cannot watch yet - say so if the question is about it.');
211
+ }
212
+ return parts.length ? `(${parts.join(' ')})` : '';
213
+ }
214
+ /**
215
+ * How the question is kept in the conversation history: the pictures are not repeated in later turns, a marker says
216
+ * there were some.
217
+ *
218
+ * @param question the question
219
+ * @param images number of pictures
220
+ * @param video whether a video came along
221
+ */
222
+ function historyText(question, images, video) {
223
+ const marks = [images ? `[${images > 1 ? `${images} pictures` : 'picture'}]` : '', video ? '[video]' : ''];
224
+ return [...marks, question].filter(Boolean).join(' ');
225
+ }
226
+ //# sourceMappingURL=media.js.map