refactor: Updated to the most recent Rust version with 2024 syntax
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
use crate::client::{list_models, ModelType};
|
||||
use crate::config::{list_agents, Config};
|
||||
use clap_complete::{generate, CompletionCandidate, Shell};
|
||||
use crate::client::{ModelType, list_models};
|
||||
use crate::config::{Config, list_agents};
|
||||
use clap_complete::{CompletionCandidate, Shell, generate};
|
||||
use clap_complete_nushell::Nushell;
|
||||
use std::ffi::OsStr;
|
||||
use std::io;
|
||||
|
||||
+4
-4
@@ -1,15 +1,15 @@
|
||||
mod completer;
|
||||
|
||||
use crate::cli::completer::{
|
||||
agent_completer, macro_completer, model_completer, rag_completer, role_completer,
|
||||
secrets_completer, session_completer, ShellCompletion,
|
||||
ShellCompletion, agent_completer, macro_completer, model_completer, rag_completer,
|
||||
role_completer, secrets_completer, session_completer,
|
||||
};
|
||||
use anyhow::{Context, Result};
|
||||
use clap::ValueHint;
|
||||
use clap::{crate_authors, crate_description, crate_name, crate_version, Parser};
|
||||
use clap::{Parser, crate_authors, crate_description, crate_name, crate_version};
|
||||
use clap_complete::ArgValueCompleter;
|
||||
use is_terminal::IsTerminal;
|
||||
use std::io::{stdin, Read};
|
||||
use std::io::{Read, stdin};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use anyhow::{Result, anyhow};
|
||||
use chrono::Utc;
|
||||
use indexmap::IndexMap;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
+31
-29
@@ -2,7 +2,7 @@ use super::*;
|
||||
|
||||
use crate::utils::{base64_decode, encode_uri, hex_encode, hmac_sha256, sha256, strip_think_tag};
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{Context, Result, bail};
|
||||
use aws_smithy_eventstream::frame::{DecodedFrame, MessageFrameDecoder};
|
||||
use aws_smithy_eventstream::smithy::parse_response_headers;
|
||||
use bytes::BytesMut;
|
||||
@@ -11,7 +11,7 @@ use futures_util::StreamExt;
|
||||
use indexmap::IndexMap;
|
||||
use reqwest::{Client as ReqwestClient, Method, RequestBuilder};
|
||||
use serde::Deserialize;
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct BedrockConfig {
|
||||
@@ -222,29 +222,29 @@ async fn chat_completions_streaming(
|
||||
debug!("stream-data: {smithy_type} {data}");
|
||||
match smithy_type {
|
||||
"contentBlockStart" => {
|
||||
if let Some(tool_use) = data["start"]["toolUse"].as_object() {
|
||||
if let (Some(id), Some(name)) = (
|
||||
if let Some(tool_use) = data["start"]["toolUse"].as_object()
|
||||
&& let (Some(id), Some(name)) = (
|
||||
json_str_from_map(tool_use, "toolUseId"),
|
||||
json_str_from_map(tool_use, "name"),
|
||||
) {
|
||||
if !function_name.is_empty() {
|
||||
if function_arguments.is_empty() {
|
||||
function_arguments = String::from("{}");
|
||||
}
|
||||
let arguments: Value =
|
||||
)
|
||||
{
|
||||
if !function_name.is_empty() {
|
||||
if function_arguments.is_empty() {
|
||||
function_arguments = String::from("{}");
|
||||
}
|
||||
let arguments: Value =
|
||||
function_arguments.parse().with_context(|| {
|
||||
format!("Tool call '{function_name}' have non-JSON arguments '{function_arguments}'")
|
||||
})?;
|
||||
handler.tool_call(ToolCall::new(
|
||||
function_name.clone(),
|
||||
arguments,
|
||||
Some(function_id.clone()),
|
||||
))?;
|
||||
}
|
||||
function_arguments.clear();
|
||||
function_name = name.into();
|
||||
function_id = id.into();
|
||||
handler.tool_call(ToolCall::new(
|
||||
function_name.clone(),
|
||||
arguments,
|
||||
Some(function_id.clone()),
|
||||
))?;
|
||||
}
|
||||
function_arguments.clear();
|
||||
function_name = name.into();
|
||||
function_id = id.into();
|
||||
}
|
||||
}
|
||||
"contentBlockDelta" => {
|
||||
@@ -291,7 +291,9 @@ async fn chat_completions_streaming(
|
||||
bail!("Invalid response data: {data} (smithy_type: {smithy_type})")
|
||||
}
|
||||
_ => {
|
||||
bail!("Unrecognized message, message_type: {message_type}, smithy_type: {smithy_type}",);
|
||||
bail!(
|
||||
"Unrecognized message, message_type: {message_type}, smithy_type: {smithy_type}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -494,18 +496,18 @@ fn extract_chat_completions(data: &Value) -> Result<ChatCompletionsOutput> {
|
||||
if let Some(text) = json_str_from_map(reasoning_text, "text") {
|
||||
reasoning = Some(text.to_string());
|
||||
}
|
||||
} else if let Some(tool_use) = item["toolUse"].as_object() {
|
||||
if let (Some(id), Some(name), Some(input)) = (
|
||||
} else if let Some(tool_use) = item["toolUse"].as_object()
|
||||
&& let (Some(id), Some(name), Some(input)) = (
|
||||
json_str_from_map(tool_use, "toolUseId"),
|
||||
json_str_from_map(tool_use, "name"),
|
||||
tool_use.get("input"),
|
||||
) {
|
||||
tool_calls.push(ToolCall::new(
|
||||
name.to_string(),
|
||||
input.clone(),
|
||||
Some(id.to_string()),
|
||||
))
|
||||
}
|
||||
)
|
||||
{
|
||||
tool_calls.push(ToolCall::new(
|
||||
name.to_string(),
|
||||
input.clone(),
|
||||
Some(id.to_string()),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -2,10 +2,10 @@ use super::openai::*;
|
||||
use super::openai_compatible::*;
|
||||
use super::*;
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{Context, Result, bail};
|
||||
use reqwest::RequestBuilder;
|
||||
use serde::Deserialize;
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
const API_BASE: &str = "https://api.cohere.ai/v2";
|
||||
|
||||
@@ -49,10 +49,10 @@ fn prepare_chat_completions(
|
||||
|
||||
let url = format!("{}/chat", api_base.trim_end_matches('/'));
|
||||
let mut body = openai_build_chat_completions_body(data, &self_.model);
|
||||
if let Some(obj) = body.as_object_mut() {
|
||||
if let Some(top_p) = obj.remove("top_p") {
|
||||
obj.insert("p".to_string(), top_p);
|
||||
}
|
||||
if let Some(obj) = body.as_object_mut()
|
||||
&& let Some(top_p) = obj.remove("top_p")
|
||||
{
|
||||
obj.insert("p".to_string(), top_p);
|
||||
}
|
||||
|
||||
let mut request_data = RequestData::new(url, body);
|
||||
@@ -218,10 +218,10 @@ fn extract_chat_completions(data: &Value) -> Result<ChatCompletionsOutput> {
|
||||
|
||||
let mut tool_calls = vec![];
|
||||
if let Some(calls) = data["message"]["tool_calls"].as_array() {
|
||||
if text.is_empty() {
|
||||
if let Some(tool_plain) = data["message"]["tool_plan"].as_str() {
|
||||
text = tool_plain.to_string();
|
||||
}
|
||||
if text.is_empty()
|
||||
&& let Some(tool_plain) = data["message"]["tool_plan"].as_str()
|
||||
{
|
||||
text = tool_plain.to_string();
|
||||
}
|
||||
for call in calls {
|
||||
if let (Some(name), Some(arguments), Some(id)) = (
|
||||
|
||||
@@ -2,21 +2,21 @@ use super::*;
|
||||
|
||||
use crate::{
|
||||
config::{Config, GlobalConfig, Input},
|
||||
function::{eval_tool_calls, FunctionDeclaration, ToolCall, ToolResult},
|
||||
function::{FunctionDeclaration, ToolCall, ToolResult, eval_tool_calls},
|
||||
render::render_stream,
|
||||
utils::*,
|
||||
};
|
||||
|
||||
use crate::vault::Vault;
|
||||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{Context, Result, bail};
|
||||
use fancy_regex::Regex;
|
||||
use indexmap::IndexMap;
|
||||
use inquire::{
|
||||
list_option::ListOption, required, validator::Validation, MultiSelect, Select, Text,
|
||||
MultiSelect, Select, Text, list_option::ListOption, required, validator::Validation,
|
||||
};
|
||||
use reqwest::{Client as ReqwestClient, RequestBuilder};
|
||||
use serde::Deserialize;
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
use std::sync::LazyLock;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::mpsc::unbounded_channel;
|
||||
@@ -180,11 +180,11 @@ pub trait Client: Sync + Send {
|
||||
};
|
||||
for (key, patch) in patch_map {
|
||||
let key = ESCAPE_SLASH_RE.replace_all(&key, r"\/");
|
||||
if let Ok(regex) = Regex::new(&format!("^({key})$")) {
|
||||
if let Ok(true) = regex.is_match(self.model().name()) {
|
||||
request_data.apply_patch(patch);
|
||||
return;
|
||||
}
|
||||
if let Ok(regex) = Regex::new(&format!("^({key})$"))
|
||||
&& let Ok(true) = regex.is_match(self.model().name())
|
||||
{
|
||||
request_data.apply_patch(patch);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,10 +119,10 @@ impl MessageContent {
|
||||
}
|
||||
for tool_result in tool_results {
|
||||
let mut parts = vec!["Call".to_string()];
|
||||
if let Some((agent_name, functions)) = agent_info {
|
||||
if functions.contains(&tool_result.call.name) {
|
||||
parts.push(agent_name.clone())
|
||||
}
|
||||
if let Some((agent_name, functions)) = agent_info
|
||||
&& functions.contains(&tool_result.call.name)
|
||||
{
|
||||
parts.push(agent_name.clone())
|
||||
}
|
||||
parts.push(tool_result.call.name.clone());
|
||||
parts.push(tool_result.call.arguments.to_string());
|
||||
|
||||
+6
-7
@@ -1,13 +1,12 @@
|
||||
use super::{
|
||||
list_all_models, list_client_names,
|
||||
ApiPatch, MessageContentToolCalls, RequestPatch, list_all_models, list_client_names,
|
||||
message::{Message, MessageContent, MessageContentPart},
|
||||
ApiPatch, MessageContentToolCalls, RequestPatch,
|
||||
};
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::utils::{estimate_token_length, strip_think_tag};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{Result, bail};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::fmt::Display;
|
||||
@@ -275,10 +274,10 @@ impl Model {
|
||||
|
||||
pub fn guard_max_input_tokens(&self, messages: &[Message]) -> Result<()> {
|
||||
let total_tokens = self.total_tokens(messages) + BASIS_TOKENS;
|
||||
if let Some(max_input_tokens) = self.data.max_input_tokens {
|
||||
if total_tokens >= max_input_tokens {
|
||||
bail!("Exceed max_input_tokens limit")
|
||||
}
|
||||
if let Some(max_input_tokens) = self.data.max_input_tokens
|
||||
&& total_tokens >= max_input_tokens
|
||||
{
|
||||
bail!("Exceed max_input_tokens limit")
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use super::*;
|
||||
use anyhow::{Context, Result};
|
||||
use reqwest::RequestBuilder;
|
||||
use serde::Deserialize;
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct OpenAICompatibleConfig {
|
||||
@@ -124,12 +124,12 @@ pub async fn generic_rerank(builder: RequestBuilder, _model: &Model) -> Result<R
|
||||
if !status.is_success() {
|
||||
catch_error(&data, status.as_u16())?;
|
||||
}
|
||||
if data.get("results").is_none() && data.get("data").is_some() {
|
||||
if let Some(data_obj) = data.as_object_mut() {
|
||||
if let Some(value) = data_obj.remove("data") {
|
||||
data_obj.insert("results".to_string(), value);
|
||||
}
|
||||
}
|
||||
if data.get("results").is_none()
|
||||
&& data.get("data").is_some()
|
||||
&& let Some(data_obj) = data.as_object_mut()
|
||||
&& let Some(value) = data_obj.remove("data")
|
||||
{
|
||||
data_obj.insert("results".to_string(), value);
|
||||
}
|
||||
let res_body: GenericRerankResBody =
|
||||
serde_json::from_value(data).context("Invalid rerank data")?;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::{catch_error, ToolCall};
|
||||
use super::{ToolCall, catch_error};
|
||||
use crate::utils::AbortSignal;
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use futures_util::{Stream, StreamExt};
|
||||
use reqwest::RequestBuilder;
|
||||
use reqwest_eventsource::{Error as EventSourceError, Event, RequestBuilderExt};
|
||||
@@ -214,11 +214,11 @@ impl JsonStreamParser {
|
||||
}
|
||||
'}' => {
|
||||
self.balances.pop();
|
||||
if self.balances.is_empty() {
|
||||
if let Some(start) = self.start.take() {
|
||||
let value: String = self.buffer[start..=i].iter().collect();
|
||||
handle(&value)?;
|
||||
}
|
||||
if self.balances.is_empty()
|
||||
&& let Some(start) = self.start.take()
|
||||
{
|
||||
let value: String = self.buffer[start..=i].iter().collect();
|
||||
handle(&value)?;
|
||||
}
|
||||
}
|
||||
']' => {
|
||||
|
||||
+18
-18
@@ -2,13 +2,13 @@ use super::*;
|
||||
|
||||
use crate::{
|
||||
client::Model,
|
||||
function::{run_llm_function, Functions},
|
||||
function::{Functions, run_llm_function},
|
||||
};
|
||||
|
||||
use crate::vault::SECRET_RE;
|
||||
use anyhow::{Context, Result};
|
||||
use fancy_regex::Captures;
|
||||
use inquire::{validator::Validation, Text};
|
||||
use inquire::{Text, validator::Validation};
|
||||
use rust_embed::Embed;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{ffi::OsStr, path::Path};
|
||||
@@ -530,23 +530,23 @@ impl AgentConfig {
|
||||
if let Some(v) = read_env_value::<f64>(&with_prefix("top_p")) {
|
||||
self.top_p = v;
|
||||
}
|
||||
if let Ok(v) = env::var(with_prefix("global_tools")) {
|
||||
if let Ok(v) = serde_json::from_str(&v) {
|
||||
self.global_tools = v;
|
||||
}
|
||||
if let Ok(v) = env::var(with_prefix("global_tools"))
|
||||
&& let Ok(v) = serde_json::from_str(&v)
|
||||
{
|
||||
self.global_tools = v;
|
||||
}
|
||||
if let Ok(v) = env::var(with_prefix("mcp_servers")) {
|
||||
if let Ok(v) = serde_json::from_str(&v) {
|
||||
self.mcp_servers = v;
|
||||
}
|
||||
if let Ok(v) = env::var(with_prefix("mcp_servers"))
|
||||
&& let Ok(v) = serde_json::from_str(&v)
|
||||
{
|
||||
self.mcp_servers = v;
|
||||
}
|
||||
if let Some(v) = read_env_value::<String>(&with_prefix("agent_session")) {
|
||||
self.agent_session = v;
|
||||
}
|
||||
if let Ok(v) = env::var(with_prefix("variables")) {
|
||||
if let Ok(v) = serde_json::from_str(&v) {
|
||||
self.variables = v;
|
||||
}
|
||||
if let Ok(v) = env::var(with_prefix("variables"))
|
||||
&& let Ok(v) = serde_json::from_str(&v)
|
||||
{
|
||||
self.variables = v;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -619,10 +619,10 @@ pub fn list_agents() -> Vec<String> {
|
||||
let mut agents = Vec::new();
|
||||
if let Ok(entries) = read_dir(agents_data_dir) {
|
||||
for entry in entries.flatten() {
|
||||
if entry.path().is_dir() {
|
||||
if let Some(name) = entry.file_name().to_str() {
|
||||
agents.push(name.to_string());
|
||||
}
|
||||
if entry.path().is_dir()
|
||||
&& let Some(name) = entry.file_name().to_str()
|
||||
{
|
||||
agents.push(name.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,13 +1,13 @@
|
||||
use super::*;
|
||||
|
||||
use crate::client::{
|
||||
init_client, patch_messages, ChatCompletionsData, Client, ImageUrl, Message, MessageContent,
|
||||
MessageContentPart, MessageContentToolCalls, MessageRole, Model,
|
||||
ChatCompletionsData, Client, ImageUrl, Message, MessageContent, MessageContentPart,
|
||||
MessageContentToolCalls, MessageRole, Model, init_client, patch_messages,
|
||||
};
|
||||
use crate::function::ToolResult;
|
||||
use crate::utils::{base64_encode, is_loader_protocol, sha256, AbortSignal};
|
||||
use crate::utils::{AbortSignal, base64_encode, is_loader_protocol, sha256};
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{Context, Result, bail};
|
||||
use indexmap::IndexSet;
|
||||
use std::{collections::HashMap, fs::File, io::Read};
|
||||
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::config::{ensure_parent_exists, Config, GlobalConfig, RoleLike};
|
||||
use crate::config::{Config, GlobalConfig, RoleLike, ensure_parent_exists};
|
||||
use crate::repl::{run_repl_command, split_args_text};
|
||||
use crate::utils::{multiline_text, AbortSignal};
|
||||
use anyhow::{anyhow, Result};
|
||||
use crate::utils::{AbortSignal, multiline_text};
|
||||
use anyhow::{Result, anyhow};
|
||||
use indexmap::IndexMap;
|
||||
use parking_lot::RwLock;
|
||||
use rust_embed::Embed;
|
||||
|
||||
+87
-89
@@ -4,18 +4,18 @@ mod macros;
|
||||
mod role;
|
||||
mod session;
|
||||
|
||||
pub use self::agent::{complete_agent_variables, list_agents, Agent, AgentVariables};
|
||||
pub use self::agent::{Agent, AgentVariables, complete_agent_variables, list_agents};
|
||||
pub use self::input::Input;
|
||||
pub use self::role::{
|
||||
Role, RoleLike, CODE_ROLE, CREATE_TITLE_ROLE, EXPLAIN_SHELL_ROLE, SHELL_ROLE,
|
||||
CODE_ROLE, CREATE_TITLE_ROLE, EXPLAIN_SHELL_ROLE, Role, RoleLike, SHELL_ROLE,
|
||||
};
|
||||
use self::session::Session;
|
||||
pub use macros::macro_execute;
|
||||
use mem::take;
|
||||
|
||||
use crate::client::{
|
||||
create_client_config, list_client_types, list_models, ClientConfig, MessageContentToolCalls,
|
||||
Model, ModelType, ProviderModels, OPENAI_COMPATIBLE_PROVIDERS,
|
||||
ClientConfig, MessageContentToolCalls, Model, ModelType, OPENAI_COMPATIBLE_PROVIDERS,
|
||||
ProviderModels, create_client_config, list_client_types, list_models,
|
||||
};
|
||||
use crate::function::{FunctionDeclaration, Functions, ToolResult};
|
||||
use crate::rag::Rag;
|
||||
@@ -24,14 +24,14 @@ use crate::utils::*;
|
||||
|
||||
use crate::config::macros::Macro;
|
||||
use crate::mcp::{
|
||||
McpRegistry, MCP_INVOKE_META_FUNCTION_NAME_PREFIX, MCP_LIST_META_FUNCTION_NAME_PREFIX,
|
||||
MCP_INVOKE_META_FUNCTION_NAME_PREFIX, MCP_LIST_META_FUNCTION_NAME_PREFIX, McpRegistry,
|
||||
};
|
||||
use crate::vault::{create_vault_password_file, interpolate_secrets, GlobalVault, Vault};
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use crate::vault::{GlobalVault, Vault, create_vault_password_file, interpolate_secrets};
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use fancy_regex::Regex;
|
||||
use indexmap::IndexMap;
|
||||
use indoc::formatdoc;
|
||||
use inquire::{list_option::ListOption, validator::Validation, Confirm, MultiSelect, Select, Text};
|
||||
use inquire::{Confirm, MultiSelect, Select, Text, list_option::ListOption, validator::Validation};
|
||||
use log::LevelFilter;
|
||||
use parking_lot::RwLock;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -41,7 +41,7 @@ use std::sync::LazyLock;
|
||||
use std::{
|
||||
env,
|
||||
fs::{
|
||||
create_dir_all, read_dir, read_to_string, remove_dir_all, remove_file, File, OpenOptions,
|
||||
File, OpenOptions, create_dir_all, read_dir, read_to_string, remove_dir_all, remove_file,
|
||||
},
|
||||
io::Write,
|
||||
mem,
|
||||
@@ -50,7 +50,7 @@ use std::{
|
||||
sync::{Arc, OnceLock},
|
||||
};
|
||||
use syntect::highlighting::ThemeSet;
|
||||
use terminal_colorsaurus::{color_scheme, ColorScheme, QueryOptions};
|
||||
use terminal_colorsaurus::{ColorScheme, QueryOptions, color_scheme};
|
||||
use tokio::runtime::Handle;
|
||||
|
||||
pub const TEMP_ROLE_NAME: &str = "temp";
|
||||
@@ -547,10 +547,10 @@ impl Config {
|
||||
|
||||
for entry in read_dir(Self::agent_data_dir(name))? {
|
||||
let entry = entry?;
|
||||
if let Some(file) = entry.file_name().to_str() {
|
||||
if allowed.contains(&file) {
|
||||
return Ok(entry.path());
|
||||
}
|
||||
if let Some(file) = entry.file_name().to_str()
|
||||
&& allowed.contains(&file)
|
||||
{
|
||||
return Ok(entry.path());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -783,24 +783,24 @@ impl Config {
|
||||
}
|
||||
"enabled_mcp_servers" => {
|
||||
let value: Option<String> = parse_value(value)?;
|
||||
if let Some(servers) = value.as_ref() {
|
||||
if let Some(registry) = &config.read().mcp_registry {
|
||||
if registry.list_configured_servers().is_empty() {
|
||||
bail!(
|
||||
"No MCP servers are configured. Please configure MCP servers first before setting 'enabled_mcp_servers'."
|
||||
);
|
||||
}
|
||||
if let Some(servers) = value.as_ref()
|
||||
&& let Some(registry) = &config.read().mcp_registry
|
||||
{
|
||||
if registry.list_configured_servers().is_empty() {
|
||||
bail!(
|
||||
"No MCP servers are configured. Please configure MCP servers first before setting 'enabled_mcp_servers'."
|
||||
);
|
||||
}
|
||||
|
||||
if !servers.split(',').all(|s| {
|
||||
registry
|
||||
.list_configured_servers()
|
||||
.contains(&s.trim().to_string())
|
||||
|| s == "all"
|
||||
}) {
|
||||
bail!(
|
||||
"Some of the specified MCP servers in 'enabled_mcp_servers' are configured. Please check your MCP server configuration."
|
||||
);
|
||||
}
|
||||
if !servers.split(',').all(|s| {
|
||||
registry
|
||||
.list_configured_servers()
|
||||
.contains(&s.trim().to_string())
|
||||
|| s == "all"
|
||||
}) {
|
||||
bail!(
|
||||
"Some of the specified MCP servers in 'enabled_mcp_servers' are configured. Please check your MCP server configuration."
|
||||
);
|
||||
}
|
||||
}
|
||||
config.write().set_enabled_mcp_servers(value.clone());
|
||||
@@ -1399,18 +1399,16 @@ impl Config {
|
||||
output,
|
||||
continuous,
|
||||
}) = &self.last_message
|
||||
&& (*continuous && !output.is_empty())
|
||||
&& self.agent.is_some() == input.with_agent()
|
||||
{
|
||||
if (*continuous && !output.is_empty())
|
||||
&& self.agent.is_some() == input.with_agent()
|
||||
{
|
||||
let ans = Confirm::new(
|
||||
"Start a session that incorporates the last question and answer?",
|
||||
)
|
||||
.with_default(false)
|
||||
.prompt()?;
|
||||
if ans {
|
||||
session.add_message(input, output)?;
|
||||
}
|
||||
let ans = Confirm::new(
|
||||
"Start a session that incorporates the last question and answer?",
|
||||
)
|
||||
.with_default(false)
|
||||
.prompt()?;
|
||||
if ans {
|
||||
session.add_message(input, output)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1520,11 +1518,11 @@ impl Config {
|
||||
{
|
||||
let mut config = config.write();
|
||||
let compression_threshold = config.compression_threshold;
|
||||
if let Some(session) = config.session.as_mut() {
|
||||
if session.needs_compression(compression_threshold) {
|
||||
session.set_compressing(true);
|
||||
needs_compression = true;
|
||||
}
|
||||
if let Some(session) = config.session.as_mut()
|
||||
&& session.needs_compression(compression_threshold)
|
||||
{
|
||||
session.set_compressing(true);
|
||||
needs_compression = true;
|
||||
}
|
||||
};
|
||||
if !needs_compression {
|
||||
@@ -1587,11 +1585,11 @@ impl Config {
|
||||
|
||||
pub fn maybe_autoname_session(config: GlobalConfig) {
|
||||
let mut need_autoname = false;
|
||||
if let Some(session) = config.write().session.as_mut() {
|
||||
if session.need_autoname() {
|
||||
session.set_autonaming(true);
|
||||
need_autoname = true;
|
||||
}
|
||||
if let Some(session) = config.write().session.as_mut()
|
||||
&& session.need_autoname()
|
||||
{
|
||||
session.set_autonaming(true);
|
||||
need_autoname = true;
|
||||
}
|
||||
if !need_autoname {
|
||||
return;
|
||||
@@ -2439,15 +2437,15 @@ impl Config {
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
);
|
||||
if let Some(temperature) = role.temperature() {
|
||||
if temperature != 0.0 {
|
||||
output.insert("temperature", temperature.to_string());
|
||||
}
|
||||
if let Some(temperature) = role.temperature()
|
||||
&& temperature != 0.0
|
||||
{
|
||||
output.insert("temperature", temperature.to_string());
|
||||
}
|
||||
if let Some(top_p) = role.top_p() {
|
||||
if top_p != 0.0 {
|
||||
output.insert("top_p", top_p.to_string());
|
||||
}
|
||||
if let Some(top_p) = role.top_p()
|
||||
&& top_p != 0.0
|
||||
{
|
||||
output.insert("top_p", top_p.to_string());
|
||||
}
|
||||
if self.dry_run {
|
||||
output.insert("dry_run", "true".to_string());
|
||||
@@ -2458,10 +2456,10 @@ impl Config {
|
||||
if self.save {
|
||||
output.insert("save", "true".to_string());
|
||||
}
|
||||
if let Some(wrap) = &self.wrap {
|
||||
if wrap != "no" {
|
||||
output.insert("wrap", wrap.clone());
|
||||
}
|
||||
if let Some(wrap) = &self.wrap
|
||||
&& wrap != "no"
|
||||
{
|
||||
output.insert("wrap", wrap.clone());
|
||||
}
|
||||
if !role.is_derived() {
|
||||
output.insert("role", role.name().to_string());
|
||||
@@ -2724,10 +2722,10 @@ impl Config {
|
||||
if let Some(Some(v)) = read_env_bool(&get_env_name("save")) {
|
||||
self.save = v;
|
||||
}
|
||||
if let Ok(v) = env::var(get_env_name("keybindings")) {
|
||||
if v == "vi" {
|
||||
self.keybindings = v;
|
||||
}
|
||||
if let Ok(v) = env::var(get_env_name("keybindings"))
|
||||
&& v == "vi"
|
||||
{
|
||||
self.keybindings = v;
|
||||
}
|
||||
if let Some(v) = read_env_value::<String>(&get_env_name("editor")) {
|
||||
self.editor = v;
|
||||
@@ -2742,10 +2740,10 @@ impl Config {
|
||||
if let Some(Some(v)) = read_env_bool(&get_env_name("function_calling_support")) {
|
||||
self.function_calling_support = v;
|
||||
}
|
||||
if let Ok(v) = env::var(get_env_name("mapping_tools")) {
|
||||
if let Ok(v) = serde_json::from_str(&v) {
|
||||
self.mapping_tools = v;
|
||||
}
|
||||
if let Ok(v) = env::var(get_env_name("mapping_tools"))
|
||||
&& let Ok(v) = serde_json::from_str(&v)
|
||||
{
|
||||
self.mapping_tools = v;
|
||||
}
|
||||
if let Some(v) = read_env_value::<String>(&get_env_name("enabled_tools")) {
|
||||
self.enabled_tools = v;
|
||||
@@ -2754,10 +2752,10 @@ impl Config {
|
||||
if let Some(Some(v)) = read_env_bool(&get_env_name("mcp_server_support")) {
|
||||
self.mcp_server_support = v;
|
||||
}
|
||||
if let Ok(v) = env::var(get_env_name("mapping_mcp_servers")) {
|
||||
if let Ok(v) = serde_json::from_str(&v) {
|
||||
self.mapping_mcp_servers = v;
|
||||
}
|
||||
if let Ok(v) = env::var(get_env_name("mapping_mcp_servers"))
|
||||
&& let Ok(v) = serde_json::from_str(&v)
|
||||
{
|
||||
self.mapping_mcp_servers = v;
|
||||
}
|
||||
if let Some(v) = read_env_value::<String>(&get_env_name("enabled_mcp_servers")) {
|
||||
self.enabled_mcp_servers = v;
|
||||
@@ -2805,10 +2803,10 @@ impl Config {
|
||||
self.rag_template = v;
|
||||
}
|
||||
|
||||
if let Ok(v) = env::var(get_env_name("document_loaders")) {
|
||||
if let Ok(v) = serde_json::from_str(&v) {
|
||||
self.document_loaders = v;
|
||||
}
|
||||
if let Ok(v) = env::var(get_env_name("document_loaders"))
|
||||
&& let Ok(v) = serde_json::from_str(&v)
|
||||
{
|
||||
self.document_loaders = v;
|
||||
}
|
||||
|
||||
if let Some(Some(v)) = read_env_bool(&get_env_name("highlight")) {
|
||||
@@ -2820,14 +2818,14 @@ impl Config {
|
||||
if self.highlight && self.theme.is_none() {
|
||||
if let Some(v) = read_env_value::<String>(&get_env_name("theme")) {
|
||||
self.theme = v;
|
||||
} else if *IS_STDOUT_TERMINAL {
|
||||
if let Ok(color_scheme) = color_scheme(QueryOptions::default()) {
|
||||
let theme = match color_scheme {
|
||||
ColorScheme::Dark => "dark",
|
||||
ColorScheme::Light => "light",
|
||||
};
|
||||
self.theme = Some(theme.into());
|
||||
}
|
||||
} else if *IS_STDOUT_TERMINAL
|
||||
&& let Ok(color_scheme) = color_scheme(QueryOptions::default())
|
||||
{
|
||||
let theme = match color_scheme {
|
||||
ColorScheme::Dark => "dark",
|
||||
ColorScheme::Light => "light",
|
||||
};
|
||||
self.theme = Some(theme.into());
|
||||
}
|
||||
}
|
||||
if let Some(v) = read_env_value::<String>(&get_env_name("left_prompt")) {
|
||||
@@ -2934,7 +2932,7 @@ pub fn load_env_file() -> Result<()> {
|
||||
continue;
|
||||
}
|
||||
if let Some((key, value)) = line.split_once('=') {
|
||||
env::set_var(key.trim(), value.trim());
|
||||
unsafe { env::set_var(key.trim(), value.trim()) };
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
+18
-21
@@ -64,11 +64,11 @@ impl Role {
|
||||
pub fn new(name: &str, content: &str) -> Self {
|
||||
let mut metadata = "";
|
||||
let mut prompt = content.trim();
|
||||
if let Ok(Some(caps)) = RE_METADATA.captures(content) {
|
||||
if let (Some(metadata_value), Some(prompt_value)) = (caps.get(1), caps.get(2)) {
|
||||
metadata = metadata_value.as_str().trim();
|
||||
prompt = prompt_value.as_str().trim();
|
||||
}
|
||||
if let Ok(Some(caps)) = RE_METADATA.captures(content)
|
||||
&& let (Some(metadata_value), Some(prompt_value)) = (caps.get(1), caps.get(2))
|
||||
{
|
||||
metadata = metadata_value.as_str().trim();
|
||||
prompt = prompt_value.as_str().trim();
|
||||
}
|
||||
let mut prompt = prompt.to_string();
|
||||
interpolate_variables(&mut prompt);
|
||||
@@ -77,23 +77,20 @@ impl Role {
|
||||
prompt,
|
||||
..Default::default()
|
||||
};
|
||||
if !metadata.is_empty() {
|
||||
if let Ok(value) = serde_yaml::from_str::<Value>(metadata) {
|
||||
if let Some(value) = value.as_object() {
|
||||
for (key, value) in value {
|
||||
match key.as_str() {
|
||||
"model" => role.model_id = value.as_str().map(|v| v.to_string()),
|
||||
"temperature" => role.temperature = value.as_f64(),
|
||||
"top_p" => role.top_p = value.as_f64(),
|
||||
"enabled_tools" => {
|
||||
role.enabled_tools = value.as_str().map(|v| v.to_string())
|
||||
}
|
||||
"enabled_mcp_servers" => {
|
||||
role.enabled_mcp_servers = value.as_str().map(|v| v.to_string())
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
if !metadata.is_empty()
|
||||
&& let Ok(value) = serde_yaml::from_str::<Value>(metadata)
|
||||
&& let Some(value) = value.as_object()
|
||||
{
|
||||
for (key, value) in value {
|
||||
match key.as_str() {
|
||||
"model" => role.model_id = value.as_str().map(|v| v.to_string()),
|
||||
"temperature" => role.temperature = value.as_f64(),
|
||||
"top_p" => role.top_p = value.as_f64(),
|
||||
"enabled_tools" => role.enabled_tools = value.as_str().map(|v| v.to_string()),
|
||||
"enabled_mcp_servers" => {
|
||||
role.enabled_mcp_servers = value.as_str().map(|v| v.to_string())
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+22
-20
@@ -4,9 +4,9 @@ use super::*;
|
||||
use crate::client::{Message, MessageContent, MessageRole};
|
||||
use crate::render::MarkdownRender;
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{Context, Result, bail};
|
||||
use fancy_regex::Regex;
|
||||
use inquire::{validator::Validation, Confirm, Text};
|
||||
use inquire::{Confirm, Text, validator::Validation};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use std::collections::HashMap;
|
||||
@@ -98,10 +98,10 @@ impl Session {
|
||||
session.path = Some(path.display().to_string());
|
||||
}
|
||||
|
||||
if let Some(role_name) = &session.role_name {
|
||||
if let Ok(role) = config.retrieve_role(role_name) {
|
||||
session.role_prompt = role.prompt().to_string();
|
||||
}
|
||||
if let Some(role_name) = &session.role_name
|
||||
&& let Ok(role) = config.retrieve_role(role_name)
|
||||
{
|
||||
session.role_prompt = role.prompt().to_string();
|
||||
}
|
||||
|
||||
session.update_tokens();
|
||||
@@ -473,23 +473,25 @@ impl Session {
|
||||
|
||||
pub fn guard_empty(&self) -> Result<()> {
|
||||
if !self.is_empty() {
|
||||
bail!("Cannot perform this operation because the session has messages, please `.empty session` first.");
|
||||
bail!(
|
||||
"Cannot perform this operation because the session has messages, please `.empty session` first."
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_message(&mut self, input: &Input, output: &str) -> Result<()> {
|
||||
if input.continue_output().is_some() {
|
||||
if let Some(message) = self.messages.last_mut() {
|
||||
if let MessageContent::Text(text) = &mut message.content {
|
||||
*text = format!("{text}{output}");
|
||||
}
|
||||
if let Some(message) = self.messages.last_mut()
|
||||
&& let MessageContent::Text(text) = &mut message.content
|
||||
{
|
||||
*text = format!("{text}{output}");
|
||||
}
|
||||
} else if input.regenerate() {
|
||||
if let Some(message) = self.messages.last_mut() {
|
||||
if let MessageContent::Text(text) = &mut message.content {
|
||||
*text = output.to_string();
|
||||
}
|
||||
if let Some(message) = self.messages.last_mut()
|
||||
&& let MessageContent::Text(text) = &mut message.content
|
||||
{
|
||||
*text = output.to_string();
|
||||
}
|
||||
} else {
|
||||
if self.messages.is_empty() {
|
||||
@@ -553,14 +555,14 @@ impl Session {
|
||||
if len == 0 {
|
||||
messages = input.role().build_messages(input);
|
||||
need_add_msg = false;
|
||||
} else if len == 1 && self.compressed_messages.len() >= 2 {
|
||||
if let Some(index) = self
|
||||
} else if len == 1
|
||||
&& self.compressed_messages.len() >= 2
|
||||
&& let Some(index) = self
|
||||
.compressed_messages
|
||||
.iter()
|
||||
.rposition(|v| v.role == MessageRole::User)
|
||||
{
|
||||
messages.extend(self.compressed_messages[index..].to_vec());
|
||||
}
|
||||
{
|
||||
messages.extend(self.compressed_messages[index..].to_vec());
|
||||
}
|
||||
if need_add_msg {
|
||||
messages.push(Message::new(MessageRole::User, input.message_content()));
|
||||
|
||||
+2
-2
@@ -6,12 +6,12 @@ use crate::{
|
||||
use crate::config::ensure_parent_exists;
|
||||
use crate::mcp::{MCP_INVOKE_META_FUNCTION_NAME_PREFIX, MCP_LIST_META_FUNCTION_NAME_PREFIX};
|
||||
use crate::parsers::{bash, python};
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use indexmap::IndexMap;
|
||||
use indoc::formatdoc;
|
||||
use rust_embed::Embed;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
+5
-4
@@ -15,11 +15,12 @@ mod vault;
|
||||
extern crate log;
|
||||
|
||||
use crate::client::{
|
||||
call_chat_completions, call_chat_completions_streaming, list_models, ModelType,
|
||||
ModelType, call_chat_completions, call_chat_completions_streaming, list_models,
|
||||
};
|
||||
use crate::config::{
|
||||
ensure_parent_exists, list_agents, load_env_file, macro_execute, Agent, Config, GlobalConfig,
|
||||
Input, WorkingMode, CODE_ROLE, EXPLAIN_SHELL_ROLE, SHELL_ROLE, TEMP_SESSION_NAME,
|
||||
Agent, CODE_ROLE, Config, EXPLAIN_SHELL_ROLE, GlobalConfig, Input, SHELL_ROLE,
|
||||
TEMP_SESSION_NAME, WorkingMode, ensure_parent_exists, list_agents, load_env_file,
|
||||
macro_execute,
|
||||
};
|
||||
use crate::render::render_error;
|
||||
use crate::repl::Repl;
|
||||
@@ -27,7 +28,7 @@ use crate::utils::*;
|
||||
|
||||
use crate::cli::Cli;
|
||||
use crate::vault::Vault;
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{Result, bail};
|
||||
use clap::{CommandFactory, Parser};
|
||||
use clap_complete::CompleteEnv;
|
||||
use inquire::Text;
|
||||
|
||||
+7
-5
@@ -1,16 +1,16 @@
|
||||
use crate::config::Config;
|
||||
use crate::utils::{abortable_run_with_spinner, AbortSignal};
|
||||
use crate::utils::{AbortSignal, abortable_run_with_spinner};
|
||||
use crate::vault::interpolate_secrets;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use anyhow::{Context, Result, anyhow};
|
||||
use futures_util::future::BoxFuture;
|
||||
use futures_util::{stream, StreamExt, TryStreamExt};
|
||||
use futures_util::{StreamExt, TryStreamExt, stream};
|
||||
use indoc::formatdoc;
|
||||
use rmcp::model::{CallToolRequestParam, CallToolResult};
|
||||
use rmcp::service::RunningService;
|
||||
use rmcp::transport::TokioChildProcess;
|
||||
use rmcp::{RoleClient, ServiceExt};
|
||||
use serde::Deserialize;
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs::OpenOptions;
|
||||
@@ -148,7 +148,9 @@ impl McpRegistry {
|
||||
enabled_mcp_servers: Option<String>,
|
||||
) -> Result<()> {
|
||||
if self.config.is_none() {
|
||||
debug!("MCP config is not present; assuming MCP servers are disabled globally. Skipping MCP initialization");
|
||||
debug!(
|
||||
"MCP config is not present; assuming MCP servers are disabled globally. Skipping MCP initialization"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
use crate::function::{FunctionDeclaration, JsonSchema};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{Context, Result, bail};
|
||||
use argc::{ChoiceValue, CommandValue, FlagOptionValue};
|
||||
use indexmap::IndexMap;
|
||||
use std::fs::File;
|
||||
@@ -95,10 +95,10 @@ fn with_description(mut schema: JsonSchema, describe: &str) -> JsonSchema {
|
||||
}
|
||||
|
||||
fn with_enum(mut schema: JsonSchema, choice: &Option<ChoiceValue>) -> JsonSchema {
|
||||
if let Some(ChoiceValue::Values(values)) = choice {
|
||||
if !values.is_empty() {
|
||||
schema.enum_value = Some(values.clone());
|
||||
}
|
||||
if let Some(ChoiceValue::Values(values)) = choice
|
||||
&& !values.is_empty()
|
||||
{
|
||||
schema.enum_value = Some(values.clone());
|
||||
}
|
||||
schema
|
||||
}
|
||||
|
||||
+11
-12
@@ -1,9 +1,9 @@
|
||||
use crate::function::{FunctionDeclaration, JsonSchema};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{Context, Result, bail};
|
||||
use ast::{Stmt, StmtFunctionDef};
|
||||
use indexmap::IndexMap;
|
||||
use rustpython_ast::{Constant, Expr, UnaryOp};
|
||||
use rustpython_parser::{ast, Mode};
|
||||
use rustpython_parser::{Mode, ast};
|
||||
use serde_json::Value;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
@@ -104,12 +104,11 @@ fn python_to_function_declarations(
|
||||
|
||||
fn get_docstring_from_body(body: &[Stmt]) -> Option<String> {
|
||||
let first = body.first()?;
|
||||
if let Stmt::Expr(expr_stmt) = first {
|
||||
if let Expr::Constant(constant) = &*expr_stmt.value {
|
||||
if let Constant::Str(s) = &constant.value {
|
||||
return Some(s.clone());
|
||||
}
|
||||
}
|
||||
if let Stmt::Expr(expr_stmt) = first
|
||||
&& let Expr::Constant(constant) = &*expr_stmt.value
|
||||
&& let Constant::Str(s) = &constant.value
|
||||
{
|
||||
return Some(s.clone());
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -351,10 +350,10 @@ fn build_parameters_schema(params: &[Param], _description: &str) -> JsonSchema {
|
||||
"str"
|
||||
};
|
||||
|
||||
if let Some(d) = &p.doc_desc {
|
||||
if !d.is_empty() {
|
||||
schema.description = Some(d.clone());
|
||||
}
|
||||
if let Some(d) = &p.doc_desc
|
||||
&& !d.is_empty()
|
||||
{
|
||||
schema.description = Some(d.clone());
|
||||
}
|
||||
|
||||
apply_type_to_schema(ty, &mut schema);
|
||||
|
||||
+12
-17
@@ -7,11 +7,11 @@ use crate::utils::*;
|
||||
mod serde_vectors;
|
||||
mod splitter;
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use bm25::{Language, SearchEngine, SearchEngineBuilder};
|
||||
use hnsw_rs::prelude::*;
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use inquire::{required, validator::Validation, Confirm, Select, Text};
|
||||
use inquire::{Confirm, Select, Text, required, validator::Validation};
|
||||
use parking_lot::RwLock;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
@@ -434,19 +434,18 @@ impl Rag {
|
||||
} in loaded_documents
|
||||
{
|
||||
let hash = sha256(&contents);
|
||||
if let Some(file_ids) = to_deleted.get_mut(&hash) {
|
||||
if let Some((i, _)) = file_ids
|
||||
if let Some(file_ids) = to_deleted.get_mut(&hash)
|
||||
&& let Some((i, _)) = file_ids
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, v)| self.data.files[*v].path == path)
|
||||
{
|
||||
if file_ids.len() == 1 {
|
||||
to_deleted.swap_remove(&hash);
|
||||
} else {
|
||||
file_ids.remove(i);
|
||||
}
|
||||
continue;
|
||||
{
|
||||
if file_ids.len() == 1 {
|
||||
to_deleted.swap_remove(&hash);
|
||||
} else {
|
||||
file_ids.remove(i);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let extension = metadata
|
||||
.swap_remove(EXTENSION_METADATA)
|
||||
@@ -679,7 +678,7 @@ impl Rag {
|
||||
Err(e) => {
|
||||
return Err(e).with_context(|| {
|
||||
format!("Failed to create embedding after {retry_limit} attempts")
|
||||
})?
|
||||
})?;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -926,11 +925,7 @@ fn add_documents() -> Result<Vec<String>> {
|
||||
.split(';')
|
||||
.filter_map(|v| {
|
||||
let v = v.trim().to_string();
|
||||
if v.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(v)
|
||||
}
|
||||
if v.is_empty() { None } else { Some(v) }
|
||||
})
|
||||
.collect();
|
||||
Ok(paths)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::*;
|
||||
|
||||
use base64::{engine::general_purpose::STANDARD, Engine};
|
||||
use serde::{de, Deserializer, Serializer};
|
||||
use base64::{Engine, engine::general_purpose::STANDARD};
|
||||
use serde::{Deserializer, Serializer, de};
|
||||
|
||||
pub fn serialize<S>(
|
||||
vectors: &IndexMap<DocumentId, Vec<f32>>,
|
||||
|
||||
+6
-10
@@ -120,10 +120,10 @@ impl RecursiveCharacterTextSplitter {
|
||||
}
|
||||
};
|
||||
|
||||
if prev_chunk.is_some() {
|
||||
if let Some(chunk_overlap_header) = chunk_overlap_header {
|
||||
page_content += chunk_overlap_header;
|
||||
}
|
||||
if prev_chunk.is_some()
|
||||
&& let Some(chunk_overlap_header) = chunk_overlap_header
|
||||
{
|
||||
page_content += chunk_overlap_header;
|
||||
}
|
||||
|
||||
let metadata = metadatas[i].clone();
|
||||
@@ -240,11 +240,7 @@ impl RecursiveCharacterTextSplitter {
|
||||
|
||||
fn join_docs(&self, docs: &[String], separator: &str) -> Option<String> {
|
||||
let text = docs.join(separator).trim().to_string();
|
||||
if text.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(text)
|
||||
}
|
||||
if text.is_empty() { None } else { Some(text) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,7 +305,7 @@ mod tests {
|
||||
use super::*;
|
||||
use indexmap::IndexMap;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
fn build_metadata(source: &str) -> Value {
|
||||
json!({ "source": source })
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::utils::decode_bin;
|
||||
|
||||
use ansi_colours::AsRGB;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use anyhow::{Context, Result, anyhow};
|
||||
use crossterm::style::{Color, Stylize};
|
||||
use crossterm::terminal;
|
||||
use std::collections::HashMap;
|
||||
@@ -122,10 +122,10 @@ impl MarkdownRender {
|
||||
line_type = LineType::Normal;
|
||||
}
|
||||
LineType::CodeBegin => {
|
||||
if code_syntax.is_none() {
|
||||
if let Some(syntax) = self.syntax_set.find_syntax_by_first_line(line) {
|
||||
code_syntax = Some(syntax.clone());
|
||||
}
|
||||
if code_syntax.is_none()
|
||||
&& let Some(syntax) = self.syntax_set.find_syntax_by_first_line(line)
|
||||
{
|
||||
code_syntax = Some(syntax.clone());
|
||||
}
|
||||
line_type = LineType::CodeInner;
|
||||
is_code = true;
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ mod stream;
|
||||
pub use self::markdown::{MarkdownRender, RenderOptions};
|
||||
use self::stream::{markdown_stream, raw_stream};
|
||||
|
||||
use crate::utils::{error_text, pretty_error, AbortSignal, IS_STDOUT_TERMINAL};
|
||||
use crate::utils::{AbortSignal, IS_STDOUT_TERMINAL, error_text, pretty_error};
|
||||
use crate::{client::SseEvent, config::GlobalConfig};
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::{MarkdownRender, SseEvent};
|
||||
|
||||
use crate::utils::{poll_abort_signal, spawn_spinner, AbortSignal};
|
||||
use crate::utils::{AbortSignal, poll_abort_signal, spawn_spinner};
|
||||
|
||||
use anyhow::Result;
|
||||
use crossterm::{
|
||||
@@ -8,7 +8,7 @@ use crossterm::{
|
||||
terminal::{self, disable_raw_mode, enable_raw_mode},
|
||||
};
|
||||
use std::{
|
||||
io::{stdout, Stdout, Write},
|
||||
io::{Stdout, Write, stdout},
|
||||
time::Duration,
|
||||
};
|
||||
use textwrap::core::display_width;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::{ReplCommand, REPL_COMMANDS};
|
||||
use super::{REPL_COMMANDS, ReplCommand};
|
||||
|
||||
use crate::{config::GlobalConfig, utils::fuzzy_filter};
|
||||
|
||||
|
||||
+10
-10
@@ -8,23 +8,23 @@ use self::prompt::ReplPrompt;
|
||||
|
||||
use crate::client::{call_chat_completions, call_chat_completions_streaming};
|
||||
use crate::config::{
|
||||
macro_execute, AgentVariables, AssertState, Config, GlobalConfig, Input, LastMessage,
|
||||
StateFlags,
|
||||
AgentVariables, AssertState, Config, GlobalConfig, Input, LastMessage, StateFlags,
|
||||
macro_execute,
|
||||
};
|
||||
use crate::render::render_error;
|
||||
use crate::utils::{
|
||||
abortable_run_with_spinner, create_abort_signal, dimmed_text, set_text, temp_file, AbortSignal,
|
||||
AbortSignal, abortable_run_with_spinner, create_abort_signal, dimmed_text, set_text, temp_file,
|
||||
};
|
||||
|
||||
use crate::mcp::McpRegistry;
|
||||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{Context, Result, bail};
|
||||
use crossterm::cursor::SetCursorStyle;
|
||||
use fancy_regex::Regex;
|
||||
use reedline::CursorConfig;
|
||||
use reedline::{
|
||||
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
|
||||
ColumnarMenu, EditCommand, EditMode, Emacs, KeyCode, KeyModifiers, Keybindings, Reedline,
|
||||
ReedlineEvent, ReedlineMenu, ValidationResult, Validator, Vi,
|
||||
ReedlineEvent, ReedlineMenu, ValidationResult, Validator, Vi, default_emacs_keybindings,
|
||||
default_vi_insert_keybindings, default_vi_normal_keybindings,
|
||||
};
|
||||
use reedline::{MenuBuilder, Signal};
|
||||
use std::sync::LazyLock;
|
||||
@@ -382,10 +382,10 @@ pub async fn run_repl_command(
|
||||
abort_signal: AbortSignal,
|
||||
mut line: &str,
|
||||
) -> Result<bool> {
|
||||
if let Ok(Some(captures)) = MULTILINE_RE.captures(line) {
|
||||
if let Some(text_match) = captures.get(1) {
|
||||
line = text_match.as_str();
|
||||
}
|
||||
if let Ok(Some(captures)) = MULTILINE_RE.captures(line)
|
||||
&& let Some(text_match) = captures.get(1)
|
||||
{
|
||||
line = text_match.as_str();
|
||||
}
|
||||
match parse_command(line) {
|
||||
Some((cmd, args)) => match cmd {
|
||||
|
||||
+13
-13
@@ -2,8 +2,8 @@ use anyhow::Result;
|
||||
use crossterm::event::{self, Event, KeyCode, KeyModifiers};
|
||||
use std::{
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
atomic::{AtomicBool, Ordering},
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
@@ -69,19 +69,19 @@ pub async fn wait_abort_signal(abort_signal: &AbortSignal) {
|
||||
}
|
||||
|
||||
pub fn poll_abort_signal(abort_signal: &AbortSignal) -> Result<bool> {
|
||||
if event::poll(Duration::from_millis(25))? {
|
||||
if let Event::Key(key) = event::read()? {
|
||||
match key.code {
|
||||
KeyCode::Char('c') if key.modifiers == KeyModifiers::CONTROL => {
|
||||
abort_signal.set_ctrlc();
|
||||
return Ok(true);
|
||||
}
|
||||
KeyCode::Char('d') if key.modifiers == KeyModifiers::CONTROL => {
|
||||
abort_signal.set_ctrld();
|
||||
return Ok(true);
|
||||
}
|
||||
_ => {}
|
||||
if event::poll(Duration::from_millis(25))?
|
||||
&& let Event::Key(key) = event::read()?
|
||||
{
|
||||
match key.code {
|
||||
KeyCode::Char('c') if key.modifiers == KeyModifiers::CONTROL => {
|
||||
abort_signal.set_ctrlc();
|
||||
return Ok(true);
|
||||
}
|
||||
KeyCode::Char('d') if key.modifiers == KeyModifiers::CONTROL => {
|
||||
abort_signal.set_ctrld();
|
||||
return Ok(true);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
|
||||
@@ -3,7 +3,7 @@ use anyhow::Context;
|
||||
#[cfg(not(any(target_os = "android", target_os = "emscripten")))]
|
||||
mod internal {
|
||||
use arboard::Clipboard;
|
||||
use base64::{engine::general_purpose::STANDARD, Engine as _};
|
||||
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
||||
use std::sync::{LazyLock, Mutex};
|
||||
|
||||
static CLIPBOARD: LazyLock<Mutex<Option<Clipboard>>> =
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::{
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use dirs::home_dir;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use base64::{engine::general_purpose::STANDARD, Engine};
|
||||
use base64::{Engine, engine::general_purpose::STANDARD};
|
||||
use hmac::{Hmac, Mac};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use html_to_markdown::{markdown, TagHandler};
|
||||
use html_to_markdown::{TagHandler, markdown};
|
||||
|
||||
pub fn html_to_md(html: &str) -> String {
|
||||
let mut handlers: Vec<TagHandler> = vec![
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
use anyhow::Result;
|
||||
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyModifiers};
|
||||
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
|
||||
use std::io::{stdout, Write};
|
||||
use std::io::{Write, stdout};
|
||||
|
||||
/// Reads a single character from stdin without requiring Enter
|
||||
/// Returns the character if it's one of the valid options, or the default if Enter is pressed
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
use super::*;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use anyhow::{Context, Result, anyhow};
|
||||
use indexmap::IndexMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ pub use self::variables::*;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use fancy_regex::Regex;
|
||||
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
|
||||
use fuzzy_matcher::{FuzzyMatcher, skim::SkimMatcherV2};
|
||||
use is_terminal::IsTerminal;
|
||||
use std::borrow::Cow;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
+19
-20
@@ -1,7 +1,7 @@
|
||||
use std::fs;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{Result, bail};
|
||||
use fancy_regex::Regex;
|
||||
use indexmap::IndexSet;
|
||||
use path_absolutize::Absolutize;
|
||||
@@ -97,11 +97,12 @@ pub fn to_absolute_path(path: &str) -> Result<String> {
|
||||
|
||||
pub fn resolve_home_dir(path: &str) -> String {
|
||||
let mut path = path.to_string();
|
||||
if path.starts_with("~/") || path.starts_with("~\\") {
|
||||
if let Some(home_dir) = dirs::home_dir() {
|
||||
path.replace_range(..1, &home_dir.display().to_string());
|
||||
}
|
||||
if (path.starts_with("~/") || path.starts_with("~\\"))
|
||||
&& let Some(home_dir) = dirs::home_dir()
|
||||
{
|
||||
path.replace_range(..1, &home_dir.display().to_string());
|
||||
}
|
||||
|
||||
path
|
||||
}
|
||||
|
||||
@@ -241,22 +242,20 @@ fn add_file(files: &mut IndexSet<String>, suffixes: Option<&Vec<String>>, path:
|
||||
|
||||
fn is_valid_extension(suffixes: Option<&Vec<String>>, path: &Path) -> bool {
|
||||
let filename_regex = Regex::new(r"^.+\.*").unwrap();
|
||||
if let Some(suffixes) = suffixes {
|
||||
if !suffixes.is_empty() {
|
||||
if let Ok(Some(_)) = filename_regex.find(&suffixes.join(",")) {
|
||||
let file_name = path
|
||||
.file_name()
|
||||
.and_then(|v| v.to_str())
|
||||
.expect("invalid filename")
|
||||
.to_string();
|
||||
return suffixes.contains(&file_name);
|
||||
} else if let Some(extension) =
|
||||
path.extension().map(|v| v.to_string_lossy().to_string())
|
||||
{
|
||||
return suffixes.contains(&extension);
|
||||
}
|
||||
return false;
|
||||
if let Some(suffixes) = suffixes
|
||||
&& !suffixes.is_empty()
|
||||
{
|
||||
if let Ok(Some(_)) = filename_regex.find(&suffixes.join(",")) {
|
||||
let file_name = path
|
||||
.file_name()
|
||||
.and_then(|v| v.to_str())
|
||||
.expect("invalid filename")
|
||||
.to_string();
|
||||
return suffixes.contains(&file_name);
|
||||
} else if let Some(extension) = path.extension().map(|v| v.to_string_lossy().to_string()) {
|
||||
return suffixes.contains(&extension);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use super::*;
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use fancy_regex::Regex;
|
||||
use futures_util::{stream, StreamExt};
|
||||
use futures_util::{StreamExt, stream};
|
||||
use http::header::CONTENT_TYPE;
|
||||
use reqwest::Url;
|
||||
use scraper::{Html, Selector};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use super::{poll_abort_signal, wait_abort_signal, AbortSignal, IS_STDOUT_TERMINAL};
|
||||
use super::{AbortSignal, IS_STDOUT_TERMINAL, poll_abort_signal, wait_abort_signal};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{Result, bail};
|
||||
use crossterm::{cursor, queue, style, terminal};
|
||||
use std::{
|
||||
future::Future,
|
||||
io::{stdout, Write},
|
||||
io::{Write, stdout},
|
||||
time::Duration,
|
||||
};
|
||||
use tokio::{
|
||||
|
||||
+2
-2
@@ -9,9 +9,9 @@ use crate::config::Config;
|
||||
use crate::vault::utils::ensure_password_file_initialized;
|
||||
use anyhow::{Context, Result};
|
||||
use fancy_regex::Regex;
|
||||
use gman::providers::local::LocalProvider;
|
||||
use gman::providers::SecretProvider;
|
||||
use inquire::{required, Password, PasswordDisplayMode};
|
||||
use gman::providers::local::LocalProvider;
|
||||
use inquire::{Password, PasswordDisplayMode, required};
|
||||
use std::sync::{Arc, LazyLock};
|
||||
use tokio::runtime::Handle;
|
||||
|
||||
|
||||
+20
-8
@@ -1,11 +1,11 @@
|
||||
use crate::config::ensure_parent_exists;
|
||||
use crate::vault::{Vault, SECRET_RE};
|
||||
use anyhow::anyhow;
|
||||
use crate::vault::{SECRET_RE, Vault};
|
||||
use anyhow::Result;
|
||||
use anyhow::anyhow;
|
||||
use gman::providers::local::LocalProvider;
|
||||
use indoc::formatdoc;
|
||||
use inquire::validator::Validation;
|
||||
use inquire::{min_length, required, Confirm, Password, PasswordDisplayMode, Text};
|
||||
use inquire::{Confirm, Password, PasswordDisplayMode, Text, min_length, required};
|
||||
use std::borrow::Cow;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -21,11 +21,16 @@ pub fn ensure_password_file_initialized(local_provider: &mut LocalProvider) -> R
|
||||
if !file_contents.trim().is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!("The configured password file '{}' is empty. Please populate it with a password and try again.", vault_password_file.display()))
|
||||
Err(anyhow!(
|
||||
"The configured password file '{}' is empty. Please populate it with a password and try again.",
|
||||
vault_password_file.display()
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(anyhow!("A password file is required to utilize the Loki vault. Please configure a password file in your config file and try again."))
|
||||
Err(anyhow!(
|
||||
"A password file is required to utilize the Loki vault. Please configure a password file in your config file and try again."
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +45,9 @@ pub fn create_vault_password_file(vault: &mut Vault) -> Result<()> {
|
||||
{
|
||||
let file_contents = std::fs::read_to_string(&vault_password_file)?;
|
||||
if !file_contents.trim().is_empty() {
|
||||
debug!("create_vault_password_file was called but the password file already exists and is non-empty");
|
||||
debug!(
|
||||
"create_vault_password_file was called but the password file already exists and is non-empty"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
@@ -56,7 +63,10 @@ pub fn create_vault_password_file(vault: &mut Vault) -> Result<()> {
|
||||
.prompt()?;
|
||||
|
||||
if !ans {
|
||||
return Err(anyhow!("The configured password file '{}' is empty. Please populate it with a password and try again.", vault_password_file.display()));
|
||||
return Err(anyhow!(
|
||||
"The configured password file '{}' is empty. Please populate it with a password and try again.",
|
||||
vault_password_file.display()
|
||||
));
|
||||
}
|
||||
|
||||
let password = Password::new("Enter a password to encrypt all vault secrets:")
|
||||
@@ -85,7 +95,9 @@ pub fn create_vault_password_file(vault: &mut Vault) -> Result<()> {
|
||||
.prompt()?;
|
||||
|
||||
if !ans {
|
||||
return Err(anyhow!("A password file is required to utilize the Loki vault. Please configure a password file in your config file and try again."));
|
||||
return Err(anyhow!(
|
||||
"A password file is required to utilize the Loki vault. Please configure a password file in your config file and try again."
|
||||
));
|
||||
}
|
||||
|
||||
let password_file: PathBuf = Text::new("Enter the path to the password file to create:")
|
||||
|
||||
Reference in New Issue
Block a user