1#[cfg(feature = "model")]
6use aiway_protocol::model::Provider;
7use serde::{Deserialize, Serialize};
8use std::any::Any;
9use std::collections::HashMap;
10
11use crate::PluginError;
12use aiway_protocol::context::{
13 HeaderOp, HttpContext, REQUEST_HEADER_PATCH, REQUEST_URI_PATCH, RESPONSE_HEADER_PATCH,
14 parts::SerdeParts,
15};
16use http::Uri;
17
18pub const LOG_ERROR: i32 = 1;
20pub const LOG_WARN: i32 = 2;
21pub const LOG_INFO: i32 = 3;
22pub const LOG_DEBUG: i32 = 4;
23pub const LOG_TRACE: i32 = 5;
24
25#[derive(Serialize, Deserialize)]
27pub struct HttpRequest {
28 pub method: String,
29 pub url: String,
30 pub headers: Vec<(String, String)>,
31 pub body: Option<Vec<u8>>,
32 pub form: Option<HashMap<String, String>>,
34 pub multipart: Option<Vec<FormPart>>,
36 pub timeout_ms: u64,
37}
38
39#[derive(Serialize, Deserialize)]
41pub struct FormPart {
42 pub key: String,
43 pub value: Vec<u8>,
45 pub file_name: Option<String>,
47 pub mime_type: Option<String>,
49}
50
51pub struct HttpRequestBuilder {
53 method: String,
54 url: String,
55 headers: Vec<(String, String)>,
56 body: Option<Vec<u8>>,
57 form: Option<HashMap<String, String>>,
58 multipart: Option<Vec<FormPart>>,
59 timeout_ms: u64,
60}
61
62impl HttpRequestBuilder {
63 pub fn new(method: impl Into<String>, url: impl Into<String>) -> Self {
65 Self {
66 method: method.into(),
67 url: url.into(),
68 headers: Vec::new(),
69 body: None,
70 form: None,
71 multipart: None,
72 timeout_ms: 10_000,
73 }
74 }
75
76 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
78 self.headers.push((key.into(), value.into()));
79 self
80 }
81
82 pub fn body(mut self, body: Vec<u8>) -> Self {
84 self.body = Some(body);
85 self
86 }
87
88 pub fn form(mut self, form: HashMap<String, String>) -> Self {
90 self.form = Some(form);
91 self
92 }
93
94 pub fn add_form_field(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
96 self.form
97 .get_or_insert_with(HashMap::new)
98 .insert(key.into(), value.into());
99 self
100 }
101
102 pub fn multipart(mut self, parts: Vec<FormPart>) -> Self {
104 self.multipart = Some(parts);
105 self
106 }
107
108 pub fn add_multipart_part(mut self, part: FormPart) -> Self {
110 self.multipart.get_or_insert_with(Vec::new).push(part);
111 self
112 }
113
114 pub fn timeout_ms(mut self, timeout_ms: u64) -> Self {
116 self.timeout_ms = timeout_ms;
117 self
118 }
119
120 pub fn build(self) -> HttpRequest {
122 HttpRequest {
123 method: self.method,
124 url: self.url,
125 headers: self.headers,
126 body: self.body,
127 form: self.form,
128 multipart: self.multipart,
129 timeout_ms: self.timeout_ms,
130 }
131 }
132}
133
134#[derive(Serialize, Deserialize)]
136pub struct HttpResponse {
137 pub status: u16,
138 pub headers: Vec<(String, String)>,
139 pub body: Vec<u8>,
140}
141
142impl HttpResponse {
143 pub fn text(&self) -> Result<String, PluginError> {
145 String::from_utf8(self.body.clone())
146 .map_err(|e| PluginError::HttpError(format!("invalid UTF-8 response: {e}")))
147 }
148
149 pub fn json<T: serde::de::DeserializeOwned>(&self) -> Result<T, PluginError> {
151 serde_json::from_slice(&self.body)
152 .map_err(|e| PluginError::SerdeError(format!("JSON deserialize failed: {e}")))
153 }
154}
155
156pub trait PluginContext: Send {
161 fn request_id(&self) -> String;
163 fn request_ts(&self) -> i64;
165 fn is_sse(&self) -> bool;
167 fn is_websocket(&self) -> bool;
169 fn get_request_header(&self, name: &str) -> Option<String>;
171 fn get_response_header(&self, name: &str) -> Option<String>;
173 fn method(&self) -> Option<String>;
175 fn uri(&self) -> Option<Uri>;
177 fn set_uri(&mut self, uri: Uri);
179 fn status(&self) -> Option<u16>;
181 fn get_route_name(&self) -> Option<String>;
183 fn get_routing_url(&self) -> Option<String>;
185 fn get_response_body_size(&self) -> Option<i64>;
187 fn set_response_body_size(&mut self, size: i64);
189
190 fn set_request_header(&mut self, name: &str, value: &str);
192 fn set_response_header(&mut self, name: &str, value: &str);
194 fn append_request_header(&mut self, name: &str, value: &str);
196 fn append_response_header(&mut self, name: &str, value: &str);
198 fn remove_request_header(&mut self, name: &str);
200 fn remove_response_header(&mut self, name: &str);
202 #[cfg(feature = "model")]
204 fn get_model_name(&self) -> Option<String>;
205 #[cfg(feature = "model")]
207 fn get_model_provider(&self) -> Option<Provider>;
208
209 fn log(&self, level: i32, msg: &str);
211 fn log_error(&self, msg: &str) {
213 self.log(LOG_ERROR, msg);
214 }
215 fn log_warn(&self, msg: &str) {
217 self.log(LOG_WARN, msg);
218 }
219 fn log_info(&self, msg: &str) {
221 self.log(LOG_INFO, msg);
222 }
223 fn log_debug(&self, msg: &str) {
225 self.log(LOG_DEBUG, msg);
226 }
227 fn log_trace(&self, msg: &str) {
229 self.log(LOG_TRACE, msg);
230 }
231
232 fn http_request(&self, _req: &HttpRequest) -> Result<HttpResponse, PluginError> {
234 Err(PluginError::HttpError(
235 "http_request not supported in this context".into(),
236 ))
237 }
238
239 fn as_any_mut(&mut self) -> &mut dyn Any;
241}
242
243impl PluginContext for HttpContext {
244 fn request_id(&self) -> String {
245 HttpContext::request_id(self)
246 }
247
248 fn request_ts(&self) -> i64 {
249 HttpContext::request_ts(self)
250 }
251
252 fn is_sse(&self) -> bool {
253 HttpContext::is_sse(self)
254 }
255
256 fn is_websocket(&self) -> bool {
257 HttpContext::is_websocket(self)
258 }
259
260 fn get_request_header(&self, name: &str) -> Option<String> {
261 self.get_state::<SerdeParts>(Self::REQUEST_RAW_PARTS)
262 .and_then(|parts| {
263 parts
264 .headers
265 .as_ref()?
266 .get(name)
267 .and_then(|v| v.to_str().ok())
268 .map(|s| s.to_string())
269 })
270 }
271
272 fn get_response_header(&self, name: &str) -> Option<String> {
273 self.get_state::<SerdeParts>(Self::RESPONSE_SERDE_PARTS)
274 .and_then(|parts| {
275 parts
276 .headers
277 .as_ref()?
278 .get(name)
279 .and_then(|v| v.to_str().ok())
280 .map(|s| s.to_string())
281 })
282 }
283
284 fn method(&self) -> Option<String> {
285 self.get_state::<SerdeParts>(Self::REQUEST_RAW_PARTS)
286 .and_then(|parts| parts.method.map(|m| m.to_string()))
287 }
288
289 fn uri(&self) -> Option<Uri> {
290 self.get_state::<SerdeParts>(Self::REQUEST_RAW_PARTS)
291 .and_then(|parts| parts.uri)
292 }
293
294 fn set_uri(&mut self, uri: Uri) {
295 self.insert_any_state(REQUEST_URI_PATCH, uri);
296 }
297
298 fn status(&self) -> Option<u16> {
299 self.get_state::<SerdeParts>(Self::RESPONSE_SERDE_PARTS)
300 .and_then(|parts| parts.status_code.map(|s| s.as_u16()))
301 }
302
303 fn get_route_name(&self) -> Option<String> {
304 self.get_route().map(|r| r.name.clone())
305 }
306
307 fn get_routing_url(&self) -> Option<String> {
308 HttpContext::get_routing_url(self).cloned()
309 }
310
311 fn get_response_body_size(&self) -> Option<i64> {
312 self.get_state::<i64>(Self::RESPONSE_BODY_SIZE)
313 }
314
315 fn set_response_body_size(&mut self, size: i64) {
316 self.insert_state(Self::RESPONSE_BODY_SIZE, size);
317 }
318
319 fn set_request_header(&mut self, name: &str, value: &str) {
320 let mut ops = self
321 .get_any_state::<Vec<HeaderOp>>(REQUEST_HEADER_PATCH)
322 .map(|arc| (*arc).clone())
323 .unwrap_or_default();
324 ops.push(HeaderOp::Set(name.to_string(), value.to_string()));
325 self.insert_any_state(REQUEST_HEADER_PATCH, ops);
326 }
327
328 fn set_response_header(&mut self, name: &str, value: &str) {
329 let mut ops = self
330 .get_any_state::<Vec<HeaderOp>>(RESPONSE_HEADER_PATCH)
331 .map(|arc| (*arc).clone())
332 .unwrap_or_default();
333 ops.push(HeaderOp::Set(name.to_string(), value.to_string()));
334 self.insert_any_state(RESPONSE_HEADER_PATCH, ops);
335 }
336
337 fn append_request_header(&mut self, name: &str, value: &str) {
338 let mut ops = self
339 .get_any_state::<Vec<HeaderOp>>(REQUEST_HEADER_PATCH)
340 .map(|arc| (*arc).clone())
341 .unwrap_or_default();
342 ops.push(HeaderOp::Append(name.to_string(), value.to_string()));
343 self.insert_any_state(REQUEST_HEADER_PATCH, ops);
344 }
345
346 fn append_response_header(&mut self, name: &str, value: &str) {
347 let mut ops = self
348 .get_any_state::<Vec<HeaderOp>>(RESPONSE_HEADER_PATCH)
349 .map(|arc| (*arc).clone())
350 .unwrap_or_default();
351 ops.push(HeaderOp::Append(name.to_string(), value.to_string()));
352 self.insert_any_state(RESPONSE_HEADER_PATCH, ops);
353 }
354
355 fn remove_request_header(&mut self, name: &str) {
356 let mut ops = self
357 .get_any_state::<Vec<HeaderOp>>(REQUEST_HEADER_PATCH)
358 .map(|arc| (*arc).clone())
359 .unwrap_or_default();
360 ops.push(HeaderOp::Remove(name.to_string()));
361 self.insert_any_state(REQUEST_HEADER_PATCH, ops);
362 }
363
364 fn remove_response_header(&mut self, name: &str) {
365 let mut ops = self
366 .get_any_state::<Vec<HeaderOp>>(RESPONSE_HEADER_PATCH)
367 .map(|arc| (*arc).clone())
368 .unwrap_or_default();
369 ops.push(HeaderOp::Remove(name.to_string()));
370 self.insert_any_state(RESPONSE_HEADER_PATCH, ops);
371 }
372
373 #[cfg(feature = "model")]
374 fn get_model_name(&self) -> Option<String> {
375 self.get_proxy_model_name()
376 }
377
378 #[cfg(feature = "model")]
379 fn get_model_provider(&self) -> Option<Provider> {
380 self.get_proxy_model_provider()
381 }
382
383 fn log(&self, level: i32, msg: &str) {
384 match level {
385 LOG_ERROR => log::error!("{}", msg),
386 LOG_WARN => log::warn!("{}", msg),
387 LOG_INFO => log::info!("{}", msg),
388 LOG_DEBUG => log::debug!("{}", msg),
389 LOG_TRACE => log::trace!("{}", msg),
390 _ => log::info!("{}", msg),
391 }
392 }
393
394 fn as_any_mut(&mut self) -> &mut dyn Any {
395 self
396 }
397}