- Add Deepseek client using OpenAI SDK with custom baseURL - Implement createDeepseekClient() factory function with lazy initialization - Add ChatMessage type definitions for type safety - Include portfolio-specific system prompt for the chatbot - Support both streaming and non-streaming chat completions - Configure default model (deepseek-chat) and sensible defaults Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
93 lines
2.8 KiB
TypeScript
93 lines
2.8 KiB
TypeScript
import OpenAI from 'openai';
|
|
|
|
// Deepseek configuration constants
|
|
const DEEPSEEK_BASE_URL = 'https://api.deepseek.com';
|
|
const DEEPSEEK_DEFAULT_MODEL = 'deepseek-chat';
|
|
|
|
// Create Deepseek client using OpenAI SDK
|
|
// The client is lazy-initialized to avoid issues during build time
|
|
let deepseekClient: OpenAI | null = null;
|
|
|
|
export function createDeepseekClient(): OpenAI {
|
|
if (!process.env.DEEPSEEK_API_KEY) {
|
|
throw new Error('DEEPSEEK_API_KEY environment variable is not set');
|
|
}
|
|
|
|
if (!deepseekClient) {
|
|
deepseekClient = new OpenAI({
|
|
baseURL: DEEPSEEK_BASE_URL,
|
|
apiKey: process.env.DEEPSEEK_API_KEY,
|
|
});
|
|
}
|
|
|
|
return deepseekClient;
|
|
}
|
|
|
|
// Type definitions for chat messages
|
|
export type ChatRole = 'system' | 'user' | 'assistant';
|
|
|
|
export interface ChatMessage {
|
|
role: ChatRole;
|
|
content: string;
|
|
}
|
|
|
|
// System prompt for the portfolio chatbot
|
|
export const PORTFOLIO_SYSTEM_PROMPT = `You are a helpful AI assistant on Damjan Savić's portfolio website.
|
|
Damjan is a full-stack web developer and AI specialist based in Vienna, Austria.
|
|
You can help visitors learn about Damjan's skills, services, and experience.
|
|
Be friendly, professional, and concise in your responses.
|
|
If asked about topics unrelated to Damjan or web development, politely redirect the conversation.
|
|
Respond in the same language the user writes to you (German, English, or Serbian).`;
|
|
|
|
// Chat completion options
|
|
export interface ChatCompletionOptions {
|
|
messages: ChatMessage[];
|
|
model?: string;
|
|
temperature?: number;
|
|
maxTokens?: number;
|
|
stream?: boolean;
|
|
}
|
|
|
|
// Create a chat completion (non-streaming)
|
|
export async function createChatCompletion(
|
|
options: ChatCompletionOptions
|
|
): Promise<string> {
|
|
const client = createDeepseekClient();
|
|
|
|
const completion = await client.chat.completions.create({
|
|
model: options.model ?? DEEPSEEK_DEFAULT_MODEL,
|
|
messages: options.messages,
|
|
temperature: options.temperature ?? 0.7,
|
|
max_tokens: options.maxTokens ?? 1024,
|
|
stream: false,
|
|
});
|
|
|
|
return completion.choices[0]?.message?.content ?? '';
|
|
}
|
|
|
|
// Create a streaming chat completion
|
|
export async function createStreamingChatCompletion(
|
|
options: Omit<ChatCompletionOptions, 'stream'>
|
|
): Promise<AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>> {
|
|
const client = createDeepseekClient();
|
|
|
|
const stream = await client.chat.completions.create({
|
|
model: options.model ?? DEEPSEEK_DEFAULT_MODEL,
|
|
messages: options.messages,
|
|
temperature: options.temperature ?? 0.7,
|
|
max_tokens: options.maxTokens ?? 1024,
|
|
stream: true,
|
|
});
|
|
|
|
return stream;
|
|
}
|
|
|
|
// Default export for convenience
|
|
export default {
|
|
createClient: createDeepseekClient,
|
|
createCompletion: createChatCompletion,
|
|
createStreamingCompletion: createStreamingChatCompletion,
|
|
SYSTEM_PROMPT: PORTFOLIO_SYSTEM_PROMPT,
|
|
DEFAULT_MODEL: DEEPSEEK_DEFAULT_MODEL,
|
|
};
|