mirror of
https://github.com/katanemo/plano.git
synced 2026-04-28 18:36:34 +02:00
Add initial scaffolding
Signed-off-by: José Ulises Niño Rivera <junr03@users.noreply.github.com>
This commit is contained in:
parent
71eccbe2eb
commit
cd2a1db493
8 changed files with 376 additions and 18 deletions
114
envoyfilter/src/lib.rs
Normal file
114
envoyfilter/src/lib.rs
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
use log::info;
|
||||
use std::time::Duration;
|
||||
|
||||
use proxy_wasm::traits::*;
|
||||
use proxy_wasm::types::*;
|
||||
|
||||
proxy_wasm::main! {{
|
||||
proxy_wasm::set_log_level(LogLevel::Trace);
|
||||
proxy_wasm::set_root_context(|_| -> Box<dyn RootContext> {
|
||||
Box::new(HttpHeaderRoot {
|
||||
header_content: String::new(),
|
||||
})
|
||||
});
|
||||
}}
|
||||
|
||||
struct HttpHeader {
|
||||
context_id: u32,
|
||||
header_content: String,
|
||||
}
|
||||
|
||||
// HttpContext is the trait that allows the Rust code to interact with HTTP objects.
|
||||
impl HttpContext for HttpHeader {
|
||||
// Envoy's HTTP model is event driven. The WASM ABI has given implementors events to hook onto
|
||||
// the lifecycle of the http request and response.
|
||||
fn on_http_request_headers(&mut self, _num_headers: usize, _end_of_stream: bool) -> Action {
|
||||
// Example of reading the HTTP headers on the incoming request
|
||||
for (name, value) in &self.get_http_request_headers() {
|
||||
info!("#{} -> {}: {}", self.context_id, name, value);
|
||||
}
|
||||
|
||||
// Example logic of branching based on a request header.
|
||||
match self.get_http_request_header(":path") {
|
||||
// If the path header is present and the path is /inline
|
||||
Some(path) if path == "/inline" => {
|
||||
// Dispatch an HTTP call inline. This is the model that we will use for the LLM routing host.
|
||||
self.dispatch_http_call(
|
||||
"httpbin",
|
||||
vec![
|
||||
(":method", "GET"),
|
||||
(":path", "/bytes/1"),
|
||||
(":authority", "httpbin.org"),
|
||||
],
|
||||
None,
|
||||
vec![],
|
||||
Duration::from_secs(5),
|
||||
)
|
||||
.unwrap();
|
||||
// Pause the filter until the out of band HTTP response arrives.
|
||||
Action::Pause
|
||||
}
|
||||
|
||||
// Otherwise let the HTTP request continue.
|
||||
_ => Action::Continue,
|
||||
}
|
||||
}
|
||||
|
||||
fn on_http_response_headers(&mut self, _: usize, _: bool) -> Action {
|
||||
// Note that the filter can add custom headers. In this case the header is coming from a config value.
|
||||
self.add_http_response_header("custom-header", self.header_content.as_str());
|
||||
Action::Continue
|
||||
}
|
||||
}
|
||||
|
||||
impl Context for HttpHeader {
|
||||
// Note that the event driven model continues here from the return of the on_http_request_headers above.
|
||||
fn on_http_call_response(&mut self, _: u32, _: usize, body_size: usize, _: usize) {
|
||||
if let Some(body) = self.get_http_call_response_body(0, body_size) {
|
||||
if !body.is_empty() && body[0] % 2 == 0 {
|
||||
info!("Access granted.");
|
||||
// This call allows the filter to continue operating on the HTTP request sent by the user.
|
||||
// In Katanemo's use case the call would continue after the LLM host has responded with routing
|
||||
// decisions.
|
||||
self.resume_http_request();
|
||||
return;
|
||||
}
|
||||
}
|
||||
info!("Access forbidden.");
|
||||
// This is an example of short-circuiting the http request and sending back a response to the client.
|
||||
// i.e there was never an external HTTP request made. This could be used for example if the user prompt requires
|
||||
// more information before it can be sent out to a third party API.
|
||||
self.send_http_response(
|
||||
403,
|
||||
vec![("Powered-By", "Katanemo")],
|
||||
Some(b"Access forbidden.\n"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
struct HttpHeaderRoot {
|
||||
header_content: String,
|
||||
}
|
||||
|
||||
impl Context for HttpHeaderRoot {}
|
||||
|
||||
// RootContext allows the Rust code to reach into the Envoy Config
|
||||
impl RootContext for HttpHeaderRoot {
|
||||
fn on_configure(&mut self, _: usize) -> bool {
|
||||
if let Some(config_bytes) = self.get_plugin_configuration() {
|
||||
self.header_content = String::from_utf8(config_bytes).unwrap()
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn create_http_context(&self, context_id: u32) -> Option<Box<dyn HttpContext>> {
|
||||
Some(Box::new(HttpHeader {
|
||||
context_id,
|
||||
header_content: self.header_content.clone(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn get_type(&self) -> Option<ContextType> {
|
||||
Some(ContextType::HttpContext)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
fn main() {
|
||||
println!("Hello, world!\nMy favourite number is {}", some_fn());
|
||||
}
|
||||
|
||||
fn some_fn() -> i32 {
|
||||
42
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn some_fn_is_42() {
|
||||
assert_eq!(some_fn(), 42);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue