diff --git a/Cargo.toml b/Cargo.toml index 941fb28..28f8015 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "crates/fluent-core", "crates/fluent-engines", "crates/fluent-storage", + "crates/fluent-sdk", "crates/fluent-lambda", ] @@ -17,16 +18,56 @@ fluent-cli = { path = "crates/fluent-cli" } fluent-core = { path = "crates/fluent-core" } fluent-engines = { path = "crates/fluent-engines" } fluent-storage = { path = "crates/fluent-storage" } -tokio = { version = "1", features = ["full"] } -clap = { version = "4.5.8", features = ["derive"] } -anyhow = "1.0" -env_logger = "0.11.3" -neo4rs = "0.8.0" -chrono = { version = "0.4", features = ["serde"] } -uuid = { version = "1.3", features = ["v4", "serde"] } +fluent-sdk = { path = "crates/fluent-sdk" } +tokio = { workspace = true, features = ["full"] } +clap = { workspace = true, features = ["derive"] } +anyhow = { workspace = true } +env_logger = { workspace = true } +neo4rs = { workspace = true } +chrono = { workspace = true, features = ["serde"] } +uuid = { workspace = true, features = ["v4", "serde"] } [workspace.dependencies] +reqwest = { version = "^0.12", default-features = false, features = [ + "json", + "stream", + "multipart", + "rustls-tls", +] } fluent-cli = { path = "crates/fluent-cli" } fluent-core = { path = "crates/fluent-core" } fluent-engines = { path = "crates/fluent-engines" } fluent-storage = { path = "crates/fluent-storage" } +fluent-sdk = { path = "crates/fluent-sdk" } +serde = { version = "^1.0" } +lambda_runtime = "^0.13" +serde_json = "^1.0" +tracing-subscriber = "^0.3" +tracing = "^0.1" +tokio = { version = "^1" } +anyhow = { version = "^1.0" } +strum = { version = "^0.26" } +clap = { version = "^4.5" } +env_logger = { version = "^0.11" } +neo4rs = { version = "^0.8" } +chrono = { version = "^0.4" } +uuid = { version = "^1.3" } +indicatif = "^0.17" +owo-colors = "^4.0" +regex = "^1.10" +serde_yaml = "^0.9" +async-trait = "^0.1" +log = "^0.4" +unicode-segmentation = "^1.11" +rust-stemmers = "^1.2" +stop-words = "^0.8" +base64 = "^0.22" +url = "^2.5" +termimad = "^0.30" +syntect = "^5.2" +pdf-extract = "^0.7" +tokio-util = { version = "^0.7" } +mime_guess = { version = "^2.0" } +futures-util = { version = "^0.3" } +tempfile = "^3.10" +futures = "^0.3" diff --git a/crates/fluent-cli/Cargo.toml b/crates/fluent-cli/Cargo.toml index a02573d..fde0e8d 100644 --- a/crates/fluent-cli/Cargo.toml +++ b/crates/fluent-cli/Cargo.toml @@ -6,18 +6,18 @@ default-run = "fluent-cli" [dependencies] -clap = { version = "4.5.8", features = ["derive"] } -fluent-core = { path = "../fluent-core" } -fluent-engines = { path = "../fluent-engines" } -tokio = { version = "1", features = ["full"] } -anyhow = "1.0" -log = "0.4.22" -uuid = { version = "1.9.1", features = ["v4"] } -serde_json = "1.0.120" -indicatif = "0.17.8" -owo-colors = "4.0.0" -regex = "1.10.5" -serde_yaml = "0.9.34" +clap = { workspace = true, features = ["derive"] } +fluent-core = { workspace = true } +fluent-engines = { workspace = true } +tokio = { workspace = true, features = ["full"] } +anyhow = { workspace = true } +log = { workspace = true } +uuid = { workspace = true, features = ["v4"] } +serde_json = { workspace = true } +indicatif = { workspace = true } +owo-colors = { workspace = true } +regex = { workspace = true } +serde_yaml = { workspace = true } #fluent-storage = { path = "../fluent-storage" } # is not used #clap_complete = "4.5.1" #is not used #atty = "0.2.14" "use standard std::io::IsTerminal" diff --git a/crates/fluent-core/Cargo.toml b/crates/fluent-core/Cargo.toml index 89bf422..60228a8 100644 --- a/crates/fluent-core/Cargo.toml +++ b/crates/fluent-core/Cargo.toml @@ -5,29 +5,29 @@ version = "0.1.0" edition = "2021" [dependencies] -reqwest = { version = "0.12.5", default-features = false, features = [ +neo4rs = { workspace = true } +reqwest = { workspace = true, default-features = false, features = [ "json", "rustls-tls", ] } -serde = { version = "1.0", features = ["derive"] } -anyhow = "1.0" -serde_json = "1.0.120" -async-trait = "0.1.80" -log = "0.4.22" -chrono = "0.4.38" -uuid = { version = "1.9.1", features = ["v4"] } -neo4rs = "0.7.1" -unicode-segmentation = "1.11.0" -rust-stemmers = "1.2.0" -stop-words = "0.8.0" -base64 = "0.22.1" -tokio = "1.39.2" -regex = "1.10.6" -url = "2.5.2" -termimad = "0.29.4" -syntect = "5.2.0" -owo-colors = "4.0.0" -pdf-extract = "0.7.7" +serde = { workspace = true, features = ["derive"] } +anyhow = { workspace = true } +serde_json = { workspace = true } +async-trait = { workspace = true } +log = { workspace = true } +chrono = { workspace = true } +uuid = { workspace = true, features = ["v4"] } +unicode-segmentation = { workspace = true } +rust-stemmers = { workspace = true } +stop-words = { workspace = true } +base64 = { workspace = true } +tokio = { workspace = true } +regex = { workspace = true } +url = { workspace = true } +termimad = { workspace = true } +syntect = { workspace = true } +owo-colors = { workspace = true } +pdf-extract = { workspace = true } #rust-bert = { version = "0.18.0" } #Is not used diff --git a/crates/fluent-engines/Cargo.toml b/crates/fluent-engines/Cargo.toml index e953b5b..a9e448c 100644 --- a/crates/fluent-engines/Cargo.toml +++ b/crates/fluent-engines/Cargo.toml @@ -5,28 +5,19 @@ version = "0.1.0" edition = "2021" [dependencies] -fluent-core = { path = "../fluent-core" } -reqwest = { version = "0.12.5", default-features = false, features = [ - "json", - "stream", - "multipart", - "rustls-tls", -] } -serde_json = "1.0.120" -anyhow = "1.0.86" -async-trait = "0.1.80" -log = "0.4.22" -tokio = "1.38.0" -tokio-util = "0.7.11" -base64 = "0.22.1" -mime_guess = "2.0.3" -serde = { version = "1.0.204", features = ["derive"] } -uuid = "1.9.1" -futures-util = "0.3.30" -tempfile = "3.10.1" -futures = "0.3.30" -strum = { version = "0.26.3", features = ["derive"] } - - -#indicatif = "0.17.8" -#clap = "4.5.8" +fluent-core = { workspace = true } +reqwest = { workspace = true } +serde_json = { workspace = true } +anyhow = { workspace = true } +async-trait = { workspace = true } +log = { workspace = true } +tokio = { workspace = true } +tokio-util = { workspace = true } +base64 = { workspace = true } +mime_guess = { workspace = true } +serde = { workspace = true, features = ["derive"] } +uuid = { workspace = true } +futures-util = { workspace = true } +tempfile = { workspace = true } +futures = { workspace = true } +strum = { workspace = true, features = ["derive"] } diff --git a/crates/fluent-lambda/Cargo.toml b/crates/fluent-lambda/Cargo.toml index 97f9903..5de952a 100644 --- a/crates/fluent-lambda/Cargo.toml +++ b/crates/fluent-lambda/Cargo.toml @@ -5,13 +5,14 @@ edition = "2021" [dependencies] +tokio = { workspace = true, features = ["macros"] } +anyhow = { workspace = true } fluent-core.workspace = true fluent-engines.workspace = true -serde = { version = "1.0" } -lambda_runtime = "0.13.0" -serde_json = "1.0.122" -tracing-subscriber = "0.3.18" -tracing = "0.1.40" -tokio = { version = "1", features = ["macros"] } -anyhow = "1.0.86" -strum = { version = "0.26.3", features = ["derive"] } +fluent-sdk.workspace = true +serde = { workspace = true } +lambda_runtime = { workspace = true } +serde_json = { workspace = true } +tracing-subscriber = { workspace = true } +tracing = { workspace = true } +strum = { workspace = true, features = ["derive"] } diff --git a/crates/fluent-lambda/src/main.rs b/crates/fluent-lambda/src/main.rs index 89c4257..8aaa9c0 100644 --- a/crates/fluent-lambda/src/main.rs +++ b/crates/fluent-lambda/src/main.rs @@ -1,5 +1,11 @@ -use fluent_lambda::{run, Request, Response}; +use fluent_sdk::FluentRequest; use lambda_runtime::{service_fn, Error, LambdaEvent}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize, Serialize)] +pub struct Response { + pub data: fluent_core::types::Response, +} #[tokio::main] async fn main() -> Result<(), Error> { @@ -8,13 +14,18 @@ async fn main() -> Result<(), Error> { .with_target(false) .without_time() .init(); - lambda_runtime::run(service_fn(|event: LambdaEvent| async { + lambda_runtime::run(service_fn(|event: LambdaEvent| async { lambda_handler(event).await })) .await } #[tracing::instrument(skip(event), fields(req_id = %event.context.request_id))] -async fn lambda_handler(event: LambdaEvent) -> Result { - run(event.payload).await.map_err(Error::from) +async fn lambda_handler(event: LambdaEvent) -> Result { + event + .payload + .run() + .await + .map_err(Error::from) + .map(|r| Response { data: r.data }) } diff --git a/crates/fluent-sdk/.gitignore b/crates/fluent-sdk/.gitignore new file mode 100644 index 0000000..c41cc9e --- /dev/null +++ b/crates/fluent-sdk/.gitignore @@ -0,0 +1 @@ +/target \ No newline at end of file diff --git a/crates/fluent-sdk/Cargo.toml b/crates/fluent-sdk/Cargo.toml new file mode 100644 index 0000000..51ac68a --- /dev/null +++ b/crates/fluent-sdk/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "fluent-sdk" +version = "0.1.0" +edition = "2021" + + +[dependencies] +tokio = { workspace = true, features = ["macros"] } +anyhow = { workspace = true } +fluent-core.workspace = true +fluent-engines.workspace = true +serde = { workspace = true } +serde_json = { workspace = true } +strum = { workspace = true, features = ["derive"] } +async-trait = { workspace = true } diff --git a/crates/fluent-lambda/src/config.json b/crates/fluent-sdk/src/config.json similarity index 99% rename from crates/fluent-lambda/src/config.json rename to crates/fluent-sdk/src/config.json index 8e5ce67..6c37fb7 100644 --- a/crates/fluent-lambda/src/config.json +++ b/crates/fluent-sdk/src/config.json @@ -1,7 +1,7 @@ { "engines": [ { - "name": "openai", + "name": "openai-chat-completions", "engine": "openai", "connection": { "protocol": "https", @@ -17,6 +17,7 @@ "temperature": 1.0, "top_p": 1, "n": 1, + "stop": null, "stream": false, "presence_penalty": 0, "frequency_penalty": 0 diff --git a/crates/fluent-lambda/src/lib.rs b/crates/fluent-sdk/src/lib.rs similarity index 52% rename from crates/fluent-lambda/src/lib.rs rename to crates/fluent-sdk/src/lib.rs index 9506a14..aa84fce 100644 --- a/crates/fluent-lambda/src/lib.rs +++ b/crates/fluent-sdk/src/lib.rs @@ -1,75 +1,98 @@ use anyhow::anyhow; -use fluent_core::config::load_engine_config; -use fluent_engines::create_engine; use serde::{Deserialize, Serialize}; use serde_json::Value; -use std::{collections::HashMap, pin::Pin}; +use std::collections::HashMap; use strum::{Display, EnumString}; +pub mod openai; -pub async fn run(request: Request) -> anyhow::Result { - let engine_name = request - .engine - .map(|t| t.to_string()) - .ok_or_else(|| anyhow!("Engine is required"))?; - - let config_content = include_str!("config.json"); - - let overrides = request - .overrides - .unwrap_or_default() - .into_iter() - .map(|kv| (kv.key, kv.value)) - .collect::>(); - - let credentials = request - .credentials - .unwrap_or_default() - .into_iter() - .map(|kv| (kv.key, kv.value)) - .collect::>(); - - let user_prompt = request - .request - .ok_or_else(|| anyhow!("Prompt is required"))?; - - let engine_config = load_engine_config(config_content, &engine_name, &overrides, &credentials)?; - - let max_tokens = engine_config - .parameters - .get("max_tokens") - .and_then(|v| v.as_i64()); - - //TODO: Add support for other extended input - let mut combined_request = user_prompt; - - if let Some(max_tokens) = max_tokens { - if combined_request.len() > max_tokens as usize { - combined_request.truncate(max_tokens as usize); - combined_request += "... [truncated]"; - } - } - - let engine = create_engine(&engine_config).await?; - - let fluent_request = fluent_core::types::Request { - flowname: engine_name, - payload: combined_request, - }; - - let fluent_response = Pin::from(engine.execute(&fluent_request)).await?; +pub mod prelude { + pub use crate::openai::*; + pub use crate::{FluentRequest, FluentSdkRequest, KeyValue}; +} - //TODO: Add support for other extended output +#[derive(Debug, Deserialize, Serialize)] +pub struct Response { + pub data: fluent_core::types::Response, +} +#[async_trait::async_trait] +pub trait FluentSdkRequest: Into + Clone { + fn as_request(&self) -> FluentRequest { + self.clone().into() + } + async fn run(&self) -> anyhow::Result { + self.as_request().run().await + } +} - Ok(Response { - data: fluent_response, - }) +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct FluentRequest { + // The template to use (openai or anthropic) + pub engine: Option, + // The credentials to be used on the request + pub credentials: Option>, + //Overrides for the configuration parameters + pub overrides: Option>, + // The user prompt to process + pub request: Option, + // Parse and display code blocks from the output + pub parse_code: Option, +} +impl FluentRequest { + pub async fn run(&self) -> anyhow::Result { + // Convert the implementing type into a FluentRequest + let request = self.clone(); + // Perform the run logic that was previously in the `run` function + let engine_name = request + .engine + .map(|t| t.to_string()) + .ok_or_else(|| anyhow!("Engine is required"))?; + let config_content = include_str!("config.json"); + let overrides = request.overrides.unwrap_or_default(); + let credentials = request + .credentials + .unwrap_or_default() + .into_iter() + .map(|kv| (kv.key, kv.value)) + .collect::>(); + let user_prompt = request + .request + .ok_or_else(|| anyhow!("Request is required"))?; + let engine_config = fluent_core::config::load_engine_config( + config_content, + &engine_name, + &overrides, + &credentials, + )?; + let max_tokens = engine_config + .parameters + .get("max_tokens") + .and_then(|v| v.as_i64()); + // Prepare the combined request + let mut combined_request = user_prompt; + if let Some(max_tokens) = max_tokens { + if combined_request.len() > max_tokens as usize { + combined_request.truncate(max_tokens as usize); + combined_request += "... [truncated]"; + } + } + let engine = fluent_engines::create_engine(&engine_config).await?; + let fluent_core_request = fluent_core::types::Request { + flowname: engine_name, + payload: combined_request, + }; + let fluent_core_response = + std::pin::Pin::from(engine.execute(&fluent_core_request)).await?; + Ok(Response { + data: fluent_core_response, + }) + } } -#[derive(Debug, PartialEq, EnumString, Serialize, Deserialize, Display)] -pub enum Template { - #[strum(ascii_case_insensitive, to_string = "openai")] - #[serde(alias = "openai")] - OpenAI, +#[derive(Debug, PartialEq, EnumString, Serialize, Deserialize, Display, Clone)] +pub enum EngineTemplate { + #[strum(ascii_case_insensitive, to_string = "openai-chat-completions")] + #[serde(alias = "openai-chat-completions", alias = "openai")] + OpenAIChatCompletions, #[strum(ascii_case_insensitive, to_string = "anthropic")] #[serde(alias = "anthropic")] @@ -189,38 +212,31 @@ pub enum Template { #[strum(ascii_case_insensitive)] DalleHorizontal, } - -#[derive(Debug, Deserialize, Serialize)] -pub struct Request { - // The template to use (openai or anthropic) - engine: Option