From ca13154c2afa697b8d437e87d201c31f82d337e1 Mon Sep 17 00:00:00 2001 From: OhOhWonOne <7220+njfio@users.noreply.github.com> Date: Wed, 8 May 2024 17:14:22 -0400 Subject: [PATCH] Refactor code and update configurations (v.0.3.5.5-cleanup) his commit updates various files including Cargo.toml, config.json, and multiple source code files (client.rs, config.rs, lib.rs, and main.rs) to refactor code and configurations as part of the v.0.3.5.5-cleanup branch. building clean no warnings or issues. --- fluent_cli/Cargo.toml | 3 +- fluent_cli/config.json | 31 ++- .../functional_tests/functional_test_02.sh | 44 ++-- fluent_cli/src/client.rs | 249 ++---------------- fluent_cli/src/config.rs | 16 +- fluent_cli/src/lib.rs | 4 +- fluent_cli/src/main.rs | 47 ++-- 7 files changed, 108 insertions(+), 286 deletions(-) diff --git a/fluent_cli/Cargo.toml b/fluent_cli/Cargo.toml index 046ffde..9deab4e 100644 --- a/fluent_cli/Cargo.toml +++ b/fluent_cli/Cargo.toml @@ -10,13 +10,14 @@ serde_json = "1.0" reqwest = { version = "0.11", features = ["json", "blocking", "multipart", "stream"] } tokio = { version = "1", features = ["full"] } clap = { version = "3.0", features = ["derive"] } +clap_complete_fig = "3.1" tokio-console = "0.1" # For debugging async tasks config = "0.12" log = "0.4" env_logger = "0.9" atty = "0.2.14" amber = "0.2.0" -base64 = "0.21.7" +base64 = "0.22.1" infer = "0.8.0" pulldown-cmark = "0.9.0" regex = "1.10.4" diff --git a/fluent_cli/config.json b/fluent_cli/config.json index 7982b0d..59a073a 100644 --- a/fluent_cli/config.json +++ b/fluent_cli/config.json @@ -387,8 +387,9 @@ "allowImageUploads": false, "temperature": 0.9, "frequencyPenalty": 0.9, + "systemMessage": "You are a helpful assistant", "openAIToolAgent_0": { - "systemMessage": "You are a helpful assistant" + } }, "tweaks": { @@ -495,6 +496,34 @@ }, "timeout_ms": 500000 }, + { + "name": "GroqLLama370bToolAgentRepoCloud", + "engine": "flowise", + "protocol": "https", + "hostname": "flowise.fluentcli.com", + "port": 443, + "chat_id": "19e0c3b5-d5ff-40aa-bf6a-bc6db22a7a58", + "request_path": "/api/v1/prediction/", + "sessionId": "", + "bearer_token": "AMBER_REPO_CLOUD_FLUENT_DEMO_KEY", + "overrideConfig": { + "sessionId": "AMBER_FLUENT_SESSION_ID_01", + "groqApiKey": "AMBER_FLUENT_GROQ_API_KEY_01", + "serpApiKey": "AMBER_FLUENT_SERPAPI_KEY_01", + "chainName": "groqChain", + "systemMessagePrompt": "You are a helpful assistant", + "temperature": 0.8, + "openAIApiKey": { + "openAIEmbeddings_0": "AMBER_FLUENT_OPENAI_API_KEY_01" + }, + "modelName":"llama3-70b-8192", + "memoryKey": "AMBER_FLUENT_SESSION_ID_01" + }, + "tweaks": { + + }, + "timeout_ms": 500000 + }, { "name": "GroqGemma7bAgentRepoCloud", "engine": "flowise", diff --git a/fluent_cli/functional_tests/functional_test_02.sh b/fluent_cli/functional_tests/functional_test_02.sh index 0fcbdb1..7783c77 100755 --- a/fluent_cli/functional_tests/functional_test_02.sh +++ b/fluent_cli/functional_tests/functional_test_02.sh @@ -76,52 +76,52 @@ for FLOWNAME in "${FLOWNAMES[@]}"; do echo "" run_test "$FLOWNAME" "Base Command Test" \ - "$CLI_PATH $FLOWNAME 'This is a test, respond that this is a test'" \ + "$CLI_PATH $FLOWNAME 'This is a test, reply this is a test'" \ "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request is about this is a test'" run_test "$FLOWNAME" "Stdin Context Test" \ - "cat \"$CONTEXT_FILE\" | $CLI_PATH $FLOWNAME 'Repeat what the context'" \ - "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request has the word northstar or North Star'" + "cat \"$CONTEXT_FILE\" | $CLI_PATH $FLOWNAME 'This is a test, Summarize this'" \ + "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request references the word northstar or North Star'" run_test "$FLOWNAME" "Additional Context File Test" \ - "$CLI_PATH $FLOWNAME 'Repeat the outline ' --additional-context-file \"$OUTLINE_FILE\"" \ - "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request contains the word TheLardCatFellFlatOnTheMat'" + "$CLI_PATH $FLOWNAME 'This is a test, Summarize what I provide ' --additional-context-file \"$OUTLINE_FILE\"" \ + "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request references the word TheLardCatFellFlatOnTheMat'" run_test "$FLOWNAME" "Combined Stdin and Additional Context Test" \ - "cat \"$CONTEXT_FILE\" | $CLI_PATH $FLOWNAME 'Repeat what I provide for context, and for the outline'' --additional-context-file \"$OUTLINE_FILE\"" \ - "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request contains TheLardCatFellFlatOnTheMat and talks about the word northstar or North Star'" + "cat \"$CONTEXT_FILE\" | $CLI_PATH $FLOWNAME 'This is a test, Summarize what I provide: ' --additional-context-file \"$OUTLINE_FILE\"" \ + "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the response contains TheLardCatFellFlatOnTheMat and references the word northstar or North Star'" run_test "$FLOWNAME" "Base Command Test and --system-prompt-override-inline" \ "$CLI_PATH $FLOWNAME 'This is a test, respond that this is a test' --system-prompt-override-inline 'You can only reply in German'" \ - "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request is about this is a test and is in German'" + "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if request is in German'" run_test "$FLOWNAME" "Stdin Context Test and --system-prompt-override-inline" \ - "cat \"$CONTEXT_FILE\" | $CLI_PATH $FLOWNAME 'Repeat what I provide for context' --system-prompt-override-inline 'You can only reply in German' " \ - "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request has the word northstar or North Star and is in German'" + "cat \"$CONTEXT_FILE\" | $CLI_PATH $FLOWNAME 'This is a test, Summarize what what I provide in the request:' --system-prompt-override-inline 'You can only reply in German' " \ + "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request is partly in German'" run_test "$FLOWNAME" "Additional Context File Test and --system-prompt-override-inline" \ - "$CLI_PATH $FLOWNAME 'Repeat what I provide for context, and for the outline' --additional-context-file \"$OUTLINE_FILE\" --system-prompt-override-inline 'You can only reply in German' " \ - "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request contains the word TheLardCatFellFlatOnTheMat and is in German'" + "$CLI_PATH $FLOWNAME 'This is a test, Summarize what I provided in the request' --additional-context-file \"$OUTLINE_FILE\" --system-prompt-override-inline 'You can only reply in German' " \ + "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request is partly in German'" run_test "$FLOWNAME" "Combined Stdin and Additional Context Test and --system-prompt-override-inline" \ - "cat \"$CONTEXT_FILE\" | $CLI_PATH $FLOWNAME 'Repeat what I provide for context, and for the outline' --additional-context-file \"$OUTLINE_FILE\" --system-prompt-override-inline 'You can only reply in German'" \ - "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request contains TheLardCatFellFlatOnTheMat and talks about the word northstar or North Star and is in German'" + "cat \"$CONTEXT_FILE\" | $CLI_PATH $FLOWNAME 'This is a test, Summarize what what I provide in the request' --additional-context-file \"$OUTLINE_FILE\" --system-prompt-override-inline 'You can only reply in German'" \ + "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request is partly in German'" run_test "$FLOWNAME" "Base Command Test and --system-prompt-override-file" \ - "$CLI_PATH $FLOWNAME 'This is a test, respond that this is a test' --system-prompt-override-file \"$SYSTEM_PROMPT_FILE\" " \ - "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request is about this is a test and is in Spanish'" + "$CLI_PATH $FLOWNAME 'This is a test, Summarize that this is a test' --system-prompt-override-file \"$SYSTEM_PROMPT_FILE\" " \ + "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request has spanish words'" run_test "$FLOWNAME" "Stdin Context Test and --system-prompt-override-file" \ - "cat \"$CONTEXT_FILE\" | $CLI_PATH $FLOWNAME 'Repeat what I provide for context:' --system-prompt-override-file \"$SYSTEM_PROMPT_FILE\" " \ - "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request has the word northstar or North Star and is in Spanish'" + "cat \"$CONTEXT_FILE\" | $CLI_PATH $FLOWNAME 'This is a test, Summarize what with what I provide in the request ' --system-prompt-override-file \"$SYSTEM_PROMPT_FILE\" " \ + "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request has Spanish words'" run_test "$FLOWNAME" "Additional Context File Test and --system-prompt-override-file" \ - "$CLI_PATH $FLOWNAME 'Repeat what I provide for context, and for the outline' --additional-context-file \"$OUTLINE_FILE\" --system-prompt-override-file \"$SYSTEM_PROMPT_FILE\" " \ - "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request contains the word TheLardCatFellFlatOnTheMat and is in Spanish'" + "$CLI_PATH $FLOWNAME 'This is a test, Summarize what I provide in the request' --additional-context-file \"$OUTLINE_FILE\" --system-prompt-override-file \"$SYSTEM_PROMPT_FILE\" " \ + "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request has Spanish words'" run_test "$FLOWNAME" "Combined Stdin and Additional Context Test and --system-prompt-override-file" \ - "cat \"$CONTEXT_FILE\" | $CLI_PATH $FLOWNAME 'Repeat what I provide for context, and for the outline' --additional-context-file \"$OUTLINE_FILE\" --system-prompt-override-file \"$SYSTEM_PROMPT_FILE\" " \ - "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request contains TheLardCatFellFlatOnTheMat and talks about the word northstar or North Star and is in Spanish'" + "cat \"$CONTEXT_FILE\" | $CLI_PATH $FLOWNAME 'This is a test, Summarize what I provide in the request:' --additional-context-file \"$OUTLINE_FILE\" --system-prompt-override-file \"$SYSTEM_PROMPT_FILE\" " \ + "$VALIDATION_CLI $VALIDATION_FLOWNAME 'Answer PASS or FAIL only if the request has Spanish words'" done diff --git a/fluent_cli/src/client.rs b/fluent_cli/src/client.rs index 856e21c..6586aee 100644 --- a/fluent_cli/src/client.rs +++ b/fluent_cli/src/client.rs @@ -1,4 +1,4 @@ -use log::{debug, error}; +use log::{debug}; use std::env; use reqwest::{Client}; @@ -6,18 +6,14 @@ use serde_json::{json, Value}; use crate::config::{FlowConfig, replace_with_env_var}; - use serde::{Deserialize, Serialize}; use serde_json::Result; use tokio::fs::File; use tokio::io::AsyncReadExt; - // Add serde_yaml to your Cargo.toml if not already included - - #[derive(Serialize, Deserialize, Debug)] -struct FluentCliOutput { +pub struct FluentCliOutput { pub(crate) text: String, pub(crate) question: String, #[serde(rename = "chatId")] @@ -40,7 +36,8 @@ struct Question { #[derive(Serialize, Deserialize)] struct RequestPayload { question: String, - overrideConfig: std::collections::HashMap, + #[serde(rename="overrideConfig")] + override_config: std::collections::HashMap, uploads: Option>, } @@ -53,6 +50,7 @@ struct Upload { } #[derive(Debug)] +#[allow(dead_code)] struct ResponseOutput { response_text: Option, question: Option, @@ -130,19 +128,19 @@ pub async fn handle_langflow_response(response_body: &str, matches: &clap::ArgMa .collect::>() .join("\n"); - if let Some(directory) = matches.value_of("download-media") { + if let Some(directory) = matches.get_one::("download-media").map(|s| s.as_str()) { let urls = extract_urls(&response_text); // Adjust the URL extraction as needed download_media(urls, directory).await; } - if matches.is_present("markdown-output") { + if matches.contains_id("markdown-output") { pretty_format_markdown(&response_text); - } else if matches.is_present("parse-code-output") { + } else if matches.contains_id("parse-code-output") { let code_blocks = extract_code_blocks(&response_text); for block in code_blocks { println!("{}", block); } - } else if matches.is_present("full-output") { + } else if matches.contains_id("full-output") { println!("{}", response_body); // Output the full raw response } else { println!("{}", response_text); // Default output @@ -150,7 +148,7 @@ pub async fn handle_langflow_response(response_body: &str, matches: &clap::ArgMa }, Err(e) => { eprintln!("Error parsing LangFlow response: {:?}", e); - if let Some(directory) = matches.value_of("download-media") { + if let Some(directory) = matches.get_one::("download-media").map(|s| s.as_str()) { let urls = extract_urls(response_body); // Fallback to raw response download_media(urls, directory).await; } @@ -161,34 +159,9 @@ pub async fn handle_langflow_response(response_body: &str, matches: &clap::ArgMa Ok(()) } -use terminal_size::{Width, terminal_size}; - -pub fn generate_tick_strings() -> Vec { - if let Some((Width(w), _)) = terminal_size() { - let total_width = w as usize; - let bar_length = total_width / 3; // Calculate 33% of the terminal width - let mut ticks = vec![]; - let tick_symbol = "⫸"; - let mut current_length = 0; - - while current_length < bar_length { - let tick = tick_symbol.repeat(current_length / 2 + 1); - ticks.push(tick); - current_length += 2; - } - // Add reverse order to complete the cycle - let mut reverse_ticks = ticks.clone(); - reverse_ticks.reverse(); - ticks.extend(reverse_ticks); - ticks - } else { - vec!["".to_string(); 20] // Default fallback if terminal size cannot be determined - } -} -use serde_json::Error as SerdeError; pub async fn handle_response(response_body: &str, matches: &clap::ArgMatches) -> Result<()> { // Parse the response body, handle error properly here instead of unwrapping @@ -200,18 +173,18 @@ pub async fn handle_response(response_body: &str, matches: &clap::ArgMatches) -> Ok(parsed_output) => { // If parsing is successful, use the parsed data debug!("{:?}", parsed_output); - if let Some(directory) = matches.value_of("download-media") { + if let Some(directory) = matches.get_one::("download-media").map(|s| s.as_str()) { let urls = extract_urls(response_body); // Assume extract_urls can handle any text download_media(urls, directory).await; } - if matches.is_present("markdown-output") { + if matches.contains_id("markdown-output") { pretty_format_markdown(&parsed_output.text); // Output the text used, whether parsed or raw, but only if the --markdown-output flag is not - } else if matches.is_present("parse-code-output") { + } else if matches.contains_id("parse-code-output") { let code_blocks = extract_code_blocks(&parsed_output.text); for block in code_blocks { println!("{}", block); } - } else if matches.is_present("full-output") { + } else if matches.contains_id("full-output") { println!("{}", response_body); // Output the text used, whether parsed or raw } else { println!("{}", parsed_output.text); // Output the text used, whether parsed or raw, but only if the --markdown-output flag is not set").text; @@ -222,7 +195,7 @@ pub async fn handle_response(response_body: &str, matches: &clap::ArgMatches) -> if let Some(cause) = e.source() { eprintln!("{:?}", cause); } - if let Some(directory) = matches.value_of("download-media") { + if let Some(directory) = matches.get_one::("download-media").map(|s| s.as_str()) { let urls = extract_urls(response_body); // Assume extract_urls can handle any text debug!("Extracted URLs: {:?}", urls); download_media(urls, directory).await; @@ -245,24 +218,12 @@ fn extract_urls(text: &str) -> Vec { .collect() } -use term_size; + pub fn print_full_width_bar(string: &str) -> String { - let buffer = 1; let width = terminal_size::terminal_size().map(|(terminal_size::Width(w), _)| w as usize).unwrap_or(80); string.repeat(width).dark_yellow().to_string() } -pub fn print_nearly_full_width_bar() -> String { - let total_width = terminal_size::terminal_size() - .map(|(terminal_size::Width(w), _)| w as usize) - .unwrap_or(80); // Default to 80 if terminal size can't be determined - - let bar_length = total_width * 85 / 100; // Calculate 90% of the terminal width for the bar length - let padding = (total_width - bar_length) / 2; // Calculate padding to center the bar - - let bar = "-".repeat(bar_length).yellow().to_string(); - format!("{:padding$}{}{:padding$}", "", bar, "", padding = padding) -} use termimad::*; fn pretty_format_markdown(markdown_content: &str) { @@ -280,7 +241,7 @@ fn pretty_format_markdown(markdown_content: &str) { skin.code_block.set_fg(crossterm::style::Color::White); //skin.set_bg(crossterm::style::Color::Black); - skin.paragraph.left_margin = 4;; + skin.paragraph.left_margin = 4; skin.paragraph.right_margin = 4; let formatted_text = skin.print_text(markdown_content); // skin.display_markdown(&format!("\n{}\n", markdown_content))); // @@ -297,10 +258,6 @@ fn extract_code_blocks(markdown_content: &str) -> Vec { .collect() } -pub fn parse_fluent_cli_output(json_data: &str) -> Result { - let output: FluentCliOutput = serde_json::from_str(json_data)?; - Ok(output) -} use reqwest; use tokio::io::AsyncWriteExt; @@ -392,150 +349,9 @@ pub async fn send_request(flow: &FlowConfig, payload: &Value) -> reqwest::Resul } -pub async fn process_webhook_payload(flow: &FlowConfig, request: &str, file_contents: String, context: Option<&str>, file_path: Option<&str>) -> Result<()> { - debug!("Processing WebhookFlow: {:?}", flow); - - let client = reqwest::Client::new(); - let bearer_token = if flow.bearer_token.starts_with("AMBER_") { - env::var(&flow.bearer_token[6..]).unwrap_or_else(|_| flow.bearer_token.clone()) - } else { - flow.bearer_token.clone() - }; - debug!("Bearer token: {}", bearer_token); - - let mut override_config = flow.override_config.clone(); - debug!("Override config before update: {:?}", override_config); - replace_with_env_var(&mut override_config); - debug!("Override config after update: {:?}", override_config); - - let url = format!("{}://{}:{}{}{}", flow.protocol, flow.hostname, flow.port, flow.request_path, flow.chat_id); - - let request_builder = client.post(&url); - - - let mut form = Form::new(); - let file_paths_clone = file_path.clone(); - - for file_path_item in file_paths_clone.iter() { - let path = Path::new(file_path_item); - debug!("File path: {}", path.display()); - let mime_type = mime_guess::from_path(path).first_or_octet_stream().essence_str().to_string(); // Convert to String here - debug!("MIME type: {}", mime_type); - let mut file = match File::open(path).await { - Ok(f) => f, - Err(e) => return Err(to_serde_json_error(e)), - }; - debug!("File opened: {}", file_path_item); - let mut buffer = Vec::new(); - if let Err(e) = file.read_to_end(&mut buffer).await { - return Err(to_serde_json_error(e)); - } - - let part = Part::bytes(buffer) - .file_name(path.file_name().unwrap().to_str().unwrap().to_owned()) - .mime_str(&mime_type).map_err(to_serde_json_error)?; // Use a reference to the owned String - - form = form.part("files", part); - } - - - // Constructing the payload - let payload = json!({ - - "request": request, - "file_content": file_contents, - "context": context, - "override_config": override_config - }); - - debug!("Webhook Payload: {:?}", payload); - let response = request_builder - .header("Authorization", format!("Bearer {}", bearer_token)) - .json(&payload) - .send() - .await; - - debug!("Webhook Response: {:?}", response); - match response { - Ok(resp) => { - if resp.status().is_success() { - debug!("Webhook payload successfully sent."); - } else { - error!("Failed to send webhook payload: {}", resp.status()); - return Err(serde_json::Error::custom("Failed to send webhook payload: {}")); - } - }, - Err(e) => { - error!("Failed to send request: {}", e); - return Err(serde_json::Error::custom("Failed to send webhook payload: {}")); - } - } - - Ok(()) -} - - -pub(crate) fn build_request_payload(question: &str, context: Option<&str>) -> Value { - // Construct the basic question - let full_question = if let Some(ctx) = context { - format!("{} {}", question, ctx) // Concatenate question and context - } else { - question.to_string() // Use question as is if no context - }; - - // Start building the payload with the question - let mut payload = json!({ - "question": full_question, // Use the potentially modified question - }); - - // Add the context to the payload if it exists - if let Some(ctx) = context { - payload.as_object_mut().unwrap().insert("context".to_string(), serde_json::Value::String(ctx.to_string())); - } - - payload - -} - - - -use thiserror::Error; -use reqwest::Error as ReqwestError; -use serde_json::Error as SerdeJsonError; -use std::io::Error as IoError; - -#[derive(Error, Debug)] -pub enum MyError { - #[error("Network error: {0}")] - Network(#[from] ReqwestError), - - #[error("JSON error: {0}")] - Json(#[from] SerdeJsonError), - - #[error("I/O error: {0}")] - Io(#[from] IoError), - - #[error("Other error: {0}")] - Other(String), -} - - - -// Function returns Result<(), serde_json::Error - - use std::error::Error as StdError; // Import the StdError trait for `source` method - - -fn to_json_error(error: E) -> SerdeError { - // Simulate a JSON parsing error - let faulty_json = format!("{{: \"{}\"", error); // Intentionally malformed JSON - serde_json::from_str::(&faulty_json).unwrap_err() -} - - fn to_serde_json_error(err: E) -> serde_json::Error { serde_json::Error::custom(err.to_string()) } @@ -621,20 +437,12 @@ pub async fn upload_files(api_url: &str, file_paths: Vec<&str>) -> Result<()> { } - - - use tokio::fs::File as TokioFile; // Alias to avoid confusion with std::fs::File use tokio::io::{AsyncReadExt as TokioAsyncReadExt, Result as IoResult}; -use base64::encode; - - use std::path::Path; use clap::ArgMatches; - - use regex::Regex; use reqwest::multipart::{Form, Part}; use serde::de::Error; @@ -642,8 +450,9 @@ use serde::de::Error; use termimad::{MadSkin}; use termimad::crossterm::style::Stylize; +use base64::{engine::general_purpose::STANDARD, Engine}; -pub(crate) async fn prepare_payload(flow: &FlowConfig, question: &str, file_path: Option<&str>, actual_final_context: Option, cli_args: &ArgMatches, file_contents: &str, +pub async fn prepare_payload(flow: &FlowConfig, question: &str, file_path: Option<&str>, actual_final_context: Option, cli_args: &ArgMatches, _file_contents: &str, ) -> IoResult { let mut override_config = flow.override_config.clone(); let mut tweaks_config = flow.tweaks.clone(); @@ -683,13 +492,15 @@ pub(crate) async fn prepare_payload(flow: &FlowConfig, question: &str, file_path }; - - if cli_args.is_present("upload-image-path") && file_path.is_some() { + if cli_args.contains_id("upload-image-path") && file_path.is_some() { let path = file_path.unwrap(); let mut file = TokioFile::open(path).await?; let mut buffer = Vec::new(); TokioAsyncReadExt::read_to_end(&mut file, &mut buffer).await?; - let encoded_image = encode(&buffer); + + // Encoding using the STANDARD engine + let encoded_image = STANDARD.encode(&buffer); // Correct use of the encode method with the STANDARD engine + let uploads = json!([{ "data": format!("data:image/png;base64,{}", encoded_image), "type": "file", @@ -699,17 +510,5 @@ pub(crate) async fn prepare_payload(flow: &FlowConfig, question: &str, file_path body.as_object_mut().unwrap().insert("uploads".to_string(), uploads); } - if flow.engine == "webhook" { - let webhook_details = json!({ - "question": question.to_string(), - "context": actual_final_context.unwrap_or_default(), - "file_contents": file_contents - }); - // Assuming additional customization for webhook is done here - body.as_object_mut().unwrap().insert("webhook_details".to_string(), json!({ - "webhook": webhook_details - })); - } - Ok(body) } diff --git a/fluent_cli/src/config.rs b/fluent_cli/src/config.rs index 3818bdf..a094f16 100644 --- a/fluent_cli/src/config.rs +++ b/fluent_cli/src/config.rs @@ -70,6 +70,7 @@ pub(crate) struct EnvVarGuard { keys: Vec, } +#[allow(dead_code)] impl EnvVarGuard { pub fn new() -> Self { EnvVarGuard { keys: Vec::new() } @@ -164,8 +165,6 @@ impl Drop for EnvVarGuard { } - - pub fn load_config() -> Result, Box> { let config_path = env::var("FLUENT_CLI_CONFIG_PATH") .map_err(|_| "FLUENT_CLI_CONFIG_PATH environment variable is not set")?; @@ -177,19 +176,6 @@ pub fn load_config() -> Result, Box> { Ok(configs) } -pub fn generate_json_autocomplete_script() -> String { - return format!(r#" -# Assuming FLUENT_CLI_CONFIG_PATH points to a JSON file containing configuration -autocomplete_flows() {{ - local current_word="${{COMP_WORDS[COMP_CWORD]}}" - local flow_names=$(jq -r '.[].name' "$FLUENT_CLI_CONFIG_PATH") - COMPREPLY=($(compgen -W "${{flow_names}}" -- "$current_word")) -}} -complete -F autocomplete_flows fluent_cli -"#) -} - - pub fn generate_bash_autocomplete_script() -> String { diff --git a/fluent_cli/src/lib.rs b/fluent_cli/src/lib.rs index 57fad5f..c8d185e 100644 --- a/fluent_cli/src/lib.rs +++ b/fluent_cli/src/lib.rs @@ -1,2 +1,2 @@ -mod config; -mod client; \ No newline at end of file +pub mod config; +pub mod client; \ No newline at end of file diff --git a/fluent_cli/src/main.rs b/fluent_cli/src/main.rs index e297674..969acf2 100644 --- a/fluent_cli/src/main.rs +++ b/fluent_cli/src/main.rs @@ -1,29 +1,31 @@ -mod config; mod client; - +mod config; use clap::{Arg, Command}; + use tokio; use log::{debug}; + use env_logger; + use tokio::fs::File; -use tokio::io::{AsyncReadExt}; -use crate::client::{generate_tick_strings, handle_response, print_full_width_bar}; -use crate::config::{EnvVarGuard, generate_bash_autocomplete_script, replace_with_env_var}; +use tokio::io::{AsyncReadExt}; +use crate::client::{ handle_response, print_full_width_bar }; +use crate::config::{EnvVarGuard, generate_bash_autocomplete_script, replace_with_env_var}; use colored::*; // Import the colored crate use serde::de::Error as SerdeError; use indicatif::{ProgressBar, ProgressStyle}; + use std::time::Duration; -use colored::*; // Ensure the colored crate is included - // Ensure you've added `term_size` to your Cargo.toml + @@ -105,6 +107,11 @@ async fn main() -> Result<()> { .short('g') // Assigns a short flag .help("Generates a bash autocomplete script") .takes_value(false)) + .arg(Arg::new("generate-fig-autocomplete") + .long("generate-fig-autocomplete") + .short('g') // Assigns a short flag + .help("Generates a bash autocomplete script") + .takes_value(false)) .arg(Arg::new("parse-code-output") .long("parse-code-output") .short('p') // Assigns a short flag @@ -137,7 +144,7 @@ async fn main() -> Result<()> { .help("Uploads a file to the specified endpoint") .multiple_values(true) .required(false)) - .arg(Arg::with_name("webhook") + .arg(Arg::new("webhook") .long("webhook") .help("Sends the command payload to the webhook URL specified in config.json") .takes_value(false)) @@ -151,15 +158,16 @@ async fn main() -> Result<()> { return Ok(()); } - let flowname = matches.value_of("flowname").unwrap(); + + let flowname = matches.get_one::("flowname").map(|s| s.as_str()).unwrap(); let flow = configs.iter_mut().find(|f| f.name == flowname).context("Flow not found")?; let flow_clone = flow.clone(); let flow_clone2 = flow.clone(); let flow_clone3 = flow.clone(); - let request = matches.value_of("request").unwrap(); + let request = matches.get_one::("request").map(|s| s.as_str()).unwrap(); // Load context from stdin if not provided - let context = matches.value_of("context"); + let context = matches.get_one::("context").map(|s| s.as_str()); let mut additional_context = String::new(); if context.is_none() && !atty::is(atty::Stream::Stdin) { tokio::io::stdin().read_to_string(&mut additional_context).await?; @@ -170,8 +178,8 @@ async fn main() -> Result<()> { // Load override value from CLI if specified for system prompt override, file will always win - let system_prompt_inline = matches.value_of("system-prompt-override-inline"); - let system_prompt_file = matches.value_of("system-prompt-override-file"); + let system_prompt_inline = matches.get_one::("system-prompt-override-inline").map(|s| s.as_str()); + let system_prompt_file = matches.get_one::("system-prompt-override-file").map(|s| s.as_str()); // Load override value from file if specified let system_message_override = if let Some(file_path) = system_prompt_file { let mut file = File::open(file_path).await?; // Corrected async file opening @@ -197,12 +205,12 @@ async fn main() -> Result<()> { } - let file_path = matches.value_of("upload-image-path"); - let _file_path_clone = matches.value_of("upload-image-path").clone(); + let file_path = matches.get_one::("upload-image-path").map(|s| s.as_str()); + let _file_path_clone = matches.get_one::("upload-image-path").map(|s| s.as_str()); // Determine the final context from various sources - let file_context = matches.value_of("additional-context-file"); + let file_context = matches.get_one::("additional-context-file").map(|s| s.as_str()); let mut file_contents = String::new(); if let Some(file_path) = file_context { let mut file = File::open(file_path).await?; @@ -234,8 +242,8 @@ async fn main() -> Result<()> { debug!("EnvGuard result: {:?}", env_guard_result); // Within the main function after parsing command-line arguments - if let Some(files) = matches.values_of("upsert-with-upload") { - let file_paths: Vec<&str> = files.collect(); + if let Some(files) = matches.get_one::("upsert-with-upload").map(|s| s.as_str()) { + let file_paths: Vec<&str> = files.split(',').collect(); debug!("Uploading files: {:?}", file_paths); let flow = configs_clone.iter().find(|f| f.name == flowname).expect("Flow not found"); debug!("Flow: {:?}", flow); @@ -249,7 +257,7 @@ async fn main() -> Result<()> { } } - if let Some(json_str) = matches.value_of("upsert-no-upload") { + if let Some(json_str) = matches.get_one::("upsert-no-upload").map(|s| s.as_str()) { let inline_json: serde_json::Value = serde_json::from_str(json_str).unwrap_or_else(|err| { eprintln!("Error parsing inline JSON: {}", err); serde_json::json!({}) @@ -283,7 +291,6 @@ async fn main() -> Result<()> { } let spinner = ProgressBar::new_spinner(); - let tick_strings = generate_tick_strings(); spinner.set_style(ProgressStyle::default_spinner() .tick_strings(&[ "",