mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 08:20:14 +01:00
🎚️ feat: Reasoning Parameters for Custom Endpoints (#10297)
Some checks are pending
Docker Dev Images Build / build (Dockerfile, librechat-dev, node) (push) Waiting to run
Docker Dev Images Build / build (Dockerfile.multi, librechat-dev-api, api-build) (push) Waiting to run
Sync Locize Translations & Create Translation PR / Sync Translation Keys with Locize (push) Waiting to run
Sync Locize Translations & Create Translation PR / Create Translation PR on Version Published (push) Blocked by required conditions
Some checks are pending
Docker Dev Images Build / build (Dockerfile, librechat-dev, node) (push) Waiting to run
Docker Dev Images Build / build (Dockerfile.multi, librechat-dev-api, api-build) (push) Waiting to run
Sync Locize Translations & Create Translation PR / Sync Translation Keys with Locize (push) Waiting to run
Sync Locize Translations & Create Translation PR / Create Translation PR on Version Published (push) Blocked by required conditions
This commit is contained in:
parent
861ef98d29
commit
e6aeec9f25
7 changed files with 99 additions and 13 deletions
|
|
@ -48,7 +48,7 @@
|
||||||
"@langchain/google-genai": "^0.2.13",
|
"@langchain/google-genai": "^0.2.13",
|
||||||
"@langchain/google-vertexai": "^0.2.13",
|
"@langchain/google-vertexai": "^0.2.13",
|
||||||
"@langchain/textsplitters": "^0.1.0",
|
"@langchain/textsplitters": "^0.1.0",
|
||||||
"@librechat/agents": "^2.4.89",
|
"@librechat/agents": "^2.4.90",
|
||||||
"@librechat/api": "*",
|
"@librechat/api": "*",
|
||||||
"@librechat/data-schemas": "*",
|
"@librechat/data-schemas": "*",
|
||||||
"@microsoft/microsoft-graph-client": "^3.0.7",
|
"@microsoft/microsoft-graph-client": "^3.0.7",
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ const initializeClient = async ({
|
||||||
modelOptions.model = modelName;
|
modelOptions.model = modelName;
|
||||||
clientOptions = Object.assign({ modelOptions }, clientOptions);
|
clientOptions = Object.assign({ modelOptions }, clientOptions);
|
||||||
clientOptions.modelOptions.user = req.user.id;
|
clientOptions.modelOptions.user = req.user.id;
|
||||||
const options = getOpenAIConfig(apiKey, clientOptions);
|
const options = getOpenAIConfig(apiKey, clientOptions, endpoint);
|
||||||
if (options != null && serverless === true) {
|
if (options != null && serverless === true) {
|
||||||
options.useLegacyContent = true;
|
options.useLegacyContent = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
package-lock.json
generated
12
package-lock.json
generated
|
|
@ -64,7 +64,7 @@
|
||||||
"@langchain/google-genai": "^0.2.13",
|
"@langchain/google-genai": "^0.2.13",
|
||||||
"@langchain/google-vertexai": "^0.2.13",
|
"@langchain/google-vertexai": "^0.2.13",
|
||||||
"@langchain/textsplitters": "^0.1.0",
|
"@langchain/textsplitters": "^0.1.0",
|
||||||
"@librechat/agents": "^2.4.89",
|
"@librechat/agents": "^2.4.90",
|
||||||
"@librechat/api": "*",
|
"@librechat/api": "*",
|
||||||
"@librechat/data-schemas": "*",
|
"@librechat/data-schemas": "*",
|
||||||
"@microsoft/microsoft-graph-client": "^3.0.7",
|
"@microsoft/microsoft-graph-client": "^3.0.7",
|
||||||
|
|
@ -21690,9 +21690,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@librechat/agents": {
|
"node_modules/@librechat/agents": {
|
||||||
"version": "2.4.89",
|
"version": "2.4.90",
|
||||||
"resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-2.4.89.tgz",
|
"resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-2.4.90.tgz",
|
||||||
"integrity": "sha512-QMqaNkkfcDHI8mpaqpgdb1Zz3KT3uVLaaU4NCeXafNH6JvGdG4ueORQH2dM8xtVm3+5DEXwauTdSAi5gHV5tJQ==",
|
"integrity": "sha512-CZI0K0NjIO1mvw4f4tAAMN8x4fFXFyZyrP+NLTCHDvdrIpyTY2k9qRwQHneCGnMgltuUQ+53eXbS3s9psjsAOA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@langchain/anthropic": "^0.3.26",
|
"@langchain/anthropic": "^0.3.26",
|
||||||
|
|
@ -51984,7 +51984,7 @@
|
||||||
},
|
},
|
||||||
"packages/api": {
|
"packages/api": {
|
||||||
"name": "@librechat/api",
|
"name": "@librechat/api",
|
||||||
"version": "1.4.1",
|
"version": "1.5.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/preset-env": "^7.21.5",
|
"@babel/preset-env": "^7.21.5",
|
||||||
|
|
@ -52023,7 +52023,7 @@
|
||||||
"@azure/storage-blob": "^12.27.0",
|
"@azure/storage-blob": "^12.27.0",
|
||||||
"@keyv/redis": "^4.3.3",
|
"@keyv/redis": "^4.3.3",
|
||||||
"@langchain/core": "^0.3.62",
|
"@langchain/core": "^0.3.62",
|
||||||
"@librechat/agents": "^2.4.89",
|
"@librechat/agents": "^2.4.90",
|
||||||
"@librechat/data-schemas": "*",
|
"@librechat/data-schemas": "*",
|
||||||
"@modelcontextprotocol/sdk": "^1.17.1",
|
"@modelcontextprotocol/sdk": "^1.17.1",
|
||||||
"axios": "^1.12.1",
|
"axios": "^1.12.1",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@librechat/api",
|
"name": "@librechat/api",
|
||||||
"version": "1.4.1",
|
"version": "1.5.0",
|
||||||
"type": "commonjs",
|
"type": "commonjs",
|
||||||
"description": "MCP services for LibreChat",
|
"description": "MCP services for LibreChat",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|
@ -80,7 +80,7 @@
|
||||||
"@azure/storage-blob": "^12.27.0",
|
"@azure/storage-blob": "^12.27.0",
|
||||||
"@keyv/redis": "^4.3.3",
|
"@keyv/redis": "^4.3.3",
|
||||||
"@langchain/core": "^0.3.62",
|
"@langchain/core": "^0.3.62",
|
||||||
"@librechat/agents": "^2.4.89",
|
"@librechat/agents": "^2.4.90",
|
||||||
"@librechat/data-schemas": "*",
|
"@librechat/data-schemas": "*",
|
||||||
"@modelcontextprotocol/sdk": "^1.17.1",
|
"@modelcontextprotocol/sdk": "^1.17.1",
|
||||||
"axios": "^1.12.1",
|
"axios": "^1.12.1",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
import { Verbosity, ReasoningEffort, ReasoningSummary } from 'librechat-data-provider';
|
import {
|
||||||
|
Verbosity,
|
||||||
|
EModelEndpoint,
|
||||||
|
ReasoningEffort,
|
||||||
|
ReasoningSummary,
|
||||||
|
} from 'librechat-data-provider';
|
||||||
import type { RequestInit } from 'undici';
|
import type { RequestInit } from 'undici';
|
||||||
import type { OpenAIParameters, AzureOptions } from '~/types';
|
import type { OpenAIParameters, AzureOptions } from '~/types';
|
||||||
import { getOpenAIConfig } from './config';
|
import { getOpenAIConfig } from './config';
|
||||||
|
|
@ -103,12 +108,89 @@ describe('getOpenAIConfig', () => {
|
||||||
|
|
||||||
const result = getOpenAIConfig(mockApiKey, { modelOptions });
|
const result = getOpenAIConfig(mockApiKey, { modelOptions });
|
||||||
|
|
||||||
|
/** When no endpoint is specified, it's treated as non-openAI/azureOpenAI, so uses reasoning object */
|
||||||
|
expect(result.llmConfig.reasoning).toEqual({
|
||||||
|
effort: ReasoningEffort.high,
|
||||||
|
summary: ReasoningSummary.detailed,
|
||||||
|
});
|
||||||
|
expect((result.llmConfig as Record<string, unknown>).reasoning_effort).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use reasoning_effort for openAI endpoint without useResponsesApi', () => {
|
||||||
|
const modelOptions = {
|
||||||
|
reasoning_effort: ReasoningEffort.high,
|
||||||
|
reasoning_summary: ReasoningSummary.detailed,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = getOpenAIConfig(mockApiKey, { modelOptions }, EModelEndpoint.openAI);
|
||||||
|
|
||||||
expect((result.llmConfig as Record<string, unknown>).reasoning_effort).toBe(
|
expect((result.llmConfig as Record<string, unknown>).reasoning_effort).toBe(
|
||||||
ReasoningEffort.high,
|
ReasoningEffort.high,
|
||||||
);
|
);
|
||||||
expect(result.llmConfig.reasoning).toBeUndefined();
|
expect(result.llmConfig.reasoning).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should use reasoning_effort for azureOpenAI endpoint without useResponsesApi', () => {
|
||||||
|
const modelOptions = {
|
||||||
|
reasoning_effort: ReasoningEffort.high,
|
||||||
|
reasoning_summary: ReasoningSummary.detailed,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = getOpenAIConfig(mockApiKey, { modelOptions }, EModelEndpoint.azureOpenAI);
|
||||||
|
|
||||||
|
expect((result.llmConfig as Record<string, unknown>).reasoning_effort).toBe(
|
||||||
|
ReasoningEffort.high,
|
||||||
|
);
|
||||||
|
expect(result.llmConfig.reasoning).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use reasoning object for openAI endpoint with useResponsesApi=true', () => {
|
||||||
|
const modelOptions = {
|
||||||
|
reasoning_effort: ReasoningEffort.high,
|
||||||
|
reasoning_summary: ReasoningSummary.detailed,
|
||||||
|
useResponsesApi: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = getOpenAIConfig(mockApiKey, { modelOptions }, EModelEndpoint.openAI);
|
||||||
|
|
||||||
|
expect(result.llmConfig.reasoning).toEqual({
|
||||||
|
effort: ReasoningEffort.high,
|
||||||
|
summary: ReasoningSummary.detailed,
|
||||||
|
});
|
||||||
|
expect((result.llmConfig as Record<string, unknown>).reasoning_effort).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use reasoning object for azureOpenAI endpoint with useResponsesApi=true', () => {
|
||||||
|
const modelOptions = {
|
||||||
|
reasoning_effort: ReasoningEffort.high,
|
||||||
|
reasoning_summary: ReasoningSummary.detailed,
|
||||||
|
useResponsesApi: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = getOpenAIConfig(mockApiKey, { modelOptions }, EModelEndpoint.azureOpenAI);
|
||||||
|
|
||||||
|
expect(result.llmConfig.reasoning).toEqual({
|
||||||
|
effort: ReasoningEffort.high,
|
||||||
|
summary: ReasoningSummary.detailed,
|
||||||
|
});
|
||||||
|
expect((result.llmConfig as Record<string, unknown>).reasoning_effort).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use reasoning object for non-openAI/azureOpenAI endpoints', () => {
|
||||||
|
const modelOptions = {
|
||||||
|
reasoning_effort: ReasoningEffort.high,
|
||||||
|
reasoning_summary: ReasoningSummary.detailed,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = getOpenAIConfig(mockApiKey, { modelOptions }, 'custom-endpoint');
|
||||||
|
|
||||||
|
expect(result.llmConfig.reasoning).toEqual({
|
||||||
|
effort: ReasoningEffort.high,
|
||||||
|
summary: ReasoningSummary.detailed,
|
||||||
|
});
|
||||||
|
expect((result.llmConfig as Record<string, unknown>).reasoning_effort).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
it('should handle OpenRouter configuration', () => {
|
it('should handle OpenRouter configuration', () => {
|
||||||
const reverseProxyUrl = 'https://openrouter.ai/api/v1';
|
const reverseProxyUrl = 'https://openrouter.ai/api/v1';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ export function getOpenAIConfig(
|
||||||
azure,
|
azure,
|
||||||
apiKey,
|
apiKey,
|
||||||
baseURL,
|
baseURL,
|
||||||
|
endpoint,
|
||||||
streaming,
|
streaming,
|
||||||
addParams,
|
addParams,
|
||||||
dropParams,
|
dropParams,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { removeNullishValues } from 'librechat-data-provider';
|
import { EModelEndpoint, removeNullishValues } from 'librechat-data-provider';
|
||||||
import type { BindToolsInput } from '@langchain/core/language_models/chat_models';
|
import type { BindToolsInput } from '@langchain/core/language_models/chat_models';
|
||||||
import type { AzureOpenAIInput } from '@langchain/openai';
|
import type { AzureOpenAIInput } from '@langchain/openai';
|
||||||
import type { OpenAI } from 'openai';
|
import type { OpenAI } from 'openai';
|
||||||
|
|
@ -79,6 +79,7 @@ export function getOpenAILLMConfig({
|
||||||
azure,
|
azure,
|
||||||
apiKey,
|
apiKey,
|
||||||
baseURL,
|
baseURL,
|
||||||
|
endpoint,
|
||||||
streaming,
|
streaming,
|
||||||
addParams,
|
addParams,
|
||||||
dropParams,
|
dropParams,
|
||||||
|
|
@ -88,6 +89,7 @@ export function getOpenAILLMConfig({
|
||||||
apiKey: string;
|
apiKey: string;
|
||||||
streaming: boolean;
|
streaming: boolean;
|
||||||
baseURL?: string | null;
|
baseURL?: string | null;
|
||||||
|
endpoint?: EModelEndpoint | string | null;
|
||||||
modelOptions: Partial<t.OpenAIParameters>;
|
modelOptions: Partial<t.OpenAIParameters>;
|
||||||
addParams?: Record<string, unknown>;
|
addParams?: Record<string, unknown>;
|
||||||
dropParams?: string[];
|
dropParams?: string[];
|
||||||
|
|
@ -155,7 +157,8 @@ export function getOpenAILLMConfig({
|
||||||
|
|
||||||
if (
|
if (
|
||||||
hasReasoningParams({ reasoning_effort, reasoning_summary }) &&
|
hasReasoningParams({ reasoning_effort, reasoning_summary }) &&
|
||||||
(llmConfig.useResponsesApi === true || useOpenRouter)
|
(llmConfig.useResponsesApi === true ||
|
||||||
|
(endpoint !== EModelEndpoint.openAI && endpoint !== EModelEndpoint.azureOpenAI))
|
||||||
) {
|
) {
|
||||||
llmConfig.reasoning = removeNullishValues(
|
llmConfig.reasoning = removeNullishValues(
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue