1use std::future::Future;
2use std::pin::Pin;
3use std::sync::Arc;
4
5use serde_json::Value;
6
7use crate::error::ToolError;
8use crate::types::{CodeModeCaller, CodeModeSurface, ToolDescriptor, ToolScope, UiLink};
9
10pub type HostFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
11
12#[derive(Debug, Clone)]
13pub struct ToolsRender {
14 pub fingerprint: String,
15 pub entries: Arc<[ToolDescriptor]>,
16 pub catalog_json: Arc<str>,
17 pub serialized_size: usize,
18}
19
20impl ToolsRender {
21 #[must_use]
22 pub fn empty() -> Self {
23 Self {
24 fingerprint: String::new(),
25 entries: Arc::from([]),
26 catalog_json: Arc::from("[]"),
27 serialized_size: 2,
28 }
29 }
30}
31
32#[derive(Debug, Clone)]
33pub struct ResolvedSnippet {
34 pub name: String,
35 pub code: String,
36 pub input: Value,
37}
38
39#[derive(Debug, Clone)]
40pub struct ToolCallOutcome {
41 pub value: Value,
42 pub ui: Option<UiLink>,
43}
44
45#[derive(Debug, Clone)]
46pub struct ExecCtx {
47 pub seq: u64,
48 pub execution_id: Option<Arc<str>>,
49 pub step_ordinal: Option<u64>,
50}
51
52impl ExecCtx {
53 #[must_use]
54 pub fn none() -> Self {
55 Self {
56 seq: 0,
57 execution_id: None,
58 step_ordinal: None,
59 }
60 }
61}
62
63#[derive(Debug, Clone, PartialEq)]
64pub enum StepDecision {
65 Replay(Value),
66 Execute,
67 Error { kind: String, message: String },
68}
69
70pub trait CodeModeHost: Send + Sync {
71 fn list_tools<'a>(
72 &'a self,
73 caller: &'a CodeModeCaller,
74 surface: CodeModeSurface,
75 scope: &'a ToolScope,
76 include_snippets: bool,
77 use_cache: bool,
78 ) -> HostFuture<'a, Result<ToolsRender, ToolError>>;
79
80 fn call_tool<'a>(
81 &'a self,
82 id: &'a str,
83 params: Value,
84 caller: &'a CodeModeCaller,
85 surface: CodeModeSurface,
86 scope: &'a ToolScope,
87 ctx: ExecCtx,
88 ) -> HostFuture<'a, Result<ToolCallOutcome, ToolError>>;
89
90 fn resolve_snippet<'a>(
91 &'a self,
92 name: &'a str,
93 input: Value,
94 ) -> HostFuture<'a, Result<ResolvedSnippet, ToolError>>;
95
96 fn semantic_search<'a>(
97 &'a self,
98 query: &'a str,
99 caller: &'a CodeModeCaller,
100 surface: CodeModeSurface,
101 scope: &'a ToolScope,
102 top_k: usize,
103 ) -> HostFuture<'a, Result<Vec<(String, f32)>, ToolError>> {
104 let _ = (query, caller, surface, scope, top_k);
105 Box::pin(async { Ok(Vec::new()) })
106 }
107
108 fn decide_step<'a>(&'a self, ctx: ExecCtx, name: &'a str) -> HostFuture<'a, StepDecision> {
109 let _ = (ctx, name);
110 Box::pin(async { StepDecision::Execute })
111 }
112
113 fn record_step<'a>(
114 &'a self,
115 ctx: ExecCtx,
116 name: &'a str,
117 value: &'a Value,
118 ) -> HostFuture<'a, Result<(), ToolError>> {
119 let _ = (ctx, name, value);
120 Box::pin(async { Ok(()) })
121 }
122
123 #[cfg(feature = "openapi")]
124 fn openapi_registry(&self) -> Option<soma_openapi::OpenApiRegistry> {
125 None
126 }
127
128 #[cfg(feature = "openapi")]
129 fn openapi_http_client(&self) -> Option<reqwest::Client> {
130 None
131 }
132}
133
134#[derive(Debug, Default)]
135pub struct NoopHost;
136
137impl CodeModeHost for NoopHost {
138 fn list_tools<'a>(
139 &'a self,
140 caller: &'a CodeModeCaller,
141 surface: CodeModeSurface,
142 scope: &'a ToolScope,
143 include_snippets: bool,
144 use_cache: bool,
145 ) -> HostFuture<'a, Result<ToolsRender, ToolError>> {
146 let _ = (caller, surface, scope, include_snippets, use_cache);
147 Box::pin(async { Ok(ToolsRender::empty()) })
148 }
149
150 fn call_tool<'a>(
151 &'a self,
152 id: &'a str,
153 params: Value,
154 caller: &'a CodeModeCaller,
155 surface: CodeModeSurface,
156 scope: &'a ToolScope,
157 ctx: ExecCtx,
158 ) -> HostFuture<'a, Result<ToolCallOutcome, ToolError>> {
159 let _ = (params, caller, surface, scope, ctx);
160 Box::pin(async move {
161 Err(ToolError::UnknownAction {
162 message: format!("unknown Code Mode tool `{id}`"),
163 valid: Vec::new(),
164 hint: None,
165 })
166 })
167 }
168
169 fn resolve_snippet<'a>(
170 &'a self,
171 name: &'a str,
172 input: Value,
173 ) -> HostFuture<'a, Result<ResolvedSnippet, ToolError>> {
174 let _ = input;
175 Box::pin(async move {
176 Err(ToolError::UnknownInstance {
177 message: format!("unknown Code Mode snippet `{name}`"),
178 valid: Vec::new(),
179 })
180 })
181 }
182}