From 67ff31104fdd29e63fbe2a19a19186176e2e19d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Fri, 14 Nov 2025 19:52:13 +0100 Subject: [PATCH] chore: Bump version to 0.1.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix: Skip empty/whitespace text content blocks - Fix: Validate function call arguments and skip malformed calls - Fix: Skip outputs for skipped function calls to maintain consistency - Resolves Anthropic API errors: - "messages: text content blocks must contain non-whitespace text" - "Extra data: line 1 column 26 (char 25)" (invalid JSON) - "unexpected `tool_use_id` found in `tool_result` blocks" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- llmx-rs/Cargo.lock | 82 +++++++++---------- llmx-rs/Cargo.toml | 2 +- .../app-server/tests/common/mcp_process.rs | 2 +- llmx-rs/app-server/tests/suite/user_agent.rs | 2 +- llmx-rs/core/src/chat_completions.rs | 18 ++++ .../mcp-server/tests/common/mcp_process.rs | 4 +- ...tatus_snapshot_includes_monthly_limit.snap | 2 +- ...s_snapshot_includes_reasoning_details.snap | 2 +- ...s_snapshot_shows_empty_limits_message.snap | 2 +- ...snapshot_shows_missing_limits_message.snap | 2 +- ...s_snapshot_shows_stale_limits_message.snap | 2 +- ...snapshot_truncates_in_narrow_terminal.snap | 2 +- 12 files changed, 70 insertions(+), 52 deletions(-) diff --git a/llmx-rs/Cargo.lock b/llmx-rs/Cargo.lock index c2d34038..acf396b0 100644 --- a/llmx-rs/Cargo.lock +++ b/llmx-rs/Cargo.lock @@ -178,7 +178,7 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "app_test_support" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "assert_cmd", @@ -945,7 +945,7 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core_test_support" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "assert_cmd", @@ -2822,7 +2822,7 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "llmx-ansi-escape" -version = "0.1.2" +version = "0.1.4" dependencies = [ "ansi-to-tui", "ratatui", @@ -2831,7 +2831,7 @@ dependencies = [ [[package]] name = "llmx-app-server" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "app_test_support", @@ -2866,7 +2866,7 @@ dependencies = [ [[package]] name = "llmx-app-server-protocol" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "clap", @@ -2884,7 +2884,7 @@ dependencies = [ [[package]] name = "llmx-apply-patch" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "assert_cmd", @@ -2899,7 +2899,7 @@ dependencies = [ [[package]] name = "llmx-arg0" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "dotenvy", @@ -2912,7 +2912,7 @@ dependencies = [ [[package]] name = "llmx-async-utils" -version = "0.1.2" +version = "0.1.4" dependencies = [ "async-trait", "pretty_assertions", @@ -2936,7 +2936,7 @@ dependencies = [ [[package]] name = "llmx-backend-openapi-models" -version = "0.1.2" +version = "0.1.4" dependencies = [ "serde", "serde_json", @@ -2945,7 +2945,7 @@ dependencies = [ [[package]] name = "llmx-chatgpt" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "clap", @@ -2960,7 +2960,7 @@ dependencies = [ [[package]] name = "llmx-cli" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "assert_cmd", @@ -3000,7 +3000,7 @@ dependencies = [ [[package]] name = "llmx-cloud-tasks" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "async-trait", @@ -3026,7 +3026,7 @@ dependencies = [ [[package]] name = "llmx-cloud-tasks-client" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "async-trait", @@ -3041,7 +3041,7 @@ dependencies = [ [[package]] name = "llmx-common" -version = "0.1.2" +version = "0.1.4" dependencies = [ "clap", "llmx-app-server-protocol", @@ -3053,7 +3053,7 @@ dependencies = [ [[package]] name = "llmx-core" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "askama", @@ -3134,7 +3134,7 @@ dependencies = [ [[package]] name = "llmx-exec" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "assert_cmd", @@ -3167,7 +3167,7 @@ dependencies = [ [[package]] name = "llmx-execpolicy" -version = "0.1.2" +version = "0.1.4" dependencies = [ "allocative", "anyhow", @@ -3187,7 +3187,7 @@ dependencies = [ [[package]] name = "llmx-feedback" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "llmx-protocol", @@ -3198,7 +3198,7 @@ dependencies = [ [[package]] name = "llmx-file-search" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "clap", @@ -3211,7 +3211,7 @@ dependencies = [ [[package]] name = "llmx-git" -version = "0.1.2" +version = "0.1.4" dependencies = [ "assert_matches", "once_cell", @@ -3227,7 +3227,7 @@ dependencies = [ [[package]] name = "llmx-keyring-store" -version = "0.1.2" +version = "0.1.4" dependencies = [ "keyring", "tracing", @@ -3235,7 +3235,7 @@ dependencies = [ [[package]] name = "llmx-linux-sandbox" -version = "0.1.2" +version = "0.1.4" dependencies = [ "clap", "landlock", @@ -3248,7 +3248,7 @@ dependencies = [ [[package]] name = "llmx-login" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "base64", @@ -3272,7 +3272,7 @@ dependencies = [ [[package]] name = "llmx-mcp-server" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "assert_cmd", @@ -3299,7 +3299,7 @@ dependencies = [ [[package]] name = "llmx-ollama" -version = "0.1.2" +version = "0.1.4" dependencies = [ "assert_matches", "async-stream", @@ -3315,7 +3315,7 @@ dependencies = [ [[package]] name = "llmx-otel" -version = "0.1.2" +version = "0.1.4" dependencies = [ "chrono", "eventsource-stream", @@ -3336,14 +3336,14 @@ dependencies = [ [[package]] name = "llmx-process-hardening" -version = "0.1.2" +version = "0.1.4" dependencies = [ "libc", ] [[package]] name = "llmx-protocol" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "base64", @@ -3369,7 +3369,7 @@ dependencies = [ [[package]] name = "llmx-responses-api-proxy" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "clap", @@ -3385,7 +3385,7 @@ dependencies = [ [[package]] name = "llmx-rmcp-client" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "axum", @@ -3414,7 +3414,7 @@ dependencies = [ [[package]] name = "llmx-stdio-to-uds" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "assert_cmd", @@ -3425,7 +3425,7 @@ dependencies = [ [[package]] name = "llmx-tui" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "arboard", @@ -3490,7 +3490,7 @@ dependencies = [ [[package]] name = "llmx-utils-cache" -version = "0.1.2" +version = "0.1.4" dependencies = [ "lru", "sha1", @@ -3499,7 +3499,7 @@ dependencies = [ [[package]] name = "llmx-utils-image" -version = "0.1.2" +version = "0.1.4" dependencies = [ "base64", "image", @@ -3511,7 +3511,7 @@ dependencies = [ [[package]] name = "llmx-utils-json-to-toml" -version = "0.1.2" +version = "0.1.4" dependencies = [ "pretty_assertions", "serde_json", @@ -3520,7 +3520,7 @@ dependencies = [ [[package]] name = "llmx-utils-pty" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "portable-pty", @@ -3529,7 +3529,7 @@ dependencies = [ [[package]] name = "llmx-utils-readiness" -version = "0.1.2" +version = "0.1.4" dependencies = [ "assert_matches", "async-trait", @@ -3540,11 +3540,11 @@ dependencies = [ [[package]] name = "llmx-utils-string" -version = "0.1.2" +version = "0.1.4" [[package]] name = "llmx-utils-tokenizer" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "pretty_assertions", @@ -3655,7 +3655,7 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "mcp-types" -version = "0.1.2" +version = "0.1.4" dependencies = [ "schemars 0.8.22", "serde", @@ -3665,7 +3665,7 @@ dependencies = [ [[package]] name = "mcp_test_support" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "assert_cmd", diff --git a/llmx-rs/Cargo.toml b/llmx-rs/Cargo.toml index 729c222f..380cdd91 100644 --- a/llmx-rs/Cargo.toml +++ b/llmx-rs/Cargo.toml @@ -43,7 +43,7 @@ members = [ resolver = "2" [workspace.package] -version = "0.1.3" +version = "0.1.4" # Track the edition for all workspace crates in one place. Individual # crates can still override this value, but keeping it here means new # crates created with `cargo new -w ...` automatically inherit the 2024 diff --git a/llmx-rs/app-server/tests/common/mcp_process.rs b/llmx-rs/app-server/tests/common/mcp_process.rs index 493ef18c..860fbc08 100644 --- a/llmx-rs/app-server/tests/common/mcp_process.rs +++ b/llmx-rs/app-server/tests/common/mcp_process.rs @@ -138,7 +138,7 @@ impl McpProcess { client_info: ClientInfo { name: "llmx-app-server-tests".to_string(), title: None, - version: "0.1.3".to_string(), + version: "0.1.4".to_string(), }, })?); let req_id = self.send_request("initialize", params).await?; diff --git a/llmx-rs/app-server/tests/suite/user_agent.rs b/llmx-rs/app-server/tests/suite/user_agent.rs index a1f9b8f3..b9ca8783 100644 --- a/llmx-rs/app-server/tests/suite/user_agent.rs +++ b/llmx-rs/app-server/tests/suite/user_agent.rs @@ -26,7 +26,7 @@ async fn get_user_agent_returns_current_llmx_user_agent() -> Result<()> { let os_info = os_info::get(); let user_agent = format!( - "llmx_cli_rs/0.1.3 ({} {}; {}) {} (llmx-app-server-tests; 0.1.3)", + "llmx_cli_rs/0.1.4 ({} {}; {}) {} (llmx-app-server-tests; 0.1.4)", os_info.os_type(), os_info.version(), os_info.architecture().unwrap_or("unknown"), diff --git a/llmx-rs/core/src/chat_completions.rs b/llmx-rs/core/src/chat_completions.rs index 10b708ca..d5d7a947 100644 --- a/llmx-rs/core/src/chat_completions.rs +++ b/llmx-rs/core/src/chat_completions.rs @@ -161,6 +161,9 @@ pub(crate) async fn stream_chat_completions( // aggregated assistant message was recorded alongside an earlier partial). let mut last_assistant_text: Option = None; + // Track call_ids of skipped function calls so we can also skip their outputs + let mut skipped_call_ids: std::collections::HashSet = std::collections::HashSet::new(); + for (idx, item) in input.iter().enumerate() { match item { ResponseItem::Message { role, content, .. } => { @@ -227,6 +230,15 @@ pub(crate) async fn stream_chat_completions( call_id, .. } => { + // Validate that arguments is valid JSON before sending to API + // If invalid, skip this function call to avoid API errors + if serde_json::from_str::(arguments).is_err() { + debug!("Skipping malformed function call with invalid JSON arguments: {}", arguments); + // Track this call_id so we can also skip its corresponding output + skipped_call_ids.insert(call_id.clone()); + continue; + } + let mut msg = json!({ "role": "assistant", "content": null, @@ -271,6 +283,12 @@ pub(crate) async fn stream_chat_completions( messages.push(msg); } ResponseItem::FunctionCallOutput { call_id, output } => { + // Skip outputs for function calls that were skipped due to malformed arguments + if skipped_call_ids.contains(call_id) { + debug!("Skipping function call output for skipped call_id: {}", call_id); + continue; + } + // Prefer structured content items when available (e.g., images) // otherwise fall back to the legacy plain-string content. let content_value = if let Some(items) = &output.content_items { diff --git a/llmx-rs/mcp-server/tests/common/mcp_process.rs b/llmx-rs/mcp-server/tests/common/mcp_process.rs index db7e140d..e46d2f2e 100644 --- a/llmx-rs/mcp-server/tests/common/mcp_process.rs +++ b/llmx-rs/mcp-server/tests/common/mcp_process.rs @@ -144,7 +144,7 @@ impl McpProcess { let initialized = self.read_jsonrpc_message().await?; let os_info = os_info::get(); let user_agent = format!( - "llmx_cli_rs/0.1.3 ({} {}; {}) {} (elicitation test; 0.0.0)", + "llmx_cli_rs/0.1.4 ({} {}; {}) {} (elicitation test; 0.0.0)", os_info.os_type(), os_info.version(), os_info.architecture().unwrap_or("unknown"), @@ -163,7 +163,7 @@ impl McpProcess { "serverInfo": { "name": "llmx-mcp-server", "title": "LLMX", - "version": "0.1.3", + "version": "0.1.4", "user_agent": user_agent }, "protocolVersion": mcp_types::MCP_SCHEMA_VERSION diff --git a/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_includes_monthly_limit.snap b/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_includes_monthly_limit.snap index 65566e5d..e1921e81 100644 --- a/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_includes_monthly_limit.snap +++ b/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_includes_monthly_limit.snap @@ -5,7 +5,7 @@ expression: sanitized /status ╭───────────────────────────────────────────────────────────────────────────╮ -│ >_ LLMX (v0.1.3) │ +│ >_ LLMX (v0.1.4) │ │ │ │ Visit https://chatgpt.com/llmx/settings/usage for up-to-date │ │ information on rate limits and credits │ diff --git a/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_includes_reasoning_details.snap b/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_includes_reasoning_details.snap index 87f0bd7d..7e248db7 100644 --- a/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_includes_reasoning_details.snap +++ b/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_includes_reasoning_details.snap @@ -5,7 +5,7 @@ expression: sanitized /status ╭─────────────────────────────────────────────────────────────────╮ -│ >_ LLMX (v0.1.3) │ +│ >_ LLMX (v0.1.4) │ │ │ │ Visit https://chatgpt.com/llmx/settings/usage for up-to-date │ │ information on rate limits and credits │ diff --git a/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_shows_empty_limits_message.snap b/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_shows_empty_limits_message.snap index 2cfce215..e36d886e 100644 --- a/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_shows_empty_limits_message.snap +++ b/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_shows_empty_limits_message.snap @@ -5,7 +5,7 @@ expression: sanitized /status ╭──────────────────────────────────────────────────────────────╮ -│ >_ LLMX (v0.1.3) │ +│ >_ LLMX (v0.1.4) │ │ │ │ Visit https://chatgpt.com/llmx/settings/usage for up-to-date │ │ information on rate limits and credits │ diff --git a/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_shows_missing_limits_message.snap b/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_shows_missing_limits_message.snap index 2cfce215..e36d886e 100644 --- a/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_shows_missing_limits_message.snap +++ b/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_shows_missing_limits_message.snap @@ -5,7 +5,7 @@ expression: sanitized /status ╭──────────────────────────────────────────────────────────────╮ -│ >_ LLMX (v0.1.3) │ +│ >_ LLMX (v0.1.4) │ │ │ │ Visit https://chatgpt.com/llmx/settings/usage for up-to-date │ │ information on rate limits and credits │ diff --git a/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_shows_stale_limits_message.snap b/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_shows_stale_limits_message.snap index 9ec72123..a872bc05 100644 --- a/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_shows_stale_limits_message.snap +++ b/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_shows_stale_limits_message.snap @@ -5,7 +5,7 @@ expression: sanitized /status ╭───────────────────────────────────────────────────────────────────╮ -│ >_ LLMX (v0.1.3) │ +│ >_ LLMX (v0.1.4) │ │ │ │ Visit https://chatgpt.com/llmx/settings/usage for up-to-date │ │ information on rate limits and credits │ diff --git a/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_truncates_in_narrow_terminal.snap b/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_truncates_in_narrow_terminal.snap index 50632973..264e0b3d 100644 --- a/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_truncates_in_narrow_terminal.snap +++ b/llmx-rs/tui/src/status/snapshots/llmx_tui__status__tests__status_snapshot_truncates_in_narrow_terminal.snap @@ -5,7 +5,7 @@ expression: sanitized /status ╭────────────────────────────────────────────╮ -│ >_ LLMX (v0.1.3) │ +│ >_ LLMX (v0.1.4) │ │ │ │ Visit https://chatgpt.com/llmx/settings/ │ │ usage for up-to-date │