@ai-sdk/xai
Advanced tools
+4
-0
@@ -68,2 +68,4 @@ import { z } from 'zod/v4'; | ||
| error: z.ZodString; | ||
| }, z.core.$strip>, z.ZodObject<{ | ||
| error: z.ZodString; | ||
| }, z.core.$strip>]>; | ||
@@ -222,2 +224,4 @@ type XaiErrorData = z.infer<typeof xaiErrorDataSchema>; | ||
| textNormalization?: boolean | null | undefined; | ||
| withTimestamps?: boolean | null | undefined; | ||
| replace?: Record<string, string> | null | undefined; | ||
| }>; | ||
@@ -224,0 +228,0 @@ type XaiSpeechModelOptions = InferSchema<typeof xaiSpeechModelOptionsSchema>; |
+1
-1
| { | ||
| "name": "@ai-sdk/xai", | ||
| "version": "4.0.39", | ||
| "version": "4.0.40", | ||
| "type": "module", | ||
@@ -5,0 +5,0 @@ "license": "Apache-2.0", |
+12
-2
@@ -18,5 +18,11 @@ import { createJsonErrorResponseHandler } from '@ai-sdk/provider-utils'; | ||
| // Text to Speech error shape, e.g. {"error":"speed must be between 0.7 and 1.5"} | ||
| const speechErrorSchema = z.object({ | ||
| error: z.string(), | ||
| }); | ||
| export const xaiErrorDataSchema = z.union([ | ||
| chatCompletionsErrorSchema, | ||
| responsesErrorSchema, | ||
| speechErrorSchema, | ||
| ]); | ||
@@ -28,4 +34,8 @@ | ||
| errorSchema: xaiErrorDataSchema, | ||
| errorToMessage: data => | ||
| 'code' in data ? `${data.code}: ${data.error}` : data.error.message, | ||
| errorToMessage: data => { | ||
| if (typeof data.error === 'string') { | ||
| return 'code' in data ? `${data.code}: ${data.error}` : data.error; | ||
| } | ||
| return data.error.message; | ||
| }, | ||
| }); |
@@ -49,2 +49,16 @@ import { | ||
| textNormalization: z.boolean().nullish(), | ||
| /** | ||
| * Return character-level timing metadata alongside the audio. When | ||
| * enabled, the response carries per-character start/end times and the | ||
| * total duration, exposed via `providerMetadata.xai`. | ||
| */ | ||
| withTimestamps: z.boolean().nullish(), | ||
| /** | ||
| * Map of phrases to spoken substitutions applied before synthesis. | ||
| * Values may be respellings (`{ 'Acme Mobile': 'Acme Mobull' }`) or IPA | ||
| * phonetics (`{ nginx: '/ˈɛndʒɪn ˈɛks/' }`). | ||
| */ | ||
| replace: z.record(z.string(), z.string()).nullish(), | ||
| }), | ||
@@ -51,0 +65,0 @@ ), |
| import type { SharedV4Warning, SpeechModelV4 } from '@ai-sdk/provider'; | ||
| import { | ||
| combineHeaders, | ||
| convertBase64ToUint8Array, | ||
| createBinaryResponseHandler, | ||
| createJsonResponseHandler, | ||
| parseProviderOptions, | ||
@@ -14,2 +16,3 @@ postJsonToApi, | ||
| } from '@ai-sdk/provider-utils'; | ||
| import { z } from 'zod/v4'; | ||
| import { xaiFailedResponseHandler } from './xai-error'; | ||
@@ -126,5 +129,11 @@ import { xaiSpeechModelOptionsSchema } from './xai-speech-model-options'; | ||
| text_normalization: xaiOptions?.textNormalization, | ||
| with_timestamps: xaiOptions?.withTimestamps, | ||
| replace: xaiOptions?.replace, | ||
| }; | ||
| return { requestBody, warnings }; | ||
| return { | ||
| requestBody, | ||
| warnings, | ||
| withTimestamps: xaiOptions?.withTimestamps === true, | ||
| }; | ||
| } | ||
@@ -136,9 +145,11 @@ | ||
| const currentDate = this.config._internal?.currentDate?.() ?? new Date(); | ||
| const { requestBody, warnings } = await this.getArgs(options); | ||
| const { requestBody, warnings, withTimestamps } = | ||
| await this.getArgs(options); | ||
| const { | ||
| value: audio, | ||
| responseHeaders, | ||
| rawValue: rawResponse, | ||
| } = await postJsonToApi({ | ||
| // With `with_timestamps` the API returns a JSON envelope carrying | ||
| // base64-encoded audio plus character-level timings instead of raw | ||
| // audio bytes. | ||
| const { value, responseHeaders, rawValue } = await postJsonToApi< | ||
| Uint8Array | XaiSpeechTimestampsResponse | ||
| >({ | ||
| url: `${this.config.baseURL}/tts`, | ||
@@ -151,3 +162,5 @@ headers: combineHeaders( | ||
| failedResponseHandler: xaiFailedResponseHandler, | ||
| successfulResponseHandler: createBinaryResponseHandler(), | ||
| successfulResponseHandler: withTimestamps | ||
| ? createJsonResponseHandler(xaiSpeechTimestampsResponseSchema) | ||
| : createBinaryResponseHandler(), | ||
| abortSignal: options.abortSignal, | ||
@@ -157,2 +170,19 @@ fetch: this.config.fetch, | ||
| let audio: Uint8Array; | ||
| let envelope: XaiSpeechTimestampsResponse | undefined; | ||
| if (value instanceof Uint8Array) { | ||
| audio = value; | ||
| } else { | ||
| envelope = value; | ||
| // Empty audio is returned as-is so the core layer throws | ||
| // NoSpeechGeneratedError. | ||
| audio = | ||
| envelope.audio != null | ||
| ? convertBase64ToUint8Array(envelope.audio) | ||
| : new Uint8Array(0); | ||
| } | ||
| // xAI returns a trace id on every response (success and error). | ||
| const traceId = responseHeaders?.['x-trace-id']; | ||
| return { | ||
@@ -168,6 +198,44 @@ audio, | ||
| headers: responseHeaders, | ||
| body: rawResponse, | ||
| body: rawValue, | ||
| }, | ||
| providerMetadata: { | ||
| xai: { | ||
| ...(traceId != null ? { traceId } : {}), | ||
| ...(envelope?.duration != null | ||
| ? { duration: envelope.duration } | ||
| : {}), | ||
| ...(envelope?.content_type != null | ||
| ? { contentType: envelope.content_type } | ||
| : {}), | ||
| ...(envelope?.audio_timestamps != null | ||
| ? { | ||
| audioTimestamps: { | ||
| graphChars: envelope.audio_timestamps.graph_chars, | ||
| graphTimes: envelope.audio_timestamps.graph_times, | ||
| }, | ||
| } | ||
| : {}), | ||
| }, | ||
| }, | ||
| }; | ||
| } | ||
| } | ||
| // Minimal schema for the `with_timestamps` JSON envelope: only the fields | ||
| // the implementation reads, with `.nullish()` so provider API changes don't | ||
| // break parsing. | ||
| const xaiSpeechTimestampsResponseSchema = z.object({ | ||
| audio: z.string().nullish(), | ||
| content_type: z.string().nullish(), | ||
| duration: z.number().nullish(), | ||
| audio_timestamps: z | ||
| .object({ | ||
| graph_chars: z.array(z.string()), | ||
| graph_times: z.array(z.tuple([z.number(), z.number()])), | ||
| }) | ||
| .nullish(), | ||
| }); | ||
| type XaiSpeechTimestampsResponse = z.infer< | ||
| typeof xaiSpeechTimestampsResponseSchema | ||
| >; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
877950
1.44%12424
1.19%