diff --git a/crates/goose-acp/Cargo.toml b/crates/goose-acp/Cargo.toml index 41d63633ca4b..8e8481f493eb 100644 --- a/crates/goose-acp/Cargo.toml +++ b/crates/goose-acp/Cargo.toml @@ -16,8 +16,9 @@ name = "generate-acp-schema" path = "src/bin/generate_acp_schema.rs" [features] -default = ["code-mode"] +default = ["code-mode", "local-inference"] code-mode = ["goose/code-mode"] +local-inference = ["goose/local-inference"] [lints] workspace = true diff --git a/crates/goose-acp/tests/common_tests/mod.rs b/crates/goose-acp/tests/common_tests/mod.rs index a9f537af85a4..44b70f52c561 100644 --- a/crates/goose-acp/tests/common_tests/mod.rs +++ b/crates/goose-acp/tests/common_tests/mod.rs @@ -357,6 +357,7 @@ pub async fn run_prompt_basic() { expected_session_id.assert_matches(&session.session_id().0); } +#[cfg(feature = "code-mode")] pub async fn run_prompt_codemode() { let expected_session_id = ExpectedSessionId::default(); let prompt = diff --git a/crates/goose-acp/tests/provider_test.rs b/crates/goose-acp/tests/provider_test.rs index 5b1da7be4299..d9e1848e30e8 100644 --- a/crates/goose-acp/tests/provider_test.rs +++ b/crates/goose-acp/tests/provider_test.rs @@ -3,12 +3,13 @@ mod common_tests; use common_tests::fixtures::provider::ClientToProviderConnection; use common_tests::fixtures::run_test; +#[cfg(feature = "code-mode")] +use common_tests::run_prompt_codemode; use common_tests::{ run_config_mcp, run_fs_read_text_file_true, run_fs_write_text_file_false, run_fs_write_text_file_true, run_initialize_doesnt_hit_provider, run_load_model, run_load_session_mcp, run_model_list, run_model_set, run_permission_persistence, - run_prompt_basic, run_prompt_codemode, run_prompt_image, run_prompt_image_attachment, - run_prompt_mcp, + run_prompt_basic, run_prompt_image, run_prompt_image_attachment, run_prompt_mcp, }; #[test] @@ -71,6 +72,7 @@ fn test_prompt_basic() { } #[test] +#[cfg(feature = "code-mode")] fn test_prompt_codemode() { run_test(async { run_prompt_codemode::().await }); } diff --git a/crates/goose-acp/tests/server_test.rs b/crates/goose-acp/tests/server_test.rs index 0f65f24fb4ca..627f88ac0210 100644 --- a/crates/goose-acp/tests/server_test.rs +++ b/crates/goose-acp/tests/server_test.rs @@ -1,12 +1,13 @@ mod common_tests; use common_tests::fixtures::run_test; use common_tests::fixtures::server::ClientToAgentConnection; +#[cfg(feature = "code-mode")] +use common_tests::run_prompt_codemode; use common_tests::{ run_config_mcp, run_fs_read_text_file_true, run_fs_write_text_file_false, run_fs_write_text_file_true, run_initialize_doesnt_hit_provider, run_load_model, run_load_session_mcp, run_model_list, run_model_set, run_permission_persistence, - run_prompt_basic, run_prompt_codemode, run_prompt_image, run_prompt_image_attachment, - run_prompt_mcp, + run_prompt_basic, run_prompt_image, run_prompt_image_attachment, run_prompt_mcp, }; #[test] @@ -65,6 +66,7 @@ fn test_prompt_basic() { } #[test] +#[cfg(feature = "code-mode")] fn test_prompt_codemode() { run_test(async { run_prompt_codemode::().await }); } diff --git a/crates/goose-cli/Cargo.toml b/crates/goose-cli/Cargo.toml index d85ec6c4b619..a0d7422edc45 100644 --- a/crates/goose-cli/Cargo.toml +++ b/crates/goose-cli/Cargo.toml @@ -66,9 +66,10 @@ comfy-table = "7.2.2" winapi = { version = "0.3", features = ["wincred"] } [features] -default = ["code-mode"] +default = ["code-mode", "local-inference"] code-mode = ["goose/code-mode", "goose-acp/code-mode"] cuda = ["goose/cuda"] +local-inference = ["goose/local-inference", "goose-acp/local-inference"] # disables the update command disable-update = [] diff --git a/crates/goose-cli/src/cli.rs b/crates/goose-cli/src/cli.rs index b6a5072acb9c..633c96929b5f 100644 --- a/crates/goose-cli/src/cli.rs +++ b/crates/goose-cli/src/cli.rs @@ -858,6 +858,7 @@ enum Command { command: TermCommand, }, /// Manage local inference models + #[cfg(feature = "local-inference")] #[command(about = "Manage local inference models", visible_alias = "lm")] LocalModels { #[command(subcommand)] @@ -885,6 +886,7 @@ enum Command { }, } +#[cfg(feature = "local-inference")] #[derive(Subcommand)] enum LocalModelsCommand { /// Search HuggingFace for GGUF models @@ -1006,6 +1008,7 @@ fn get_command_name(command: &Option) -> &'static str { Some(Command::Update { .. }) => "update", Some(Command::Recipe { .. }) => "recipe", Some(Command::Term { .. }) => "term", + #[cfg(feature = "local-inference")] Some(Command::LocalModels { .. }) => "local-models", Some(Command::Completion { .. }) => "completion", Some(Command::ValidateExtensions { .. }) => "validate-extensions", @@ -1459,6 +1462,7 @@ async fn handle_term_subcommand(command: TermCommand) -> Result<()> { } } +#[cfg(feature = "local-inference")] async fn handle_local_models_command(command: LocalModelsCommand) -> Result<()> { use goose::providers::local_inference::hf_models; use goose::providers::local_inference::local_model_registry::{ @@ -1744,6 +1748,7 @@ pub async fn cli() -> anyhow::Result<()> { } Some(Command::Recipe { command }) => handle_recipe_subcommand(command), Some(Command::Term { command }) => handle_term_subcommand(command).await, + #[cfg(feature = "local-inference")] Some(Command::LocalModels { command }) => handle_local_models_command(command).await, Some(Command::ValidateExtensions { file }) => { use goose::agents::validate_extensions::validate_bundled_extensions; diff --git a/crates/goose-server/Cargo.toml b/crates/goose-server/Cargo.toml index c8902014b017..804c4c73cb33 100644 --- a/crates/goose-server/Cargo.toml +++ b/crates/goose-server/Cargo.toml @@ -11,9 +11,10 @@ description.workspace = true workspace = true [features] -default = ["code-mode"] +default = ["code-mode", "local-inference"] code-mode = ["goose/code-mode"] cuda = ["goose/cuda"] +local-inference = ["goose/local-inference"] [dependencies] goose = { path = "../goose", default-features = false } diff --git a/crates/goose-server/src/openapi.rs b/crates/goose-server/src/openapi.rs index d5770225e15b..576bf85852bc 100644 --- a/crates/goose-server/src/openapi.rs +++ b/crates/goose-server/src/openapi.rs @@ -380,105 +380,297 @@ derive_utoipa!(ResourceContents as ResourceContentsSchema); derive_utoipa!(JsonObject as JsonObjectSchema); derive_utoipa!(Icon as IconSchema); -#[derive(OpenApi)] -#[openapi( - paths( - super::routes::status::status, - super::routes::status::system_info, - super::routes::status::diagnostics, - super::routes::mcp_ui_proxy::mcp_ui_proxy, - super::routes::config_management::backup_config, - super::routes::config_management::detect_provider, - super::routes::config_management::recover_config, - super::routes::config_management::validate_config, - super::routes::config_management::init_config, - super::routes::config_management::upsert_config, - super::routes::config_management::remove_config, - super::routes::config_management::read_config, - super::routes::config_management::add_extension, - super::routes::config_management::remove_extension, - super::routes::config_management::get_extensions, - super::routes::config_management::read_all_config, - super::routes::config_management::providers, - super::routes::config_management::get_provider_models, - super::routes::config_management::get_slash_commands, - super::routes::config_management::upsert_permissions, - super::routes::config_management::create_custom_provider, - super::routes::config_management::get_custom_provider, - super::routes::config_management::update_custom_provider, - super::routes::config_management::remove_custom_provider, - super::routes::config_management::get_provider_catalog, - super::routes::config_management::get_provider_catalog_template, - super::routes::config_management::check_provider, - super::routes::config_management::set_config_provider, - super::routes::config_management::configure_provider_oauth, - super::routes::config_management::get_canonical_model_info, - super::routes::prompts::get_prompts, - super::routes::prompts::get_prompt, - super::routes::prompts::save_prompt, - super::routes::prompts::reset_prompt, - super::routes::agent::start_agent, - super::routes::agent::resume_agent, - super::routes::agent::stop_agent, - super::routes::agent::restart_agent, - super::routes::agent::update_working_dir, - super::routes::agent::get_tools, - super::routes::agent::read_resource, - super::routes::agent::call_tool, - super::routes::agent::list_apps, - super::routes::agent::export_app, - super::routes::agent::import_app, - super::routes::agent::update_from_session, - super::routes::agent::agent_add_extension, - super::routes::agent::agent_remove_extension, - super::routes::agent::update_agent_provider, - super::routes::action_required::confirm_tool_action, - super::routes::reply::reply, - super::routes::session::list_sessions, - super::routes::session::search_sessions, - super::routes::session::get_session, - super::routes::session::get_session_insights, - super::routes::session::update_session_name, - super::routes::session::delete_session, - super::routes::session::export_session, - super::routes::session::import_session, - super::routes::session::update_session_user_recipe_values, - super::routes::session::fork_session, - super::routes::session::get_session_extensions, - super::routes::schedule::create_schedule, - super::routes::schedule::list_schedules, - super::routes::schedule::delete_schedule, - super::routes::schedule::update_schedule, - super::routes::schedule::run_now_handler, - super::routes::schedule::pause_schedule, - super::routes::schedule::unpause_schedule, - super::routes::schedule::kill_running_job, - super::routes::schedule::inspect_running_job, - super::routes::schedule::sessions_handler, - super::routes::recipe::create_recipe, - super::routes::recipe::encode_recipe, - super::routes::recipe::decode_recipe, - super::routes::recipe::scan_recipe, - super::routes::recipe::list_recipes, - super::routes::recipe::delete_recipe, - super::routes::recipe::schedule_recipe, - super::routes::recipe::set_recipe_slash_command, - super::routes::recipe::save_recipe, - super::routes::recipe::parse_recipe, - super::routes::recipe::recipe_to_yaml, - super::routes::setup::start_openrouter_setup, - super::routes::setup::start_tetrate_setup, - super::routes::tunnel::start_tunnel, - super::routes::tunnel::stop_tunnel, - super::routes::tunnel::get_tunnel_status, - super::routes::telemetry::send_telemetry_event, - super::routes::dictation::transcribe_dictation, - super::routes::dictation::get_dictation_config, - super::routes::dictation::list_models, - super::routes::dictation::download_model, - super::routes::dictation::get_download_progress, - super::routes::dictation::cancel_download, - super::routes::dictation::delete_model, +macro_rules! define_api_doc { + ( + extra_paths: [$($extra_path:path),* $(,)?], + extra_schemas: [$($extra_schema:path),* $(,)?] + ) => { + #[derive(OpenApi)] + #[openapi( + paths( + super::routes::status::status, + super::routes::status::system_info, + super::routes::status::diagnostics, + super::routes::mcp_ui_proxy::mcp_ui_proxy, + super::routes::config_management::backup_config, + super::routes::config_management::detect_provider, + super::routes::config_management::recover_config, + super::routes::config_management::validate_config, + super::routes::config_management::init_config, + super::routes::config_management::upsert_config, + super::routes::config_management::remove_config, + super::routes::config_management::read_config, + super::routes::config_management::add_extension, + super::routes::config_management::remove_extension, + super::routes::config_management::get_extensions, + super::routes::config_management::read_all_config, + super::routes::config_management::providers, + super::routes::config_management::get_provider_models, + super::routes::config_management::get_slash_commands, + super::routes::config_management::upsert_permissions, + super::routes::config_management::create_custom_provider, + super::routes::config_management::get_custom_provider, + super::routes::config_management::update_custom_provider, + super::routes::config_management::remove_custom_provider, + super::routes::config_management::get_provider_catalog, + super::routes::config_management::get_provider_catalog_template, + super::routes::config_management::check_provider, + super::routes::config_management::set_config_provider, + super::routes::config_management::configure_provider_oauth, + super::routes::config_management::get_canonical_model_info, + super::routes::prompts::get_prompts, + super::routes::prompts::get_prompt, + super::routes::prompts::save_prompt, + super::routes::prompts::reset_prompt, + super::routes::agent::start_agent, + super::routes::agent::resume_agent, + super::routes::agent::stop_agent, + super::routes::agent::restart_agent, + super::routes::agent::update_working_dir, + super::routes::agent::get_tools, + super::routes::agent::read_resource, + super::routes::agent::call_tool, + super::routes::agent::list_apps, + super::routes::agent::export_app, + super::routes::agent::import_app, + super::routes::agent::update_from_session, + super::routes::agent::agent_add_extension, + super::routes::agent::agent_remove_extension, + super::routes::agent::update_agent_provider, + super::routes::action_required::confirm_tool_action, + super::routes::reply::reply, + super::routes::session::list_sessions, + super::routes::session::search_sessions, + super::routes::session::get_session, + super::routes::session::get_session_insights, + super::routes::session::update_session_name, + super::routes::session::delete_session, + super::routes::session::export_session, + super::routes::session::import_session, + super::routes::session::update_session_user_recipe_values, + super::routes::session::fork_session, + super::routes::session::get_session_extensions, + super::routes::schedule::create_schedule, + super::routes::schedule::list_schedules, + super::routes::schedule::delete_schedule, + super::routes::schedule::update_schedule, + super::routes::schedule::run_now_handler, + super::routes::schedule::pause_schedule, + super::routes::schedule::unpause_schedule, + super::routes::schedule::kill_running_job, + super::routes::schedule::inspect_running_job, + super::routes::schedule::sessions_handler, + super::routes::recipe::create_recipe, + super::routes::recipe::encode_recipe, + super::routes::recipe::decode_recipe, + super::routes::recipe::scan_recipe, + super::routes::recipe::list_recipes, + super::routes::recipe::delete_recipe, + super::routes::recipe::schedule_recipe, + super::routes::recipe::set_recipe_slash_command, + super::routes::recipe::save_recipe, + super::routes::recipe::parse_recipe, + super::routes::recipe::recipe_to_yaml, + super::routes::setup::start_openrouter_setup, + super::routes::setup::start_tetrate_setup, + super::routes::tunnel::start_tunnel, + super::routes::tunnel::stop_tunnel, + super::routes::tunnel::get_tunnel_status, + super::routes::telemetry::send_telemetry_event, + super::routes::dictation::transcribe_dictation, + super::routes::dictation::get_dictation_config, + super::routes::dictation::list_models, + super::routes::dictation::download_model, + super::routes::dictation::get_download_progress, + super::routes::dictation::cancel_download, + super::routes::dictation::delete_model, + $($extra_path,)* + ), + components(schemas( + super::routes::config_management::UpsertConfigQuery, + super::routes::config_management::ConfigKeyQuery, + super::routes::config_management::DetectProviderRequest, + super::routes::config_management::DetectProviderResponse, + super::routes::config_management::ConfigResponse, + super::routes::config_management::ProvidersResponse, + super::routes::config_management::ProviderDetails, + super::routes::config_management::SlashCommandsResponse, + super::routes::config_management::SlashCommand, + super::routes::config_management::CommandType, + super::routes::config_management::ExtensionResponse, + super::routes::config_management::ExtensionQuery, + super::routes::config_management::ToolPermission, + super::routes::config_management::UpsertPermissionsQuery, + super::routes::config_management::UpdateCustomProviderRequest, + goose::providers::catalog::ProviderCatalogEntry, + goose::providers::catalog::ProviderTemplate, + goose::providers::catalog::ModelTemplate, + goose::providers::catalog::ModelCapabilities, + super::routes::config_management::CheckProviderRequest, + super::routes::config_management::SetProviderRequest, + super::routes::config_management::ModelInfoQuery, + super::routes::config_management::ModelInfoResponse, + super::routes::config_management::ModelInfoData, + super::routes::prompts::PromptsListResponse, + super::routes::prompts::PromptContentResponse, + super::routes::prompts::SavePromptRequest, + goose::prompt_template::Template, + super::routes::action_required::ConfirmToolActionRequest, + super::routes::reply::ChatRequest, + super::routes::session::ImportSessionRequest, + super::routes::session::SessionListResponse, + super::routes::session::UpdateSessionNameRequest, + super::routes::session::UpdateSessionUserRecipeValuesRequest, + super::routes::session::UpdateSessionUserRecipeValuesResponse, + super::routes::session::ForkRequest, + super::routes::session::ForkResponse, + super::routes::session::SessionExtensionsResponse, + Message, + MessageContent, + MessageMetadata, + TokenState, + ContentSchema, + EmbeddedResourceSchema, + ImageContentSchema, + AnnotationsSchema, + TextContentSchema, + RawTextContentSchema, + RawImageContentSchema, + RawAudioContentSchema, + RawEmbeddedResourceSchema, + RawResourceSchema, + ToolResponse, + ToolRequest, + ToolConfirmationRequest, + ActionRequired, + ActionRequiredData, + ThinkingContent, + RedactedThinkingContent, + ReasoningContent, + FrontendToolRequest, + ResourceContentsSchema, + SystemNotificationType, + SystemNotificationContent, + MessageEvent, + JsonObjectSchema, + RoleSchema, + ProviderMetadata, + ProviderType, + LoadedProvider, + ProviderEngine, + DeclarativeProviderConfig, + EnvVarConfig, + ExtensionEntry, + ExtensionConfig, + ConfigKey, + Envs, + RecipeManifest, + ToolSchema, + ToolAnnotationsSchema, + ToolExecutionSchema, + TaskSupportSchema, + ToolInfo, + PermissionLevel, + Permission, + PrincipalType, + ModelInfo, + ModelConfig, + Session, + SessionInsights, + SessionType, + SystemInfo, + Conversation, + IconSchema, + goose::session::extension_data::ExtensionData, + super::routes::schedule::CreateScheduleRequest, + super::routes::schedule::UpdateScheduleRequest, + super::routes::schedule::KillJobResponse, + super::routes::schedule::InspectJobResponse, + goose::scheduler::ScheduledJob, + super::routes::schedule::RunNowResponse, + super::routes::schedule::ListSchedulesResponse, + super::routes::schedule::SessionsQuery, + super::routes::schedule::SessionDisplayInfo, + super::routes::recipe::CreateRecipeRequest, + super::routes::recipe::AuthorRequest, + super::routes::recipe::CreateRecipeResponse, + super::routes::recipe::EncodeRecipeRequest, + super::routes::recipe::EncodeRecipeResponse, + super::routes::recipe::DecodeRecipeRequest, + super::routes::recipe::DecodeRecipeResponse, + super::routes::recipe::ScanRecipeRequest, + super::routes::recipe::ScanRecipeResponse, + super::routes::recipe::ListRecipeResponse, + super::routes::recipe::ScheduleRecipeRequest, + super::routes::recipe::SetSlashCommandRequest, + super::routes::recipe::DeleteRecipeRequest, + super::routes::recipe::SaveRecipeRequest, + super::routes::recipe::SaveRecipeResponse, + super::routes::errors::ErrorResponse, + super::routes::recipe::ParseRecipeRequest, + super::routes::recipe::ParseRecipeResponse, + super::routes::recipe::RecipeToYamlRequest, + super::routes::recipe::RecipeToYamlResponse, + goose::recipe::Recipe, + goose::recipe::Author, + goose::recipe::Settings, + goose::recipe::RecipeParameter, + goose::recipe::RecipeParameterInputType, + goose::recipe::RecipeParameterRequirement, + goose::recipe::Response, + goose::recipe::SubRecipe, + goose::agents::types::RetryConfig, + goose::agents::types::SuccessCheck, + super::routes::agent::UpdateProviderRequest, + super::routes::agent::GetToolsQuery, + super::routes::agent::ReadResourceRequest, + super::routes::agent::ReadResourceResponse, + super::routes::agent::CallToolRequest, + super::routes::agent::CallToolResponse, + ContentBlockSchema, + super::routes::agent::ListAppsRequest, + super::routes::agent::ListAppsResponse, + super::routes::agent::ImportAppRequest, + super::routes::agent::ImportAppResponse, + super::routes::agent::StartAgentRequest, + super::routes::agent::ResumeAgentRequest, + super::routes::agent::StopAgentRequest, + super::routes::agent::RestartAgentRequest, + super::routes::agent::UpdateWorkingDirRequest, + super::routes::agent::UpdateFromSessionRequest, + super::routes::agent::AddExtensionRequest, + super::routes::agent::RemoveExtensionRequest, + super::routes::agent::ResumeAgentResponse, + super::routes::agent::RestartAgentResponse, + goose::agents::ExtensionLoadResult, + super::routes::setup::SetupResponse, + super::tunnel::TunnelInfo, + super::tunnel::TunnelState, + super::routes::telemetry::TelemetryEventRequest, + goose::goose_apps::GooseApp, + goose::goose_apps::WindowProps, + goose::goose_apps::McpAppResource, + goose::goose_apps::CspMetadata, + goose::goose_apps::PermissionsMetadata, + goose::goose_apps::UiMetadata, + goose::goose_apps::ResourceMetadata, + super::routes::dictation::TranscribeRequest, + super::routes::dictation::TranscribeResponse, + goose::dictation::providers::DictationProvider, + super::routes::dictation::DictationProviderStatus, + super::routes::dictation::WhisperModelResponse, + DownloadProgress, + DownloadStatus, + $($extra_schema,)* + )) + )] + pub struct ApiDoc; + }; +} + +#[cfg(feature = "local-inference")] +define_api_doc! { + extra_paths: [ super::routes::local_inference::list_local_models, super::routes::local_inference::search_hf_models, super::routes::local_inference::get_repo_files, @@ -488,180 +680,8 @@ derive_utoipa!(Icon as IconSchema); super::routes::local_inference::delete_local_model, super::routes::local_inference::get_model_settings, super::routes::local_inference::update_model_settings, - ), - components(schemas( - super::routes::config_management::UpsertConfigQuery, - super::routes::config_management::ConfigKeyQuery, - super::routes::config_management::DetectProviderRequest, - super::routes::config_management::DetectProviderResponse, - super::routes::config_management::ConfigResponse, - super::routes::config_management::ProvidersResponse, - super::routes::config_management::ProviderDetails, - super::routes::config_management::SlashCommandsResponse, - super::routes::config_management::SlashCommand, - super::routes::config_management::CommandType, - super::routes::config_management::ExtensionResponse, - super::routes::config_management::ExtensionQuery, - super::routes::config_management::ToolPermission, - super::routes::config_management::UpsertPermissionsQuery, - super::routes::config_management::UpdateCustomProviderRequest, - goose::providers::catalog::ProviderCatalogEntry, - goose::providers::catalog::ProviderTemplate, - goose::providers::catalog::ModelTemplate, - goose::providers::catalog::ModelCapabilities, - super::routes::config_management::CheckProviderRequest, - super::routes::config_management::SetProviderRequest, - super::routes::config_management::ModelInfoQuery, - super::routes::config_management::ModelInfoResponse, - super::routes::config_management::ModelInfoData, - super::routes::prompts::PromptsListResponse, - super::routes::prompts::PromptContentResponse, - super::routes::prompts::SavePromptRequest, - goose::prompt_template::Template, - super::routes::action_required::ConfirmToolActionRequest, - super::routes::reply::ChatRequest, - super::routes::session::ImportSessionRequest, - super::routes::session::SessionListResponse, - super::routes::session::UpdateSessionNameRequest, - super::routes::session::UpdateSessionUserRecipeValuesRequest, - super::routes::session::UpdateSessionUserRecipeValuesResponse, - super::routes::session::ForkRequest, - super::routes::session::ForkResponse, - super::routes::session::SessionExtensionsResponse, - Message, - MessageContent, - MessageMetadata, - TokenState, - ContentSchema, - EmbeddedResourceSchema, - ImageContentSchema, - AnnotationsSchema, - TextContentSchema, - RawTextContentSchema, - RawImageContentSchema, - RawAudioContentSchema, - RawEmbeddedResourceSchema, - RawResourceSchema, - ToolResponse, - ToolRequest, - ToolConfirmationRequest, - ActionRequired, - ActionRequiredData, - ThinkingContent, - RedactedThinkingContent, - ReasoningContent, - FrontendToolRequest, - ResourceContentsSchema, - SystemNotificationType, - SystemNotificationContent, - MessageEvent, - JsonObjectSchema, - RoleSchema, - ProviderMetadata, - ProviderType, - LoadedProvider, - ProviderEngine, - DeclarativeProviderConfig, - EnvVarConfig, - ExtensionEntry, - ExtensionConfig, - ConfigKey, - Envs, - RecipeManifest, - ToolSchema, - ToolAnnotationsSchema, - ToolExecutionSchema, - TaskSupportSchema, - ToolInfo, - PermissionLevel, - Permission, - PrincipalType, - ModelInfo, - ModelConfig, - Session, - SessionInsights, - SessionType, - SystemInfo, - Conversation, - IconSchema, - goose::session::extension_data::ExtensionData, - super::routes::schedule::CreateScheduleRequest, - super::routes::schedule::UpdateScheduleRequest, - super::routes::schedule::KillJobResponse, - super::routes::schedule::InspectJobResponse, - goose::scheduler::ScheduledJob, - super::routes::schedule::RunNowResponse, - super::routes::schedule::ListSchedulesResponse, - super::routes::schedule::SessionsQuery, - super::routes::schedule::SessionDisplayInfo, - super::routes::recipe::CreateRecipeRequest, - super::routes::recipe::AuthorRequest, - super::routes::recipe::CreateRecipeResponse, - super::routes::recipe::EncodeRecipeRequest, - super::routes::recipe::EncodeRecipeResponse, - super::routes::recipe::DecodeRecipeRequest, - super::routes::recipe::DecodeRecipeResponse, - super::routes::recipe::ScanRecipeRequest, - super::routes::recipe::ScanRecipeResponse, - super::routes::recipe::ListRecipeResponse, - super::routes::recipe::ScheduleRecipeRequest, - super::routes::recipe::SetSlashCommandRequest, - super::routes::recipe::DeleteRecipeRequest, - super::routes::recipe::SaveRecipeRequest, - super::routes::recipe::SaveRecipeResponse, - super::routes::errors::ErrorResponse, - super::routes::recipe::ParseRecipeRequest, - super::routes::recipe::ParseRecipeResponse, - super::routes::recipe::RecipeToYamlRequest, - super::routes::recipe::RecipeToYamlResponse, - goose::recipe::Recipe, - goose::recipe::Author, - goose::recipe::Settings, - goose::recipe::RecipeParameter, - goose::recipe::RecipeParameterInputType, - goose::recipe::RecipeParameterRequirement, - goose::recipe::Response, - goose::recipe::SubRecipe, - goose::agents::types::RetryConfig, - goose::agents::types::SuccessCheck, - super::routes::agent::UpdateProviderRequest, - super::routes::agent::GetToolsQuery, - super::routes::agent::ReadResourceRequest, - super::routes::agent::ReadResourceResponse, - super::routes::agent::CallToolRequest, - super::routes::agent::CallToolResponse, - ContentBlockSchema, - super::routes::agent::ListAppsRequest, - super::routes::agent::ListAppsResponse, - super::routes::agent::ImportAppRequest, - super::routes::agent::ImportAppResponse, - super::routes::agent::StartAgentRequest, - super::routes::agent::ResumeAgentRequest, - super::routes::agent::StopAgentRequest, - super::routes::agent::RestartAgentRequest, - super::routes::agent::UpdateWorkingDirRequest, - super::routes::agent::UpdateFromSessionRequest, - super::routes::agent::AddExtensionRequest, - super::routes::agent::RemoveExtensionRequest, - super::routes::agent::ResumeAgentResponse, - super::routes::agent::RestartAgentResponse, - goose::agents::ExtensionLoadResult, - super::routes::setup::SetupResponse, - super::tunnel::TunnelInfo, - super::tunnel::TunnelState, - super::routes::telemetry::TelemetryEventRequest, - goose::goose_apps::GooseApp, - goose::goose_apps::WindowProps, - goose::goose_apps::McpAppResource, - goose::goose_apps::CspMetadata, - goose::goose_apps::PermissionsMetadata, - goose::goose_apps::UiMetadata, - goose::goose_apps::ResourceMetadata, - super::routes::dictation::TranscribeRequest, - super::routes::dictation::TranscribeResponse, - goose::dictation::providers::DictationProvider, - super::routes::dictation::DictationProviderStatus, - super::routes::dictation::WhisperModelResponse, + ], + extra_schemas: [ super::routes::local_inference::LocalModelResponse, super::routes::local_inference::ModelDownloadStatus, super::routes::local_inference::DownloadModelRequest, @@ -671,11 +691,14 @@ derive_utoipa!(Icon as IconSchema); super::routes::local_inference::RepoVariantsResponse, goose::providers::local_inference::local_model_registry::ModelSettings, goose::providers::local_inference::local_model_registry::SamplingConfig, - DownloadProgress, - DownloadStatus, - )) -)] -pub struct ApiDoc; + ] +} + +#[cfg(not(feature = "local-inference"))] +define_api_doc! { + extra_paths: [], + extra_schemas: [] +} #[allow(dead_code)] // Used by generate_schema binary pub fn generate_schema() -> String { diff --git a/crates/goose-server/src/routes/mod.rs b/crates/goose-server/src/routes/mod.rs index 22d4e3e6af31..672aa46aff98 100644 --- a/crates/goose-server/src/routes/mod.rs +++ b/crates/goose-server/src/routes/mod.rs @@ -4,6 +4,7 @@ pub mod config_management; pub mod dictation; pub mod errors; pub mod gateway; +#[cfg(feature = "local-inference")] pub mod local_inference; pub mod mcp_app_proxy; pub mod mcp_ui_proxy; @@ -26,13 +27,12 @@ use axum::Router; // Function to configure all routes pub fn configure(state: Arc, secret_key: String) -> Router { - Router::new() + let router = Router::new() .merge(status::routes(state.clone())) .merge(reply::routes(state.clone())) .merge(action_required::routes(state.clone())) .merge(agent::routes(state.clone())) .merge(dictation::routes(state.clone())) - .merge(local_inference::routes(state.clone())) .merge(config_management::routes(state.clone())) .merge(prompts::routes()) .merge(recipe::routes(state.clone())) @@ -43,6 +43,10 @@ pub fn configure(state: Arc, secret_key: String) -> Rout .merge(tunnel::routes(state.clone())) .merge(gateway::routes(state.clone())) .merge(mcp_ui_proxy::routes(secret_key.clone())) - .merge(mcp_app_proxy::routes(secret_key)) - .merge(sampling::routes(state)) + .merge(mcp_app_proxy::routes(secret_key)); + + #[cfg(feature = "local-inference")] + let router = router.merge(local_inference::routes(state.clone())); + + router.merge(sampling::routes(state)) } diff --git a/crates/goose-server/src/state.rs b/crates/goose-server/src/state.rs index da00db4a56eb..4c6a770418f2 100644 --- a/crates/goose-server/src/state.rs +++ b/crates/goose-server/src/state.rs @@ -12,6 +12,7 @@ use tokio::task::JoinHandle; use crate::tunnel::TunnelManager; use goose::agents::ExtensionLoadResult; use goose::gateway::manager::GatewayManager; +#[cfg(feature = "local-inference")] use goose::providers::local_inference::InferenceRuntime; type ExtensionLoadingTasks = @@ -25,6 +26,7 @@ pub struct AppState { pub tunnel_manager: Arc, pub gateway_manager: Arc, pub extension_loading_tasks: ExtensionLoadingTasks, + #[cfg(feature = "local-inference")] pub inference_runtime: Arc, } @@ -43,6 +45,7 @@ impl AppState { tunnel_manager, gateway_manager, extension_loading_tasks: Arc::new(Mutex::new(HashMap::new())), + #[cfg(feature = "local-inference")] inference_runtime: InferenceRuntime::get_or_init(), })) } diff --git a/crates/goose/Cargo.toml b/crates/goose/Cargo.toml index 68b49a2ad049..2696fc717eba 100644 --- a/crates/goose/Cargo.toml +++ b/crates/goose/Cargo.toml @@ -8,9 +8,10 @@ repository.workspace = true description.workspace = true [features] -default = ["code-mode"] +default = ["code-mode", "local-inference"] code-mode = ["dep:pctx_code_mode"] -cuda = ["candle-core/cuda", "candle-nn/cuda", "llama-cpp-2/cuda"] +cuda = ["candle-core/cuda", "candle-nn/cuda", "llama-cpp-2?/cuda"] +local-inference = ["dep:llama-cpp-2"] [lints] workspace = true @@ -138,7 +139,7 @@ tree-sitter-typescript = { workspace = true } which = { workspace = true } pctx_code_mode = { version = "^0.2.3", optional = true } pulldown-cmark = "0.13.0" -llama-cpp-2 = { version = "0.1.137", features = ["sampler"] } +llama-cpp-2 = { version = "0.1.137", features = ["sampler"], optional = true } encoding_rs = "0.8.35" pastey = "0.2.1" shell-words = "1.1.1" @@ -150,7 +151,7 @@ winapi = { version = "0.3", features = ["wincred"] } [target.'cfg(target_os = "macos")'.dependencies] candle-core = { version = "0.9", default-features = false, features = ["metal"] } candle-nn = { version = "0.9", default-features = false, features = ["metal"] } -llama-cpp-2 = { version = "0.1.137", features = ["sampler", "metal"] } +llama-cpp-2 = { version = "0.1.137", features = ["sampler", "metal"], optional = true } [dev-dependencies] serial_test = { workspace = true } diff --git a/crates/goose/src/agents/prompt_manager.rs b/crates/goose/src/agents/prompt_manager.rs index 4049056a4df8..8d369435bf82 100644 --- a/crates/goose/src/agents/prompt_manager.rs +++ b/crates/goose/src/agents/prompt_manager.rs @@ -445,6 +445,9 @@ mod tests { .with_extensions(extensions.into_iter()) .build(); + #[cfg(feature = "code-mode")] assert_snapshot!(system_prompt); + #[cfg(not(feature = "code-mode"))] + assert_snapshot!("all_platform_extensions_no_code_mode", system_prompt); } } diff --git a/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__all_platform_extensions_no_code_mode.snap b/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__all_platform_extensions_no_code_mode.snap new file mode 100644 index 000000000000..c5cb84d04651 --- /dev/null +++ b/crates/goose/src/agents/snapshots/goose__agents__prompt_manager__tests__all_platform_extensions_no_code_mode.snap @@ -0,0 +1,110 @@ +--- +source: crates/goose/src/agents/prompt_manager.rs +expression: system_prompt +--- +You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal. +goose is being developed as an open-source software project. + +# Extensions + +Extensions provide additional tools and context from different data sources and applications. +You can dynamically enable or disable extensions as needed to help complete tasks. + +Because you dynamically load extensions, your conversation history may refer +to interactions with extensions that are not currently active. The currently +active extensions are below. Each of these extensions provides tools that are +in your tool specification. + + +## Extension Manager + +### Instructions +Extension Management + +Use these tools to discover, enable, and disable extensions, as well as review resources. + +Available tools: +- search_available_extensions: Find extensions available to enable/disable +- manage_extensions: Enable or disable extensions +- list_resources: List resources from extensions +- read_resource: Read specific resources from extensions + +When you lack the tools needed to complete a task, use search_available_extensions first +to discover what extensions can help. + +Use manage_extensions to enable or disable specific extensions by name. +Use list_resources and read_resource to work with extension data and resources. + +## analyze + +### Instructions +Analyze code structure using tree-sitter AST parsing. Three auto-selected modes: +- Directory path → structure overview (file tree with function/class counts) +- File path → semantic details (functions, classes, imports, call counts) +- Any path + focus parameter → symbol call graph (incoming/outgoing chains) + +For large codebases, delegate analysis to a subagent and retain only the summary. + +## apps + +apps supports resources. +### Instructions +Use this extension to create, manage, and iterate on custom HTML/CSS/JavaScript apps. +## chatrecall + +### Instructions +Chat Recall + +Search past conversations and load session summaries when the user expects some memory or context. + +Two modes: +- Search mode: Use query with keywords/synonyms to find relevant messages +- Load mode: Use session_id to get first and last messages of a specific session + +## developer + +### Instructions +Use the developer extension to build software and operate a terminal. + +Make sure to use the tools *efficiently* - reading all the content you need in as few +iterations as possible and then making the requested edits or running commands. You are +responsible for managing your context window, and to minimize unnecessary turns which +cost the user money. + +For editing software, prefer the flow of using tree to understand the codebase structure +and file sizes. When you need to search, prefer rg which correctly respects gitignored +content. Then use cat or sed to gather the context you need, always reading before editing. +Use write and edit to efficiently make changes. Test and verify as appropriate. + +## summon + +### Instructions + + +You have these skills at your disposal, when it is clear they can help you solve a problem or you are asked to use them: +• goose-doc-guide - Reference goose documentation to create, configure, or explain goose-specific features like recipes, extensions, sessions, and providers. You MUST fetch relevant goose docs before answering. You MUST NOT rely on training data or assumptions for any goose-specific fields, values, names, syntax, or commands. +## todo + +### Instructions +Your todo content is automatically available in your context. + +Workflow: +- Start: write initial checklist +- During: update progress +- End: verify all complete + +Template: +- [x] Requirement 1 +- [ ] Task + - [ ] Sub-task +- [ ] Requirement 2 +- [ ] Another task + +## tom + + + + +# Response Guidelines + +Use Markdown formatting for all responses. diff --git a/crates/goose/src/providers/init.rs b/crates/goose/src/providers/init.rs index e9fd9b8879ae..726e516363c3 100644 --- a/crates/goose/src/providers/init.rs +++ b/crates/goose/src/providers/init.rs @@ -19,7 +19,6 @@ use super::{ google::GoogleProvider, lead_worker::LeadWorkerProvider, litellm::LiteLLMProvider, - local_inference::LocalInferenceProvider, ollama::OllamaProvider, openai::OpenAiProvider, openrouter::OpenRouterProvider, @@ -33,6 +32,8 @@ use super::{ use crate::config::ExtensionConfig; use crate::model::ModelConfig; use crate::providers::base::ProviderType; +#[cfg(feature = "local-inference")] +use crate::providers::local_inference::LocalInferenceProvider; use crate::{ config::declarative_providers::register_declarative_providers, providers::provider_registry::ProviderEntry, @@ -52,6 +53,7 @@ async fn init_registry() -> RwLock { registry.register::(false); registry.register::(false); registry.register::(false); + #[cfg(feature = "local-inference")] registry.register::(false); registry.register::(true); registry.register::(false); diff --git a/crates/goose/src/providers/mod.rs b/crates/goose/src/providers/mod.rs index c07bcb0d246a..ee17098b5e90 100644 --- a/crates/goose/src/providers/mod.rs +++ b/crates/goose/src/providers/mod.rs @@ -27,6 +27,7 @@ pub mod google; mod init; pub mod lead_worker; pub mod litellm; +#[cfg(feature = "local-inference")] pub mod local_inference; pub mod oauth; pub mod ollama; diff --git a/ui/desktop/src/components/settings/SettingsView.tsx b/ui/desktop/src/components/settings/SettingsView.tsx index ba533da72f23..b8ad48f7daa7 100644 --- a/ui/desktop/src/components/settings/SettingsView.tsx +++ b/ui/desktop/src/components/settings/SettingsView.tsx @@ -19,6 +19,7 @@ import KeyboardShortcutsSection from './keyboard/KeyboardShortcutsSection'; import LocalInferenceSection from './localInference/LocalInferenceSection'; import { CONFIGURATION_ENABLED } from '../../updates'; import { trackSettingsTabViewed } from '../../utils/analytics'; +import { useLocalInferenceAvailable } from '../../hooks/useLocalInferenceAvailable'; export type SettingsViewOptions = { deepLinkConfig?: ExtensionConfig; @@ -38,6 +39,7 @@ export default function SettingsView({ const [activeTab, setActiveTab] = useState('models'); const [tunnelDisabled, setTunnelDisabled] = useState(false); const hasTrackedInitialTab = useRef(false); + const { isAvailable: localInferenceAvailable } = useLocalInferenceAvailable(); const handleTabChange = (tab: string) => { setActiveTab(tab); @@ -77,6 +79,12 @@ export default function SettingsView({ } }, [activeTab]); + useEffect(() => { + if (!localInferenceAvailable && activeTab === 'local-inference') { + setActiveTab('models'); + } + }, [localInferenceAvailable, activeTab]); + useEffect(() => { getTunnelStatus() .then(({ data }) => { @@ -129,14 +137,16 @@ export default function SettingsView({ Models - - - Local Inference - + {localInferenceAvailable && ( + + + Local Inference + + )} Chat @@ -180,12 +190,14 @@ export default function SettingsView({ - - - + {localInferenceAvailable && ( + + + + )} { const [downloads, setDownloads] = useState>(new Map()); const [showAllFeatured, setShowAllFeatured] = useState(false); const [settingsOpenFor, setSettingsOpenFor] = useState(null); + const [featureDisabled, setFeatureDisabled] = useState(false); const { currentModel, currentProvider, refreshCurrentModelAndProvider } = useModelAndProvider(); const downloadSectionRef = useRef(null); const selectedModelId = currentProvider === 'local' ? currentModel : null; @@ -35,6 +36,10 @@ export const LocalInferenceSettings = () => { const loadModels = useCallback(async () => { try { const response = await listLocalModels(); + if (response.response?.status === 404) { + setFeatureDisabled(true); + return; + } if (response.data) { setModels(response.data); } @@ -165,6 +170,20 @@ export const LocalInferenceSettings = () => { const displayedFeatured = showAllFeatured ? notDownloadedModels : recommendedModels; const showFeaturedToggle = notDownloadedModels.length > recommendedModels.length; + if (featureDisabled) { + return ( +
+
+

Local Inference

+

+ Local inference is not available in this build. The server was compiled without + local inference support. +

+
+
+ ); + } + return (
diff --git a/ui/desktop/src/components/settings/models/bottom_bar/ModelsBottomBar.tsx b/ui/desktop/src/components/settings/models/bottom_bar/ModelsBottomBar.tsx index e3e1a51b4c73..a777360b4a62 100644 --- a/ui/desktop/src/components/settings/models/bottom_bar/ModelsBottomBar.tsx +++ b/ui/desktop/src/components/settings/models/bottom_bar/ModelsBottomBar.tsx @@ -18,6 +18,7 @@ import { Alert } from '../../../alerts'; import BottomMenuAlertPopover from '../../../bottom_menu/BottomMenuAlertPopover'; import { ModelSettingsPanel } from '../../localInference/ModelSettingsPanel'; import { ScrollArea } from '../../../ui/scroll-area'; +import { useLocalInferenceAvailable } from '../../../../hooks/useLocalInferenceAvailable'; interface ModelsBottomBarProps { sessionId: string | null; @@ -48,6 +49,7 @@ export default function ModelsBottomBar({ const currentModelInfo = useCurrentModelInfo(); const { read, getProviders } = useConfig(); + const { isAvailable: localInferenceAvailable } = useLocalInferenceAvailable(); const [displayProvider, setDisplayProvider] = useState(null); const [displayModelName, setDisplayModelName] = useState('Select Model'); const [isAddModelModalOpen, setIsAddModelModalOpen] = useState(false); @@ -188,7 +190,7 @@ export default function ModelsBottomBar({ Lead/Worker Settings - {currentProvider === 'local' && currentModel && ( + {localInferenceAvailable && currentProvider === 'local' && currentModel && ( setIsLocalModelSettingsOpen(true)}> Local Model Settings diff --git a/ui/desktop/src/components/settings/models/subcomponents/SwitchModelModal.tsx b/ui/desktop/src/components/settings/models/subcomponents/SwitchModelModal.tsx index 279abdbb5cd3..1c7a279824ed 100644 --- a/ui/desktop/src/components/settings/models/subcomponents/SwitchModelModal.tsx +++ b/ui/desktop/src/components/settings/models/subcomponents/SwitchModelModal.tsx @@ -20,6 +20,7 @@ import Model, { getProviderMetadata, fetchModelsForProviders } from '../modelInt import { getPredefinedModelsFromEnv, shouldShowPredefinedModels } from '../predefinedModelsUtils'; import { ProviderType } from '../../../../api'; import { trackModelChanged } from '../../../../utils/analytics'; +import { useLocalInferenceAvailable } from '../../../../hooks/useLocalInferenceAvailable'; const THINKING_LEVEL_OPTIONS = [ { value: 'low', label: 'Low - Better latency, lighter reasoning' }, @@ -106,6 +107,7 @@ export const SwitchModelModal = ({ currentModel: configModel, currentProvider: configProvider, } = useModelAndProvider(); + const { isAvailable: localInferenceAvailable } = useLocalInferenceAvailable(); // Use session-specific model/provider if available, otherwise fall back to config defaults const currentModel = sessionModel ?? configModel; const currentProvider = sessionProvider ?? configProvider; @@ -307,7 +309,9 @@ export const SwitchModelModal = ({ (async () => { try { const providersResponse = await getProviders(false); - const activeProviders = providersResponse.filter((provider) => provider.is_configured); + const activeProviders = providersResponse.filter( + (provider) => provider.is_configured && (provider.name !== 'local' || localInferenceAvailable) + ); // Create provider options and add "Use other provider" option setProviderOptions([ ...activeProviders.map(({ metadata, name }) => ({ @@ -380,7 +384,7 @@ export const SwitchModelModal = ({ setLoadingModels(false); } })(); - }, [getProviders, usePredefinedModels, read]); + }, [getProviders, usePredefinedModels, read, localInferenceAvailable]); const filteredModelOptions = provider ? modelOptions.filter((group) => group.options[0]?.provider === provider) diff --git a/ui/desktop/src/hooks/useLocalInferenceAvailable.ts b/ui/desktop/src/hooks/useLocalInferenceAvailable.ts new file mode 100644 index 000000000000..fa124e6e0ed2 --- /dev/null +++ b/ui/desktop/src/hooks/useLocalInferenceAvailable.ts @@ -0,0 +1,32 @@ +import { useState, useEffect } from 'react'; +import { listLocalModels } from '../api'; + +export function useLocalInferenceAvailable() { + const [isAvailable, setIsAvailable] = useState(true); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + let cancelled = false; + + (async () => { + try { + const result = await listLocalModels(); + if (cancelled) return; + + if (result.response?.status === 404) { + setIsAvailable(false); + } + } catch { + // Transient errors (network, 500, etc.) — assume available + } finally { + if (!cancelled) setIsLoading(false); + } + })(); + + return () => { + cancelled = true; + }; + }, []); + + return { isAvailable, isLoading }; +}