Skip to content

Commit

Permalink
feat: working caching
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Jones <[email protected]>
  • Loading branch information
AlexsJones committed Oct 31, 2023
1 parent a1762e2 commit bc74b11
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 31 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "isotope"
version = "0.0.2"
version = "0.0.3"
repository = "https://github.com/isotope-rs/isotope.git"
edition = "2021"
description = "Isotope scans AWS services and makes suggestions on how to improve them using Artificial Intelligence."
Expand Down Expand Up @@ -37,6 +37,7 @@ aws-smithy-runtime-api = { version = "0.56.1", features = ["client"] }
unescape = "0.1.0"
aws-sdk-bedrock = "0.3.0"
aws-sdk-bedrockruntime = "0.3.0"
base64 = { version = "0.21.4", features = [] }

# Config for 'cargo dist'
[workspace.metadata.dist]
Expand Down
80 changes: 51 additions & 29 deletions src/analyze/mod.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::ops::DerefMut;
use crate::analyzer::analyzer_trait::Analyzer;
use crate::analyzer::types::AnalysisResults;
use crate::config::Conf;
use crate::{analyzer, Args, bedrock};
use crate::{analyzer, bedrock, Args};
use crate::{config, outputs};
use aws_config::meta::region::{ProvideRegion, RegionProviderChain};
use colored::Colorize;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::ops::DerefMut;
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
use colored::Colorize;

pub async fn run_analysis(args: &Args) {
let mut conf: Conf = config::Conf {
cloud: String::new(),
stored_advice: HashMap::new(),
};
let c = config::get_or_create_config();
match c {
Expand All @@ -26,14 +27,24 @@ pub async fn run_analysis(args: &Args) {
// Setup bedrock
let bedrockClient = bedrock::BedrockClient::new(config.clone());

println!("Current AWS region: {}", RegionProviderChain::default_provider().region().await.unwrap().as_ref().yellow());
println!(
"Current AWS region: {}",
RegionProviderChain::default_provider()
.region()
.await
.unwrap()
.as_ref()
.yellow()
);
// Create channels
let (tx, rx): (Sender<Vec<AnalysisResults>>, Receiver<Vec<AnalysisResults>>) = mpsc::channel();
let analyzers: Vec<Box<dyn Analyzer>> = analyzer::generate_analyzers(config.clone());

match &args.analyzer {
Some(analyzer_arg) => {
let filtered_analyzer = &analyzers.iter().find(|x| x.get_name().as_str() == analyzer_arg);
let filtered_analyzer = &analyzers
.iter()
.find(|x| x.get_name().as_str() == analyzer_arg);
match filtered_analyzer {
Some(x) => {
let thread_tx = tx.clone();
Expand Down Expand Up @@ -80,39 +91,50 @@ pub async fn run_analysis(args: &Args) {
task.await.unwrap();
}

let mut processed_results: HashMap<String,Vec<AnalysisResults>> = HashMap::new();
let mut processed_results: HashMap<String, Vec<AnalysisResults>> = HashMap::new();
// generate Vectors aligned to each analyzer type

// Feed results into Bedrock
for mut res in results {
if !res.message.is_empty() {
let result = bedrockClient.enrich(res.message.clone()).await;
// TODO: missing step to copy the bedrock result into res
match result {
Ok(x) => {
res.advice = x.clone();
// pass ownership over of advice
// check if the processed results analyzer exists as key
// upsert the analysis result into the vector
match processed_results.entry(x) {
Entry::Occupied(mut e) => {
e.get_mut().push(res);
},
Entry::Vacant(e) => {
e.insert(vec![res]);
},
}
// Check if the data is in the cache
match conf.fetch_from_cache(&res.message) {
Some(x) => {
res.advice = x.clone()
},
Err(e) => (
None => {
let result = bedrockClient.enrich(res.message.clone()).await;
// TODO: missing step to copy the bedrock result into res
match result {
Ok(x) => {
res.advice = x.clone();
// upsert into the cache for next time
conf.clone().upsert_into_cache(&res.message,&x);
// pass ownership over of advice
// check if the processed results analyzer exists as key
// upsert the analysis result into the vector

),
}
Err(e) => (),
}
}
}
match processed_results.entry(res.analyzer_name.clone()) {
Entry::Occupied(mut e) => {
e.get_mut().push(res);
}
Entry::Vacant(e) => {
e.insert(vec![res]);
}
}
}
}
//

match args.json {
Some(x) => {
let mut p = outputs::Processor::new(processed_results, Some(outputs::Configuration::new(x)));
let mut p = outputs::Processor::new(
processed_results,
Some(outputs::Configuration::new(x)),
);
p.print();
}
None => {
Expand Down
1 change: 1 addition & 0 deletions src/bedrock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::io::{stdout, Write};
use aws_sdk_config::config::Region;

mod prompt;

#[derive(Serialize)]
struct ClaudParams {
prompt: String,
Expand Down
49 changes: 49 additions & 0 deletions src/config/cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use std::error::Error;
use std::io::Bytes;
use crate::config::{Conf, save_config};
use base64::{Engine as _, engine::{self, general_purpose}, alphabet};

impl Conf {
fn encode_cache_key(&self,raw_cache_key: &str) -> String {
general_purpose::STANDARD.encode(raw_cache_key)
}

pub fn upsert_into_cache(mut self, raw_cache_key: &str, payload: &str) -> Result<(), Box<dyn Error>> {

let encoded_key = self.encode_cache_key(raw_cache_key);
let encoded_payload = self.encode_cache_key(payload);
// append the new data to the hashmap
self.stored_advice.insert(encoded_key,
encoded_payload);

// Save the config
save_config(self)?;
Ok(())
}
pub fn fetch_from_cache(&self,raw_cache_key: &str) -> Option<String> {
let encoded_key = self.encode_cache_key(raw_cache_key);
let found_keys: Vec<&String> = self.stored_advice.iter().filter_map(|(k,v)|
if k == encoded_key.as_str() {Some(v)} else {None}).collect();

// TODO: verify this
if found_keys.is_empty() {
return None
}

let mut found = String::new();
let decoded_bytes = match general_purpose::STANDARD.decode(found_keys.first().unwrap().to_string()) {
Ok(bytes) => {
let decoded_string = match String::from_utf8(bytes) {
Ok(s) => found = s,
Err(e) => {
eprintln!("Error converting bytes to String: {}", e);
}
};
},
Err(e) => {
eprintln!("Error decoding Base64: {}", e);
}
};
Some(found)
}
}
9 changes: 8 additions & 1 deletion src/config/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
mod cache;

use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use simple_home_dir::*;
use std::error::Error;
Expand All @@ -7,9 +10,12 @@ use std::path::Path;

pub const CONFFILE: &str = "isotope.config";

#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Conf {
pub cloud: String,
// This is stored as a hashed string representing the issue e.g. S3 bucket public
// With a subsequent value (also b64 encoded)
pub stored_advice: HashMap<String,String>
}
pub fn get_conf_path() -> String {
let home = home_dir().unwrap();
Expand All @@ -33,6 +39,7 @@ pub fn get_or_create_config() -> Result<Conf, Box<dyn Error>> {
let p = get_conf_path();
let c = Conf {
cloud: String::new(),
stored_advice: HashMap::new(),
};
if !Path::new(&p).exists() {
let mut f = File::create(&p)?;
Expand Down

0 comments on commit bc74b11

Please sign in to comment.