mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
🌐 feat: OpenRouter Web Search (#9853)
* 🌐 feat: OpenRouter Web Search
- Added tests for handling web_search parameter with OpenRouter in various scenarios.
- Implemented logic to manage web_search in modelOptions and addParams/dropParams.
- Ensured correct configuration of llmConfig and modelKwargs for OpenRouter, including handling of plugins.
- Improved overall integration of OpenRouter with OpenAI API, ensuring expected behavior across different configurations.
* chore: bump @librechat/agents to v2.4.81
This commit is contained in:
parent
823015160c
commit
3d7eaf0fcc
5 changed files with 128 additions and 8 deletions
|
|
@ -49,7 +49,7 @@
|
|||
"@langchain/google-vertexai": "^0.2.13",
|
||||
"@langchain/openai": "^0.5.18",
|
||||
"@langchain/textsplitters": "^0.1.0",
|
||||
"@librechat/agents": "^2.4.80",
|
||||
"@librechat/agents": "^2.4.81",
|
||||
"@librechat/api": "*",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@microsoft/microsoft-graph-client": "^3.0.7",
|
||||
|
|
|
|||
10
package-lock.json
generated
10
package-lock.json
generated
|
|
@ -65,7 +65,7 @@
|
|||
"@langchain/google-vertexai": "^0.2.13",
|
||||
"@langchain/openai": "^0.5.18",
|
||||
"@langchain/textsplitters": "^0.1.0",
|
||||
"@librechat/agents": "^2.4.80",
|
||||
"@librechat/agents": "^2.4.81",
|
||||
"@librechat/api": "*",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@microsoft/microsoft-graph-client": "^3.0.7",
|
||||
|
|
@ -21910,9 +21910,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@librechat/agents": {
|
||||
"version": "2.4.80",
|
||||
"resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-2.4.80.tgz",
|
||||
"integrity": "sha512-+GlWHMrgiwkptnUip7jJrz9oAQSz4+uJNoa+X6zR9W6x90NnknFwoQsXhQlnXoDGW75uGb+U7KI9mQz5H/YL6Q==",
|
||||
"version": "2.4.81",
|
||||
"resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-2.4.81.tgz",
|
||||
"integrity": "sha512-uPepwOepQS03NJg9jzLvYGonyewy33QDB7iENKHooO8+6eIOv2QC4gm1k/fYKgsIfBfng2caRmn532UTrkE3rQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@langchain/anthropic": "^0.3.26",
|
||||
|
|
@ -51641,7 +51641,7 @@
|
|||
},
|
||||
"peerDependencies": {
|
||||
"@langchain/core": "^0.3.62",
|
||||
"@librechat/agents": "^2.4.80",
|
||||
"@librechat/agents": "^2.4.81",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@modelcontextprotocol/sdk": "^1.17.1",
|
||||
"axios": "^1.12.1",
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@
|
|||
},
|
||||
"peerDependencies": {
|
||||
"@langchain/core": "^0.3.62",
|
||||
"@librechat/agents": "^2.4.80",
|
||||
"@librechat/agents": "^2.4.81",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@modelcontextprotocol/sdk": "^1.17.1",
|
||||
"axios": "^1.12.1",
|
||||
|
|
|
|||
|
|
@ -687,6 +687,82 @@ describe('getOpenAIConfig', () => {
|
|||
expect(result.provider).toBe('openrouter');
|
||||
});
|
||||
|
||||
it('should handle web_search with OpenRouter using plugins format', () => {
|
||||
const modelOptions = {
|
||||
model: 'gpt-4',
|
||||
web_search: true,
|
||||
};
|
||||
|
||||
const result = getOpenAIConfig(mockApiKey, {
|
||||
reverseProxyUrl: 'https://openrouter.ai/api/v1',
|
||||
modelOptions,
|
||||
});
|
||||
|
||||
// Should use plugins format for OpenRouter, not tools
|
||||
expect(result.llmConfig.modelKwargs).toEqual({
|
||||
plugins: [{ id: 'web' }],
|
||||
});
|
||||
expect(result.tools).toEqual([]);
|
||||
// Should NOT set useResponsesApi for OpenRouter
|
||||
expect(result.llmConfig.useResponsesApi).toBeUndefined();
|
||||
expect(result.provider).toBe('openrouter');
|
||||
});
|
||||
|
||||
it('should handle web_search false with OpenRouter', () => {
|
||||
const modelOptions = {
|
||||
model: 'gpt-4',
|
||||
web_search: false,
|
||||
};
|
||||
|
||||
const result = getOpenAIConfig(mockApiKey, {
|
||||
reverseProxyUrl: 'https://openrouter.ai/api/v1',
|
||||
modelOptions,
|
||||
});
|
||||
|
||||
// Should not have plugins when web_search is false
|
||||
expect(result.llmConfig.modelKwargs).toBeUndefined();
|
||||
expect(result.tools).toEqual([]);
|
||||
expect(result.provider).toBe('openrouter');
|
||||
});
|
||||
|
||||
it('should handle web_search with OpenRouter from addParams', () => {
|
||||
const addParams = {
|
||||
web_search: true,
|
||||
customParam: 'value',
|
||||
};
|
||||
|
||||
const result = getOpenAIConfig(mockApiKey, {
|
||||
reverseProxyUrl: 'https://openrouter.ai/api/v1',
|
||||
addParams,
|
||||
});
|
||||
|
||||
// Should use plugins format and include other params
|
||||
expect(result.llmConfig.modelKwargs).toEqual({
|
||||
plugins: [{ id: 'web' }],
|
||||
customParam: 'value',
|
||||
});
|
||||
expect(result.tools).toEqual([]);
|
||||
expect(result.provider).toBe('openrouter');
|
||||
});
|
||||
|
||||
it('should handle web_search with OpenRouter and dropParams', () => {
|
||||
const modelOptions = {
|
||||
model: 'gpt-4',
|
||||
web_search: true,
|
||||
};
|
||||
|
||||
const result = getOpenAIConfig(mockApiKey, {
|
||||
reverseProxyUrl: 'https://openrouter.ai/api/v1',
|
||||
modelOptions,
|
||||
dropParams: ['web_search'],
|
||||
});
|
||||
|
||||
// dropParams should disable web_search even for OpenRouter
|
||||
expect(result.llmConfig.modelKwargs).toBeUndefined();
|
||||
expect(result.tools).toEqual([]);
|
||||
expect(result.provider).toBe('openrouter');
|
||||
});
|
||||
|
||||
it('should handle OpenRouter with reasoning params', () => {
|
||||
const modelOptions = {
|
||||
reasoning_effort: ReasoningEffort.high,
|
||||
|
|
@ -995,6 +1071,45 @@ describe('getOpenAIConfig', () => {
|
|||
}),
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle all configuration with OpenRouter and web_search', () => {
|
||||
const complexConfig = {
|
||||
modelOptions: {
|
||||
model: 'gpt-4-turbo',
|
||||
temperature: 0.7,
|
||||
max_tokens: 2000,
|
||||
verbosity: Verbosity.medium,
|
||||
reasoning_effort: ReasoningEffort.high,
|
||||
web_search: true,
|
||||
},
|
||||
reverseProxyUrl: 'https://openrouter.ai/api/v1',
|
||||
headers: { 'X-Custom': 'value' },
|
||||
streaming: false,
|
||||
addParams: {
|
||||
customParam: 'custom-value',
|
||||
temperature: 0.8,
|
||||
},
|
||||
};
|
||||
|
||||
const result = getOpenAIConfig(mockApiKey, complexConfig);
|
||||
|
||||
expect(result.llmConfig).toMatchObject({
|
||||
model: 'gpt-4-turbo',
|
||||
temperature: 0.8,
|
||||
streaming: false,
|
||||
include_reasoning: true, // OpenRouter specific
|
||||
});
|
||||
// Should NOT have useResponsesApi for OpenRouter
|
||||
expect(result.llmConfig.useResponsesApi).toBeUndefined();
|
||||
expect(result.llmConfig.maxTokens).toBe(2000);
|
||||
expect(result.llmConfig.modelKwargs).toEqual({
|
||||
verbosity: Verbosity.medium,
|
||||
customParam: 'custom-value',
|
||||
plugins: [{ id: 'web' }], // OpenRouter web search format
|
||||
});
|
||||
expect(result.tools).toEqual([]); // No tools for OpenRouter web search
|
||||
expect(result.provider).toBe('openrouter');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Real Usage Integration Tests', () => {
|
||||
|
|
|
|||
|
|
@ -180,7 +180,12 @@ export function getOpenAILLMConfig({
|
|||
enableWebSearch = false;
|
||||
}
|
||||
|
||||
if (enableWebSearch) {
|
||||
if (useOpenRouter && enableWebSearch) {
|
||||
/** OpenRouter expects web search as a plugins parameter */
|
||||
modelKwargs.plugins = [{ id: 'web' }];
|
||||
hasModelKwargs = true;
|
||||
} else if (enableWebSearch) {
|
||||
/** Standard OpenAI web search uses tools API */
|
||||
llmConfig.useResponsesApi = true;
|
||||
tools.push({ type: 'web_search_preview' });
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue