Skip to main content

defect_core/llm/
capability.rs

1//! Provider and model capability matrix.
2
3use serde::{Deserialize, Serialize};
4
5/// Provider-level capability matrix.
6///
7/// Model-level differences are expressed via [`ModelCapabilityOverrides`]; the main loop
8/// merges them as needed:
9/// a model-level `Some(_)` overrides the provider level, while `None` falls back to the
10/// provider level.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12pub struct Capabilities {
13    /// Tool calls (content_block contains `tool_use` / `tool_calls` fields).
14    pub tool_calls: FeatureSupport,
15    /// Multiple concurrent tool_use calls within a single turn.
16    pub parallel_tool_calls: FeatureSupport,
17    /// Chain of thought.
18    pub thinking: FeatureSupport,
19    /// Multimodal input (images).
20    pub vision: FeatureSupport,
21    /// Prompt cache.
22    pub prompt_cache: FeatureSupport,
23    /// Thinking content replay strategy. See [`ThinkingEcho`].
24    pub thinking_echo: ThinkingEcho,
25}
26
27/// Model-level overrides. `None` means fall back to the provider-level [`Capabilities`]
28/// field.
29///
30/// The field set is limited to properties that actually vary per model in practice, and
31/// does not mechanically mirror [`Capabilities`]. Additional fields may be added later as
32/// new differences emerge.
33#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
34pub struct ModelCapabilityOverrides {
35    pub thinking: Option<FeatureSupport>,
36    pub vision: Option<FeatureSupport>,
37    pub prompt_cache: Option<FeatureSupport>,
38    pub parallel_tool_calls: Option<FeatureSupport>,
39    pub thinking_echo: Option<ThinkingEcho>,
40}
41
42/// Policy for replaying thinking content.
43///
44/// `Required` — the previous assistant turn's thinking must be included in the next
45/// request (Anthropic extended thinking, DeepSeek-v4-pro). `Forbidden` — replay is
46/// rejected by the server (DeepSeek-R1, official OpenAI o1/o3). `Optional` — the server
47/// accepts either behavior.
48#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
49#[serde(rename_all = "snake_case")]
50pub enum ThinkingEcho {
51    #[default]
52    Forbidden,
53    Required,
54    Optional,
55}
56
57/// Tri-state feature support declaration.
58///
59/// Using a tri-state instead of `bool` allows expressing
60/// [`FeatureSupport::PassthroughAsTool`] — pseudo-support via adaptation. Even though
61/// nothing currently produces this value, defining a tri-state from the start is simpler
62/// than upgrading from `bool` to an enum later.
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
64#[serde(rename_all = "snake_case")]
65pub enum FeatureSupport {
66    Supported,
67    Unsupported,
68    /// Passthrough support via adapter.
69    ///
70    /// For example, a provider may not natively support `web_search`, but the agent wraps
71    /// it as a tool exposed to the LLM, thereby "pretending" to support it.
72    PassthroughAsTool,
73}
74
75/// The set of hosted capabilities that the provider advertises.
76///
77/// Distinguished from [`Capabilities`]:
78/// - [`Capabilities`] describes model-level abilities (thinking, vision, tool_calls,
79///   etc.)
80/// - [`HostedCapabilities`] describes the provider adapter's own implementation state:
81///   whether the current adapter can declare hosted `web_search`, `fetch`, or
82///   `code_execution` on the wire.
83///
84/// At session startup, this struct is obtained via
85/// [`super::LlmProvider::hosted_capabilities`] and, together with
86/// `capabilities.web_search.mode`, determines the source of web search capability for the
87/// session. Note that local grep/glob tools (the `search` tool) are not part of the
88/// capability layer and are managed separately by `[tools.search]`.
89///
90/// Native metadata returned by the model after a completions call.
91#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
92pub struct HostedCapabilities {
93    /// Whether the provider adapter supports hosted web search.
94    ///
95    /// The hosted tool version is hardcoded internally by the adapter to always use the
96    /// latest (Anthropic `web_search_20260209`, OpenAI Responses API `web_search`); the
97    /// agent is unaware of the specific version field.
98    pub web_search: bool,
99}
100
101impl HostedCapabilities {
102    /// Constructs from a single field. A convenience entry point for cross-crate tests and
103    /// adapter implementations.
104    #[must_use]
105    pub const fn with_web_search(web_search: bool) -> Self {
106        Self { web_search }
107    }
108}