use crate::types::FieldMap;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Prediction<'a> {
#[serde(borrow)]
pub outputs: FieldMap<'a>,
#[serde(borrow)]
pub metadata: Option<PredictionMetadata<'a>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PredictionMetadata<'a> {
#[serde(borrow)]
pub model: Option<Cow<'a, str>>,
pub tokens: Option<TokenUsage>,
pub latency_ms: Option<u64>,
pub confidence: Option<f64>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct TokenUsage {
pub prompt_tokens: u32,
pub completion_tokens: u32,
pub total_tokens: u32,
}
impl<'a> Prediction<'a> {
pub fn new() -> Self {
Self {
outputs: FieldMap::new(),
metadata: None,
}
}
pub fn with_outputs(outputs: FieldMap<'a>) -> Self {
Self {
outputs,
metadata: None,
}
}
pub fn with_metadata(mut self, metadata: PredictionMetadata<'a>) -> Self {
self.metadata = Some(metadata);
self
}
pub fn get(&self, key: &str) -> Option<&str> {
self.outputs.get(key).map(|v| v.as_ref())
}
pub fn insert(&mut self, key: impl Into<Cow<'a, str>>, value: impl Into<Cow<'a, str>>) {
self.outputs.insert(key.into(), value.into());
}
pub fn into_owned(self) -> Prediction<'static> {
Prediction {
outputs: self
.outputs
.into_iter()
.map(|(k, v)| (Cow::Owned(k.into_owned()), Cow::Owned(v.into_owned())))
.collect(),
metadata: self.metadata.map(|m| PredictionMetadata {
model: m.model.map(|s| Cow::Owned(s.into_owned())),
tokens: m.tokens,
latency_ms: m.latency_ms,
confidence: m.confidence,
}),
}
}
}
impl<'a> Default for Prediction<'a> {
fn default() -> Self {
Self::new()
}
}
impl TokenUsage {
pub fn new(prompt_tokens: u32, completion_tokens: u32) -> Self {
Self {
prompt_tokens,
completion_tokens,
total_tokens: prompt_tokens + completion_tokens,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_token_usage_new() {
let usage = TokenUsage::new(10, 5);
assert_eq!(usage.prompt_tokens, 10);
assert_eq!(usage.completion_tokens, 5);
assert_eq!(usage.total_tokens, 15);
}
#[test]
fn test_prediction_creation() {
let pred = Prediction::new();
assert!(pred.outputs.is_empty());
assert!(pred.metadata.is_none());
}
#[test]
fn test_prediction_insert_and_get() {
let mut pred = Prediction::new();
pred.insert("answer", "42");
assert_eq!(pred.get("answer"), Some("42"));
assert_eq!(pred.get("missing"), None);
}
#[test]
fn test_prediction_with_metadata() {
let metadata = PredictionMetadata {
model: Some(Cow::Borrowed("gpt-4")),
tokens: Some(TokenUsage::new(10, 5)),
latency_ms: Some(250),
confidence: Some(0.95),
};
let pred = Prediction::new().with_metadata(metadata);
assert!(pred.metadata.is_some());
let meta = pred.metadata.unwrap();
assert_eq!(meta.model, Some(Cow::Borrowed("gpt-4")));
assert_eq!(meta.latency_ms, Some(250));
assert_eq!(meta.confidence, Some(0.95));
}
#[test]
fn test_prediction_into_owned() {
let mut pred = Prediction::new();
pred.insert("key", "value");
let owned = pred.into_owned();
assert_eq!(owned.get("key"), Some("value"));
}
}