iobroker.assistant 1.2.0 → 1.3.1

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.
Files changed (60) hide show
  1. package/README.md +17 -0
  2. package/admin/custom/.vite/manifest.json +18 -18
  3. package/admin/custom/assets/Components-D77NmS_I.js +2 -0
  4. package/admin/custom/assets/{_virtual_mf-localSharedImportMap___mfe_internal__ConfigCustomAssistant__mf_owner__1-C5NPM0Of.js → _virtual_mf-localSharedImportMap___mfe_internal__ConfigCustomAssistant__mf_owner__1-B4YbqhBb.js} +1 -1
  5. package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomAssistant__mf_owner__163539842885036__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-Cdyjt5KT.js → _virtual_mf___mfe_internal__ConfigCustomAssistant__mf_owner__163539842885036__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-o4EU_Ut5.js} +10 -10
  6. package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomAssistant__mf_owner__163539842885036__loadShare__shared-Dsw5zWS8.js → _virtual_mf___mfe_internal__ConfigCustomAssistant__mf_owner__163539842885036__loadShare__shared--be2hWbV.js} +1 -1
  7. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomAssistant__mf_owner__163539842885036__prebuild___mf_0_iobroker_mf_1_json_mf_2_config__prebuild__-BsTz_EWE.js +122 -0
  8. package/admin/custom/assets/{hostInit-jJe8bIfV.js → hostInit-Blh7a-Qe.js} +1 -1
  9. package/admin/custom/assets/{lib-CL8mXvI6.js → lib-DBbNtolG.js} +1 -1
  10. package/admin/custom/assets/{mf-entry-bootstrap-0-ab227aa6.js → mf-entry-bootstrap-0-7abd30d3.js} +2 -2
  11. package/admin/custom/assets/{pendingShares-wrGtfliQ.js → pendingShares-U0D1MYK4.js} +1 -1
  12. package/admin/custom/customComponents.js +2 -2
  13. package/admin/custom/i18n/de.json +16 -0
  14. package/admin/custom/i18n/en.json +16 -0
  15. package/admin/custom/i18n/es.json +16 -0
  16. package/admin/custom/i18n/fr.json +16 -0
  17. package/admin/custom/i18n/it.json +16 -0
  18. package/admin/custom/i18n/nl.json +16 -0
  19. package/admin/custom/i18n/pl.json +16 -0
  20. package/admin/custom/i18n/pt.json +16 -0
  21. package/admin/custom/i18n/ru.json +16 -0
  22. package/admin/custom/i18n/uk.json +16 -0
  23. package/admin/custom/i18n/zh-cn.json +16 -0
  24. package/admin/custom/index.html +2 -2
  25. package/admin/custom/mf-manifest.json +1 -1
  26. package/admin/custom/mf-stats.json +1 -1
  27. package/admin/i18n/de.json +25 -2
  28. package/admin/i18n/en.json +25 -2
  29. package/admin/i18n/es.json +25 -2
  30. package/admin/i18n/fr.json +25 -2
  31. package/admin/i18n/it.json +25 -2
  32. package/admin/i18n/nl.json +25 -2
  33. package/admin/i18n/pl.json +25 -2
  34. package/admin/i18n/pt.json +25 -2
  35. package/admin/i18n/ru.json +25 -2
  36. package/admin/i18n/uk.json +25 -2
  37. package/admin/i18n/zh-cn.json +25 -2
  38. package/admin/jsonConfig.json +197 -13
  39. package/build/lib/credentials.js +11 -5
  40. package/build/lib/credentials.js.map +1 -1
  41. package/build/lib/llm.js +17 -3
  42. package/build/lib/llm.js.map +1 -1
  43. package/build/lib/nlu.js +263 -9
  44. package/build/lib/nlu.js.map +1 -1
  45. package/build/lib/piAgent.js +94 -0
  46. package/build/lib/piAgent.js.map +1 -0
  47. package/build/lib/systemAi.js +124 -0
  48. package/build/lib/systemAi.js.map +1 -0
  49. package/build/lib/voice/engines.js +31 -3
  50. package/build/lib/voice/engines.js.map +1 -1
  51. package/build/lib/voice/esphome.js +3 -0
  52. package/build/lib/voice/esphome.js.map +1 -1
  53. package/build/lib/voice/openrouter.js +220 -0
  54. package/build/lib/voice/openrouter.js.map +1 -0
  55. package/build/main.js +691 -116
  56. package/build/main.js.map +1 -1
  57. package/io-package.json +40 -30
  58. package/package.json +1 -1
  59. package/admin/custom/assets/Components-BSewCBfS.js +0 -2
  60. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomAssistant__mf_owner__163539842885036__prebuild___mf_0_iobroker_mf_1_json_mf_2_config__prebuild__-SgSagTOv.js +0 -122
@@ -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/custom all use the OpenAI-compatible SDK path. */\nexport type Provider = 'openai' | 'anthropic' | 'gemini' | 'deepseek' | '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 custom: { sdk: 'openai', baseUrl: '', defaultModel: '' },\n};\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({ apiKey: opts.apiKey, baseURL: opts.baseUrl || undefined });\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 await client.models.list();\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":";;;;;;AAoCA,0CAUC;AAOD,0CAEC;AAvDD,oDAA4B;AAC5B,4DAA0C;AAE1C,uCAAwE;AAcxE,wGAAwG;AAC3F,QAAA,gBAAgB,GAAqC;IAC9D,MAAM,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,aAAa,EAAE;IACnE,SAAS,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,mBAAmB,EAAE;IAC/E,MAAM,EAAE;QACJ,GAAG,EAAE,QAAQ;QACb,OAAO,EAAE,0DAA0D;QACnE,YAAY,EAAE,kBAAkB;KACnC;IACD,QAAQ,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,0BAA0B,EAAE,YAAY,EAAE,eAAe,EAAE;IAC/F,oDAAoD;IACpD,UAAU,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,8BAA8B,EAAE,YAAY,EAAE,oBAAoB,EAAE;IAC1G,MAAM,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE;CAC3D,CAAC;AAEF,mGAAmG;AACtF,QAAA,kBAAkB,GAAG,EAAE,cAAc,EAAE,0BAA0B,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;AAExG,0GAA0G;AAC1G,SAAgB,eAAe,CAC3B,QAA4B,EAC5B,aAAsB;IAEtB,MAAM,MAAM,GAAG,wBAAgB,CAAE,QAAqB,IAAI,QAAQ,CAAC,IAAI,wBAAgB,CAAC,MAAM,CAAC;IAC/F,OAAO;QACH,GAAG,EAAE,MAAM,CAAC,GAAG;QACf,OAAO,EAAE,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO;QACrE,YAAY,EAAE,MAAM,CAAC,YAAY;KACpC,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,SAAgB,eAAe,CAAC,QAA4B,EAAE,MAAc;IACxE,OAAO,MAAM,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAC7D,CAAC;AAeD,kFAAkF;AAClF,SAAS,QAAQ,CAAC,KAAU;IACxB,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO,SAAS,CAAC;IACrB,CAAC;IACD,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QACnC,OAAO;YACH,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC;YACtC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC;YACxC,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,IAAI,SAAS;YAC7D,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,2BAA2B,CAAC,IAAI,SAAS;SACrE,CAAC;IACN,CAAC;IACD,OAAO;QACH,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC;QACvC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC;QAC5C,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,aAAa,CAAC,IAAI,SAAS;KAC7E,CAAC;AACN,CAAC;AAED,mGAAmG;AACnG,SAAS,UAAU,CAAC,KAAa,EAAE,SAAmB;IAClD,OAAO,SAAS,KAAK,GAAG,CAAC,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;AACxF,CAAC;AAED;;;;;;;;;GASG;AACH,MAAa,QAAQ;IACD,QAAQ,CAAyB;IACjD,gFAAgF;IACxE,eAAe,CAAS;IACxB,WAAW,GAA2B,IAAI,CAAC;IAElC,YAAY,CAAS;IACrB,SAAS,CAAS;IAClB,KAAK,CAAS;IACd,OAAO,CAAuB;IAC9B,GAAG,CAAkB;IACrB,SAAS,GAAG,CAAC,CAAC,CAAC,qCAAqC;IACrE,2FAA2F;IAC1E,YAAY,CAAS;IACrB,OAAO,CAAS;IAChB,KAAK,CAAW;IAEhB,MAAM,CAAU;IAChB,SAAS,CAAa;IAEvC,YAAY,IAAqB;QAC7B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC;QACvE,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC;QACnD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;QAClC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACxB,IAAI,CAAC,eAAe,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACjD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC;QAC5C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC;QACxC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;QAC9B,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACpB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAEpE,IAAI,IAAI,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;YAChC,IAAI,CAAC,SAAS,GAAG,IAAI,aAAS,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,SAAS,EAAE,CAAC,CAAC;QAChG,CAAC;aAAM,CAAC;YACJ,IAAI,CAAC,MAAM,GAAG,IAAI,gBAAM,CAAC;gBACrB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,SAAS;gBAClC,cAAc,EAAE,IAAI,CAAC,QAAQ,KAAK,YAAY,CAAC,CAAC,CAAC,0BAAkB,CAAC,CAAC,CAAC,SAAS;aAClF,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAED,2GAA2G;IAC3G,IAAW,KAAK;QACZ,OAAO,IAAI,CAAC,eAAe,CAAC;IAChC,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,WAAW;QACrB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC,eAAe,CAAC;QAChC,CAAC;QACD,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,UAAU,EAAE;aACjC,IAAI,CAAC,MAAM,CAAC,EAAE;YACX,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;YACrE,CAAC;YACD,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACjC,IAAI,CAAC,GAAG,CAAC,IAAI,CACT,gCAAgC,MAAM,CAAC,CAAC,CAAC,oBACrC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAC/D,6DAA6D,CAChE,CAAC;YACF,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;QACrB,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,CAAC,EAAE;YACP,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC,gEAAgE;YACzF,MAAM,IAAI,KAAK,CACX,4EAA6E,CAAW,CAAC,OAAO,GAAG,CACtG,CAAC;QACN,CAAC,CAAC,CAAC;QACP,OAAO,IAAI,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,GAAG,CACZ,QAAgB,EAChB,YAAqB,EACrB,UAA6D,EAAE;QAE/D,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QACzB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,MAAM,OAAO,CAAC,MAAM,cAAc,QAAQ,EAAE,CAAC,CAAC;QACpG,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,YAAY,IAAI,IAAI,CAAC,YAAY,CAAC;QAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,KAAK,EAAE,OAAO;YACd,KAAK,EAAE,GAAG,IAAI,CAAC,YAAY,MAAM,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM,WAAW,OAAO,CAAC,MAAM,kBAAkB;YAC5G,IAAI,EAAE;gBACF,QAAQ,EAAE,IAAI,CAAC,YAAY;gBAC3B,GAAG,EAAE,IAAI,CAAC,QAAQ;gBAClB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,SAAS;gBAClC,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,MAAM,EAAE,GAAG;gBACX,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;aACrC;SACJ,CAAC,CAAC;QACH,MAAM,MAAM,GACR,IAAI,CAAC,QAAQ,KAAK,WAAW;YACzB,CAAC,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,CAAC;YACjD,CAAC,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;QACvD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,eAAe,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,SAAS,MAAM,EAAE,CAAC,CAAC;QACrE,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,2EAA2E;IACnE,IAAI,CAAC,KAAa,EAAE,OAAgB;QACxC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,KAAK,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC;YAC3D,OAAO,CAAC,8DAA8D;QAC1E,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QACrD,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YACD,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACL,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAC3B,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,KAAK,KAAK,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACvG,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,SAAiB;QACpD,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;YAChC,MAAM,MAAM,GAAG,IAAI,CAAC,SAAsB,CAAC;YAC3C,MAAM,IAAI,GAAQ,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAC3C,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,UAAU,EAAE,SAAS;gBACrB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;aAChD,CAAC,CAAC;YACH,OAAO,IAAI,CAAC,OAAO;iBACd,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;iBACrC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;iBACvB,IAAI,CAAC,EAAE,CAAC;iBACR,IAAI,EAAE,CAAC;QAChB,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAgB,CAAC;QACrC,MAAM,IAAI,GAAQ,MAAM,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;YACnD,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,UAAU,EAAE,SAAS;YACrB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;SAChD,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAChE,CAAC;IAED,8FAA8F;IACvF,KAAK,CAAC,SAAS,CAAC,IAAY,EAAE,cAAsB;QACvD,MAAM,OAAO,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACpC,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,OAAO,EAAE,CAAC;QACd,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAChB,+DAA+D,cAAc,IAAI;YAC7E,yFAAyF,OAAO,EAAE,EACtG,EAAE,CACL,CAAC;IACN,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,QAAQ,CAAC,IAAY,EAAE,QAAgB,EAAE,OAAO,GAAG,EAAE;QAC9D,MAAM,OAAO,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACpC,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,OAAO,EAAE,CAAC;QACd,CAAC;QACD,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC9B,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,yDAAyD;gBAC9F,SAAS,QAAQ,CAAC,CAAC,CAAC,QAAQ,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,gDAAgD;gBAC3F,4FAA4F;gBAC5F,8CAA8C,OAAO,EAAE,EAC3D,GAAG,CACN,CAAC;YACF,OAAO,MAAM,IAAI,OAAO,CAAC;QAC7B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,6CAA8C,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;YACnF,OAAO,OAAO,CAAC;QACnB,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,kBAAkB,CAAC,IAAY,EAAE,IAAY,EAAE,QAAgB,EAAE,OAAO,GAAG,EAAE;QACtF,MAAM,OAAO,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACpC,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,OAAO,EAAE,CAAC;QACd,CAAC;QACD,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC9B,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,oDAAoD;gBACzF,kCAAkC,QAAQ,CAAC,CAAC,CAAC,QAAQ,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI;gBACxE,wEAAwE;gBACxE,wFAAwF;gBACxF,gEAAgE;gBAChE,sFAAsF;gBACtF,uFAAuF;gBACvF,uBAAuB,IAAI,mDAAmD,OAAO,EAAE,EAC3F,GAAG,CACN,CAAC;YACF,OAAO,MAAM,IAAI,OAAO,CAAC;QAC7B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,yDAA0D,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;YAC/F,OAAO,OAAO,CAAC;QACnB,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,QAAgB;QAClD,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACrC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACjB,OAAO,KAAK,CAAC;QACjB,CAAC;QACD,IAAI,CAAC;YACD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAC/B,qCAAqC,IAAI,4CAA4C;gBACjF,mFAAmF,IAAI,EAAE,EAC7F,CAAC,CACJ,CAAC;YACF,OAAO,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC9C,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,qBAAqB,IAAI,OAAQ,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;YACtE,OAAO,KAAK,CAAC;QACjB,CAAC;IACL,CAAC;IAED,6FAA6F;IACtF,KAAK,CAAC,cAAc;QACvB,IAAI,CAAC;YACD,IAAI,IAAI,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;gBAChC,MAAM,MAAM,GAAG,IAAI,CAAC,SAAsB,CAAC;gBAC3C,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;oBACzB,KAAK,EAAE,MAAM,IAAI,CAAC,WAAW,EAAE;oBAC/B,UAAU,EAAE,CAAC;oBACb,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;iBAChD,CAAC,CAAC;YACP,CAAC;iBAAM,CAAC;gBACJ,MAAM,MAAM,GAAG,IAAI,CAAC,MAAgB,CAAC;gBACrC,IAAI,IAAI,CAAC,YAAY,KAAK,YAAY,EAAE,CAAC;oBACrC,uEAAuE;oBACvE,MAAM,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBAC7B,CAAC;qBAAM,CAAC;oBACJ,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC/B,CAAC;YACL,CAAC;YACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;QACxB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAG,CAAW,CAAC,OAAO,EAAE,CAAC;QACtD,CAAC;IACL,CAAC;IAED,+EAA+E;IACxE,KAAK,CAAC,UAAU;QACnB,IAAI,IAAI,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;YAChC,MAAM,MAAM,GAAG,IAAI,CAAC,SAAsB,CAAC;YAC3C,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAA2C,CAAC;YACnF,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3C,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAgB,CAAC;QACrC,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAA2C,CAAC;QACnF,gGAAgG;QAChG,MAAM,OAAO,GACT,uGAAuG,CAAC;QAC5G,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;aAClB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;aACtB,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;aAC/B,IAAI,EAAE,CAAC;IAChB,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,UAAU,CACpB,KAAa,EACb,IAAe,EACf,IAAwB;QAExB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC;YACD,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;QAC1D,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,IAAI,CAAC,KAAK,EAAE,CAAC;gBACT,KAAK,EAAE,OAAO;gBACd,KAAK,EAAE,OAAO;gBACd,KAAK,EAAE,SAAS,KAAK,GAAG,CAAC,YAAa,CAAW,CAAC,OAAO,EAAE;gBAC3D,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;gBACtB,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAA,mBAAS,EAAC,CAAC,CAAC,EAAE;aACtC,CAAC,CAAC;YACH,MAAM,CAAC,CAAC;QACZ,CAAC;IACL,CAAC;IAEO,aAAa;QACjB,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,IAAI,CAAC,SAAS,uBAAuB,EAAE,CAAC,CAAC;QAC/G,OAAO,oEAAoE,CAAC;IAChF,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,IAAY,EAAE,IAA6B;QAC7D,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI,EAAE,CAAC;YACR,OAAO,EAAE,KAAK,EAAE,iBAAiB,IAAI,EAAE,EAAE,CAAC;QAC9C,CAAC;QACD,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;YAC1C,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACjG,OAAO,MAAM,CAAC;QAClB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,YAAa,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;YAC9D,OAAO,EAAE,KAAK,EAAG,CAAW,CAAC,OAAO,EAAE,CAAC;QAC3C,CAAC;IACL,CAAC;IAED,2EAA2E;IACnE,KAAK,CAAC,SAAS,CACnB,QAAgB,EAChB,YAAoB,EACpB,UAA6D,EAAE;QAE/D,MAAM,MAAM,GAAG,IAAI,CAAC,MAAgB,CAAC;QACrC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC/B,IAAI,EAAE,UAAmB;YACzB,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE;SACnF,CAAC,CAAC,CAAC;QAEJ,MAAM,QAAQ,GAAU,EAAE,CAAC;QAC3B,IAAI,YAAY,EAAE,CAAC;YACf,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;QACnD,uFAAuF;QACvF,IAAI,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAEjC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC;YAClD,MAAM,OAAO,GAAG;gBACZ,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,QAAQ;gBACR,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;gBACvC,UAAU,EAAE,IAAI,CAAC,SAAS;aAC7B,CAAC;YACF,IAAI,CAAC,IAAI,CAAC,sBAAsB,KAAK,EAAE,EAAE,OAAO,CAAC,CAAC;YAClD,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YACvG,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,sBAAsB,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YAC9E,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnB,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC;YACxB,IAAI,CAAC,KAAK,EAAE,CAAC;gBACT,KAAK,EAAE,OAAO;gBACd,KAAK,EAAE,UAAU,CACb,KAAK,EACL,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAO,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CACrE;gBACD,EAAE;gBACF,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;gBAC5B,IAAI,EAAE;oBACF,IAAI;oBACJ,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,OAAO,EAAE,GAAG,EAAE;oBACxE,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,KAAK,EAAE,IAAI,CAAC,KAAK;iBACpB;aACJ,CAAC,CAAC;YAEH,IAAI,CAAC,GAAG,CAAC,UAAU,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;gBAC5C,OAAO,MAAM,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YAC5C,CAAC;YAED,KAAK,MAAM,EAAE,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;gBAC9B,IAAI,IAAI,GAA4B,EAAE,CAAC;gBACvC,IAAI,CAAC;oBACD,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC;gBACrD,CAAC;gBAAC,MAAM,CAAC;oBACL,iBAAiB;gBACrB,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBAC1D,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC1F,CAAC;QACL,CAAC;QACD,OAAO,IAAI,CAAC,aAAa,EAAE,CAAC;IAChC,CAAC;IAED;;;OAGG;IACK,uBAAuB,CAAC,QAAe;QAC3C,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACvB,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC3B,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;oBACxB,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;wBAC7B,OAAO,CAAC,CAAC,aAAa,CAAC;oBAC3B,CAAC;gBACL,CAAC;YACL,CAAC;QACL,CAAC;QACD,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC3C,IAAI,CAAC,IAAI,EAAE,CAAC;YACR,OAAO;QACX,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YACnC,IAAI,CAAC,OAAO,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;QAChG,CAAC;aAAM,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC5D,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAChD,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;gBAC7B,CAAC,CAAC,aAAa,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;YAC5C,CAAC;QACL,CAAC;IACL,CAAC;IAED,2EAA2E;IACnE,KAAK,CAAC,YAAY,CACtB,QAAgB,EAChB,YAAoB,EACpB,UAA6D,EAAE;QAE/D,MAAM,MAAM,GAAG,IAAI,CAAC,SAAsB,CAAC;QAC3C,MAAM,KAAK,GAAU,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACtC,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,YAAY,EAAE,CAAC,CAAC,UAAwC;SAC3D,CAAC,CAAC,CAAC;QACJ,4FAA4F;QAC5F,iGAAiG;QACjG,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACf,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC;QACnG,CAAC;QACD,MAAM,MAAM,GAAQ,YAAY;YAC5B,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC;YAC9E,CAAC,CAAC,SAAS,CAAC;QAEhB,MAAM,QAAQ,GAAU;YACpB,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YAC3D,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE;SACtC,CAAC;QACF,wFAAwF;QACxF,IAAI,KAAK,GAAG,CAAC,CAAC;QAEd,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC;YAClD,yFAAyF;YACzF,oFAAoF;YACpF,IAAI,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC;YACvC,MAAM,OAAO,GAAG;gBACZ,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,UAAU,EAAE,IAAI,CAAC,SAAS;gBAC1B,MAAM;gBACN,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;gBACvC,QAAQ;aACX,CAAC;YACF,IAAI,CAAC,IAAI,CAAC,yBAAyB,KAAK,EAAE,EAAE,OAAO,CAAC,CAAC;YACrD,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YAC/F,IAAI,CAAC,IAAI,CAAC,yBAAyB,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YAE1F,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;YACxE,IAAI,CAAC,KAAK,EAAE,CAAC;gBACT,KAAK,EAAE,OAAO;gBACd,KAAK,EAAE,UAAU,CACb,KAAK,EACL,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAC3C;gBACD,EAAE;gBACF,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;gBAC5B,IAAI,EAAE;oBACF,IAAI;oBACJ,QAAQ,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE;oBAClE,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,KAAK,EAAE,IAAI,CAAC,KAAK;iBACpB;aACJ,CAAC,CAAC;YACH,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;gBACnB,OAAO,IAAI,CAAC,OAAO;qBACd,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;qBACrC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;qBACvB,IAAI,CAAC,EAAE,CAAC;qBACR,IAAI,EAAE,CAAC;YAChB,CAAC;YAED,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5D,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC;YACxB,MAAM,WAAW,GAAG,EAAE,CAAC;YACvB,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;gBACxB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;gBACrD,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACnG,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,OAAO,IAAI,CAAC,aAAa,EAAE,CAAC;IAChC,CAAC;CACJ;AAvgBD,4BAugBC","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"]}
package/build/lib/nlu.js CHANGED
@@ -11,10 +11,12 @@
11
11
  */
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  exports.Nlu = void 0;
14
+ exports.spelledNumbersToDigits = spelledNumbersToDigits;
14
15
  exports.parseClockTime = parseClockTime;
15
16
  exports.parseWeekdays = parseWeekdays;
16
17
  exports.parseDurationSeconds = parseDurationSeconds;
17
18
  exports.isStopCommand = isStopCommand;
19
+ exports.mentionsScript = mentionsScript;
18
20
  exports.normalize = normalize;
19
21
  exports.splitCommands = splitCommands;
20
22
  const TURN_ON = new Set([
@@ -303,6 +305,158 @@ const NUMBER_WORDS = {
303
305
  девять: '9',
304
306
  десять: '10',
305
307
  };
308
+ /** Units 1–9 (normalized), de/en/ru — the parts of a spelled-out number above twelve */
309
+ const UNIT_WORDS = {
310
+ eins: 1,
311
+ zwei: 2,
312
+ drei: 3,
313
+ vier: 4,
314
+ fuenf: 5,
315
+ sechs: 6,
316
+ sieben: 7,
317
+ acht: 8,
318
+ neun: 9,
319
+ one: 1,
320
+ two: 2,
321
+ three: 3,
322
+ four: 4,
323
+ five: 5,
324
+ six: 6,
325
+ seven: 7,
326
+ eight: 8,
327
+ nine: 9,
328
+ один: 1,
329
+ одна: 1,
330
+ одно: 1,
331
+ два: 2,
332
+ две: 2,
333
+ три: 3,
334
+ четыре: 4,
335
+ пять: 5,
336
+ шесть: 6,
337
+ семь: 7,
338
+ восемь: 8,
339
+ девять: 9,
340
+ };
341
+ /** 10–19, de/en/ru */
342
+ const TEEN_WORDS = {
343
+ zehn: 10,
344
+ elf: 11,
345
+ zwoelf: 12,
346
+ dreizehn: 13,
347
+ vierzehn: 14,
348
+ fuenfzehn: 15,
349
+ sechzehn: 16,
350
+ siebzehn: 17,
351
+ achtzehn: 18,
352
+ neunzehn: 19,
353
+ ten: 10,
354
+ eleven: 11,
355
+ twelve: 12,
356
+ thirteen: 13,
357
+ fourteen: 14,
358
+ fifteen: 15,
359
+ sixteen: 16,
360
+ seventeen: 17,
361
+ eighteen: 18,
362
+ nineteen: 19,
363
+ десять: 10,
364
+ одиннадцать: 11,
365
+ двенадцать: 12,
366
+ тринадцать: 13,
367
+ четырнадцать: 14,
368
+ пятнадцать: 15,
369
+ шестнадцать: 16,
370
+ семнадцать: 17,
371
+ восемнадцать: 18,
372
+ девятнадцать: 19,
373
+ };
374
+ /** Tens 20–90, de/en/ru */
375
+ const TEN_WORDS = {
376
+ zwanzig: 20,
377
+ dreissig: 30,
378
+ vierzig: 40,
379
+ fuenfzig: 50,
380
+ sechzig: 60,
381
+ siebzig: 70,
382
+ achtzig: 80,
383
+ neunzig: 90,
384
+ twenty: 20,
385
+ thirty: 30,
386
+ forty: 40,
387
+ fifty: 50,
388
+ sixty: 60,
389
+ seventy: 70,
390
+ eighty: 80,
391
+ ninety: 90,
392
+ двадцать: 20,
393
+ тридцать: 30,
394
+ сорок: 40,
395
+ пятьдесят: 50,
396
+ шестьдесят: 60,
397
+ семьдесят: 70,
398
+ восемьдесят: 80,
399
+ девяносто: 90,
400
+ };
401
+ const HUNDRED_WORDS = new Set(['hundert', 'einhundert', 'hundred', 'сто']);
402
+ /** Value of one spelled-out number word ("fuenfundzwanzig", "twenty-five", "fünfzig"), or null */
403
+ function numberWordValue(word) {
404
+ if (HUNDRED_WORDS.has(word)) {
405
+ return 100;
406
+ }
407
+ const direct = TEN_WORDS[word] ?? TEEN_WORDS[word] ?? UNIT_WORDS[word];
408
+ if (direct !== undefined) {
409
+ return direct;
410
+ }
411
+ // de "fünfundzwanzig" (unit first, "ein" as in "einundzwanzig"), en "twenty-five"
412
+ const de = /^(\p{L}+?)und(\p{L}+)$/u.exec(word);
413
+ if (de && TEN_WORDS[de[2]] !== undefined && (de[1] === 'ein' || UNIT_WORDS[de[1]] !== undefined)) {
414
+ return TEN_WORDS[de[2]] + (UNIT_WORDS[de[1]] ?? 1);
415
+ }
416
+ const en = /^(\p{L}+)-(\p{L}+)$/u.exec(word);
417
+ if (en && TEN_WORDS[en[1]] !== undefined && UNIT_WORDS[en[2]] !== undefined) {
418
+ return TEN_WORDS[en[1]] + UNIT_WORDS[en[2]];
419
+ }
420
+ return null;
421
+ }
422
+ /**
423
+ * Spelled-out whole numbers up to 100 → digits in normalized text (de/en/ru): "fuenfzig prozent" → "50 prozent",
424
+ * "twenty five" → "25", "двадцать пять" → "25", "one hundred" → "100". Speech recognition writes numbers as words
425
+ * often enough that a level command would otherwise miss. Articles ("ein", "a") stay words.
426
+ *
427
+ * @param text normalized text
428
+ */
429
+ function spelledNumbersToDigits(text) {
430
+ const words = text.split(/\s+/).filter(Boolean);
431
+ const out = [];
432
+ for (let i = 0; i < words.length; i++) {
433
+ const [, word, tail] = /^(.*?)([.,!?;:]*)$/u.exec(words[i]);
434
+ let value = numberWordValue(word);
435
+ if (value === null) {
436
+ // "one hundred", "a hundred", "ein hundert"
437
+ const next = words[i + 1]?.replace(/[.,!?;:]+$/u, '');
438
+ if (['one', 'a', 'ein'].includes(word) && next && HUNDRED_WORDS.has(next)) {
439
+ out.push(`100${/[.,!?;:]+$/u.exec(words[i + 1])?.[0] || ''}`);
440
+ i++;
441
+ continue;
442
+ }
443
+ out.push(words[i]);
444
+ continue;
445
+ }
446
+ let end = tail;
447
+ // en "twenty five", ru "двадцать пять": a ten followed by a unit
448
+ if (TEN_WORDS[word] !== undefined && !tail && i + 1 < words.length) {
449
+ const [, nextWord, nextTail] = /^(.*?)([.,!?;:]*)$/u.exec(words[i + 1]);
450
+ if (UNIT_WORDS[nextWord] !== undefined) {
451
+ value += UNIT_WORDS[nextWord];
452
+ end = nextTail;
453
+ i++;
454
+ }
455
+ }
456
+ out.push(`${value}${end}`);
457
+ }
458
+ return out.join(' ');
459
+ }
306
460
  /** Fixed phrases replaced up front so their fractions don't collide with the general number-word pass. */
307
461
  const DURATION_PHRASES = [
308
462
  [/(?<![\p{L}])dreiviertelstunde(?![\p{L}])/iu, '45 minuten'],
@@ -315,6 +469,26 @@ const DURATION_PHRASES = [
315
469
  [/quarter hour/iu, '15 minutes'],
316
470
  [/(?<![\p{L}])полтора часа(?![\p{L}])/iu, '90 минут'],
317
471
  [/(?<![\p{L}])полчаса(?![\p{L}])/iu, '30 минут'],
472
+ // a unit without a number: "in einer Stunde", "in an hour", "через час", "через минуту"
473
+ [/(?<![\p{L}])einer halben stunde(?![\p{L}])/iu, '30 minuten'],
474
+ [/(?<![\p{L}])einer? (stunde|minute)(?![\p{L}])/iu, '1 $1'],
475
+ [/(?<![\p{L}])an? (hour|minute)(?![\p{L}])/iu, '1 $1'],
476
+ [/(?<![\p{L}])через (час|минуту)(?![\p{L}])/iu, 'через 1 $1'],
477
+ ];
478
+ /** Prepositions of a delay: "in 2 Stunden", "nach 10 Minuten", "через 2 часа", "after 5 minutes" */
479
+ const DELAY_PREPOSITION_RE = /(?<![\p{L}])(?:in|nach|через|after)\s+/gu;
480
+ /**
481
+ * A clock time that cannot be anything else: "22:30", "7 Uhr", "7 o'clock", "7 pm", "в 7 часов", "в 10 вечера",
482
+ * "halb acht" — unlike a bare "um 3", which is as often "um 3 lauter" or "um 20 Prozent".
483
+ */
484
+ const CLOCK_RE = /\d{1,2}:\d{2}|\d\s*(?:uhr|o.?clock|[ap]m)(?![\p{L}])|(?<![\p{L}])(?:в|во)\s+\d{1,2}\s*(?:час\p{L}*|утра|вечера|ночи|дня)(?![\p{L}])|(?<![\p{L}])(?:halb|viertel|dreiviertel|quarter|half past)\s/u;
485
+ /** Percent values, removed before looking for a clock time ("um 20 Prozent" is no time) */
486
+ const PERCENT_RE = /\d+(?:[.,]\d+)?\s*(?:%|prozent|percent|процент\p{L}*)/giu;
487
+ /** Day words for a clock time, the longer first ("übermorgen" before "morgen") */
488
+ const DAY_WORDS = [
489
+ [/(?<![\p{L}])(?:uebermorgen|day after tomorrow|послезавтра)(?![\p{L}])/u, 2],
490
+ [/(?<![\p{L}])(?:morgen|tomorrow|завтра)(?![\p{L}])/u, 1],
491
+ [/(?<![\p{L}])(?:heute|today|сегодня)(?![\p{L}])/u, 0],
318
492
  ];
319
493
  const HOUR_RE = /(\d+(?:[.,]\d+)?)\s*(?:stunden?|hours?|hrs?|h|час(?:а|ов)?)(?![\p{L}])/giu;
320
494
  const MIN_RE = /(\d+(?:[.,]\d+)?)\s*(?:minuten?|mins?|minutes?|минут[уы]?)(?![\p{L}])/giu;
@@ -570,6 +744,19 @@ const STOP_RE = /(?<![\p{L}])(stopp?\p{L}*|halt|aufhoer\p{L}*|ruhe|ruhig|genug|s
570
744
  function isStopCommand(text) {
571
745
  return STOP_RE.test(normalize(text));
572
746
  }
747
+ /**
748
+ * "Skript"/"script"/"скрипт" in any form, German compounds ("Lichtskript") and Cyrillic flexion ("скрипте") included,
749
+ * plus JavaScript/TypeScript/Blockly. "script" only at the start of a word, so "description" or "subscription" do not
750
+ * count; "скрипка"/"скрипит" are other words.
751
+ */
752
+ const SCRIPT_RE = /skript|(?<![\p{L}])(?:java|type)?script|скрипт|blockly/iu;
753
+ /**
754
+ * True if the text is about a script. Such a request goes to the cloud model only: the rules would take its details for
755
+ * commands of their own ("… a script that switches the kitchen light every hour" was answered with the time).
756
+ */
757
+ function mentionsScript(text) {
758
+ return SCRIPT_RE.test(normalize(text));
759
+ }
573
760
  /** Normalize for matching: lowercase, German umlauts → ascii, ß → ss, Russian ё → е. */
574
761
  function normalize(s) {
575
762
  return s
@@ -702,6 +889,12 @@ class Nlu {
702
889
  if (combined.length > 1) {
703
890
  return combined;
704
891
  }
892
+ // Several commands, one of them for later, and not every part understood: the time must not be applied to
893
+ // the whole sentence ("Licht an und Rollo in 10 Minuten zu" would switch the light on in 10 minutes).
894
+ const whole = this.prepare(text);
895
+ if (whole && splitCommands(text).length > 1 && this.parseWhen(whole)) {
896
+ return [];
897
+ }
705
898
  const single = this.parse(text);
706
899
  return single ? [single] : [];
707
900
  }
@@ -725,15 +918,18 @@ class Nlu {
725
918
  const intents = [];
726
919
  for (let i = 0; i < parts.length; i++) {
727
920
  // A segment stating no action borrows the nearest sibling's — looking ahead first, because the
728
- // verb usually comes last in this shape ("schalte A | und B **an**").
729
- let inherited = null;
921
+ // verb usually comes last in this shape ("schalte A | und B **an**"). The time comes with it: in
922
+ // "schalte A und B in 10 Minuten aus" both wait.
923
+ let from = -1;
730
924
  if (isBare(features[i])) {
731
- inherited = features.slice(i + 1).find(f => !isBare(f)) || null;
732
- for (let j = i - 1; !inherited && j >= 0; j--) {
733
- inherited = isBare(features[j]) ? null : features[j];
925
+ from = features.findIndex((f, j) => j > i && !isBare(f));
926
+ for (let j = i - 1; from < 0 && j >= 0; j--) {
927
+ from = isBare(features[j]) ? -1 : j;
734
928
  }
735
929
  }
736
- const intent = this.parsePrepared(parts[i], inherited);
930
+ const inherited = from >= 0 ? features[from] : null;
931
+ const inheritedTime = from >= 0 ? this.parseWhen(parts[from]) : null;
932
+ const intent = this.parsePrepared(parts[i], inherited, inheritedTime && inheritedTime !== 'unclear' ? inheritedTime : null);
737
933
  if (intent) {
738
934
  intents.push(intent);
739
935
  }
@@ -754,13 +950,27 @@ class Nlu {
754
950
  * first and never inherit anything; `inherited` supplies the action for a segment that names only a
755
951
  * device (see {@link parseCombined}).
756
952
  */
757
- parsePrepared(p, inherited) {
953
+ parsePrepared(p, inherited, inheritedTime = null) {
758
954
  // Timer / alarm intents ("timer 5 minuten", "weck mich um 7 Uhr", "wie lange noch") — before
759
955
  // window/device matching, and device-independent.
760
956
  const schedule = this.parseSchedule(p.raw, p.norm, p.joined);
761
957
  if (schedule) {
762
958
  return schedule;
763
959
  }
960
+ // A command for later ("Licht in 2 Stunden aus", "выключи свет через 2 часа") must never run now: it
961
+ // becomes a deferred device intent, or goes to the LLM. Before the time query — "через 2 часа" has "час".
962
+ const time = this.parseWhen(p);
963
+ if (time) {
964
+ const roomName = this.findRoom(p.joined);
965
+ const device = this.findDevice(p.joined, roomName);
966
+ if (time !== 'unclear') {
967
+ return device ? this.deferred(p, device, roomName, time) : null;
968
+ }
969
+ if (device) {
970
+ return null; // "Licht um 7 aus" — maybe 7 o'clock, maybe not: the LLM asks or knows
971
+ }
972
+ // no device: "um 3 lauter" is a volume step, go on
973
+ }
764
974
  // Time / date query ("what time is it", "which day is today") — device-independent, answered from
765
975
  // the host clock. After the schedule parse so a clock phrase with a timer/alarm keyword still wins.
766
976
  const timeQuery = this.parseTimeQuery(p.joined);
@@ -784,9 +994,52 @@ class Nlu {
784
994
  let features = this.extractFeatures(p);
785
995
  if (inherited && this.namesOnly(p, device, roomName)) {
786
996
  features = inherited;
997
+ if (inheritedTime) {
998
+ return this.deferred(p, device, roomName, inheritedTime, inherited);
999
+ }
787
1000
  }
788
1001
  return this.buildDeviceIntent(device, roomName, features);
789
1002
  }
1003
+ /**
1004
+ * When a command is meant to run: after a delay ("in 2 Stunden", "через полчаса", "in 10 minutes") or at a clock
1005
+ * time ("um 22 Uhr", "morgen um 7:30", "в 10 вечера", "at 10 pm"). `unclear`: a time that could be meant
1006
+ * otherwise ("um 7", a weekday, "jeden Tag") — a device command with it is left to the LLM. Null: no time.
1007
+ */
1008
+ parseWhen(p) {
1009
+ const norm = spelledNumbersToDigits(p.norm);
1010
+ DELAY_PREPOSITION_RE.lastIndex = 0;
1011
+ let m;
1012
+ while ((m = DELAY_PREPOSITION_RE.exec(norm))) {
1013
+ // the duration right after the preposition: "in der Küche" is no delay, "in 2 Stunden" is
1014
+ const words = norm.substring(m.index).split(/\s+/).slice(0, 5).join(' ');
1015
+ const seconds = parseDurationSeconds(words, false);
1016
+ if (seconds) {
1017
+ return { delaySec: seconds };
1018
+ }
1019
+ }
1020
+ const raw = p.raw.replace(PERCENT_RE, ' ');
1021
+ const clock = parseClockTime(raw);
1022
+ if (!clock) {
1023
+ return null;
1024
+ }
1025
+ if (!CLOCK_RE.test(spelledNumbersToDigits(normalize(raw))) || parseWeekdays(p.norm).length) {
1026
+ return 'unclear';
1027
+ }
1028
+ const day = DAY_WORDS.find(([re]) => re.test(p.norm))?.[1];
1029
+ return { at: day === undefined ? { ...clock } : { ...clock, day } };
1030
+ }
1031
+ /** A device command for later: only switching and setting (a question about later is the LLM's) */
1032
+ deferred(p, device, roomName, time, features = this.extractFeatures(p)) {
1033
+ // "Licht an und in 2 Stunden wieder aus": two actions with one time — which one is meant for later?
1034
+ if (p.tokens.some(t => TURN_ON.has(t)) && p.tokens.some(t => TURN_OFF.has(t))) {
1035
+ return null;
1036
+ }
1037
+ const intent = this.buildDeviceIntent(device, roomName, features);
1038
+ if (!intent || !['on', 'off', 'level', 'color'].includes(intent.action)) {
1039
+ return null;
1040
+ }
1041
+ return { ...intent, ...time };
1042
+ }
790
1043
  /** The action half of a command: on/off, level, color, and whether it is a question. */
791
1044
  extractFeatures(p) {
792
1045
  return {
@@ -1076,7 +1329,8 @@ class Nlu {
1076
1329
  }
1077
1330
  const percent = /(?:prozent|percent|процент\p{L}*|%)/u.test(p.norm);
1078
1331
  // "ein bisschen lauter" is no step count — only digits and the number words from two upwards count.
1079
- const num = p.tokens
1332
+ const num = spelledNumbersToDigits(p.tokens.join(' '))
1333
+ .split(' ')
1080
1334
  .map(w => (/^\d+$/.test(w) ? w : ['ein', 'eine', 'einen'].includes(w) ? '' : NUMBER_WORDS[w] || ''))
1081
1335
  .find(w => /^\d+$/.test(w));
1082
1336
  const n = num === undefined ? null : parseInt(num, 10);
@@ -1182,7 +1436,7 @@ class Nlu {
1182
1436
  return null;
1183
1437
  }
1184
1438
  findLevel(text) {
1185
- const m = text.match(/(\d+(?:[.,]\d+)?)\s*(?:prozent|percent|процент\w*|%)/u);
1439
+ const m = spelledNumbersToDigits(text).match(/(\d+(?:[.,]\d+)?)\s*(?:prozent|percent|процент\w*|%)/u);
1186
1440
  return m ? parseFloat(m[1].replace(',', '.')) : null;
1187
1441
  }
1188
1442
  findColor(text) {