feat: enhance AI model integration and configuration

- Added support for OpenAI alongside existing Anthropic model.
- Introduced environment variables for API key, base URL, model, and AI provider.
- Updated model selection logic to accommodate multiple AI providers.
- Refactored model functions to streamline API key retrieval and model initialization.
- Enhanced sidebar component by removing unused imports.

Additionally, updated package dependencies and added a new entry to .gitignore for history files.
This commit is contained in:
Zachary Hou 2024-12-21 01:55:03 +08:00
parent eda10b1212
commit 4a6d8a3a9f
7 changed files with 675 additions and 137 deletions

2
.gitignore vendored
View File

@ -28,3 +28,5 @@ dist-ssr
*.vars
.wrangler
_worker.bundle
.history

View File

@ -2,7 +2,6 @@ import { motion, type Variants } from 'framer-motion';
import { useCallback, useEffect, useRef, useState } from 'react';
import { toast } from 'react-toastify';
import { Dialog, DialogButton, DialogDescription, DialogRoot, DialogTitle } from '~/components/ui/Dialog';
import { IconButton } from '~/components/ui/IconButton';
import { ThemeSwitch } from '~/components/ui/ThemeSwitch';
import { db, deleteById, getAll, chatId, type ChatHistoryItem } from '~/lib/persistence';
import { cubicEasingFn } from '~/utils/easings';

View File

@ -1,9 +1,56 @@
import { createAnthropic } from '@ai-sdk/anthropic';
import { createOpenAI } from '@ai-sdk/openai';
import { getAPIKey } from './api-key';
export function getAnthropicModel(apiKey: string) {
export const aiProvider = {
openAI: 'openai',
anthropic: 'anthropic',
custom: 'custom',
} as const;
export type AiProviderType = (typeof aiProvider)[keyof typeof aiProvider];
export const defaultModels: Record<AiProviderType, string> = {
[aiProvider.openAI]: 'chatgpt-4o-latest',
[aiProvider.anthropic]: 'claude-3-5-sonnet-20240620',
[aiProvider.custom]: 'llama3.1',
} as const;
export function isValidProvider(provider: string): provider is AiProviderType {
return Object.values(aiProvider).includes(provider as AiProviderType);
}
export function getDefaultModel(provider: AiProviderType): string {
return defaultModels[provider];
}
export function getAnthropicModel(env: Env) {
const defaultModel = getDefaultModel(aiProvider.anthropic);
const anthropic = createAnthropic({
apiKey,
apiKey: getAPIKey(env),
});
return anthropic('claude-3-5-sonnet-20240620');
return anthropic(process.env.MODEL || env.MODEL || defaultModel);
}
export function getOpenAIModel(env: Env) {
const defaultModel = getDefaultModel(aiProvider.openAI);
const openai = createOpenAI({
compatibility: 'strict', // strict mode, enabled when using the OpenAI API
apiKey: process.env.API_KEY || env.API_KEY,
});
return openai(process.env.MODEL || env.MODEL || defaultModel);
}
export function getCustomModel(env: Env) {
const defaultModel = getDefaultModel(aiProvider.custom);
const openaiCompatible = createOpenAI({
compatibility: 'compatible',
apiKey: process.env.API_KEY || env.API_KEY || 'bogus', // if local model, do not need api key
baseURL: process.env.BASE_URL || env.BASE_URL || 'http://localhost:11434/v1',
});
return openaiCompatible(process.env.MODEL || env.MODEL || defaultModel);
}

View File

@ -1,6 +1,5 @@
import { streamText as _streamText, convertToCoreMessages } from 'ai';
import { getAPIKey } from '~/lib/.server/llm/api-key';
import { getAnthropicModel } from '~/lib/.server/llm/model';
import { getAnthropicModel, getOpenAIModel, getCustomModel } from '~/lib/.server/llm/model';
import { MAX_TOKENS } from './constants';
import { getSystemPrompt } from './prompts';
@ -22,14 +21,44 @@ export type Messages = Message[];
export type StreamingOptions = Omit<Parameters<typeof _streamText>[0], 'model'>;
export function streamText(messages: Messages, env: Env, options?: StreamingOptions) {
return _streamText({
model: getAnthropicModel(getAPIKey(env)),
system: getSystemPrompt(),
maxTokens: MAX_TOKENS,
headers: {
'anthropic-beta': 'max-tokens-3-5-sonnet-2024-07-15',
},
messages: convertToCoreMessages(messages),
...options,
});
const aiProvider = process.env.AI_PROVIDER || env.AI_PROVIDER || 'anthropic';
switch (aiProvider) {
case 'anthropic': {
return _streamText({
model: getAnthropicModel(env),
system: getSystemPrompt(),
maxTokens: MAX_TOKENS,
messages: convertToCoreMessages(messages),
...options,
});
}
case 'openai': {
return _streamText({
model: getOpenAIModel(env),
system: getSystemPrompt(),
maxTokens: MAX_TOKENS,
messages: convertToCoreMessages(messages),
...options,
});
}
case 'custom': {
console.log('1111', messages);
console.log('222', process.env.MODEL);
return _streamText({
model: getCustomModel(env),
system: getSystemPrompt(),
maxTokens: MAX_TOKENS,
messages,
...options,
});
}
default: {
throw new Error(`Invalid AI provider: ${aiProvider}`);
}
}
}

View File

@ -24,6 +24,8 @@
},
"dependencies": {
"@ai-sdk/anthropic": "^0.0.39",
"@ai-sdk/openai": "^0.0.58",
"@ai-sdk/provider": "^1.0.2",
"@codemirror/autocomplete": "^6.17.0",
"@codemirror/commands": "^6.6.0",
"@codemirror/lang-cpp": "^6.0.2",
@ -49,21 +51,21 @@
"@remix-run/cloudflare-pages": "^2.10.2",
"@remix-run/react": "^2.10.2",
"@uiw/codemirror-theme-vscode": "^4.23.0",
"@unocss/reset": "^0.61.0",
"@unocss/reset": "^0.61.3",
"@webcontainer/api": "1.3.0-internal.10",
"@xterm/addon-fit": "^0.10.0",
"@xterm/addon-web-links": "^0.11.0",
"@xterm/xterm": "^5.5.0",
"ai": "^3.3.4",
"ai": "^3.4.33",
"date-fns": "^3.6.0",
"diff": "^5.2.0",
"framer-motion": "^11.2.12",
"isbot": "^4.1.0",
"isbot": "^4.4.0",
"istextorbinary": "^9.5.0",
"jose": "^5.6.3",
"nanostores": "^0.10.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hotkeys-hook": "^4.5.0",
"react-markdown": "^9.0.1",
"react-resizable-panels": "^2.0.20",
@ -81,8 +83,8 @@
"@cloudflare/workers-types": "^4.20240620.0",
"@remix-run/dev": "^2.10.0",
"@types/diff": "^5.2.1",
"@types/react": "^18.2.20",
"@types/react-dom": "^18.2.7",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"fast-glob": "^3.3.2",
"is-ci": "^3.0.1",
"node-fetch": "^3.3.2",
@ -99,6 +101,7 @@
"zod": "^3.23.8"
},
"resolutions": {
"@typescript-eslint/utils": "^8.0.0-alpha.30"
"@typescript-eslint/utils": "^8.0.0-alpha.30",
"@ai-sdk/provider": "1.0.2"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,7 @@
interface Env {
ANTHROPIC_API_KEY: string;
API_KEY: string;
BASE_URL: string;
MODEL: string;
AI_PROVIDER: string;
}