1use agent_stream_kit::{
2 ASKit, Agent, AgentContext, AgentData, AgentError, AgentOutput, AgentSpec, AgentValue, AsAgent,
3 askit_agent, async_trait,
4};
5use handlebars::Handlebars;
6
7static CATEGORY: &str = "Std/String";
8
9static PIN_DATA: &str = "data";
10static PIN_STRING: &str = "string";
11static PIN_STRINGS: &str = "strings";
12
13static CONFIG_SEP: &str = "sep";
14static CONFIG_TEMPLATE: &str = "template";
15
16#[askit_agent(
32 title = "String Join",
33 category = CATEGORY,
34 inputs = [PIN_STRINGS],
35 outputs = [PIN_STRING],
36 string_config(name = CONFIG_SEP, default = "\\n")
37)]
38struct StringJoinAgent {
39 data: AgentData,
40}
41
42#[async_trait]
43impl AsAgent for StringJoinAgent {
44 fn new(askit: ASKit, id: String, spec: AgentSpec) -> Result<Self, AgentError> {
45 Ok(Self {
46 data: AgentData::new(askit, id, spec),
47 })
48 }
49
50 async fn process(
51 &mut self,
52 ctx: AgentContext,
53 _pin: String,
54 value: AgentValue,
55 ) -> Result<(), AgentError> {
56 let config = self.configs()?;
57
58 let sep = config.get_string_or_default(CONFIG_SEP);
59
60 if value.is_array() {
61 let mut out = Vec::new();
62 for v in value
63 .as_array()
64 .ok_or_else(|| AgentError::InvalidArrayValue("Expected array".into()))?
65 {
66 out.push(v.as_str().unwrap_or_default());
67 }
68 let mut out = out.join(&sep);
69 out = out.replace("\\n", "\n");
70 out = out.replace("\\t", "\t");
71 out = out.replace("\\r", "\r");
72 out = out.replace("\\\\", "\\");
73 let out_value = AgentValue::string(out);
74 self.try_output(ctx, PIN_STRING, out_value)
75 } else {
76 self.try_output(ctx, PIN_STRING, value)
77 }
78 }
79}
80
81#[askit_agent(
83 title = "Template String",
84 category = CATEGORY,
85 inputs = [PIN_DATA],
86 outputs = [PIN_STRING],
87 string_config(name = CONFIG_TEMPLATE, default = "{{value}}")
88)]
89struct TemplateStringAgent {
90 data: AgentData,
91}
92
93#[async_trait]
94impl AsAgent for TemplateStringAgent {
95 fn new(askit: ASKit, id: String, spec: AgentSpec) -> Result<Self, AgentError> {
96 Ok(Self {
97 data: AgentData::new(askit, id, spec),
98 })
99 }
100
101 async fn process(
102 &mut self,
103 ctx: AgentContext,
104 _pin: String,
105 value: AgentValue,
106 ) -> Result<(), AgentError> {
107 let config = self.configs()?;
108
109 let template = config.get_string_or_default(CONFIG_TEMPLATE);
110 if template.is_empty() {
111 return Err(AgentError::InvalidConfig("template is not set".into()));
112 }
113
114 let reg = handlebars_new();
115
116 if value.is_array() {
117 let mut out_arr = Vec::new();
118 for v in value
119 .as_array()
120 .ok_or_else(|| AgentError::InvalidArrayValue("Expected array".into()))?
121 {
122 let rendered_string = reg.render_template(&template, v).map_err(|e| {
123 AgentError::InvalidValue(format!("Failed to render template: {}", e))
124 })?;
125 out_arr.push(rendered_string.into());
126 }
127 self.try_output(ctx, PIN_STRING, AgentValue::array(out_arr))
128 } else {
129 let rendered_string = reg.render_template(&template, &value).map_err(|e| {
130 AgentError::InvalidValue(format!("Failed to render template: {}", e))
131 })?;
132 let out_value = AgentValue::string(rendered_string);
133 self.try_output(ctx, PIN_STRING, out_value)
134 }
135 }
136}
137
138#[askit_agent(
140 title = "Template Text",
141 category = CATEGORY,
142 inputs = [PIN_DATA],
143 outputs = [PIN_STRING],
144 text_config(name = CONFIG_TEMPLATE, default = "{{value}}")
145)]
146struct TemplateTextAgent {
147 data: AgentData,
148}
149
150#[async_trait]
151impl AsAgent for TemplateTextAgent {
152 fn new(askit: ASKit, id: String, spec: AgentSpec) -> Result<Self, AgentError> {
153 Ok(Self {
154 data: AgentData::new(askit, id, spec),
155 })
156 }
157
158 async fn process(
159 &mut self,
160 ctx: AgentContext,
161 _pin: String,
162 value: AgentValue,
163 ) -> Result<(), AgentError> {
164 let config = self.configs()?;
165
166 let template = config.get_string_or_default(CONFIG_TEMPLATE);
167 if template.is_empty() {
168 return Err(AgentError::InvalidConfig("template is not set".into()));
169 }
170
171 let reg = handlebars_new();
172
173 if value.is_array() {
174 let mut out_arr = Vec::new();
175 for v in value
176 .as_array()
177 .ok_or_else(|| AgentError::InvalidArrayValue("Expected array".into()))?
178 {
179 let rendered_string = reg.render_template(&template, v).map_err(|e| {
180 AgentError::InvalidValue(format!("Failed to render template: {}", e))
181 })?;
182 out_arr.push(rendered_string.into());
183 }
184 self.try_output(ctx, PIN_STRING, AgentValue::array(out_arr))
185 } else {
186 let rendered_string = reg.render_template(&template, &value).map_err(|e| {
187 AgentError::InvalidValue(format!("Failed to render template: {}", e))
188 })?;
189 let out_value = AgentValue::string(rendered_string);
190 self.try_output(ctx, PIN_STRING, out_value)
191 }
192 }
193}
194
195#[askit_agent(
197 title = "Template Array",
198 category = CATEGORY,
199 inputs = [PIN_DATA],
200 outputs = [PIN_STRING],
201 text_config(name = CONFIG_TEMPLATE, default = "{{value}}")
202)]
203struct TemplateArrayAgent {
204 data: AgentData,
205}
206
207#[async_trait]
208impl AsAgent for TemplateArrayAgent {
209 fn new(askit: ASKit, id: String, spec: AgentSpec) -> Result<Self, AgentError> {
210 Ok(Self {
211 data: AgentData::new(askit, id, spec),
212 })
213 }
214
215 async fn process(
216 &mut self,
217 ctx: AgentContext,
218 _pin: String,
219 value: AgentValue,
220 ) -> Result<(), AgentError> {
221 let config = self.configs()?;
222
223 let template = config.get_string_or_default(CONFIG_TEMPLATE);
224 if template.is_empty() {
225 return Err(AgentError::InvalidConfig("template is not set".into()));
226 }
227
228 let reg = handlebars_new();
229
230 if value.is_array() {
231 let rendered_string = reg.render_template(&template, &value).map_err(|e| {
232 AgentError::InvalidValue(format!("Failed to render template: {}", e))
233 })?;
234 self.try_output(ctx, PIN_STRING, AgentValue::string(rendered_string))
235 } else {
236 let d = AgentValue::array(vec![value.clone()]);
237 let rendered_string = reg.render_template(&template, &d).map_err(|e| {
238 AgentError::InvalidValue(format!("Failed to render template: {}", e))
239 })?;
240 let out_value = AgentValue::string(rendered_string);
241 self.try_output(ctx, PIN_STRING, out_value)
242 }
243 }
244}
245
246fn handlebars_new<'a>() -> Handlebars<'a> {
247 let mut reg = Handlebars::new();
248 reg.register_escape_fn(handlebars::no_escape);
249 reg.register_helper("to_json", Box::new(to_json_helper));
250
251 #[cfg(feature = "yaml")]
252 reg.register_helper("to_yaml", Box::new(to_yaml_helper));
253
254 reg
255}
256
257fn to_json_helper(
258 h: &handlebars::Helper<'_>,
259 _: &handlebars::Handlebars<'_>,
260 _: &handlebars::Context,
261 _: &mut handlebars::RenderContext<'_, '_>,
262 out: &mut dyn handlebars::Output,
263) -> handlebars::HelperResult {
264 if let Some(value) = h.param(0) {
265 let json_str = serde_json::to_string_pretty(&value.value()).map_err(|e| {
266 handlebars::RenderErrorReason::Other(format!("Failed to serialize to JSON: {}", e))
267 })?;
268 out.write(&json_str)?;
269 }
270 Ok(())
271}
272
273#[cfg(feature = "yaml")]
274fn to_yaml_helper(
275 h: &handlebars::Helper<'_>,
276 _: &handlebars::Handlebars<'_>,
277 _: &handlebars::Context,
278 _: &mut handlebars::RenderContext<'_, '_>,
279 out: &mut dyn handlebars::Output,
280) -> handlebars::HelperResult {
281 if let Some(value) = h.param(0) {
282 let yaml_str = serde_yaml_ng::to_string(&value.value()).map_err(|e| {
283 handlebars::RenderErrorReason::Other(format!("Failed to serialize to YAML: {}", e))
284 })?;
285 out.write(&yaml_str)?;
286 }
287 Ok(())
288}