diff --git a/server/utils/openai.ts b/server/utils/openai.ts index a67416e..c3961e9 100644 --- a/server/utils/openai.ts +++ b/server/utils/openai.ts @@ -1,31 +1,45 @@ // server/utils/openai.ts import OpenAI from 'openai' -import { spellIcaoDigits, toIcaoPhonetic } from '../../shared/utils/radioSpeech' -import type { LLMDecision, LLMDecisionInput } from '../../shared/types/llm' -import { getServerRuntimeConfig } from './runtimeConfig' +import {spellIcaoDigits, toIcaoPhonetic} from '../../shared/utils/radioSpeech' +import type {LLMDecision, LLMDecisionInput} from '../../shared/types/llm' +import {getServerRuntimeConfig} from './runtimeConfig' let openaiClient: OpenAI | null = null let cachedModel: string | null = null +import https from 'node:https' + +const httpsAgent = new https.Agent({ + keepAlive: true, + maxSockets: 50, // bei Bedarf anpassen + maxFreeSockets: 10, + timeout: 0 // keine Socket-Idle-Timeouts durch Node +}) + function ensureOpenAI(): OpenAI { if (!openaiClient) { - const { openaiKey, openaiProject, llmModel } = getServerRuntimeConfig() + const {openaiKey, openaiProject, llmModel} = getServerRuntimeConfig() if (!openaiKey) { throw new Error('OPENAI_API_KEY is missing. Please set the key before using AI features.') } - const clientOptions: ConstructorParameters[0] = { apiKey: openaiKey } + const clientOptions: ConstructorParameters[0] = {apiKey: openaiKey, + defaultHeaders: { 'Connection': 'keep-alive' }, + defaultHttpAgent: httpsAgent + } if (openaiProject) { clientOptions.project = openaiProject } + console.log("using connection opened client") openaiClient = new OpenAI(clientOptions) cachedModel = llmModel } + console.log("returning existing openai client") return openaiClient } function getModel(): string { if (!cachedModel) { - const { llmModel } = getServerRuntimeConfig() + const {llmModel} = getServerRuntimeConfig() cachedModel = llmModel } return cachedModel @@ -91,12 +105,12 @@ const READBACK_JSON_SCHEMA = { }, missing: { type: 'array', - items: { type: 'string' }, + items: {type: 'string'}, default: [] }, incorrect: { type: 'array', - items: { type: 'string' }, + items: {type: 'string'}, default: [] }, confidence: { @@ -314,18 +328,22 @@ function optimizeInputForLLM(input: LLMDecisionInput) { export async function routeDecision(input: LLMDecisionInput): Promise { const pilotUtterance = (input.pilot_utterance || '').trim() const pilotText = pilotUtterance.toLowerCase() - const trace: LLMDecisionTrace = { calls: [] } + const trace: LLMDecisionTrace = {calls: []} const finalize = (decision: LLMDecision): LLMDecisionResult => { if (!trace.calls.length && !trace.fallback) { - return { decision } + return {decision} } - return { decision, trace } + return {decision, trace} } async function handleReadbackCheck(): Promise { const requiredKeys = READBACK_REQUIREMENTS[input.state_id] || input.state.readback_required || [] - const expectedItems = requiredKeys.reduce>((acc, key) => { + const expectedItems = requiredKeys.reduce>((acc, key) => { const value = resolveReadbackValue(key, input) if (!value) { return acc @@ -349,7 +367,7 @@ export async function routeDecision(input: LLMDecisionInput): Promise c.state?.role === 'system') if (interruptCandidate) { - return finalize({ next_state: interruptCandidate.id }) + return finalize({next_state: interruptCandidate.id}) } } @@ -454,10 +475,10 @@ export async function routeDecision(input: LLMDecisionInput): Promise 0) { - return finalize({ next_state: input.candidates[0].id }) + return finalize({next_state: input.candidates[0].id}) } // Compact yet informative prompt — includes variable info for intelligent responses @@ -500,10 +521,10 @@ export async function routeDecision(input: LLMDecisionInput): Promise + const fallbackInfo = {used: true, reason: errorMessage} as NonNullable trace.fallback = fallbackInfo console.error('LLM JSON parse error, using smart fallback:', e)