{"version":3,"sources":["../../src/providers/groq.ts","../../src/providers/utils.ts","../../src/providers/openai-compatible.ts"],"sourcesContent":["import type { Message, ReforgeProvider } from \"./types.js\";\r\nimport {\r\n  openaiCompatible,\r\n  type OpenAICompatibleClient,\r\n  type OpenAICompatibleCallOptions,\r\n} from \"./openai-compatible.js\";\r\n\r\nexport interface GroqCallOptions extends OpenAICompatibleCallOptions {\r\n  top_p?: number;\r\n  frequency_penalty?: number;\r\n  presence_penalty?: number;\r\n  systemPromptBehavior?: \"default\" | \"prepend_to_user\";\r\n}\r\n\r\nfunction normalizeGroqMessages(\r\n  messages: Message[],\r\n  behavior: GroqCallOptions[\"systemPromptBehavior\"],\r\n): Message[] {\r\n  if (behavior !== \"prepend_to_user\") {\r\n    return messages;\r\n  }\r\n\r\n  const system = messages\r\n    .filter((m) => m.role === \"system\")\r\n    .map((m) => (typeof m.content === \"string\" ? m.content : \"\"))\r\n    .filter((v) => v.length > 0)\r\n    .join(\"\\n\\n\");\r\n\r\n  if (!system) return messages;\r\n\r\n  const nonSystem = messages.filter((m) => m.role !== \"system\");\r\n  const firstUserIndex = nonSystem.findIndex((m) => m.role === \"user\");\r\n  if (firstUserIndex === -1) return nonSystem;\r\n\r\n  const firstUser = nonSystem[firstUserIndex]!;\r\n  const firstUserContent = typeof firstUser.content === \"string\" ? firstUser.content : \"\";\r\n\r\n  const adjusted = [...nonSystem];\r\n  adjusted[firstUserIndex] = {\r\n    ...firstUser,\r\n    content: `[SYSTEM]\\n${system}\\n\\n[USER]\\n${firstUserContent}`,\r\n  };\r\n\r\n  return adjusted;\r\n}\r\n\r\n/**\r\n * Groq-specific OpenAI-compatible adapter.\r\n */\r\nexport function groq(\r\n  client: OpenAICompatibleClient,\r\n  model: string,\r\n): ReforgeProvider<GroqCallOptions> {\r\n  const base = openaiCompatible(client, model);\r\n\r\n  return {\r\n    id: `groq:${model}`,\r\n    async call(messages: Message[], options?: GroqCallOptions): Promise<string> {\r\n      const normalizedMessages = normalizeGroqMessages(\r\n        messages,\r\n        options?.systemPromptBehavior,\r\n      );\r\n\r\n      const merged: OpenAICompatibleCallOptions = {\r\n        ...(options ?? {}),\r\n      };\r\n\r\n      delete (merged as Record<string, unknown>).systemPromptBehavior;\r\n\r\n      return base.call(normalizedMessages, merged);\r\n    },\r\n  };\r\n}\r\n","import type {\r\n  ProviderCallOptions,\r\n  Message,\r\n  MessageContent,\r\n  MessageContentBlock,\r\n  MessageImageUrlBlock,\r\n} from \"./types.js\";\r\n\r\n/**\r\n * Keys managed by Reforge that should not be spread into the\r\n * provider-specific options object (they are mapped explicitly).\r\n */\r\nconst REFORGE_KEYS = new Set<string>([]);\r\n\r\n/**\r\n * Strip Reforge-managed keys from a `ProviderCallOptions` object so\r\n * they aren't double-sent when spreading into provider SDK calls.\r\n *\r\n * Returns a plain object with only the pass-through keys, or\r\n * `undefined` if there's nothing left.\r\n */\r\nexport function filterReforgeKeys(\r\n  options?: ProviderCallOptions,\r\n): Record<string, unknown> | undefined {\r\n  if (!options) return undefined;\r\n\r\n  const filtered: Record<string, unknown> = {};\r\n  let hasKeys = false;\r\n\r\n  for (const key of Object.keys(options)) {\r\n    if (!REFORGE_KEYS.has(key)) {\r\n      filtered[key] = options[key];\r\n      hasKeys = true;\r\n    }\r\n  }\r\n\r\n  return hasKeys ? filtered : undefined;\r\n}\r\n\r\nfunction isImageBlock(block: MessageContentBlock): block is MessageImageUrlBlock {\r\n  return block.type === \"image_url\";\r\n}\r\n\r\n/**\r\n * Convert message content into plain text for providers that do not support\r\n * image/message blocks in their input shape.\r\n */\r\nexport function messageContentToText(content: MessageContent): string {\r\n  if (typeof content === \"string\") {\r\n    return content;\r\n  }\r\n\r\n  const parts: string[] = [];\r\n  for (const block of content) {\r\n    if (block.type === \"text\") {\r\n      parts.push(block.text);\r\n      continue;\r\n    }\r\n\r\n    if (isImageBlock(block)) {\r\n      parts.push(`[image_url:${block.image_url.url}]`);\r\n    }\r\n  }\r\n\r\n  return parts.join(\"\\n\");\r\n}\r\n\r\n/**\r\n * Extract only text blocks from a message for providers with text-only system\r\n * channels.\r\n */\r\nexport function getMessageText(message: Message): string {\r\n  return messageContentToText(message.content);\r\n}\r\n","import type {\r\n  ReforgeProvider,\r\n  Message,\r\n  ProviderCallOptions,\r\n  MessageContent,\r\n} from \"./types.js\";\r\nimport { filterReforgeKeys } from \"./utils.js\";\r\n\r\nexport interface OpenAICompatibleCallOptions extends ProviderCallOptions {\r\n  temperature?: number;\r\n  max_tokens?: number;\r\n  response_format?: unknown;\r\n  tools?: unknown;\r\n  tool_choice?: unknown;\r\n  reasoning_effort?: \"low\" | \"medium\" | \"high\";\r\n  parallel_tool_calls?: boolean;\r\n  stream?: boolean;\r\n}\r\n\r\n/**\r\n * Minimal subset of the `OpenAI` client used by the adapter.\r\n * This avoids importing the full `openai` package at the type level\r\n * while still providing type-safety for users who pass their client.\r\n */\r\nexport interface OpenAICompatibleClient {\r\n  chat: {\r\n    completions: {\r\n      create(params: Record<string, unknown>): Promise<{\r\n        choices: Array<{\r\n          message?: {\r\n            content?: unknown;\r\n            refusal?: unknown;\r\n          };\r\n          text?: unknown;\r\n          finish_reason?: unknown;\r\n        }>;\r\n      }>;\r\n    };\r\n  };\r\n}\r\n\r\nfunction isNonEmptyString(value: unknown): value is string {\r\n  return typeof value === \"string\" && value.trim().length > 0;\r\n}\r\n\r\nfunction extractTextFromUnknown(value: unknown): string | undefined {\r\n  if (isNonEmptyString(value)) {\r\n    return value;\r\n  }\r\n\r\n  if (!Array.isArray(value)) {\r\n    return undefined;\r\n  }\r\n\r\n  const parts: string[] = [];\r\n\r\n  for (const part of value) {\r\n    if (isNonEmptyString(part)) {\r\n      parts.push(part);\r\n      continue;\r\n    }\r\n\r\n    if (!part || typeof part !== \"object\") {\r\n      continue;\r\n    }\r\n\r\n    const maybeText = (part as { text?: unknown }).text;\r\n    if (isNonEmptyString(maybeText)) {\r\n      parts.push(maybeText);\r\n      continue;\r\n    }\r\n\r\n    const maybeContent = (part as { content?: unknown }).content;\r\n    if (isNonEmptyString(maybeContent)) {\r\n      parts.push(maybeContent);\r\n    }\r\n  }\r\n\r\n  if (parts.length === 0) {\r\n    return undefined;\r\n  }\r\n\r\n  return parts.join(\"\\n\");\r\n}\r\n\r\n/**\r\n * Create a `ReforgeProvider` for any OpenAI-compatible API.\r\n *\r\n * Works with: **OpenAI**, **OpenRouter**, **Together AI**, **Groq**,\r\n * **Fireworks**, **Perplexity**, **Ollama**, **LM Studio**, **vLLM**,\r\n * **Deepseek**, **Mistral**, and any other provider that implements the\r\n * `/v1/chat/completions` API shape.\r\n *\r\n * The user passes their own pre-configured client (with `baseURL` and\r\n * `apiKey` already set). Reforge never manages credentials.\r\n *\r\n * @param client - An `OpenAI` client instance (from the `openai` npm package).\r\n * @param model  - The model identifier (e.g. `\"gpt-4o\"`, `\"llama-3-70b\"`).\r\n * @returns A `ReforgeProvider` ready to use with `forge()`.\r\n *\r\n * @example\r\n * ```ts\r\n * import OpenAI from 'openai';\r\n * import { openaiCompatible } from 'reforge-ai/openai-compatible';\r\n *\r\n * // Direct OpenAI\r\n * const provider = openaiCompatible(new OpenAI(), 'gpt-4o');\r\n *\r\n * // OpenRouter\r\n * const provider = openaiCompatible(\r\n *   new OpenAI({ baseURL: 'https://openrouter.ai/api/v1', apiKey: '...' }),\r\n *   'anthropic/claude-3.5-sonnet',\r\n * );\r\n * ```\r\n */\r\nexport function openaiCompatible(\r\n  client: OpenAICompatibleClient,\r\n  model: string,\r\n): ReforgeProvider<OpenAICompatibleCallOptions> {\r\n  function toOpenAIContent(content: MessageContent): unknown {\r\n    if (typeof content === \"string\") {\r\n      return content;\r\n    }\r\n\r\n    return content.map((block) => {\r\n      if (block.type === \"text\") {\r\n        return { type: \"text\", text: block.text };\r\n      }\r\n\r\n      return {\r\n        type: \"image_url\",\r\n        image_url: {\r\n          url: block.image_url.url,\r\n          ...(block.image_url.detail ? { detail: block.image_url.detail } : {}),\r\n        },\r\n      };\r\n    });\r\n  }\r\n\r\n  return {\r\n    id: `openai-compatible:${model}`,\r\n    async call(\r\n      messages: Message[],\r\n      options?: OpenAICompatibleCallOptions,\r\n    ): Promise<string> {\r\n      const extra = filterReforgeKeys(options);\r\n\r\n      const params: Record<string, unknown> = {\r\n        model,\r\n        messages: messages.map((m) => ({\r\n          role: m.role,\r\n          content: toOpenAIContent(m.content),\r\n          ...(m.toolCalls && m.toolCalls.length > 0 ? { tool_calls: m.toolCalls } : {}),\r\n        })),\r\n        ...extra,\r\n      };\r\n\r\n      const response = await client.chat.completions.create(params);\r\n      const choices = Array.isArray(response.choices)\r\n        ? response.choices\r\n        : [];\r\n      const firstChoice = choices[0];\r\n      const candidates: unknown[] = [\r\n        firstChoice?.message?.content,\r\n        firstChoice?.text,\r\n        firstChoice?.message?.refusal,\r\n      ];\r\n\r\n      for (const candidate of candidates) {\r\n        const text = extractTextFromUnknown(candidate);\r\n        if (text !== undefined) {\r\n          return text;\r\n        }\r\n      }\r\n\r\n      const finishReason = firstChoice?.finish_reason;\r\n      const finishReasonInfo = isNonEmptyString(finishReason)\r\n        ? ` (finish_reason: ${finishReason})`\r\n        : \"\";\r\n\r\n      throw new Error(\r\n        `OpenAI-compatible provider returned an empty response${finishReasonInfo}`,\r\n      );\r\n    },\r\n  };\r\n}\r\n"],"mappings":"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","names":[]}