@tanstack/ai-grok 0.12.4 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,113 @@
1
- import { convertFunctionToolToChatCompletionsFormat, convertToolsToChatCompletionsFormat } from "@tanstack/openai-base";
1
+ import { brandProviderTool } from "@tanstack/ai";
2
+ import { convertFunctionToolToResponsesFormat } from "@tanstack/openai-base";
3
+ import { convertFunctionToolToResponsesFormat as convertFunctionToolToResponsesFormat2 } from "@tanstack/openai-base";
4
+ function providerTool(kind, description, metadata) {
5
+ return brandProviderTool({
6
+ name: kind,
7
+ description,
8
+ metadata: {
9
+ __kind: `grok.${kind}`,
10
+ ...metadata
11
+ }
12
+ });
13
+ }
14
+ function grokWebSearchTool(config = {}) {
15
+ if (config.filters?.allowed_domains !== void 0 && config.filters.excluded_domains !== void 0) {
16
+ throw new Error(
17
+ "allowed_domains and excluded_domains cannot both be provided."
18
+ );
19
+ }
20
+ if (config.filters?.allowed_domains !== void 0 && config.filters.allowed_domains.length > 5) {
21
+ throw new Error("allowed_domains supports at most 5 domains.");
22
+ }
23
+ if (config.filters?.excluded_domains !== void 0 && config.filters.excluded_domains.length > 5) {
24
+ throw new Error("excluded_domains supports at most 5 domains.");
25
+ }
26
+ return providerTool("web_search", "Search the web", {
27
+ type: "web_search",
28
+ ...config
29
+ });
30
+ }
31
+ function grokXSearchTool(config = {}) {
32
+ if (config.allowed_x_handles !== void 0 && config.excluded_x_handles !== void 0) {
33
+ throw new Error(
34
+ "allowed_x_handles and excluded_x_handles cannot both be provided."
35
+ );
36
+ }
37
+ if (config.allowed_x_handles !== void 0 && config.allowed_x_handles.length > 20) {
38
+ throw new Error("allowed_x_handles supports at most 20 handles.");
39
+ }
40
+ if (config.excluded_x_handles !== void 0 && config.excluded_x_handles.length > 20) {
41
+ throw new Error("excluded_x_handles supports at most 20 handles.");
42
+ }
43
+ return providerTool("x_search", "Search X posts", {
44
+ type: "x_search",
45
+ ...config
46
+ });
47
+ }
48
+ function grokFileSearchTool(config) {
49
+ if (config.vector_store_ids.length === 0) {
50
+ throw new Error("vector_store_ids must contain at least one collection id.");
51
+ }
52
+ if (config.max_num_results !== void 0) {
53
+ if (config.max_num_results < 1 || config.max_num_results > 50) {
54
+ throw new Error("max_num_results must be between 1 and 50.");
55
+ }
56
+ }
57
+ return providerTool("file_search", "Search xAI file collections", {
58
+ type: "file_search",
59
+ ...config
60
+ });
61
+ }
62
+ function grokMCPTool(config) {
63
+ if (!config.server_url) {
64
+ throw new Error("server_url must be provided.");
65
+ }
66
+ return providerTool("mcp", config.server_description || "Remote MCP server", {
67
+ type: "mcp",
68
+ ...config
69
+ });
70
+ }
71
+ function getGrokProviderToolKind(tool) {
72
+ const kind = tool.metadata?.__kind;
73
+ switch (kind) {
74
+ case "grok.web_search":
75
+ return "web_search";
76
+ case "grok.x_search":
77
+ return "x_search";
78
+ case "grok.file_search":
79
+ return "file_search";
80
+ case "grok.mcp":
81
+ return "mcp";
82
+ default:
83
+ return void 0;
84
+ }
85
+ }
86
+ function convertGrokProviderToolToAdapterFormat(tool, kind) {
87
+ const metadata = tool.metadata;
88
+ if (metadata.type !== kind) {
89
+ throw new Error(
90
+ `convertGrokProviderToolToAdapterFormat: tool "${tool.name}" has mismatched Grok tool metadata.`
91
+ );
92
+ }
93
+ const { __kind: _kind, ...toolConfig } = metadata;
94
+ return toolConfig;
95
+ }
96
+ function convertToolsToProviderFormat(tools) {
97
+ return tools.map((tool) => {
98
+ const grokProviderToolKind = getGrokProviderToolKind(tool);
99
+ if (grokProviderToolKind) {
100
+ return convertGrokProviderToolToAdapterFormat(tool, grokProviderToolKind);
101
+ }
102
+ return convertFunctionToolToResponsesFormat(tool);
103
+ });
104
+ }
2
105
  export {
3
- convertFunctionToolToChatCompletionsFormat as convertFunctionToolToAdapterFormat,
4
- convertToolsToChatCompletionsFormat as convertToolsToProviderFormat
106
+ convertFunctionToolToResponsesFormat2 as convertFunctionToolToAdapterFormat,
107
+ convertToolsToProviderFormat,
108
+ grokFileSearchTool,
109
+ grokMCPTool,
110
+ grokWebSearchTool,
111
+ grokXSearchTool
5
112
  };
6
113
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/tools/index.ts"],"sourcesContent":["import { brandProviderTool } from '@tanstack/ai'\nimport { convertFunctionToolToResponsesFormat } from '@tanstack/openai-base'\nimport type { ProviderTool, Tool } from '@tanstack/ai'\nimport type { ResponsesFunctionTool } from '@tanstack/openai-base'\nimport type { GrokProviderToolKind } from '../model-meta'\n\nexport type FunctionTool = ResponsesFunctionTool\n\nexport { convertFunctionToolToResponsesFormat as convertFunctionToolToAdapterFormat }\n\nexport type GrokProviderTool<TKind extends GrokProviderToolKind> = ProviderTool<\n 'grok',\n TKind\n>\n\ntype GrokToolKindMarker<TKind extends GrokProviderToolKind> = `grok.${TKind}`\n\nexport interface GrokWebSearchToolConfig {\n type: 'web_search'\n filters?: {\n allowed_domains?: Array<string>\n excluded_domains?: Array<string>\n }\n enable_image_understanding?: boolean\n enable_image_search?: boolean\n}\n\nexport interface GrokXSearchToolConfig {\n type: 'x_search'\n allowed_x_handles?: Array<string>\n excluded_x_handles?: Array<string>\n from_date?: string\n to_date?: string\n enable_image_understanding?: boolean\n enable_video_understanding?: boolean\n}\n\nexport interface GrokFileSearchToolConfig {\n type: 'file_search'\n vector_store_ids: Array<string>\n max_num_results?: number\n}\n\nexport interface GrokMCPToolConfig {\n type: 'mcp'\n server_label: string\n server_url: string\n allowed_tools?: Array<string>\n server_description?: string\n authorization?: string\n headers?: Record<string, string>\n}\n\nexport type GrokServerTool =\n | GrokWebSearchToolConfig\n | GrokXSearchToolConfig\n | GrokFileSearchToolConfig\n | GrokMCPToolConfig\n\ntype GrokProviderToolMetadata<TKind extends GrokProviderToolKind> = Extract<\n GrokServerTool,\n { type: TKind }\n> & {\n __kind: GrokToolKindMarker<TKind>\n}\n\nexport type GrokResponsesTool = GrokServerTool | ResponsesFunctionTool\n\nfunction providerTool<TKind extends GrokProviderToolKind>(\n kind: TKind,\n description: string,\n metadata: Extract<GrokServerTool, { type: TKind }>,\n): GrokProviderTool<TKind> {\n return brandProviderTool<GrokProviderTool<TKind>>({\n name: kind,\n description,\n metadata: {\n __kind: `grok.${kind}`,\n ...metadata,\n },\n })\n}\n\nexport function grokWebSearchTool(\n config: Omit<GrokWebSearchToolConfig, 'type'> = {},\n): GrokProviderTool<'web_search'> {\n if (\n config.filters?.allowed_domains !== undefined &&\n config.filters.excluded_domains !== undefined\n ) {\n throw new Error(\n 'allowed_domains and excluded_domains cannot both be provided.',\n )\n }\n if (\n config.filters?.allowed_domains !== undefined &&\n config.filters.allowed_domains.length > 5\n ) {\n throw new Error('allowed_domains supports at most 5 domains.')\n }\n if (\n config.filters?.excluded_domains !== undefined &&\n config.filters.excluded_domains.length > 5\n ) {\n throw new Error('excluded_domains supports at most 5 domains.')\n }\n return providerTool('web_search', 'Search the web', {\n type: 'web_search',\n ...config,\n })\n}\n\nexport function grokXSearchTool(\n config: Omit<GrokXSearchToolConfig, 'type'> = {},\n): GrokProviderTool<'x_search'> {\n if (\n config.allowed_x_handles !== undefined &&\n config.excluded_x_handles !== undefined\n ) {\n throw new Error(\n 'allowed_x_handles and excluded_x_handles cannot both be provided.',\n )\n }\n if (\n config.allowed_x_handles !== undefined &&\n config.allowed_x_handles.length > 20\n ) {\n throw new Error('allowed_x_handles supports at most 20 handles.')\n }\n if (\n config.excluded_x_handles !== undefined &&\n config.excluded_x_handles.length > 20\n ) {\n throw new Error('excluded_x_handles supports at most 20 handles.')\n }\n return providerTool('x_search', 'Search X posts', {\n type: 'x_search',\n ...config,\n })\n}\n\nexport function grokFileSearchTool(\n config: Omit<GrokFileSearchToolConfig, 'type'>,\n): GrokProviderTool<'file_search'> {\n if (config.vector_store_ids.length === 0) {\n throw new Error('vector_store_ids must contain at least one collection id.')\n }\n if (config.max_num_results !== undefined) {\n if (config.max_num_results < 1 || config.max_num_results > 50) {\n throw new Error('max_num_results must be between 1 and 50.')\n }\n }\n return providerTool('file_search', 'Search xAI file collections', {\n type: 'file_search',\n ...config,\n })\n}\n\nexport function grokMCPTool(\n config: Omit<GrokMCPToolConfig, 'type'>,\n): GrokProviderTool<'mcp'> {\n if (!config.server_url) {\n throw new Error('server_url must be provided.')\n }\n return providerTool('mcp', config.server_description || 'Remote MCP server', {\n type: 'mcp',\n ...config,\n })\n}\n\nfunction getGrokProviderToolKind(tool: Tool): GrokProviderToolKind | undefined {\n const kind = (tool.metadata as { __kind?: unknown } | undefined)?.__kind\n switch (kind) {\n case 'grok.web_search':\n return 'web_search'\n case 'grok.x_search':\n return 'x_search'\n case 'grok.file_search':\n return 'file_search'\n case 'grok.mcp':\n return 'mcp'\n default:\n return undefined\n }\n}\n\nfunction convertGrokProviderToolToAdapterFormat(\n tool: Tool,\n kind: GrokProviderToolKind,\n): GrokServerTool {\n const metadata = tool.metadata as GrokProviderToolMetadata<typeof kind>\n if (metadata.type !== kind) {\n throw new Error(\n `convertGrokProviderToolToAdapterFormat: tool \"${tool.name}\" has mismatched Grok tool metadata.`,\n )\n }\n const { __kind: _kind, ...toolConfig } = metadata\n void _kind\n return toolConfig\n}\n\nexport function convertToolsToProviderFormat(\n tools: Array<Tool>,\n): Array<GrokResponsesTool> {\n return tools.map((tool) => {\n const grokProviderToolKind = getGrokProviderToolKind(tool)\n if (grokProviderToolKind) {\n return convertGrokProviderToolToAdapterFormat(tool, grokProviderToolKind)\n }\n return convertFunctionToolToResponsesFormat(tool)\n })\n}\n"],"names":[],"mappings":";;;AAoEA,SAAS,aACP,MACA,aACA,UACyB;AACzB,SAAO,kBAA2C;AAAA,IAChD,MAAM;AAAA,IACN;AAAA,IACA,UAAU;AAAA,MACR,QAAQ,QAAQ,IAAI;AAAA,MACpB,GAAG;AAAA,IAAA;AAAA,EACL,CACD;AACH;AAEO,SAAS,kBACd,SAAgD,IAChB;AAChC,MACE,OAAO,SAAS,oBAAoB,UACpC,OAAO,QAAQ,qBAAqB,QACpC;AACA,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AACA,MACE,OAAO,SAAS,oBAAoB,UACpC,OAAO,QAAQ,gBAAgB,SAAS,GACxC;AACA,UAAM,IAAI,MAAM,6CAA6C;AAAA,EAC/D;AACA,MACE,OAAO,SAAS,qBAAqB,UACrC,OAAO,QAAQ,iBAAiB,SAAS,GACzC;AACA,UAAM,IAAI,MAAM,8CAA8C;AAAA,EAChE;AACA,SAAO,aAAa,cAAc,kBAAkB;AAAA,IAClD,MAAM;AAAA,IACN,GAAG;AAAA,EAAA,CACJ;AACH;AAEO,SAAS,gBACd,SAA8C,IAChB;AAC9B,MACE,OAAO,sBAAsB,UAC7B,OAAO,uBAAuB,QAC9B;AACA,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AACA,MACE,OAAO,sBAAsB,UAC7B,OAAO,kBAAkB,SAAS,IAClC;AACA,UAAM,IAAI,MAAM,gDAAgD;AAAA,EAClE;AACA,MACE,OAAO,uBAAuB,UAC9B,OAAO,mBAAmB,SAAS,IACnC;AACA,UAAM,IAAI,MAAM,iDAAiD;AAAA,EACnE;AACA,SAAO,aAAa,YAAY,kBAAkB;AAAA,IAChD,MAAM;AAAA,IACN,GAAG;AAAA,EAAA,CACJ;AACH;AAEO,SAAS,mBACd,QACiC;AACjC,MAAI,OAAO,iBAAiB,WAAW,GAAG;AACxC,UAAM,IAAI,MAAM,2DAA2D;AAAA,EAC7E;AACA,MAAI,OAAO,oBAAoB,QAAW;AACxC,QAAI,OAAO,kBAAkB,KAAK,OAAO,kBAAkB,IAAI;AAC7D,YAAM,IAAI,MAAM,2CAA2C;AAAA,IAC7D;AAAA,EACF;AACA,SAAO,aAAa,eAAe,+BAA+B;AAAA,IAChE,MAAM;AAAA,IACN,GAAG;AAAA,EAAA,CACJ;AACH;AAEO,SAAS,YACd,QACyB;AACzB,MAAI,CAAC,OAAO,YAAY;AACtB,UAAM,IAAI,MAAM,8BAA8B;AAAA,EAChD;AACA,SAAO,aAAa,OAAO,OAAO,sBAAsB,qBAAqB;AAAA,IAC3E,MAAM;AAAA,IACN,GAAG;AAAA,EAAA,CACJ;AACH;AAEA,SAAS,wBAAwB,MAA8C;AAC7E,QAAM,OAAQ,KAAK,UAA+C;AAClE,UAAQ,MAAA;AAAA,IACN,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EAAA;AAEb;AAEA,SAAS,uCACP,MACA,MACgB;AAChB,QAAM,WAAW,KAAK;AACtB,MAAI,SAAS,SAAS,MAAM;AAC1B,UAAM,IAAI;AAAA,MACR,iDAAiD,KAAK,IAAI;AAAA,IAAA;AAAA,EAE9D;AACA,QAAM,EAAE,QAAQ,OAAO,GAAG,eAAe;AAEzC,SAAO;AACT;AAEO,SAAS,6BACd,OAC0B;AAC1B,SAAO,MAAM,IAAI,CAAC,SAAS;AACzB,UAAM,uBAAuB,wBAAwB,IAAI;AACzD,QAAI,sBAAsB;AACxB,aAAO,uCAAuC,MAAM,oBAAoB;AAAA,IAC1E;AACA,WAAO,qCAAqC,IAAI;AAAA,EAClD,CAAC;AACH;"}
@@ -0,0 +1,135 @@
1
+ import { DurationOptions } from '@tanstack/ai/adapters';
2
+ import { GrokVideoModel } from '../model-meta.js';
3
+ /**
4
+ * Aspect ratios accepted by the grok-imagine video models.
5
+ *
6
+ * Note: this is a narrower set than the grok-imagine image models — the
7
+ * video endpoint rejects the phone-screen ratios ('9:19.5', '9:20', …) and
8
+ * 'auto'.
9
+ *
10
+ * @experimental Video generation is an experimental feature and may change.
11
+ */
12
+ export type GrokVideoAspectRatio = '1:1' | '16:9' | '9:16' | '4:3' | '3:4' | '3:2' | '2:3';
13
+ /**
14
+ * Resolution tiers for the grok-imagine video models.
15
+ *
16
+ * @experimental Video generation is an experimental feature and may change.
17
+ */
18
+ export type GrokVideoResolution = '480p' | '720p' | '1080p';
19
+ /**
20
+ * Size strings for grok-imagine video models. The Imagine API is
21
+ * aspect-ratio based rather than pixel-size based; like the grok-imagine
22
+ * image models, the generic `size` option uses an
23
+ * `aspectRatio_resolution` template ("16:9_720p") — the resolution suffix
24
+ * is optional ("16:9" uses the API default).
25
+ *
26
+ * @experimental Video generation is an experimental feature and may change.
27
+ */
28
+ export type GrokVideoSize = GrokVideoAspectRatio | `${GrokVideoAspectRatio}_${GrokVideoResolution}`;
29
+ /**
30
+ * Video duration limits enforced by the Imagine API (seconds).
31
+ */
32
+ export declare const GROK_VIDEO_MIN_DURATION = 1;
33
+ export declare const GROK_VIDEO_MAX_DURATION = 15;
34
+ /**
35
+ * Parses a grok video size string into its components.
36
+ * Format: "aspectRatio" or "aspectRatio_resolution",
37
+ * e.g. "16:9_720p" → { aspectRatio: "16:9", resolution: "720p" }.
38
+ * Returns undefined when the string doesn't match the template.
39
+ */
40
+ export declare function parseGrokVideoSize(size: string): {
41
+ aspectRatio: string;
42
+ resolution?: string;
43
+ } | undefined;
44
+ /**
45
+ * Validate the `size` template for a given grok video model.
46
+ *
47
+ * @experimental Video generation is an experimental feature and may change.
48
+ */
49
+ export declare function validateVideoSize(model: string, size?: string): asserts size is GrokVideoSize | undefined;
50
+ /**
51
+ * Per-model duration type. The Imagine API accepts any integer second in the
52
+ * 1–15 range, so this is a continuous range expressed as `number` (a literal
53
+ * union can't represent it). `snapDuration()` coerces a raw seconds value into
54
+ * the valid range at runtime.
55
+ *
56
+ * @experimental Video generation is an experimental feature and may change.
57
+ */
58
+ export type GrokVideoModelDurationByName = {
59
+ 'grok-imagine-video': number;
60
+ 'grok-imagine-video-1.5': number;
61
+ };
62
+ /**
63
+ * Runtime duration table backing `availableDurations()` / `snapDuration()`.
64
+ * Both grok-imagine video models accept the same continuous 1–15 integer-second
65
+ * range.
66
+ *
67
+ * @experimental Video generation is an experimental feature and may change.
68
+ */
69
+ export declare const GROK_VIDEO_DURATIONS: {
70
+ readonly [TModel in GrokVideoModel]: DurationOptions<GrokVideoModelDurationByName[TModel]>;
71
+ };
72
+ /**
73
+ * Look up the duration options for a grok video model.
74
+ *
75
+ * @experimental Video generation is an experimental feature and may change.
76
+ */
77
+ export declare function getGrokVideoDurationOptions<TModel extends GrokVideoModel>(model: TModel): DurationOptions<GrokVideoModelDurationByName[TModel]>;
78
+ /**
79
+ * Provider-specific options for grok video generation. These map directly
80
+ * onto the Imagine API request body and take precedence over the generic
81
+ * `size` / `duration` options when both are provided.
82
+ *
83
+ * @experimental Video generation is an experimental feature and may change.
84
+ */
85
+ export interface GrokVideoProviderOptions {
86
+ /**
87
+ * Output aspect ratio.
88
+ */
89
+ aspect_ratio?: GrokVideoAspectRatio;
90
+ /**
91
+ * Output resolution tier.
92
+ */
93
+ resolution?: GrokVideoResolution;
94
+ /**
95
+ * Video duration in integer seconds (1–15).
96
+ */
97
+ duration?: number;
98
+ }
99
+ /**
100
+ * Type-only map from model name to its specific provider options.
101
+ *
102
+ * @experimental Video generation is an experimental feature and may change.
103
+ */
104
+ export type GrokVideoModelProviderOptionsByName = {
105
+ 'grok-imagine-video': GrokVideoProviderOptions;
106
+ 'grok-imagine-video-1.5': GrokVideoProviderOptions;
107
+ };
108
+ /**
109
+ * Type-only map from model name to its supported `size` strings.
110
+ *
111
+ * @experimental Video generation is an experimental feature and may change.
112
+ */
113
+ export type GrokVideoModelSizeByName = {
114
+ 'grok-imagine-video': GrokVideoSize;
115
+ 'grok-imagine-video-1.5': GrokVideoSize;
116
+ };
117
+ /**
118
+ * Type-only map from model name to the non-text prompt modalities it accepts.
119
+ * Both models accept an `image` prompt part as the starting frame:
120
+ * `grok-imagine-video` (v1.0) does text-to-video and image-to-video, while
121
+ * `grok-imagine-video-1.5` is image-to-video only (the image is required).
122
+ *
123
+ * @experimental Video generation is an experimental feature and may change.
124
+ */
125
+ export type GrokVideoModelInputModalitiesByName = {
126
+ 'grok-imagine-video': readonly ['image'];
127
+ 'grok-imagine-video-1.5': readonly ['image'];
128
+ };
129
+ /**
130
+ * True when the model only supports image-to-video (a starting frame is
131
+ * required).
132
+ *
133
+ * @experimental Video generation is an experimental feature and may change.
134
+ */
135
+ export declare function isImageToVideoOnlyModel(model: string): boolean;
@@ -0,0 +1,67 @@
1
+ const GROK_VIDEO_ASPECT_RATIOS = [
2
+ "1:1",
3
+ "16:9",
4
+ "9:16",
5
+ "4:3",
6
+ "3:4",
7
+ "3:2",
8
+ "2:3"
9
+ ];
10
+ const GROK_VIDEO_RESOLUTIONS = ["480p", "720p", "1080p"];
11
+ const GROK_VIDEO_MIN_DURATION = 1;
12
+ const GROK_VIDEO_MAX_DURATION = 15;
13
+ function parseGrokVideoSize(size) {
14
+ const match = size.match(/^([\d.]+:[\d.]+)(?:_(.+))?$/);
15
+ const [, aspectRatio, resolution] = match ?? [];
16
+ if (aspectRatio === void 0) return void 0;
17
+ return { aspectRatio, ...resolution !== void 0 && { resolution } };
18
+ }
19
+ function validateVideoSize(model, size) {
20
+ if (size === void 0) return;
21
+ const parsed = parseGrokVideoSize(size);
22
+ if (!parsed || !GROK_VIDEO_ASPECT_RATIOS.includes(parsed.aspectRatio)) {
23
+ throw new Error(
24
+ `Size "${size}" is not supported by model "${model}". Expected "aspectRatio" or "aspectRatio_resolution" (e.g. "16:9_720p") with aspect ratio one of: ${GROK_VIDEO_ASPECT_RATIOS.join(", ")}`
25
+ );
26
+ }
27
+ if (parsed.resolution !== void 0 && !GROK_VIDEO_RESOLUTIONS.includes(parsed.resolution)) {
28
+ throw new Error(
29
+ `Resolution "${parsed.resolution}" is not supported by model "${model}". Supported resolutions: ${GROK_VIDEO_RESOLUTIONS.join(", ")}`
30
+ );
31
+ }
32
+ }
33
+ const GROK_VIDEO_DURATIONS = {
34
+ "grok-imagine-video": {
35
+ kind: "range",
36
+ min: GROK_VIDEO_MIN_DURATION,
37
+ max: GROK_VIDEO_MAX_DURATION,
38
+ step: 1,
39
+ unit: "seconds"
40
+ },
41
+ "grok-imagine-video-1.5": {
42
+ kind: "range",
43
+ min: GROK_VIDEO_MIN_DURATION,
44
+ max: GROK_VIDEO_MAX_DURATION,
45
+ step: 1,
46
+ unit: "seconds"
47
+ }
48
+ };
49
+ function getGrokVideoDurationOptions(model) {
50
+ return GROK_VIDEO_DURATIONS[model];
51
+ }
52
+ const GROK_VIDEO_IMAGE_TO_VIDEO_ONLY = /* @__PURE__ */ new Set([
53
+ "grok-imagine-video-1.5"
54
+ ]);
55
+ function isImageToVideoOnlyModel(model) {
56
+ return GROK_VIDEO_IMAGE_TO_VIDEO_ONLY.has(model);
57
+ }
58
+ export {
59
+ GROK_VIDEO_DURATIONS,
60
+ GROK_VIDEO_MAX_DURATION,
61
+ GROK_VIDEO_MIN_DURATION,
62
+ getGrokVideoDurationOptions,
63
+ isImageToVideoOnlyModel,
64
+ parseGrokVideoSize,
65
+ validateVideoSize
66
+ };
67
+ //# sourceMappingURL=video-provider-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-provider-options.js","sources":["../../../src/video/video-provider-options.ts"],"sourcesContent":["/**\n * Grok Video Generation Provider Options (xAI Imagine API)\n *\n * Based on https://docs.x.ai/docs/guides/video-generations\n *\n * @experimental Video generation is an experimental feature and may change.\n */\n\nimport type { DurationOptions } from '@tanstack/ai/adapters'\nimport type { GrokVideoModel } from '../model-meta'\n\n/**\n * Aspect ratios accepted by the grok-imagine video models.\n *\n * Note: this is a narrower set than the grok-imagine image models — the\n * video endpoint rejects the phone-screen ratios ('9:19.5', '9:20', …) and\n * 'auto'.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport type GrokVideoAspectRatio =\n | '1:1'\n | '16:9'\n | '9:16'\n | '4:3'\n | '3:4'\n | '3:2'\n | '2:3'\n\n/**\n * Resolution tiers for the grok-imagine video models.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport type GrokVideoResolution = '480p' | '720p' | '1080p'\n\n/**\n * Size strings for grok-imagine video models. The Imagine API is\n * aspect-ratio based rather than pixel-size based; like the grok-imagine\n * image models, the generic `size` option uses an\n * `aspectRatio_resolution` template (\"16:9_720p\") — the resolution suffix\n * is optional (\"16:9\" uses the API default).\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport type GrokVideoSize =\n | GrokVideoAspectRatio\n | `${GrokVideoAspectRatio}_${GrokVideoResolution}`\n\nconst GROK_VIDEO_ASPECT_RATIOS: ReadonlyArray<string> = [\n '1:1',\n '16:9',\n '9:16',\n '4:3',\n '3:4',\n '3:2',\n '2:3',\n]\n\nconst GROK_VIDEO_RESOLUTIONS: ReadonlyArray<string> = ['480p', '720p', '1080p']\n\n/**\n * Video duration limits enforced by the Imagine API (seconds).\n */\nexport const GROK_VIDEO_MIN_DURATION = 1\nexport const GROK_VIDEO_MAX_DURATION = 15\n\n/**\n * Parses a grok video size string into its components.\n * Format: \"aspectRatio\" or \"aspectRatio_resolution\",\n * e.g. \"16:9_720p\" → { aspectRatio: \"16:9\", resolution: \"720p\" }.\n * Returns undefined when the string doesn't match the template.\n */\nexport function parseGrokVideoSize(\n size: string,\n): { aspectRatio: string; resolution?: string } | undefined {\n const match = size.match(/^([\\d.]+:[\\d.]+)(?:_(.+))?$/)\n const [, aspectRatio, resolution] = match ?? []\n if (aspectRatio === undefined) return undefined\n return { aspectRatio, ...(resolution !== undefined && { resolution }) }\n}\n\n/**\n * Validate the `size` template for a given grok video model.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport function validateVideoSize(\n model: string,\n size?: string,\n): asserts size is GrokVideoSize | undefined {\n if (size === undefined) return\n const parsed = parseGrokVideoSize(size)\n if (!parsed || !GROK_VIDEO_ASPECT_RATIOS.includes(parsed.aspectRatio)) {\n throw new Error(\n `Size \"${size}\" is not supported by model \"${model}\". Expected ` +\n `\"aspectRatio\" or \"aspectRatio_resolution\" (e.g. \"16:9_720p\") with ` +\n `aspect ratio one of: ${GROK_VIDEO_ASPECT_RATIOS.join(', ')}`,\n )\n }\n if (\n parsed.resolution !== undefined &&\n !GROK_VIDEO_RESOLUTIONS.includes(parsed.resolution)\n ) {\n throw new Error(\n `Resolution \"${parsed.resolution}\" is not supported by model \"${model}\". ` +\n `Supported resolutions: ${GROK_VIDEO_RESOLUTIONS.join(', ')}`,\n )\n }\n}\n\n/**\n * Per-model duration type. The Imagine API accepts any integer second in the\n * 1–15 range, so this is a continuous range expressed as `number` (a literal\n * union can't represent it). `snapDuration()` coerces a raw seconds value into\n * the valid range at runtime.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport type GrokVideoModelDurationByName = {\n 'grok-imagine-video': number\n 'grok-imagine-video-1.5': number\n}\n\n/**\n * Runtime duration table backing `availableDurations()` / `snapDuration()`.\n * Both grok-imagine video models accept the same continuous 1–15 integer-second\n * range.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport const GROK_VIDEO_DURATIONS: {\n readonly [TModel in GrokVideoModel]: DurationOptions<\n GrokVideoModelDurationByName[TModel]\n >\n} = {\n 'grok-imagine-video': {\n kind: 'range',\n min: GROK_VIDEO_MIN_DURATION,\n max: GROK_VIDEO_MAX_DURATION,\n step: 1,\n unit: 'seconds',\n },\n 'grok-imagine-video-1.5': {\n kind: 'range',\n min: GROK_VIDEO_MIN_DURATION,\n max: GROK_VIDEO_MAX_DURATION,\n step: 1,\n unit: 'seconds',\n },\n}\n\n/**\n * Look up the duration options for a grok video model.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport function getGrokVideoDurationOptions<TModel extends GrokVideoModel>(\n model: TModel,\n): DurationOptions<GrokVideoModelDurationByName[TModel]> {\n return GROK_VIDEO_DURATIONS[model]\n}\n\n/**\n * Provider-specific options for grok video generation. These map directly\n * onto the Imagine API request body and take precedence over the generic\n * `size` / `duration` options when both are provided.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport interface GrokVideoProviderOptions {\n /**\n * Output aspect ratio.\n */\n aspect_ratio?: GrokVideoAspectRatio\n\n /**\n * Output resolution tier.\n */\n resolution?: GrokVideoResolution\n\n /**\n * Video duration in integer seconds (1–15).\n */\n duration?: number\n}\n\n/**\n * Type-only map from model name to its specific provider options.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport type GrokVideoModelProviderOptionsByName = {\n 'grok-imagine-video': GrokVideoProviderOptions\n 'grok-imagine-video-1.5': GrokVideoProviderOptions\n}\n\n/**\n * Type-only map from model name to its supported `size` strings.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport type GrokVideoModelSizeByName = {\n 'grok-imagine-video': GrokVideoSize\n 'grok-imagine-video-1.5': GrokVideoSize\n}\n\n/**\n * Type-only map from model name to the non-text prompt modalities it accepts.\n * Both models accept an `image` prompt part as the starting frame:\n * `grok-imagine-video` (v1.0) does text-to-video and image-to-video, while\n * `grok-imagine-video-1.5` is image-to-video only (the image is required).\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport type GrokVideoModelInputModalitiesByName = {\n 'grok-imagine-video': readonly ['image']\n 'grok-imagine-video-1.5': readonly ['image']\n}\n\n/**\n * Models that only support image-to-video — a starting-frame image is\n * required and text-to-video is rejected by the Imagine API. Used by the\n * adapter to fail fast with a clear message instead of surfacing the raw\n * \"Text-to-video is not supported for this model\" 400.\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nconst GROK_VIDEO_IMAGE_TO_VIDEO_ONLY: ReadonlySet<string> = new Set([\n 'grok-imagine-video-1.5',\n])\n\n/**\n * True when the model only supports image-to-video (a starting frame is\n * required).\n *\n * @experimental Video generation is an experimental feature and may change.\n */\nexport function isImageToVideoOnlyModel(model: string): boolean {\n return GROK_VIDEO_IMAGE_TO_VIDEO_ONLY.has(model)\n}\n"],"names":[],"mappings":"AAiDA,MAAM,2BAAkD;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,yBAAgD,CAAC,QAAQ,QAAQ,OAAO;AAKvE,MAAM,0BAA0B;AAChC,MAAM,0BAA0B;AAQhC,SAAS,mBACd,MAC0D;AAC1D,QAAM,QAAQ,KAAK,MAAM,6BAA6B;AACtD,QAAM,GAAG,aAAa,UAAU,IAAI,SAAS,CAAA;AAC7C,MAAI,gBAAgB,OAAW,QAAO;AACtC,SAAO,EAAE,aAAa,GAAI,eAAe,UAAa,EAAE,aAAW;AACrE;AAOO,SAAS,kBACd,OACA,MAC2C;AAC3C,MAAI,SAAS,OAAW;AACxB,QAAM,SAAS,mBAAmB,IAAI;AACtC,MAAI,CAAC,UAAU,CAAC,yBAAyB,SAAS,OAAO,WAAW,GAAG;AACrE,UAAM,IAAI;AAAA,MACR,SAAS,IAAI,gCAAgC,KAAK,sGAExB,yBAAyB,KAAK,IAAI,CAAC;AAAA,IAAA;AAAA,EAEjE;AACA,MACE,OAAO,eAAe,UACtB,CAAC,uBAAuB,SAAS,OAAO,UAAU,GAClD;AACA,UAAM,IAAI;AAAA,MACR,eAAe,OAAO,UAAU,gCAAgC,KAAK,6BACzC,uBAAuB,KAAK,IAAI,CAAC;AAAA,IAAA;AAAA,EAEjE;AACF;AAsBO,MAAM,uBAIT;AAAA,EACF,sBAAsB;AAAA,IACpB,MAAM;AAAA,IACN,KAAK;AAAA,IACL,KAAK;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA,EAAA;AAAA,EAER,0BAA0B;AAAA,IACxB,MAAM;AAAA,IACN,KAAK;AAAA,IACL,KAAK;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA,EAAA;AAEV;AAOO,SAAS,4BACd,OACuD;AACvD,SAAO,qBAAqB,KAAK;AACnC;AAmEA,MAAM,qDAA0D,IAAI;AAAA,EAClE;AACF,CAAC;AAQM,SAAS,wBAAwB,OAAwB;AAC9D,SAAO,+BAA+B,IAAI,KAAK;AACjD;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-grok",
3
- "version": "0.12.4",
3
+ "version": "0.14.0",
4
4
  "description": "xAI Grok adapter for TanStack AI chat, image generation, realtime, and structured outputs.",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -56,12 +56,12 @@
56
56
  "devDependencies": {
57
57
  "@vitest/coverage-v8": "4.0.14",
58
58
  "vite": "^7.3.3",
59
- "@tanstack/ai": "0.34.0",
60
- "@tanstack/ai-client": "0.18.2"
59
+ "@tanstack/ai": "0.34.1",
60
+ "@tanstack/ai-client": "0.18.3"
61
61
  },
62
62
  "peerDependencies": {
63
63
  "zod": "^4.0.0",
64
- "@tanstack/ai": "^0.34.0"
64
+ "@tanstack/ai": "^0.34.1"
65
65
  },
66
66
  "scripts": {
67
67
  "build": "vite build",
@@ -17,14 +17,14 @@ export type GrokSummarizeModel = (typeof GROK_CHAT_MODELS)[number]
17
17
  * Creates a Grok summarize adapter with explicit API key.
18
18
  * Type resolution happens here at the call site.
19
19
  *
20
- * @param model - The model name (e.g., 'grok-3', 'grok-4')
20
+ * @param model - The model name (e.g., 'grok-build-0.1')
21
21
  * @param apiKey - Your xAI API key
22
22
  * @param config - Optional additional configuration
23
23
  * @returns Configured Grok summarize adapter instance with resolved types
24
24
  *
25
25
  * @example
26
26
  * ```typescript
27
- * const adapter = createGrokSummarize('grok-3', "xai-...");
27
+ * const adapter = createGrokSummarize('grok-build-0.1', "xai-...");
28
28
  * ```
29
29
  */
30
30
  export function createGrokSummarize<TModel extends GrokSummarizeModel>(
@@ -50,7 +50,7 @@ export function createGrokSummarize<TModel extends GrokSummarizeModel>(
50
50
  * - `process.env` (Node.js)
51
51
  * - `window.env` (Browser with injected env)
52
52
  *
53
- * @param model - The model name (e.g., 'grok-3', 'grok-4')
53
+ * @param model - The model name (e.g., 'grok-build-0.1')
54
54
  * @param config - Optional configuration (excluding apiKey which is auto-detected)
55
55
  * @returns Configured Grok summarize adapter instance with resolved types
56
56
  * @throws Error if XAI_API_KEY is not found in environment
@@ -58,7 +58,7 @@ export function createGrokSummarize<TModel extends GrokSummarizeModel>(
58
58
  * @example
59
59
  * ```typescript
60
60
  * // Automatically uses XAI_API_KEY from environment
61
- * const adapter = grokSummarize('grok-3');
61
+ * const adapter = grokSummarize('grok-build-0.1');
62
62
  *
63
63
  * await summarize({
64
64
  * adapter,
@@ -1,16 +1,17 @@
1
1
  import OpenAI from 'openai'
2
- import { OpenAIBaseChatCompletionsTextAdapter } from '@tanstack/openai-base'
2
+ import { OpenAIBaseResponsesTextAdapter } from '@tanstack/openai-base'
3
3
  import { getGrokApiKeyFromEnv, withGrokDefaults } from '../utils/client'
4
- import { GROK_COMBINED_TOOLS_AND_SCHEMA_MODELS } from '../model-meta'
4
+ import { convertToolsToProviderFormat } from '../tools'
5
5
  import type {
6
6
  GROK_CHAT_MODELS,
7
7
  GrokChatModelToolCapabilitiesByName,
8
8
  ResolveInputModalities,
9
9
  ResolveProviderOptions,
10
10
  } from '../model-meta'
11
- import type { Modality } from '@tanstack/ai'
11
+ import type { Modality, TextOptions } from '@tanstack/ai'
12
12
  import type { GrokMessageMetadataByModality } from '../message-types'
13
13
  import type { GrokClientConfig } from '../utils'
14
+ import type { ResponseCreateParams } from 'openai/resources/responses/responses'
14
15
 
15
16
  /**
16
17
  * Resolve tool capabilities for a specific Grok model.
@@ -34,20 +35,21 @@ export type { ExternalTextProviderOptions as GrokTextProviderOptions } from '../
34
35
  * Grok Text (Chat) Adapter
35
36
  *
36
37
  * Tree-shakeable adapter for Grok chat/text completion functionality.
37
- * Uses OpenAI-compatible Chat Completions API (not Responses API).
38
+ * Uses xAI's OpenAI-compatible Responses API.
38
39
  *
39
- * Delegates implementation to {@link OpenAIBaseChatCompletionsTextAdapter}
40
+ * Delegates implementation to {@link OpenAIBaseResponsesTextAdapter}
40
41
  * from `@tanstack/openai-base` and threads Grok-specific tool-capability
41
42
  * typing through the 5th generic of the base class.
42
43
  */
43
44
  export class GrokTextAdapter<
44
45
  TModel extends (typeof GROK_CHAT_MODELS)[number],
45
- TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,
46
+ TProviderOptions extends Record<string, unknown> =
47
+ ResolveProviderOptions<TModel>,
46
48
  TInputModalities extends ReadonlyArray<Modality> =
47
49
  ResolveInputModalities<TModel>,
48
50
  TToolCapabilities extends ReadonlyArray<string> =
49
51
  ResolveToolCapabilities<TModel>,
50
- > extends OpenAIBaseChatCompletionsTextAdapter<
52
+ > extends OpenAIBaseResponsesTextAdapter<
51
53
  TModel,
52
54
  TProviderOptions,
53
55
  TInputModalities,
@@ -61,36 +63,34 @@ export class GrokTextAdapter<
61
63
  super(model, 'grok', new OpenAI(withGrokDefaults(config)))
62
64
  }
63
65
 
64
- /**
65
- * Surfaces xAI reasoning deltas on Grok reasoning models. The DeepSeek-style
66
- * convention puts the chain-of-thought on `delta.reasoning_content`; some
67
- * Grok variants also populate `delta.reasoning`. Reading both keeps
68
- * reasoning flowing through the base's REASONING_* lifecycle for both
69
- * `chatStream` and `structuredOutputStream`.
70
- */
71
- protected override extractReasoning(
72
- chunk: OpenAI.Chat.Completions.ChatCompletionChunk,
73
- ): { text: string } | undefined {
74
- const delta = chunk.choices[0]?.delta as
75
- | { reasoning?: unknown; reasoning_content?: unknown }
76
- | undefined
77
- const raw = delta?.reasoning_content ?? delta?.reasoning
78
- if (typeof raw === 'string' && raw.length > 0) {
79
- return { text: raw }
66
+ protected override mapOptionsToRequest(
67
+ options: TextOptions<TProviderOptions>,
68
+ ): Omit<ResponseCreateParams, 'stream'> {
69
+ const { tools: _baseTools, ...request } = super.mapOptionsToRequest({
70
+ ...options,
71
+ tools: undefined,
72
+ })
73
+ void _baseTools
74
+
75
+ if (this.model === 'grok-build-0.1' && request.reasoning !== undefined) {
76
+ throw new Error(
77
+ 'grok-build-0.1 does not support reasoning modelOptions; omit reasoning for this model.',
78
+ )
80
79
  }
81
- return undefined
82
- }
83
80
 
84
- /**
85
- * Grok's combined tools + schema support is gated to the Grok 4 family
86
- * per xAI's structured-output docs; Grok 2 / 3 reject the combination.
87
- * The wiring on the wire is already correct (inherits the OpenAI Chat
88
- * Completions `response_format: json_schema` attach from the base
89
- * adapter); this override just narrows the capability claim to the
90
- * supported model family.
91
- */
92
- override supportsCombinedToolsAndSchema(): boolean {
93
- return GROK_COMBINED_TOOLS_AND_SCHEMA_MODELS.has(this.model)
81
+ const tools = options.tools
82
+ ? convertToolsToProviderFormat(options.tools)
83
+ : undefined
84
+
85
+ return {
86
+ ...request,
87
+ // xAI recommends encrypted reasoning for reasoning-capable Responses
88
+ // requests; callers can still override either field in modelOptions.
89
+ store: request.store ?? false,
90
+ include: request.include ?? ['reasoning.encrypted_content'],
91
+ ...(tools &&
92
+ tools.length > 0 && { tools: tools as ResponseCreateParams['tools'] }),
93
+ }
94
94
  }
95
95
  }
96
96
 
@@ -98,15 +98,15 @@ export class GrokTextAdapter<
98
98
  * Creates a Grok text adapter with explicit API key.
99
99
  * Type resolution happens here at the call site.
100
100
  *
101
- * @param model - The model name (e.g., 'grok-3', 'grok-4')
101
+ * @param model - The model name (e.g., 'grok-build-0.1')
102
102
  * @param apiKey - Your xAI API key
103
103
  * @param config - Optional additional configuration
104
104
  * @returns Configured Grok text adapter instance with resolved types
105
105
  *
106
106
  * @example
107
107
  * ```typescript
108
- * const adapter = createGrokText('grok-3', "xai-...");
109
- * // adapter has type-safe providerOptions for grok-3
108
+ * const adapter = createGrokText('grok-build-0.1', "xai-...");
109
+ * // adapter has type-safe providerOptions for grok-build-0.1
110
110
  * ```
111
111
  */
112
112
  export function createGrokText<
@@ -127,7 +127,7 @@ export function createGrokText<
127
127
  * - `process.env` (Node.js)
128
128
  * - `window.env` (Browser with injected env)
129
129
  *
130
- * @param model - The model name (e.g., 'grok-3', 'grok-4')
130
+ * @param model - The model name (e.g., 'grok-build-0.1')
131
131
  * @param config - Optional configuration (excluding apiKey which is auto-detected)
132
132
  * @returns Configured Grok text adapter instance with resolved types
133
133
  * @throws Error if XAI_API_KEY is not found in environment
@@ -135,7 +135,7 @@ export function createGrokText<
135
135
  * @example
136
136
  * ```typescript
137
137
  * // Automatically uses XAI_API_KEY from environment
138
- * const adapter = grokText('grok-3');
138
+ * const adapter = grokText('grok-build-0.1');
139
139
  *
140
140
  * const stream = chat({
141
141
  * adapter,