From 597d4f1711cdad8bfa662574ea2023abbba59f2f Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Sat, 11 Jul 2026 14:31:18 +0530 Subject: [PATCH] major(llm): Support Embedding Providers --- apps/gui/next-env.d.ts | 2 +- apps/gui/src/app/config/page.tsx | 101 ++++++++++++--- apps/gui/src/app/play/actions.ts | 18 ++- apps/gui/src/lib/simulation.ts | 118 +++++++++++++++-- packages/llm/src/llm.ts | 17 ++- packages/llm/src/provider-manager.ts | 143 ++++++++++++++------- packages/llm/src/providers/google-genai.ts | 46 ++++++- packages/llm/src/providers/mock.ts | 20 ++- packages/llm/src/providers/openrouter.ts | 2 +- packages/llm/tests/mock.test.ts | 20 ++- 10 files changed, 398 insertions(+), 89 deletions(-) diff --git a/apps/gui/next-env.d.ts b/apps/gui/next-env.d.ts index 9edff1c..c4b7818 100644 --- a/apps/gui/next-env.d.ts +++ b/apps/gui/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/gui/src/app/config/page.tsx b/apps/gui/src/app/config/page.tsx index c27aa6e..af9b2ee 100644 --- a/apps/gui/src/app/config/page.tsx +++ b/apps/gui/src/app/config/page.tsx @@ -11,8 +11,9 @@ import { setProviderMapping, updateProviderInstance, getAvailableProviders, + regenerateEmbeddings, } from "@/app/play/actions"; -import type { LLMProviderInstance, LLMProviderMeta } from "@omnia/llm"; +import type { ModelProviderInstance, ModelProviderMeta } from "@omnia/llm"; interface ConfigStatus { apiKeySet: boolean; @@ -23,9 +24,9 @@ interface ConfigStatus { export default function ConfigPage() { const [config, setConfig] = useState(null); - const [instances, setInstances] = useState([]); + const [instances, setInstances] = useState([]); const [mappings, setMappings] = useState>({}); - const [availableProviders, setAvailableProviders] = useState([]); + const [availableProviders, setAvailableProviders] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); @@ -35,6 +36,7 @@ export default function ConfigPage() { const [editKey, setEditKey] = useState(""); const [editModel, setEditModel] = useState("gemini-2.5-flash"); const [editIsActive, setEditIsActive] = useState(false); + const [editType, setEditType] = useState<"generative" | "embedding">("generative"); useEffect(() => { if (selectedInstanceId === "new") { @@ -42,6 +44,7 @@ export default function ConfigPage() { const defaultProvider = "google-genai"; setEditProvider(defaultProvider); setEditKey(""); + setEditType("generative"); const pMeta = availableProviders.find((p) => p.id === defaultProvider); setEditModel(pMeta?.defaultModel || "gemini-2.5-flash"); setEditIsActive(false); @@ -51,8 +54,9 @@ export default function ConfigPage() { setEditName(inst.name); setEditProvider(inst.providerName); setEditKey(""); + setEditType(inst.type || "generative"); const pMeta = availableProviders.find((p) => p.id === inst.providerName); - setEditModel(inst.modelName || pMeta?.defaultModel || "gemini-2.5-flash"); + setEditModel(inst.modelName || (inst.type === "embedding" ? pMeta?.defaultEmbeddingModel : pMeta?.defaultModel) || "gemini-2.5-flash"); setEditIsActive(inst.isActive); } } @@ -62,7 +66,15 @@ export default function ConfigPage() { setEditProvider(providerId); const pMeta = availableProviders.find((p) => p.id === providerId); if (pMeta) { - setEditModel(pMeta.defaultModel); + setEditModel(editType === "embedding" ? pMeta.defaultEmbeddingModel : pMeta.defaultModel); + } + }; + + const handleTypeChange = (type: "generative" | "embedding") => { + setEditType(type); + const pMeta = availableProviders.find((p) => p.id === editProvider); + if (pMeta) { + setEditModel(type === "embedding" ? pMeta.defaultEmbeddingModel : pMeta.defaultModel); } }; @@ -116,19 +128,42 @@ export default function ConfigPage() { setLoading(true); setError(""); + let shouldRegenerate = false; + let targetInstanceId = selectedInstanceId; + if (selectedInstanceId === "new") { if (!editKey.trim()) { setError("API Key is required for new instances."); setLoading(false); return; } - const created = await createProviderInstance(editName, editProvider, editKey, editModel || undefined); + const created = await createProviderInstance(editName, editProvider, editKey, editModel || undefined, editType); if (editIsActive) { await setActiveProviderInstance(created.id); } + targetInstanceId = created.id; setSelectedInstanceId(created.id); } else { - await updateProviderInstance(selectedInstanceId, editName, editProvider, editKey || undefined, editModel || undefined); + const inst = instances.find((i) => i.id === selectedInstanceId); + if (inst && inst.type === "embedding") { + const isMapped = mappings["embeddings"] === selectedInstanceId; + const isActive = inst.isActive && !mappings["embeddings"]; + if (isMapped || isActive) { + const hasChanged = inst.providerName !== editProvider || inst.modelName !== editModel; + if (hasChanged) { + const confirmChange = window.confirm( + "You have changed the configuration of the active embedding provider. This will delete all existing embeddings and regenerate them from scratch. Are you sure you want to do this?" + ); + if (!confirmChange) { + setLoading(false); + return; + } + shouldRegenerate = true; + } + } + } + + await updateProviderInstance(selectedInstanceId, editName, editProvider, editKey || undefined, editModel || undefined, editType); if (editIsActive) { await setActiveProviderInstance(selectedInstanceId); } @@ -136,6 +171,10 @@ export default function ConfigPage() { await loadInstances(); await loadMappings(); + + if (shouldRegenerate && targetInstanceId !== "new") { + await regenerateEmbeddings(targetInstanceId); + } } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { @@ -162,9 +201,19 @@ export default function ConfigPage() { }; const handleUpdateMapping = async (task: string, providerInstanceId: string) => { + if (task === "embeddings" && mappings[task] !== providerInstanceId) { + const confirmChange = window.confirm( + "Changing the embeddings provider will delete all existing embeddings and regenerate them from scratch. Are you sure you want to do this?" + ); + if (!confirmChange) return; + } + try { setLoading(true); await setProviderMapping(task, providerInstanceId); + if (task === "embeddings") { + await regenerateEmbeddings(providerInstanceId); + } await loadMappings(); } catch (err) { setError(err instanceof Error ? err.message : String(err)); @@ -219,7 +268,7 @@ export default function ConfigPage() { >
{inst.name}
- {inst.providerName} + {inst.providerName} ({inst.type || "generative"}) {inst.isActive && ( Active @@ -257,6 +306,21 @@ export default function ConfigPage() { />
+
+ + +
+