{"version":3,"file":"groq-schema.cjs","names":[],"sources":["../../src/utils/groq-schema.ts"],"sourcesContent":["/**\n * Groq JSON Schema Strict Mode Transformation\n *\n * Groq's strict JSON Schema mode has specific requirements:\n * 1. additionalProperties: false on all objects\n * 2. ALL properties must be in the `required` array\n * 3. Optional properties must be nullable (type union with \"null\")\n * 4. Root schema cannot have anyOf/oneOf/enum/not\n *\n * @see https://console.groq.com/docs/structured-outputs\n */\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype JsonSchema = Record<string, any>;\n\n/**\n * Make a schema nullable using type array syntax (NOT anyOf).\n * Groq strict mode forbids anyOf at top level, so we use type: [T, \"null\"].\n * Handles: simple types, enums, anyOf, arrays, and objects.\n * Unknown schema structures (e.g., `$ref`) are returned unchanged.\n */\nfunction makeNullable(schema: JsonSchema): JsonSchema {\n  if (!schema || typeof schema !== \"object\") return schema;\n\n  // Already nullable\n  if (schema.type === \"null\") return schema;\n  if (Array.isArray(schema.type) && schema.type.includes(\"null\")) return schema;\n\n  // Enum: add null to enum values and type\n  if (Array.isArray(schema.enum)) {\n    const nextEnum = schema.enum.includes(null)\n      ? schema.enum\n      : [...schema.enum, null];\n    const baseType = Array.isArray(schema.type)\n      ? // eslint-disable-next-line @typescript-eslint/no-explicit-any\n        schema.type.filter((t: any) => t !== \"null\")\n      : typeof schema.type === \"string\"\n        ? [schema.type]\n        : [\"string\"]; // Default to string for enums\n    const nextType = Array.from(new Set([...baseType, \"null\"]));\n    return { ...schema, type: nextType, enum: nextEnum };\n  }\n\n  // anyOf: flatten to type array if possible, otherwise add null variant\n  if (Array.isArray(schema.anyOf)) {\n    const hasNull = schema.anyOf.some(\n      // eslint-disable-next-line @typescript-eslint/no-explicit-any\n      (s: any) =>\n        s?.type === \"null\" ||\n        (Array.isArray(s?.type) && s.type.includes(\"null\"))\n    );\n    if (hasNull) return schema;\n\n    // Try to extract types from anyOf and use type array instead\n    const types = schema.anyOf\n      // eslint-disable-next-line @typescript-eslint/no-explicit-any\n      .map((s: any) => s?.type)\n      // eslint-disable-next-line @typescript-eslint/no-explicit-any\n      .filter((t: any) => t && typeof t === \"string\");\n\n    if (types.length === schema.anyOf.length) {\n      // All variants are simple types - can convert to type array\n      const { anyOf: _, ...rest } = schema;\n      return { ...rest, type: [...types, \"null\"] };\n    }\n\n    // Complex anyOf - add null variant (but this should be avoided at root)\n    return { ...schema, anyOf: [...schema.anyOf, { type: \"null\" }] };\n  }\n\n  // Simple type: convert to array with null\n  if (typeof schema.type === \"string\") {\n    return { ...schema, type: [schema.type, \"null\"] };\n  }\n\n  // Type array: add null\n  if (Array.isArray(schema.type)) {\n    return { ...schema, type: Array.from(new Set([...schema.type, \"null\"])) };\n  }\n\n  // Has properties but no type - it's an object\n  if (schema.properties) {\n    return { ...schema, type: [\"object\", \"null\"] };\n  }\n\n  // Unknown schema structure (e.g., $ref) - return unchanged\n  return schema;\n}\n\n/**\n * Transform a JSON Schema to be Groq strict-mode compatible.\n *\n * Recursively:\n * 1. Sets additionalProperties: false on all objects\n * 2. Moves all properties to required array\n * 3. Makes previously optional properties nullable\n * 4. Throws if root schema has oneOf/not; extracts object variant from anyOf (Groq forbids these at top level)\n *\n * @param schema - The schema to transform\n * @param isRoot - Whether this is the root schema (default: true on first call)\n */\nexport function groqStrictifySchema(\n  schema: JsonSchema,\n  isRoot = true\n): JsonSchema {\n  if (!schema || typeof schema !== \"object\") return schema;\n  if (Array.isArray(schema))\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    return schema.map((s) => groqStrictifySchema(s, false)) as any;\n\n  // Deep clone to avoid mutating original\n  const result = { ...schema };\n\n  // Recurse into compound schemas (NOT root after this)\n  for (const k of [\"anyOf\", \"oneOf\", \"allOf\"]) {\n    if (Array.isArray(result[k])) {\n      result[k] = result[k].map((s: JsonSchema) =>\n        groqStrictifySchema(s, false)\n      );\n    }\n  }\n\n  // Recurse into array items\n  if (result.items) {\n    result.items = groqStrictifySchema(result.items, false);\n  }\n\n  // Recurse into $defs (definitions)\n  if (result.$defs) {\n    result.$defs = { ...result.$defs };\n    for (const key of Object.keys(result.$defs)) {\n      result.$defs[key] = groqStrictifySchema(result.$defs[key], false);\n    }\n  }\n\n  // Check if this is an object schema\n  const isObject =\n    result.type === \"object\" ||\n    (Array.isArray(result.type) && result.type.includes(\"object\")) ||\n    result.properties;\n\n  if (isObject && result.properties && typeof result.properties === \"object\") {\n    // Set additionalProperties: false for strict mode\n    result.additionalProperties = false;\n\n    const propKeys = Object.keys(result.properties);\n    const prevRequired = new Set<string>(\n      Array.isArray(result.required) ? result.required : []\n    );\n\n    // Clone and transform properties\n    result.properties = { ...result.properties };\n\n    for (const key of propKeys) {\n      // Recursively strictify nested schemas\n      result.properties[key] = groqStrictifySchema(\n        result.properties[key],\n        false\n      );\n\n      // If property was optional (not in required), make it nullable\n      if (!prevRequired.has(key)) {\n        result.properties[key] = makeNullable(result.properties[key]);\n      }\n    }\n\n    // All properties must be required in strict mode\n    result.required = propKeys;\n  }\n\n  // ROOT PROTECTION: Groq forbids anyOf/oneOf/enum/not at top level\n  if (isRoot) {\n    // If root has anyOf, extract the object variant\n    if (Array.isArray(result.anyOf)) {\n      const objectVariant = result.anyOf.find(\n        // eslint-disable-next-line @typescript-eslint/no-explicit-any\n        (s: any) => s?.type === \"object\" || s?.properties\n      );\n      if (objectVariant) {\n        // Replace root with the object variant\n        const cleaned = groqStrictifySchema(objectVariant, true);\n        return { ...cleaned, type: \"object\" };\n      }\n      // No object variant found - schema can't be used with Groq strict mode\n      throw new Error(\n        \"Groq strict mode requires the root schema to be an object type, \" +\n          \"but the schema has anyOf/oneOf with no object variant.\"\n      );\n    }\n\n    // Throw for oneOf/not at root (Groq forbids these)\n    if (result.oneOf) {\n      throw new Error(\n        \"Groq strict mode does not support oneOf at the root schema level.\"\n      );\n    }\n    if (result.not) {\n      throw new Error(\n        \"Groq strict mode does not support 'not' at the root schema level.\"\n      );\n    }\n\n    if (result.properties) {\n      result.type = \"object\";\n    }\n  }\n\n  return result;\n}\n\n/**\n * Check if a model supports native JSON Schema structured output.\n * Uses prefix matching so new gpt-oss models are automatically supported.\n */\nfunction supportsJsonSchema(model: string): boolean {\n  return model.startsWith(\"openai/gpt-oss\");\n}\n\n/**\n * Supported structured output methods for Groq.\n */\nexport const SUPPORTED_STRUCTURED_OUTPUT_METHODS = [\n  \"jsonSchema\",\n  \"functionCalling\",\n  \"jsonMode\",\n] as const;\n\nexport type GroqStructuredOutputMethod =\n  (typeof SUPPORTED_STRUCTURED_OUTPUT_METHODS)[number];\n\n/**\n * Get the structured output method for a given Groq model.\n *\n * - Returns `jsonSchema` if the model supports native JSON Schema and no method is specified\n * - Throws if an invalid method is provided\n * - Throws if `jsonSchema` is requested for a model that doesn't support it\n *\n * @param model - The model name\n * @param method - Optional method override\n * @returns The structured output method to use\n */\nexport function getGroqStructuredOutputMethod(\n  model: string,\n  method?: string\n): GroqStructuredOutputMethod {\n  // Validate method if provided\n  if (\n    method !== undefined &&\n    !SUPPORTED_STRUCTURED_OUTPUT_METHODS.includes(\n      method as GroqStructuredOutputMethod\n    )\n  ) {\n    throw new Error(\n      `Invalid structured output method: ${method}. Supported methods are: ${SUPPORTED_STRUCTURED_OUTPUT_METHODS.join(\", \")}`\n    );\n  }\n\n  const modelSupportsJsonSchema = supportsJsonSchema(model);\n\n  // If model supports JSON Schema and no method specified, use it by default\n  if (modelSupportsJsonSchema && !method) {\n    return \"jsonSchema\";\n  }\n\n  // If jsonSchema requested but not supported, throw\n  if (!modelSupportsJsonSchema && method === \"jsonSchema\") {\n    throw new Error(\n      `Native JSON Schema structured output is not supported for model \"${model}\". ` +\n        `Only models with the \"openai/gpt-oss\" prefix are supported. ` +\n        `Use \"functionCalling\" or \"jsonMode\" instead.`\n    );\n  }\n\n  // Return the specified method or default to functionCalling\n  return (method as GroqStructuredOutputMethod) ?? \"functionCalling\";\n}\n"],"mappings":"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"}