{"version":3,"file":"openai_completions_stream.cjs","names":["finalizeContentBlock"],"sources":["../../src/language_models/openai_completions_stream.ts"],"sourcesContent":["/**\n * Converts OpenAI Chat Completions-shaped stream chunks into\n * {@link ChatModelStreamEvent}s.\n *\n * Used by `@langchain/openai` and OpenAI-compatible providers (Groq, Mistral,\n * OpenRouter, IBM watsonx, etc.) without requiring a dependency on\n * `@langchain/openai`.\n *\n * @module\n */\n\nimport { finalizeContentBlock } from \"./compat.js\";\nimport type { ChatModelStreamEvent, FinishReason } from \"./event.js\";\nimport type { ContentBlock } from \"../messages/content/index.js\";\nimport type { UsageMetadata } from \"../messages/metadata.js\";\n\nexport type OpenAICompletionsFinishReason =\n  | \"stop\"\n  | \"length\"\n  | \"max_tokens\"\n  | \"tool_calls\"\n  | \"function_call\"\n  | \"content_filter\"\n  | null;\n\nexport interface OpenAICompletionsUsage {\n  prompt_tokens?: number;\n  completion_tokens?: number;\n  total_tokens?: number;\n  prompt_tokens_details?: {\n    audio_tokens?: number;\n    cached_tokens?: number;\n  };\n  completion_tokens_details?: {\n    audio_tokens?: number;\n    reasoning_tokens?: number;\n  };\n}\n\nexport interface OpenAICompletionsToolCallDelta {\n  index?: number;\n  id?: string;\n  type?: \"function\";\n  function?: {\n    name?: string;\n    arguments?: string;\n  };\n}\n\nexport interface OpenAICompletionsDelta {\n  role?: string;\n  content?: string | null;\n  reasoning_content?: string;\n  reasoning?: string;\n  tool_calls?: OpenAICompletionsToolCallDelta[];\n  function_call?: unknown;\n  audio?: {\n    id?: string;\n    data?: string;\n    transcript?: string;\n  };\n}\n\nexport interface OpenAICompletionsStreamChoice {\n  index?: number;\n  finish_reason?: OpenAICompletionsFinishReason;\n  logprobs?: unknown;\n  delta?: OpenAICompletionsDelta;\n}\n\nexport interface OpenAICompletionsStreamChunk {\n  id?: string;\n  model?: string;\n  service_tier?: string | null;\n  system_fingerprint?: string | null;\n  usage?: OpenAICompletionsUsage | null;\n  x_groq?: {\n    usage?: {\n      prompt_tokens?: number;\n      completion_tokens?: number;\n      total_tokens?: number;\n    };\n  };\n  choices?: OpenAICompletionsStreamChoice[];\n}\n\nexport interface ConvertOpenAICompletionsStreamOptions {\n  streamUsage?: boolean;\n  /** Provider id for passthrough events (default `\"openai\"`). */\n  provider?: string;\n  /** Optional per-chunk transform before conversion. */\n  mapChunk?: (\n    chunk: OpenAICompletionsStreamChunk\n  ) => OpenAICompletionsStreamChunk;\n}\n\ntype BlockKey = \"text\" | \"reasoning\" | \"audio\" | `tool:${number}`;\n\n/**\n * Convert an async iterable of OpenAI Chat Completions-shaped stream chunks into\n * LangChain `ChatModelStreamEvent`s with typed deltas.\n */\nexport async function* convertOpenAICompletionsStream(\n  source: AsyncIterable<OpenAICompletionsStreamChunk>,\n  options: ConvertOpenAICompletionsStreamOptions = {}\n): AsyncGenerator<ChatModelStreamEvent> {\n  const shouldStreamUsage = options.streamUsage ?? true;\n  const provider = options.provider ?? \"openai\";\n  const mapChunk = options.mapChunk;\n\n  const blockAccumulators = new Map<\n    number,\n    // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n    Record<string, any>\n  >();\n  const blockKeyToIndex = new Map<BlockKey, number>();\n  let nextBlockIndex = 0;\n  let messageStarted = false;\n  let usageSnapshot: UsageMetadata | undefined;\n  let finishReason: FinishReason | undefined;\n  let responseMetadata: Record<string, unknown> | undefined;\n  let emittedProviderMetadata = false;\n\n  const getOrCreateBlockIndex = (\n    key: BlockKey,\n    initial: Record<string, unknown>\n  ): { index: number; isNew: boolean } => {\n    const existing = blockKeyToIndex.get(key);\n    if (existing !== undefined) {\n      return { index: existing, isNew: false };\n    }\n    const index = nextBlockIndex++;\n    blockKeyToIndex.set(key, index);\n    blockAccumulators.set(index, { ...initial });\n    return { index, isNew: true };\n  };\n\n  for await (let data of source) {\n    if (mapChunk) {\n      data = mapChunk(data);\n    }\n    if (!messageStarted) {\n      messageStarted = true;\n      yield {\n        event: \"message-start\" as const,\n        id: data.id,\n      };\n    }\n\n    if (!emittedProviderMetadata && (data.model || data.service_tier)) {\n      emittedProviderMetadata = true;\n      yield {\n        event: \"provider\" as const,\n        provider,\n        name: \"stream_metadata\",\n        payload: {\n          model: data.model,\n          service_tier: data.service_tier,\n        },\n      };\n    }\n\n    if (data.usage && shouldStreamUsage) {\n      usageSnapshot = buildUsageSnapshot(data.usage);\n      yield { event: \"usage\" as const, usage: usageSnapshot };\n    }\n\n    const groqUsage = data.x_groq?.usage;\n    if (groqUsage && shouldStreamUsage) {\n      usageSnapshot = buildGroqUsageSnapshot(groqUsage);\n      yield { event: \"usage\" as const, usage: usageSnapshot };\n    }\n\n    const choice = data.choices?.[0];\n    if (!choice) {\n      continue;\n    }\n\n    if (choice.finish_reason != null) {\n      finishReason = mapFinishReason(choice.finish_reason);\n      responseMetadata = buildResponseMetadata(data, choice);\n    }\n\n    const { delta } = choice;\n    if (!delta) {\n      continue;\n    }\n\n    const reasoningText = getReasoningDeltaText(delta);\n    if (reasoningText) {\n      const key: BlockKey = \"reasoning\";\n      const { index, isNew } = getOrCreateBlockIndex(key, {\n        type: \"reasoning\" as const,\n        reasoning: \"\",\n      });\n      if (isNew) {\n        yield {\n          event: \"content-block-start\" as const,\n          index,\n          content: { type: \"reasoning\", reasoning: \"\" } as ContentBlock,\n        };\n      }\n      const acc = blockAccumulators.get(index)!;\n      acc.reasoning = (acc.reasoning ?? \"\") + reasoningText;\n      yield {\n        event: \"content-block-delta\" as const,\n        index,\n        delta: {\n          type: \"reasoning-delta\" as const,\n          reasoning: reasoningText,\n        },\n      };\n    }\n\n    if (delta.content) {\n      const key: BlockKey = \"text\";\n      const { index, isNew } = getOrCreateBlockIndex(key, {\n        type: \"text\" as const,\n        text: \"\",\n      });\n      if (isNew) {\n        yield {\n          event: \"content-block-start\" as const,\n          index,\n          content: { type: \"text\", text: \"\" } as ContentBlock,\n        };\n      }\n      const acc = blockAccumulators.get(index)!;\n      acc.text = (acc.text ?? \"\") + delta.content;\n      yield {\n        event: \"content-block-delta\" as const,\n        index,\n        delta: { type: \"text-delta\" as const, text: delta.content },\n      };\n    }\n\n    if (Array.isArray(delta.tool_calls)) {\n      for (const rawToolCall of delta.tool_calls) {\n        const toolIndex = rawToolCall.index ?? 0;\n        const key: BlockKey = `tool:${toolIndex}`;\n        const { index, isNew } = getOrCreateBlockIndex(key, {\n          type: \"tool_call_chunk\" as const,\n          id: rawToolCall.id,\n          name: rawToolCall.function?.name,\n          args: \"\",\n          index: toolIndex,\n        });\n        if (isNew) {\n          yield {\n            event: \"content-block-start\" as const,\n            index,\n            content: {\n              type: \"tool_call_chunk\",\n              id: rawToolCall.id,\n              name: rawToolCall.function?.name,\n              args: \"\",\n              index: toolIndex,\n            } as ContentBlock,\n          };\n        }\n\n        const acc = blockAccumulators.get(index)!;\n        if (rawToolCall.id != null) acc.id = rawToolCall.id;\n        if (rawToolCall.function?.name != null) {\n          acc.name = rawToolCall.function.name;\n        }\n        const argDelta = rawToolCall.function?.arguments ?? \"\";\n        acc.args = (acc.args ?? \"\") + argDelta;\n        yield {\n          event: \"content-block-delta\" as const,\n          index,\n          delta: {\n            type: \"block-delta\" as const,\n            fields: {\n              type: \"tool_call_chunk\",\n              ...(acc.id != null ? { id: acc.id } : {}),\n              ...(acc.name != null ? { name: acc.name } : {}),\n              args: acc.args,\n            },\n          },\n        };\n      }\n    }\n\n    if (delta.audio) {\n      const key: BlockKey = \"audio\";\n      const { index, isNew } = getOrCreateBlockIndex(key, {\n        type: \"audio\" as const,\n        id: delta.audio.id,\n        data: \"\",\n        mimeType: \"audio/pcm\",\n        transcript: delta.audio.transcript ?? \"\",\n      });\n      if (isNew) {\n        yield {\n          event: \"content-block-start\" as const,\n          index,\n          content: {\n            type: \"audio\",\n            id: delta.audio.id,\n            data: \"\",\n            mimeType: \"audio/pcm\",\n            transcript: delta.audio.transcript ?? \"\",\n          } as ContentBlock,\n        };\n      }\n      const acc = blockAccumulators.get(index)!;\n      if (delta.audio.transcript) {\n        acc.transcript = (acc.transcript ?? \"\") + delta.audio.transcript;\n        yield {\n          event: \"content-block-delta\" as const,\n          index,\n          delta: {\n            type: \"block-delta\" as const,\n            fields: {\n              type: \"audio\",\n              transcript: acc.transcript,\n            },\n          },\n        };\n      }\n      if (delta.audio.data) {\n        acc.data = (acc.data ?? \"\") + delta.audio.data;\n        yield {\n          event: \"content-block-delta\" as const,\n          index,\n          delta: {\n            type: \"data-delta\" as const,\n            data: delta.audio.data,\n            encoding: \"base64\" as const,\n          },\n        };\n      }\n    }\n\n    if (delta.function_call) {\n      yield {\n        event: \"provider\" as const,\n        provider,\n        name: \"function_call\",\n        payload: delta.function_call,\n      };\n    }\n\n    if (choice.logprobs) {\n      yield {\n        event: \"provider\" as const,\n        provider,\n        name: \"logprobs\",\n        payload: choice.logprobs,\n      };\n    }\n  }\n\n  for (const [index, acc] of blockAccumulators) {\n    const finalized = finalizeContentBlock(acc as ContentBlock);\n    yield {\n      event: \"content-block-finish\" as const,\n      index,\n      content: finalized,\n    };\n  }\n\n  yield {\n    event: \"message-finish\" as const,\n    reason: finishReason,\n    ...(usageSnapshot ? { usage: usageSnapshot } : {}),\n    ...(responseMetadata ? { responseMetadata } : {}),\n  };\n}\n\nfunction getReasoningDeltaText(\n  delta: OpenAICompletionsDelta\n): string | undefined {\n  const reasoning = delta.reasoning_content ?? delta.reasoning;\n  return typeof reasoning === \"string\" && reasoning.length > 0\n    ? reasoning\n    : undefined;\n}\n\nfunction buildGroqUsageSnapshot(usage: {\n  prompt_tokens?: number;\n  completion_tokens?: number;\n  total_tokens?: number;\n}): UsageMetadata {\n  return {\n    input_tokens: usage.prompt_tokens ?? 0,\n    output_tokens: usage.completion_tokens ?? 0,\n    total_tokens: usage.total_tokens ?? 0,\n  };\n}\n\nfunction mapFinishReason(reason: OpenAICompletionsFinishReason): FinishReason {\n  switch (reason) {\n    case \"stop\":\n      return \"stop\";\n    case \"length\":\n    case \"max_tokens\":\n      return \"length\";\n    case \"tool_calls\":\n    case \"function_call\":\n      return \"tool_use\";\n    case \"content_filter\":\n      return \"content_filter\";\n    default:\n      return \"stop\";\n  }\n}\n\nfunction buildUsageSnapshot(usage: OpenAICompletionsUsage): UsageMetadata {\n  const inputTokenDetails = {\n    ...(usage.prompt_tokens_details?.audio_tokens != null && {\n      audio: usage.prompt_tokens_details.audio_tokens,\n    }),\n    ...(usage.prompt_tokens_details?.cached_tokens != null && {\n      cache_read: usage.prompt_tokens_details.cached_tokens,\n    }),\n  };\n  const outputTokenDetails = {\n    ...(usage.completion_tokens_details?.audio_tokens != null && {\n      audio: usage.completion_tokens_details.audio_tokens,\n    }),\n    ...(usage.completion_tokens_details?.reasoning_tokens != null && {\n      reasoning: usage.completion_tokens_details.reasoning_tokens,\n    }),\n  };\n  return {\n    input_tokens: usage.prompt_tokens ?? 0,\n    output_tokens: usage.completion_tokens ?? 0,\n    total_tokens: usage.total_tokens ?? 0,\n    ...(Object.keys(inputTokenDetails).length > 0 && {\n      input_token_details: inputTokenDetails,\n    }),\n    ...(Object.keys(outputTokenDetails).length > 0 && {\n      output_token_details: outputTokenDetails,\n    }),\n  };\n}\n\nfunction buildResponseMetadata(\n  data: OpenAICompletionsStreamChunk,\n  choice: OpenAICompletionsStreamChoice\n): Record<string, unknown> {\n  return {\n    model_provider: \"openai\",\n    model_name: data.model,\n    system_fingerprint: data.system_fingerprint,\n    service_tier: data.service_tier,\n    finish_reason: choice.finish_reason,\n    ...(data.usage ? { usage: data.usage } : {}),\n  };\n}\n"],"mappings":"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"}