mirror of
https://github.com/sortedcord/omnia.git
synced 2026-07-22 12:02:49 +05:30
feat(llm): Added Groq and Deepseek providers
This commit is contained in:
@@ -5,6 +5,8 @@ const LLMConfigSchema = z.object({
|
||||
OPENROUTER_API_KEY: z.string().optional(),
|
||||
ANTHROPIC_API_KEY: z.string().optional(),
|
||||
OPENAI_API_KEY: z.string().optional(),
|
||||
GROQ_API_KEY: z.string().optional(),
|
||||
DEEPSEEK_API_KEY: z.string().optional(),
|
||||
});
|
||||
|
||||
export const llmConfig = LLMConfigSchema.parse(process.env);
|
||||
|
||||
@@ -6,4 +6,6 @@ export * from "./providers/ollama.js";
|
||||
export * from "./providers/openrouter.js";
|
||||
export * from "./providers/anthropic.js";
|
||||
export * from "./providers/openai.js";
|
||||
export * from "./providers/groq.js";
|
||||
export * from "./providers/deepseek.js";
|
||||
export * from "./provider-manager.js";
|
||||
|
||||
@@ -90,6 +90,20 @@ export const AVAILABLE_PROVIDERS: ModelProviderMeta[] = [
|
||||
defaultModel: "claude-3-5-sonnet-latest",
|
||||
defaultEmbeddingModel: "",
|
||||
},
|
||||
{
|
||||
id: "groq",
|
||||
displayName: "Groq",
|
||||
description: "Official Groq integration using @langchain/groq SDK",
|
||||
defaultModel: "llama-3.3-70b-versatile",
|
||||
defaultEmbeddingModel: "",
|
||||
},
|
||||
{
|
||||
id: "deepseek",
|
||||
displayName: "DeepSeek",
|
||||
description: "Official DeepSeek integration using @langchain/deepseek SDK",
|
||||
defaultModel: "deepseek-chat",
|
||||
defaultEmbeddingModel: "",
|
||||
},
|
||||
{
|
||||
id: "openrouter",
|
||||
displayName: "OpenRouter",
|
||||
|
||||
@@ -101,6 +101,8 @@ function getSettingsDb() {
|
||||
const openRouterKey = process.env.OPENROUTER_API_KEY;
|
||||
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
||||
const openaiKey = process.env.OPENAI_API_KEY;
|
||||
const groqKey = process.env.GROQ_API_KEY;
|
||||
const deepseekKey = process.env.DEEPSEEK_API_KEY;
|
||||
let hasInsertedGenerative = false;
|
||||
let hasInsertedEmbedding = false;
|
||||
|
||||
@@ -209,6 +211,52 @@ function getSettingsDb() {
|
||||
}
|
||||
}
|
||||
|
||||
if (groqKey && groqKey.trim()) {
|
||||
const id = "provider-default-groq";
|
||||
const isActive = hasInsertedGenerative ? 0 : 1;
|
||||
db.prepare(
|
||||
`
|
||||
INSERT INTO provider_instances (id, name, providerName, apiKey, isActive, modelName, type, maxContext)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`,
|
||||
).run(
|
||||
id,
|
||||
"Groq (Env)",
|
||||
"groq",
|
||||
groqKey.trim(),
|
||||
isActive,
|
||||
"llama-3.3-70b-versatile",
|
||||
"generative",
|
||||
8192,
|
||||
);
|
||||
if (isActive === 1) {
|
||||
hasInsertedGenerative = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (deepseekKey && deepseekKey.trim()) {
|
||||
const id = "provider-default-deepseek";
|
||||
const isActive = hasInsertedGenerative ? 0 : 1;
|
||||
db.prepare(
|
||||
`
|
||||
INSERT INTO provider_instances (id, name, providerName, apiKey, isActive, modelName, type, maxContext)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`,
|
||||
).run(
|
||||
id,
|
||||
"DeepSeek (Env)",
|
||||
"deepseek",
|
||||
deepseekKey.trim(),
|
||||
isActive,
|
||||
"deepseek-chat",
|
||||
"generative",
|
||||
64000,
|
||||
);
|
||||
if (isActive === 1) {
|
||||
hasInsertedGenerative = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (openRouterKey && openRouterKey.trim()) {
|
||||
const id = "provider-default-openrouter";
|
||||
const isActive = hasInsertedGenerative ? 0 : 1;
|
||||
@@ -464,6 +512,8 @@ export class ProviderManager {
|
||||
const openRouterKey = process.env.OPENROUTER_API_KEY;
|
||||
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
||||
const openaiKey = process.env.OPENAI_API_KEY;
|
||||
const groqKey = process.env.GROQ_API_KEY;
|
||||
const deepseekKey = process.env.DEEPSEEK_API_KEY;
|
||||
let hasInsertedGenerative = false;
|
||||
let hasInsertedEmbedding = false;
|
||||
|
||||
@@ -572,6 +622,52 @@ export class ProviderManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (groqKey && groqKey.trim()) {
|
||||
const id = "provider-default-groq";
|
||||
const isActive = hasInsertedGenerative ? 0 : 1;
|
||||
db.prepare(
|
||||
`
|
||||
INSERT INTO provider_instances (id, name, providerName, apiKey, isActive, modelName, type, maxContext)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`,
|
||||
).run(
|
||||
id,
|
||||
"Groq (Env)",
|
||||
"groq",
|
||||
groqKey.trim(),
|
||||
isActive,
|
||||
"llama-3.3-70b-versatile",
|
||||
"generative",
|
||||
8192,
|
||||
);
|
||||
if (isActive === 1) {
|
||||
hasInsertedGenerative = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (deepseekKey && deepseekKey.trim()) {
|
||||
const id = "provider-default-deepseek";
|
||||
const isActive = hasInsertedGenerative ? 0 : 1;
|
||||
db.prepare(
|
||||
`
|
||||
INSERT INTO provider_instances (id, name, providerName, apiKey, isActive, modelName, type, maxContext)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`,
|
||||
).run(
|
||||
id,
|
||||
"DeepSeek (Env)",
|
||||
"deepseek",
|
||||
deepseekKey.trim(),
|
||||
isActive,
|
||||
"deepseek-chat",
|
||||
"generative",
|
||||
64000,
|
||||
);
|
||||
if (isActive === 1) {
|
||||
hasInsertedGenerative = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (openRouterKey && openRouterKey.trim()) {
|
||||
const id = "provider-default-openrouter";
|
||||
const isActive = hasInsertedGenerative ? 0 : 1;
|
||||
@@ -757,6 +853,32 @@ export class ProviderManager {
|
||||
maxContext: 200000,
|
||||
};
|
||||
}
|
||||
const groqKey = process.env.GROQ_API_KEY;
|
||||
if (groqKey && groqKey.trim()) {
|
||||
return {
|
||||
id: "provider-default-env-fallback",
|
||||
name: "Groq (Env Fallback)",
|
||||
providerName: "groq",
|
||||
apiKey: groqKey.trim(),
|
||||
isActive: true,
|
||||
modelName: "llama-3.3-70b-versatile",
|
||||
type: "generative",
|
||||
maxContext: 8192,
|
||||
};
|
||||
}
|
||||
const deepseekKey = process.env.DEEPSEEK_API_KEY;
|
||||
if (deepseekKey && deepseekKey.trim()) {
|
||||
return {
|
||||
id: "provider-default-env-fallback",
|
||||
name: "DeepSeek (Env Fallback)",
|
||||
providerName: "deepseek",
|
||||
apiKey: deepseekKey.trim(),
|
||||
isActive: true,
|
||||
modelName: "deepseek-chat",
|
||||
type: "generative",
|
||||
maxContext: 64000,
|
||||
};
|
||||
}
|
||||
const openRouterKey = process.env.OPENROUTER_API_KEY;
|
||||
if (openRouterKey && openRouterKey.trim()) {
|
||||
return {
|
||||
|
||||
114
packages/llm/src/providers/deepseek.ts
Normal file
114
packages/llm/src/providers/deepseek.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { z } from "zod";
|
||||
import { ChatDeepSeek } from "@langchain/deepseek";
|
||||
import {
|
||||
ILLMProvider,
|
||||
LLMRequest,
|
||||
LLMResponse,
|
||||
LLMCallRecord,
|
||||
} from "../llm.js";
|
||||
import { llmConfig } from "../config.js";
|
||||
import { ProviderManager } from "../provider-manager.js";
|
||||
|
||||
export class DeepSeekProvider implements ILLMProvider {
|
||||
static readonly providerId = "deepseek";
|
||||
static readonly displayName = "DeepSeek";
|
||||
static readonly description =
|
||||
"Official DeepSeek integration using @langchain/deepseek SDK";
|
||||
static readonly defaultModel = "deepseek-chat";
|
||||
|
||||
providerName = "DeepSeek";
|
||||
private model: ChatDeepSeek;
|
||||
private modelNameUsed: string;
|
||||
private providerInstanceName?: string;
|
||||
private maxContextUsed?: number;
|
||||
lastCalls: LLMCallRecord[] = [];
|
||||
|
||||
constructor(
|
||||
apiKey?: string,
|
||||
modelName?: string,
|
||||
providerInstanceName?: string,
|
||||
maxContext?: number,
|
||||
) {
|
||||
let key = apiKey;
|
||||
let model = modelName;
|
||||
this.providerInstanceName = providerInstanceName;
|
||||
this.maxContextUsed = maxContext;
|
||||
|
||||
if (!key) {
|
||||
const active = ProviderManager.getActive("generative");
|
||||
if (active && active.providerName === DeepSeekProvider.providerId) {
|
||||
key = active.apiKey;
|
||||
if (!model) {
|
||||
model = active.modelName;
|
||||
}
|
||||
if (!this.providerInstanceName) {
|
||||
this.providerInstanceName = active.name;
|
||||
}
|
||||
if (this.maxContextUsed === undefined) {
|
||||
this.maxContextUsed = active.maxContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
key = llmConfig.DEEPSEEK_API_KEY;
|
||||
if (!this.providerInstanceName && key) {
|
||||
this.providerInstanceName = "Environment Variable";
|
||||
}
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
throw new Error(
|
||||
"DEEPSEEK_API_KEY is required to initialize DeepSeekProvider",
|
||||
);
|
||||
}
|
||||
|
||||
this.modelNameUsed = model || DeepSeekProvider.defaultModel;
|
||||
this.model = new ChatDeepSeek({
|
||||
apiKey: key,
|
||||
model: this.modelNameUsed,
|
||||
});
|
||||
}
|
||||
|
||||
async generateStructuredResponse<T extends z.ZodTypeAny>(
|
||||
request: LLMRequest<T>,
|
||||
): Promise<LLMResponse<z.infer<T>>> {
|
||||
const structuredModel = this.model.withStructuredOutput(request.schema, {
|
||||
includeRaw: true,
|
||||
});
|
||||
const result = (await structuredModel.invoke([
|
||||
{ role: "system", content: request.systemPrompt },
|
||||
{ role: "user", content: request.userContext },
|
||||
])) as unknown as {
|
||||
parsed?: z.infer<T>;
|
||||
raw?: {
|
||||
usage_metadata?: {
|
||||
input_tokens?: number;
|
||||
output_tokens?: number;
|
||||
total_tokens?: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const parsed = result?.parsed;
|
||||
const raw = result?.raw;
|
||||
|
||||
const usage = {
|
||||
inputTokens: raw?.usage_metadata?.input_tokens || 0,
|
||||
outputTokens: raw?.usage_metadata?.output_tokens || 0,
|
||||
totalTokens: raw?.usage_metadata?.total_tokens || 0,
|
||||
modelName: this.modelNameUsed,
|
||||
providerInstanceName: this.providerInstanceName || "Default",
|
||||
maxContext:
|
||||
this.maxContextUsed !== undefined ? this.maxContextUsed : 64000,
|
||||
};
|
||||
|
||||
this.lastCalls.push({
|
||||
systemPrompt: request.systemPrompt,
|
||||
userContext: request.userContext,
|
||||
usage,
|
||||
});
|
||||
|
||||
return { success: true, data: parsed, usage };
|
||||
}
|
||||
}
|
||||
112
packages/llm/src/providers/groq.ts
Normal file
112
packages/llm/src/providers/groq.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { z } from "zod";
|
||||
import { ChatGroq } from "@langchain/groq";
|
||||
import {
|
||||
ILLMProvider,
|
||||
LLMRequest,
|
||||
LLMResponse,
|
||||
LLMCallRecord,
|
||||
} from "../llm.js";
|
||||
import { llmConfig } from "../config.js";
|
||||
import { ProviderManager } from "../provider-manager.js";
|
||||
|
||||
export class GroqProvider implements ILLMProvider {
|
||||
static readonly providerId = "groq";
|
||||
static readonly displayName = "Groq";
|
||||
static readonly description =
|
||||
"Official Groq integration using @langchain/groq SDK";
|
||||
static readonly defaultModel = "llama-3.3-70b-versatile";
|
||||
|
||||
providerName = "Groq";
|
||||
private model: ChatGroq;
|
||||
private modelNameUsed: string;
|
||||
private providerInstanceName?: string;
|
||||
private maxContextUsed?: number;
|
||||
lastCalls: LLMCallRecord[] = [];
|
||||
|
||||
constructor(
|
||||
apiKey?: string,
|
||||
modelName?: string,
|
||||
providerInstanceName?: string,
|
||||
maxContext?: number,
|
||||
) {
|
||||
let key = apiKey;
|
||||
let model = modelName;
|
||||
this.providerInstanceName = providerInstanceName;
|
||||
this.maxContextUsed = maxContext;
|
||||
|
||||
if (!key) {
|
||||
const active = ProviderManager.getActive("generative");
|
||||
if (active && active.providerName === GroqProvider.providerId) {
|
||||
key = active.apiKey;
|
||||
if (!model) {
|
||||
model = active.modelName;
|
||||
}
|
||||
if (!this.providerInstanceName) {
|
||||
this.providerInstanceName = active.name;
|
||||
}
|
||||
if (this.maxContextUsed === undefined) {
|
||||
this.maxContextUsed = active.maxContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
key = llmConfig.GROQ_API_KEY;
|
||||
if (!this.providerInstanceName && key) {
|
||||
this.providerInstanceName = "Environment Variable";
|
||||
}
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
throw new Error("GROQ_API_KEY is required to initialize GroqProvider");
|
||||
}
|
||||
|
||||
this.modelNameUsed = model || GroqProvider.defaultModel;
|
||||
this.model = new ChatGroq({
|
||||
apiKey: key,
|
||||
model: this.modelNameUsed,
|
||||
});
|
||||
}
|
||||
|
||||
async generateStructuredResponse<T extends z.ZodTypeAny>(
|
||||
request: LLMRequest<T>,
|
||||
): Promise<LLMResponse<z.infer<T>>> {
|
||||
const structuredModel = this.model.withStructuredOutput(request.schema, {
|
||||
includeRaw: true,
|
||||
});
|
||||
const result = (await structuredModel.invoke([
|
||||
{ role: "system", content: request.systemPrompt },
|
||||
{ role: "user", content: request.userContext },
|
||||
])) as unknown as {
|
||||
parsed?: z.infer<T>;
|
||||
raw?: {
|
||||
usage_metadata?: {
|
||||
input_tokens?: number;
|
||||
output_tokens?: number;
|
||||
total_tokens?: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const parsed = result?.parsed;
|
||||
const raw = result?.raw;
|
||||
|
||||
const usage = {
|
||||
inputTokens: raw?.usage_metadata?.input_tokens || 0,
|
||||
outputTokens: raw?.usage_metadata?.output_tokens || 0,
|
||||
totalTokens: raw?.usage_metadata?.total_tokens || 0,
|
||||
modelName: this.modelNameUsed,
|
||||
providerInstanceName: this.providerInstanceName || "Default",
|
||||
maxContext:
|
||||
this.maxContextUsed !== undefined ? this.maxContextUsed : 8192,
|
||||
};
|
||||
|
||||
this.lastCalls.push({
|
||||
systemPrompt: request.systemPrompt,
|
||||
userContext: request.userContext,
|
||||
usage,
|
||||
});
|
||||
|
||||
return { success: true, data: parsed, usage };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user