mirror of
https://github.com/TECHNOFAB11/pay-respects.git
synced 2025-12-11 22:10:09 +01:00
refactor: split request-ai into module
This commit is contained in:
parent
fedee1dc8d
commit
c9b9f66f89
13 changed files with 151 additions and 106 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
|
@ -471,17 +471,25 @@ name = "pay-respects"
|
|||
version = "0.5.15"
|
||||
dependencies = [
|
||||
"colored",
|
||||
"curl",
|
||||
"inquire",
|
||||
"pay-respects-parser",
|
||||
"pay-respects-utils",
|
||||
"regex-lite",
|
||||
"rust-i18n",
|
||||
"sys-locale",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pay-respects-fallback-request-ai"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"colored",
|
||||
"curl",
|
||||
"rust-i18n",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sys-locale",
|
||||
"textwrap",
|
||||
"toml 0.8.19",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ members = [
|
|||
"pay-respects-utils",
|
||||
# optional modules
|
||||
"pay-respects-module-runtime-rules",
|
||||
# "pay-respects-module-request-ai",
|
||||
"pay-respects-fallback-request-ai",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
|
|
|
|||
21
pay-respects-fallback-request-ai/Cargo.toml
Normal file
21
pay-respects-fallback-request-ai/Cargo.toml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
[package]
|
||||
name = "pay-respects-fallback-request-ai"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
colored = "2"
|
||||
sys-locale = "0.3"
|
||||
rust-i18n = "3"
|
||||
serde_json = { version = "1.0" }
|
||||
serde = { version = "1.0", features = ["derive"]}
|
||||
textwrap = { version = "0.16", features = ["terminal_size"] }
|
||||
|
||||
|
||||
curl = { version = "0.4", optional = true }
|
||||
|
||||
[features]
|
||||
# linking to libcurl dynamically requires openssl when compiling and
|
||||
# complicates cross compilation
|
||||
libcurl = ["dep:curl"]
|
||||
|
||||
14
pay-respects-fallback-request-ai/i18n/i18n.toml
Normal file
14
pay-respects-fallback-request-ai/i18n/i18n.toml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
_version = 2
|
||||
|
||||
[ai-suggestion]
|
||||
en = "Suggestion from AI"
|
||||
es = "Sugerencia de la IA"
|
||||
de = "Vorschlag von KI"
|
||||
fr = "Suggestion de l'IA"
|
||||
it = "Proposta dall'IA"
|
||||
pt = "Sugestão da IA"
|
||||
ru = "Предложение от ИИ"
|
||||
ja = "AIからの提案"
|
||||
ko = "AI 제안"
|
||||
zh = "AI 建议"
|
||||
|
||||
36
pay-respects-fallback-request-ai/src/main.rs
Normal file
36
pay-respects-fallback-request-ai/src/main.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
use crate::requests::ai_suggestion;
|
||||
use colored::Colorize;
|
||||
use textwrap::{fill, termwidth};
|
||||
mod requests;
|
||||
|
||||
#[macro_use]
|
||||
extern crate rust_i18n;
|
||||
i18n!("i18n", fallback = "en", minify_key = true);
|
||||
|
||||
fn main() -> Result<(), std::io::Error> {
|
||||
let command = std::env::var("_PR_LAST_COMMAND").expect("_PR_LAST_COMMAND not set");
|
||||
let error = std::env::var("_PR_ERROR_MSG").expect("_PR_ERROR_MSG not set");
|
||||
colored::control::set_override(true);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
eprintln!("last_command: {}", command);
|
||||
eprintln!("error_msg: {}", error);
|
||||
}
|
||||
|
||||
// skip for commands with no arguments,
|
||||
// very likely to be an error showing the usage
|
||||
if command.split_whitespace().count() == 1 {
|
||||
return Ok(());
|
||||
}
|
||||
let suggest = ai_suggestion(&command, &error);
|
||||
if let Some(suggest) = suggest {
|
||||
let warn = format!("{}:", t!("ai-suggestion")).bold().blue();
|
||||
let note = fill(&suggest.note, termwidth());
|
||||
|
||||
eprintln!("{}\n{}", warn, note);
|
||||
let command = suggest.command;
|
||||
print!("{}<_PR_BR>", command);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
use std::collections::HashMap;
|
||||
use sys_locale::get_locale;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
|
@ -71,7 +72,16 @@ pub fn ai_suggestion(last_command: &str, error_msg: &str) -> Option<AISuggest> {
|
|||
Err(_) => "llama3-8b-8192".to_string(),
|
||||
};
|
||||
|
||||
let user_locale = std::env::var("_PR_AI_LOCALE").unwrap_or("en-US".to_string());
|
||||
let user_locale = {
|
||||
let locale = std::env::var("_PR_AI_LOCALE")
|
||||
.unwrap_or_else(|_| get_locale().unwrap_or("en".to_string()));
|
||||
if locale.len() < 2 {
|
||||
"en-US".to_string()
|
||||
} else {
|
||||
locale
|
||||
}
|
||||
};
|
||||
|
||||
let set_locale = if !user_locale.starts_with("en") {
|
||||
format!(". Use language for locale {}", user_locale)
|
||||
} else {
|
||||
|
|
@ -19,21 +19,7 @@ sys-locale = "0.3"
|
|||
rust-i18n = "3"
|
||||
regex-lite = "0.1"
|
||||
|
||||
toml = { version = "0.8", optional = true }
|
||||
serde_json = { version = "1.0", optional = true }
|
||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||
curl = { version = "0.4", optional = true }
|
||||
textwrap = { version = "0.16", features = ["terminal_size"], optional = true }
|
||||
|
||||
inquire = "0.7.5"
|
||||
|
||||
pay-respects-parser = { version = "0.3.2", path = "../pay-respects-parser" }
|
||||
pay-respects-utils = {version = "0.1.0", path = "../pay-respects-utils"}
|
||||
|
||||
[features]
|
||||
runtime-rules = ["dep:serde", "dep:toml"]
|
||||
request-ai = ["dep:serde", "dep:serde_json", "dep:textwrap"]
|
||||
|
||||
# linking to libcurl dynamically requires openssl when compiling and
|
||||
# complicates cross compilation
|
||||
libcurl = ["dep:curl"]
|
||||
|
|
|
|||
|
|
@ -142,18 +142,6 @@ ja = "不足しているコマンドのパッケージが見つかりました"
|
|||
ko = "누락된 명령에 대한 패키지가 발견되었습니다"
|
||||
zh = "找到缺失命令的包"
|
||||
|
||||
[ai-suggestion]
|
||||
en = "Suggestion from AI"
|
||||
es = "Sugerencia de la IA"
|
||||
de = "Vorschlag von KI"
|
||||
fr = "Suggestion de l'IA"
|
||||
it = "Proposta dall'IA"
|
||||
pt = "Sugestão da IA"
|
||||
ru = "Предложение от ИИ"
|
||||
ja = "AIからの提案"
|
||||
ko = "AI 제안"
|
||||
zh = "AI 建议"
|
||||
|
||||
[confirm]
|
||||
en = "Execute suggestion?"
|
||||
es = "¿Ejecutar sugerencia?"
|
||||
|
|
@ -87,17 +87,4 @@ fn print_version() {
|
|||
"version: {}",
|
||||
option_env!("CARGO_PKG_VERSION").unwrap_or("unknown")
|
||||
);
|
||||
println!("compile features:");
|
||||
#[cfg(feature = "runtime-rules")]
|
||||
{
|
||||
println!(" - runtime-rules");
|
||||
}
|
||||
#[cfg(feature = "request-ai")]
|
||||
{
|
||||
println!(" - request-ai");
|
||||
}
|
||||
#[cfg(feature = "libcurl")]
|
||||
{
|
||||
println!(" - libcurl");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,9 +24,6 @@ mod style;
|
|||
mod suggestions;
|
||||
mod system;
|
||||
|
||||
#[cfg(feature = "request-ai")]
|
||||
mod requests;
|
||||
|
||||
#[macro_use]
|
||||
extern crate rust_i18n;
|
||||
i18n!("i18n", fallback = "en", minify_key = true);
|
||||
|
|
@ -67,7 +64,7 @@ fn init() -> Result<shell::Data, args::Status> {
|
|||
let locale = {
|
||||
let sys_locale = get_locale().unwrap_or("en-US".to_string());
|
||||
if sys_locale.len() < 2 {
|
||||
"en_US".to_string()
|
||||
"en-US".to_string()
|
||||
} else {
|
||||
sys_locale
|
||||
}
|
||||
|
|
@ -85,12 +82,5 @@ fn init() -> Result<shell::Data, args::Status> {
|
|||
_ => {}
|
||||
}
|
||||
|
||||
#[cfg(feature = "request-ai")]
|
||||
{
|
||||
if std::env::var("_PR_AI_LOCALE").is_err() {
|
||||
std::env::set_var("_PR_AI_LOCALE", &locale);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(shell::Data::init())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,16 @@ use crate::shell::Data;
|
|||
use pay_respects_parser::parse_rules;
|
||||
use pay_respects_utils::evals::*;
|
||||
|
||||
pub fn match_pattern(executable: &str, data: &mut Data) {
|
||||
pub fn match_pattern(executable: &str, data: &Data) -> Option<Vec<String>> {
|
||||
let error_msg = &data.error;
|
||||
let shell = &data.shell;
|
||||
let last_command = &data.command;
|
||||
let executables = &data.executables;
|
||||
let candidates = &mut data.candidates;
|
||||
let mut candidates = vec![];
|
||||
parse_rules!("rules");
|
||||
if candidates.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(candidates)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,21 +156,6 @@ impl Data {
|
|||
self.suggest = Some(suggest.to_string());
|
||||
};
|
||||
}
|
||||
|
||||
pub fn add_candidate(&mut self, candidate: &str) {
|
||||
let candidate = candidate.trim();
|
||||
if candidate != self.command {
|
||||
self.candidates.push(candidate.to_string());
|
||||
}
|
||||
}
|
||||
pub fn add_candidates(&mut self, candidates: &Vec<String>) {
|
||||
for candidate in candidates {
|
||||
let candidate = candidate.trim();
|
||||
if candidate != self.command {
|
||||
self.candidates.push(candidate.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn elevate(data: &mut Data, command: &mut String) {
|
||||
|
|
@ -182,6 +167,19 @@ pub fn elevate(data: &mut Data, command: &mut String) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn add_candidates_no_dup(
|
||||
command: &str,
|
||||
candidates: &mut Vec<String>,
|
||||
new_candidates: &Vec<String>,
|
||||
) {
|
||||
for candidate in new_candidates {
|
||||
let candidate = candidate.trim();
|
||||
if candidate != command && !candidates.contains(&candidate.to_string()) {
|
||||
candidates.push(candidate.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_error(shell: &str, command: &str) -> String {
|
||||
let error_msg = std::env::var("_PR_ERROR_MSG");
|
||||
let error = if let Ok(error_msg) = error_msg {
|
||||
|
|
@ -255,6 +253,10 @@ pub fn module_output(data: &Data, module: &str) -> Option<Vec<String>> {
|
|||
.output()
|
||||
.expect("failed to execute process");
|
||||
|
||||
if !output.stderr.is_empty() {
|
||||
eprintln!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
}
|
||||
|
||||
if output.stdout.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,55 +6,53 @@ use colored::Colorize;
|
|||
use inquire::*;
|
||||
|
||||
use crate::rules::match_pattern;
|
||||
use crate::shell::{module_output, shell_evaluated_commands, Data};
|
||||
use crate::shell::{add_candidates_no_dup, module_output, shell_evaluated_commands, Data};
|
||||
use crate::style::highlight_difference;
|
||||
|
||||
pub fn suggest_candidates(data: &mut Data) {
|
||||
let executable = &data.split[0].to_string();
|
||||
let privilege = &data.privilege.clone();
|
||||
let executable = &data.split[0];
|
||||
let command = &data.command;
|
||||
let privilege = &data.privilege;
|
||||
let mut suggest_candidates = vec![];
|
||||
|
||||
let modules = &data.modules;
|
||||
let fallbacks = &data.fallbacks;
|
||||
|
||||
if privilege.is_none() {
|
||||
match_pattern("_PR_privilege", data);
|
||||
if let Some(candidates) = match_pattern("_PR_privilege", data) {
|
||||
add_candidates_no_dup(command, &mut suggest_candidates, &candidates);
|
||||
}
|
||||
}
|
||||
if let Some(candidates) = match_pattern(executable, data) {
|
||||
add_candidates_no_dup(command, &mut suggest_candidates, &candidates);
|
||||
}
|
||||
if let Some(candidates) = match_pattern("_PR_general", data) {
|
||||
add_candidates_no_dup(command, &mut suggest_candidates, &candidates);
|
||||
}
|
||||
match_pattern(executable, data);
|
||||
match_pattern("_PR_general", data);
|
||||
|
||||
let modules = &data.modules.clone();
|
||||
eprintln!("modules: {modules:?}");
|
||||
eprintln!("fallbacks: {fallbacks:?}");
|
||||
|
||||
for module in modules {
|
||||
let candidates = module_output(data, module);
|
||||
eprintln!("runtime: {candidates:?}");
|
||||
if candidates.is_some() {
|
||||
data.add_candidates(&candidates.unwrap());
|
||||
let new_candidates = module_output(data, module);
|
||||
|
||||
if let Some(candidates) = new_candidates {
|
||||
add_candidates_no_dup(command, &mut suggest_candidates, &candidates);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "request-ai")]
|
||||
{
|
||||
if !data.candidates.is_empty() {
|
||||
if !suggest_candidates.is_empty() {
|
||||
data.candidates = suggest_candidates;
|
||||
return;
|
||||
}
|
||||
for fallback in fallbacks {
|
||||
let candidates = module_output(data, fallback);
|
||||
eprintln!("fallback: {candidates:?}");
|
||||
if candidates.is_some() {
|
||||
add_candidates_no_dup(command, &mut suggest_candidates, &candidates.unwrap());
|
||||
data.candidates = suggest_candidates;
|
||||
return;
|
||||
}
|
||||
use crate::requests::ai_suggestion;
|
||||
use textwrap::{fill, termwidth};
|
||||
let command = &data.command;
|
||||
let split_command = &data.split;
|
||||
let error = &data.error.clone();
|
||||
|
||||
// skip for commands with no arguments,
|
||||
// very likely to be an error showing the usage
|
||||
if privilege.is_some() && split_command.len() > 2
|
||||
|| privilege.is_none() && split_command.len() > 1
|
||||
{
|
||||
let suggest = ai_suggestion(command, error);
|
||||
if let Some(suggest) = suggest {
|
||||
let warn = format!("{}:", t!("ai-suggestion")).bold().blue();
|
||||
let note = fill(&suggest.note, termwidth());
|
||||
|
||||
eprintln!("{}\n{}\n", warn, note);
|
||||
let command = suggest.command;
|
||||
data.add_candidate(&command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue