{"version":3,"file":"ai-model/service-caller/openai-error.mjs","sources":["../../../../src/ai-model/service-caller/openai-error.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\n\nconst MAX_ERROR_RESPONSE_BODY_LENGTH = 4000;\nconst MAX_FETCH_ERROR_LENGTH = 4000;\n\nconst debugOpenAIFetch = getDebug('ai:call');\n\nexport interface OpenAIErrorResponseContext {\n  responseRequestIds?: Array<{\n    attempt: number;\n    requestId: string;\n    status: number;\n    ok: boolean;\n  }>;\n  rawResponseBodies?: Array<{\n    attempt: number;\n    body: string;\n  }>;\n  fetchErrors?: Array<{\n    attempt: number;\n    error: string;\n  }>;\n}\n\nfunction truncateText(text: string, maxLength: number): string {\n  if (text.length <= maxLength) {\n    return text;\n  }\n\n  return `${text.slice(0, maxLength)}... [truncated, ${text.length} chars total]`;\n}\n\nfunction truncateErrorResponseBody(body: string): string {\n  return truncateText(body, MAX_ERROR_RESPONSE_BODY_LENGTH);\n}\n\nfunction getErrorCode(error: unknown): string | undefined {\n  if (!error || typeof error !== 'object') {\n    return undefined;\n  }\n\n  const code = (error as { code?: unknown }).code;\n  return typeof code === 'string' ? code : undefined;\n}\n\nfunction formatErrorSummary(error: unknown): string {\n  if (error instanceof Error) {\n    const code = getErrorCode(error);\n    const codeText = code ? ` [${code}]` : '';\n    return `${error.name}${codeText}: ${error.message}`;\n  }\n\n  return String(error);\n}\n\nfunction formatFetchErrorForReport(error: unknown): string {\n  const details = [formatErrorSummary(error)];\n  const cause =\n    error && typeof error === 'object'\n      ? (error as { cause?: unknown }).cause\n      : undefined;\n\n  if (cause !== undefined) {\n    details.push(`Cause: ${formatErrorSummary(cause)}`);\n  }\n\n  return truncateText(details.join('\\n'), MAX_FETCH_ERROR_LENGTH);\n}\n\n// Mirrors OpenAI SDK's default fetch selection:\n// openai@6.3.0 src/client.ts sets `this.fetch = options.fetch ?? Shims.getDefaultFetch()`,\n// and src/internal/shims.ts resolves that default to global `fetch`.\nfunction getDefaultFetch(): typeof fetch {\n  if (typeof globalThis.fetch === 'function') {\n    return globalThis.fetch;\n  }\n\n  throw new Error(\n    '`fetch` is not defined as a global; check that the runtime provides globalThis.fetch or polyfill it before creating the OpenAI client',\n  );\n}\n\nexport function wrapOpenAICompatibleFetch(\n  context: OpenAIErrorResponseContext,\n): typeof fetch {\n  const baseFetch = getDefaultFetch();\n  let attempt = 0;\n\n  return async (input, init) => {\n    attempt += 1;\n    let response: Response;\n    try {\n      response = await baseFetch(input, init);\n    } catch (error) {\n      const fetchErrorSummary = formatFetchErrorForReport(error);\n      debugOpenAIFetch('OpenAI-compatible fetch failed', fetchErrorSummary);\n      context.fetchErrors ??= [];\n      context.fetchErrors.push({\n        attempt,\n        error: fetchErrorSummary,\n      });\n      throw error;\n    }\n\n    const requestId =\n      response.headers.get('x-request-id') ??\n      response.headers.get('x-model-request-id');\n\n    if (requestId) {\n      context.responseRequestIds ??= [];\n      context.responseRequestIds.push({\n        attempt,\n        requestId,\n        status: response.status,\n        ok: response.ok,\n      });\n    }\n\n    if (!response.ok) {\n      // OpenAI SDK only exposes the `error` field for JSON error responses.\n      // Non-standard provider bodies like `{ err: 'xxx' }` would otherwise be\n      // hidden from Midscene's final error message.\n      const rawResponseBody = await response\n        .clone()\n        .text()\n        .catch(() => undefined);\n\n      if (rawResponseBody !== undefined) {\n        context.rawResponseBodies ??= [];\n        context.rawResponseBodies.push({\n          attempt,\n          body: rawResponseBody,\n        });\n      }\n    }\n\n    return response;\n  };\n}\n\nexport function formatOpenAIAPIErrorDetails(\n  _error: unknown,\n  context: OpenAIErrorResponseContext,\n): string {\n  const details: string[] = [];\n\n  if (context.rawResponseBodies?.length === 1) {\n    details.push(\n      `OpenAI raw error response body: ${truncateErrorResponseBody(\n        context.rawResponseBodies[0].body,\n      )}`,\n    );\n  } else if (context.rawResponseBodies?.length) {\n    const rawResponseBodyDetails = context.rawResponseBodies\n      .map(\n        ({ attempt, body }) =>\n          `Attempt ${attempt}: ${truncateErrorResponseBody(body)}`,\n      )\n      .join('\\n');\n\n    details.push(\n      `OpenAI raw error response bodies:\\n${rawResponseBodyDetails}`,\n    );\n  }\n\n  const errorResponseRequestIds = context.responseRequestIds?.filter(\n    ({ ok }) => !ok,\n  );\n  if (errorResponseRequestIds?.length === 1) {\n    const { attempt, requestId, status } = errorResponseRequestIds[0];\n    details.push(\n      `OpenAI error response request ID (attempt ${attempt}, status ${status}): ${requestId}`,\n    );\n  } else if (errorResponseRequestIds?.length) {\n    const requestIdDetails = errorResponseRequestIds\n      .map(\n        ({ attempt, requestId, status }) =>\n          `Attempt ${attempt} (status ${status}): ${requestId}`,\n      )\n      .join('\\n');\n    details.push(`OpenAI error response request IDs:\\n${requestIdDetails}`);\n  }\n\n  if (context.fetchErrors?.length === 1) {\n    details.push(\n      `OpenAI fetch error (attempt ${context.fetchErrors[0].attempt}): ${context.fetchErrors[0].error}`,\n    );\n  } else if (context.fetchErrors?.length) {\n    const fetchErrorDetails = context.fetchErrors\n      .map(({ attempt, error }) => `Attempt ${attempt}: ${error}`)\n      .join('\\n');\n\n    details.push(`OpenAI fetch errors:\\n${fetchErrorDetails}`);\n  }\n\n  if (!details.length) {\n    return '';\n  }\n\n  return `\\n${details.join('\\n')}`;\n}\n"],"names":["MAX_ERROR_RESPONSE_BODY_LENGTH","MAX_FETCH_ERROR_LENGTH","debugOpenAIFetch","getDebug","truncateText","text","maxLength","truncateErrorResponseBody","body","getErrorCode","error","code","undefined","formatErrorSummary","Error","codeText","String","formatFetchErrorForReport","details","cause","getDefaultFetch","globalThis","wrapOpenAICompatibleFetch","context","baseFetch","attempt","input","init","response","fetchErrorSummary","requestId","rawResponseBody","formatOpenAIAPIErrorDetails","_error","rawResponseBodyDetails","errorResponseRequestIds","ok","status","requestIdDetails","fetchErrorDetails"],"mappings":"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"}