chore: align infrastructure with server
This commit is contained in:
+72
-14
@@ -1,31 +1,89 @@
|
||||
export const generateAIResponse = async (prompt: string, systemPrompt = 'You are a helpful assistant.') => {
|
||||
const config = useRuntimeConfig()
|
||||
const apiKey = config.deepseekApiKey || process.env.DEEPSEEK_API_KEY
|
||||
/**
|
||||
* XFlow AI Utility — OpenRouter Integration (Stable Models)
|
||||
*
|
||||
* Поддерживаемые модели (наиболее стабильные и без жестких rate-limit):
|
||||
*
|
||||
* • poolside/laguna-m.1:free — быстрые инженерные задачи, Claude-based (default)
|
||||
* • nvidia/nemotron-3-super-120b-a12b:free — MoE 120B, тяжёлая аналитика, сложные рассуждения
|
||||
*/
|
||||
|
||||
const OPENROUTER_BASE_URL = 'https://openrouter.ai/api/v1/chat/completions'
|
||||
|
||||
/** Доступные модели с описанием для выбора в UI */
|
||||
export const AI_MODELS = {
|
||||
/** Сложные инженерные и архитектурные задачи, высокая стабильность */
|
||||
laguna: 'poolside/laguna-m.1:free',
|
||||
/** MoE 120B тяжёлая аналитика, архитектурный анализ, сложные рассуждения */
|
||||
nemotron: 'nvidia/nemotron-3-super-120b-a12b:free',
|
||||
} as const
|
||||
|
||||
export type AiModelKey = keyof typeof AI_MODELS
|
||||
|
||||
/** Дефолтная модель, если не указана */
|
||||
const DEFAULT_MODEL: AiModelKey = 'laguna'
|
||||
|
||||
export const generateAIResponse = async (
|
||||
prompt: string,
|
||||
systemPrompt = 'You are a helpful assistant.',
|
||||
modelKey?: AiModelKey | string,
|
||||
): Promise<string> => {
|
||||
const apiKey =
|
||||
process.env.NUXT_OPEN_ROUTER_API_KEY ||
|
||||
process.env.OPEN_ROUTER_API_KEY
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error('DEEPSEEK_API_KEY is not configured')
|
||||
throw new Error('OPEN_ROUTER_API_KEY is not configured')
|
||||
}
|
||||
|
||||
// Резолвим модель: принимаем как ключ ('laguna') так и полное имя
|
||||
const resolvedModel: string =
|
||||
modelKey && modelKey in AI_MODELS
|
||||
? AI_MODELS[modelKey as AiModelKey]
|
||||
: modelKey && modelKey.includes('/')
|
||||
? modelKey
|
||||
: AI_MODELS[DEFAULT_MODEL]
|
||||
|
||||
console.log(`[AI] Using model: ${resolvedModel}`)
|
||||
|
||||
try {
|
||||
const response = await $fetch('https://api.deepseek.com/v1/chat/completions', {
|
||||
const response = await $fetch<any>(OPENROUTER_BASE_URL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${apiKey}`,
|
||||
'Content-Type': 'application/json'
|
||||
'Content-Type': 'application/json',
|
||||
'HTTP-Referer': 'https://a-crm.controlsystem.kz',
|
||||
'X-Title': 'XFlow AI Node',
|
||||
},
|
||||
body: {
|
||||
model: 'deepseek-chat',
|
||||
model: resolvedModel,
|
||||
messages: [
|
||||
{ role: 'system', content: systemPrompt },
|
||||
{ role: 'user', content: prompt }
|
||||
{ role: 'user', content: prompt },
|
||||
],
|
||||
stream: false
|
||||
}
|
||||
}) as any
|
||||
stream: false,
|
||||
},
|
||||
})
|
||||
|
||||
return response.choices[0].message.content
|
||||
const content = response?.choices?.[0]?.message?.content
|
||||
if (!content) {
|
||||
throw new Error('Empty response from AI model')
|
||||
}
|
||||
return content
|
||||
} catch (error: any) {
|
||||
console.error('DeepSeek AI error:', error.data || error.message)
|
||||
throw new Error(`AI generation failed: ${error.message}`)
|
||||
console.error(`[AI] Error with model ${resolvedModel}:`, error?.data || error?.message)
|
||||
throw new Error(`AI generation failed (${resolvedModel}): ${error?.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/** Хелпер: список моделей для фронтенда */
|
||||
export const getAvailableModels = () =>
|
||||
Object.entries(AI_MODELS).map(([key, modelId]) => ({
|
||||
key,
|
||||
modelId,
|
||||
label: modelLabels[key as AiModelKey],
|
||||
}))
|
||||
|
||||
const modelLabels: Record<AiModelKey, string> = {
|
||||
laguna: '🏊 Laguna M.1 — стабильная инженерная (Claude)',
|
||||
nemotron: '🔬 Nemotron Super 120B — тяжёлая аналитика (NVIDIA)',
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user