mirror of
https://github.com/ItzCrazyKns/Perplexica.git
synced 2025-07-07 09:08:30 +00:00
feat: add AIML API provider
Introduces support for the AI/ML API provider, including configuration options, chat and embedding model loading, and UI integration. Updates documentation and sample config to reflect the new provider.
This commit is contained in:
@ -90,7 +90,9 @@ There are mainly 2 ways of installing Perplexica - With Docker, Without Docker.
|
|||||||
- `OLLAMA`: Your Ollama API URL. You should enter it as `http://host.docker.internal:PORT_NUMBER`. If you installed Ollama on port 11434, use `http://host.docker.internal:11434`. For other ports, adjust accordingly. **You need to fill this if you wish to use Ollama's models instead of OpenAI's**.
|
- `OLLAMA`: Your Ollama API URL. You should enter it as `http://host.docker.internal:PORT_NUMBER`. If you installed Ollama on port 11434, use `http://host.docker.internal:11434`. For other ports, adjust accordingly. **You need to fill this if you wish to use Ollama's models instead of OpenAI's**.
|
||||||
- `GROQ`: Your Groq API key. **You only need to fill this if you wish to use Groq's hosted models**.
|
- `GROQ`: Your Groq API key. **You only need to fill this if you wish to use Groq's hosted models**.
|
||||||
- `ANTHROPIC`: Your Anthropic API key. **You only need to fill this if you wish to use Anthropic models**.
|
- `ANTHROPIC`: Your Anthropic API key. **You only need to fill this if you wish to use Anthropic models**.
|
||||||
- `Gemini`: Your Gemini API key. **You only need to fill this if you wish to use Google's models**.
|
- `Gemini`: Your Gemini API key. **You only need to fill this if you wish to use Google's models**.
|
||||||
|
- `DEEPSEEK`: Your Deepseek API key. **Only needed if you want Deepseek models.**
|
||||||
|
- `AIMLAPI`: Your AI/ML API key. **Only needed if you want to use AI/ML API models and embeddings.**
|
||||||
|
|
||||||
**Note**: You can change these after starting Perplexica from the settings dialog.
|
**Note**: You can change these after starting Perplexica from the settings dialog.
|
||||||
|
|
||||||
|
@ -25,6 +25,9 @@ API_URL = "" # Ollama API URL - http://host.docker.internal:11434
|
|||||||
[MODELS.DEEPSEEK]
|
[MODELS.DEEPSEEK]
|
||||||
API_KEY = ""
|
API_KEY = ""
|
||||||
|
|
||||||
|
[MODELS.AIMLAPI]
|
||||||
|
API_KEY = "" # Required to use AI/ML API chat and embedding models
|
||||||
|
|
||||||
[MODELS.LM_STUDIO]
|
[MODELS.LM_STUDIO]
|
||||||
API_URL = "" # LM Studio API URL - http://host.docker.internal:1234
|
API_URL = "" # LM Studio API URL - http://host.docker.internal:1234
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
getOllamaApiEndpoint,
|
getOllamaApiEndpoint,
|
||||||
getOpenaiApiKey,
|
getOpenaiApiKey,
|
||||||
getDeepseekApiKey,
|
getDeepseekApiKey,
|
||||||
|
getAimlApiKey,
|
||||||
getLMStudioApiEndpoint,
|
getLMStudioApiEndpoint,
|
||||||
updateConfig,
|
updateConfig,
|
||||||
} from '@/lib/config';
|
} from '@/lib/config';
|
||||||
@ -57,6 +58,7 @@ export const GET = async (req: Request) => {
|
|||||||
config['groqApiKey'] = getGroqApiKey();
|
config['groqApiKey'] = getGroqApiKey();
|
||||||
config['geminiApiKey'] = getGeminiApiKey();
|
config['geminiApiKey'] = getGeminiApiKey();
|
||||||
config['deepseekApiKey'] = getDeepseekApiKey();
|
config['deepseekApiKey'] = getDeepseekApiKey();
|
||||||
|
config['aimlApiKey'] = getAimlApiKey();
|
||||||
config['customOpenaiApiUrl'] = getCustomOpenaiApiUrl();
|
config['customOpenaiApiUrl'] = getCustomOpenaiApiUrl();
|
||||||
config['customOpenaiApiKey'] = getCustomOpenaiApiKey();
|
config['customOpenaiApiKey'] = getCustomOpenaiApiKey();
|
||||||
config['customOpenaiModelName'] = getCustomOpenaiModelName();
|
config['customOpenaiModelName'] = getCustomOpenaiModelName();
|
||||||
@ -95,6 +97,9 @@ export const POST = async (req: Request) => {
|
|||||||
DEEPSEEK: {
|
DEEPSEEK: {
|
||||||
API_KEY: config.deepseekApiKey,
|
API_KEY: config.deepseekApiKey,
|
||||||
},
|
},
|
||||||
|
AIMLAPI: {
|
||||||
|
API_KEY: config.aimlApiKey,
|
||||||
|
},
|
||||||
LM_STUDIO: {
|
LM_STUDIO: {
|
||||||
API_URL: config.lmStudioApiUrl,
|
API_URL: config.lmStudioApiUrl,
|
||||||
},
|
},
|
||||||
|
@ -23,6 +23,7 @@ interface SettingsType {
|
|||||||
ollamaApiUrl: string;
|
ollamaApiUrl: string;
|
||||||
lmStudioApiUrl: string;
|
lmStudioApiUrl: string;
|
||||||
deepseekApiKey: string;
|
deepseekApiKey: string;
|
||||||
|
aimlApiKey: string;
|
||||||
customOpenaiApiKey: string;
|
customOpenaiApiKey: string;
|
||||||
customOpenaiApiUrl: string;
|
customOpenaiApiUrl: string;
|
||||||
customOpenaiModelName: string;
|
customOpenaiModelName: string;
|
||||||
@ -862,6 +863,25 @@ const Page = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col space-y-1">
|
||||||
|
<p className="text-black/70 dark:text-white/70 text-sm">
|
||||||
|
AI/ML API Key
|
||||||
|
</p>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="AI/ML API Key"
|
||||||
|
value={config.aimlApiKey}
|
||||||
|
isSaving={savingStates['aimlApiKey']}
|
||||||
|
onChange={(e) => {
|
||||||
|
setConfig((prev) => ({
|
||||||
|
...prev!,
|
||||||
|
aimlApiKey: e.target.value,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
onSave={(value) => saveConfig('aimlApiKey', value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col space-y-1">
|
<div className="flex flex-col space-y-1">
|
||||||
<p className="text-black/70 dark:text-white/70 text-sm">
|
<p className="text-black/70 dark:text-white/70 text-sm">
|
||||||
LM Studio API URL
|
LM Studio API URL
|
||||||
|
@ -35,6 +35,9 @@ interface Config {
|
|||||||
DEEPSEEK: {
|
DEEPSEEK: {
|
||||||
API_KEY: string;
|
API_KEY: string;
|
||||||
};
|
};
|
||||||
|
AIMLAPI: {
|
||||||
|
API_KEY: string;
|
||||||
|
};
|
||||||
LM_STUDIO: {
|
LM_STUDIO: {
|
||||||
API_URL: string;
|
API_URL: string;
|
||||||
};
|
};
|
||||||
@ -85,6 +88,8 @@ export const getOllamaApiEndpoint = () => loadConfig().MODELS.OLLAMA.API_URL;
|
|||||||
|
|
||||||
export const getDeepseekApiKey = () => loadConfig().MODELS.DEEPSEEK.API_KEY;
|
export const getDeepseekApiKey = () => loadConfig().MODELS.DEEPSEEK.API_KEY;
|
||||||
|
|
||||||
|
export const getAimlApiKey = () => loadConfig().MODELS.AIMLAPI.API_KEY;
|
||||||
|
|
||||||
export const getCustomOpenaiApiKey = () =>
|
export const getCustomOpenaiApiKey = () =>
|
||||||
loadConfig().MODELS.CUSTOM_OPENAI.API_KEY;
|
loadConfig().MODELS.CUSTOM_OPENAI.API_KEY;
|
||||||
|
|
||||||
|
94
src/lib/providers/aimlapi.ts
Normal file
94
src/lib/providers/aimlapi.ts
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import { ChatOpenAI, OpenAIEmbeddings } from '@langchain/openai';
|
||||||
|
import { getAimlApiKey } from '../config';
|
||||||
|
import { ChatModel, EmbeddingModel } from '.';
|
||||||
|
import { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
||||||
|
import { Embeddings } from '@langchain/core/embeddings';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export const PROVIDER_INFO = {
|
||||||
|
key: 'aimlapi',
|
||||||
|
displayName: 'AI/ML API',
|
||||||
|
};
|
||||||
|
|
||||||
|
interface AimlApiModel {
|
||||||
|
id: string;
|
||||||
|
name?: string;
|
||||||
|
type?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const API_URL = 'https://api.aimlapi.com';
|
||||||
|
|
||||||
|
export const loadAimlApiChatModels = async () => {
|
||||||
|
const apiKey = getAimlApiKey();
|
||||||
|
|
||||||
|
if (!apiKey) return {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${API_URL}/models`, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${apiKey}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const chatModels: Record<string, ChatModel> = {};
|
||||||
|
|
||||||
|
response.data.data.forEach((model: AimlApiModel) => {
|
||||||
|
if (model.type === 'chat-completion') {
|
||||||
|
chatModels[model.id] = {
|
||||||
|
displayName: model.name || model.id,
|
||||||
|
model: new ChatOpenAI({
|
||||||
|
openAIApiKey: apiKey,
|
||||||
|
modelName: model.id,
|
||||||
|
temperature: 0.7,
|
||||||
|
configuration: {
|
||||||
|
baseURL: API_URL,
|
||||||
|
},
|
||||||
|
}) as unknown as BaseChatModel,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return chatModels;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error loading AI/ML API models: ${err}`);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loadAimlApiEmbeddingModels = async () => {
|
||||||
|
const apiKey = getAimlApiKey();
|
||||||
|
|
||||||
|
if (!apiKey) return {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${API_URL}/models`, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${apiKey}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const embeddingModels: Record<string, EmbeddingModel> = {};
|
||||||
|
|
||||||
|
response.data.data.forEach((model: AimlApiModel) => {
|
||||||
|
if (model.type === 'embedding') {
|
||||||
|
embeddingModels[model.id] = {
|
||||||
|
displayName: model.name || model.id,
|
||||||
|
model: new OpenAIEmbeddings({
|
||||||
|
openAIApiKey: apiKey,
|
||||||
|
modelName: model.id,
|
||||||
|
configuration: {
|
||||||
|
baseURL: API_URL,
|
||||||
|
},
|
||||||
|
}) as unknown as Embeddings,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return embeddingModels;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error loading AI/ML API embeddings models: ${err}`);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
@ -35,6 +35,11 @@ import {
|
|||||||
loadDeepseekChatModels,
|
loadDeepseekChatModels,
|
||||||
PROVIDER_INFO as DeepseekInfo,
|
PROVIDER_INFO as DeepseekInfo,
|
||||||
} from './deepseek';
|
} from './deepseek';
|
||||||
|
import {
|
||||||
|
loadAimlApiChatModels,
|
||||||
|
loadAimlApiEmbeddingModels,
|
||||||
|
PROVIDER_INFO as AimlApiInfo,
|
||||||
|
} from './aimlapi';
|
||||||
import {
|
import {
|
||||||
loadLMStudioChatModels,
|
loadLMStudioChatModels,
|
||||||
loadLMStudioEmbeddingsModels,
|
loadLMStudioEmbeddingsModels,
|
||||||
@ -49,6 +54,7 @@ export const PROVIDER_METADATA = {
|
|||||||
gemini: GeminiInfo,
|
gemini: GeminiInfo,
|
||||||
transformers: TransformersInfo,
|
transformers: TransformersInfo,
|
||||||
deepseek: DeepseekInfo,
|
deepseek: DeepseekInfo,
|
||||||
|
aimlapi: AimlApiInfo,
|
||||||
lmstudio: LMStudioInfo,
|
lmstudio: LMStudioInfo,
|
||||||
custom_openai: {
|
custom_openai: {
|
||||||
key: 'custom_openai',
|
key: 'custom_openai',
|
||||||
@ -76,6 +82,7 @@ export const chatModelProviders: Record<
|
|||||||
anthropic: loadAnthropicChatModels,
|
anthropic: loadAnthropicChatModels,
|
||||||
gemini: loadGeminiChatModels,
|
gemini: loadGeminiChatModels,
|
||||||
deepseek: loadDeepseekChatModels,
|
deepseek: loadDeepseekChatModels,
|
||||||
|
aimlapi: loadAimlApiChatModels,
|
||||||
lmstudio: loadLMStudioChatModels,
|
lmstudio: loadLMStudioChatModels,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -87,6 +94,7 @@ export const embeddingModelProviders: Record<
|
|||||||
ollama: loadOllamaEmbeddingModels,
|
ollama: loadOllamaEmbeddingModels,
|
||||||
gemini: loadGeminiEmbeddingModels,
|
gemini: loadGeminiEmbeddingModels,
|
||||||
transformers: loadTransformersEmbeddingsModels,
|
transformers: loadTransformersEmbeddingsModels,
|
||||||
|
aimlapi: loadAimlApiEmbeddingModels,
|
||||||
lmstudio: loadLMStudioEmbeddingsModels,
|
lmstudio: loadLMStudioEmbeddingsModels,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user