@tanstack/ai-grok 0.14.9 → 0.14.10
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.
- package/dist/esm/adapters/image.js +228 -193
- package/dist/esm/adapters/image.js.map +1 -1
- package/dist/esm/adapters/summarize.js +49 -12
- package/dist/esm/adapters/summarize.js.map +1 -1
- package/dist/esm/adapters/text.js +82 -38
- package/dist/esm/adapters/text.js.map +1 -1
- package/dist/esm/adapters/transcription.js +133 -114
- package/dist/esm/adapters/transcription.js.map +1 -1
- package/dist/esm/adapters/tts.js +147 -130
- package/dist/esm/adapters/tts.js.map +1 -1
- package/dist/esm/adapters/video.js +275 -226
- package/dist/esm/adapters/video.js.map +1 -1
- package/dist/esm/image/image-provider-options.js +64 -66
- package/dist/esm/image/image-provider-options.js.map +1 -1
- package/dist/esm/index.js +3 -31
- package/dist/esm/model-meta.js +149 -47
- package/dist/esm/model-meta.js.map +1 -1
- package/dist/esm/realtime/adapter.js +729 -812
- package/dist/esm/realtime/adapter.js.map +1 -1
- package/dist/esm/realtime/index.js +3 -0
- package/dist/esm/realtime/token.js +78 -72
- package/dist/esm/realtime/token.js.map +1 -1
- package/dist/esm/tools/index.js +57 -96
- package/dist/esm/tools/index.js.map +1 -1
- package/dist/esm/utils/audio.js +116 -162
- package/dist/esm/utils/audio.js.map +1 -1
- package/dist/esm/utils/client.js +22 -16
- package/dist/esm/utils/client.js.map +1 -1
- package/dist/esm/utils/index.js +5 -0
- package/dist/esm/utils/schema-converter.js +2 -0
- package/dist/esm/video/video-provider-options.js +83 -57
- package/dist/esm/video/video-provider-options.js.map +1 -1
- package/package.json +8 -8
- package/src/realtime/adapter.ts +1 -1
- package/src/utils/audio.ts +1 -1
- package/dist/esm/index.js.map +0 -1
|
@@ -1,44 +1,88 @@
|
|
|
1
|
+
import { getGrokApiKeyFromEnv, withGrokDefaults } from "../utils/client.js";
|
|
2
|
+
import { convertToolsToProviderFormat } from "../tools/index.js";
|
|
1
3
|
import OpenAI from "openai";
|
|
2
4
|
import { OpenAIBaseResponsesTextAdapter } from "@tanstack/openai-base";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
5
|
+
//#region src/adapters/text.ts
|
|
6
|
+
/**
|
|
7
|
+
* Grok Text (Chat) Adapter
|
|
8
|
+
*
|
|
9
|
+
* Tree-shakeable adapter for Grok chat/text completion functionality.
|
|
10
|
+
* Uses xAI's OpenAI-compatible Responses API.
|
|
11
|
+
*
|
|
12
|
+
* Delegates implementation to {@link OpenAIBaseResponsesTextAdapter}
|
|
13
|
+
* from `@tanstack/openai-base` and threads Grok-specific tool-capability
|
|
14
|
+
* typing through the 5th generic of the base class.
|
|
15
|
+
*/
|
|
16
|
+
var GrokTextAdapter = class extends OpenAIBaseResponsesTextAdapter {
|
|
17
|
+
kind = "text";
|
|
18
|
+
name = "grok";
|
|
19
|
+
constructor(config, model) {
|
|
20
|
+
super(model, "grok", new OpenAI(withGrokDefaults(config)));
|
|
21
|
+
}
|
|
22
|
+
mapOptionsToRequest(options) {
|
|
23
|
+
const { tools: _baseTools, ...request } = super.mapOptionsToRequest({
|
|
24
|
+
...options,
|
|
25
|
+
tools: void 0
|
|
26
|
+
});
|
|
27
|
+
if (this.model === "grok-build-0.1" && request.reasoning !== void 0) throw new Error("grok-build-0.1 does not support reasoning modelOptions; omit reasoning for this model.");
|
|
28
|
+
const tools = options.tools ? convertToolsToProviderFormat(options.tools) : void 0;
|
|
29
|
+
return {
|
|
30
|
+
...request,
|
|
31
|
+
store: request.store ?? false,
|
|
32
|
+
include: request.include ?? ["reasoning.encrypted_content"],
|
|
33
|
+
...tools && tools.length > 0 && { tools }
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Creates a Grok text adapter with explicit API key.
|
|
39
|
+
* Type resolution happens here at the call site.
|
|
40
|
+
*
|
|
41
|
+
* @param model - The model name (e.g., 'grok-build-0.1')
|
|
42
|
+
* @param apiKey - Your xAI API key
|
|
43
|
+
* @param config - Optional additional configuration
|
|
44
|
+
* @returns Configured Grok text adapter instance with resolved types
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* const adapter = createGrokText('grok-build-0.1', "xai-...");
|
|
49
|
+
* // adapter has type-safe providerOptions for grok-build-0.1
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
32
52
|
function createGrokText(model, apiKey, config) {
|
|
33
|
-
|
|
53
|
+
return new GrokTextAdapter({
|
|
54
|
+
apiKey,
|
|
55
|
+
...config
|
|
56
|
+
}, model);
|
|
34
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Creates a Grok text adapter with automatic API key detection from environment variables.
|
|
60
|
+
* Type resolution happens here at the call site.
|
|
61
|
+
*
|
|
62
|
+
* Looks for `XAI_API_KEY` in:
|
|
63
|
+
* - `process.env` (Node.js)
|
|
64
|
+
* - `window.env` (Browser with injected env)
|
|
65
|
+
*
|
|
66
|
+
* @param model - The model name (e.g., 'grok-build-0.1')
|
|
67
|
+
* @param config - Optional configuration (excluding apiKey which is auto-detected)
|
|
68
|
+
* @returns Configured Grok text adapter instance with resolved types
|
|
69
|
+
* @throws Error if XAI_API_KEY is not found in environment
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* // Automatically uses XAI_API_KEY from environment
|
|
74
|
+
* const adapter = grokText('grok-build-0.1');
|
|
75
|
+
*
|
|
76
|
+
* const stream = chat({
|
|
77
|
+
* adapter,
|
|
78
|
+
* messages: [{ role: "user", content: "Hello!" }]
|
|
79
|
+
* });
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
35
82
|
function grokText(model, config) {
|
|
36
|
-
|
|
37
|
-
return createGrokText(model, apiKey, config);
|
|
83
|
+
return createGrokText(model, getGrokApiKeyFromEnv(), config);
|
|
38
84
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
};
|
|
44
|
-
//# sourceMappingURL=text.js.map
|
|
85
|
+
//#endregion
|
|
86
|
+
export { GrokTextAdapter, createGrokText, grokText };
|
|
87
|
+
|
|
88
|
+
//# sourceMappingURL=text.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.js","sources":["../../../src/adapters/text.ts"],"sourcesContent":["import OpenAI from 'openai'\nimport { OpenAIBaseResponsesTextAdapter } from '@tanstack/openai-base'\nimport { getGrokApiKeyFromEnv, withGrokDefaults } from '../utils/client'\nimport { convertToolsToProviderFormat } from '../tools'\nimport type {\n GROK_CHAT_MODELS,\n GrokChatModelToolCapabilitiesByName,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\nimport type { Modality, TextOptions } from '@tanstack/ai'\nimport type { GrokMessageMetadataByModality } from '../message-types'\nimport type { GrokClientConfig } from '../utils/client'\nimport type { ResponseCreateParams } from 'openai/resources/responses/responses'\n\n/**\n * Resolve tool capabilities for a specific Grok model.\n */\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof GrokChatModelToolCapabilitiesByName\n ? NonNullable<GrokChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n/**\n * Configuration for Grok text adapter\n */\nexport interface GrokTextConfig extends GrokClientConfig {}\n\n/**\n * Alias for TextProviderOptions for external use\n */\nexport type { ExternalTextProviderOptions as GrokTextProviderOptions } from '../text/text-provider-options'\n\n/**\n * Grok Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Grok chat/text completion functionality.\n * Uses xAI's OpenAI-compatible Responses API.\n *\n * Delegates implementation to {@link OpenAIBaseResponsesTextAdapter}\n * from `@tanstack/openai-base` and threads Grok-specific tool-capability\n * typing through the 5th generic of the base class.\n */\nexport class GrokTextAdapter<\n TModel extends (typeof GROK_CHAT_MODELS)[number],\n // Use `Record<string, any>` (not `unknown`) to match the OpenAI text\n // adapter: the resolved Grok provider options are a type-alias intersection\n // with no explicit index signature, which is assignable to\n // `Record<string, any>` but not `Record<string, unknown>`. See issue #821.\n TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends OpenAIBaseResponsesTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n GrokMessageMetadataByModality,\n TToolCapabilities\n> {\n override readonly kind = 'text' as const\n override readonly name = 'grok' as const\n\n constructor(config: GrokTextConfig, model: TModel) {\n super(model, 'grok', new OpenAI(withGrokDefaults(config)))\n }\n\n protected override mapOptionsToRequest(\n options: TextOptions<TProviderOptions>,\n ): Omit<ResponseCreateParams, 'stream'> {\n const { tools: _baseTools, ...request } = super.mapOptionsToRequest({\n ...options,\n tools: undefined,\n })\n void _baseTools\n\n if (this.model === 'grok-build-0.1' && request.reasoning !== undefined) {\n throw new Error(\n 'grok-build-0.1 does not support reasoning modelOptions; omit reasoning for this model.',\n )\n }\n\n const tools = options.tools\n ? convertToolsToProviderFormat(options.tools)\n : undefined\n\n return {\n ...request,\n // xAI recommends encrypted reasoning for reasoning-capable Responses\n // requests; callers can still override either field in modelOptions.\n store: request.store ?? false,\n include: request.include ?? ['reasoning.encrypted_content'],\n ...(tools &&\n tools.length > 0 && { tools: tools as ResponseCreateParams['tools'] }),\n }\n }\n}\n\n/**\n * Creates a Grok text adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'grok-build-0.1')\n * @param apiKey - Your xAI API key\n * @param config - Optional additional configuration\n * @returns Configured Grok text adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGrokText('grok-build-0.1', \"xai-...\");\n * // adapter has type-safe providerOptions for grok-build-0.1\n * ```\n */\nexport function createGrokText<\n TModel extends (typeof GROK_CHAT_MODELS)[number],\n>(\n model: TModel,\n apiKey: string,\n config?: Omit<GrokTextConfig, 'apiKey'>,\n): GrokTextAdapter<TModel> {\n return new GrokTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Grok text adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * Looks for `XAI_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'grok-build-0.1')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Grok text adapter instance with resolved types\n * @throws Error if XAI_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses XAI_API_KEY from environment\n * const adapter = grokText('grok-build-0.1');\n *\n * const stream = chat({\n * adapter,\n * messages: [{ role: \"user\", content: \"Hello!\" }]\n * });\n * ```\n */\nexport function grokText<TModel extends (typeof GROK_CHAT_MODELS)[number]>(\n model: TModel,\n config?: Omit<GrokTextConfig, 'apiKey'>,\n): GrokTextAdapter<TModel> {\n const apiKey = getGrokApiKeyFromEnv()\n return createGrokText(model, apiKey, config)\n}\n"],"
|
|
1
|
+
{"version":3,"file":"text.js","names":[],"sources":["../../../src/adapters/text.ts"],"sourcesContent":["import OpenAI from 'openai'\nimport { OpenAIBaseResponsesTextAdapter } from '@tanstack/openai-base'\nimport { getGrokApiKeyFromEnv, withGrokDefaults } from '../utils/client'\nimport { convertToolsToProviderFormat } from '../tools'\nimport type {\n GROK_CHAT_MODELS,\n GrokChatModelToolCapabilitiesByName,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\nimport type { Modality, TextOptions } from '@tanstack/ai'\nimport type { GrokMessageMetadataByModality } from '../message-types'\nimport type { GrokClientConfig } from '../utils/client'\nimport type { ResponseCreateParams } from 'openai/resources/responses/responses'\n\n/**\n * Resolve tool capabilities for a specific Grok model.\n */\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof GrokChatModelToolCapabilitiesByName\n ? NonNullable<GrokChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n/**\n * Configuration for Grok text adapter\n */\nexport interface GrokTextConfig extends GrokClientConfig {}\n\n/**\n * Alias for TextProviderOptions for external use\n */\nexport type { ExternalTextProviderOptions as GrokTextProviderOptions } from '../text/text-provider-options'\n\n/**\n * Grok Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Grok chat/text completion functionality.\n * Uses xAI's OpenAI-compatible Responses API.\n *\n * Delegates implementation to {@link OpenAIBaseResponsesTextAdapter}\n * from `@tanstack/openai-base` and threads Grok-specific tool-capability\n * typing through the 5th generic of the base class.\n */\nexport class GrokTextAdapter<\n TModel extends (typeof GROK_CHAT_MODELS)[number],\n // Use `Record<string, any>` (not `unknown`) to match the OpenAI text\n // adapter: the resolved Grok provider options are a type-alias intersection\n // with no explicit index signature, which is assignable to\n // `Record<string, any>` but not `Record<string, unknown>`. See issue #821.\n TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends OpenAIBaseResponsesTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n GrokMessageMetadataByModality,\n TToolCapabilities\n> {\n override readonly kind = 'text' as const\n override readonly name = 'grok' as const\n\n constructor(config: GrokTextConfig, model: TModel) {\n super(model, 'grok', new OpenAI(withGrokDefaults(config)))\n }\n\n protected override mapOptionsToRequest(\n options: TextOptions<TProviderOptions>,\n ): Omit<ResponseCreateParams, 'stream'> {\n const { tools: _baseTools, ...request } = super.mapOptionsToRequest({\n ...options,\n tools: undefined,\n })\n void _baseTools\n\n if (this.model === 'grok-build-0.1' && request.reasoning !== undefined) {\n throw new Error(\n 'grok-build-0.1 does not support reasoning modelOptions; omit reasoning for this model.',\n )\n }\n\n const tools = options.tools\n ? convertToolsToProviderFormat(options.tools)\n : undefined\n\n return {\n ...request,\n // xAI recommends encrypted reasoning for reasoning-capable Responses\n // requests; callers can still override either field in modelOptions.\n store: request.store ?? false,\n include: request.include ?? ['reasoning.encrypted_content'],\n ...(tools &&\n tools.length > 0 && { tools: tools as ResponseCreateParams['tools'] }),\n }\n }\n}\n\n/**\n * Creates a Grok text adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'grok-build-0.1')\n * @param apiKey - Your xAI API key\n * @param config - Optional additional configuration\n * @returns Configured Grok text adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGrokText('grok-build-0.1', \"xai-...\");\n * // adapter has type-safe providerOptions for grok-build-0.1\n * ```\n */\nexport function createGrokText<\n TModel extends (typeof GROK_CHAT_MODELS)[number],\n>(\n model: TModel,\n apiKey: string,\n config?: Omit<GrokTextConfig, 'apiKey'>,\n): GrokTextAdapter<TModel> {\n return new GrokTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Grok text adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * Looks for `XAI_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'grok-build-0.1')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Grok text adapter instance with resolved types\n * @throws Error if XAI_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses XAI_API_KEY from environment\n * const adapter = grokText('grok-build-0.1');\n *\n * const stream = chat({\n * adapter,\n * messages: [{ role: \"user\", content: \"Hello!\" }]\n * });\n * ```\n */\nexport function grokText<TModel extends (typeof GROK_CHAT_MODELS)[number]>(\n model: TModel,\n config?: Omit<GrokTextConfig, 'apiKey'>,\n): GrokTextAdapter<TModel> {\n const apiKey = getGrokApiKeyFromEnv()\n return createGrokText(model, apiKey, config)\n}\n"],"mappings":";;;;;;;;;;;;;;;AA2CA,IAAa,kBAAb,cAWU,+BAMR;CACA,OAAyB;CACzB,OAAyB;CAEzB,YAAY,QAAwB,OAAe;EACjD,MAAM,OAAO,QAAQ,IAAI,OAAO,iBAAiB,MAAM,CAAC,CAAC;CAC3D;CAEA,oBACE,SACsC;EACtC,MAAM,EAAE,OAAO,YAAY,GAAG,YAAY,MAAM,oBAAoB;GAClE,GAAG;GACH,OAAO,KAAA;EACT,CAAC;EAGD,IAAI,KAAK,UAAU,oBAAoB,QAAQ,cAAc,KAAA,GAC3D,MAAM,IAAI,MACR,wFACF;EAGF,MAAM,QAAQ,QAAQ,QAClB,6BAA6B,QAAQ,KAAK,IAC1C,KAAA;EAEJ,OAAO;GACL,GAAG;GAGH,OAAO,QAAQ,SAAS;GACxB,SAAS,QAAQ,WAAW,CAAC,6BAA6B;GAC1D,GAAI,SACF,MAAM,SAAS,KAAK,EAAS,MAAuC;EACxE;CACF;AACF;;;;;;;;;;;;;;;;AAiBA,SAAgB,eAGd,OACA,QACA,QACyB;CACzB,OAAO,IAAI,gBAAgB;EAAE;EAAQ,GAAG;CAAO,GAAG,KAAK;AACzD;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,SAAgB,SACd,OACA,QACyB;CAEzB,OAAO,eAAe,OADP,qBACc,GAAQ,MAAM;AAC7C"}
|
|
@@ -1,122 +1,141 @@
|
|
|
1
|
-
import { BaseTranscriptionAdapter } from "@tanstack/ai/adapters";
|
|
2
|
-
import { generateId } from "@tanstack/ai-utils";
|
|
3
1
|
import { getGrokApiKeyFromEnv } from "../utils/client.js";
|
|
4
|
-
import "@tanstack/openai-base";
|
|
5
2
|
import { toAudioFile } from "../utils/audio.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
3
|
+
import { generateId } from "../utils/index.js";
|
|
4
|
+
import { BaseTranscriptionAdapter } from "@tanstack/ai/adapters";
|
|
5
|
+
//#region src/adapters/transcription.ts
|
|
6
|
+
var DEFAULT_GROK_BASE_URL = "https://api.x.ai/v1";
|
|
7
|
+
/**
|
|
8
|
+
* Grok Speech-to-Text Adapter.
|
|
9
|
+
*
|
|
10
|
+
* Talks to `POST {baseURL}/stt` per
|
|
11
|
+
* https://docs.x.ai/developers/rest-api-reference/inference/voice
|
|
12
|
+
*/
|
|
13
|
+
var GrokTranscriptionAdapter = class extends BaseTranscriptionAdapter {
|
|
14
|
+
name = "grok";
|
|
15
|
+
apiKey;
|
|
16
|
+
baseURL;
|
|
17
|
+
defaultHeaders;
|
|
18
|
+
constructor(config, model) {
|
|
19
|
+
super(model, config);
|
|
20
|
+
this.apiKey = config.apiKey;
|
|
21
|
+
this.baseURL = (config.baseURL ?? DEFAULT_GROK_BASE_URL).replace(/\/+$/, "");
|
|
22
|
+
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
23
|
+
}
|
|
24
|
+
async transcribe(options) {
|
|
25
|
+
const { logger } = options;
|
|
26
|
+
const { model, audio, language, modelOptions } = options;
|
|
27
|
+
logger.request(`activity=generateTranscription provider=grok model=${model}`, {
|
|
28
|
+
provider: "grok",
|
|
29
|
+
model
|
|
30
|
+
});
|
|
31
|
+
const form = buildTranscriptionFormData({
|
|
32
|
+
file: toAudioFile(audio, modelOptions?.audio_format),
|
|
33
|
+
language,
|
|
34
|
+
modelOptions
|
|
35
|
+
});
|
|
36
|
+
try {
|
|
37
|
+
const response = await fetch(`${this.baseURL}/stt`, {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers: {
|
|
40
|
+
...this.defaultHeaders,
|
|
41
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
42
|
+
},
|
|
43
|
+
body: form
|
|
44
|
+
});
|
|
45
|
+
if (!response.ok) {
|
|
46
|
+
const errorText = await response.text();
|
|
47
|
+
throw new Error(`Grok transcription request failed: ${response.status} ${errorText}`);
|
|
48
|
+
}
|
|
49
|
+
const data = await response.json();
|
|
50
|
+
const words = data.words?.map((w) => {
|
|
51
|
+
const tw = {
|
|
52
|
+
word: w.text,
|
|
53
|
+
start: w.start,
|
|
54
|
+
end: w.end
|
|
55
|
+
};
|
|
56
|
+
if (w.confidence !== void 0) tw.confidence = w.confidence;
|
|
57
|
+
if (w.speaker !== void 0) tw.speaker = w.speaker;
|
|
58
|
+
return tw;
|
|
59
|
+
});
|
|
60
|
+
const resolvedLanguage = data.language ?? language;
|
|
61
|
+
const usage = data.duration !== void 0 && data.duration > 0 ? {
|
|
62
|
+
promptTokens: 0,
|
|
63
|
+
completionTokens: 0,
|
|
64
|
+
totalTokens: 0,
|
|
65
|
+
durationSeconds: data.duration
|
|
66
|
+
} : void 0;
|
|
67
|
+
return {
|
|
68
|
+
id: generateId(this.name),
|
|
69
|
+
model,
|
|
70
|
+
text: data.text,
|
|
71
|
+
...resolvedLanguage !== void 0 && { language: resolvedLanguage },
|
|
72
|
+
duration: data.duration,
|
|
73
|
+
...words !== void 0 && { words },
|
|
74
|
+
...usage !== void 0 && { usage }
|
|
75
|
+
};
|
|
76
|
+
} catch (error) {
|
|
77
|
+
logger.errors("grok.transcribe fatal", {
|
|
78
|
+
error,
|
|
79
|
+
source: "grok.transcribe"
|
|
80
|
+
});
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Build the multipart/form-data body for `POST /v1/stt`, coercing SDK-level
|
|
87
|
+
* model options into xAI's wire format (booleans as `'true'`/`'false'`
|
|
88
|
+
* strings, numeric fields stringified, etc.).
|
|
89
|
+
*
|
|
90
|
+
* Wire-field mapping:
|
|
91
|
+
* - `modelOptions.inverse_text_normalization` → `format` (xAI's chosen
|
|
92
|
+
* wire-field name for the ITN boolean; the SDK surfaces it under the
|
|
93
|
+
* clearer `inverse_text_normalization` key).
|
|
94
|
+
* - `modelOptions.audio_format`, `sample_rate`, `multichannel`, `channels`,
|
|
95
|
+
* `diarize` map to same-named form fields.
|
|
96
|
+
*/
|
|
81
97
|
function buildTranscriptionFormData(options) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
form.set(
|
|
94
|
-
"format",
|
|
95
|
-
modelOptions.inverse_text_normalization ? "true" : "false"
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
if (modelOptions?.multichannel !== void 0) {
|
|
99
|
-
form.set("multichannel", modelOptions.multichannel ? "true" : "false");
|
|
100
|
-
}
|
|
101
|
-
if (modelOptions?.channels !== void 0) {
|
|
102
|
-
form.set("channels", String(modelOptions.channels));
|
|
103
|
-
}
|
|
104
|
-
if (modelOptions?.diarize !== void 0) {
|
|
105
|
-
form.set("diarize", modelOptions.diarize ? "true" : "false");
|
|
106
|
-
}
|
|
107
|
-
return form;
|
|
98
|
+
const { file, language, modelOptions } = options;
|
|
99
|
+
const form = new FormData();
|
|
100
|
+
form.set("file", file);
|
|
101
|
+
if (language) form.set("language", language);
|
|
102
|
+
if (modelOptions?.audio_format !== void 0) form.set("audio_format", modelOptions.audio_format);
|
|
103
|
+
if (modelOptions?.sample_rate !== void 0) form.set("sample_rate", String(modelOptions.sample_rate));
|
|
104
|
+
if (modelOptions?.inverse_text_normalization !== void 0) form.set("format", modelOptions.inverse_text_normalization ? "true" : "false");
|
|
105
|
+
if (modelOptions?.multichannel !== void 0) form.set("multichannel", modelOptions.multichannel ? "true" : "false");
|
|
106
|
+
if (modelOptions?.channels !== void 0) form.set("channels", String(modelOptions.channels));
|
|
107
|
+
if (modelOptions?.diarize !== void 0) form.set("diarize", modelOptions.diarize ? "true" : "false");
|
|
108
|
+
return form;
|
|
108
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Creates a Grok transcription adapter with an explicit API key.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```typescript
|
|
115
|
+
* const adapter = createGrokTranscription('grok-stt', 'xai-...')
|
|
116
|
+
* const result = await generateTranscription({
|
|
117
|
+
* adapter,
|
|
118
|
+
* audio: audioFile,
|
|
119
|
+
* language: 'en',
|
|
120
|
+
* })
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
109
123
|
function createGrokTranscription(model, apiKey, config) {
|
|
110
|
-
|
|
124
|
+
return new GrokTranscriptionAdapter({
|
|
125
|
+
apiKey,
|
|
126
|
+
...config
|
|
127
|
+
}, model);
|
|
111
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Creates a Grok transcription adapter, reading the API key from
|
|
131
|
+
* `XAI_API_KEY` in the environment.
|
|
132
|
+
*
|
|
133
|
+
* @throws Error if `XAI_API_KEY` is not set.
|
|
134
|
+
*/
|
|
112
135
|
function grokTranscription(model, config) {
|
|
113
|
-
|
|
114
|
-
return createGrokTranscription(model, apiKey, config);
|
|
136
|
+
return createGrokTranscription(model, getGrokApiKeyFromEnv(), config);
|
|
115
137
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
grokTranscription
|
|
121
|
-
};
|
|
122
|
-
//# sourceMappingURL=transcription.js.map
|
|
138
|
+
//#endregion
|
|
139
|
+
export { GrokTranscriptionAdapter, buildTranscriptionFormData, createGrokTranscription, grokTranscription };
|
|
140
|
+
|
|
141
|
+
//# sourceMappingURL=transcription.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transcription.js","sources":["../../../src/adapters/transcription.ts"],"sourcesContent":["import { BaseTranscriptionAdapter } from '@tanstack/ai/adapters'\nimport { generateId, getGrokApiKeyFromEnv, toAudioFile } from '../utils'\nimport type {\n TokenUsage,\n TranscriptionOptions,\n TranscriptionResult,\n TranscriptionWord,\n} from '@tanstack/ai'\nimport type { GrokTranscriptionModel } from '../model-meta'\nimport type { GrokTranscriptionProviderOptions } from '../audio/transcription-provider-options'\n\n/**\n * Grok-specific extension of `TranscriptionWord` that surfaces the extra\n * fields xAI returns when diarization / confidence are enabled. The base\n * cross-provider `TranscriptionWord` contract doesn't include these, so\n * callers who know they're using Grok can narrow with:\n *\n * ```ts\n * const words = result.words as Array<GrokTranscriptionWord> | undefined\n * ```\n */\nexport interface GrokTranscriptionWord extends TranscriptionWord {\n /** Model confidence for the word, when xAI returns one. */\n confidence?: number\n /** Speaker index, populated when `modelOptions.diarize === true`. */\n speaker?: number\n}\n\nconst DEFAULT_GROK_BASE_URL = 'https://api.x.ai/v1'\n\n/**\n * Configuration for the Grok transcription adapter.\n *\n * Uses direct `fetch` rather than the OpenAI SDK because xAI's `/v1/stt`\n * endpoint is not OpenAI-compatible.\n */\nexport interface GrokTranscriptionConfig {\n apiKey: string\n baseURL?: string\n /** Additional headers to merge into every request (e.g., test IDs). */\n defaultHeaders?: Record<string, string>\n}\n\n/**\n * xAI STT response shape from `POST /v1/stt`.\n * Grok returns word-level timestamps only; no segment array.\n */\ninterface GrokSTTWord {\n text: string\n start: number\n end: number\n confidence?: number\n speaker?: number\n}\n\ninterface GrokSTTResponse {\n text: string\n language?: string\n duration?: number\n words?: Array<GrokSTTWord>\n channels?: Array<unknown>\n}\n\n/**\n * Grok Speech-to-Text Adapter.\n *\n * Talks to `POST {baseURL}/stt` per\n * https://docs.x.ai/developers/rest-api-reference/inference/voice\n */\nexport class GrokTranscriptionAdapter<\n TModel extends GrokTranscriptionModel,\n> extends BaseTranscriptionAdapter<TModel, GrokTranscriptionProviderOptions> {\n readonly name = 'grok' as const\n\n private readonly apiKey: string\n private readonly baseURL: string\n private readonly defaultHeaders: Record<string, string>\n\n constructor(config: GrokTranscriptionConfig, model: TModel) {\n super(model, config)\n this.apiKey = config.apiKey\n this.baseURL = (config.baseURL ?? DEFAULT_GROK_BASE_URL).replace(/\\/+$/, '')\n this.defaultHeaders = config.defaultHeaders ?? {}\n }\n\n async transcribe(\n options: TranscriptionOptions<GrokTranscriptionProviderOptions>,\n ): Promise<TranscriptionResult> {\n const { logger } = options\n const { model, audio, language, modelOptions } = options\n\n logger.request(\n `activity=generateTranscription provider=grok model=${model}`,\n { provider: 'grok', model },\n )\n\n const file = toAudioFile(audio, modelOptions?.audio_format)\n const form = buildTranscriptionFormData({ file, language, modelOptions })\n\n try {\n const response = await fetch(`${this.baseURL}/stt`, {\n method: 'POST',\n headers: {\n // `defaultHeaders` first so Authorization always wins.\n ...this.defaultHeaders,\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: form,\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new Error(\n `Grok transcription request failed: ${response.status} ${errorText}`,\n )\n }\n\n const data = (await response.json()) as GrokSTTResponse\n\n const words: Array<TranscriptionWord> | undefined = data.words?.map(\n (w) => {\n // Construct a GrokTranscriptionWord so that `confidence` and\n // `speaker` (when xAI returns them under `diarize` / confidence\n // mode) are preserved on the result. The returned array is typed\n // as `Array<TranscriptionWord>` per the cross-provider contract;\n // callers who want the extras narrow via `as Array<GrokTranscriptionWord>`.\n const tw: GrokTranscriptionWord = {\n word: w.text,\n start: w.start,\n end: w.end,\n }\n if (w.confidence !== undefined) tw.confidence = w.confidence\n if (w.speaker !== undefined) tw.speaker = w.speaker\n return tw\n },\n )\n\n const resolvedLanguage = data.language ?? language\n // xAI's /v1/stt response carries no token counts — STT is duration-billed —\n // so surface the audio duration as `durationSeconds`, mirroring the\n // whisper-1 path in the OpenAI transcription adapter.\n const usage: TokenUsage | undefined =\n data.duration !== undefined && data.duration > 0\n ? {\n promptTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n durationSeconds: data.duration,\n }\n : undefined\n return {\n id: generateId(this.name),\n model,\n text: data.text,\n ...(resolvedLanguage !== undefined && { language: resolvedLanguage }),\n duration: data.duration,\n ...(words !== undefined && { words }),\n ...(usage !== undefined && { usage }),\n }\n } catch (error) {\n logger.errors('grok.transcribe fatal', {\n error,\n source: 'grok.transcribe',\n })\n throw error\n }\n }\n}\n\n/**\n * Build the multipart/form-data body for `POST /v1/stt`, coercing SDK-level\n * model options into xAI's wire format (booleans as `'true'`/`'false'`\n * strings, numeric fields stringified, etc.).\n *\n * Wire-field mapping:\n * - `modelOptions.inverse_text_normalization` → `format` (xAI's chosen\n * wire-field name for the ITN boolean; the SDK surfaces it under the\n * clearer `inverse_text_normalization` key).\n * - `modelOptions.audio_format`, `sample_rate`, `multichannel`, `channels`,\n * `diarize` map to same-named form fields.\n */\nexport function buildTranscriptionFormData(options: {\n file: File\n language: string | undefined\n modelOptions: GrokTranscriptionProviderOptions | undefined\n}): FormData {\n const { file, language, modelOptions } = options\n const form = new FormData()\n form.set('file', file)\n if (language) form.set('language', language)\n if (modelOptions?.audio_format !== undefined) {\n form.set('audio_format', modelOptions.audio_format)\n }\n if (modelOptions?.sample_rate !== undefined) {\n form.set('sample_rate', String(modelOptions.sample_rate))\n }\n if (modelOptions?.inverse_text_normalization !== undefined) {\n form.set(\n 'format',\n modelOptions.inverse_text_normalization ? 'true' : 'false',\n )\n }\n if (modelOptions?.multichannel !== undefined) {\n form.set('multichannel', modelOptions.multichannel ? 'true' : 'false')\n }\n if (modelOptions?.channels !== undefined) {\n form.set('channels', String(modelOptions.channels))\n }\n if (modelOptions?.diarize !== undefined) {\n form.set('diarize', modelOptions.diarize ? 'true' : 'false')\n }\n return form\n}\n\n/**\n * Creates a Grok transcription adapter with an explicit API key.\n *\n * @example\n * ```typescript\n * const adapter = createGrokTranscription('grok-stt', 'xai-...')\n * const result = await generateTranscription({\n * adapter,\n * audio: audioFile,\n * language: 'en',\n * })\n * ```\n */\nexport function createGrokTranscription<TModel extends GrokTranscriptionModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GrokTranscriptionConfig, 'apiKey'>,\n): GrokTranscriptionAdapter<TModel> {\n return new GrokTranscriptionAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Grok transcription adapter, reading the API key from\n * `XAI_API_KEY` in the environment.\n *\n * @throws Error if `XAI_API_KEY` is not set.\n */\nexport function grokTranscription<TModel extends GrokTranscriptionModel>(\n model: TModel,\n config?: Omit<GrokTranscriptionConfig, 'apiKey'>,\n): GrokTranscriptionAdapter<TModel> {\n const apiKey = getGrokApiKeyFromEnv()\n return createGrokTranscription(model, apiKey, config)\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"transcription.js","names":[],"sources":["../../../src/adapters/transcription.ts"],"sourcesContent":["import { BaseTranscriptionAdapter } from '@tanstack/ai/adapters'\nimport { generateId, getGrokApiKeyFromEnv, toAudioFile } from '../utils'\nimport type {\n TokenUsage,\n TranscriptionOptions,\n TranscriptionResult,\n TranscriptionWord,\n} from '@tanstack/ai'\nimport type { GrokTranscriptionModel } from '../model-meta'\nimport type { GrokTranscriptionProviderOptions } from '../audio/transcription-provider-options'\n\n/**\n * Grok-specific extension of `TranscriptionWord` that surfaces the extra\n * fields xAI returns when diarization / confidence are enabled. The base\n * cross-provider `TranscriptionWord` contract doesn't include these, so\n * callers who know they're using Grok can narrow with:\n *\n * ```ts\n * const words = result.words as Array<GrokTranscriptionWord> | undefined\n * ```\n */\nexport interface GrokTranscriptionWord extends TranscriptionWord {\n /** Model confidence for the word, when xAI returns one. */\n confidence?: number\n /** Speaker index, populated when `modelOptions.diarize === true`. */\n speaker?: number\n}\n\nconst DEFAULT_GROK_BASE_URL = 'https://api.x.ai/v1'\n\n/**\n * Configuration for the Grok transcription adapter.\n *\n * Uses direct `fetch` rather than the OpenAI SDK because xAI's `/v1/stt`\n * endpoint is not OpenAI-compatible.\n */\nexport interface GrokTranscriptionConfig {\n apiKey: string\n baseURL?: string\n /** Additional headers to merge into every request (e.g., test IDs). */\n defaultHeaders?: Record<string, string>\n}\n\n/**\n * xAI STT response shape from `POST /v1/stt`.\n * Grok returns word-level timestamps only; no segment array.\n */\ninterface GrokSTTWord {\n text: string\n start: number\n end: number\n confidence?: number\n speaker?: number\n}\n\ninterface GrokSTTResponse {\n text: string\n language?: string\n duration?: number\n words?: Array<GrokSTTWord>\n channels?: Array<unknown>\n}\n\n/**\n * Grok Speech-to-Text Adapter.\n *\n * Talks to `POST {baseURL}/stt` per\n * https://docs.x.ai/developers/rest-api-reference/inference/voice\n */\nexport class GrokTranscriptionAdapter<\n TModel extends GrokTranscriptionModel,\n> extends BaseTranscriptionAdapter<TModel, GrokTranscriptionProviderOptions> {\n readonly name = 'grok' as const\n\n private readonly apiKey: string\n private readonly baseURL: string\n private readonly defaultHeaders: Record<string, string>\n\n constructor(config: GrokTranscriptionConfig, model: TModel) {\n super(model, config)\n this.apiKey = config.apiKey\n this.baseURL = (config.baseURL ?? DEFAULT_GROK_BASE_URL).replace(/\\/+$/, '')\n this.defaultHeaders = config.defaultHeaders ?? {}\n }\n\n async transcribe(\n options: TranscriptionOptions<GrokTranscriptionProviderOptions>,\n ): Promise<TranscriptionResult> {\n const { logger } = options\n const { model, audio, language, modelOptions } = options\n\n logger.request(\n `activity=generateTranscription provider=grok model=${model}`,\n { provider: 'grok', model },\n )\n\n const file = toAudioFile(audio, modelOptions?.audio_format)\n const form = buildTranscriptionFormData({ file, language, modelOptions })\n\n try {\n const response = await fetch(`${this.baseURL}/stt`, {\n method: 'POST',\n headers: {\n // `defaultHeaders` first so Authorization always wins.\n ...this.defaultHeaders,\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: form,\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new Error(\n `Grok transcription request failed: ${response.status} ${errorText}`,\n )\n }\n\n const data = (await response.json()) as GrokSTTResponse\n\n const words: Array<TranscriptionWord> | undefined = data.words?.map(\n (w) => {\n // Construct a GrokTranscriptionWord so that `confidence` and\n // `speaker` (when xAI returns them under `diarize` / confidence\n // mode) are preserved on the result. The returned array is typed\n // as `Array<TranscriptionWord>` per the cross-provider contract;\n // callers who want the extras narrow via `as Array<GrokTranscriptionWord>`.\n const tw: GrokTranscriptionWord = {\n word: w.text,\n start: w.start,\n end: w.end,\n }\n if (w.confidence !== undefined) tw.confidence = w.confidence\n if (w.speaker !== undefined) tw.speaker = w.speaker\n return tw\n },\n )\n\n const resolvedLanguage = data.language ?? language\n // xAI's /v1/stt response carries no token counts — STT is duration-billed —\n // so surface the audio duration as `durationSeconds`, mirroring the\n // whisper-1 path in the OpenAI transcription adapter.\n const usage: TokenUsage | undefined =\n data.duration !== undefined && data.duration > 0\n ? {\n promptTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n durationSeconds: data.duration,\n }\n : undefined\n return {\n id: generateId(this.name),\n model,\n text: data.text,\n ...(resolvedLanguage !== undefined && { language: resolvedLanguage }),\n duration: data.duration,\n ...(words !== undefined && { words }),\n ...(usage !== undefined && { usage }),\n }\n } catch (error) {\n logger.errors('grok.transcribe fatal', {\n error,\n source: 'grok.transcribe',\n })\n throw error\n }\n }\n}\n\n/**\n * Build the multipart/form-data body for `POST /v1/stt`, coercing SDK-level\n * model options into xAI's wire format (booleans as `'true'`/`'false'`\n * strings, numeric fields stringified, etc.).\n *\n * Wire-field mapping:\n * - `modelOptions.inverse_text_normalization` → `format` (xAI's chosen\n * wire-field name for the ITN boolean; the SDK surfaces it under the\n * clearer `inverse_text_normalization` key).\n * - `modelOptions.audio_format`, `sample_rate`, `multichannel`, `channels`,\n * `diarize` map to same-named form fields.\n */\nexport function buildTranscriptionFormData(options: {\n file: File\n language: string | undefined\n modelOptions: GrokTranscriptionProviderOptions | undefined\n}): FormData {\n const { file, language, modelOptions } = options\n const form = new FormData()\n form.set('file', file)\n if (language) form.set('language', language)\n if (modelOptions?.audio_format !== undefined) {\n form.set('audio_format', modelOptions.audio_format)\n }\n if (modelOptions?.sample_rate !== undefined) {\n form.set('sample_rate', String(modelOptions.sample_rate))\n }\n if (modelOptions?.inverse_text_normalization !== undefined) {\n form.set(\n 'format',\n modelOptions.inverse_text_normalization ? 'true' : 'false',\n )\n }\n if (modelOptions?.multichannel !== undefined) {\n form.set('multichannel', modelOptions.multichannel ? 'true' : 'false')\n }\n if (modelOptions?.channels !== undefined) {\n form.set('channels', String(modelOptions.channels))\n }\n if (modelOptions?.diarize !== undefined) {\n form.set('diarize', modelOptions.diarize ? 'true' : 'false')\n }\n return form\n}\n\n/**\n * Creates a Grok transcription adapter with an explicit API key.\n *\n * @example\n * ```typescript\n * const adapter = createGrokTranscription('grok-stt', 'xai-...')\n * const result = await generateTranscription({\n * adapter,\n * audio: audioFile,\n * language: 'en',\n * })\n * ```\n */\nexport function createGrokTranscription<TModel extends GrokTranscriptionModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GrokTranscriptionConfig, 'apiKey'>,\n): GrokTranscriptionAdapter<TModel> {\n return new GrokTranscriptionAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Grok transcription adapter, reading the API key from\n * `XAI_API_KEY` in the environment.\n *\n * @throws Error if `XAI_API_KEY` is not set.\n */\nexport function grokTranscription<TModel extends GrokTranscriptionModel>(\n model: TModel,\n config?: Omit<GrokTranscriptionConfig, 'apiKey'>,\n): GrokTranscriptionAdapter<TModel> {\n const apiKey = getGrokApiKeyFromEnv()\n return createGrokTranscription(model, apiKey, config)\n}\n"],"mappings":"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"}
|