mirror of
https://github.com/aljazceru/goose.git
synced 2025-12-18 06:34:26 +01:00
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:
5
.github/workflows/bundle-desktop.yml
vendored
5
.github/workflows/bundle-desktop.yml
vendored
@@ -154,6 +154,11 @@ jobs:
|
||||
# Check disk space after cleanup
|
||||
df -h
|
||||
|
||||
- name: Install protobuf
|
||||
run: |
|
||||
brew install protobuf
|
||||
echo "PROTOC=$(which protoc)" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup Rust
|
||||
uses: dtolnay/rust-toolchain@38b70195107dddab2c7bbd522bcf763bac00963b # pin@stable
|
||||
with:
|
||||
|
||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -57,7 +57,7 @@ jobs:
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
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
|
||||
uses: dtolnay/rust-toolchain@38b70195107dddab2c7bbd522bcf763bac00963b # pin@stable
|
||||
|
||||
2214
Cargo.lock
generated
2214
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -824,6 +824,11 @@ pub fn remove_extension_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?")
|
||||
.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(
|
||||
"tool_permission",
|
||||
"Tool Permission",
|
||||
@@ -850,6 +855,9 @@ pub async fn configure_settings_dialog() -> Result<(), Box<dyn Error>> {
|
||||
"goose_mode" => {
|
||||
configure_goose_mode_dialog()?;
|
||||
}
|
||||
"goose_router_strategy" => {
|
||||
configure_goose_router_strategy_dialog()?;
|
||||
}
|
||||
"tool_permission" => {
|
||||
configure_tool_permissions_dialog().await.and(Ok(()))?;
|
||||
}
|
||||
@@ -921,6 +929,49 @@ pub fn configure_goose_mode_dialog() -> Result<(), Box<dyn Error>> {
|
||||
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>> {
|
||||
let config = Config::global();
|
||||
// Check if GOOSE_CLI_MIN_PRIORITY is set as an environment variable
|
||||
|
||||
@@ -55,7 +55,13 @@ pub async fn build_session(session_config: SessionBuilderConfig) -> Session {
|
||||
// Create the agent
|
||||
let agent: Agent = Agent::new();
|
||||
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
|
||||
if let Some(max_repetitions) = session_config.max_tool_repetitions {
|
||||
|
||||
@@ -268,12 +268,16 @@ async fn remove_extension(
|
||||
.get_agent()
|
||||
.await
|
||||
.map_err(|_| StatusCode::PRECONDITION_FAILED)?;
|
||||
agent.remove_extension(&name).await;
|
||||
|
||||
Ok(Json(ExtensionResponse {
|
||||
match agent.remove_extension(&name).await {
|
||||
Ok(_) => Ok(Json(ExtensionResponse {
|
||||
error: false,
|
||||
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.
|
||||
|
||||
@@ -76,6 +76,10 @@ blake3 = "1.5"
|
||||
fs2 = "0.4.3"
|
||||
futures-util = "0.3.31"
|
||||
|
||||
# Vector database for tool selection
|
||||
lancedb = "0.13"
|
||||
arrow = "52.2"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = { version = "0.3", features = ["wincred"] }
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ use serde_json::Value;
|
||||
use tokio::sync::{mpsc, Mutex};
|
||||
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::platform_tools::{
|
||||
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,
|
||||
};
|
||||
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::{FrontendTool, ToolResultReceiver};
|
||||
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_rx: ToolResultReceiver,
|
||||
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 {
|
||||
@@ -60,16 +62,6 @@ impl Agent {
|
||||
let (confirm_tx, confirm_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 {
|
||||
provider: Mutex::new(None),
|
||||
extension_manager: Mutex::new(ExtensionManager::new()),
|
||||
@@ -81,9 +73,7 @@ impl Agent {
|
||||
tool_result_tx: tool_tx,
|
||||
tool_result_rx: Arc::new(Mutex::new(tool_rx)),
|
||||
tool_monitor: Mutex::new(None),
|
||||
router_tool_selector: Mutex::new(Some(create_tool_selector(
|
||||
router_tool_selection_strategy,
|
||||
))),
|
||||
router_tool_selector: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,8 +194,8 @@ impl Agent {
|
||||
"Frontend tool execution required".to_string(),
|
||||
))
|
||||
} else if tool_call.name == ROUTER_VECTOR_SEARCH_TOOL_NAME {
|
||||
let router_tool_selector = self.router_tool_selector.lock().await;
|
||||
if let Some(selector) = router_tool_selector.as_ref() {
|
||||
let selector = self.router_tool_selector.lock().await.clone();
|
||||
if let Some(selector) = selector {
|
||||
selector.select_tools(tool_call.arguments.clone()).await
|
||||
} else {
|
||||
Err(ToolError::ExecutionError(
|
||||
@@ -284,6 +274,33 @@ impl Agent {
|
||||
})
|
||||
.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)
|
||||
}
|
||||
|
||||
@@ -317,10 +334,32 @@ impl Agent {
|
||||
}
|
||||
_ => {
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -350,8 +389,6 @@ impl Agent {
|
||||
&self,
|
||||
strategy: Option<RouterToolSelectionStrategy>,
|
||||
) -> Vec<Tool> {
|
||||
let extension_manager = self.extension_manager.lock().await;
|
||||
|
||||
let mut prefixed_tools = vec![];
|
||||
match strategy {
|
||||
Some(RouterToolSelectionStrategy::Vector) => {
|
||||
@@ -359,22 +396,50 @@ impl Agent {
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
prefixed_tools.push(platform_tools::search_available_extensions_tool());
|
||||
prefixed_tools.push(platform_tools::manage_extensions_tool());
|
||||
|
||||
if extension_manager.supports_resources() {
|
||||
prefixed_tools.push(platform_tools::read_resource_tool());
|
||||
prefixed_tools.push(platform_tools::list_resources_tool());
|
||||
// Get recent tool calls from router tool selector if available
|
||||
let selector = self.router_tool_selector.lock().await.clone();
|
||||
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
|
||||
}
|
||||
|
||||
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;
|
||||
extension_manager
|
||||
.remove_extension(name)
|
||||
.await
|
||||
.expect("Failed to remove extension");
|
||||
extension_manager.remove_extension(name).await?;
|
||||
|
||||
// If vector tool selection is enabled, remove tools from the index
|
||||
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> {
|
||||
@@ -447,6 +512,26 @@ impl Agent {
|
||||
filtered_response) =
|
||||
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 filtered_response.clone();
|
||||
@@ -598,7 +683,35 @@ impl Agent {
|
||||
|
||||
/// Update the provider used by this agent
|
||||
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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ mod reply_parts;
|
||||
mod router_tool_selector;
|
||||
mod router_tools;
|
||||
mod tool_execution;
|
||||
mod tool_router_index_manager;
|
||||
pub(crate) mod tool_vectordb;
|
||||
mod types;
|
||||
|
||||
pub use agent::Agent;
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::agents::router_tool_selector::RouterToolSelectionStrategy;
|
||||
use crate::config::Config;
|
||||
use crate::message::{Message, MessageContent, ToolRequest};
|
||||
use crate::providers::base::{Provider, ProviderUsage};
|
||||
use crate::providers::errors::ProviderError;
|
||||
@@ -19,16 +20,17 @@ impl Agent {
|
||||
pub(crate) async fn prepare_tools_and_prompt(
|
||||
&self,
|
||||
) -> anyhow::Result<(Vec<Tool>, Vec<Tool>, String)> {
|
||||
// Get tool selection strategy
|
||||
let 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
|
||||
}
|
||||
});
|
||||
// Get tool selection strategy from config
|
||||
let config = Config::global();
|
||||
let router_tool_selection_strategy = config
|
||||
.get_param("GOOSE_ROUTER_TOOL_SELECTION_STRATEGY")
|
||||
.unwrap_or_else(|_| "default".to_string());
|
||||
|
||||
let tool_selection_strategy = match router_tool_selection_strategy.to_lowercase().as_str() {
|
||||
"vector" => Some(RouterToolSelectionStrategy::Vector),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Get tools from extension manager
|
||||
let mut tools = match tool_selection_strategy {
|
||||
Some(RouterToolSelectionStrategy::Vector) => {
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
use mcp_core::content::TextContent;
|
||||
use mcp_core::tool::Tool;
|
||||
use mcp_core::{Content, ToolError};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use async_trait::async_trait;
|
||||
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 {
|
||||
Vector,
|
||||
}
|
||||
@@ -10,27 +22,194 @@ pub enum RouterToolSelectionStrategy {
|
||||
#[async_trait]
|
||||
pub trait RouterToolSelector: Send + Sync {
|
||||
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]
|
||||
impl RouterToolSelector for VectorToolSelector {
|
||||
async fn select_tools(&self, params: Value) -> Result<Vec<Content>, ToolError> {
|
||||
let query = params.get("query").and_then(|v| v.as_str());
|
||||
println!("query: {:?}", query);
|
||||
let selected_tools = Vec::new();
|
||||
// TODO: placeholder for vector tool selection
|
||||
let query = params
|
||||
.get("query")
|
||||
.and_then(|v| v.as_str())
|
||||
.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)
|
||||
}
|
||||
|
||||
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
|
||||
pub fn create_tool_selector(
|
||||
pub async fn create_tool_selector(
|
||||
strategy: Option<RouterToolSelectionStrategy>,
|
||||
) -> Box<dyn RouterToolSelector> {
|
||||
provider: Arc<dyn Provider>,
|
||||
table_name: String,
|
||||
) -> Result<Box<dyn RouterToolSelector>> {
|
||||
match strategy {
|
||||
Some(RouterToolSelectionStrategy::Vector) => Box::new(VectorToolSelector),
|
||||
_ => Box::new(VectorToolSelector), // Default to VectorToolSelector
|
||||
Some(RouterToolSelectionStrategy::Vector) => {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ pub fn vector_search_tool() -> Tool {
|
||||
"type": "object",
|
||||
"required": ["query"],
|
||||
"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 {
|
||||
|
||||
106
crates/goose/src/agents/tool_router_index_manager.rs
Normal file
106
crates/goose/src/agents/tool_router_index_manager.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
440
crates/goose/src/agents/tool_vectordb.rs
Normal file
440
crates/goose/src/agents/tool_vectordb.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
@@ -183,6 +183,18 @@ pub trait Provider: Send + Sync {
|
||||
async fn fetch_supported_models_async(&self) -> Result<Option<Vec<String>>, ProviderError> {
|
||||
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)]
|
||||
|
||||
@@ -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::embedding::EmbeddingCapable;
|
||||
use super::errors::ProviderError;
|
||||
use super::formats::databricks::{create_request, get_usage, response_to_message};
|
||||
use super::oauth;
|
||||
@@ -14,8 +8,16 @@ use crate::config::ConfigError;
|
||||
use crate::message::Message;
|
||||
use crate::model::ModelConfig;
|
||||
use mcp_core::tool::Tool;
|
||||
use serde_json::json;
|
||||
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_REDIRECT_URL: &str = "http://localhost:8020";
|
||||
// "offline_access" scope is used to request an OAuth 2.0 Refresh Token
|
||||
@@ -128,7 +130,6 @@ impl DatabricksProvider {
|
||||
///
|
||||
/// * `host` - The Databricks host URL
|
||||
/// * `token` - The Databricks API token
|
||||
/// * `model` - The model configuration
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
@@ -166,7 +167,17 @@ impl DatabricksProvider {
|
||||
async fn post(&self, payload: Value) -> Result<Value, ProviderError> {
|
||||
let base_url = Url::parse(&self.host)
|
||||
.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| {
|
||||
ProviderError::RequestFailed(format!("Failed to construct endpoint URL: {e}"))
|
||||
})?;
|
||||
@@ -295,4 +306,47 @@ impl Provider for DatabricksProvider {
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
24
crates/goose/src/providers/embedding.rs
Normal file
24
crates/goose/src/providers/embedding.rs
Normal 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>>>;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ pub mod azureauth;
|
||||
pub mod base;
|
||||
pub mod bedrock;
|
||||
pub mod databricks;
|
||||
pub mod embedding;
|
||||
pub mod errors;
|
||||
mod factory;
|
||||
pub mod formats;
|
||||
|
||||
@@ -6,6 +6,7 @@ use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
|
||||
use super::base::{ConfigKey, Provider, ProviderMetadata, ProviderUsage, Usage};
|
||||
use super::embedding::{EmbeddingCapable, EmbeddingRequest, EmbeddingResponse};
|
||||
use super::errors::ProviderError;
|
||||
use super::formats::openai::{create_request, get_usage, response_to_message};
|
||||
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> {
|
||||
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 mut request = self
|
||||
.client
|
||||
.post(url)
|
||||
.header("Authorization", format!("Bearer {}", self.api_key));
|
||||
|
||||
/// Helper function to add OpenAI-specific headers to a request
|
||||
fn add_headers(&self, mut request: reqwest::RequestBuilder) -> reqwest::RequestBuilder {
|
||||
// Add organization header if present
|
||||
if let Some(org) = &self.organization {
|
||||
request = request.header("OpenAI-Organization", org);
|
||||
@@ -102,12 +93,30 @@ impl OpenAiProvider {
|
||||
request = request.header("OpenAI-Project", project);
|
||||
}
|
||||
|
||||
// Add custom headers if present
|
||||
if let Some(custom_headers) = &self.custom_headers {
|
||||
for (key, value) in custom_headers {
|
||||
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?;
|
||||
|
||||
handle_response_openai_compat(response).await
|
||||
@@ -209,6 +218,16 @@ impl Provider for OpenAiProvider {
|
||||
models.sort();
|
||||
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> {
|
||||
@@ -221,3 +240,57 @@ fn parse_custom_headers(s: String) -> HashMap<String, String> {
|
||||
})
|
||||
.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())
|
||||
}
|
||||
}
|
||||
|
||||
86
documentation/docs/guides/tool-router.md
Normal file
86
documentation/docs/guides/tool-router.md
Normal 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
|
||||
@@ -84,7 +84,7 @@
|
||||
"@algolia/requester-fetch" "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"
|
||||
resolved "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.20.0.tgz"
|
||||
integrity sha512-KL1zWTzrlN4MSiaK1ea560iCA/UewMbS4ZsLQRPoDTWyrbDKVbztkPwwv764LAqgXk0fvkNZvJ3IelcK7DqhjQ==
|
||||
@@ -177,7 +177,7 @@
|
||||
resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz"
|
||||
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"
|
||||
resolved "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz"
|
||||
integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==
|
||||
@@ -1598,7 +1598,7 @@
|
||||
utility-types "^3.10.0"
|
||||
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"
|
||||
resolved "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.7.0.tgz"
|
||||
integrity sha512-GXg5V7kC9FZE4FkUZA8oo/NrlRb06UwuICzI6tcbzj0+TVgjq/mpUXXzSgKzMS82YByi4dY2Q808njcBCyy6tQ==
|
||||
@@ -2013,7 +2013,7 @@
|
||||
"@nodelib/fs.stat" "2.0.5"
|
||||
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"
|
||||
resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz"
|
||||
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
|
||||
@@ -2152,7 +2152,7 @@
|
||||
"@svgr/babel-plugin-transform-react-native-svg" "8.1.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"
|
||||
resolved "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz"
|
||||
integrity sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==
|
||||
@@ -2473,7 +2473,7 @@
|
||||
"@types/history" "^4.7.11"
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*", "@types/react@>= 16.8.0 < 19.0.0", "@types/react@>=16", "@types/react@>=18":
|
||||
"@types/react@*":
|
||||
version "18.3.18"
|
||||
resolved "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz"
|
||||
integrity sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==
|
||||
@@ -2558,7 +2558,7 @@
|
||||
resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz"
|
||||
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"
|
||||
resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz"
|
||||
integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==
|
||||
@@ -2659,7 +2659,7 @@
|
||||
"@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":
|
||||
version "1.14.1"
|
||||
resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz"
|
||||
integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==
|
||||
@@ -2709,7 +2709,7 @@ acorn-walk@^8.0.0:
|
||||
dependencies:
|
||||
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"
|
||||
resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz"
|
||||
integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
|
||||
@@ -2734,12 +2734,7 @@ ajv-formats@^2.1.1:
|
||||
dependencies:
|
||||
ajv "^8.0.0"
|
||||
|
||||
ajv-keywords@^3.4.1:
|
||||
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:
|
||||
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==
|
||||
@@ -2751,7 +2746,7 @@ ajv-keywords@^5.1.0:
|
||||
dependencies:
|
||||
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"
|
||||
resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz"
|
||||
integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==
|
||||
@@ -2778,7 +2773,7 @@ algoliasearch-helper@^3.22.6:
|
||||
dependencies:
|
||||
"@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"
|
||||
resolved "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.20.0.tgz"
|
||||
integrity sha512-groO71Fvi5SWpxjI9Ia+chy0QBwT61mg6yxJV27f5YFf+Mw+STT75K6SHySpP8Co5LsCrtsbCH5dJZSRtkSKaQ==
|
||||
@@ -3045,7 +3040,7 @@ braces@^3.0.3, braces@~3.0.2:
|
||||
dependencies:
|
||||
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"
|
||||
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz"
|
||||
integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==
|
||||
@@ -3708,27 +3703,20 @@ debounce@^1.2.1:
|
||||
resolved "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz"
|
||||
integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==
|
||||
|
||||
debug@^2.6.0:
|
||||
debug@2.6.9, debug@^2.6.0:
|
||||
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"
|
||||
|
||||
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"
|
||||
resolved "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz"
|
||||
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
|
||||
dependencies:
|
||||
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:
|
||||
version "1.0.2"
|
||||
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"
|
||||
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:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz"
|
||||
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:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz"
|
||||
@@ -4366,7 +4354,7 @@ figures@^3.2.0:
|
||||
dependencies:
|
||||
escape-string-regexp "^1.0.5"
|
||||
|
||||
file-loader@*, file-loader@^6.2.0:
|
||||
file-loader@^6.2.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz"
|
||||
integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==
|
||||
@@ -4591,14 +4579,7 @@ glob-parent@^5.1.2, glob-parent@~5.1.2:
|
||||
dependencies:
|
||||
is-glob "^4.0.1"
|
||||
|
||||
glob-parent@^6.0.1:
|
||||
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:
|
||||
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==
|
||||
@@ -4707,16 +4688,16 @@ got@^12.1.0:
|
||||
p-cancelable "^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:
|
||||
version "4.2.10"
|
||||
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz"
|
||||
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:
|
||||
version "4.0.3"
|
||||
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"
|
||||
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:
|
||||
version "2.0.0"
|
||||
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"
|
||||
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:
|
||||
version "0.5.9"
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
|
||||
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"
|
||||
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:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz"
|
||||
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:
|
||||
version "0.2.4"
|
||||
resolved "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz"
|
||||
@@ -5169,16 +5150,16 @@ invariant@^2.2.4:
|
||||
dependencies:
|
||||
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:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz"
|
||||
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:
|
||||
version "2.0.1"
|
||||
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"
|
||||
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:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
|
||||
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:
|
||||
version "2.0.0"
|
||||
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"
|
||||
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":
|
||||
version "1.53.0"
|
||||
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"
|
||||
integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==
|
||||
|
||||
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-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:
|
||||
mime-types@2.1.18, mime-types@~2.1.17:
|
||||
version "2.1.18"
|
||||
resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz"
|
||||
integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==
|
||||
dependencies:
|
||||
mime-db "~1.33.0"
|
||||
|
||||
mime-types@~2.1.24:
|
||||
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:
|
||||
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==
|
||||
@@ -6422,7 +6382,7 @@ minimalistic-assert@^1.0.0:
|
||||
resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz"
|
||||
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"
|
||||
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz"
|
||||
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"
|
||||
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:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
|
||||
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:
|
||||
version "7.2.5"
|
||||
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"
|
||||
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:
|
||||
version "0.6.3"
|
||||
resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz"
|
||||
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:
|
||||
version "2.6.2"
|
||||
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"
|
||||
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:
|
||||
version "0.1.12"
|
||||
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"
|
||||
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:
|
||||
version "4.0.0"
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz"
|
||||
integrity sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==
|
||||
@@ -7614,21 +7574,16 @@ randombytes@^2.1.0:
|
||||
dependencies:
|
||||
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:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz"
|
||||
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:
|
||||
version "2.5.2"
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz"
|
||||
integrity sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==
|
||||
@@ -7724,7 +7679,7 @@ react-loadable-ssr-addon-v5-slorber@^1.0.1:
|
||||
dependencies:
|
||||
"@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"
|
||||
resolved "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz"
|
||||
integrity sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==
|
||||
@@ -7768,7 +7723,7 @@ react-router-dom@^5.3.4:
|
||||
tiny-invariant "^1.0.2"
|
||||
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"
|
||||
resolved "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz"
|
||||
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-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"
|
||||
resolved "https://registry.npmjs.org/react/-/react-19.0.0.tgz"
|
||||
integrity sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==
|
||||
@@ -8145,7 +8100,7 @@ run-parallel@^1.1.9:
|
||||
dependencies:
|
||||
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"
|
||||
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
|
||||
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"
|
||||
integrity sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==
|
||||
|
||||
schema-utils@^3.0.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz"
|
||||
integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==
|
||||
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.8"
|
||||
ajv "^6.12.5"
|
||||
ajv-keywords "^3.5.2"
|
||||
"@types/json-schema" "^7.0.4"
|
||||
ajv "^6.12.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"
|
||||
resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz"
|
||||
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-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:
|
||||
version "1.0.0"
|
||||
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"
|
||||
source-map "^0.6.0"
|
||||
|
||||
source-map@^0.6.0:
|
||||
source-map@^0.6.0, 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==
|
||||
@@ -8508,11 +8449,6 @@ source-map@^0.7.0:
|
||||
resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz"
|
||||
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:
|
||||
version "2.0.2"
|
||||
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"
|
||||
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:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz"
|
||||
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:
|
||||
version "3.8.0"
|
||||
resolved "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz"
|
||||
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":
|
||||
version "4.2.3"
|
||||
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"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
string-width@^4.1.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:
|
||||
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==
|
||||
@@ -8616,6 +8529,20 @@ string-width@^5.0.1, string-width@^5.1.2:
|
||||
emoji-regex "^9.2.2"
|
||||
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:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz"
|
||||
@@ -8909,7 +8836,7 @@ typedarray-to-buffer@^3.1.5:
|
||||
dependencies:
|
||||
is-typedarray "^1.0.0"
|
||||
|
||||
"typescript@>= 2.7", typescript@>=4.9.5, typescript@~5.6.2:
|
||||
typescript@~5.6.2:
|
||||
version "5.6.3"
|
||||
resolved "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz"
|
||||
integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==
|
||||
@@ -9017,7 +8944,7 @@ universalify@^2.0.0:
|
||||
resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz"
|
||||
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"
|
||||
resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz"
|
||||
integrity sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==
|
||||
@@ -9276,7 +9203,7 @@ webpackbar@^6.0.1:
|
||||
std-env "^3.7.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"
|
||||
resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz"
|
||||
integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { View, ViewOptions } from '../../App';
|
||||
import ExtensionsSection from './extensions/ExtensionsSection';
|
||||
import ModelsSection from './models/ModelsSection';
|
||||
import { ModeSection } from './mode/ModeSection';
|
||||
import { ToolSelectionStrategySection } from './tool_selection_strategy/ToolSelectionStrategySection';
|
||||
import SessionSharingSection from './sessions/SessionSharingSection';
|
||||
import { ResponseStylesSection } from './response_styles/ResponseStylesSection';
|
||||
import { ExtensionConfig } from '../../api';
|
||||
@@ -50,6 +51,8 @@ export default function SettingsView({
|
||||
<SessionSharingSection />
|
||||
{/* Response Styles */}
|
||||
<ResponseStylesSection />
|
||||
{/* Tool Selection Strategy */}
|
||||
<ToolSelectionStrategySection setView={setView} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user