1use rustc_hash::FxHashMap;
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Deserialize, Serialize)]
6#[serde(deny_unknown_fields)]
7pub struct ForgeConfig {
8 pub forge: ForgeMetadata,
9 #[serde(default)]
10 pub tools: Vec<ToolDef>,
11 #[serde(default)]
12 pub resources: Vec<ResourceDef>,
13 #[serde(default)]
14 pub prompts: Vec<PromptDef>,
15 #[serde(default)]
16 pub state: Option<StateDef>,
17}
18
19#[derive(Debug, Clone, Deserialize, Serialize)]
20pub struct ForgeMetadata {
21 pub name: String,
22 pub version: String,
23 #[serde(default = "default_transport")]
24 pub transport: TransportType,
25 #[serde(default)]
26 pub optimization: OptimizationLevel,
27}
28
29#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
30#[serde(rename_all = "lowercase")]
31pub enum TransportType {
32 Stdio,
33 Sse,
34 #[serde(rename = "websocket")]
35 WebSocket,
36}
37
38#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Default)]
39#[serde(rename_all = "lowercase")]
40pub enum OptimizationLevel {
41 #[default]
42 Debug,
43 Release,
44}
45
46#[derive(Debug, Clone, Deserialize, Serialize)]
47#[serde(tag = "type", rename_all = "snake_case")]
48pub enum ToolDef {
49 Native {
50 name: String,
51 description: String,
52 handler: HandlerRef,
53 params: ParamSchema,
54 #[serde(default)]
55 timeout_ms: Option<u64>,
56 },
57 Cli {
58 name: String,
59 description: String,
60 command: String,
61 args: Vec<String>,
62 #[serde(default)]
63 cwd: Option<String>,
64 #[serde(default)]
65 env: FxHashMap<String, String>,
66 #[serde(default)]
67 stream: bool,
68 #[serde(default)]
70 timeout_ms: Option<u64>,
71 },
72 Http {
73 name: String,
74 description: String,
75 endpoint: String,
76 method: HttpMethod,
77 #[serde(default)]
78 auth: Option<AuthConfig>,
79 #[serde(default)]
80 headers: FxHashMap<String, String>,
81 #[serde(default)]
83 timeout_ms: Option<u64>,
84 },
85 Pipeline {
86 name: String,
87 description: String,
88 steps: Vec<PipelineStep>,
89 },
90}
91
92impl ToolDef {
93 pub fn name(&self) -> &str {
94 match self {
95 Self::Native { name, .. }
96 | Self::Cli { name, .. }
97 | Self::Http { name, .. }
98 | Self::Pipeline { name, .. } => name,
99 }
100 }
101}
102
103#[derive(Debug, Clone, Deserialize, Serialize)]
104pub struct HandlerRef {
105 pub path: String,
106 #[serde(default)]
107 pub inline: Option<String>,
108}
109
110#[derive(Debug, Clone, Deserialize, Serialize)]
111pub struct ParamSchema {
112 #[serde(flatten)]
113 pub fields: FxHashMap<String, ParamType>,
114}
115
116#[derive(Debug, Clone, Deserialize, Serialize)]
117#[serde(untagged)]
118pub enum ParamType {
119 Simple(SimpleType),
120 Complex {
121 #[serde(rename = "type")]
122 ty: SimpleType,
123 #[serde(default)]
124 required: bool,
125 #[serde(default)]
126 default: Option<serde_json::Value>,
127 #[serde(default)]
128 description: Option<String>,
129 #[serde(default)]
130 validation: Option<Validation>,
131 },
132}
133
134#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
135#[serde(rename_all = "lowercase")]
136pub enum SimpleType {
137 String,
138 Integer,
139 Float,
140 Boolean,
141 Array,
142 Object,
143}
144
145#[derive(Debug, Clone, Deserialize, Serialize)]
146pub struct Validation {
147 #[serde(default)]
148 pub min: Option<f64>,
149 #[serde(default)]
150 pub max: Option<f64>,
151 #[serde(default)]
152 pub pattern: Option<String>,
153 #[serde(default)]
154 pub min_length: Option<usize>,
155 #[serde(default)]
156 pub max_length: Option<usize>,
157}
158
159#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
160#[serde(rename_all = "UPPERCASE")]
161pub enum HttpMethod {
162 Get,
163 Post,
164 Put,
165 Delete,
166 Patch,
167}
168
169#[derive(Debug, Clone, Deserialize, Serialize)]
170#[serde(tag = "type", rename_all = "lowercase")]
171pub enum AuthConfig {
172 Bearer { token: String },
173 Basic { username: String, password: String },
174 ApiKey { key: String, header: String },
175}
176
177#[derive(Debug, Clone, Deserialize, Serialize)]
178pub struct PipelineStep {
179 pub tool: String,
180 #[serde(default)]
181 pub input: Option<serde_json::Value>,
182 #[serde(default)]
183 pub output_var: Option<String>,
184 #[serde(default)]
185 pub condition: Option<String>,
186 #[serde(default)]
187 pub error_policy: ErrorPolicy,
188}
189
190#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Default)]
191#[serde(rename_all = "snake_case")]
192pub enum ErrorPolicy {
193 #[default]
194 FailFast,
195 Continue,
196}
197
198#[derive(Debug, Clone, Deserialize, Serialize)]
199pub struct ResourceDef {
200 pub uri_template: String,
201 pub handler: HandlerRef,
202 #[serde(default)]
203 pub supports: Vec<ResourceOperation>,
204}
205
206#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
207#[serde(rename_all = "lowercase")]
208pub enum ResourceOperation {
209 Read,
210 Write,
211 Subscribe,
212}
213
214#[derive(Debug, Clone, Deserialize, Serialize)]
215pub struct PromptDef {
216 pub name: String,
217 pub description: String,
218 pub template: String,
219 #[serde(default)]
220 pub arguments: FxHashMap<String, ParamType>,
221}
222
223#[derive(Debug, Clone, Deserialize, Serialize)]
224pub struct StateDef {
225 pub backend: StateBackend,
226 pub path: String,
227 #[serde(default)]
228 pub options: FxHashMap<String, serde_json::Value>,
229}
230
231#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
232#[serde(rename_all = "lowercase")]
233pub enum StateBackend {
234 Sled,
235 Memory,
236}
237
238const fn default_transport() -> TransportType {
239 TransportType::Stdio
240}