50 lines
2.2 KiB
Rust
50 lines
2.2 KiB
Rust
impl ApiClient for AnthropicRuntimeClient {
|
|
#[allow(clippy::too_many_lines)]
|
|
fn stream(&mut self, request: ApiRequest) -> Result<Vec<AssistantEvent>, RuntimeError> {
|
|
if let Some(progress_reporter) = &self.progress_reporter {
|
|
progress_reporter.mark_model_phase();
|
|
}
|
|
let is_post_tool = request_ends_with_tool_result(&request);
|
|
let message_request = MessageRequest {
|
|
model: self.model.clone(),
|
|
max_tokens: max_tokens_for_model(&self.model),
|
|
messages: convert_messages(&request.messages),
|
|
system: (!request.system_prompt.is_empty()).then(|| request.system_prompt.join("\n\n")),
|
|
tools: self
|
|
.enable_tools
|
|
.then(|| filter_tool_specs(&self.tool_registry, self.allowed_tools.as_ref())),
|
|
tool_choice: self.enable_tools.then_some(ToolChoice::Auto),
|
|
stream: true,
|
|
reasoning_effort: self.reasoning_effort.clone(),
|
|
..Default::default()
|
|
};
|
|
|
|
self.runtime.block_on(async {
|
|
// When resuming after tool execution, apply a stall timeout on the
|
|
// first stream event. If the model does not respond within the
|
|
// deadline we drop the stalled connection and re-send the request as
|
|
// a continuation nudge (one retry only).
|
|
let max_attempts: usize = if is_post_tool { 2 } else { 1 };
|
|
|
|
for attempt in 1..=max_attempts {
|
|
let result = self
|
|
.consume_stream(&message_request, is_post_tool && attempt == 1)
|
|
.await;
|
|
match result {
|
|
Ok(events) => return Ok(events),
|
|
Err(error)
|
|
if error.to_string().contains("post-tool stall")
|
|
&& attempt < max_attempts =>
|
|
{
|
|
// Stalled after tool completion — nudge the model by
|
|
// re-sending the same request.
|
|
}
|
|
Err(error) => return Err(error),
|
|
}
|
|
}
|
|
|
|
Err(RuntimeError::new("post-tool continuation nudge exhausted"))
|
|
})
|
|
}
|
|
}
|