{"version":3,"file":"ai-model/service-caller/index.mjs","sources":["../../../../src/ai-model/service-caller/index.ts"],"sourcesContent":["import type { AIUsageInfo } from '@/types';\nimport type { CodeGenerationChunk, StreamingCallback } from '@/types';\n\n// Error class that preserves usage and rawResponse when AI call parsing fails\nexport class AIResponseParseError extends Error {\n  usage?: AIUsageInfo;\n  /**\n   * Adapter-extracted content used by Midscene for parsing. This is not the\n   * full provider response or choices[0].message.\n   */\n  rawResponse: string;\n  rawChoiceMessage?: unknown;\n  reasoningContent?: string;\n\n  constructor(\n    message: string,\n    rawResponse: string,\n    usage?: AIUsageInfo,\n    rawChoiceMessage?: unknown,\n    reasoningContent?: string,\n  ) {\n    super(message);\n    this.name = 'AIResponseParseError';\n    this.rawResponse = rawResponse;\n    this.usage = usage;\n    this.rawChoiceMessage = rawChoiceMessage;\n    this.reasoningContent = reasoningContent;\n  }\n}\nimport {\n  type IModelConfig,\n  MIDSCENE_LANGFUSE_DEBUG,\n  MIDSCENE_LANGSMITH_DEBUG,\n  type TModelFamily,\n  globalConfigManager,\n} from '@midscene/shared/env';\n\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert, ifInBrowser } from '@midscene/shared/utils';\nimport OpenAI from 'openai';\nimport type { ChatCompletionMessageParam } from 'openai/resources/index';\nimport type { Stream } from 'openai/streaming';\nimport type { ModelRuntime } from '../models';\nimport type { AIArgs } from '../types';\nimport {\n  callAIWithCodexAppServer,\n  isCodexAppServerProvider,\n} from './codex-app-server';\nimport type { JsonParserSource } from './json';\nimport {\n  type OpenAIErrorResponseContext,\n  formatOpenAIAPIErrorDetails,\n  wrapOpenAICompatibleFetch,\n} from './openai-error';\nimport {\n  buildRequestAbortSignal,\n  isHardTimeoutError,\n  resolveEffectiveTimeoutMs,\n} from './request-timeout';\nimport { callAiAndParseWithRetry } from './semantic-retry';\nexport {\n  extractJSONFromCodeBlock,\n  parseModelResponseJson,\n} from './json';\nexport type { JsonParser } from './json';\n\n/**\n * Internal field name stamped onto every AIUsageInfo shaped by callAI().\n * Used for cross-path dedup when the provider does not return a request_id.\n */\nexport const INTERNAL_CALL_ID_FIELD = '_midscene_call_id';\n\nlet internalCallIdCounter = 0;\nfunction nextInternalCallId(): string {\n  internalCallIdCounter += 1;\n  return `call_${internalCallIdCounter}`;\n}\n\nfunction stringifyForDebug(value: unknown): string {\n  try {\n    return JSON.stringify(value);\n  } catch (_error) {\n    return String(value);\n  }\n}\n\nfunction getLatestSuccessfulResponseRequestId(\n  context: OpenAIErrorResponseContext,\n): string | undefined {\n  return context.responseRequestIds?.reduce<string | undefined>(\n    (latestRequestId, response) =>\n      response.ok ? response.requestId : latestRequestId,\n    undefined,\n  );\n}\n\nfunction getErrorMessage(error: unknown): string {\n  return error instanceof Error ? error.message : String(error);\n}\n\nfunction toError(error: unknown): Error {\n  return error instanceof Error ? error : new Error(String(error));\n}\n\nfunction normalizeRetryCount(retryCount: unknown): number {\n  if (typeof retryCount !== 'number' || !Number.isFinite(retryCount)) {\n    return 1;\n  }\n\n  return Math.max(0, Math.floor(retryCount));\n}\n\nfunction appendAIRequestFailureSummary<T extends Error>(\n  error: T,\n  attemptErrors: Array<{ attempt: number; error: unknown }>,\n  maxAttempts: number,\n): T {\n  const failedAttempts = attemptErrors.length;\n  const retries = Math.max(0, failedAttempts - 1);\n  const retryLabel = retries === 1 ? 'retry' : 'retries';\n  const originalMessage = error.message;\n  const previousAttemptErrors = attemptErrors.slice(0, -1);\n\n  error.message = `AI model request failed after ${retries} ${retryLabel} (${failedAttempts}/${maxAttempts} attempts). Last error: ${originalMessage}`;\n\n  if (previousAttemptErrors.length === 0) {\n    return error;\n  }\n\n  const details = previousAttemptErrors\n    .map(\n      ({ attempt, error }) => `Attempt ${attempt}: ${getErrorMessage(error)}`,\n    )\n    .join('\\n');\n\n  error.message = `${error.message}\\nPrevious AI call attempt errors:\\n${details}`;\n  return error;\n}\n\nexport async function createChatClient({\n  modelConfig,\n}: {\n  modelConfig: IModelConfig;\n}): Promise<{\n  completion: OpenAI.Chat.Completions;\n  modelName: string;\n  modelDescription: string;\n  modelFamily: TModelFamily | undefined;\n  openAIErrorResponseContext: OpenAIErrorResponseContext;\n}> {\n  const {\n    socksProxy,\n    httpProxy,\n    modelName,\n    openaiBaseURL,\n    openaiApiKey,\n    openaiExtraConfig,\n    modelDescription,\n    modelFamily,\n    createOpenAIClient,\n    timeout,\n  } = modelConfig;\n\n  let proxyAgent: any = undefined;\n  const warnClient = getDebug('ai:call', { console: true });\n  const debugProxy = getDebug('ai:call:proxy');\n  const warnProxy = getDebug('ai:call:proxy', { console: true });\n\n  // Helper function to sanitize proxy URL for logging (remove credentials)\n  // Uses URL API instead of regex to avoid ReDoS vulnerabilities\n  const sanitizeProxyUrl = (url: string): string => {\n    try {\n      const parsed = new URL(url);\n      if (parsed.username) {\n        // Keep username for debugging, hide password for security\n        parsed.password = '****';\n        return parsed.href;\n      }\n      return url;\n    } catch {\n      // If URL parsing fails, return original URL (will be caught later)\n      return url;\n    }\n  };\n\n  if (httpProxy) {\n    debugProxy('using http proxy', sanitizeProxyUrl(httpProxy));\n    if (ifInBrowser) {\n      warnProxy(\n        'HTTP proxy is configured but not supported in browser environment',\n      );\n    } else {\n      // Dynamic import with variable to avoid bundler static analysis\n      const moduleName = 'undici';\n      const { ProxyAgent } = await import(moduleName);\n      proxyAgent = new ProxyAgent({\n        uri: httpProxy,\n        // Note: authentication is handled via the URI (e.g., http://user:pass@proxy.com:8080)\n      });\n    }\n  } else if (socksProxy) {\n    debugProxy('using socks proxy', sanitizeProxyUrl(socksProxy));\n    if (ifInBrowser) {\n      warnProxy(\n        'SOCKS proxy is configured but not supported in browser environment',\n      );\n    } else {\n      try {\n        // Dynamic import with variable to avoid bundler static analysis\n        const moduleName = 'fetch-socks';\n        const { socksDispatcher } = await import(moduleName);\n        // Parse SOCKS proxy URL (e.g., socks5://127.0.0.1:1080)\n        const proxyUrl = new URL(socksProxy);\n\n        // Validate hostname\n        if (!proxyUrl.hostname) {\n          throw new Error('SOCKS proxy URL must include a valid hostname');\n        }\n\n        // Validate and parse port\n        const port = Number.parseInt(proxyUrl.port, 10);\n        if (!proxyUrl.port || Number.isNaN(port)) {\n          throw new Error('SOCKS proxy URL must include a valid port');\n        }\n\n        // Parse SOCKS version from protocol\n        const protocol = proxyUrl.protocol.replace(':', '');\n        const socksType =\n          protocol === 'socks4' ? 4 : protocol === 'socks5' ? 5 : 5;\n\n        proxyAgent = socksDispatcher({\n          type: socksType,\n          host: proxyUrl.hostname,\n          port,\n          ...(proxyUrl.username\n            ? {\n                userId: decodeURIComponent(proxyUrl.username),\n                password: decodeURIComponent(proxyUrl.password || ''),\n              }\n            : {}),\n        });\n        debugProxy('socks proxy configured successfully', {\n          type: socksType,\n          host: proxyUrl.hostname,\n          port: port,\n        });\n      } catch (error) {\n        warnProxy('Failed to configure SOCKS proxy:', error);\n        throw new Error(\n          `Invalid SOCKS proxy URL: ${socksProxy}. Expected format: socks4://host:port, socks5://host:port, or with authentication: socks5://user:pass@host:port`,\n        );\n      }\n    }\n  }\n\n  const effectiveTimeoutMs = resolveEffectiveTimeoutMs({ timeout });\n  const openAIErrorResponseContext: OpenAIErrorResponseContext = {};\n  const openAIOptions = {\n    baseURL: openaiBaseURL,\n    apiKey: openaiApiKey,\n    // Use fetchOptions.dispatcher for fetch-based SDK instead of httpAgent\n    // Note: Type assertion needed due to undici version mismatch between dependencies\n    ...(proxyAgent ? { fetchOptions: { dispatcher: proxyAgent as any } } : {}),\n    ...openaiExtraConfig,\n    fetch: wrapOpenAICompatibleFetch(openAIErrorResponseContext),\n    // Midscene already handles retries in callAI(), so disable SDK-level retries\n    // to avoid duplicate attempts and duplicated backoff latency.\n    maxRetries: 0,\n    // When disabled (timeoutMs === null) fall through to the SDK default so\n    // only the caller-provided abortSignal can cancel the request.\n    ...(effectiveTimeoutMs !== null ? { timeout: effectiveTimeoutMs } : {}),\n    dangerouslyAllowBrowser: true,\n  };\n\n  const baseOpenAI = new OpenAI(openAIOptions);\n\n  let openai: OpenAI = baseOpenAI;\n\n  // LangSmith wrapper\n  if (\n    openai &&\n    globalConfigManager.getEnvConfigInBoolean(MIDSCENE_LANGSMITH_DEBUG)\n  ) {\n    if (ifInBrowser) {\n      throw new Error('langsmith is not supported in browser');\n    }\n    warnClient('DEBUGGING MODE: langsmith wrapper enabled');\n    // Use variable to prevent static analysis by bundlers\n    const langsmithModule = 'langsmith/wrappers';\n    const { wrapOpenAI } = await import(langsmithModule);\n    openai = wrapOpenAI(openai);\n  }\n\n  // Langfuse wrapper\n  if (\n    openai &&\n    globalConfigManager.getEnvConfigInBoolean(MIDSCENE_LANGFUSE_DEBUG)\n  ) {\n    if (ifInBrowser) {\n      throw new Error('langfuse is not supported in browser');\n    }\n    warnClient('DEBUGGING MODE: langfuse wrapper enabled');\n    // Use variable to prevent static analysis by bundlers\n    const langfuseModule = '@langfuse/openai';\n    const { observeOpenAI } = await import(langfuseModule);\n    openai = observeOpenAI(openai);\n  }\n\n  if (createOpenAIClient) {\n    const wrappedClient = await createOpenAIClient(baseOpenAI, openAIOptions);\n\n    if (wrappedClient) {\n      openai = wrappedClient as OpenAI;\n    }\n  }\n\n  return {\n    completion: openai.chat.completions,\n    modelName,\n    modelDescription,\n    modelFamily,\n    openAIErrorResponseContext,\n  };\n}\n\ninterface CallAIOptions {\n  stream?: boolean;\n  onChunk?: StreamingCallback;\n  abortSignal?: AbortSignal;\n  requiresOriginalImageDetail?: boolean;\n}\n\nexport async function callAI(\n  messages: ChatCompletionMessageParam[],\n  modelRuntime: ModelRuntime,\n  options?: CallAIOptions,\n): Promise<{\n  content: string;\n  reasoning_content?: string;\n  rawChoiceMessage?: unknown;\n  usage?: AIUsageInfo;\n  isStreamed: boolean;\n}> {\n  const { config: modelConfig, adapter } = modelRuntime;\n\n  // Stable internal ID for this call, used by the agent to deduplicate usage\n  // across the onUsage callback and the task-dump-based collectUsageMetrics()\n  // path when the provider does not return a request_id.\n  const internalCallId = nextInternalCallId();\n\n  if (isCodexAppServerProvider(modelConfig.openaiBaseURL)) {\n    const codexResult = await callAIWithCodexAppServer(messages, modelConfig, {\n      stream: options?.stream,\n      onChunk: options?.onChunk,\n      reasoningEnabled: modelConfig.reasoningEnabled,\n      abortSignal: options?.abortSignal,\n    });\n    if (codexResult.usage) {\n      (codexResult.usage as any)[INTERNAL_CALL_ID_FIELD] = internalCallId;\n      if (modelRuntime.onUsage) {\n        modelRuntime.onUsage(codexResult.usage);\n      }\n    }\n    return codexResult;\n  }\n\n  const {\n    completion,\n    modelName,\n    modelDescription,\n    modelFamily,\n    openAIErrorResponseContext,\n  } = await createChatClient({\n    modelConfig,\n  });\n  const effectiveTimeoutMs = resolveEffectiveTimeoutMs(modelConfig);\n\n  const extraBody = modelConfig.extraBody;\n\n  const debugCall = getDebug('ai:call');\n  const warnCall = getDebug('ai:call', { console: true });\n  const debugProfileStats = getDebug('ai:profile:stats');\n  const debugProfileDetail = getDebug('ai:profile:detail');\n\n  const startTime = Date.now();\n\n  const isStreaming = options?.stream && options?.onChunk;\n  const chatCompletionInput = {\n    intent: modelConfig.intent,\n    userConfig: {\n      temperature: modelConfig.temperature,\n      reasoningEnabled: modelConfig.reasoningEnabled,\n      reasoningEffort: modelConfig.reasoningEffort,\n      reasoningBudget: modelConfig.reasoningBudget,\n    },\n    requiresOriginalImageDetail: options?.requiresOriginalImageDetail,\n  };\n  const { config: adapterChatCompletionParams } =\n    adapter.chatCompletion.buildChatCompletionParams(chatCompletionInput);\n  debugCall(\n    `adapter chat completion params: ${stringifyForDebug({\n      config: adapterChatCompletionParams,\n    })}`,\n  );\n  let content: string | undefined;\n  let accumulated = '';\n  let accumulatedReasoning = '';\n  let rawChoiceMessage: unknown;\n  let usage: OpenAI.CompletionUsage | undefined;\n  let timeCost: number | undefined;\n  let requestId: string | null | undefined;\n  let responseModelName: string | undefined;\n  // Tracks whether onUsage has already been fired for this call (e.g. from\n  // the streaming final-chunk handler), so the final return does not double-fire.\n  let usageReported = false;\n\n  const hasUsableText = (value: string | null | undefined): value is string =>\n    typeof value === 'string' && value.trim().length > 0;\n\n  const resolveContentWithReasoningFallback = (\n    contentValue: string | undefined,\n    reasoningContent: string,\n  ) => {\n    if (\n      !hasUsableText(contentValue) &&\n      adapter.chatCompletion.useReasoningAsContentFallback &&\n      hasUsableText(reasoningContent)\n    ) {\n      warnCall('empty content from AI model, using reasoning content');\n      return reasoningContent;\n    }\n\n    return contentValue;\n  };\n\n  const buildUsageInfo = (\n    usageData?: OpenAI.CompletionUsage,\n    requestId?: string | null,\n  ) => {\n    if (!usageData) return undefined;\n\n    const cachedInputTokens = (\n      usageData as { prompt_tokens_details?: { cached_tokens?: number } }\n    )?.prompt_tokens_details?.cached_tokens;\n\n    return {\n      ...usageData,\n      prompt_tokens: usageData.prompt_tokens ?? 0,\n      completion_tokens: usageData.completion_tokens ?? 0,\n      total_tokens: usageData.total_tokens ?? 0,\n      cached_input: cachedInputTokens ?? 0,\n      time_cost: timeCost ?? 0,\n      model_name: modelName,\n      model_description: modelDescription,\n      response_model_name: responseModelName,\n      slot: modelConfig.slot,\n      // Left undefined at the raw call layer. The agent's onUsage callback\n      // fills it from modelConfig.slot for metrics collection, and task\n      // layers use withUsageIntent() to stamp a more specific semantic\n      // intent (e.g. 'planning', 'insight') when attaching usage to tasks.\n      intent: undefined,\n      request_id: requestId ?? undefined,\n      // Internal stable ID for cross-path dedup when request_id is absent.\n      [INTERNAL_CALL_ID_FIELD]: internalCallId,\n    } satisfies AIUsageInfo;\n  };\n\n  const requestConfig = {\n    ...adapterChatCompletionParams,\n    ...(extraBody ?? {}),\n  };\n  const temperature = requestConfig.temperature;\n\n  const imageDetail =\n    adapter.chatCompletion.resolveImageDetail(chatCompletionInput);\n\n  // Some adapters request original image detail to preserve screenshot\n  // resolution for localization-sensitive tasks.\n  const messagesWithImageDetail: ChatCompletionMessageParam[] = (() => {\n    if (!imageDetail) {\n      return messages;\n    }\n\n    return messages.map((msg) => {\n      if (!Array.isArray(msg.content)) {\n        return msg;\n      }\n\n      const content = msg.content.map((part) => {\n        if (part && part.type === 'image_url' && part.image_url?.url) {\n          return {\n            ...part,\n            image_url: {\n              ...part.image_url,\n              detail: imageDetail,\n            },\n          };\n        }\n        return part;\n      });\n\n      return {\n        ...msg,\n        content,\n      } as ChatCompletionMessageParam;\n    });\n  })();\n\n  try {\n    debugCall(\n      `sending ${isStreaming ? 'streaming ' : ''}request to ${modelName}`,\n    );\n\n    if (isStreaming) {\n      const { signal: streamSignal, cleanup: cleanupStreamSignal } =\n        buildRequestAbortSignal(effectiveTimeoutMs, options?.abortSignal);\n      try {\n        const stream = (await completion.create(\n          {\n            model: modelName,\n            messages: messagesWithImageDetail,\n            ...requestConfig,\n            stream: true,\n          },\n          {\n            stream: true,\n            signal: streamSignal,\n          },\n        )) as Stream<OpenAI.Chat.Completions.ChatCompletionChunk> & {\n          _request_id?: string | null;\n        };\n\n        requestId =\n          getLatestSuccessfulResponseRequestId(openAIErrorResponseContext) ??\n          stream._request_id;\n\n        for await (const chunk of stream) {\n          const parsedChunk = adapter.chatCompletion.extractContentAndReasoning(\n            chunk.choices?.[0]?.delta,\n          );\n          const content = parsedChunk.content || '';\n          const reasoning_content = parsedChunk.reasoning_content || '';\n\n          // Check for usage info in any chunk (OpenAI provides usage in separate chunks)\n          if (chunk.usage) {\n            usage = chunk.usage;\n          }\n          if (chunk.model) {\n            responseModelName = chunk.model;\n          }\n\n          if (content || reasoning_content) {\n            accumulated += content;\n            accumulatedReasoning += reasoning_content;\n            const chunkData: CodeGenerationChunk = {\n              content,\n              reasoning_content,\n              accumulated,\n              isComplete: false,\n              usage: undefined,\n            };\n            options.onChunk!(chunkData);\n          }\n\n          // Check if stream is complete\n          if (chunk.choices?.[0]?.finish_reason) {\n            timeCost = Date.now() - startTime;\n\n            // If usage is not available from the stream, provide a basic usage info\n            if (!usage) {\n              // Estimate token counts based on content length (rough approximation)\n              const estimatedTokens = Math.max(\n                1,\n                Math.floor(accumulated.length / 4),\n              );\n              usage = {\n                prompt_tokens: estimatedTokens,\n                completion_tokens: estimatedTokens,\n                total_tokens: estimatedTokens * 2,\n              };\n            }\n\n            const finalAccumulated = resolveContentWithReasoningFallback(\n              accumulated,\n              accumulatedReasoning,\n            );\n            accumulated = finalAccumulated || '';\n\n            // Send final chunk\n            const finalUsage = buildUsageInfo(usage, requestId);\n            if (finalUsage && modelRuntime.onUsage) {\n              modelRuntime.onUsage(finalUsage);\n              usageReported = true;\n            }\n            const finalChunk: CodeGenerationChunk = {\n              content: '',\n              accumulated,\n              reasoning_content: '',\n              isComplete: true,\n              usage: finalUsage,\n            };\n            options.onChunk!(finalChunk);\n            break;\n          }\n        }\n      } finally {\n        cleanupStreamSignal();\n      }\n      content = accumulated;\n      debugProfileStats(\n        `streaming model, ${modelName}, mode, ${modelFamily || 'default'}, cost-ms, ${timeCost}, temperature, ${temperature ?? ''}`,\n      );\n    } else {\n      // Non-streaming with retry logic\n      const retryCount = normalizeRetryCount(modelConfig.retryCount);\n      const retryInterval = modelConfig.retryInterval ?? 2000;\n      const maxAttempts = retryCount + 1; // retryCount=1 means 2 total attempts (1 initial + 1 retry)\n\n      let lastError: Error | undefined;\n      const attemptErrors: Array<{ attempt: number; error: unknown }> = [];\n\n      for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n        const { signal: attemptSignal, cleanup: cleanupAttemptSignal } =\n          buildRequestAbortSignal(effectiveTimeoutMs, options?.abortSignal);\n        try {\n          const result = await completion.create(\n            {\n              model: modelName,\n              messages: messagesWithImageDetail,\n              ...requestConfig,\n              stream: false,\n            } as any,\n            { signal: attemptSignal },\n          );\n\n          timeCost = Date.now() - startTime;\n          requestId =\n            getLatestSuccessfulResponseRequestId(openAIErrorResponseContext) ??\n            result._request_id;\n\n          debugProfileStats(\n            `model, ${modelName}, mode, ${modelFamily || 'default'}, prompt-tokens, ${result.usage?.prompt_tokens || ''}, completion-tokens, ${result.usage?.completion_tokens || ''}, total-tokens, ${result.usage?.total_tokens || ''}, cost-ms, ${timeCost}, requestId, ${requestId || ''}, temperature, ${temperature ?? ''}`,\n          );\n\n          debugProfileDetail(\n            `model usage detail: ${JSON.stringify(result.usage)}`,\n          );\n\n          if (!result.choices) {\n            throw new Error(\n              `invalid response from LLM service: ${JSON.stringify(result)}`,\n            );\n          }\n\n          rawChoiceMessage = result.choices[0].message;\n          const parsedMessage =\n            adapter.chatCompletion.extractContentAndReasoning(\n              result.choices[0].message,\n            );\n          content = parsedMessage.content;\n          accumulatedReasoning = parsedMessage.reasoning_content;\n          usage = result.usage;\n          responseModelName = result.model;\n\n          content = resolveContentWithReasoningFallback(\n            content,\n            accumulatedReasoning,\n          );\n\n          if (!hasUsableText(content)) {\n            const errorUsage = buildUsageInfo(usage, requestId);\n            if (errorUsage && modelRuntime.onUsage) {\n              modelRuntime.onUsage(errorUsage);\n            }\n            throw new AIResponseParseError(\n              'empty content from AI model',\n              content || '',\n              errorUsage,\n              rawChoiceMessage,\n            );\n          }\n\n          break; // Success, exit retry loop\n        } catch (error) {\n          lastError = toError(error);\n          attemptErrors.push({ attempt, error });\n          const wasHardTimeout = isHardTimeoutError(lastError);\n          if (wasHardTimeout) {\n            warnCall(\n              `AI call hit hard timeout (${effectiveTimeoutMs}ms, attempt ${attempt}/${maxAttempts}, model ${modelName}, slot ${modelConfig.slot})`,\n            );\n          }\n          // Do not retry if the request was aborted by the caller\n          if (options?.abortSignal?.aborted) {\n            break;\n          }\n          if (attempt < maxAttempts) {\n            warnCall(\n              `AI call failed (attempt ${attempt}/${maxAttempts}), retrying in ${retryInterval}ms... Error: ${lastError.message}`,\n            );\n            await new Promise((resolve) => setTimeout(resolve, retryInterval));\n          }\n        } finally {\n          cleanupAttemptSignal();\n        }\n      }\n\n      if (!content) {\n        assert(\n          lastError,\n          'AI model request failed without recording an attempt error',\n        );\n        throw appendAIRequestFailureSummary(\n          lastError,\n          attemptErrors,\n          maxAttempts,\n        );\n      }\n    }\n\n    debugCall(`response reasoning content: ${accumulatedReasoning}`);\n    debugCall(`response content: ${content}`);\n\n    // Ensure we always have usage info for streaming responses\n    if (isStreaming && !usage) {\n      // Estimate token counts based on content length (rough approximation)\n      const estimatedTokens = Math.max(\n        1,\n        Math.floor((content || '').length / 4),\n      );\n      usage = {\n        prompt_tokens: estimatedTokens,\n        completion_tokens: estimatedTokens,\n        total_tokens: estimatedTokens * 2,\n      } as OpenAI.CompletionUsage;\n    }\n\n    const finalUsage = buildUsageInfo(usage, requestId);\n    // Report usage to the runtime-level collector if not already reported\n    // (e.g. from the streaming final-chunk handler).\n    if (!usageReported && finalUsage && modelRuntime.onUsage) {\n      modelRuntime.onUsage(finalUsage);\n    }\n\n    return {\n      content: content || '',\n      reasoning_content: accumulatedReasoning || undefined,\n      rawChoiceMessage,\n      usage: finalUsage,\n      isStreamed: !!isStreaming,\n    };\n  } catch (e: any) {\n    warnCall('call AI error', e);\n\n    if (e instanceof AIResponseParseError) {\n      throw e;\n    }\n\n    const newError = new Error(\n      `failed to call ${isStreaming ? 'streaming ' : ''}AI model service (${modelName}): ${e.message}${formatOpenAIAPIErrorDetails(e, openAIErrorResponseContext)}\\nTrouble shooting: https://midscenejs.com/model-provider.html`,\n      {\n        cause: e,\n      },\n    );\n    throw newError;\n  }\n}\n\nexport async function callAIWithObjectResponse<T>(\n  messages: ChatCompletionMessageParam[],\n  modelRuntime: ModelRuntime,\n  options?: {\n    abortSignal?: AbortSignal;\n    jsonParserSource?: JsonParserSource;\n    retryTimes?: number;\n  },\n): Promise<{\n  // TODO: `content` is a misleading name here because this is already the parsed object response. Consider renaming it to `object` or `data`.\n  content: T;\n  contentString: string;\n  usage?: AIUsageInfo;\n  reasoning_content?: string;\n  rawChoiceMessage?: unknown;\n}> {\n  const { config: modelConfig, adapter } = modelRuntime;\n  return callAiAndParseWithRetry({\n    callAi: () =>\n      callAI(messages, modelRuntime, {\n        abortSignal: options?.abortSignal,\n      }),\n    parseResponse: (response) => {\n      assert(response, 'empty response');\n      const jsonContent = adapter.jsonParser(response.content, {\n        source: options?.jsonParserSource ?? 'generic-object',\n      });\n      // This API expects a JSON object. Bare JSON primitives are valid JSON,\n      // but do not satisfy object-response callers.\n      if (!jsonContent || typeof jsonContent !== 'object') {\n        throw new Error(\n          `failed to parse json response from model (${modelConfig.modelName}): ${response.content}`,\n        );\n      }\n      return {\n        content: jsonContent as T,\n        contentString: response.content,\n        usage: response.usage,\n        reasoning_content: response.reasoning_content,\n        rawChoiceMessage: response.rawChoiceMessage,\n      };\n    },\n    toParseError: (error, response) => {\n      const errorMessage =\n        error instanceof Error ? error.message : String(error);\n      return new AIResponseParseError(\n        errorMessage,\n        response.content,\n        response.usage,\n        response.rawChoiceMessage,\n        response.reasoning_content,\n      );\n    },\n    parseRetryTimes: options?.retryTimes,\n    abortSignal: options?.abortSignal,\n  });\n}\n\nexport async function callAIWithStringResponse(\n  msgs: AIArgs,\n  modelRuntime: ModelRuntime,\n  options?: Pick<CallAIOptions, 'abortSignal' | 'requiresOriginalImageDetail'>,\n): Promise<{\n  content: string;\n  usage?: AIUsageInfo;\n  rawChoiceMessage?: unknown;\n}> {\n  const { content, usage, rawChoiceMessage } = await callAI(\n    msgs,\n    modelRuntime,\n    options,\n  );\n  return { content, usage, rawChoiceMessage };\n}\n"],"names":["AIResponseParseError","Error","message","rawResponse","usage","rawChoiceMessage","reasoningContent","INTERNAL_CALL_ID_FIELD","internalCallIdCounter","nextInternalCallId","stringifyForDebug","value","JSON","_error","String","getLatestSuccessfulResponseRequestId","context","latestRequestId","response","undefined","getErrorMessage","error","toError","normalizeRetryCount","retryCount","Number","Math","appendAIRequestFailureSummary","attemptErrors","maxAttempts","failedAttempts","retries","retryLabel","originalMessage","previousAttemptErrors","details","attempt","createChatClient","modelConfig","socksProxy","httpProxy","modelName","openaiBaseURL","openaiApiKey","openaiExtraConfig","modelDescription","modelFamily","createOpenAIClient","timeout","proxyAgent","warnClient","getDebug","debugProxy","warnProxy","sanitizeProxyUrl","url","parsed","URL","ifInBrowser","moduleName","ProxyAgent","socksDispatcher","proxyUrl","port","protocol","socksType","decodeURIComponent","effectiveTimeoutMs","resolveEffectiveTimeoutMs","openAIErrorResponseContext","openAIOptions","wrapOpenAICompatibleFetch","baseOpenAI","OpenAI","openai","globalConfigManager","MIDSCENE_LANGSMITH_DEBUG","langsmithModule","wrapOpenAI","MIDSCENE_LANGFUSE_DEBUG","langfuseModule","observeOpenAI","wrappedClient","callAI","messages","modelRuntime","options","adapter","internalCallId","isCodexAppServerProvider","codexResult","callAIWithCodexAppServer","completion","extraBody","debugCall","warnCall","debugProfileStats","debugProfileDetail","startTime","Date","isStreaming","chatCompletionInput","adapterChatCompletionParams","content","accumulated","accumulatedReasoning","timeCost","requestId","responseModelName","usageReported","hasUsableText","resolveContentWithReasoningFallback","contentValue","buildUsageInfo","usageData","cachedInputTokens","requestConfig","temperature","imageDetail","messagesWithImageDetail","msg","Array","part","streamSignal","cleanupStreamSignal","buildRequestAbortSignal","stream","chunk","parsedChunk","reasoning_content","chunkData","estimatedTokens","finalAccumulated","finalUsage","finalChunk","retryInterval","lastError","attemptSignal","cleanupAttemptSignal","result","parsedMessage","errorUsage","wasHardTimeout","isHardTimeoutError","Promise","resolve","setTimeout","assert","e","newError","formatOpenAIAPIErrorDetails","callAIWithObjectResponse","callAiAndParseWithRetry","jsonContent","errorMessage","callAIWithStringResponse","msgs"],"mappings":"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"}