mirror of
https://github.com/ItzCrazyKns/Perplexica.git
synced 2025-11-24 14:08:14 +00:00
Compare commits
1 Commits
387da5dbdd
...
develop/ne
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f9eb3b1f3 |
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 641 KiB |
36
.github/workflows/docker-build.yaml
vendored
36
.github/workflows/docker-build.yaml
vendored
@@ -4,7 +4,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- canary
|
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
@@ -44,19 +43,6 @@ jobs:
|
|||||||
-t itzcrazykns1337/${IMAGE_NAME}:amd64 \
|
-t itzcrazykns1337/${IMAGE_NAME}:amd64 \
|
||||||
--push .
|
--push .
|
||||||
|
|
||||||
- name: Build and push AMD64 Canary Docker image
|
|
||||||
if: github.ref == 'refs/heads/canary' && github.event_name == 'push'
|
|
||||||
run: |
|
|
||||||
DOCKERFILE=app.dockerfile
|
|
||||||
IMAGE_NAME=perplexica
|
|
||||||
docker buildx build --platform linux/amd64 \
|
|
||||||
--cache-from=type=registry,ref=itzcrazykns1337/${IMAGE_NAME}:canary-amd64 \
|
|
||||||
--cache-to=type=inline \
|
|
||||||
--provenance false \
|
|
||||||
-f $DOCKERFILE \
|
|
||||||
-t itzcrazykns1337/${IMAGE_NAME}:canary-amd64 \
|
|
||||||
--push .
|
|
||||||
|
|
||||||
- name: Build and push AMD64 release Docker image
|
- name: Build and push AMD64 release Docker image
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
run: |
|
run: |
|
||||||
@@ -105,19 +91,6 @@ jobs:
|
|||||||
-t itzcrazykns1337/${IMAGE_NAME}:arm64 \
|
-t itzcrazykns1337/${IMAGE_NAME}:arm64 \
|
||||||
--push .
|
--push .
|
||||||
|
|
||||||
- name: Build and push ARM64 Canary Docker image
|
|
||||||
if: github.ref == 'refs/heads/canary' && github.event_name == 'push'
|
|
||||||
run: |
|
|
||||||
DOCKERFILE=app.dockerfile
|
|
||||||
IMAGE_NAME=perplexica
|
|
||||||
docker buildx build --platform linux/arm64 \
|
|
||||||
--cache-from=type=registry,ref=itzcrazykns1337/${IMAGE_NAME}:canary-arm64 \
|
|
||||||
--cache-to=type=inline \
|
|
||||||
--provenance false \
|
|
||||||
-f $DOCKERFILE \
|
|
||||||
-t itzcrazykns1337/${IMAGE_NAME}:canary-arm64 \
|
|
||||||
--push .
|
|
||||||
|
|
||||||
- name: Build and push ARM64 release Docker image
|
- name: Build and push ARM64 release Docker image
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
run: |
|
run: |
|
||||||
@@ -155,15 +128,6 @@ jobs:
|
|||||||
--amend itzcrazykns1337/${IMAGE_NAME}:arm64
|
--amend itzcrazykns1337/${IMAGE_NAME}:arm64
|
||||||
docker manifest push itzcrazykns1337/${IMAGE_NAME}:main
|
docker manifest push itzcrazykns1337/${IMAGE_NAME}:main
|
||||||
|
|
||||||
- name: Create and push multi-arch manifest for canary
|
|
||||||
if: github.ref == 'refs/heads/canary' && github.event_name == 'push'
|
|
||||||
run: |
|
|
||||||
IMAGE_NAME=perplexica
|
|
||||||
docker manifest create itzcrazykns1337/${IMAGE_NAME}:canary \
|
|
||||||
--amend itzcrazykns1337/${IMAGE_NAME}:canary-amd64 \
|
|
||||||
--amend itzcrazykns1337/${IMAGE_NAME}:canary-arm64
|
|
||||||
docker manifest push itzcrazykns1337/${IMAGE_NAME}:canary
|
|
||||||
|
|
||||||
- name: Create and push multi-arch manifest for releases
|
- name: Create and push multi-arch manifest for releases
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ COPY drizzle ./drizzle
|
|||||||
RUN mkdir -p /home/perplexica/data
|
RUN mkdir -p /home/perplexica/data
|
||||||
RUN yarn build
|
RUN yarn build
|
||||||
|
|
||||||
|
RUN yarn add --dev @vercel/ncc
|
||||||
|
RUN yarn ncc build ./src/lib/db/migrate.ts -o migrator
|
||||||
|
|
||||||
FROM node:24.5.0-slim
|
FROM node:24.5.0-slim
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y python3 python3-pip sqlite3 && rm -rf /var/lib/apt/lists/*
|
RUN apt-get update && apt-get install -y python3 python3-pip sqlite3 && rm -rf /var/lib/apt/lists/*
|
||||||
@@ -27,6 +30,8 @@ COPY --from=builder /home/perplexica/.next/static ./public/_next/static
|
|||||||
COPY --from=builder /home/perplexica/.next/standalone ./
|
COPY --from=builder /home/perplexica/.next/standalone ./
|
||||||
COPY --from=builder /home/perplexica/data ./data
|
COPY --from=builder /home/perplexica/data ./data
|
||||||
COPY drizzle ./drizzle
|
COPY drizzle ./drizzle
|
||||||
|
COPY --from=builder /home/perplexica/migrator/build ./build
|
||||||
|
COPY --from=builder /home/perplexica/migrator/index.js ./migrate.js
|
||||||
|
|
||||||
RUN mkdir /home/perplexica/uploads
|
RUN mkdir /home/perplexica/uploads
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
node migrate.js
|
||||||
|
|
||||||
exec node server.js
|
exec node server.js
|
||||||
10
package.json
10
package.json
@@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "perplexica-frontend",
|
"name": "perplexica-frontend",
|
||||||
"version": "1.11.0-rc3",
|
"version": "1.11.0-rc2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"author": "ItzCrazyKns",
|
"author": "ItzCrazyKns",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "npm run db:migrate && next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"format:write": "prettier . --write"
|
"format:write": "prettier . --write",
|
||||||
|
"db:migrate": "node ./src/lib/db/migrate.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/react": "^2.2.0",
|
"@headlessui/react": "^2.2.0",
|
||||||
"@headlessui/tailwindcss": "^0.2.2",
|
|
||||||
"@iarna/toml": "^2.2.5",
|
"@iarna/toml": "^2.2.5",
|
||||||
"@icons-pack/react-simple-icons": "^12.3.0",
|
"@icons-pack/react-simple-icons": "^12.3.0",
|
||||||
"@langchain/anthropic": "^0.3.24",
|
"@langchain/anthropic": "^0.3.24",
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
"@langchain/core": "^0.3.66",
|
"@langchain/core": "^0.3.66",
|
||||||
"@langchain/google-genai": "^0.2.15",
|
"@langchain/google-genai": "^0.2.15",
|
||||||
"@langchain/groq": "^0.2.3",
|
"@langchain/groq": "^0.2.3",
|
||||||
|
"@langchain/langgraph": "^0.4.9",
|
||||||
"@langchain/ollama": "^0.2.3",
|
"@langchain/ollama": "^0.2.3",
|
||||||
"@langchain/openai": "^0.6.2",
|
"@langchain/openai": "^0.6.2",
|
||||||
"@langchain/textsplitters": "^0.1.0",
|
"@langchain/textsplitters": "^0.1.0",
|
||||||
@@ -65,7 +66,6 @@
|
|||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"tailwindcss": "^3.3.0",
|
"tailwindcss": "^3.3.0",
|
||||||
"ts-node": "^10.9.2",
|
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ const Focus = () => {
|
|||||||
<Popover className="relative w-full max-w-[15rem] md:max-w-md lg:max-w-lg mt-[6.5px]">
|
<Popover className="relative w-full max-w-[15rem] md:max-w-md lg:max-w-lg mt-[6.5px]">
|
||||||
<PopoverButton
|
<PopoverButton
|
||||||
type="button"
|
type="button"
|
||||||
className="active:border-none headless-open:text-black dark:headless-open:text-white text-black/50 dark:text-white/50 rounded-xl hover:bg-light-secondary dark:hover:bg-dark-secondary active:scale-95 transition duration-200 hover:text-black dark:hover:text-white"
|
className=" text-black/50 dark:text-white/50 rounded-xl hover:bg-light-secondary dark:hover:bg-dark-secondary active:scale-95 transition duration-200 hover:text-black dark:hover:text-white"
|
||||||
>
|
>
|
||||||
{focusMode !== 'webSearch' ? (
|
{focusMode !== 'webSearch' ? (
|
||||||
<div className="flex flex-row items-center space-x-1">
|
<div className="flex flex-row items-center space-x-1">
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
export const register = async () => {
|
|
||||||
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
||||||
try {
|
|
||||||
console.log('Running database migrations...');
|
|
||||||
await import('./lib/db/migrate');
|
|
||||||
console.log('Database migrations completed successfully');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to run database migrations:', error);
|
|
||||||
}
|
|
||||||
|
|
||||||
await import('./lib/config/index');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
import path from 'node:path';
|
|
||||||
import fs from 'fs';
|
|
||||||
import { Config, ConfigModelProvider, EnvMap, UIConfigSections } from './types';
|
|
||||||
import ModelRegistry from '../models/registry';
|
|
||||||
import { hashObj } from '../serverUtils';
|
|
||||||
|
|
||||||
class ConfigManager {
|
|
||||||
configPath: string = path.join(
|
|
||||||
process.env.DATA_DIR || process.cwd(),
|
|
||||||
'/data/config.json',
|
|
||||||
);
|
|
||||||
configVersion = 1;
|
|
||||||
currentConfig: Config = {
|
|
||||||
version: this.configVersion,
|
|
||||||
general: {},
|
|
||||||
modelProviders: [],
|
|
||||||
};
|
|
||||||
uiConfigSections: UIConfigSections = {
|
|
||||||
general: [],
|
|
||||||
modelProviders: [],
|
|
||||||
};
|
|
||||||
modelRegistry = new ModelRegistry();
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
private initialize() {
|
|
||||||
this.initializeConfig();
|
|
||||||
this.initializeFromEnv();
|
|
||||||
}
|
|
||||||
|
|
||||||
private saveConfig() {
|
|
||||||
fs.writeFileSync(
|
|
||||||
this.configPath,
|
|
||||||
JSON.stringify(this.currentConfig, null, 2),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeConfig() {
|
|
||||||
const exists = fs.existsSync(this.configPath);
|
|
||||||
if (!exists) {
|
|
||||||
fs.writeFileSync(
|
|
||||||
this.configPath,
|
|
||||||
JSON.stringify(this.currentConfig, null, 2),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
this.currentConfig = JSON.parse(
|
|
||||||
fs.readFileSync(this.configPath, 'utf-8'),
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof SyntaxError) {
|
|
||||||
console.error(
|
|
||||||
`Error parsing config file at ${this.configPath}:`,
|
|
||||||
err,
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
'Loading default config and overwriting the existing file.',
|
|
||||||
);
|
|
||||||
fs.writeFileSync(
|
|
||||||
this.configPath,
|
|
||||||
JSON.stringify(this.currentConfig, null, 2),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
console.log('Unknown error reading config file:', err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.currentConfig = this.migrateConfigNeeded(this.currentConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private migrateConfigNeeded(config: Config): Config {
|
|
||||||
/* TODO: Add migrations */
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeFromEnv() {
|
|
||||||
const providerConfigSections = this.modelRegistry.getUIConfigSection();
|
|
||||||
|
|
||||||
this.uiConfigSections.modelProviders = providerConfigSections;
|
|
||||||
|
|
||||||
const newProviders: ConfigModelProvider[] = [];
|
|
||||||
|
|
||||||
providerConfigSections.forEach((provider) => {
|
|
||||||
const newProvider: ConfigModelProvider & { required?: string[] } = {
|
|
||||||
id: crypto.randomUUID(),
|
|
||||||
name: `${provider.name} ${Math.floor(Math.random() * 1000)}`,
|
|
||||||
type: provider.key,
|
|
||||||
chatModels: [],
|
|
||||||
embeddingModels: [],
|
|
||||||
config: {},
|
|
||||||
required: [],
|
|
||||||
hash: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
provider.fields.forEach((field) => {
|
|
||||||
newProvider.config[field.key] =
|
|
||||||
process.env[field.env!] ||
|
|
||||||
field.default ||
|
|
||||||
''; /* Env var must exist for providers */
|
|
||||||
|
|
||||||
if (field.required) newProvider.required?.push(field.key);
|
|
||||||
});
|
|
||||||
|
|
||||||
let configured = true;
|
|
||||||
|
|
||||||
newProvider.required?.forEach((r) => {
|
|
||||||
if (!newProvider.config[r]) {
|
|
||||||
configured = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (configured) {
|
|
||||||
const hash = hashObj(newProvider.config);
|
|
||||||
newProvider.hash = hash;
|
|
||||||
delete newProvider.required;
|
|
||||||
|
|
||||||
const exists = this.currentConfig.modelProviders.find(
|
|
||||||
(p) => p.hash === hash,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!exists) {
|
|
||||||
newProviders.push(newProvider);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.currentConfig.modelProviders.push(...newProviders);
|
|
||||||
|
|
||||||
this.saveConfig();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new ConfigManager();
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
type BaseUIConfigField = {
|
|
||||||
name: string;
|
|
||||||
key: string;
|
|
||||||
required: boolean;
|
|
||||||
description: string;
|
|
||||||
scope: 'client' | 'server';
|
|
||||||
env?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type StringUIConfigField = BaseUIConfigField & {
|
|
||||||
type: 'string';
|
|
||||||
placeholder?: string;
|
|
||||||
default?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type SelectUIConfigFieldOptions = {
|
|
||||||
name: string;
|
|
||||||
key: string;
|
|
||||||
value: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type SelectUIConfigField = BaseUIConfigField & {
|
|
||||||
type: 'select';
|
|
||||||
default?: string;
|
|
||||||
options: SelectUIConfigFieldOptions[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type PasswordUIConfigField = BaseUIConfigField & {
|
|
||||||
type: 'password';
|
|
||||||
placeholder?: string;
|
|
||||||
default?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type UIConfigField =
|
|
||||||
| StringUIConfigField
|
|
||||||
| SelectUIConfigField
|
|
||||||
| PasswordUIConfigField;
|
|
||||||
|
|
||||||
type ConfigModelProvider = {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
chatModels: string[];
|
|
||||||
embeddingModels: string[];
|
|
||||||
config: { [key: string]: any };
|
|
||||||
hash: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Config = {
|
|
||||||
version: number;
|
|
||||||
general: {
|
|
||||||
[key: string]: any;
|
|
||||||
};
|
|
||||||
modelProviders: ConfigModelProvider[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type EnvMap = {
|
|
||||||
[key: string]: {
|
|
||||||
fieldKey: string;
|
|
||||||
providerKey: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type ModelProviderUISection = {
|
|
||||||
name: string;
|
|
||||||
key: string;
|
|
||||||
fields: UIConfigField[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type UIConfigSections = {
|
|
||||||
general: UIConfigField[];
|
|
||||||
modelProviders: ModelProviderUISection[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type {
|
|
||||||
UIConfigField,
|
|
||||||
Config,
|
|
||||||
EnvMap,
|
|
||||||
UIConfigSections,
|
|
||||||
ModelProviderUISection,
|
|
||||||
ConfigModelProvider,
|
|
||||||
};
|
|
||||||
@@ -2,12 +2,9 @@ import Database from 'better-sqlite3';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
||||||
const DATA_DIR = process.env.DATA_DIR || process.cwd();
|
const db = new Database(path.join(process.cwd(), 'data', 'db.sqlite'));
|
||||||
const dbPath = path.join(DATA_DIR, './data/db.sqlite');
|
|
||||||
|
|
||||||
const db = new Database(dbPath);
|
const migrationsFolder = path.join(process.cwd(), 'drizzle');
|
||||||
|
|
||||||
const migrationsFolder = path.join(DATA_DIR, 'drizzle');
|
|
||||||
|
|
||||||
db.exec(`
|
db.exec(`
|
||||||
CREATE TABLE IF NOT EXISTS ran_migrations (
|
CREATE TABLE IF NOT EXISTS ran_migrations (
|
||||||
@@ -57,7 +54,7 @@ fs.readdirSync(migrationsFolder)
|
|||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
type TEXT NOT NULL,
|
type TEXT NOT NULL,
|
||||||
chatId TEXT NOT NULL,
|
chatId TEXT NOT NULL,
|
||||||
createdAt TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
createdAt TEXT NOT NULL,
|
||||||
messageId TEXT NOT NULL,
|
messageId TEXT NOT NULL,
|
||||||
content TEXT,
|
content TEXT,
|
||||||
sources TEXT DEFAULT '[]'
|
sources TEXT DEFAULT '[]'
|
||||||
@@ -70,10 +67,8 @@ fs.readdirSync(migrationsFolder)
|
|||||||
`);
|
`);
|
||||||
|
|
||||||
messages.forEach((msg: any) => {
|
messages.forEach((msg: any) => {
|
||||||
while (typeof msg.metadata === 'string') {
|
|
||||||
msg.metadata = JSON.parse(msg.metadata || '{}');
|
|
||||||
}
|
|
||||||
if (msg.type === 'user') {
|
if (msg.type === 'user') {
|
||||||
|
msg.metadata = JSON.parse(msg.metadata || '{}');
|
||||||
insertMessage.run(
|
insertMessage.run(
|
||||||
'user',
|
'user',
|
||||||
msg.chatId,
|
msg.chatId,
|
||||||
@@ -83,6 +78,7 @@ fs.readdirSync(migrationsFolder)
|
|||||||
'[]',
|
'[]',
|
||||||
);
|
);
|
||||||
} else if (msg.type === 'assistant') {
|
} else if (msg.type === 'assistant') {
|
||||||
|
msg.metadata = JSON.parse(msg.metadata || '{}');
|
||||||
insertMessage.run(
|
insertMessage.run(
|
||||||
'assistant',
|
'assistant',
|
||||||
msg.chatId,
|
msg.chatId,
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
import { Embeddings } from '@langchain/core/embeddings';
|
|
||||||
import { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
||||||
import { Model, ModelList, ProviderMetadata } from '../types';
|
|
||||||
import { UIConfigField } from '@/lib/config/types';
|
|
||||||
|
|
||||||
abstract class BaseModelProvider<CONFIG> {
|
|
||||||
constructor(protected config: CONFIG) {}
|
|
||||||
abstract getDefaultModels(): Promise<ModelList>;
|
|
||||||
abstract getModelList(): Promise<ModelList>;
|
|
||||||
abstract loadChatModel(modelName: string): Promise<BaseChatModel>;
|
|
||||||
abstract loadEmbeddingModel(modelName: string): Promise<Embeddings>;
|
|
||||||
static getProviderConfigFields(): UIConfigField[] {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
static getProviderMetadata(): ProviderMetadata {
|
|
||||||
throw new Error('Method not Implemented.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BaseModelProvider;
|
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
import { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
||||||
import { Model, ModelList, ProviderMetadata } from '../types';
|
|
||||||
import BaseModelProvider from './baseProvider';
|
|
||||||
import { ChatOpenAI, OpenAIEmbeddings } from '@langchain/openai';
|
|
||||||
import { Embeddings } from '@langchain/core/embeddings';
|
|
||||||
import { UIConfigField } from '@/lib/config/types';
|
|
||||||
|
|
||||||
interface OpenAIConfig {
|
|
||||||
name: string;
|
|
||||||
apiKey: string;
|
|
||||||
baseURL: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultChatModels: Model[] = [
|
|
||||||
{
|
|
||||||
name: 'GPT-3.5 Turbo',
|
|
||||||
key: 'gpt-3.5-turbo',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'GPT-4',
|
|
||||||
key: 'gpt-4',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'GPT-4 turbo',
|
|
||||||
key: 'gpt-4-turbo',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'GPT-4 omni',
|
|
||||||
key: 'gpt-4o',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'GPT-4o (2024-05-13)',
|
|
||||||
key: 'gpt-4o-2024-05-13',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'GPT-4 omni mini',
|
|
||||||
key: 'gpt-4o-mini',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'GPT 4.1 nano',
|
|
||||||
key: 'gpt-4.1-nano',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'GPT 4.1 mini',
|
|
||||||
key: 'gpt-4.1-mini',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'GPT 4.1',
|
|
||||||
key: 'gpt-4.1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'GPT 5 nano',
|
|
||||||
key: 'gpt-5-nano',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'GPT 5',
|
|
||||||
key: 'gpt-5',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'GPT 5 Mini',
|
|
||||||
key: 'gpt-5-mini',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'o1',
|
|
||||||
key: 'o1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'o3',
|
|
||||||
key: 'o3',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'o3 Mini',
|
|
||||||
key: 'o3-mini',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'o4 Mini',
|
|
||||||
key: 'o4-mini',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const defaultEmbeddingModels: Model[] = [
|
|
||||||
{
|
|
||||||
name: 'Text Embedding 3 Small',
|
|
||||||
key: 'text-embedding-3-small',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Text Embedding 3 Large',
|
|
||||||
key: 'text-embedding-3-large',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const providerConfigFields: UIConfigField[] = [
|
|
||||||
/* {
|
|
||||||
type: 'string',
|
|
||||||
name: 'Name (Optional)',
|
|
||||||
key: 'name',
|
|
||||||
description: 'An optional name for this provider configuration',
|
|
||||||
required: false,
|
|
||||||
placeholder: 'Provider Name',
|
|
||||||
scope: 'server',
|
|
||||||
}, */ /* FOR NAME DIRECTLY CREATE INPUT IN FRONTEND */
|
|
||||||
{
|
|
||||||
type: 'password',
|
|
||||||
name: 'API Key',
|
|
||||||
key: 'apiKey',
|
|
||||||
description: 'Your OpenAI API key',
|
|
||||||
required: true,
|
|
||||||
placeholder: 'OpenAI API Key',
|
|
||||||
env: 'OPENAI_API_KEY',
|
|
||||||
scope: 'server',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'string',
|
|
||||||
name: 'Base URL',
|
|
||||||
key: 'baseURL',
|
|
||||||
description: 'The base URL for the OpenAI API',
|
|
||||||
required: true,
|
|
||||||
placeholder: 'OpenAI Base URL',
|
|
||||||
default: 'https://api.openai.com/v1',
|
|
||||||
env: 'OPENAI_BASE_URL',
|
|
||||||
scope: 'server',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
class OpenAIProvider extends BaseModelProvider<OpenAIConfig> {
|
|
||||||
constructor(config: OpenAIConfig) {
|
|
||||||
super(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getDefaultModels(): Promise<ModelList> {
|
|
||||||
if (this.config.baseURL === 'https://api.openai.com/v1') {
|
|
||||||
return {
|
|
||||||
embedding: defaultEmbeddingModels,
|
|
||||||
chat: defaultChatModels,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
embedding: [],
|
|
||||||
chat: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async getModelList(): Promise<ModelList> {
|
|
||||||
/* Todo: IMPLEMENT MODEL READING FROM CONFIG FILE */
|
|
||||||
const defaultModels = await this.getDefaultModels();
|
|
||||||
|
|
||||||
return {
|
|
||||||
embedding: [...defaultModels.embedding],
|
|
||||||
chat: [...defaultModels.chat],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadChatModel(key: string): Promise<BaseChatModel> {
|
|
||||||
const modelList = await this.getModelList();
|
|
||||||
|
|
||||||
const exists = modelList.chat.filter((m) => m.key === key);
|
|
||||||
|
|
||||||
if (!exists) {
|
|
||||||
throw new Error(
|
|
||||||
'Error Loading OpenAI Chat Model. Invalid Model Selected',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ChatOpenAI({
|
|
||||||
apiKey: this.config.apiKey,
|
|
||||||
temperature: 0.7,
|
|
||||||
model: key,
|
|
||||||
configuration: {
|
|
||||||
baseURL: this.config.baseURL,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadEmbeddingModel(key: string): Promise<Embeddings> {
|
|
||||||
const modelList = await this.getModelList();
|
|
||||||
|
|
||||||
const exists = modelList.chat.filter((m) => m.key === key);
|
|
||||||
|
|
||||||
if (!exists) {
|
|
||||||
throw new Error(
|
|
||||||
'Error Loading OpenAI Embedding Model. Invalid Model Selected.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new OpenAIEmbeddings({
|
|
||||||
apiKey: this.config.apiKey,
|
|
||||||
model: key,
|
|
||||||
configuration: {
|
|
||||||
baseURL: this.config.baseURL,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static getProviderConfigFields(): UIConfigField[] {
|
|
||||||
return providerConfigFields;
|
|
||||||
}
|
|
||||||
|
|
||||||
static getProviderMetadata(): ProviderMetadata {
|
|
||||||
return {
|
|
||||||
key: 'openai',
|
|
||||||
name: 'OpenAI',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default OpenAIProvider;
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import { ModelProviderUISection, UIConfigField } from '../config/types';
|
|
||||||
import { ProviderMetadata } from './types';
|
|
||||||
import BaseModelProvider from './providers/baseProvider';
|
|
||||||
import OpenAIProvider from './providers/openai';
|
|
||||||
|
|
||||||
interface ProviderClass<T> {
|
|
||||||
new (config: T): BaseModelProvider<T>;
|
|
||||||
getProviderConfigFields(): UIConfigField[];
|
|
||||||
getProviderMetadata(): ProviderMetadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
const providers: Record<string, ProviderClass<any>> = {
|
|
||||||
openai: OpenAIProvider,
|
|
||||||
};
|
|
||||||
|
|
||||||
class ModelRegistry {
|
|
||||||
constructor() {}
|
|
||||||
|
|
||||||
getUIConfigSection(): ModelProviderUISection[] {
|
|
||||||
return Object.entries(providers).map(([k, p]) => {
|
|
||||||
const configFields = p.getProviderConfigFields();
|
|
||||||
const metadata = p.getProviderMetadata();
|
|
||||||
|
|
||||||
return {
|
|
||||||
fields: configFields,
|
|
||||||
key: k,
|
|
||||||
name: metadata.name,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ModelRegistry;
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
type Model = {
|
|
||||||
name: string;
|
|
||||||
key: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type ModelList = {
|
|
||||||
embedding: Model[];
|
|
||||||
chat: Model[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type ProviderMetadata = {
|
|
||||||
name: string;
|
|
||||||
key: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type { Model, ModelList, ProviderMetadata };
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import crypto from 'crypto';
|
|
||||||
|
|
||||||
export const hashObj = (obj: { [key: string]: any }) => {
|
|
||||||
const json = JSON.stringify(obj, Object.keys(obj).sort());
|
|
||||||
const hash = crypto.createHash('sha256').update(json).digest('hex');
|
|
||||||
return hash;
|
|
||||||
};
|
|
||||||
@@ -2,17 +2,17 @@ import type { Config } from 'tailwindcss';
|
|||||||
import type { DefaultColors } from 'tailwindcss/types/generated/colors';
|
import type { DefaultColors } from 'tailwindcss/types/generated/colors';
|
||||||
|
|
||||||
const themeDark = (colors: DefaultColors) => ({
|
const themeDark = (colors: DefaultColors) => ({
|
||||||
50: '#0d1117',
|
50: '#111116',
|
||||||
100: '#161b22',
|
100: '#1f202b',
|
||||||
200: '#21262d',
|
200: '#2d2f3f',
|
||||||
300: '#30363d',
|
300: '#3a3c4c',
|
||||||
});
|
});
|
||||||
|
|
||||||
const themeLight = (colors: DefaultColors) => ({
|
const themeLight = (colors: DefaultColors) => ({
|
||||||
50: '#ffffff',
|
50: '#ffffff',
|
||||||
100: '#f6f8fa',
|
100: '#f1f5f9',
|
||||||
200: '#d0d7de',
|
200: '#c4c7c5',
|
||||||
300: '#afb8c1',
|
300: '#9ca3af',
|
||||||
});
|
});
|
||||||
|
|
||||||
const config: Config = {
|
const config: Config = {
|
||||||
@@ -49,6 +49,6 @@ const config: Config = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [require('@tailwindcss/typography'), require('@headlessui/tailwindcss')({ prefix: 'headless' })],
|
plugins: [require('@tailwindcss/typography')],
|
||||||
};
|
};
|
||||||
export default config;
|
export default config;
|
||||||
|
|||||||
39
yarn.lock
39
yarn.lock
@@ -407,11 +407,6 @@
|
|||||||
"@react-aria/interactions" "^3.21.3"
|
"@react-aria/interactions" "^3.21.3"
|
||||||
"@tanstack/react-virtual" "^3.8.1"
|
"@tanstack/react-virtual" "^3.8.1"
|
||||||
|
|
||||||
"@headlessui/tailwindcss@^0.2.2":
|
|
||||||
version "0.2.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/@headlessui/tailwindcss/-/tailwindcss-0.2.2.tgz#8ebde73fabca72d48636ea56ae790209dc5f0d49"
|
|
||||||
integrity sha512-xNe42KjdyA4kfUKLLPGzME9zkH7Q3rOZ5huFihWNWOQFxnItxPB3/67yBI8/qBfY8nwBRx5GHn4VprsoluVMGw==
|
|
||||||
|
|
||||||
"@huggingface/jinja@^0.2.2":
|
"@huggingface/jinja@^0.2.2":
|
||||||
version "0.2.2"
|
version "0.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@huggingface/jinja/-/jinja-0.2.2.tgz#faeb205a9d6995089bef52655ddd8245d3190627"
|
resolved "https://registry.yarnpkg.com/@huggingface/jinja/-/jinja-0.2.2.tgz#faeb205a9d6995089bef52655ddd8245d3190627"
|
||||||
@@ -666,6 +661,33 @@
|
|||||||
groq-sdk "^0.19.0"
|
groq-sdk "^0.19.0"
|
||||||
zod "^3.22.4"
|
zod "^3.22.4"
|
||||||
|
|
||||||
|
"@langchain/langgraph-checkpoint@^0.1.1":
|
||||||
|
version "0.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-0.1.1.tgz#500569a02af4b85172d775de63eeba06afa0c189"
|
||||||
|
integrity sha512-h2bP0RUikQZu0Um1ZUPErQLXyhzroJqKRbRcxYRTAh49oNlsfeq4A3K4YEDRbGGuyPZI/Jiqwhks1wZwY73AZw==
|
||||||
|
dependencies:
|
||||||
|
uuid "^10.0.0"
|
||||||
|
|
||||||
|
"@langchain/langgraph-sdk@~0.1.0":
|
||||||
|
version "0.1.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/@langchain/langgraph-sdk/-/langgraph-sdk-0.1.9.tgz#5442bd1a4257b5d94927af6e09b0aed341ae8a1d"
|
||||||
|
integrity sha512-7WEDHtbI3pYPUiiHq+dPaF92ZN2W7lqObdpK0X+roa8zPdHUjve/HiqYuKNWS12u1N+L5QIuQWqZvVNvUA7BfQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/json-schema" "^7.0.15"
|
||||||
|
p-queue "^6.6.2"
|
||||||
|
p-retry "4"
|
||||||
|
uuid "^9.0.0"
|
||||||
|
|
||||||
|
"@langchain/langgraph@^0.4.9":
|
||||||
|
version "0.4.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/@langchain/langgraph/-/langgraph-0.4.9.tgz#470a238ea98662d6ec9dfc42859a00acad00fc81"
|
||||||
|
integrity sha512-+rcdTGi4Ium4X/VtIX3Zw4RhxEkYWpwUyz806V6rffjHOAMamg6/WZDxpJbrP33RV/wJG1GH12Z29oX3Pqq3Aw==
|
||||||
|
dependencies:
|
||||||
|
"@langchain/langgraph-checkpoint" "^0.1.1"
|
||||||
|
"@langchain/langgraph-sdk" "~0.1.0"
|
||||||
|
uuid "^10.0.0"
|
||||||
|
zod "^3.25.32"
|
||||||
|
|
||||||
"@langchain/ollama@^0.2.3":
|
"@langchain/ollama@^0.2.3":
|
||||||
version "0.2.3"
|
version "0.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/@langchain/ollama/-/ollama-0.2.3.tgz#4868e66db4fc480f08c42fc652274abbab0416f0"
|
resolved "https://registry.yarnpkg.com/@langchain/ollama/-/ollama-0.2.3.tgz#4868e66db4fc480f08c42fc652274abbab0416f0"
|
||||||
@@ -944,6 +966,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/html-to-text/-/html-to-text-9.0.4.tgz#4a83dd8ae8bfa91457d0b1ffc26f4d0537eff58c"
|
resolved "https://registry.yarnpkg.com/@types/html-to-text/-/html-to-text-9.0.4.tgz#4a83dd8ae8bfa91457d0b1ffc26f4d0537eff58c"
|
||||||
integrity sha512-pUY3cKH/Nm2yYrEmDlPR1mR7yszjGx4DrwPjQ702C4/D5CwHuZTgZdIdwPkRbcuhs7BAh2L5rg3CL5cbRiGTCQ==
|
integrity sha512-pUY3cKH/Nm2yYrEmDlPR1mR7yszjGx4DrwPjQ702C4/D5CwHuZTgZdIdwPkRbcuhs7BAh2L5rg3CL5cbRiGTCQ==
|
||||||
|
|
||||||
|
"@types/json-schema@^7.0.15":
|
||||||
|
version "7.0.15"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
||||||
|
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
|
||||||
|
|
||||||
"@types/json5@^0.0.29":
|
"@types/json5@^0.0.29":
|
||||||
version "0.0.29"
|
version "0.0.29"
|
||||||
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
||||||
@@ -5138,7 +5165,7 @@ uuid@^11.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.1.0.tgz#9549028be1753bb934fc96e2bca09bb4105ae912"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.1.0.tgz#9549028be1753bb934fc96e2bca09bb4105ae912"
|
||||||
integrity sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==
|
integrity sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==
|
||||||
|
|
||||||
uuid@^9.0.1:
|
uuid@^9.0.0, uuid@^9.0.1:
|
||||||
version "9.0.1"
|
version "9.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
|
||||||
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
|
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
|
||||||
|
|||||||
Reference in New Issue
Block a user