vv-agent 0.4.8

VectorVein agent runtime, SDK, CLI, tools, and workspace backends
Documentation
use serde_json::Value;

use super::blocks::{block_type, is_thinking_block_type};

pub(super) fn estimate_tokens(char_count: usize) -> usize {
    if char_count == 0 {
        0
    } else {
        char_count.div_ceil(4)
    }
}

pub(super) fn estimate_tool_chars(tool: &Value) -> usize {
    sorted_json(tool).len()
}

pub(super) fn estimate_block_chars(block: &Value) -> usize {
    let block_type = block_type(block);
    match block_type.as_str() {
        "text" => value_to_string(block.get("text")).chars().count(),
        "tool_result" => json_string(block.get("content").unwrap_or(&Value::Null)).len(),
        "tool_use" => {
            value_to_string(block.get("name")).chars().count()
                + json_string(block.get("input").unwrap_or(&Value::Null)).len()
        }
        candidate if is_thinking_block_type(candidate) => 0,
        _ => json_string(block).len(),
    }
}

pub(super) fn value_to_string(value: Option<&Value>) -> String {
    match value {
        Some(Value::String(text)) => text.clone(),
        Some(Value::Bool(value)) => value.to_string(),
        Some(Value::Number(value)) => value.to_string(),
        Some(value @ (Value::Array(_) | Value::Object(_))) => json_string(value),
        Some(Value::Null) | None => String::new(),
    }
}

pub(super) fn value_truthy(value: &Value) -> bool {
    match value {
        Value::Null => false,
        Value::Bool(value) => *value,
        Value::Number(number) => number.as_f64().is_some_and(|value| value != 0.0),
        Value::String(text) => !text.is_empty(),
        Value::Array(items) => !items.is_empty(),
        Value::Object(object) => !object.is_empty(),
    }
}

fn json_string(value: &Value) -> String {
    serde_json::to_string(value).unwrap_or_default()
}

fn sorted_json(value: &Value) -> String {
    serde_json::to_string(&sort_value(value)).unwrap_or_default()
}

fn sort_value(value: &Value) -> Value {
    match value {
        Value::Array(items) => Value::Array(items.iter().map(sort_value).collect()),
        Value::Object(object) => Value::Object(
            object
                .iter()
                .map(|(key, value)| (key.clone(), sort_value(value)))
                .collect(),
        ),
        value => value.clone(),
    }
}