fix: Handle empty assistant messages as turn completion
Some checks failed
rust-ci / Tests — ubuntu-24.04 - x86_64-unknown-linux-gnu (push) Has been cancelled
rust-ci / Tests — ubuntu-24.04-arm - aarch64-unknown-linux-gnu (push) Has been cancelled
rust-ci / Tests — windows-11-arm - aarch64-pc-windows-msvc (push) Has been cancelled
rust-ci / Tests — windows-latest - x86_64-pc-windows-msvc (push) Has been cancelled
rust-ci / CI results (required) (push) Has been cancelled
ci / build-test (push) Failing after 4m50s
Codespell / Check for spelling errors (push) Successful in 4s
rust-ci / Detect changed areas (push) Has been cancelled
rust-ci / Format / etc (push) Has been cancelled
rust-ci / cargo shear (push) Has been cancelled
rust-ci / Lint/Build — macos-14 - aarch64-apple-darwin (push) Has been cancelled
rust-ci / Lint/Build — macos-14 - x86_64-apple-darwin (push) Has been cancelled
rust-ci / Lint/Build — ubuntu-24.04 - x86_64-unknown-linux-gnu (push) Has been cancelled
rust-ci / Lint/Build — ubuntu-24.04 - x86_64-unknown-linux-musl (push) Has been cancelled
rust-ci / Lint/Build — ubuntu-24.04-arm - aarch64-unknown-linux-gnu (push) Has been cancelled
rust-ci / Lint/Build — ubuntu-24.04-arm - aarch64-unknown-linux-musl (push) Has been cancelled
rust-ci / Lint/Build — windows-11-arm - aarch64-pc-windows-msvc (push) Has been cancelled
rust-ci / Lint/Build — windows-latest - x86_64-pc-windows-msvc (push) Has been cancelled
rust-ci / Lint/Build — macos-14 - aarch64-apple-darwin (release) (push) Has been cancelled
rust-ci / Lint/Build — ubuntu-24.04 - x86_64-unknown-linux-musl (release) (push) Has been cancelled
rust-ci / Lint/Build — windows-11-arm - aarch64-pc-windows-msvc (release) (push) Has been cancelled
rust-ci / Lint/Build — windows-latest - x86_64-pc-windows-msvc (release) (push) Has been cancelled
rust-ci / Tests — macos-14 - aarch64-apple-darwin (push) Has been cancelled
sdk / sdks (push) Has been cancelled
Some checks failed
rust-ci / Tests — ubuntu-24.04 - x86_64-unknown-linux-gnu (push) Has been cancelled
rust-ci / Tests — ubuntu-24.04-arm - aarch64-unknown-linux-gnu (push) Has been cancelled
rust-ci / Tests — windows-11-arm - aarch64-pc-windows-msvc (push) Has been cancelled
rust-ci / Tests — windows-latest - x86_64-pc-windows-msvc (push) Has been cancelled
rust-ci / CI results (required) (push) Has been cancelled
ci / build-test (push) Failing after 4m50s
Codespell / Check for spelling errors (push) Successful in 4s
rust-ci / Detect changed areas (push) Has been cancelled
rust-ci / Format / etc (push) Has been cancelled
rust-ci / cargo shear (push) Has been cancelled
rust-ci / Lint/Build — macos-14 - aarch64-apple-darwin (push) Has been cancelled
rust-ci / Lint/Build — macos-14 - x86_64-apple-darwin (push) Has been cancelled
rust-ci / Lint/Build — ubuntu-24.04 - x86_64-unknown-linux-gnu (push) Has been cancelled
rust-ci / Lint/Build — ubuntu-24.04 - x86_64-unknown-linux-musl (push) Has been cancelled
rust-ci / Lint/Build — ubuntu-24.04-arm - aarch64-unknown-linux-gnu (push) Has been cancelled
rust-ci / Lint/Build — ubuntu-24.04-arm - aarch64-unknown-linux-musl (push) Has been cancelled
rust-ci / Lint/Build — windows-11-arm - aarch64-pc-windows-msvc (push) Has been cancelled
rust-ci / Lint/Build — windows-latest - x86_64-pc-windows-msvc (push) Has been cancelled
rust-ci / Lint/Build — macos-14 - aarch64-apple-darwin (release) (push) Has been cancelled
rust-ci / Lint/Build — ubuntu-24.04 - x86_64-unknown-linux-musl (release) (push) Has been cancelled
rust-ci / Lint/Build — windows-11-arm - aarch64-pc-windows-msvc (release) (push) Has been cancelled
rust-ci / Lint/Build — windows-latest - x86_64-pc-windows-msvc (release) (push) Has been cancelled
rust-ci / Tests — macos-14 - aarch64-apple-darwin (push) Has been cancelled
sdk / sdks (push) Has been cancelled
- When the API returns an empty assistant message (content: []), treat it as turn completion signal - This fixes the "working" hang that occurs after tool calls when the API stream ends with an empty message - Updated parse_agent_message to return None for empty content - Fixes issue where llmx would hang indefinitely waiting for content that never comes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -54,7 +54,7 @@ fn parse_user_message(message: &[ContentItem]) -> Option<UserMessageItem> {
|
|||||||
Some(UserMessageItem::new(&content))
|
Some(UserMessageItem::new(&content))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_agent_message(id: Option<&String>, message: &[ContentItem]) -> AgentMessageItem {
|
fn parse_agent_message(id: Option<&String>, message: &[ContentItem]) -> Option<AgentMessageItem> {
|
||||||
let mut content: Vec<AgentMessageContent> = Vec::new();
|
let mut content: Vec<AgentMessageContent> = Vec::new();
|
||||||
for content_item in message.iter() {
|
for content_item in message.iter() {
|
||||||
match content_item {
|
match content_item {
|
||||||
@@ -69,18 +69,23 @@ fn parse_agent_message(id: Option<&String>, message: &[ContentItem]) -> AgentMes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the message has no content, return None to signal turn completion
|
||||||
|
// This happens when the API ends a turn with an empty assistant message (e.g., after tool calls)
|
||||||
|
if content.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let id = id.cloned().unwrap_or_else(|| Uuid::new_v4().to_string());
|
let id = id.cloned().unwrap_or_else(|| Uuid::new_v4().to_string());
|
||||||
AgentMessageItem { id, content }
|
Some(AgentMessageItem { id, content })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_turn_item(item: &ResponseItem) -> Option<TurnItem> {
|
pub fn parse_turn_item(item: &ResponseItem) -> Option<TurnItem> {
|
||||||
match item {
|
match item {
|
||||||
ResponseItem::Message { role, content, id } => match role.as_str() {
|
ResponseItem::Message { role, content, id } => match role.as_str() {
|
||||||
"user" => parse_user_message(content).map(TurnItem::UserMessage),
|
"user" => parse_user_message(content).map(TurnItem::UserMessage),
|
||||||
"assistant" => Some(TurnItem::AgentMessage(parse_agent_message(
|
"assistant" => parse_agent_message(id.as_ref(), content)
|
||||||
id.as_ref(),
|
.map(TurnItem::AgentMessage),
|
||||||
content,
|
|
||||||
))),
|
|
||||||
"system" => None,
|
"system" => None,
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -294,6 +294,7 @@ pub fn built_in_model_providers() -> HashMap<String, ModelProviderInfo> {
|
|||||||
request_max_retries: None,
|
request_max_retries: None,
|
||||||
stream_max_retries: None,
|
stream_max_retries: None,
|
||||||
stream_idle_timeout_ms: None,
|
stream_idle_timeout_ms: None,
|
||||||
|
max_tokens: None,
|
||||||
max_tokens: None,
|
max_tokens: None,
|
||||||
requires_openai_auth: false,
|
requires_openai_auth: false,
|
||||||
},
|
},
|
||||||
@@ -335,6 +336,7 @@ pub fn built_in_model_providers() -> HashMap<String, ModelProviderInfo> {
|
|||||||
request_max_retries: None,
|
request_max_retries: None,
|
||||||
stream_max_retries: None,
|
stream_max_retries: None,
|
||||||
stream_idle_timeout_ms: None,
|
stream_idle_timeout_ms: None,
|
||||||
|
max_tokens: None,
|
||||||
max_tokens: None,
|
max_tokens: None,
|
||||||
requires_openai_auth: true,
|
requires_openai_auth: true,
|
||||||
},
|
},
|
||||||
@@ -381,6 +383,7 @@ pub fn create_oss_provider_with_base_url(base_url: &str) -> ModelProviderInfo {
|
|||||||
request_max_retries: None,
|
request_max_retries: None,
|
||||||
stream_max_retries: None,
|
stream_max_retries: None,
|
||||||
stream_idle_timeout_ms: None,
|
stream_idle_timeout_ms: None,
|
||||||
|
max_tokens: None,
|
||||||
max_tokens: None,
|
max_tokens: None,
|
||||||
requires_openai_auth: false,
|
requires_openai_auth: false,
|
||||||
}
|
}
|
||||||
@@ -422,6 +425,7 @@ base_url = "http://localhost:11434/v1"
|
|||||||
request_max_retries: None,
|
request_max_retries: None,
|
||||||
stream_max_retries: None,
|
stream_max_retries: None,
|
||||||
stream_idle_timeout_ms: None,
|
stream_idle_timeout_ms: None,
|
||||||
|
max_tokens: None,
|
||||||
requires_openai_auth: false,
|
requires_openai_auth: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -452,6 +456,7 @@ query_params = { api-version = "2025-04-01-preview" }
|
|||||||
request_max_retries: None,
|
request_max_retries: None,
|
||||||
stream_max_retries: None,
|
stream_max_retries: None,
|
||||||
stream_idle_timeout_ms: None,
|
stream_idle_timeout_ms: None,
|
||||||
|
max_tokens: None,
|
||||||
requires_openai_auth: false,
|
requires_openai_auth: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -485,6 +490,7 @@ env_http_headers = { "X-Example-Env-Header" = "EXAMPLE_ENV_VAR" }
|
|||||||
request_max_retries: None,
|
request_max_retries: None,
|
||||||
stream_max_retries: None,
|
stream_max_retries: None,
|
||||||
stream_idle_timeout_ms: None,
|
stream_idle_timeout_ms: None,
|
||||||
|
max_tokens: None,
|
||||||
requires_openai_auth: false,
|
requires_openai_auth: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -508,6 +514,7 @@ env_http_headers = { "X-Example-Env-Header" = "EXAMPLE_ENV_VAR" }
|
|||||||
request_max_retries: None,
|
request_max_retries: None,
|
||||||
stream_max_retries: None,
|
stream_max_retries: None,
|
||||||
stream_idle_timeout_ms: None,
|
stream_idle_timeout_ms: None,
|
||||||
|
max_tokens: None,
|
||||||
requires_openai_auth: false,
|
requires_openai_auth: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -541,6 +548,7 @@ env_http_headers = { "X-Example-Env-Header" = "EXAMPLE_ENV_VAR" }
|
|||||||
request_max_retries: None,
|
request_max_retries: None,
|
||||||
stream_max_retries: None,
|
stream_max_retries: None,
|
||||||
stream_idle_timeout_ms: None,
|
stream_idle_timeout_ms: None,
|
||||||
|
max_tokens: None,
|
||||||
requires_openai_auth: false,
|
requires_openai_auth: false,
|
||||||
};
|
};
|
||||||
assert!(named_provider.is_azure_responses_endpoint());
|
assert!(named_provider.is_azure_responses_endpoint());
|
||||||
|
|||||||
Reference in New Issue
Block a user