Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1852ed5
fix(xai): a streaming transcription connect failure errors the stream
cunninghamcard-bit Oct 20, 2026
c2c7027
fix(xai): dropping a streaming transcription closes the socket
cunninghamcard-bit Oct 20, 2026
e6084f0
fix(xai): connect while building the stream, as upstream does
cunninghamcard-bit Oct 20, 2026
db4d6dc
Merge branch 'fix/xai-stt-connect-failure-errors-the-stream' into fix…
cunninghamcard-bit Oct 20, 2026
1b7c653
test(xai): mark the existing xAI tests with their upstream cases
cunninghamcard-bit Oct 20, 2026
3f132a0
test(xai): port the remaining upstream xAI tests
cunninghamcard-bit Oct 20, 2026
8c30fae
test(xai): map the xAI sources in layout.toml
cunninghamcard-bit Oct 20, 2026
9bc8bab
Merge master into fix/xai-stt-closes-the-socket-when-the-stream-is-dr…
cunninghamcard-bit Oct 24, 2026
bf71fda
Merge fix/xai-stt-closes-the-socket-when-the-stream-is-dropped into t…
cunninghamcard-bit Oct 24, 2026
8829096
Merge remote-tracking branch 'origin/master' into fix/xai-stt-closes-…
cunninghamcard-bit Oct 25, 2026
4704537
fix(xai): an unpolled transcription stream still closes the socket wh…
cunninghamcard-bit Oct 25, 2026
d049c4d
Merge remote-tracking branch 'origin/fix/xai-stt-closes-the-socket-wh…
cunninghamcard-bit Oct 25, 2026
15571d8
Merge remote-tracking branch 'origin/master' into test/port-the-upstr…
cunninghamcard-bit Oct 25, 2026
c79e7ae
Merge remote-tracking branch 'origin/master' into test/port-the-upstr…
cunninghamcard-bit Oct 25, 2026
b1d2ee9
Merge remote-tracking branch 'origin/master' into test/port-the-upstr…
cunninghamcard-bit Oct 25, 2026
214d326
Merge remote-tracking branch 'origin/master' into test/port-the-upstr…
cunninghamcard-bit Oct 25, 2026
7d99eec
Merge remote-tracking branch 'origin/master' into test/port-the-upstr…
claude Oct 25, 2026
e9ed6ce
Merge remote-tracking branch 'origin/master' into test/port-the-upstr…
cunninghamcard-bit Oct 27, 2026
ef0588d
Merge remote-tracking branch 'origin/master' into test/port-the-upstr…
cunninghamcard-bit Oct 27, 2026
3f26aa1
Merge remote-tracking branch 'origin/master' into test/port-the-upstr…
cunninghamcard-bit Oct 27, 2026
0ca5568
Merge remote-tracking branch 'origin/master' into test/port-the-upstr…
cunninghamcard-bit Oct 27, 2026
f982b63
Merge remote-tracking branch 'origin/master' into test/port-the-upstr…
cunninghamcard-bit Oct 27, 2026
3a6c902
Merge remote-tracking branch 'origin/master' into test/port-the-upstr…
cunninghamcard-bit Oct 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
259 changes: 259 additions & 0 deletions aimux-providers/layout.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2533,3 +2533,262 @@ reason = "only the HuggingFaceResponsesModelId string union; Rust takes the mode
upstream = "src/version.ts"
status = "not-applicable"
reason = "the crate version is used instead (RFC-0046 §4.2)"

[xai]
package = "@ai-sdk/xai"

[[xai.files]]
upstream = "src/files/xai-files-api.ts"
status = "missing"
issue = 275

[[xai.files]]
upstream = "src/files/xai-files-options.ts"
status = "missing"
issue = 275

[[xai.files]]
upstream = "src/files/xai-files.ts"
status = "missing"
issue = 275

[[xai.files]]
upstream = "src/get-response-metadata.ts"
rust = "xai/responses/mod.rs"
status = "mapped"

[xai.files.tests]

[[xai.files]]
upstream = "src/index.ts"
rust = "xai/mod.rs"
status = "mapped"

[xai.files.tests]

[[xai.files]]
upstream = "src/map-xai-finish-reason.ts"
status = "not-applicable"
reason = "used only by xai-batch.ts; Batch is out of scope (docs/aisdk-architecture-alignment.md, 本期范围外)"

[[xai.files]]
upstream = "src/realtime/index.ts"
status = "not-applicable"
reason = "Realtime is out of scope (docs/aisdk-architecture-alignment.md, 本期范围外)"

[[xai.files]]
upstream = "src/realtime/xai-realtime-event-mapper.ts"
status = "not-applicable"
reason = "Realtime is out of scope (docs/aisdk-architecture-alignment.md, 本期范围外)"

[[xai.files]]
upstream = "src/realtime/xai-realtime-model-options.ts"
status = "not-applicable"
reason = "Realtime is out of scope (docs/aisdk-architecture-alignment.md, 本期范围外)"

[[xai.files]]
upstream = "src/realtime/xai-realtime-model.ts"
status = "not-applicable"
reason = "Realtime is out of scope (docs/aisdk-architecture-alignment.md, 本期范围外)"

[[xai.files]]
upstream = "src/responses/convert-to-xai-responses-input.ts"
rust = "xai/responses/convert.rs"
status = "mapped"

[xai.files.tests]

[[xai.files]]
upstream = "src/responses/convert-xai-responses-usage.ts"
rust = "xai/responses/convert.rs"
status = "mapped"

[xai.files.tests]

[[xai.files]]
upstream = "src/responses/map-xai-responses-finish-reason.ts"
rust = "xai/responses/convert.rs"
status = "mapped"

[xai.files.tests]

[[xai.files]]
upstream = "src/responses/xai-responses-api.ts"
rust = "xai/responses/types.rs"
status = "mapped"

[xai.files.tests]

[[xai.files]]
upstream = "src/responses/xai-responses-language-model-options.ts"
rust = "xai/responses/convert.rs"
status = "mapped"

[xai.files.tests]

[[xai.files]]
upstream = "src/responses/xai-responses-language-model.ts"
rust = "xai/responses/mod.rs"
status = "mapped"

[xai.files.tests]

[[xai.files]]
upstream = "src/responses/xai-responses-prepare-tools.ts"
rust = "xai/responses/convert.rs"
status = "mapped"

[xai.files.tests]

[[xai.files]]
upstream = "src/supports-reasoning-effort.ts"
rust = "xai/convert.rs"
status = "mapped"

[xai.files.tests]

[[xai.files]]
upstream = "src/tool/code-execution.ts"
status = "missing"
issue = 275

[[xai.files]]
upstream = "src/tool/file-search.ts"
status = "missing"
issue = 275

[[xai.files]]
upstream = "src/tool/image-generation.ts"
status = "missing"
issue = 275

[[xai.files]]
upstream = "src/tool/index.ts"
status = "missing"
issue = 275

[[xai.files]]
upstream = "src/tool/mcp-server.ts"
status = "missing"
issue = 275

[[xai.files]]
upstream = "src/tool/view-image.ts"
status = "missing"
issue = 275

[[xai.files]]
upstream = "src/tool/view-x-video.ts"
status = "missing"
issue = 275

[[xai.files]]
upstream = "src/tool/web-search.ts"
status = "missing"
issue = 275

[[xai.files]]
upstream = "src/tool/x-search.ts"
status = "missing"
issue = 275

[[xai.files]]
upstream = "src/version.ts"
status = "not-applicable"
reason = "the crate version is used instead (RFC-0046 §4.2)"

[[xai.files]]
upstream = "src/xai-batch.ts"
status = "not-applicable"
reason = "Batch is out of scope (docs/aisdk-architecture-alignment.md, 本期范围外)"

[[xai.files]]
upstream = "src/xai-error.ts"
rust = "xai/mod.rs"
status = "mapped"

[xai.files.tests]

[[xai.files]]
upstream = "src/xai-file-part-options.ts"
rust = "xai/responses/convert.rs"
status = "mapped"

[xai.files.tests]

[[xai.files]]
upstream = "src/xai-image-model-options.ts"
status = "missing"
issue = 275

[[xai.files]]
upstream = "src/xai-image-model.ts"
status = "missing"
issue = 275

[[xai.files]]
upstream = "src/xai-image-settings.ts"
status = "missing"
issue = 275

[[xai.files]]
upstream = "src/xai-provider.ts"
rust = "xai/mod.rs"
status = "mapped"

[xai.files.tests]
sub-feature = [
{ case = "xAIProvider › imageModel › should construct an image model with correct configuration", issue = 275, reason = "aimux has no xAI image model; Provider::image_model answers NoSuchModel" },
{ case = "xAIProvider › imageModel › should pass custom headers to image model", issue = 275, reason = "aimux has no xAI image model; Provider::image_model answers NoSuchModel" },
{ case = "xAIProvider › imageModel › should use custom baseURL for image model", issue = 275, reason = "aimux has no xAI image model; Provider::image_model answers NoSuchModel" },
]

[[xai.files]]
upstream = "src/xai-speech-model-options.ts"
rust = "xai/speech.rs"
status = "mapped"

[xai.files.tests]

[[xai.files]]
upstream = "src/xai-speech-model.ts"
rust = "xai/speech.rs"
status = "mapped"

[xai.files.tests]

[[xai.files]]
upstream = "src/xai-transcription-model-options.ts"
rust = "xai/transcription.rs"
status = "mapped"

[xai.files.tests]

[[xai.files]]
upstream = "src/xai-transcription-model.ts"
rust = "xai/transcription.rs"
status = "mapped"

[xai.files.tests]

[[xai.files]]
upstream = "src/xai-video-model-options.ts"
rust = "xai/video.rs"
status = "mapped"

[xai.files.tests]

[[xai.files]]
upstream = "src/xai-video-model.ts"
rust = "xai/video.rs"
status = "mapped"

[xai.files.tests]
sub-feature = [
{ case = "XaiVideoModel › doStatus › should return error status on expired", issue = 276, reason = "aimux-core VideoOperationStatus has only Pending and Completed; an error status is Err(AiMuxError), which carries no response metadata" },
]

[[xai.files]]
upstream = "src/xai-video-settings.ts"
status = "not-applicable"
reason = "only the XaiVideoModelId string union; Rust takes the model id as &str"
3 changes: 3 additions & 0 deletions aimux-providers/src/xai/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,3 +466,6 @@ impl ProviderDiscovery for XAIProvider {
})
}
}

#[cfg(test)]
mod tests;
98 changes: 98 additions & 0 deletions aimux-providers/src/xai/tests/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//! Upstream `xai-error.test.ts`.

use aimux_core::ApiCallError;
use aimux_core::error::AiMuxError;
use aimux_provider_utils::{FetchResponse, ResponseHandlerInput};
use reqwest::header::{CONTENT_TYPE, HeaderMap, HeaderValue};
use reqwest::{StatusCode, Url};
use serde_json::{Value, json};

use crate::xai::xai_failed_response_handler;

/// The upstream `makeResponse` helper: a 400 with a JSON body.
fn make_response(url: &str, body: &Value) -> FetchResponse {
let mut headers = HeaderMap::new();
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
FetchResponse::from_bytes(
StatusCode::BAD_REQUEST,
headers,
Url::parse(url).expect("valid URL"),
serde_json::to_vec(body).expect("body serializes").into(),
)
}

/// `xaiFailedResponseHandler({ url, requestBodyValues: {}, response })`, the
/// error value it returns.
async fn failed_response_error(url: &str, body: &Value) -> ApiCallError {
let output = xai_failed_response_handler()
.handle(ResponseHandlerInput {
url: url.to_string(),
request_body_values: json!({}),
response: make_response(url, body),
abort_signal: None,
max_json_response_bytes: None,
})
.await
.expect("the failed-response handler builds an error");
match output.value {
AiMuxError::ApiCall(error) => *error,
other => panic!("expected an APICallError, got {other:?}"),
}
}

mod xai_failed_response_handler {
use super::*;

/// upstream: @ai-sdk/xai src/xai-error.test.ts › xaiFailedResponseHandler › extracts message from the nested API error shape
#[tokio::test]
async fn extracts_message_from_the_nested_api_error_shape() {
let error = failed_response_error(
"https://api.x.ai/v1/images/generations",
&json!({
"error": {
"message": "Invalid value: temperature must be between 0 and 2",
"type": "invalid_request_error",
"code": "invalid_value",
},
}),
)
.await;

assert_eq!(
error.message,
"Invalid value: temperature must be between 0 and 2"
);
}

/// upstream: @ai-sdk/xai src/xai-error.test.ts › xaiFailedResponseHandler › extracts message and code from responses api error shape
#[tokio::test]
async fn extracts_message_and_code_from_responses_api_error_shape() {
let error = failed_response_error(
"https://api.x.ai/v1/responses",
&json!({
"code": "Client specified an invalid argument",
"error": "Invalid request content: Each message must have at least one content element.",
}),
)
.await;

assert_eq!(
error.message,
"Client specified an invalid argument: Invalid request content: Each message must have at least one content element."
);
}

/// upstream: @ai-sdk/xai src/xai-error.test.ts › xaiFailedResponseHandler › extracts message from text to speech error shape
#[tokio::test]
async fn extracts_message_from_text_to_speech_error_shape() {
let error = failed_response_error(
"https://api.x.ai/v1/tts",
&json!({
"error": "speed must be between 0.7 and 1.5",
}),
)
.await;

assert_eq!(error.message, "speed must be between 0.7 and 1.5");
}
}
9 changes: 9 additions & 0 deletions aimux-providers/src/xai/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//! Ports of the `@ai-sdk/xai@5.0.12` test files.

mod error;
mod provider;
mod responses;
mod speech_model;
mod supports_reasoning_effort;
mod transcription_model;
mod video_model;
Loading
Loading