{"version":3,"file":"validation.mjs","names":[],"sources":["../../../../src/batteries/llm/webllm_chat_completions/validation.ts"],"sourcesContent":["/**\n * Runtime validation schema and wrapper for WebLLM Chat Completions adapter options.\n *\n * @module @nhtio/adk/batteries/llm/webllm_chat_completions/validation\n */\n\nimport { isError } from '@nhtio/adk/guards'\nimport { validator, ValidationError } from '@nhtio/validation'\nimport { byteStoreSchema, TokenEncoding } from '@nhtio/adk/common'\nimport { E_INVALID_WEBLLM_CHAT_COMPLETIONS_OPTIONS } from './exceptions'\nimport type { WebLLMChatCompletionsAdapterOptions } from './types'\n\nconst bucketLabelSchema = validator\n  .string()\n  .valid('standingInstructions', 'memories', 'retrievables', 'timeline')\n\nconst bucketOrderSchema = validator\n  .array()\n  .items(bucketLabelSchema)\n  .unique()\n  .default(['standingInstructions', 'memories', 'retrievables', 'timeline'])\n\nconst reasoningFieldPrecedenceSchema = validator\n  .array()\n  .items(validator.string().valid('reasoning', 'reasoning_content'))\n  .unique()\n  .min(1)\n  .default(['reasoning', 'reasoning_content'])\n\nconst tokenEncodingSchema = validator\n  .alternatives(\n    // Known values are suggestions from the canonical list, not a whitelist: consumers may provide\n    // a custom or newer tokenizer name. The field accepts any non-empty string, explicit null, or\n    // absent (undefined = \"no token counting\"). `.optional()` preserves the null/undefined\n    // disposition required by adk/require-validator-any-required.\n    validator\n      .string()\n      .min(1)\n      .description(`Known encodings: ${TokenEncoding.join(', ')}`),\n    validator.any().valid(null).optional()\n  )\n  .default(null)\n\n// Optional fallback tool-call parser (see WebLLMChatCompletionsAdapterOptions.localToolCallParser,\n// inherited from the OpenAI options). A bundled family name, 'auto' (try-all), 'none', or a custom\n// ToolCallParserFn. `.optional()` — absent = disabled. Names mirror chat_common's ToolCallParserName union.\nconst localToolCallParserSchema = validator\n  .alternatives(\n    validator\n      .string()\n      .valid(\n        'auto',\n        'hermes',\n        'gemma',\n        'gpt_oss',\n        'pythonic',\n        'bare_pythonic',\n        'loose_keyed',\n        'llama3_json',\n        'mistral',\n        'qwen3_coder',\n        'phi',\n        'none'\n      ),\n    validator.function()\n  )\n  .optional()\n\nconst helperSchema = validator.function()\n\nconst helpersSchema = validator\n  .object({\n    descriptionToChatCompletionsJsonSchema: helperSchema.optional(),\n    renderUntrustedContent: helperSchema.optional(),\n    renderTrustedContent: helperSchema.optional(),\n    renderStandingInstructions: helperSchema.optional(),\n    renderMemories: helperSchema.optional(),\n    renderRetrievables: helperSchema.optional(),\n    renderRetrievableHandleBody: helperSchema.optional(),\n    renderRetrievableSafetyDirective: helperSchema.optional(),\n    renderFirstPartyRetrievables: helperSchema.optional(),\n    renderThirdPartyPublicRetrievables: helperSchema.optional(),\n    renderThirdPartyPrivateRetrievables: helperSchema.optional(),\n    renderTimelineMessage: helperSchema.optional(),\n    renderThought: helperSchema.optional(),\n    filterThoughts: helperSchema.optional(),\n    toolsToChatCompletionsTools: helperSchema.optional(),\n    renderChatCompletionsSystemPrompt: helperSchema.optional(),\n    renderChatCompletionsToolCallResult: helperSchema.optional(),\n    buildChatCompletionsHistory: helperSchema.optional(),\n    createChatCompletionsToolCallDeltaAccumulator: helperSchema.optional(),\n  })\n  .unknown(false)\n\nconst jsonSchemaSchema = validator.object().unknown(true)\n\nconst responseFormatSchema = validator.alternatives(\n  validator.object({ type: validator.string().valid('text').required() }).unknown(false),\n  validator.object({ type: validator.string().valid('json_object').required() }).unknown(false),\n  validator\n    .object({\n      type: validator.string().valid('json_schema').required(),\n      json_schema: validator\n        .object({\n          name: validator.string().required(),\n          schema: jsonSchemaSchema.required(),\n          strict: validator.boolean().optional(),\n          description: validator.string().optional(),\n        })\n        .unknown(false)\n        .required(),\n    })\n    .unknown(false)\n)\n\nconst toolChoiceItemSchema = validator.alternatives(\n  validator\n    .object({\n      type: validator.string().valid('function').required(),\n      function: validator.object({ name: validator.string().required() }).unknown(false).required(),\n    })\n    .unknown(false),\n  validator\n    .object({\n      type: validator.string().valid('custom').required(),\n      custom: validator.object({ name: validator.string().required() }).unknown(false).required(),\n    })\n    .unknown(false)\n)\n\nconst toolChoiceSchema = validator.alternatives(\n  validator.string().valid('none', 'auto', 'required'),\n  toolChoiceItemSchema,\n  validator\n    .object({\n      type: validator.string().valid('allowed_tools').required(),\n      allowed_tools: validator\n        .object({\n          mode: validator.string().valid('auto', 'required').required(),\n          tools: validator.array().items(toolChoiceItemSchema).required(),\n        })\n        .unknown(false)\n        .required(),\n    })\n    .unknown(false)\n)\n\n/**\n * Validator schema for {@link WebLLMChatCompletionsAdapterOptions}. Rejects unknown keys\n * (`.unknown(false)`) so typos and removed fields fail loud, and fills in defaults.\n */\nexport const webLLMChatCompletionsOptionsSchema = validator\n  .object<WebLLMChatCompletionsAdapterOptions>({\n    engine: validator.object().unknown(true).optional(),\n    createEngine: validator.function().optional(),\n    onInitProgress: validator.function().optional(),\n    isWebGPUAvailable: validator.function().optional(),\n    // ── Lifecycle hooks (opt-in, normalized phase machine; additive over onInitProgress) ──\n    onLifecycle: validator.function().optional(),\n    onLoading: validator.function().optional(),\n    onCompiling: validator.function().optional(),\n    onReady: validator.function().optional(),\n    onGenerating: validator.function().optional(),\n    onComplete: validator.function().optional(),\n    onError: validator.function().optional(),\n    engineConfig: validator.object().unknown(true).optional(),\n    chatOptions: validator\n      .alternatives(\n        validator.object().unknown(true),\n        validator.array().items(validator.object().unknown(true))\n      )\n      .optional(),\n    stream: validator.boolean().default(true),\n    bucketOrder: bucketOrderSchema,\n    // Safe context budget (enforced only when `tokenEncoding` is non-null — counting is opt-in, but the\n    // ceiling is pinned so once enabled it never silently exceeds a small on-device model's window).\n    contextWindow: validator.number().integer().min(1).default(4096),\n    selfIdentity: validator.string().min(1).default('assistant'),\n    thoughtSurfacing: validator\n      .string()\n      .valid('all-self', 'latest-self', 'all')\n      .default('all-self'),\n    tokenEncoding: tokenEncodingSchema,\n    replayCompatibility: validator.array().items(validator.string().min(1)).default([]),\n    reasoningFieldPrecedence: reasoningFieldPrecedenceSchema,\n    helpers: helpersSchema.optional(),\n    spoolStore: byteStoreSchema.optional(),\n    strictToolChoice: validator.boolean().default(false),\n    // Opt-in: executor self-acks tool-call-free responses only when true.\n    // Default false hands turn completion to the implementor (see\n    // OpenAIChatCompletionsAdapterOptions.autoAck — inherited here).\n    autoAck: validator.boolean().default(false),\n    unsupportedMediaPolicy: validator\n      .alternatives(\n        validator.string().valid('throw', 'fallback-stash', 'synthetic-description'),\n        validator\n          .object({\n            mode: validator.string().valid('fallback-stash').required(),\n            stashKeys: validator.array().items(validator.string().min(1)).required(),\n          })\n          .unknown(false)\n      )\n      .default('throw'),\n\n    model: validator.string().required(),\n    frequency_penalty: validator.number().min(-2).max(2).optional(),\n    logit_bias: validator.object().pattern(validator.string(), validator.number()).optional(),\n    logprobs: validator.boolean().optional(),\n    // Generation knobs are pinned with explicit deterministic-friendly defaults so the MLC engine never\n    // falls back to a model-config guess (the source of per-model surprises). temperature 0 = greedy.\n    max_tokens: validator.number().integer().min(1).default(1024),\n    n: validator.number().integer().min(1).optional(),\n    presence_penalty: validator.number().min(-2).max(2).optional(),\n    response_format: responseFormatSchema.optional(),\n    seed: validator.number().integer().optional(),\n    stop: validator\n      .alternatives(validator.string(), validator.array().items(validator.string()))\n      .optional(),\n    stream_options: validator.object().unknown(true).optional(),\n    temperature: validator.number().min(0).max(2).default(0),\n    tool_choice: toolChoiceSchema.optional(),\n    top_logprobs: validator.number().integer().min(0).optional(),\n    top_p: validator.number().min(0).max(1).default(0.95),\n    user: validator.string().optional(),\n    repetition_penalty: validator.number().optional(),\n    ignore_eos: validator.boolean().optional(),\n    extra_body: validator.object().unknown(true).optional(),\n    max_completion_tokens: validator.number().integer().min(1).optional(),\n    // Explicit thinking flag (default OFF). Threaded into the request as extra_body.enable_thinking by\n    // the adapter so the underlying chat template never decides for itself (Qwen3/DeepSeek default ON).\n    enableThinking: validator.boolean().default(false),\n    onRawGeneration: validator.function().optional(),\n    onPromptAssembled: validator.function().optional(),\n    toolCallIdFilter: validator.function().optional(),\n    localToolCallParser: localToolCallParserSchema,\n    forgeToolsFilter: validator.function().optional(),\n  })\n  .unknown(false)\n\nconst isValidationError = (value: unknown): value is ValidationError =>\n  isError(value) && Array.isArray((value as ValidationError).details)\n\nconst formatValidationDetails = (err: ValidationError): string =>\n  err.details.map((d) => d.message).join(' and ')\n\n/**\n * Validates raw adapter options against {@link webLLMChatCompletionsOptionsSchema}, filling in\n * defaults.\n *\n * @param input - The raw options object to validate.\n * @returns The resolved options object with defaults applied.\n * @throws {@link @nhtio/adk/batteries!E_INVALID_WEBLLM_CHAT_COMPLETIONS_OPTIONS} when `input` is invalid.\n */\nexport const validateOptions = (input: unknown): WebLLMChatCompletionsAdapterOptions => {\n  const { value, error } = webLLMChatCompletionsOptionsSchema.validate(input, {\n    abortEarly: false,\n    convert: false,\n  })\n  if (error) {\n    throw new E_INVALID_WEBLLM_CHAT_COMPLETIONS_OPTIONS([formatValidationDetails(error)], {\n      cause: error,\n    })\n  }\n  return value as WebLLMChatCompletionsAdapterOptions\n}\n\nvoid isValidationError\n"],"mappings":"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"}