Patch bad models endpoint path in LM Studio embedding engine ()

* patch bad models endpoint path in lm studio embedding engine

* convert to OpenAI wrapper compatibility

* add URL force parser/validation for LMStudio connections

* remove comment

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>
This commit is contained in:
Sean Hatfield 2024-11-13 12:34:42 -08:00 committed by GitHub
parent b701660f88
commit 27b07d46b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 51 additions and 23 deletions
server/utils
AiProviders/lmStudio
EmbeddingEngines/lmstudio
agents/aibitat/providers
helpers

View file

@ -11,7 +11,7 @@ class LMStudioLLM {
const { OpenAI: OpenAIApi } = require("openai"); const { OpenAI: OpenAIApi } = require("openai");
this.lmstudio = new OpenAIApi({ this.lmstudio = new OpenAIApi({
baseURL: process.env.LMSTUDIO_BASE_PATH?.replace(/\/+$/, ""), // here is the URL to your LMStudio instance baseURL: parseLMStudioBasePath(process.env.LMSTUDIO_BASE_PATH), // here is the URL to your LMStudio instance
apiKey: null, apiKey: null,
}); });
@ -173,6 +173,23 @@ class LMStudioLLM {
} }
} }
/**
* Parse the base path for the LMStudio API. Since the base path must end in /v1 and cannot have a trailing slash,
* and the user can possibly set it to anything and likely incorrectly due to pasting behaviors, we need to ensure it is in the correct format.
* @param {string} basePath
* @returns {string}
*/
function parseLMStudioBasePath(providedBasePath = "") {
try {
const baseURL = new URL(providedBasePath);
const basePath = `${baseURL.origin}/v1`;
return basePath;
} catch (e) {
return providedBasePath;
}
}
module.exports = { module.exports = {
LMStudioLLM, LMStudioLLM,
parseLMStudioBasePath,
}; };

View file

@ -1,3 +1,4 @@
const { parseLMStudioBasePath } = require("../../AiProviders/lmStudio");
const { maximumChunkLength } = require("../../helpers"); const { maximumChunkLength } = require("../../helpers");
class LMStudioEmbedder { class LMStudioEmbedder {
@ -6,10 +7,14 @@ class LMStudioEmbedder {
throw new Error("No embedding base path was set."); throw new Error("No embedding base path was set.");
if (!process.env.EMBEDDING_MODEL_PREF) if (!process.env.EMBEDDING_MODEL_PREF)
throw new Error("No embedding model was set."); throw new Error("No embedding model was set.");
this.basePath = `${process.env.EMBEDDING_BASE_PATH}/embeddings`;
const { OpenAI: OpenAIApi } = require("openai");
this.lmstudio = new OpenAIApi({
baseURL: parseLMStudioBasePath(process.env.EMBEDDING_BASE_PATH),
apiKey: null,
});
this.model = process.env.EMBEDDING_MODEL_PREF; this.model = process.env.EMBEDDING_MODEL_PREF;
// Limit of how many strings we can process in a single pass to stay with resource or network limits
// Limit of how many strings we can process in a single pass to stay with resource or network limits // Limit of how many strings we can process in a single pass to stay with resource or network limits
this.maxConcurrentChunks = 1; this.maxConcurrentChunks = 1;
this.embeddingMaxChunkLength = maximumChunkLength(); this.embeddingMaxChunkLength = maximumChunkLength();
@ -20,10 +25,9 @@ class LMStudioEmbedder {
} }
async #isAlive() { async #isAlive() {
return await fetch(`${this.basePath}/models`, { return await this.lmstudio.models
method: "HEAD", .list()
}) .then((res) => res?.data?.length > 0)
.then((res) => res.ok)
.catch((e) => { .catch((e) => {
this.log(e.message); this.log(e.message);
return false; return false;
@ -55,29 +59,29 @@ class LMStudioEmbedder {
for (const chunk of textChunks) { for (const chunk of textChunks) {
if (hasError) break; // If an error occurred don't continue and exit early. if (hasError) break; // If an error occurred don't continue and exit early.
results.push( results.push(
await fetch(this.basePath, { await this.lmstudio.embeddings
method: "POST", .create({
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
model: this.model, model: this.model,
input: chunk, input: chunk,
}), })
}) .then((result) => {
.then((res) => res.json()) const embedding = result.data?.[0]?.embedding;
.then((json) => {
const embedding = json.data[0].embedding;
if (!Array.isArray(embedding) || !embedding.length) if (!Array.isArray(embedding) || !embedding.length)
throw { throw {
type: "EMPTY_ARR", type: "EMPTY_ARR",
message: "The embedding was empty from LMStudio", message: "The embedding was empty from LMStudio",
}; };
console.log(`Embedding length: ${embedding.length}`);
return { data: embedding, error: null }; return { data: embedding, error: null };
}) })
.catch((error) => { .catch((e) => {
e.type =
e?.response?.data?.error?.code ||
e?.response?.status ||
"failed_to_embed";
e.message = e?.response?.data?.error?.message || e.message;
hasError = true; hasError = true;
return { data: [], error }; return { data: [], error: e };
}) })
); );
} }

View file

@ -16,6 +16,7 @@ const { ChatBedrockConverse } = require("@langchain/aws");
const { ChatOllama } = require("@langchain/community/chat_models/ollama"); const { ChatOllama } = require("@langchain/community/chat_models/ollama");
const { toValidNumber } = require("../../../http"); const { toValidNumber } = require("../../../http");
const { getLLMProviderClass } = require("../../../helpers"); const { getLLMProviderClass } = require("../../../helpers");
const { parseLMStudioBasePath } = require("../../../AiProviders/lmStudio");
const DEFAULT_WORKSPACE_PROMPT = const DEFAULT_WORKSPACE_PROMPT =
"You are a helpful ai assistant who can assist the user and use tools available to help answer the users prompts and questions."; "You are a helpful ai assistant who can assist the user and use tools available to help answer the users prompts and questions.";
@ -169,7 +170,7 @@ class Provider {
case "lmstudio": case "lmstudio":
return new ChatOpenAI({ return new ChatOpenAI({
configuration: { configuration: {
baseURL: process.env.LMSTUDIO_BASE_PATH?.replace(/\/+$/, ""), baseURL: parseLMStudioBasePath(process.env.LMSTUDIO_BASE_PATH),
}, },
apiKey: "not-used", // Needs to be specified or else will assume OpenAI apiKey: "not-used", // Needs to be specified or else will assume OpenAI
...config, ...config,

View file

@ -2,6 +2,9 @@ const OpenAI = require("openai");
const Provider = require("./ai-provider.js"); const Provider = require("./ai-provider.js");
const InheritMultiple = require("./helpers/classes.js"); const InheritMultiple = require("./helpers/classes.js");
const UnTooled = require("./helpers/untooled.js"); const UnTooled = require("./helpers/untooled.js");
const {
parseLMStudioBasePath,
} = require("../../../AiProviders/lmStudio/index.js");
/** /**
* The agent provider for the LMStudio. * The agent provider for the LMStudio.
@ -18,7 +21,7 @@ class LMStudioProvider extends InheritMultiple([Provider, UnTooled]) {
const model = const model =
config?.model || process.env.LMSTUDIO_MODEL_PREF || "Loaded from Chat UI"; config?.model || process.env.LMSTUDIO_MODEL_PREF || "Loaded from Chat UI";
const client = new OpenAI({ const client = new OpenAI({
baseURL: process.env.LMSTUDIO_BASE_PATH?.replace(/\/+$/, ""), // here is the URL to your LMStudio instance baseURL: parseLMStudioBasePath(process.env.LMSTUDIO_BASE_PATH),
apiKey: null, apiKey: null,
maxRetries: 3, maxRetries: 3,
}); });

View file

@ -5,6 +5,7 @@ const { togetherAiModels } = require("../AiProviders/togetherAi");
const { fireworksAiModels } = require("../AiProviders/fireworksAi"); const { fireworksAiModels } = require("../AiProviders/fireworksAi");
const { ElevenLabsTTS } = require("../TextToSpeech/elevenLabs"); const { ElevenLabsTTS } = require("../TextToSpeech/elevenLabs");
const { fetchNovitaModels } = require("../AiProviders/novita"); const { fetchNovitaModels } = require("../AiProviders/novita");
const { parseLMStudioBasePath } = require("../AiProviders/lmStudio");
const SUPPORT_CUSTOM_MODELS = [ const SUPPORT_CUSTOM_MODELS = [
"openai", "openai",
"localai", "localai",
@ -235,7 +236,9 @@ async function getLMStudioModels(basePath = null) {
try { try {
const { OpenAI: OpenAIApi } = require("openai"); const { OpenAI: OpenAIApi } = require("openai");
const openai = new OpenAIApi({ const openai = new OpenAIApi({
baseURL: basePath || process.env.LMSTUDIO_BASE_PATH, baseURL: parseLMStudioBasePath(
basePath || process.env.LMSTUDIO_BASE_PATH
),
apiKey: null, apiKey: null,
}); });
const models = await openai.models const models = await openai.models