sac-cli 0.1.0

Terminal-based AI coding agent — fork of NAC with extended backend support and context management
Documentation
use super::*;

pub(super) fn parse_chat_completions_response(
    value: &Value,
    url: &str,
) -> Result<ModelTurnResponse> {
    let choices = value
        .get("choices")
        .and_then(Value::as_array)
        .ok_or_else(|| anyhow!("No choices in response from {}", url))?;
    let choice = choices
        .first()
        .ok_or_else(|| anyhow!("No choices in response from {}", url))?;
    let message = choice
        .get("message")
        .ok_or_else(|| anyhow!("Response from {} did not include a message", url))?;
    let tool_calls = match message.get("tool_calls") {
        Some(Value::Array(_)) => Some(
            serde_json::from_value::<Vec<ToolCall>>(message["tool_calls"].clone())
                .map_err(|e| anyhow!("Failed to parse tool calls from {}: {}", url, e))?,
        ),
        Some(Value::Null) | None => None,
        Some(_) => {
            return Err(anyhow!(
                "Response from {} included tool_calls in an unsupported format",
                url
            ))
        }
    };

    Ok(ModelTurnResponse {
        assistant: AssistantTurn {
            content: message
                .get("content")
                .and_then(Value::as_str)
                .map(ToString::to_string),
            reasoning_text: message
                .get("reasoning_content")
                .and_then(Value::as_str)
                .map(ToString::to_string),
            reasoning_details: None,
            tool_calls,
        },
        finish_reason: choice
            .get("finish_reason")
            .and_then(Value::as_str)
            .map(ToString::to_string),
        usage: parse_chat_usage(value.get("usage")),
    })
}