monothink 0.4.7

A totally non-agentic coding assistant
/*
* This file is part of monothink.
*
* monothink is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* monothink is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Wavething.
* If not, see <https://www.gnu.org/licenses/>.

*/

use rig::message::Message;
use serde::Serialize;
use unicode_width::UnicodeWidthStr;

pub trait Estimator {
    // return the estimated number of tokens in a message
    fn estimate(&self, msg: &Message) -> u64;

    fn context_pressure(
        &self,
        prompt: &rig::message::Message,
        history: &[rig::message::Message],
    ) -> u64 {
        self.estimate(prompt) + history.iter().map(|msg| self.estimate(msg)).sum::<u64>()
    }
}

#[derive(Clone)]
pub struct SimpleEstimator {
    characters_per_token: f64,
}

impl SimpleEstimator {
    pub fn new(characters_per_token: f64) -> Self {
        SimpleEstimator {
            characters_per_token,
        }
    }
}

impl Estimator for SimpleEstimator {
    fn estimate(&self, msg: &Message) -> u64 {
        f64::ceil(message_width(msg) as f64 / self.characters_per_token) as u64
    }
}

fn message_width<T: ?Sized + Serialize>(msg: &T) -> u64 {
    // We assume that a message that cannot be serialized has length 0.
    // This might cause underestimation but should not occur.
    // Worst case causes a rejected request due to an exceeded context window.
    serde_json::to_string(msg).unwrap_or_default().width() as u64
}