use serde::{Deserialize, Serialize};
use crate::schema::ModelCapability;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TaskHint {
Chat,
Classify,
Reasoning,
Code,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct IntentHint {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub task: Option<TaskHint>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub require: Vec<ModelCapability>,
#[serde(default, skip_serializing_if = "is_false")]
pub prefer_local: bool,
#[serde(default, skip_serializing_if = "is_false")]
pub prefer_fast: bool,
}
fn is_false(b: &bool) -> bool {
!*b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_intent_serializes_compactly() {
let hint = IntentHint::default();
let json = serde_json::to_string(&hint).unwrap();
assert_eq!(json, "{}");
}
#[test]
fn round_trip_with_capability_require() {
let hint = IntentHint {
task: Some(TaskHint::Code),
require: vec![ModelCapability::Code, ModelCapability::ToolUse],
prefer_local: true,
prefer_fast: false,
};
let json = serde_json::to_string(&hint).unwrap();
let back: IntentHint = serde_json::from_str(&json).unwrap();
assert_eq!(back.task, Some(TaskHint::Code));
assert_eq!(
back.require,
vec![ModelCapability::Code, ModelCapability::ToolUse]
);
assert!(back.prefer_local);
assert!(!back.prefer_fast);
}
#[test]
fn missing_fields_default_cleanly() {
let hint: IntentHint = serde_json::from_str("{}").unwrap();
assert_eq!(hint.task, None);
assert!(hint.require.is_empty());
assert!(!hint.prefer_local);
assert!(!hint.prefer_fast);
}
#[test]
fn prefer_fast_round_trips_and_skips_when_false() {
let off = IntentHint::default();
assert_eq!(serde_json::to_string(&off).unwrap(), "{}");
let on = IntentHint {
prefer_fast: true,
..IntentHint::default()
};
let json = serde_json::to_string(&on).unwrap();
assert!(json.contains("prefer_fast"));
let back: IntentHint = serde_json::from_str(&json).unwrap();
assert!(back.prefer_fast);
}
}