Files
Portfolio/src/lib/deepseek.ts
T
damjan_savicandClaude Opus 4.5 798d7e8f5b auto-claude: subtask-4-2 - Create Deepseek API client wrapper using OpenAI SDK
- 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>
2026-01-25 00:49:33 +01:00

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,
};