"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateOpenAiModelProviderService = generateOpenAiModelProviderService;
function generateOpenAiModelProviderService() {
    return /* ts */ `
import { Injectable } from '@nestjs/common'

import { sanitizeSecretString } from '@postxl/utils'

import { AiConfig } from '../ai-config'
import type { AiModelProvider, AiProviderRequest, AiProviderResult } from './model-provider.interface'

@Injectable()
export class OpenAiModelProviderService implements AiModelProvider {
  constructor(private readonly aiConfig: AiConfig) {}

  public async requestSingleToolCall(request: AiProviderRequest): Promise<AiProviderResult> {
    const apiKey = this.pickOpenAIApiKey(this.aiConfig.config.openAIApiKeys, this.aiConfig.config.openAIApiKey)
    if (!apiKey) {
      throw new Error('AI_OPENAI_KEY is not configured')
    }
    if (!/^sk-[A-Za-z0-9]/.test(apiKey)) {
      throw new Error('AI_OPENAI_KEY appears invalid (expected to start with "sk-")')
    }

    const abortController = new AbortController()
    const timeout = setTimeout(() => abortController.abort(), this.aiConfig.config.timeoutMs)

    let response: Response
    try {
      response = await fetch('https://api.openai.com/v1/chat/completions', {
        method: 'POST',
        headers: {
          Authorization: 'Bearer ' + apiKey,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          model: this.aiConfig.config.model,
          messages: request.messages,
          tools: request.tools,
          tool_choice: 'required',
          parallel_tool_calls: false,
          temperature: 0,
        }),
        signal: abortController.signal,
      })
    } catch (error) {
      if ((error as { name?: string })?.name === 'AbortError') {
        throw new Error('OpenAI request timed out')
      }

      const details = sanitizeSecretString(error instanceof Error ? error.message : String(error))
      throw new Error('OpenAI request failed: ' + details)
    } finally {
      clearTimeout(timeout)
    }

    if (!response.ok) {
      let reason = response.statusText || 'request error'
      try {
        const payload = (await response.json()) as {
          error?: { message?: unknown }
        }
        if (payload?.error?.message != null) {
          reason = sanitizeSecretString(payload.error.message)
        }
      } catch {
        // Ignore body parse errors and fall back to statusText only.
      }

      throw new Error('OpenAI request failed (' + response.status + '): ' + reason)
    }

    const payload = (await response.json()) as {
      choices?: Array<{
        message?: {
          content?: string | null
          tool_calls?: Array<{
            id: string
            type: 'function'
            function: {
              name: string
              arguments: string
            }
          }>
        }
      }>
    }

    const message = payload.choices?.[0]?.message
    const toolCalls = message?.tool_calls ?? []

    if (toolCalls.length !== 1) {
      throw new Error('AI must return exactly one tool call')
    }

    const toolCall = toolCalls[0]

    return {
      assistantMessage: {
        role: 'assistant',
        content: message?.content ?? null,
        tool_calls: toolCalls,
      },
      toolCall,
    }
  }

  private keyRotationIndex = 0

  private pickOpenAIApiKey(keysRaw?: string, fallbackKey?: string): string | undefined {
    const keys = keysRaw
      ?.split(',')
      .map((value) => value.trim())
      .filter(Boolean)

    if (keys && keys.length > 0) {
      const key = keys[this.keyRotationIndex % keys.length]
      this.keyRotationIndex = (this.keyRotationIndex + 1) % keys.length
      return key
    }

    return fallbackKey
  }
}
`;
}
//# sourceMappingURL=openai-model-provider-service.generator.js.map