Skip to content

Commit 1b862e8

Browse files
🔍 feat: Web Search via OpenAI Responses API (danny-avila#8186)
* 🔍 feat: Introduce Web Search Functionality for OpenAI API - Added a new web_search parameter to enable web search capabilities in the OpenAI configuration. - Updated the DynamicSlider component for improved styling. - Enhanced the useSetIndexOptions hook to auto-enable the Responses API when web search is activated. - Modified relevant schemas, types, and translation files to support the new web search feature. * chore: remove comments * refactor: tool handling in initializeAgent for better clarity and functionality and reflection of openai features --------- Co-authored-by: Danny Avila <[email protected]>
1 parent 303d290 commit 1b862e8

File tree

9 files changed

+53
-6
lines changed

9 files changed

+53
-6
lines changed

api/server/services/Endpoints/agents/agent.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ const initializeAgent = async ({
8585
});
8686

8787
const provider = agent.provider;
88-
const { tools, toolContextMap } =
88+
const { tools: structuredTools, toolContextMap } =
8989
(await loadTools?.({
9090
req,
9191
res,
@@ -140,12 +140,20 @@ const initializeAgent = async ({
140140
agent.provider = options.provider;
141141
}
142142

143+
/** @type {import('@librechat/agents').GenericTool[]} */
144+
let tools = options.tools ?? structuredTools;
143145
if (
144146
(agent.provider === Providers.GOOGLE || agent.provider === Providers.VERTEXAI) &&
145-
options?.tools?.length &&
146-
tools?.length
147+
options.tools?.length &&
148+
structuredTools?.length
147149
) {
148150
throw new Error(`{ "type": "${ErrorTypes.GOOGLE_TOOL_CONFLICT}"}`);
151+
} else if (
152+
(agent.provider === Providers.OPENAI || agent.provider === Providers.AZURE) &&
153+
options.tools?.length &&
154+
structuredTools?.length
155+
) {
156+
tools = structuredTools.concat(options.tools);
149157
}
150158

151159
/** @type {import('@librechat/agents').ClientOptions} */
@@ -173,7 +181,7 @@ const initializeAgent = async ({
173181
attachments,
174182
resendFiles,
175183
toolContextMap,
176-
tools: options.tools ?? tools,
184+
tools,
177185
maxContextTokens: (agentMaxContextTokens - maxTokens) * 0.9,
178186
};
179187
};

client/src/components/SidePanel/Parameters/DynamicSlider.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ function DynamicSlider({
197197
defaultTextProps,
198198
cn(
199199
optionText,
200-
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 py-1 text-xs group-hover/temp:border-gray-200',
200+
'reset-rc-number-input h-auto w-14 border-0 py-1 pl-1 text-center text-xs group-hover/temp:border-gray-200',
201201
),
202202
)}
203203
/>

client/src/hooks/Conversations/useSetIndexOptions.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ const useSetIndexOptions: TUseSetOptions = (preset = false) => {
3030
};
3131
}
3232

33+
// Auto-enable Responses API when web search is enabled
34+
if (param === 'web_search' && newValue === true) {
35+
const currentUseResponsesApi = conversation?.useResponsesApi ?? false;
36+
if (!currentUseResponsesApi) {
37+
update['useResponsesApi'] = true;
38+
}
39+
}
40+
3341
setConversation(
3442
(prevState) =>
3543
tConvoUpdateSchema.parse({

client/src/locales/en/translation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@
234234
"com_endpoint_openai_temp": "Higher values = more random, while lower values = more focused and deterministic. We recommend altering this or Top P but not both.",
235235
"com_endpoint_openai_topp": "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We recommend altering this or temperature but not both.",
236236
"com_endpoint_openai_use_responses_api": "Use the Responses API instead of Chat Completions, which includes extended features from OpenAI. Required for o1-pro, o3-pro, and to enable reasoning summaries.",
237+
"com_endpoint_openai_use_web_search": "Enable web search functionality using OpenAI's built-in search capabilities. This allows the model to search the web for up-to-date information and provide more accurate, current responses.",
237238
"com_endpoint_output": "Output",
238239
"com_endpoint_plug_image_detail": "Image Detail",
239240
"com_endpoint_plug_resend_files": "Resend Files",

packages/api/src/endpoints/openai/llm.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ProxyAgent } from 'undici';
22
import { KnownEndpoints, removeNullishValues } from 'librechat-data-provider';
3+
import type { BindToolsInput } from '@langchain/core/language_models/chat_models';
34
import type { AzureOpenAIInput } from '@langchain/openai';
45
import type { OpenAI } from 'openai';
56
import type * as t from '~/types';
@@ -176,6 +177,13 @@ export function getOpenAIConfig(
176177
delete llmConfig.max_tokens;
177178
}
178179

180+
const tools: BindToolsInput[] = [];
181+
182+
if (modelOptions.web_search) {
183+
llmConfig.useResponsesApi = true;
184+
tools.push({ type: 'web_search_preview' });
185+
}
186+
179187
/**
180188
* Note: OpenAI Web Search models do not support any known parameters besides `max_tokens`
181189
*/
@@ -216,5 +224,6 @@ export function getOpenAIConfig(
216224
return {
217225
llmConfig,
218226
configOptions,
227+
tools,
219228
};
220229
}

packages/api/src/types/openai.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { z } from 'zod';
22
import { openAISchema, EModelEndpoint } from 'librechat-data-provider';
33
import type { TEndpointOption, TAzureConfig, TEndpoint } from 'librechat-data-provider';
4+
import type { BindToolsInput } from '@langchain/core/language_models/chat_models';
45
import type { OpenAIClientOptions } from '@librechat/agents';
56
import type { AzureOptions } from './azure';
67

@@ -33,6 +34,7 @@ export type ClientOptions = OpenAIClientOptions & {
3334
export interface LLMConfigResult {
3435
llmConfig: ClientOptions;
3536
configOptions: OpenAIConfiguration;
37+
tools?: BindToolsInput[];
3638
}
3739

3840
/**

packages/data-provider/src/parameterSettings.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,19 @@ const openAIParams: Record<string, SettingDefinition> = {
247247
showDefault: false,
248248
columnSpan: 2,
249249
},
250+
web_search: {
251+
key: 'web_search',
252+
label: 'com_ui_web_search',
253+
labelCode: true,
254+
description: 'com_endpoint_openai_use_web_search',
255+
descriptionCode: true,
256+
type: 'boolean',
257+
default: false,
258+
component: 'switch',
259+
optionType: 'model',
260+
showDefault: false,
261+
columnSpan: 2,
262+
},
250263
reasoning_summary: {
251264
key: 'reasoning_summary',
252265
label: 'com_endpoint_reasoning_summary',
@@ -596,6 +609,7 @@ const openAI: SettingsConfiguration = [
596609
baseDefinitions.stop,
597610
librechat.resendFiles,
598611
baseDefinitions.imageDetail,
612+
openAIParams.web_search,
599613
openAIParams.reasoning_effort,
600614
openAIParams.useResponsesApi,
601615
openAIParams.reasoning_summary,
@@ -618,8 +632,9 @@ const openAICol2: SettingsConfiguration = [
618632
librechat.resendFiles,
619633
baseDefinitions.imageDetail,
620634
openAIParams.reasoning_effort,
621-
openAIParams.useResponsesApi,
622635
openAIParams.reasoning_summary,
636+
openAIParams.useResponsesApi,
637+
openAIParams.web_search,
623638
];
624639

625640
const anthropicConfig: SettingsConfiguration = [

packages/data-provider/src/schemas.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,8 @@ export const tConversationSchema = z.object({
634634
reasoning_summary: eReasoningSummarySchema.optional().nullable(),
635635
/* OpenAI: use Responses API */
636636
useResponsesApi: z.boolean().optional(),
637+
/* OpenAI: use Responses API with Web Search */
638+
web_search: z.boolean().optional(),
637639
/* Google: use Search Grounding */
638640
grounding: z.boolean().optional(),
639641
/* assistant */
@@ -1071,6 +1073,7 @@ export const openAIBaseSchema = tConversationSchema.pick({
10711073
reasoning_effort: true,
10721074
reasoning_summary: true,
10731075
useResponsesApi: true,
1076+
web_search: true,
10741077
});
10751078

10761079
export const openAISchema = openAIBaseSchema

packages/data-schemas/src/types/convo.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export interface IConversation extends Document {
4747
reasoning_effort?: string;
4848
reasoning_summary?: string;
4949
useResponsesApi?: boolean;
50+
web_search?: boolean;
5051
grounding?: boolean;
5152
// Additional fields
5253
files?: string[];

0 commit comments

Comments
 (0)