Initial commit: Meshkee CMS API
NestJS backend with Prisma, Docker Compose for Postgres/Redis, and deploy docs for the production VM.
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import { ServiceUnavailableException } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
export type AiProviderConfig = {
|
||||
apiKey: string;
|
||||
baseUrl: string;
|
||||
model: string;
|
||||
};
|
||||
|
||||
export function resolveAiProvider(config: ConfigService): AiProviderConfig {
|
||||
const configured = config.get<string>('AI_PROVIDER')?.trim().toLowerCase();
|
||||
const groqKey = config.get<string>('GROQ_API_KEY')?.trim();
|
||||
const openAiKey = config.get<string>('OPENAI_API_KEY')?.trim();
|
||||
|
||||
const useGroq =
|
||||
configured === 'groq' || (!configured && !!groqKey) || (!openAiKey && !!groqKey);
|
||||
|
||||
if (useGroq) {
|
||||
if (!groqKey) {
|
||||
throw new ServiceUnavailableException(
|
||||
'AI is not configured. Set GROQ_API_KEY on the server.',
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
apiKey: groqKey,
|
||||
baseUrl: 'https://api.groq.com/openai/v1',
|
||||
model: config.get<string>('GROQ_MODEL')?.trim() || 'llama-3.3-70b-versatile',
|
||||
};
|
||||
}
|
||||
|
||||
if (!openAiKey) {
|
||||
throw new ServiceUnavailableException(
|
||||
'AI is not configured. Set GROQ_API_KEY or OPENAI_API_KEY on the server.',
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
apiKey: openAiKey,
|
||||
baseUrl: 'https://api.openai.com/v1',
|
||||
model: config.get<string>('OPENAI_MODEL')?.trim() || 'gpt-4o-mini',
|
||||
};
|
||||
}
|
||||
|
||||
export async function requestAiJsonCompletion(
|
||||
provider: AiProviderConfig,
|
||||
systemPrompt: string,
|
||||
userPrompt: string,
|
||||
temperature = 0.4,
|
||||
): Promise<string> {
|
||||
const response = await fetch(`${provider.baseUrl}/chat/completions`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${provider.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: provider.model,
|
||||
temperature,
|
||||
response_format: { type: 'json_object' },
|
||||
messages: [
|
||||
{ role: 'system', content: systemPrompt },
|
||||
{ role: 'user', content: userPrompt },
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const detail = await response.text();
|
||||
throw new Error(`AI provider error (${response.status}): ${detail.slice(0, 240)}`);
|
||||
}
|
||||
|
||||
const payload = (await response.json()) as {
|
||||
choices?: { message?: { content?: string } }[];
|
||||
};
|
||||
|
||||
const content = payload.choices?.[0]?.message?.content;
|
||||
if (!content) {
|
||||
throw new Error('AI returned an empty response');
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
Reference in New Issue
Block a user