feat: lancedb vector tool selection (#2654)

Co-authored-by: Wendy Tang <wendytang@squareup.com>
Co-authored-by: Alice Hau <ahau@squareup.com>
This commit is contained in:
Alice Hau
2025-05-28 23:23:02 -04:00
committed by GitHub
parent cf7bb08ee1
commit bf1c0d51e4
23 changed files with 3661 additions and 301 deletions

View File

@@ -154,6 +154,11 @@ jobs:
# Check disk space after cleanup # Check disk space after cleanup
df -h df -h
- name: Install protobuf
run: |
brew install protobuf
echo "PROTOC=$(which protoc)" >> $GITHUB_ENV
- name: Setup Rust - name: Setup Rust
uses: dtolnay/rust-toolchain@38b70195107dddab2c7bbd522bcf763bac00963b # pin@stable uses: dtolnay/rust-toolchain@38b70195107dddab2c7bbd522bcf763bac00963b # pin@stable
with: with:

View File

@@ -57,7 +57,7 @@ jobs:
- name: Install Dependencies - name: Install Dependencies
run: | run: |
sudo apt update -y sudo apt update -y
sudo apt install -y libdbus-1-dev gnome-keyring libxcb1-dev sudo apt install -y libdbus-1-dev gnome-keyring libxcb1-dev protobuf-compiler
- name: Setup Rust - name: Setup Rust
uses: dtolnay/rust-toolchain@38b70195107dddab2c7bbd522bcf763bac00963b # pin@stable uses: dtolnay/rust-toolchain@38b70195107dddab2c7bbd522bcf763bac00963b # pin@stable

2214
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -824,6 +824,11 @@ pub fn remove_extension_dialog() -> Result<(), Box<dyn Error>> {
pub async fn configure_settings_dialog() -> Result<(), Box<dyn Error>> { pub async fn configure_settings_dialog() -> Result<(), Box<dyn Error>> {
let setting_type = cliclack::select("What setting would you like to configure?") let setting_type = cliclack::select("What setting would you like to configure?")
.item("goose_mode", "Goose Mode", "Configure Goose mode") .item("goose_mode", "Goose Mode", "Configure Goose mode")
.item(
"goose_router_strategy",
"Router Tool Selection Strategy",
"Configure the strategy for selecting tools to use",
)
.item( .item(
"tool_permission", "tool_permission",
"Tool Permission", "Tool Permission",
@@ -850,6 +855,9 @@ pub async fn configure_settings_dialog() -> Result<(), Box<dyn Error>> {
"goose_mode" => { "goose_mode" => {
configure_goose_mode_dialog()?; configure_goose_mode_dialog()?;
} }
"goose_router_strategy" => {
configure_goose_router_strategy_dialog()?;
}
"tool_permission" => { "tool_permission" => {
configure_tool_permissions_dialog().await.and(Ok(()))?; configure_tool_permissions_dialog().await.and(Ok(()))?;
} }
@@ -921,6 +929,49 @@ pub fn configure_goose_mode_dialog() -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
pub fn configure_goose_router_strategy_dialog() -> Result<(), Box<dyn Error>> {
let config = Config::global();
// Check if GOOSE_ROUTER_STRATEGY is set as an environment variable
if std::env::var("GOOSE_ROUTER_TOOL_SELECTION_STRATEGY").is_ok() {
let _ = cliclack::log::info("Notice: GOOSE_ROUTER_TOOL_SELECTION_STRATEGY environment variable is set. Configuration will override this.");
}
let strategy = cliclack::select("Which router strategy would you like to use?")
.item(
"vector",
"Vector Strategy",
"Use vector-based similarity to select tools",
)
.item(
"default",
"Default Strategy",
"Use the default tool selection strategy",
)
.interact()?;
match strategy {
"vector" => {
config.set_param(
"GOOSE_ROUTER_TOOL_SELECTION_STRATEGY",
Value::String("vector".to_string()),
)?;
cliclack::outro(
"Set to Vector Strategy - using vector-based similarity for tool selection",
)?;
}
"default" => {
config.set_param(
"GOOSE_ROUTER_TOOL_SELECTION_STRATEGY",
Value::String("default".to_string()),
)?;
cliclack::outro("Set to Default Strategy - using default tool selection")?;
}
_ => unreachable!(),
};
Ok(())
}
pub fn configure_tool_output_dialog() -> Result<(), Box<dyn Error>> { pub fn configure_tool_output_dialog() -> Result<(), Box<dyn Error>> {
let config = Config::global(); let config = Config::global();
// Check if GOOSE_CLI_MIN_PRIORITY is set as an environment variable // Check if GOOSE_CLI_MIN_PRIORITY is set as an environment variable

View File

@@ -55,7 +55,13 @@ pub async fn build_session(session_config: SessionBuilderConfig) -> Session {
// Create the agent // Create the agent
let agent: Agent = Agent::new(); let agent: Agent = Agent::new();
let new_provider = create(&provider_name, model_config).unwrap(); let new_provider = create(&provider_name, model_config).unwrap();
let _ = agent.update_provider(new_provider).await; agent
.update_provider(new_provider)
.await
.unwrap_or_else(|e| {
output::render_error(&format!("Failed to initialize agent: {}", e));
process::exit(1);
});
// Configure tool monitoring if max_tool_repetitions is set // Configure tool monitoring if max_tool_repetitions is set
if let Some(max_repetitions) = session_config.max_tool_repetitions { if let Some(max_repetitions) = session_config.max_tool_repetitions {

View File

@@ -268,12 +268,16 @@ async fn remove_extension(
.get_agent() .get_agent()
.await .await
.map_err(|_| StatusCode::PRECONDITION_FAILED)?; .map_err(|_| StatusCode::PRECONDITION_FAILED)?;
agent.remove_extension(&name).await; match agent.remove_extension(&name).await {
Ok(_) => Ok(Json(ExtensionResponse {
Ok(Json(ExtensionResponse { error: false,
error: false, message: None,
message: None, })),
})) Err(e) => Ok(Json(ExtensionResponse {
error: true,
message: Some(format!("Failed to remove extension: {:?}", e)),
})),
}
} }
/// Registers the extension management routes with the Axum router. /// Registers the extension management routes with the Axum router.

View File

@@ -76,6 +76,10 @@ blake3 = "1.5"
fs2 = "0.4.3" fs2 = "0.4.3"
futures-util = "0.3.31" futures-util = "0.3.31"
# Vector database for tool selection
lancedb = "0.13"
arrow = "52.2"
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3", features = ["wincred"] } winapi = { version = "0.3", features = ["wincred"] }

View File

@@ -18,7 +18,7 @@ use serde_json::Value;
use tokio::sync::{mpsc, Mutex}; use tokio::sync::{mpsc, Mutex};
use tracing::{debug, error, instrument}; use tracing::{debug, error, instrument};
use crate::agents::extension::{ExtensionConfig, ExtensionResult, ToolInfo}; use crate::agents::extension::{ExtensionConfig, ExtensionError, ExtensionResult, ToolInfo};
use crate::agents::extension_manager::{get_parameter_names, ExtensionManager}; use crate::agents::extension_manager::{get_parameter_names, ExtensionManager};
use crate::agents::platform_tools::{ use crate::agents::platform_tools::{
PLATFORM_LIST_RESOURCES_TOOL_NAME, PLATFORM_MANAGE_EXTENSIONS_TOOL_NAME, PLATFORM_LIST_RESOURCES_TOOL_NAME, PLATFORM_MANAGE_EXTENSIONS_TOOL_NAME,
@@ -29,6 +29,8 @@ use crate::agents::router_tool_selector::{
create_tool_selector, RouterToolSelectionStrategy, RouterToolSelector, create_tool_selector, RouterToolSelectionStrategy, RouterToolSelector,
}; };
use crate::agents::router_tools::ROUTER_VECTOR_SEARCH_TOOL_NAME; use crate::agents::router_tools::ROUTER_VECTOR_SEARCH_TOOL_NAME;
use crate::agents::tool_router_index_manager::ToolRouterIndexManager;
use crate::agents::tool_vectordb::generate_table_id;
use crate::agents::types::SessionConfig; use crate::agents::types::SessionConfig;
use crate::agents::types::{FrontendTool, ToolResultReceiver}; use crate::agents::types::{FrontendTool, ToolResultReceiver};
use mcp_core::{ use mcp_core::{
@@ -51,7 +53,7 @@ pub struct Agent {
pub(super) tool_result_tx: mpsc::Sender<(String, ToolResult<Vec<Content>>)>, pub(super) tool_result_tx: mpsc::Sender<(String, ToolResult<Vec<Content>>)>,
pub(super) tool_result_rx: ToolResultReceiver, pub(super) tool_result_rx: ToolResultReceiver,
pub(super) tool_monitor: Mutex<Option<ToolMonitor>>, pub(super) tool_monitor: Mutex<Option<ToolMonitor>>,
pub(super) router_tool_selector: Mutex<Option<Box<dyn RouterToolSelector>>>, pub(super) router_tool_selector: Mutex<Option<Arc<Box<dyn RouterToolSelector>>>>,
} }
impl Agent { impl Agent {
@@ -60,16 +62,6 @@ impl Agent {
let (confirm_tx, confirm_rx) = mpsc::channel(32); let (confirm_tx, confirm_rx) = mpsc::channel(32);
let (tool_tx, tool_rx) = mpsc::channel(32); let (tool_tx, tool_rx) = mpsc::channel(32);
let router_tool_selection_strategy = std::env::var("GOOSE_ROUTER_TOOL_SELECTION_STRATEGY")
.ok()
.and_then(|s| {
if s.eq_ignore_ascii_case("vector") {
Some(RouterToolSelectionStrategy::Vector)
} else {
None
}
});
Self { Self {
provider: Mutex::new(None), provider: Mutex::new(None),
extension_manager: Mutex::new(ExtensionManager::new()), extension_manager: Mutex::new(ExtensionManager::new()),
@@ -81,9 +73,7 @@ impl Agent {
tool_result_tx: tool_tx, tool_result_tx: tool_tx,
tool_result_rx: Arc::new(Mutex::new(tool_rx)), tool_result_rx: Arc::new(Mutex::new(tool_rx)),
tool_monitor: Mutex::new(None), tool_monitor: Mutex::new(None),
router_tool_selector: Mutex::new(Some(create_tool_selector( router_tool_selector: Mutex::new(None),
router_tool_selection_strategy,
))),
} }
} }
@@ -204,8 +194,8 @@ impl Agent {
"Frontend tool execution required".to_string(), "Frontend tool execution required".to_string(),
)) ))
} else if tool_call.name == ROUTER_VECTOR_SEARCH_TOOL_NAME { } else if tool_call.name == ROUTER_VECTOR_SEARCH_TOOL_NAME {
let router_tool_selector = self.router_tool_selector.lock().await; let selector = self.router_tool_selector.lock().await.clone();
if let Some(selector) = router_tool_selector.as_ref() { if let Some(selector) = selector {
selector.select_tools(tool_call.arguments.clone()).await selector.select_tools(tool_call.arguments.clone()).await
} else { } else {
Err(ToolError::ExecutionError( Err(ToolError::ExecutionError(
@@ -284,6 +274,33 @@ impl Agent {
}) })
.map_err(|e| ToolError::ExecutionError(e.to_string())); .map_err(|e| ToolError::ExecutionError(e.to_string()));
// Update vector index if operation was successful and vector routing is enabled
if result.is_ok() {
let selector = self.router_tool_selector.lock().await.clone();
if ToolRouterIndexManager::vector_tool_router_enabled(&selector) {
if let Some(selector) = selector {
let vector_action = if action == "disable" { "remove" } else { "add" };
let extension_manager = self.extension_manager.lock().await;
if let Err(e) = ToolRouterIndexManager::update_extension_tools(
&selector,
&extension_manager,
&extension_name,
vector_action,
)
.await
{
return (
request_id,
Err(ToolError::ExecutionError(format!(
"Failed to update vector index: {}",
e
))),
);
}
}
}
}
(request_id, result) (request_id, result)
} }
@@ -317,10 +334,32 @@ impl Agent {
} }
_ => { _ => {
let mut extension_manager = self.extension_manager.lock().await; let mut extension_manager = self.extension_manager.lock().await;
extension_manager.add_extension(extension).await?; extension_manager.add_extension(extension.clone()).await?;
} }
}; };
// If vector tool selection is enabled, index the tools
let selector = self.router_tool_selector.lock().await.clone();
if ToolRouterIndexManager::vector_tool_router_enabled(&selector) {
if let Some(selector) = selector {
let extension_manager = self.extension_manager.lock().await;
if let Err(e) = ToolRouterIndexManager::update_extension_tools(
&selector,
&extension_manager,
&extension.name(),
"add",
)
.await
{
return Err(ExtensionError::SetupError(format!(
"Failed to index tools for extension {}: {}",
extension.name(),
e
)));
}
}
}
Ok(()) Ok(())
} }
@@ -350,8 +389,6 @@ impl Agent {
&self, &self,
strategy: Option<RouterToolSelectionStrategy>, strategy: Option<RouterToolSelectionStrategy>,
) -> Vec<Tool> { ) -> Vec<Tool> {
let extension_manager = self.extension_manager.lock().await;
let mut prefixed_tools = vec![]; let mut prefixed_tools = vec![];
match strategy { match strategy {
Some(RouterToolSelectionStrategy::Vector) => { Some(RouterToolSelectionStrategy::Vector) => {
@@ -359,22 +396,50 @@ impl Agent {
} }
None => {} None => {}
} }
prefixed_tools.push(platform_tools::search_available_extensions_tool());
prefixed_tools.push(platform_tools::manage_extensions_tool());
if extension_manager.supports_resources() { // Get recent tool calls from router tool selector if available
prefixed_tools.push(platform_tools::read_resource_tool()); let selector = self.router_tool_selector.lock().await.clone();
prefixed_tools.push(platform_tools::list_resources_tool()); if let Some(selector) = selector {
if let Ok(recent_calls) = selector.get_recent_tool_calls(20).await {
let extension_manager = self.extension_manager.lock().await;
// Add recent tool calls to the list, avoiding duplicates
for tool_name in recent_calls {
// Find the tool in the extension manager's tools
if let Ok(extension_tools) = extension_manager.get_prefixed_tools(None).await {
if let Some(tool) = extension_tools.iter().find(|t| t.name == tool_name) {
// Only add if not already in prefixed_tools
if !prefixed_tools.iter().any(|t| t.name == tool.name) {
prefixed_tools.push(tool.clone());
}
}
}
}
}
} }
prefixed_tools prefixed_tools
} }
pub async fn remove_extension(&self, name: &str) { pub async fn remove_extension(&self, name: &str) -> Result<()> {
let mut extension_manager = self.extension_manager.lock().await; let mut extension_manager = self.extension_manager.lock().await;
extension_manager extension_manager.remove_extension(name).await?;
.remove_extension(name)
.await // If vector tool selection is enabled, remove tools from the index
.expect("Failed to remove extension"); let selector = self.router_tool_selector.lock().await.clone();
if ToolRouterIndexManager::vector_tool_router_enabled(&selector) {
if let Some(selector) = selector {
let extension_manager = self.extension_manager.lock().await;
ToolRouterIndexManager::update_extension_tools(
&selector,
&extension_manager,
name,
"remove",
)
.await?;
}
}
Ok(())
} }
pub async fn list_extensions(&self) -> Vec<String> { pub async fn list_extensions(&self) -> Vec<String> {
@@ -447,6 +512,26 @@ impl Agent {
filtered_response) = filtered_response) =
self.categorize_tool_requests(&response).await; self.categorize_tool_requests(&response).await;
// Record tool calls in the router selector
let selector = self.router_tool_selector.lock().await.clone();
if let Some(selector) = selector {
// Record frontend tool calls
for request in &frontend_requests {
if let Ok(tool_call) = &request.tool_call {
if let Err(e) = selector.record_tool_call(&tool_call.name).await {
tracing::error!("Failed to record frontend tool call: {}", e);
}
}
}
// Record remaining tool calls
for request in &remaining_requests {
if let Ok(tool_call) = &request.tool_call {
if let Err(e) = selector.record_tool_call(&tool_call.name).await {
tracing::error!("Failed to record tool call: {}", e);
}
}
}
}
// Yield the assistant's response with frontend tool requests filtered out // Yield the assistant's response with frontend tool requests filtered out
yield filtered_response.clone(); yield filtered_response.clone();
@@ -598,7 +683,35 @@ impl Agent {
/// Update the provider used by this agent /// Update the provider used by this agent
pub async fn update_provider(&self, provider: Arc<dyn Provider>) -> Result<()> { pub async fn update_provider(&self, provider: Arc<dyn Provider>) -> Result<()> {
*self.provider.lock().await = Some(provider); *self.provider.lock().await = Some(provider.clone());
self.update_router_tool_selector(provider).await?;
Ok(())
}
async fn update_router_tool_selector(&self, provider: Arc<dyn Provider>) -> Result<()> {
let config = Config::global();
let router_tool_selection_strategy = config
.get_param("GOOSE_ROUTER_TOOL_SELECTION_STRATEGY")
.unwrap_or_else(|_| "default".to_string());
let strategy = match router_tool_selection_strategy.to_lowercase().as_str() {
"vector" => Some(RouterToolSelectionStrategy::Vector),
_ => None,
};
if let Some(strategy) = strategy {
let table_name = generate_table_id();
let selector = create_tool_selector(Some(strategy), provider, table_name)
.await
.map_err(|e| anyhow!("Failed to create tool selector: {}", e))?;
let selector = Arc::new(selector);
*self.router_tool_selector.lock().await = Some(selector.clone());
let extension_manager = self.extension_manager.lock().await;
ToolRouterIndexManager::index_platform_tools(&selector, &extension_manager).await?;
}
Ok(()) Ok(())
} }

View File

@@ -9,6 +9,8 @@ mod reply_parts;
mod router_tool_selector; mod router_tool_selector;
mod router_tools; mod router_tools;
mod tool_execution; mod tool_execution;
mod tool_router_index_manager;
pub(crate) mod tool_vectordb;
mod types; mod types;
pub use agent::Agent; pub use agent::Agent;

View File

@@ -3,6 +3,7 @@ use std::collections::HashSet;
use std::sync::Arc; use std::sync::Arc;
use crate::agents::router_tool_selector::RouterToolSelectionStrategy; use crate::agents::router_tool_selector::RouterToolSelectionStrategy;
use crate::config::Config;
use crate::message::{Message, MessageContent, ToolRequest}; use crate::message::{Message, MessageContent, ToolRequest};
use crate::providers::base::{Provider, ProviderUsage}; use crate::providers::base::{Provider, ProviderUsage};
use crate::providers::errors::ProviderError; use crate::providers::errors::ProviderError;
@@ -19,16 +20,17 @@ impl Agent {
pub(crate) async fn prepare_tools_and_prompt( pub(crate) async fn prepare_tools_and_prompt(
&self, &self,
) -> anyhow::Result<(Vec<Tool>, Vec<Tool>, String)> { ) -> anyhow::Result<(Vec<Tool>, Vec<Tool>, String)> {
// Get tool selection strategy // Get tool selection strategy from config
let tool_selection_strategy = std::env::var("GOOSE_ROUTER_TOOL_SELECTION_STRATEGY") let config = Config::global();
.ok() let router_tool_selection_strategy = config
.and_then(|s| { .get_param("GOOSE_ROUTER_TOOL_SELECTION_STRATEGY")
if s.eq_ignore_ascii_case("vector") { .unwrap_or_else(|_| "default".to_string());
Some(RouterToolSelectionStrategy::Vector)
} else { let tool_selection_strategy = match router_tool_selection_strategy.to_lowercase().as_str() {
None "vector" => Some(RouterToolSelectionStrategy::Vector),
} _ => None,
}); };
// Get tools from extension manager // Get tools from extension manager
let mut tools = match tool_selection_strategy { let mut tools = match tool_selection_strategy {
Some(RouterToolSelectionStrategy::Vector) => { Some(RouterToolSelectionStrategy::Vector) => {

View File

@@ -1,8 +1,20 @@
use mcp_core::content::TextContent;
use mcp_core::tool::Tool;
use mcp_core::{Content, ToolError}; use mcp_core::{Content, ToolError};
use anyhow::{Context, Result};
use async_trait::async_trait; use async_trait::async_trait;
use serde_json::Value; use serde_json::Value;
use std::collections::VecDeque;
use std::env;
use std::sync::Arc;
use tokio::sync::RwLock;
use crate::agents::tool_vectordb::ToolVectorDB;
use crate::model::ModelConfig;
use crate::providers::{self, base::Provider};
#[derive(Debug, Clone, PartialEq)]
pub enum RouterToolSelectionStrategy { pub enum RouterToolSelectionStrategy {
Vector, Vector,
} }
@@ -10,27 +22,194 @@ pub enum RouterToolSelectionStrategy {
#[async_trait] #[async_trait]
pub trait RouterToolSelector: Send + Sync { pub trait RouterToolSelector: Send + Sync {
async fn select_tools(&self, params: Value) -> Result<Vec<Content>, ToolError>; async fn select_tools(&self, params: Value) -> Result<Vec<Content>, ToolError>;
async fn index_tools(&self, tools: &[Tool]) -> Result<(), ToolError>;
async fn remove_tool(&self, tool_name: &str) -> Result<(), ToolError>;
async fn record_tool_call(&self, tool_name: &str) -> Result<(), ToolError>;
async fn get_recent_tool_calls(&self, limit: usize) -> Result<Vec<String>, ToolError>;
fn selector_type(&self) -> RouterToolSelectionStrategy;
} }
pub struct VectorToolSelector; pub struct VectorToolSelector {
vector_db: Arc<RwLock<ToolVectorDB>>,
embedding_provider: Arc<dyn Provider>,
recent_tool_calls: Arc<RwLock<VecDeque<String>>>,
}
impl VectorToolSelector {
pub async fn new(provider: Arc<dyn Provider>, table_name: String) -> Result<Self> {
let vector_db = ToolVectorDB::new(Some(table_name)).await?;
let embedding_provider = if env::var("EMBEDDING_MODEL_PROVIDER").is_ok() {
// If env var is set, create a new provider for embeddings
// Get embedding model and provider from environment variables
let embedding_model = env::var("EMBEDDING_MODEL")
.unwrap_or_else(|_| "text-embedding-3-small".to_string());
let embedding_provider_name =
env::var("EMBEDDING_MODEL_PROVIDER").unwrap_or_else(|_| "openai".to_string());
// Create the provider using the factory
let model_config = ModelConfig::new(embedding_model);
providers::create(&embedding_provider_name, model_config).context(format!(
"Failed to create {} provider for embeddings. If using OpenAI, make sure OPENAI_API_KEY env var is set or that you have configured the OpenAI provider via Goose before.",
embedding_provider_name
))?
} else {
// Otherwise fall back to using the same provider instance as used for base goose model
provider.clone()
};
Ok(Self {
vector_db: Arc::new(RwLock::new(vector_db)),
embedding_provider,
recent_tool_calls: Arc::new(RwLock::new(VecDeque::with_capacity(100))),
})
}
}
#[async_trait] #[async_trait]
impl RouterToolSelector for VectorToolSelector { impl RouterToolSelector for VectorToolSelector {
async fn select_tools(&self, params: Value) -> Result<Vec<Content>, ToolError> { async fn select_tools(&self, params: Value) -> Result<Vec<Content>, ToolError> {
let query = params.get("query").and_then(|v| v.as_str()); let query = params
println!("query: {:?}", query); .get("query")
let selected_tools = Vec::new(); .and_then(|v| v.as_str())
// TODO: placeholder for vector tool selection .ok_or_else(|| ToolError::InvalidParameters("Missing 'query' parameter".to_string()))?;
let k = params.get("k").and_then(|v| v.as_u64()).unwrap_or(5) as usize;
// Check if provider supports embeddings
if !self.embedding_provider.supports_embeddings() {
return Err(ToolError::ExecutionError(
"Embedding provider does not support embeddings".to_string(),
));
}
let embeddings = self
.embedding_provider
.create_embeddings(vec![query.to_string()])
.await
.map_err(|e| {
ToolError::ExecutionError(format!("Failed to generate query embedding: {}", e))
})?;
let query_embedding = embeddings
.into_iter()
.next()
.ok_or_else(|| ToolError::ExecutionError("No embedding returned".to_string()))?;
let vector_db = self.vector_db.read().await;
let tools = vector_db
.search_tools(query_embedding, k)
.await
.map_err(|e| ToolError::ExecutionError(format!("Failed to search tools: {}", e)))?;
let selected_tools: Vec<Content> = tools
.into_iter()
.map(|tool| {
let text = format!(
"Tool: {}\nDescription: {}\nSchema: {}",
tool.tool_name, tool.description, tool.schema
);
Content::Text(TextContent {
text,
annotations: None,
})
})
.collect();
Ok(selected_tools) Ok(selected_tools)
} }
async fn index_tools(&self, tools: &[Tool]) -> Result<(), ToolError> {
let texts_to_embed: Vec<String> = tools
.iter()
.map(|tool| {
let schema_str = serde_json::to_string_pretty(&tool.input_schema)
.unwrap_or_else(|_| "{}".to_string());
format!("{} {} {}", tool.name, tool.description, schema_str)
})
.collect();
if !self.embedding_provider.supports_embeddings() {
return Err(ToolError::ExecutionError(
"Embedding provider does not support embeddings".to_string(),
));
}
let embeddings = self
.embedding_provider
.create_embeddings(texts_to_embed)
.await
.map_err(|e| {
ToolError::ExecutionError(format!("Failed to generate tool embeddings: {}", e))
})?;
// Create tool records
let tool_records: Vec<crate::agents::tool_vectordb::ToolRecord> = tools
.iter()
.zip(embeddings.into_iter())
.map(|(tool, vector)| {
let schema_str = serde_json::to_string_pretty(&tool.input_schema)
.unwrap_or_else(|_| "{}".to_string());
crate::agents::tool_vectordb::ToolRecord {
tool_name: tool.name.clone(),
description: tool.description.clone(),
schema: schema_str,
vector,
}
})
.collect();
// Index all tools at once
let vector_db = self.vector_db.read().await;
vector_db
.index_tools(tool_records)
.await
.map_err(|e| ToolError::ExecutionError(format!("Failed to index tools: {}", e)))?;
Ok(())
}
async fn remove_tool(&self, tool_name: &str) -> Result<(), ToolError> {
let vector_db = self.vector_db.read().await;
vector_db.remove_tool(tool_name).await.map_err(|e| {
ToolError::ExecutionError(format!("Failed to remove tool {}: {}", tool_name, e))
})?;
Ok(())
}
async fn record_tool_call(&self, tool_name: &str) -> Result<(), ToolError> {
let mut recent_calls = self.recent_tool_calls.write().await;
if recent_calls.len() >= 100 {
recent_calls.pop_front();
}
recent_calls.push_back(tool_name.to_string());
Ok(())
}
async fn get_recent_tool_calls(&self, limit: usize) -> Result<Vec<String>, ToolError> {
let recent_calls = self.recent_tool_calls.read().await;
Ok(recent_calls.iter().rev().take(limit).cloned().collect())
}
fn selector_type(&self) -> RouterToolSelectionStrategy {
RouterToolSelectionStrategy::Vector
}
} }
// Helper function to create a boxed tool selector // Helper function to create a boxed tool selector
pub fn create_tool_selector( pub async fn create_tool_selector(
strategy: Option<RouterToolSelectionStrategy>, strategy: Option<RouterToolSelectionStrategy>,
) -> Box<dyn RouterToolSelector> { provider: Arc<dyn Provider>,
table_name: String,
) -> Result<Box<dyn RouterToolSelector>> {
match strategy { match strategy {
Some(RouterToolSelectionStrategy::Vector) => Box::new(VectorToolSelector), Some(RouterToolSelectionStrategy::Vector) => {
_ => Box::new(VectorToolSelector), // Default to VectorToolSelector let selector = VectorToolSelector::new(provider, table_name).await?;
Ok(Box::new(selector))
}
None => {
let selector = VectorToolSelector::new(provider, table_name).await?;
Ok(Box::new(selector))
}
} }
} }

View File

@@ -22,7 +22,8 @@ pub fn vector_search_tool() -> Tool {
"type": "object", "type": "object",
"required": ["query"], "required": ["query"],
"properties": { "properties": {
"query": {"type": "string", "description": "The query to search for the most relevant tools based on the user's messages"} "query": {"type": "string", "description": "The query to search for the most relevant tools based on the user's messages"},
"k": {"type": "integer", "description": "The number of tools to retrieve (defaults to 5)", "default": 5}
} }
}), }),
Some(ToolAnnotations { Some(ToolAnnotations {

View File

@@ -0,0 +1,106 @@
use anyhow::{anyhow, Result};
use std::sync::Arc;
use tracing;
use crate::agents::extension_manager::ExtensionManager;
use crate::agents::platform_tools;
use crate::agents::router_tool_selector::{RouterToolSelectionStrategy, RouterToolSelector};
/// Manages tool indexing operations for the router when vector routing is enabled
pub struct ToolRouterIndexManager;
impl ToolRouterIndexManager {
/// Updates the vector index for tools when extensions are added or removed
pub async fn update_extension_tools(
selector: &Arc<Box<dyn RouterToolSelector>>,
extension_manager: &ExtensionManager,
extension_name: &str,
action: &str,
) -> Result<()> {
match action {
"add" => {
// Get tools for specific extension
let tools = extension_manager
.get_prefixed_tools(Some(extension_name.to_string()))
.await?;
if !tools.is_empty() {
// Index all tools at once
selector.index_tools(&tools).await.map_err(|e| {
anyhow!(
"Failed to index tools for extension {}: {}",
extension_name,
e
)
})?;
tracing::info!(
"Indexed {} tools for extension {}",
tools.len(),
extension_name
);
}
}
"remove" => {
// Get tool names for the extension to remove them
let tools = extension_manager
.get_prefixed_tools(Some(extension_name.to_string()))
.await?;
for tool in &tools {
selector
.remove_tool(&tool.name)
.await
.map_err(|e| anyhow!("Failed to remove tool {}: {}", tool.name, e))?;
}
tracing::info!(
"Removed {} tools for extension {}",
tools.len(),
extension_name
);
}
_ => {
anyhow::bail!("Invalid action '{}' for tool indexing", action);
}
}
Ok(())
}
/// Indexes platform tools (search_available_extensions, manage_extensions, etc.)
pub async fn index_platform_tools(
selector: &Arc<Box<dyn RouterToolSelector>>,
extension_manager: &ExtensionManager,
) -> Result<()> {
let mut tools = Vec::new();
// Add the standard platform tools
tools.push(platform_tools::search_available_extensions_tool());
tools.push(platform_tools::manage_extensions_tool());
// Add resource tools if supported
if extension_manager.supports_resources() {
tools.push(platform_tools::read_resource_tool());
tools.push(platform_tools::list_resources_tool());
}
// Index all platform tools at once
selector
.index_tools(&tools)
.await
.map_err(|e| anyhow!("Failed to index platform tools: {}", e))?;
tracing::info!("Indexed platform tools for vector search");
Ok(())
}
/// Helper to check if vector tool router is enabled
pub fn vector_tool_router_enabled(selector: &Option<Arc<Box<dyn RouterToolSelector>>>) -> bool {
if let Some(selector) = selector {
selector.selector_type() == RouterToolSelectionStrategy::Vector
} else {
false
}
}
}

View File

@@ -0,0 +1,440 @@
use anyhow::{Context, Result};
use arrow::array::{FixedSizeListBuilder, StringArray};
use arrow::datatypes::{DataType, Field, Schema};
use chrono::Local;
use etcetera::base_strategy::{BaseStrategy, Xdg};
use futures::TryStreamExt;
use lancedb::connect;
use lancedb::connection::Connection;
use lancedb::query::{ExecutableQuery, QueryBase};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::RwLock;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolRecord {
pub tool_name: String,
pub description: String,
pub schema: String,
pub vector: Vec<f32>,
}
pub struct ToolVectorDB {
connection: Arc<RwLock<Connection>>,
table_name: String,
}
impl ToolVectorDB {
pub async fn new(table_name: Option<String>) -> Result<Self> {
let db_path = Self::get_db_path()?;
// Ensure the directory exists
if let Some(parent) = db_path.parent() {
tokio::fs::create_dir_all(parent)
.await
.context("Failed to create database directory")?;
}
let connection = connect(db_path.to_str().unwrap())
.execute()
.await
.context("Failed to connect to LanceDB")?;
let tool_db = Self {
connection: Arc::new(RwLock::new(connection)),
table_name: table_name.unwrap_or_else(|| "tools".to_string()),
};
// Initialize the table if it doesn't exist
tool_db.init_table().await?;
Ok(tool_db)
}
fn get_db_path() -> Result<PathBuf> {
let data_dir = Xdg::new()
.context("Failed to determine base strategy")?
.data_dir();
Ok(data_dir.join("goose").join("tool_db"))
}
async fn init_table(&self) -> Result<()> {
let connection = self.connection.read().await;
// Check if table exists
let table_names = connection
.table_names()
.execute()
.await
.context("Failed to list tables")?;
if !table_names.contains(&self.table_name) {
// Create the table schema
let schema = Arc::new(Schema::new(vec![
Field::new("tool_name", DataType::Utf8, false),
Field::new("description", DataType::Utf8, false),
Field::new("schema", DataType::Utf8, false),
Field::new(
"vector",
DataType::FixedSizeList(
Arc::new(Field::new("item", DataType::Float32, true)),
1536, // OpenAI embedding dimension
),
false,
),
]));
// Create empty table
let tool_names = StringArray::from(vec![] as Vec<&str>);
let descriptions = StringArray::from(vec![] as Vec<&str>);
let schemas = StringArray::from(vec![] as Vec<&str>);
// Create empty fixed size list array for vectors
let mut vectors_builder =
FixedSizeListBuilder::new(arrow::array::Float32Builder::new(), 1536);
let vectors = vectors_builder.finish();
let batch = arrow::record_batch::RecordBatch::try_new(
schema.clone(),
vec![
Arc::new(tool_names),
Arc::new(descriptions),
Arc::new(schemas),
Arc::new(vectors),
],
)
.context("Failed to create record batch")?;
// Create an empty table with the schema
// LanceDB will create the table from the RecordBatch
drop(connection);
let connection = self.connection.write().await;
// Use the RecordBatch directly
let reader = arrow::record_batch::RecordBatchIterator::new(
vec![Ok(batch)].into_iter(),
schema.clone(),
);
connection
.create_table(&self.table_name, Box::new(reader))
.execute()
.await
.map_err(|e| {
anyhow::anyhow!("Failed to create tools table '{}': {}", self.table_name, e)
})?;
}
Ok(())
}
#[cfg(test)]
pub async fn clear_tools(&self) -> Result<()> {
let connection = self.connection.write().await;
// Try to open the table first
match connection.open_table(&self.table_name).execute().await {
Ok(table) => {
// Delete all records instead of dropping the table
table
.delete("1=1") // This will match all records
.await
.context("Failed to delete all records")?;
}
Err(_) => {
// If table doesn't exist, that's fine - we'll create it
}
}
drop(connection);
// Ensure table exists with correct schema
self.init_table().await?;
Ok(())
}
pub async fn index_tools(&self, tools: Vec<ToolRecord>) -> Result<()> {
if tools.is_empty() {
return Ok(());
}
let tool_names: Vec<&str> = tools.iter().map(|t| t.tool_name.as_str()).collect();
let descriptions: Vec<&str> = tools.iter().map(|t| t.description.as_str()).collect();
let schemas: Vec<&str> = tools.iter().map(|t| t.schema.as_str()).collect();
let vectors_data: Vec<Option<Vec<Option<f32>>>> = tools
.iter()
.map(|t| Some(t.vector.iter().map(|&v| Some(v)).collect()))
.collect();
let schema = Arc::new(Schema::new(vec![
Field::new("tool_name", DataType::Utf8, false),
Field::new("description", DataType::Utf8, false),
Field::new("schema", DataType::Utf8, false),
Field::new(
"vector",
DataType::FixedSizeList(
Arc::new(Field::new("item", DataType::Float32, true)),
1536,
),
false,
),
]));
let tool_names_array = StringArray::from(tool_names);
let descriptions_array = StringArray::from(descriptions);
let schemas_array = StringArray::from(schemas);
// Build vectors array
let mut vectors_builder =
FixedSizeListBuilder::new(arrow::array::Float32Builder::new(), 1536);
for vector_opt in vectors_data {
if let Some(vector) = vector_opt {
let values = vectors_builder.values();
for val_opt in vector {
if let Some(val) = val_opt {
values.append_value(val);
} else {
values.append_null();
}
}
vectors_builder.append(true);
} else {
vectors_builder.append(false);
}
}
let vectors_array = vectors_builder.finish();
let batch = arrow::record_batch::RecordBatch::try_new(
schema.clone(),
vec![
Arc::new(tool_names_array),
Arc::new(descriptions_array),
Arc::new(schemas_array),
Arc::new(vectors_array),
],
)
.context("Failed to create record batch")?;
let connection = self.connection.read().await;
let table = connection
.open_table(&self.table_name)
.execute()
.await
.context("Failed to open tools table")?;
// Add batch to table using RecordBatchIterator
let reader = arrow::record_batch::RecordBatchIterator::new(
vec![Ok(batch)].into_iter(),
schema.clone(),
);
table
.add(Box::new(reader))
.execute()
.await
.context("Failed to add tools to table")?;
Ok(())
}
pub async fn search_tools(&self, query_vector: Vec<f32>, k: usize) -> Result<Vec<ToolRecord>> {
let connection = self.connection.read().await;
let table = connection
.open_table(&self.table_name)
.execute()
.await
.context("Failed to open tools table")?;
let results = table
.vector_search(query_vector)
.context("Failed to create vector search")?
.limit(k)
.execute()
.await
.context("Failed to execute vector search")?;
let batches: Vec<_> = results.try_collect().await?;
let mut tools = Vec::new();
for batch in batches {
let tool_names = batch
.column_by_name("tool_name")
.context("Missing tool_name column")?
.as_any()
.downcast_ref::<StringArray>()
.context("Invalid tool_name column type")?;
let descriptions = batch
.column_by_name("description")
.context("Missing description column")?
.as_any()
.downcast_ref::<StringArray>()
.context("Invalid description column type")?;
let schemas = batch
.column_by_name("schema")
.context("Missing schema column")?
.as_any()
.downcast_ref::<StringArray>()
.context("Invalid schema column type")?;
// Get the distance scores
let distances = batch
.column_by_name("_distance")
.context("Missing _distance column")?
.as_any()
.downcast_ref::<arrow::array::Float32Array>()
.context("Invalid _distance column type")?;
for i in 0..batch.num_rows() {
let tool_name = tool_names.value(i).to_string();
let _distance = distances.value(i);
tools.push(ToolRecord {
tool_name,
description: descriptions.value(i).to_string(),
schema: schemas.value(i).to_string(),
vector: vec![], // We don't need to return the vector
});
}
}
Ok(tools)
}
pub async fn remove_tool(&self, tool_name: &str) -> Result<()> {
let connection = self.connection.read().await;
let table = connection
.open_table(&self.table_name)
.execute()
.await
.context("Failed to open tools table")?;
// Delete records matching the tool name
table
.delete(&format!("tool_name = '{}'", tool_name))
.await
.context("Failed to delete tool")?;
Ok(())
}
}
pub fn generate_table_id() -> String {
Local::now().format("%Y%m%d_%H%M%S").to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_tool_vectordb_creation() {
let db = ToolVectorDB::new(Some("test_tools_vectordb_creation".to_string()))
.await
.unwrap();
db.clear_tools().await.unwrap();
assert_eq!(db.table_name, "test_tools_vectordb_creation");
}
#[tokio::test]
async fn test_tool_vectordb_operations() -> Result<()> {
// Create a new database instance with a unique table name
let db = ToolVectorDB::new(Some("test_tool_vectordb_operations".to_string())).await?;
// Clear any existing tools
db.clear_tools().await?;
// Create test tool records
let test_tools = vec![
ToolRecord {
tool_name: "test_tool_1".to_string(),
description: "A test tool for reading files".to_string(),
schema: r#"{"type": "object", "properties": {"path": {"type": "string"}}}"#
.to_string(),
vector: vec![0.1; 1536], // Mock embedding vector
},
ToolRecord {
tool_name: "test_tool_2".to_string(),
description: "A test tool for writing files".to_string(),
schema: r#"{"type": "object", "properties": {"path": {"type": "string"}}}"#
.to_string(),
vector: vec![0.2; 1536], // Different mock embedding vector
},
];
// Index the test tools
db.index_tools(test_tools).await?;
// Search for tools using a query vector similar to test_tool_1
let query_vector = vec![0.1; 1536];
let results = db.search_tools(query_vector, 2).await?;
// Verify results
assert_eq!(results.len(), 2, "Should find both tools");
assert_eq!(
results[0].tool_name, "test_tool_1",
"First result should be test_tool_1"
);
assert_eq!(
results[1].tool_name, "test_tool_2",
"Second result should be test_tool_2"
);
Ok(())
}
#[tokio::test]
async fn test_empty_db() -> Result<()> {
// Create a new database instance with a unique table name
let db = ToolVectorDB::new(Some("test_empty_db".to_string())).await?;
// Clear any existing tools
db.clear_tools().await?;
// Search in empty database
let query_vector = vec![0.1; 1536];
let results = db.search_tools(query_vector, 2).await?;
// Verify no results returned
assert_eq!(results.len(), 0, "Empty database should return no results");
Ok(())
}
#[tokio::test]
async fn test_tool_deletion() -> Result<()> {
// Create a new database instance with a unique table name
let db = ToolVectorDB::new(Some("test_tool_deletion".to_string())).await?;
// Clear any existing tools
db.clear_tools().await?;
// Create and index a test tool
let test_tool = ToolRecord {
tool_name: "test_tool_to_delete".to_string(),
description: "A test tool that will be deleted".to_string(),
schema: r#"{"type": "object", "properties": {"path": {"type": "string"}}}"#.to_string(),
vector: vec![0.1; 1536],
};
db.index_tools(vec![test_tool]).await?;
// Verify tool exists
let query_vector = vec![0.1; 1536];
let results = db.search_tools(query_vector.clone(), 1).await?;
assert_eq!(results.len(), 1, "Tool should exist before deletion");
// Delete the tool
db.remove_tool("test_tool_to_delete").await?;
// Verify tool is gone
let results = db.search_tools(query_vector.clone(), 1).await?;
assert_eq!(results.len(), 0, "Tool should be deleted");
Ok(())
}
}

View File

@@ -183,6 +183,18 @@ pub trait Provider: Send + Sync {
async fn fetch_supported_models_async(&self) -> Result<Option<Vec<String>>, ProviderError> { async fn fetch_supported_models_async(&self) -> Result<Option<Vec<String>>, ProviderError> {
Ok(None) Ok(None)
} }
/// Check if this provider supports embeddings
fn supports_embeddings(&self) -> bool {
false
}
/// Create embeddings if supported. Default implementation returns an error.
async fn create_embeddings(&self, _texts: Vec<String>) -> Result<Vec<Vec<f32>>, ProviderError> {
Err(ProviderError::ExecutionError(
"This provider does not support embeddings".to_string(),
))
}
} }
#[cfg(test)] #[cfg(test)]

View File

@@ -1,11 +1,5 @@
use anyhow::Result;
use async_trait::async_trait;
use reqwest::{Client, StatusCode};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::time::Duration;
use super::base::{ConfigKey, Provider, ProviderMetadata, ProviderUsage, Usage}; use super::base::{ConfigKey, Provider, ProviderMetadata, ProviderUsage, Usage};
use super::embedding::EmbeddingCapable;
use super::errors::ProviderError; use super::errors::ProviderError;
use super::formats::databricks::{create_request, get_usage, response_to_message}; use super::formats::databricks::{create_request, get_usage, response_to_message};
use super::oauth; use super::oauth;
@@ -14,8 +8,16 @@ use crate::config::ConfigError;
use crate::message::Message; use crate::message::Message;
use crate::model::ModelConfig; use crate::model::ModelConfig;
use mcp_core::tool::Tool; use mcp_core::tool::Tool;
use serde_json::json;
use url::Url; use url::Url;
use anyhow::Result;
use async_trait::async_trait;
use reqwest::{Client, StatusCode};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::time::Duration;
const DEFAULT_CLIENT_ID: &str = "databricks-cli"; const DEFAULT_CLIENT_ID: &str = "databricks-cli";
const DEFAULT_REDIRECT_URL: &str = "http://localhost:8020"; const DEFAULT_REDIRECT_URL: &str = "http://localhost:8020";
// "offline_access" scope is used to request an OAuth 2.0 Refresh Token // "offline_access" scope is used to request an OAuth 2.0 Refresh Token
@@ -128,7 +130,6 @@ impl DatabricksProvider {
/// ///
/// * `host` - The Databricks host URL /// * `host` - The Databricks host URL
/// * `token` - The Databricks API token /// * `token` - The Databricks API token
/// * `model` - The model configuration
/// ///
/// # Returns /// # Returns
/// ///
@@ -166,7 +167,17 @@ impl DatabricksProvider {
async fn post(&self, payload: Value) -> Result<Value, ProviderError> { async fn post(&self, payload: Value) -> Result<Value, ProviderError> {
let base_url = Url::parse(&self.host) let base_url = Url::parse(&self.host)
.map_err(|e| ProviderError::RequestFailed(format!("Invalid base URL: {e}")))?; .map_err(|e| ProviderError::RequestFailed(format!("Invalid base URL: {e}")))?;
let path = format!("serving-endpoints/{}/invocations", self.model.model_name);
// Check if this is an embedding request by looking at the payload structure
let is_embedding = payload.get("input").is_some() && payload.get("messages").is_none();
let path = if is_embedding {
// For embeddings, use the embeddings endpoint
format!("serving-endpoints/{}/invocations", "text-embedding-3-small")
} else {
// For chat completions, use the model name in the path
format!("serving-endpoints/{}/invocations", self.model.model_name)
};
let url = base_url.join(&path).map_err(|e| { let url = base_url.join(&path).map_err(|e| {
ProviderError::RequestFailed(format!("Failed to construct endpoint URL: {e}")) ProviderError::RequestFailed(format!("Failed to construct endpoint URL: {e}"))
})?; })?;
@@ -184,7 +195,7 @@ impl DatabricksProvider {
let payload: Option<Value> = response.json().await.ok(); let payload: Option<Value> = response.json().await.ok();
match status { match status {
StatusCode::OK => payload.ok_or_else( || ProviderError::RequestFailed("Response body is not valid JSON".to_string()) ), StatusCode::OK => payload.ok_or_else(|| ProviderError::RequestFailed("Response body is not valid JSON".to_string())),
StatusCode::UNAUTHORIZED | StatusCode::FORBIDDEN => { StatusCode::UNAUTHORIZED | StatusCode::FORBIDDEN => {
Err(ProviderError::Authentication(format!("Authentication failed. Please ensure your API keys are valid and have the required permissions. \ Err(ProviderError::Authentication(format!("Authentication failed. Please ensure your API keys are valid and have the required permissions. \
Status: {}. Response: {:?}", status, payload))) Status: {}. Response: {:?}", status, payload)))
@@ -295,4 +306,47 @@ impl Provider for DatabricksProvider {
Ok((message, ProviderUsage::new(model, usage))) Ok((message, ProviderUsage::new(model, usage)))
} }
fn supports_embeddings(&self) -> bool {
true
}
async fn create_embeddings(&self, texts: Vec<String>) -> Result<Vec<Vec<f32>>, ProviderError> {
EmbeddingCapable::create_embeddings(self, texts)
.await
.map_err(|e| ProviderError::ExecutionError(e.to_string()))
}
}
#[async_trait]
impl EmbeddingCapable for DatabricksProvider {
async fn create_embeddings(&self, texts: Vec<String>) -> Result<Vec<Vec<f32>>> {
if texts.is_empty() {
return Ok(vec![]);
}
// Create request in Databricks format for embeddings
let request = json!({
"input": texts,
});
let response = self.post(request).await?;
let embeddings = response["data"]
.as_array()
.ok_or_else(|| anyhow::anyhow!("Invalid response format: missing data array"))?
.iter()
.map(|item| {
item["embedding"]
.as_array()
.ok_or_else(|| anyhow::anyhow!("Invalid embedding format"))?
.iter()
.map(|v| v.as_f64().map(|f| f as f32))
.collect::<Option<Vec<f32>>>()
.ok_or_else(|| anyhow::anyhow!("Invalid embedding values"))
})
.collect::<Result<Vec<Vec<f32>>>>()?;
Ok(embeddings)
}
} }

View File

@@ -0,0 +1,24 @@
use anyhow::Result;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmbeddingRequest {
pub input: Vec<String>,
pub model: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmbeddingResponse {
pub data: Vec<EmbeddingData>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmbeddingData {
pub embedding: Vec<f32>,
}
#[async_trait]
pub trait EmbeddingCapable {
async fn create_embeddings(&self, texts: Vec<String>) -> Result<Vec<Vec<f32>>>;
}

View File

@@ -4,6 +4,7 @@ pub mod azureauth;
pub mod base; pub mod base;
pub mod bedrock; pub mod bedrock;
pub mod databricks; pub mod databricks;
pub mod embedding;
pub mod errors; pub mod errors;
mod factory; mod factory;
pub mod formats; pub mod formats;

View File

@@ -6,6 +6,7 @@ use std::collections::HashMap;
use std::time::Duration; use std::time::Duration;
use super::base::{ConfigKey, Provider, ProviderMetadata, ProviderUsage, Usage}; use super::base::{ConfigKey, Provider, ProviderMetadata, ProviderUsage, Usage};
use super::embedding::{EmbeddingCapable, EmbeddingRequest, EmbeddingResponse};
use super::errors::ProviderError; use super::errors::ProviderError;
use super::formats::openai::{create_request, get_usage, response_to_message}; use super::formats::openai::{create_request, get_usage, response_to_message};
use super::utils::{emit_debug_trace, get_model, handle_response_openai_compat, ImageFormat}; use super::utils::{emit_debug_trace, get_model, handle_response_openai_compat, ImageFormat};
@@ -80,18 +81,8 @@ impl OpenAiProvider {
}) })
} }
async fn post(&self, payload: Value) -> Result<Value, ProviderError> { /// Helper function to add OpenAI-specific headers to a request
let base_url = url::Url::parse(&self.host) fn add_headers(&self, mut request: reqwest::RequestBuilder) -> reqwest::RequestBuilder {
.map_err(|e| ProviderError::RequestFailed(format!("Invalid base URL: {e}")))?;
let url = base_url.join(&self.base_path).map_err(|e| {
ProviderError::RequestFailed(format!("Failed to construct endpoint URL: {e}"))
})?;
let mut request = self
.client
.post(url)
.header("Authorization", format!("Bearer {}", self.api_key));
// Add organization header if present // Add organization header if present
if let Some(org) = &self.organization { if let Some(org) = &self.organization {
request = request.header("OpenAI-Organization", org); request = request.header("OpenAI-Organization", org);
@@ -102,12 +93,30 @@ impl OpenAiProvider {
request = request.header("OpenAI-Project", project); request = request.header("OpenAI-Project", project);
} }
// Add custom headers if present
if let Some(custom_headers) = &self.custom_headers { if let Some(custom_headers) = &self.custom_headers {
for (key, value) in custom_headers { for (key, value) in custom_headers {
request = request.header(key, value); request = request.header(key, value);
} }
} }
request
}
async fn post(&self, payload: Value) -> Result<Value, ProviderError> {
let base_url = url::Url::parse(&self.host)
.map_err(|e| ProviderError::RequestFailed(format!("Invalid base URL: {e}")))?;
let url = base_url.join(&self.base_path).map_err(|e| {
ProviderError::RequestFailed(format!("Failed to construct endpoint URL: {e}"))
})?;
let request = self
.client
.post(url)
.header("Authorization", format!("Bearer {}", self.api_key));
let request = self.add_headers(request);
let response = request.json(&payload).send().await?; let response = request.json(&payload).send().await?;
handle_response_openai_compat(response).await handle_response_openai_compat(response).await
@@ -209,6 +218,16 @@ impl Provider for OpenAiProvider {
models.sort(); models.sort();
Ok(Some(models)) Ok(Some(models))
} }
fn supports_embeddings(&self) -> bool {
true
}
async fn create_embeddings(&self, texts: Vec<String>) -> Result<Vec<Vec<f32>>, ProviderError> {
EmbeddingCapable::create_embeddings(self, texts)
.await
.map_err(|e| ProviderError::ExecutionError(e.to_string()))
}
} }
fn parse_custom_headers(s: String) -> HashMap<String, String> { fn parse_custom_headers(s: String) -> HashMap<String, String> {
@@ -221,3 +240,57 @@ fn parse_custom_headers(s: String) -> HashMap<String, String> {
}) })
.collect() .collect()
} }
#[async_trait]
impl EmbeddingCapable for OpenAiProvider {
async fn create_embeddings(&self, texts: Vec<String>) -> Result<Vec<Vec<f32>>> {
if texts.is_empty() {
return Ok(vec![]);
}
// Get embedding model from env var or use default
let embedding_model = std::env::var("EMBEDDING_MODEL")
.unwrap_or_else(|_| "text-embedding-3-small".to_string());
let request = EmbeddingRequest {
input: texts,
model: embedding_model,
};
// Construct embeddings endpoint URL
let base_url =
url::Url::parse(&self.host).map_err(|e| anyhow::anyhow!("Invalid base URL: {e}"))?;
let url = base_url
.join("v1/embeddings")
.map_err(|e| anyhow::anyhow!("Failed to construct embeddings URL: {e}"))?;
let req = self
.client
.post(url)
.header("Authorization", format!("Bearer {}", self.api_key))
.json(&request);
let req = self.add_headers(req);
let response = req
.send()
.await
.map_err(|e| anyhow::anyhow!("Failed to send embedding request: {e}"))?;
if !response.status().is_success() {
let error_text = response.text().await.unwrap_or_default();
return Err(anyhow::anyhow!("Embedding API error: {}", error_text));
}
let embedding_response: EmbeddingResponse = response
.json()
.await
.map_err(|e| anyhow::anyhow!("Failed to parse embedding response: {e}"))?;
Ok(embedding_response
.data
.into_iter()
.map(|d| d.embedding)
.collect())
}
}

View File

@@ -0,0 +1,86 @@
# Tool Router (preview)
## Overview
Tool Router is a powerful feature that addresses a common challenge in LLM-based development: the difficulty of selecting the right tool when multiple extensions are enabled. Traditional approaches feed an entire list of tools into the context during chat sessions, which not only consumes a significant number of tokens but also reduces the effectiveness of tool calling.
## The Problem
When you enable multiple extensions (like Slack), you get access to numerous tools such as:
- Reading threads
- Sending messages
- Creating channels
- And many more
However, you typically don't need all this functionality at once. Loading every available tool into the context can be inefficient and potentially confusing for the LLM.
## The Solution: Tool Router
Tool Router introduces a smarter way to handle tool selection through vector-based indexing. Instead of passing all tools back and forth, it:
1. Indexes all tools from your enabled extensions
2. Uses vector search to load only the relevant tools into context when needed
3. Ensures that only the functionality you actually need is available
## Configuration
To enable this feature, change the Tool Selection Strategy from default to vector.
#### CLI
To configure Tool Router in the CLI, follow these steps:
1. Run the configuration command:
```bash
./target/debug/goose configure
```
2. This will update your existing config file. Alternatively, you can edit it directly at:
```
/Users/wendytang/.config/goose/config.yaml
```
3. During configuration:
- Select "Goose Settings"
- Choose "Router Tool Selection Strategy"
- Select "Vector Strategy"
The configuration process will look like this:
```
┌ goose-configure
◇ What would you like to configure?
│ Goose Settings
◇ What setting would you like to configure?
│ Router Tool Selection Strategy
◇ Which router strategy would you like to use?
│ Vector Strategy
└ Set to Vector Strategy - using vector-based similarity for tool selection
```
#### UI
Toggle the settings button on the top right and head to 'Advanced Settings', then 'Tool Selection Strategy' at the botoom.
## Benefits
- Reduced token consumption
- More accurate tool selection
- Improved LLM performance
- Better context management
- More efficient use of available tools
## Notes
### Model Compatibility
Tool Router currently only works with Claude models served through Databricks. The embedding functionality uses OpenAI's `text-embedding-3-small` model by default.
### Feedback & Next Steps
We'd love to hear your thoughts on this feature! Please reach out in the Goose Discord channel to share your use case and experience.
Our roadmap includes:
- Expanding Tool Router support to OpenAI models
- Adding customization options for the `k` parameter that controls how many similar tools are returned during vector similarity search

View File

@@ -84,7 +84,7 @@
"@algolia/requester-fetch" "5.20.0" "@algolia/requester-fetch" "5.20.0"
"@algolia/requester-node-http" "5.20.0" "@algolia/requester-node-http" "5.20.0"
"@algolia/client-search@>= 4.9.1 < 6", "@algolia/client-search@5.20.0": "@algolia/client-search@5.20.0":
version "5.20.0" version "5.20.0"
resolved "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.20.0.tgz" resolved "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.20.0.tgz"
integrity sha512-KL1zWTzrlN4MSiaK1ea560iCA/UewMbS4ZsLQRPoDTWyrbDKVbztkPwwv764LAqgXk0fvkNZvJ3IelcK7DqhjQ== integrity sha512-KL1zWTzrlN4MSiaK1ea560iCA/UewMbS4ZsLQRPoDTWyrbDKVbztkPwwv764LAqgXk0fvkNZvJ3IelcK7DqhjQ==
@@ -177,7 +177,7 @@
resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz" resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz"
integrity sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg== integrity sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==
"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.0.0-0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.12.0", "@babel/core@^7.13.0", "@babel/core@^7.21.3", "@babel/core@^7.25.9", "@babel/core@^7.4.0 || ^8.0.0-0 <8.0.0": "@babel/core@^7.21.3", "@babel/core@^7.25.9":
version "7.26.0" version "7.26.0"
resolved "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz" resolved "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz"
integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==
@@ -1598,7 +1598,7 @@
utility-types "^3.10.0" utility-types "^3.10.0"
webpack "^5.88.1" webpack "^5.88.1"
"@docusaurus/plugin-content-docs@*", "@docusaurus/plugin-content-docs@3.7.0": "@docusaurus/plugin-content-docs@3.7.0":
version "3.7.0" version "3.7.0"
resolved "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.7.0.tgz" resolved "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.7.0.tgz"
integrity sha512-GXg5V7kC9FZE4FkUZA8oo/NrlRb06UwuICzI6tcbzj0+TVgjq/mpUXXzSgKzMS82YByi4dY2Q808njcBCyy6tQ== integrity sha512-GXg5V7kC9FZE4FkUZA8oo/NrlRb06UwuICzI6tcbzj0+TVgjq/mpUXXzSgKzMS82YByi4dY2Q808njcBCyy6tQ==
@@ -2013,7 +2013,7 @@
"@nodelib/fs.stat" "2.0.5" "@nodelib/fs.stat" "2.0.5"
run-parallel "^1.1.9" run-parallel "^1.1.9"
"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
version "2.0.5" version "2.0.5"
resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz"
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
@@ -2152,7 +2152,7 @@
"@svgr/babel-plugin-transform-react-native-svg" "8.1.0" "@svgr/babel-plugin-transform-react-native-svg" "8.1.0"
"@svgr/babel-plugin-transform-svg-component" "8.0.0" "@svgr/babel-plugin-transform-svg-component" "8.0.0"
"@svgr/core@*", "@svgr/core@8.1.0": "@svgr/core@8.1.0":
version "8.1.0" version "8.1.0"
resolved "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz" resolved "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz"
integrity sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA== integrity sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==
@@ -2473,7 +2473,7 @@
"@types/history" "^4.7.11" "@types/history" "^4.7.11"
"@types/react" "*" "@types/react" "*"
"@types/react@*", "@types/react@>= 16.8.0 < 19.0.0", "@types/react@>=16", "@types/react@>=18": "@types/react@*":
version "18.3.18" version "18.3.18"
resolved "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz" resolved "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz"
integrity sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ== integrity sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==
@@ -2558,7 +2558,7 @@
resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz" resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz"
integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==
"@webassemblyjs/ast@^1.14.1", "@webassemblyjs/ast@1.14.1": "@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1":
version "1.14.1" version "1.14.1"
resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz" resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz"
integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ== integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==
@@ -2659,7 +2659,7 @@
"@webassemblyjs/wasm-gen" "1.14.1" "@webassemblyjs/wasm-gen" "1.14.1"
"@webassemblyjs/wasm-parser" "1.14.1" "@webassemblyjs/wasm-parser" "1.14.1"
"@webassemblyjs/wasm-parser@^1.14.1", "@webassemblyjs/wasm-parser@1.14.1": "@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.14.1":
version "1.14.1" version "1.14.1"
resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz" resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz"
integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ== integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==
@@ -2709,7 +2709,7 @@ acorn-walk@^8.0.0:
dependencies: dependencies:
acorn "^8.11.0" acorn "^8.11.0"
"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.0.0, acorn@^8.0.4, acorn@^8.11.0, acorn@^8.14.0, acorn@^8.8.2: acorn@^8.0.0, acorn@^8.0.4, acorn@^8.11.0, acorn@^8.14.0, acorn@^8.8.2:
version "8.14.0" version "8.14.0"
resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz" resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz"
integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
@@ -2734,12 +2734,7 @@ ajv-formats@^2.1.1:
dependencies: dependencies:
ajv "^8.0.0" ajv "^8.0.0"
ajv-keywords@^3.4.1: ajv-keywords@^3.4.1, ajv-keywords@^3.5.2:
version "3.5.2"
resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz"
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
ajv-keywords@^3.5.2:
version "3.5.2" version "3.5.2"
resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz"
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
@@ -2751,7 +2746,7 @@ ajv-keywords@^5.1.0:
dependencies: dependencies:
fast-deep-equal "^3.1.3" fast-deep-equal "^3.1.3"
ajv@^6.12.2, ajv@^6.12.5, ajv@^6.9.1: ajv@^6.12.2, ajv@^6.12.5:
version "6.12.6" version "6.12.6"
resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -2761,7 +2756,7 @@ ajv@^6.12.2, ajv@^6.12.5, ajv@^6.9.1:
json-schema-traverse "^0.4.1" json-schema-traverse "^0.4.1"
uri-js "^4.2.2" uri-js "^4.2.2"
ajv@^8.0.0, ajv@^8.8.2, ajv@^8.9.0: ajv@^8.0.0, ajv@^8.9.0:
version "8.17.1" version "8.17.1"
resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz" resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz"
integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==
@@ -2778,7 +2773,7 @@ algoliasearch-helper@^3.22.6:
dependencies: dependencies:
"@algolia/events" "^4.0.1" "@algolia/events" "^4.0.1"
algoliasearch@^5.14.2, algoliasearch@^5.17.1, "algoliasearch@>= 3.1 < 6", "algoliasearch@>= 4.9.1 < 6": algoliasearch@^5.14.2, algoliasearch@^5.17.1:
version "5.20.0" version "5.20.0"
resolved "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.20.0.tgz" resolved "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.20.0.tgz"
integrity sha512-groO71Fvi5SWpxjI9Ia+chy0QBwT61mg6yxJV27f5YFf+Mw+STT75K6SHySpP8Co5LsCrtsbCH5dJZSRtkSKaQ== integrity sha512-groO71Fvi5SWpxjI9Ia+chy0QBwT61mg6yxJV27f5YFf+Mw+STT75K6SHySpP8Co5LsCrtsbCH5dJZSRtkSKaQ==
@@ -3045,7 +3040,7 @@ braces@^3.0.3, braces@~3.0.2:
dependencies: dependencies:
fill-range "^7.1.1" fill-range "^7.1.1"
browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.23.0, browserslist@^4.23.1, browserslist@^4.23.3, browserslist@^4.24.0, browserslist@^4.24.3, "browserslist@>= 4.21.0": browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.23.0, browserslist@^4.23.1, browserslist@^4.23.3, browserslist@^4.24.0, browserslist@^4.24.3:
version "4.24.4" version "4.24.4"
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz"
integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==
@@ -3708,27 +3703,20 @@ debounce@^1.2.1:
resolved "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz" resolved "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz"
integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==
debug@^2.6.0: debug@2.6.9, debug@^2.6.0:
version "2.6.9" version "2.6.9"
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies: dependencies:
ms "2.0.0" ms "2.0.0"
debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@4: debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1:
version "4.4.0" version "4.4.0"
resolved "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz" resolved "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz"
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
dependencies: dependencies:
ms "^2.1.3" ms "^2.1.3"
debug@2.6.9:
version "2.6.9"
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
decode-named-character-reference@^1.0.0: decode-named-character-reference@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz" resolved "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz"
@@ -3802,16 +3790,16 @@ del@^6.1.1:
rimraf "^3.0.2" rimraf "^3.0.2"
slash "^3.0.0" slash "^3.0.0"
depd@~1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz"
integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==
depd@2.0.0: depd@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
depd@~1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz"
integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==
dequal@^2.0.0: dequal@^2.0.0:
version "2.0.3" version "2.0.3"
resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz" resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz"
@@ -4366,7 +4354,7 @@ figures@^3.2.0:
dependencies: dependencies:
escape-string-regexp "^1.0.5" escape-string-regexp "^1.0.5"
file-loader@*, file-loader@^6.2.0: file-loader@^6.2.0:
version "6.2.0" version "6.2.0"
resolved "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz" resolved "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz"
integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==
@@ -4591,14 +4579,7 @@ glob-parent@^5.1.2, glob-parent@~5.1.2:
dependencies: dependencies:
is-glob "^4.0.1" is-glob "^4.0.1"
glob-parent@^6.0.1: glob-parent@^6.0.1, glob-parent@^6.0.2:
version "6.0.2"
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz"
integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
dependencies:
is-glob "^4.0.3"
glob-parent@^6.0.2:
version "6.0.2" version "6.0.2"
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz"
integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
@@ -4707,16 +4688,16 @@ got@^12.1.0:
p-cancelable "^3.0.0" p-cancelable "^3.0.0"
responselike "^3.0.0" responselike "^3.0.0"
graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9:
version "4.2.11"
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
graceful-fs@4.2.10: graceful-fs@4.2.10:
version "4.2.10" version "4.2.10"
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz"
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9:
version "4.2.11"
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
gray-matter@^4.0.3: gray-matter@^4.0.3:
version "4.0.3" version "4.0.3"
resolved "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz" resolved "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz"
@@ -5008,16 +4989,6 @@ http-deceiver@^1.2.7:
resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz" resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz"
integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==
http-errors@~1.6.2:
version "1.6.3"
resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz"
integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==
dependencies:
depd "~1.1.2"
inherits "2.0.3"
setprototypeof "1.1.0"
statuses ">= 1.4.0 < 2"
http-errors@2.0.0: http-errors@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz"
@@ -5029,6 +5000,16 @@ http-errors@2.0.0:
statuses "2.0.1" statuses "2.0.1"
toidentifier "1.0.1" toidentifier "1.0.1"
http-errors@~1.6.2:
version "1.6.3"
resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz"
integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==
dependencies:
depd "~1.1.2"
inherits "2.0.3"
setprototypeof "1.1.0"
statuses ">= 1.4.0 < 2"
http-parser-js@>=0.5.1: http-parser-js@>=0.5.1:
version "0.5.9" version "0.5.9"
resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.9.tgz" resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.9.tgz"
@@ -5132,7 +5113,7 @@ inflight@^1.0.4:
once "^1.3.0" once "^1.3.0"
wrappy "1" wrappy "1"
inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3, inherits@2, inherits@2.0.4: inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3:
version "2.0.4" version "2.0.4"
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -5142,16 +5123,16 @@ inherits@2.0.3:
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz"
integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==
ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
version "1.3.8"
resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz"
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
ini@2.0.0: ini@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz" resolved "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz"
integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==
ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
version "1.3.8"
resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz"
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
inline-style-parser@0.2.4: inline-style-parser@0.2.4:
version "0.2.4" version "0.2.4"
resolved "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz" resolved "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz"
@@ -5169,16 +5150,16 @@ invariant@^2.2.4:
dependencies: dependencies:
loose-envify "^1.0.0" loose-envify "^1.0.0"
ipaddr.js@^2.0.1:
version "2.2.0"
resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz"
integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==
ipaddr.js@1.9.1: ipaddr.js@1.9.1:
version "1.9.1" version "1.9.1"
resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz"
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
ipaddr.js@^2.0.1:
version "2.2.0"
resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz"
integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==
is-alphabetical@^2.0.0: is-alphabetical@^2.0.0:
version "2.0.1" version "2.0.1"
resolved "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz" resolved "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz"
@@ -5342,16 +5323,16 @@ is-yarn-global@^0.4.0:
resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz" resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz"
integrity sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ== integrity sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
isarray@0.0.1: isarray@0.0.1:
version "0.0.1" version "0.0.1"
resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
isexe@^2.0.0: isexe@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz"
@@ -6339,6 +6320,11 @@ micromatch@^4.0.2, micromatch@^4.0.5, micromatch@^4.0.8:
braces "^3.0.3" braces "^3.0.3"
picomatch "^2.3.1" picomatch "^2.3.1"
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
"mime-db@>= 1.43.0 < 2": "mime-db@>= 1.43.0 < 2":
version "1.53.0" version "1.53.0"
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz"
@@ -6349,40 +6335,14 @@ mime-db@~1.33.0:
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz"
integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==
mime-db@1.52.0: mime-types@2.1.18, mime-types@~2.1.17:
version "1.52.0"
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.27:
version "2.1.35"
resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mime-types@^2.1.31:
version "2.1.35"
resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mime-types@~2.1.17, mime-types@2.1.18:
version "2.1.18" version "2.1.18"
resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz"
integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==
dependencies: dependencies:
mime-db "~1.33.0" mime-db "~1.33.0"
mime-types@~2.1.24: mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.24, mime-types@~2.1.34:
version "2.1.35"
resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mime-types@~2.1.34:
version "2.1.35" version "2.1.35"
resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
@@ -6422,7 +6382,7 @@ minimalistic-assert@^1.0.0:
resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz"
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@3.1.2: minimatch@3.1.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1:
version "3.1.2" version "3.1.2"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
@@ -6463,16 +6423,16 @@ mrmime@^2.0.0:
resolved "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz" resolved "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz"
integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw== integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==
ms@^2.1.3, ms@2.1.3:
version "2.1.3"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
ms@2.0.0: ms@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
ms@2.1.3, ms@^2.1.3:
version "2.1.3"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
multicast-dns@^7.2.5: multicast-dns@^7.2.5:
version "7.2.5" version "7.2.5"
resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz" resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz"
@@ -6495,16 +6455,16 @@ nanoid@^3.3.8:
resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz" resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz"
integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==
negotiator@~0.6.4:
version "0.6.4"
resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz"
integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==
negotiator@0.6.3: negotiator@0.6.3:
version "0.6.3" version "0.6.3"
resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
negotiator@~0.6.4:
version "0.6.4"
resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz"
integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==
neo-async@^2.6.2: neo-async@^2.6.2:
version "2.6.2" version "2.6.2"
resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz"
@@ -6853,13 +6813,6 @@ path-scurry@^1.11.1:
lru-cache "^10.2.0" lru-cache "^10.2.0"
minipass "^5.0.0 || ^6.0.2 || ^7.0.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
path-to-regexp@^1.7.0:
version "1.9.0"
resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz"
integrity sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==
dependencies:
isarray "0.0.1"
path-to-regexp@0.1.12: path-to-regexp@0.1.12:
version "0.1.12" version "0.1.12"
resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz" resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz"
@@ -6870,6 +6823,13 @@ path-to-regexp@3.3.0:
resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz" resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz"
integrity sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw== integrity sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==
path-to-regexp@^1.7.0:
version "1.9.0"
resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz"
integrity sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==
dependencies:
isarray "0.0.1"
path-type@^4.0.0: path-type@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz"
@@ -7496,7 +7456,7 @@ postcss-zindex@^6.0.2:
resolved "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-6.0.2.tgz" resolved "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-6.0.2.tgz"
integrity sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg== integrity sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg==
"postcss@^7.0.0 || ^8.0.1", postcss@^8, postcss@^8.0.0, postcss@^8.0.3, postcss@^8.0.9, postcss@^8.1.0, postcss@^8.2.14, postcss@^8.2.2, postcss@^8.4, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.24, postcss@^8.4.26, postcss@^8.4.31, postcss@^8.4.33, postcss@^8.4.35, postcss@^8.4.38, postcss@^8.4.47, postcss@^8.4.6, postcss@>=8.0.9: postcss@^8.4.21, postcss@^8.4.24, postcss@^8.4.26, postcss@^8.4.33, postcss@^8.4.35, postcss@^8.4.38, postcss@^8.4.47:
version "8.5.2" version "8.5.2"
resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz" resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz"
integrity sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA== integrity sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==
@@ -7614,21 +7574,16 @@ randombytes@^2.1.0:
dependencies: dependencies:
safe-buffer "^5.1.0" safe-buffer "^5.1.0"
range-parser@^1.2.1:
version "1.2.1"
resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
range-parser@1.2.0: range-parser@1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz" resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz"
integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A== integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==
range-parser@^1.2.1, range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
raw-body@2.5.2: raw-body@2.5.2:
version "2.5.2" version "2.5.2"
resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz" resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz"
@@ -7679,7 +7634,7 @@ react-dev-utils@^12.0.1:
strip-ansi "^6.0.1" strip-ansi "^6.0.1"
text-table "^0.2.0" text-table "^0.2.0"
react-dom@*, "react-dom@^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom@^18.0.0 || ^19.0.0", react-dom@^19.0.0, "react-dom@>= 16.8.0 < 19.0.0": react-dom@^19.0.0:
version "19.0.0" version "19.0.0"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz" resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz"
integrity sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ== integrity sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==
@@ -7724,7 +7679,7 @@ react-loadable-ssr-addon-v5-slorber@^1.0.1:
dependencies: dependencies:
"@babel/runtime" "^7.10.3" "@babel/runtime" "^7.10.3"
react-loadable@*, "react-loadable@npm:@docusaurus/react-loadable@6.0.0": "react-loadable@npm:@docusaurus/react-loadable@6.0.0":
version "6.0.0" version "6.0.0"
resolved "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz" resolved "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz"
integrity sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ== integrity sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==
@@ -7768,7 +7723,7 @@ react-router-dom@^5.3.4:
tiny-invariant "^1.0.2" tiny-invariant "^1.0.2"
tiny-warning "^1.0.0" tiny-warning "^1.0.0"
react-router@^5.3.4, react-router@>=5, react-router@5.3.4: react-router@5.3.4, react-router@^5.3.4:
version "5.3.4" version "5.3.4"
resolved "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz" resolved "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz"
integrity sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA== integrity sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==
@@ -7783,7 +7738,7 @@ react-router@^5.3.4, react-router@>=5, react-router@5.3.4:
tiny-invariant "^1.0.2" tiny-invariant "^1.0.2"
tiny-warning "^1.0.0" tiny-warning "^1.0.0"
react@*, "react@^16.13.1 || ^17.0.0 || ^18.0.0", "react@^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^18.0.0 || ^19.0.0", react@^19.0.0, "react@>= 16.8.0 < 19.0.0", react@>=15, react@>=16, react@>=16.0.0, react@>=18: react@^19.0.0:
version "19.0.0" version "19.0.0"
resolved "https://registry.npmjs.org/react/-/react-19.0.0.tgz" resolved "https://registry.npmjs.org/react/-/react-19.0.0.tgz"
integrity sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ== integrity sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==
@@ -8145,7 +8100,7 @@ run-parallel@^1.1.9:
dependencies: dependencies:
queue-microtask "^1.2.2" queue-microtask "^1.2.2"
safe-buffer@^5.1.0, safe-buffer@>=5.1.0, safe-buffer@~5.2.0, safe-buffer@5.2.1: safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0:
version "5.2.1" version "5.2.1"
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
@@ -8170,16 +8125,16 @@ scheduler@^0.25.0:
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz" resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz"
integrity sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA== integrity sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==
schema-utils@^3.0.0: schema-utils@2.7.0:
version "3.3.0" version "2.7.0"
resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz"
integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==
dependencies: dependencies:
"@types/json-schema" "^7.0.8" "@types/json-schema" "^7.0.4"
ajv "^6.12.5" ajv "^6.12.2"
ajv-keywords "^3.5.2" ajv-keywords "^3.4.1"
schema-utils@^3.2.0: schema-utils@^3.0.0, schema-utils@^3.2.0:
version "3.3.0" version "3.3.0"
resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz"
integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==
@@ -8198,20 +8153,6 @@ schema-utils@^4.0.0, schema-utils@^4.0.1, schema-utils@^4.3.0:
ajv-formats "^2.1.1" ajv-formats "^2.1.1"
ajv-keywords "^5.1.0" ajv-keywords "^5.1.0"
schema-utils@2.7.0:
version "2.7.0"
resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz"
integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==
dependencies:
"@types/json-schema" "^7.0.4"
ajv "^6.12.2"
ajv-keywords "^3.4.1"
"search-insights@>= 1 < 3":
version "2.17.3"
resolved "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz"
integrity sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==
section-matter@^1.0.0: section-matter@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz" resolved "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz"
@@ -8498,7 +8439,7 @@ source-map-support@~0.5.20:
buffer-from "^1.0.0" buffer-from "^1.0.0"
source-map "^0.6.0" source-map "^0.6.0"
source-map@^0.6.0: source-map@^0.6.0, source-map@~0.6.0:
version "0.6.1" version "0.6.1"
resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
@@ -8508,11 +8449,6 @@ source-map@^0.7.0:
resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz" resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz"
integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
source-map@~0.6.0:
version "0.6.1"
resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
space-separated-tokens@^2.0.0: space-separated-tokens@^2.0.0:
version "2.0.2" version "2.0.2"
resolved "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz" resolved "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz"
@@ -8551,35 +8487,21 @@ srcset@^4.0.0:
resolved "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz" resolved "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz"
integrity sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw== integrity sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==
"statuses@>= 1.4.0 < 2":
version "1.5.0"
resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz"
integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==
statuses@2.0.1: statuses@2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz"
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
"statuses@>= 1.4.0 < 2":
version "1.5.0"
resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz"
integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==
std-env@^3.7.0: std-env@^3.7.0:
version "3.8.0" version "3.8.0"
resolved "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz" resolved "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz"
integrity sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w== integrity sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==
string_decoder@^1.1.1:
version "1.3.0"
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz"
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
dependencies:
safe-buffer "~5.2.0"
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
"string-width-cjs@npm:string-width@^4.2.0": "string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3" version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
@@ -8589,16 +8511,7 @@ string_decoder@~1.1.1:
is-fullwidth-code-point "^3.0.0" is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1" strip-ansi "^6.0.1"
string-width@^4.1.0: string-width@^4.1.0, string-width@^4.2.0:
version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^4.2.0:
version "4.2.3" version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -8616,6 +8529,20 @@ string-width@^5.0.1, string-width@^5.1.2:
emoji-regex "^9.2.2" emoji-regex "^9.2.2"
strip-ansi "^7.0.1" strip-ansi "^7.0.1"
string_decoder@^1.1.1:
version "1.3.0"
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz"
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
dependencies:
safe-buffer "~5.2.0"
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
stringify-entities@^4.0.0: stringify-entities@^4.0.0:
version "4.0.4" version "4.0.4"
resolved "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz" resolved "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz"
@@ -8909,7 +8836,7 @@ typedarray-to-buffer@^3.1.5:
dependencies: dependencies:
is-typedarray "^1.0.0" is-typedarray "^1.0.0"
"typescript@>= 2.7", typescript@>=4.9.5, typescript@~5.6.2: typescript@~5.6.2:
version "5.6.3" version "5.6.3"
resolved "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz" resolved "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz"
integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==
@@ -9017,7 +8944,7 @@ universalify@^2.0.0:
resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz" resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz"
integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
unpipe@~1.0.0, unpipe@1.0.0: unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
@@ -9233,7 +9160,7 @@ webpack-sources@^3.2.3:
resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz"
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
"webpack@^4.0.0 || ^5.0.0", "webpack@^4.37.0 || ^5.0.0", webpack@^5.0.0, webpack@^5.1.0, webpack@^5.20.0, webpack@^5.88.1, webpack@^5.95.0, "webpack@>= 4", "webpack@>=4.41.1 || 5.x", webpack@>=5, "webpack@3 || 4 || 5": webpack@^5.88.1, webpack@^5.95.0:
version "5.97.1" version "5.97.1"
resolved "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz" resolved "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz"
integrity sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg== integrity sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==
@@ -9276,7 +9203,7 @@ webpackbar@^6.0.1:
std-env "^3.7.0" std-env "^3.7.0"
wrap-ansi "^7.0.0" wrap-ansi "^7.0.0"
websocket-driver@^0.7.4, websocket-driver@>=0.5.1: websocket-driver@>=0.5.1, websocket-driver@^0.7.4:
version "0.7.4" version "0.7.4"
resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz"
integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==

View File

@@ -4,6 +4,7 @@ import type { View, ViewOptions } from '../../App';
import ExtensionsSection from './extensions/ExtensionsSection'; import ExtensionsSection from './extensions/ExtensionsSection';
import ModelsSection from './models/ModelsSection'; import ModelsSection from './models/ModelsSection';
import { ModeSection } from './mode/ModeSection'; import { ModeSection } from './mode/ModeSection';
import { ToolSelectionStrategySection } from './tool_selection_strategy/ToolSelectionStrategySection';
import SessionSharingSection from './sessions/SessionSharingSection'; import SessionSharingSection from './sessions/SessionSharingSection';
import { ResponseStylesSection } from './response_styles/ResponseStylesSection'; import { ResponseStylesSection } from './response_styles/ResponseStylesSection';
import { ExtensionConfig } from '../../api'; import { ExtensionConfig } from '../../api';
@@ -50,6 +51,8 @@ export default function SettingsView({
<SessionSharingSection /> <SessionSharingSection />
{/* Response Styles */} {/* Response Styles */}
<ResponseStylesSection /> <ResponseStylesSection />
{/* Tool Selection Strategy */}
<ToolSelectionStrategySection setView={setView} />
</div> </div>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,101 @@
import { useEffect, useState, useCallback } from 'react';
import { View, ViewOptions } from '../../../App';
import { useConfig } from '../../ConfigContext';
interface ToolSelectionStrategySectionProps {
setView: (view: View, viewOptions?: ViewOptions) => void;
}
export const all_tool_selection_strategies = [
{
key: 'default',
label: 'Default',
description: 'Loads all tools from enabled extensions',
},
{
key: 'vector',
label: 'Vector',
description:
'Filter tools based on vector-based similarity. Recommended when many extensions are enabled.',
},
];
export const ToolSelectionStrategySection = ({
setView: _setView,
}: ToolSelectionStrategySectionProps) => {
const [currentStrategy, setCurrentStrategy] = useState('default');
const { read, upsert } = useConfig();
const handleStrategyChange = async (newStrategy: string) => {
try {
await upsert('GOOSE_ROUTER_TOOL_SELECTION_STRATEGY', newStrategy, false);
setCurrentStrategy(newStrategy);
} catch (error) {
console.error('Error updating tool selection strategy:', error);
throw new Error(`Failed to store new tool selection strategy: ${newStrategy}`);
}
};
const fetchCurrentStrategy = useCallback(async () => {
try {
const strategy = (await read('GOOSE_ROUTER_TOOL_SELECTION_STRATEGY', false)) as string;
if (strategy) {
setCurrentStrategy(strategy);
}
} catch (error) {
console.error('Error fetching current tool selection strategy:', error);
}
}, [read]);
useEffect(() => {
fetchCurrentStrategy();
}, [fetchCurrentStrategy]);
return (
<section id="tool-selection-strategy" className="px-8">
<div className="flex justify-between items-center mb-2">
<h2 className="text-xl font-medium text-textStandard">Tool Selection Strategy (preview)</h2>
</div>
<div className="border-b border-borderSubtle pb-8">
<p className="text-sm text-textStandard mb-6">
Configure how Goose selects tools for your requests. Available only with Claude models
served on Databricks.
</p>
<div>
{all_tool_selection_strategies.map((strategy) => (
<div className="group hover:cursor-pointer" key={strategy.key}>
<div
className="flex items-center justify-between text-textStandard py-2 px-4 hover:bg-bgSubtle"
onClick={() => handleStrategyChange(strategy.key)}
>
<div className="flex">
<div>
<h3 className="text-textStandard">{strategy.label}</h3>
<p className="text-xs text-textSubtle mt-[2px]">{strategy.description}</p>
</div>
</div>
<div className="relative flex items-center gap-2">
<input
type="radio"
name="tool-selection-strategy"
value={strategy.key}
checked={currentStrategy === strategy.key}
onChange={() => handleStrategyChange(strategy.key)}
className="peer sr-only"
/>
<div
className="h-4 w-4 rounded-full border border-borderStandard
peer-checked:border-[6px] peer-checked:border-black dark:peer-checked:border-white
peer-checked:bg-white dark:peer-checked:bg-black
transition-all duration-200 ease-in-out group-hover:border-borderProminent"
></div>
</div>
</div>
</div>
))}
</div>
</div>
</section>
);
};