@@ -8,6 +8,10 @@ import {
88 LangSmithParams ,
99 type BaseChatModelParams ,
1010} from "@langchain/core/language_models/chat_models" ;
11+ import {
12+ isLangChainTool ,
13+ convertToOpenAITool ,
14+ } from "@langchain/core/utils/function_calling" ;
1115import { ModelProfile } from "@langchain/core/language_models/profile" ;
1216import { Serialized } from "@langchain/core/load/serializable" ;
1317import {
@@ -23,6 +27,13 @@ import {
2327 type OpenAIClient ,
2428 ChatOpenAICompletions ,
2529} from "@langchain/openai" ;
30+ import {
31+ buildSearchParametersPayload ,
32+ filterXAIBuiltInTools ,
33+ mergeSearchParams ,
34+ type XAISearchParameters ,
35+ type XAISearchParametersPayload ,
36+ } from "./live_search.js" ;
2637import PROFILES from "./profiles.js" ;
2738
2839export type OpenAIToolChoice =
@@ -34,7 +45,7 @@ export type OpenAIToolChoice =
3445 * xAI's built-in live_search tool type.
3546 * Enables the model to search the web for real-time information.
3647 */
37- export interface XAILiveSearchTool {
48+ export interface XAILiveSearchTool extends XAISearchParameters {
3849 /**
3950 * The type of the tool. Must be "live_search" for xAI's built-in search.
4051 */
@@ -46,6 +57,13 @@ export interface XAILiveSearchTool {
4657 */
4758export type XAIBuiltInTool = XAILiveSearchTool ;
4859
60+ /**
61+ * Set of all supported xAI built-in server-side tool types.
62+ * This allows us to easily extend support for future built-in tools
63+ * without changing the core detection logic.
64+ */
65+ const XAI_BUILT_IN_TOOL_TYPES = new Set < string > ( [ "live_search" ] ) ;
66+
4967/**
5068 * Tool type that includes both standard tools and xAI built-in tools.
5169 */
@@ -54,54 +72,6 @@ type ChatXAIToolType =
5472 | OpenAIClient . ChatCompletionTool
5573 | XAIBuiltInTool ;
5674
57- /**
58- * Search parameters for xAI's Live Search API.
59- * Controls how the model searches for and retrieves real-time information.
60- *
61- * @note The Live Search API is being deprecated by xAI in favor of
62- * the agentic tool calling approach. Consider using `tools: [{ type: "live_search" }]`
63- * for future compatibility.
64- */
65- export interface XAISearchParameters {
66- /**
67- * Controls when the model should perform a search.
68- * - "auto": Let the model decide when to search (default)
69- * - "on": Always search for every request
70- * - "off": Never search
71- */
72- mode ?: "auto" | "on" | "off" ;
73- /**
74- * Maximum number of search results to return.
75- * @default 5
76- */
77- max_search_results ?: number ;
78- /**
79- * Filter search results to only include content from after this date.
80- * Format: ISO 8601 date string (e.g., "2024-01-01")
81- */
82- from_date ?: string ;
83- /**
84- * Filter search results to only include content from before this date.
85- * Format: ISO 8601 date string (e.g., "2024-12-31")
86- */
87- to_date ?: string ;
88- /**
89- * Whether to return citations/sources for the search results.
90- * @default true
91- */
92- return_citations ?: boolean ;
93- /**
94- * Specific domains to include in the search.
95- * Example: ["wikipedia.org", "arxiv.org"]
96- */
97- allowed_domains ?: string [ ] ;
98- /**
99- * Specific domains to exclude from the search.
100- * Example: ["reddit.com"]
101- */
102- excluded_domains ?: string [ ] ;
103- }
104-
10575/**
10676 * xAI-specific invocation parameters that extend the OpenAI completion params
10777 * with xAI's search_parameters field.
@@ -114,15 +84,7 @@ export type ChatXAICompletionsInvocationParams = Omit<
11484 * Search parameters for xAI's Live Search API.
11585 * When present, enables the model to search the web for real-time information.
11686 */
117- search_parameters ?: {
118- mode : "auto" | "on" | "off" ;
119- max_search_results ?: number ;
120- from_date ?: string ;
121- to_date ?: string ;
122- return_citations ?: boolean ;
123- allowed_domains ?: string [ ] ;
124- excluded_domains ?: string [ ] ;
125- } ;
87+ search_parameters ?: XAISearchParametersPayload ;
12688} ;
12789
12890/**
@@ -173,7 +135,8 @@ export function isXAIBuiltInTool(
173135 typeof tool === "object" &&
174136 tool !== null &&
175137 "type" in tool &&
176- ( tool as XAIBuiltInTool ) . type === "live_search"
138+ typeof ( tool as { type ?: unknown } ) . type === "string" &&
139+ XAI_BUILT_IN_TOOL_TYPES . has ( ( tool as { type : string } ) . type )
177140 ) ;
178141}
179142
@@ -635,7 +598,9 @@ export interface ChatXAIInput extends BaseChatModelParams {
635598 * searchParameters: {
636599 * mode: "on",
637600 * max_search_results: 10,
638- * allowed_domains: ["spacex.com", "nasa.gov"],
601+ * sources: [
602+ * { type: "web", allowed_websites: ["spacex.com", "nasa.gov"] },
603+ * ],
639604 * }
640605 * });
641606 * ```
@@ -716,15 +681,7 @@ export class ChatXAI extends ChatOpenAICompletions<ChatXAICallOptions> {
716681 protected _getEffectiveSearchParameters (
717682 options ?: this[ "ParsedCallOptions" ]
718683 ) : XAISearchParameters | undefined {
719- const callSearchParams = options ?. searchParameters ;
720- if ( ! this . searchParameters && ! callSearchParams ) {
721- return undefined ;
722- }
723- // Merge instance-level with call-level, call-level takes precedence
724- return {
725- ...this . searchParameters ,
726- ...callSearchParams ,
727- } ;
684+ return mergeSearchParams ( this . searchParameters , options ?. searchParameters ) ;
728685 }
729686
730687 /**
@@ -736,6 +693,42 @@ export class ChatXAI extends ChatOpenAICompletions<ChatXAICallOptions> {
736693 return tools ?. some ( isXAIBuiltInTool ) ?? false ;
737694 }
738695
696+ /**
697+ * Formats tools to xAI/OpenAI format, preserving provider-specific definitions.
698+ *
699+ * @param tools The tools to format
700+ * @returns The formatted tools
701+ */
702+ formatStructuredToolToXAI (
703+ tools : ChatXAIToolType [ ]
704+ ) : ( OpenAIClient . ChatCompletionTool | XAIBuiltInTool ) [ ] | undefined {
705+ if ( ! tools || ! tools . length ) {
706+ return undefined ;
707+ }
708+ return tools . map ( ( tool ) => {
709+ // 1. Check for provider definition first (from xaiLiveSearch factory)
710+ if ( isLangChainTool ( tool ) && tool . extras ?. providerToolDefinition ) {
711+ return tool . extras . providerToolDefinition as XAIBuiltInTool ;
712+ }
713+ // 2. Check for built-in tools (legacy { type: "live_search" })
714+ if ( isXAIBuiltInTool ( tool ) ) {
715+ return tool ;
716+ }
717+ // 3. Convert standard tools to OpenAI format
718+ return convertToOpenAITool ( tool ) as OpenAIClient . ChatCompletionTool ;
719+ } ) ;
720+ }
721+
722+ override bindTools (
723+ tools : ChatXAIToolType [ ] ,
724+ kwargs ?: Partial < ChatXAICallOptions >
725+ ) : Runnable < BaseLanguageModelInput , AIMessageChunk , ChatXAICallOptions > {
726+ return this . withConfig ( {
727+ tools : this . formatStructuredToolToXAI ( tools ) ,
728+ ...kwargs ,
729+ } as Partial < ChatXAICallOptions > ) ;
730+ }
731+
739732 /** @internal */
740733 override invocationParams (
741734 options ?: this[ "ParsedCallOptions" ] ,
@@ -746,43 +739,22 @@ export class ChatXAI extends ChatOpenAICompletions<ChatXAICallOptions> {
746739 // Cast to xAI-specific params type
747740 const params : ChatXAICompletionsInvocationParams = { ...baseParams } ;
748741
749- // Get effective search parameters from instance and call options
750- const effectiveSearchParams = this . _getEffectiveSearchParameters ( options ) ;
751-
752742 // Check if live_search tool is being used
753- const hasLiveSearchTool = this . _hasBuiltInTools (
754- options ?. tools as ChatXAIToolType [ ] | undefined
743+ // We also need to extract params from the tool definition if present
744+ const liveSearchTool = options ?. tools ?. find ( isXAIBuiltInTool ) as
745+ | XAILiveSearchTool
746+ | undefined ;
747+
748+ const mergedSearchParams = mergeSearchParams (
749+ this . searchParameters ,
750+ options ?. searchParameters ,
751+ liveSearchTool
755752 ) ;
756753
757754 // Add search_parameters if needed
758- if ( effectiveSearchParams || hasLiveSearchTool ) {
759- const searchParams = hasLiveSearchTool
760- ? { mode : "auto" as const , ...effectiveSearchParams }
761- : effectiveSearchParams ;
762-
763- if ( searchParams ) {
764- params . search_parameters = {
765- mode : searchParams . mode ?? "auto" ,
766- ...( searchParams . max_search_results !== undefined && {
767- max_search_results : searchParams . max_search_results ,
768- } ) ,
769- ...( searchParams . from_date !== undefined && {
770- from_date : searchParams . from_date ,
771- } ) ,
772- ...( searchParams . to_date !== undefined && {
773- to_date : searchParams . to_date ,
774- } ) ,
775- ...( searchParams . return_citations !== undefined && {
776- return_citations : searchParams . return_citations ,
777- } ) ,
778- ...( searchParams . allowed_domains !== undefined && {
779- allowed_domains : searchParams . allowed_domains ,
780- } ) ,
781- ...( searchParams . excluded_domains !== undefined && {
782- excluded_domains : searchParams . excluded_domains ,
783- } ) ,
784- } ;
785- }
755+ if ( mergedSearchParams ) {
756+ params . search_parameters =
757+ buildSearchParametersPayload ( mergedSearchParams ) ;
786758 }
787759
788760 return params ;
@@ -828,16 +800,11 @@ export class ChatXAI extends ChatOpenAICompletions<ChatXAICallOptions> {
828800 return msg ;
829801 } ) ;
830802
831- // Filter out xAI built-in tools from the standard tools array
832- // Built-in tools are handled via search_parameters (added in invocationParams)
833803 let filteredTools : OpenAIClient . ChatCompletionTool [ ] | undefined ;
834804 if ( request . tools ) {
835- filteredTools = request . tools . filter (
836- ( tool ) => ! isXAIBuiltInTool ( tool )
837- ) as OpenAIClient . ChatCompletionTool [ ] ;
838- if ( filteredTools . length === 0 ) {
839- filteredTools = undefined ;
840- }
805+ filteredTools = filterXAIBuiltInTools ( request . tools ) as
806+ | OpenAIClient . ChatCompletionTool [ ]
807+ | undefined ;
841808 }
842809
843810 const newRequest = {
0 commit comments