Skip to main content

cookie_agent_models/adapters/
capabilities.rs

1use crate::{
2    adapters::OvenAdapterFamily,
3    authoring::{CancellationCapability, MediaKind, Modality, ModelCapabilities, ReplayCapability},
4};
5
6#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
7pub enum AdapterCapabilityError {
8    #[error("unsupported_model_capabilities")]
9    Unsupported,
10}
11
12pub fn validate_capability_ceiling(
13    family: OvenAdapterFamily,
14    capabilities: &ModelCapabilities,
15) -> Result<(), AdapterCapabilityError> {
16    if capabilities
17        .output
18        .iter()
19        .any(|value| *value != Modality::Text)
20        || capabilities.cancellation != CancellationCapability::LocalOnly
21        || capabilities.parallel_tool_calls && !capabilities.tool_calling
22        || capabilities.context_tokens == 0
23        || capabilities.output_tokens == 0
24        || capabilities.output_tokens > capabilities.context_tokens
25    {
26        return Err(AdapterCapabilityError::Unsupported);
27    }
28    if capabilities.reasoning
29        && matches!(
30            family,
31            OvenAdapterFamily::OpenaiResponses | OvenAdapterFamily::AzureOpenaiResponses
32        )
33        && capabilities.native_replay == ReplayCapability::Unsupported
34    {
35        return Err(AdapterCapabilityError::Unsupported);
36    }
37    let compaction_supported = matches!(
38        family,
39        OvenAdapterFamily::OpenaiResponses | OvenAdapterFamily::AzureOpenaiResponses
40    );
41    if capabilities.compaction == crate::CompactionCapability::Native && !compaction_supported {
42        return Err(AdapterCapabilityError::Unsupported);
43    }
44    for (kind, media) in &capabilities.media {
45        let modality = match kind {
46            MediaKind::Image => Modality::Image,
47            MediaKind::Audio => Modality::Audio,
48            MediaKind::Pdf => Modality::Pdf,
49            MediaKind::Video => Modality::Video,
50        };
51        if !capabilities.input.contains(&modality)
52            || media.mime_types.is_empty()
53            || media.max_bytes == 0
54            || media.max_count == 0
55        {
56            return Err(AdapterCapabilityError::Unsupported);
57        }
58    }
59    if capabilities
60        .input
61        .iter()
62        .filter(|modality| **modality != Modality::Text)
63        .any(|modality| {
64            let kind = match modality {
65                Modality::Image => MediaKind::Image,
66                Modality::Audio => MediaKind::Audio,
67                Modality::Pdf => MediaKind::Pdf,
68                Modality::Video => MediaKind::Video,
69                Modality::Text => unreachable!(),
70            };
71            !capabilities.media.contains_key(&kind)
72        })
73    {
74        return Err(AdapterCapabilityError::Unsupported);
75    }
76    Ok(())
77}