Update AI agent, settings, and terminal modules
- Update AI agent conversation, task, and prompt builder - Add notebooks execution module for blocklist actions - Update Bedrock and OpenAI response translators - Refactor settings modules across multiple subsystems - Update terminal settings and window settings - Update drive settings and search command settings
This commit is contained in:
@@ -613,12 +613,21 @@ impl ConvertAPIToolCallToAIAgentAction for api::message::ToolCall {
|
||||
return Err(ToolToAIAgentActionError::MissingTool);
|
||||
};
|
||||
|
||||
// Detect notebook tool name encoded in tool_call_id prefix.
|
||||
let (effective_tool_call_id, tool_name) =
|
||||
if let Some(stripped) = self.tool_call_id.strip_prefix("notebook::") {
|
||||
(stripped.to_string(), Some("notebook".to_string()))
|
||||
} else {
|
||||
(self.tool_call_id.clone(), None)
|
||||
};
|
||||
|
||||
let create_standard_action = |action: AIAgentActionType| {
|
||||
Ok(MaybeAIAgentAction::Action(AIAgentAction {
|
||||
id: self.tool_call_id.clone().into(),
|
||||
id: effective_tool_call_id.clone().into(),
|
||||
task_id: params.task_id.clone(),
|
||||
action,
|
||||
requires_result: true,
|
||||
tool_name: tool_name.clone(),
|
||||
}))
|
||||
};
|
||||
|
||||
|
||||
@@ -2109,7 +2109,12 @@ impl AIConversation {
|
||||
let streaming_exchange_ids: Vec<_> = self
|
||||
.task_store
|
||||
.all_exchanges()
|
||||
.filter(|exchange| matches!(exchange.output_status, AIAgentOutputStatus::Streaming { .. }))
|
||||
.filter(|exchange| {
|
||||
matches!(
|
||||
exchange.output_status,
|
||||
AIAgentOutputStatus::Streaming { .. }
|
||||
)
|
||||
})
|
||||
.map(|exchange| exchange.id)
|
||||
.collect();
|
||||
|
||||
|
||||
@@ -845,6 +845,10 @@ pub struct AIAgentAction {
|
||||
///
|
||||
/// If this is `true`, a corresponding result _must_ be included in the next query to the AI.
|
||||
pub requires_result: bool,
|
||||
|
||||
/// The original tool name as sent by the model (e.g. "create_plan", "create_notebook").
|
||||
/// Used to distinguish between tools that share the same `AIAgentActionType` variant.
|
||||
pub tool_name: Option<String>,
|
||||
}
|
||||
|
||||
impl Display for AIAgentAction {
|
||||
|
||||
@@ -138,7 +138,8 @@ mod optimistic {
|
||||
#[derive(Debug, Clone)]
|
||||
pub(super) enum Task {
|
||||
Root,
|
||||
#[allow(dead_code)] // Used in the server-mode path; Bedrock direct creates Server tasks directly
|
||||
#[allow(dead_code)]
|
||||
// Used in the server-mode path; Bedrock direct creates Server tasks directly
|
||||
CLIAgent(CLIAgentSubtask),
|
||||
}
|
||||
|
||||
@@ -185,7 +186,10 @@ impl Task {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn new_optimistic_cli_agent_subtask(block_id: BlockId, parent_task_id: Option<String>) -> Self {
|
||||
pub(super) fn new_optimistic_cli_agent_subtask(
|
||||
block_id: BlockId,
|
||||
parent_task_id: Option<String>,
|
||||
) -> Self {
|
||||
let task_id = Uuid::new_v4().to_string();
|
||||
Self {
|
||||
id: TaskId::new(task_id.clone()),
|
||||
|
||||
@@ -115,9 +115,9 @@ impl ToolExt for api::message::tool_call::Tool {
|
||||
Tool::ReadMcpResource(_) => "read_mcp_resource",
|
||||
Tool::CallMcpTool(_) => "call_mcp_tool",
|
||||
Tool::WriteToLongRunningShellCommand(_) => "write_to_lrc",
|
||||
Tool::ReadDocuments(_) => "read_documents",
|
||||
Tool::EditDocuments(_) => "edit_documents",
|
||||
Tool::CreateDocuments(_) => "create_documents",
|
||||
Tool::ReadDocuments(_) => "read_plan",
|
||||
Tool::EditDocuments(_) => "edit_plan",
|
||||
Tool::CreateDocuments(_) => "create_plan",
|
||||
Tool::ReadShellCommandOutput(_) => "read_shell_command_output",
|
||||
Tool::UseComputer(_) => "use_computer",
|
||||
Tool::RequestComputerUse(_) => "request_computer_use",
|
||||
|
||||
@@ -74,16 +74,17 @@ pub fn extract_new_input_messages(request: &api::Request) -> Vec<ConversationMes
|
||||
});
|
||||
}
|
||||
}
|
||||
Some(api::request::input::user_inputs::user_input::Input::CliAgentUserQuery(
|
||||
Some(
|
||||
api::request::input::user_inputs::user_input::Input::CliAgentUserQuery(
|
||||
cli_query,
|
||||
)) => {
|
||||
),
|
||||
) => {
|
||||
if let Some(user_query) = &cli_query.user_query {
|
||||
if !user_query.query.is_empty() {
|
||||
let query_text = if let Some(running_cmd) = &cli_query.running_command {
|
||||
let mut context = format!(
|
||||
"[Running command: {}]\n",
|
||||
running_cmd.command
|
||||
);
|
||||
let query_text =
|
||||
if let Some(running_cmd) = &cli_query.running_command {
|
||||
let mut context =
|
||||
format!("[Running command: {}]\n", running_cmd.command);
|
||||
if let Some(snapshot) = &running_cmd.snapshot {
|
||||
if !snapshot.output.is_empty() {
|
||||
context.push_str(&format!(
|
||||
@@ -256,9 +257,11 @@ pub fn extract_user_query_text(request: &api::Request) -> Option<String> {
|
||||
return Some(query.query.clone());
|
||||
}
|
||||
}
|
||||
Some(api::request::input::user_inputs::user_input::Input::CliAgentUserQuery(
|
||||
Some(
|
||||
api::request::input::user_inputs::user_input::Input::CliAgentUserQuery(
|
||||
cli_query,
|
||||
)) => {
|
||||
),
|
||||
) => {
|
||||
if let Some(user_query) = &cli_query.user_query {
|
||||
if !user_query.query.is_empty() {
|
||||
return Some(user_query.query.clone());
|
||||
@@ -388,16 +391,17 @@ fn extract_input_messages(request: &api::Request) -> Vec<api::Message> {
|
||||
});
|
||||
}
|
||||
}
|
||||
Some(api::request::input::user_inputs::user_input::Input::CliAgentUserQuery(
|
||||
Some(
|
||||
api::request::input::user_inputs::user_input::Input::CliAgentUserQuery(
|
||||
cli_query,
|
||||
)) => {
|
||||
),
|
||||
) => {
|
||||
if let Some(user_query) = &cli_query.user_query {
|
||||
if !user_query.query.is_empty() {
|
||||
let query_text = if let Some(running_cmd) = &cli_query.running_command {
|
||||
let mut context = format!(
|
||||
"[Running command: {}]\n",
|
||||
running_cmd.command
|
||||
);
|
||||
let query_text =
|
||||
if let Some(running_cmd) = &cli_query.running_command {
|
||||
let mut context =
|
||||
format!("[Running command: {}]\n", running_cmd.command);
|
||||
if let Some(snapshot) = &running_cmd.snapshot {
|
||||
if !snapshot.output.is_empty() {
|
||||
context.push_str(&format!(
|
||||
@@ -1189,34 +1193,67 @@ pub fn default_tool_definitions() -> Vec<ToolDefinition> {
|
||||
}),
|
||||
},
|
||||
ToolDefinition {
|
||||
name: "read_documents".to_string(),
|
||||
description: "Read the contents of one or more Galaxy notebook documents by their IDs.".to_string(),
|
||||
name: "read_plan".to_string(),
|
||||
description: "Read the contents of one or more Galaxy plan documents by their IDs. Plans are rich-text documents that appear in the Plans folder of Galaxy Drive.".to_string(),
|
||||
input_schema: serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"document_ids": { "type": "array", "items": { "type": "string" }, "description": "Document IDs to read" }
|
||||
"document_ids": { "type": "array", "items": { "type": "string" }, "description": "Plan document IDs to read" }
|
||||
},
|
||||
"required": ["document_ids"]
|
||||
}),
|
||||
},
|
||||
ToolDefinition {
|
||||
name: "create_documents".to_string(),
|
||||
description: "Create new Galaxy notebook documents with the specified title and content.".to_string(),
|
||||
name: "create_plan".to_string(),
|
||||
description: "Create a new plan document in Galaxy Drive's Plans folder. Plans are rich-text documents for tracking tasks, architecture decisions, and project notes.".to_string(),
|
||||
input_schema: serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"documents": { "type": "array", "items": { "type": "object", "properties": { "title": { "type": "string" }, "content": { "type": "string" } }, "required": ["title", "content"] }, "description": "Documents to create" }
|
||||
"documents": { "type": "array", "items": { "type": "object", "properties": { "title": { "type": "string" }, "content": { "type": "string" } }, "required": ["title", "content"] }, "description": "Plan documents to create" }
|
||||
},
|
||||
"required": ["documents"]
|
||||
}),
|
||||
},
|
||||
ToolDefinition {
|
||||
name: "edit_documents".to_string(),
|
||||
description: "Edit existing Galaxy notebook documents using search/replace diffs.".to_string(),
|
||||
name: "edit_plan".to_string(),
|
||||
description: "Edit an existing plan document in Galaxy Drive using search/replace diffs.".to_string(),
|
||||
input_schema: serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"diffs": { "type": "array", "items": { "type": "object", "properties": { "document_id": { "type": "string" }, "search": { "type": "string" }, "replace": { "type": "string" } }, "required": ["document_id", "search", "replace"] }, "description": "Edits to apply to documents" }
|
||||
"diffs": { "type": "array", "items": { "type": "object", "properties": { "document_id": { "type": "string" }, "search": { "type": "string" }, "replace": { "type": "string" } }, "required": ["document_id", "search", "replace"] }, "description": "Edits to apply to plan documents" }
|
||||
},
|
||||
"required": ["diffs"]
|
||||
}),
|
||||
},
|
||||
ToolDefinition {
|
||||
name: "read_notebook".to_string(),
|
||||
description: "Read the contents of one or more Galaxy Drive notebooks by their IDs. Notebooks are user-created rich-text documents stored in Galaxy Drive.".to_string(),
|
||||
input_schema: serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"document_ids": { "type": "array", "items": { "type": "string" }, "description": "Notebook IDs to read" }
|
||||
},
|
||||
"required": ["document_ids"]
|
||||
}),
|
||||
},
|
||||
ToolDefinition {
|
||||
name: "create_notebook".to_string(),
|
||||
description: "Create a new notebook in Galaxy Drive. Notebooks are rich-text documents for general notes, documentation, and reference material.".to_string(),
|
||||
input_schema: serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"documents": { "type": "array", "items": { "type": "object", "properties": { "title": { "type": "string" }, "content": { "type": "string" } }, "required": ["title", "content"] }, "description": "Notebooks to create" }
|
||||
},
|
||||
"required": ["documents"]
|
||||
}),
|
||||
},
|
||||
ToolDefinition {
|
||||
name: "edit_notebook".to_string(),
|
||||
description: "Edit an existing Galaxy Drive notebook using search/replace diffs.".to_string(),
|
||||
input_schema: serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"diffs": { "type": "array", "items": { "type": "object", "properties": { "document_id": { "type": "string" }, "search": { "type": "string" }, "replace": { "type": "string" } }, "required": ["document_id", "search", "replace"] }, "description": "Edits to apply to notebooks" }
|
||||
},
|
||||
"required": ["diffs"]
|
||||
}),
|
||||
|
||||
@@ -998,7 +998,7 @@ pub fn build_tool_call_message(
|
||||
api::message::tool_call::ReadMcpResource { uri, server_id },
|
||||
))
|
||||
}
|
||||
"read_documents" => {
|
||||
"read_plan" | "read_documents" | "read_notebook" => {
|
||||
let documents = input
|
||||
.get("document_ids")
|
||||
.and_then(|v| v.as_array())
|
||||
@@ -1016,7 +1016,7 @@ pub fn build_tool_call_message(
|
||||
api::message::tool_call::ReadDocuments { documents },
|
||||
))
|
||||
}
|
||||
"create_documents" => {
|
||||
"create_plan" | "create_documents" | "create_notebook" => {
|
||||
let new_documents = input
|
||||
.get("documents")
|
||||
.and_then(|v| v.as_array())
|
||||
@@ -1035,7 +1035,7 @@ pub fn build_tool_call_message(
|
||||
api::message::tool_call::CreateDocuments { new_documents },
|
||||
))
|
||||
}
|
||||
"edit_documents" => {
|
||||
"edit_plan" | "edit_documents" | "edit_notebook" => {
|
||||
let diffs = input
|
||||
.get("diffs")
|
||||
.and_then(|v| v.as_array())
|
||||
@@ -1211,16 +1211,24 @@ pub fn build_tool_call_message(
|
||||
}
|
||||
};
|
||||
|
||||
// For notebook tools, encode the tool name into the tool_call_id so that
|
||||
// the conversion layer can route them to the notebook executor.
|
||||
let effective_tool_call_id = if is_notebook_tool(tool_name) {
|
||||
format!("notebook::{tool_use_id}")
|
||||
} else {
|
||||
tool_use_id.to_string()
|
||||
};
|
||||
|
||||
let message = if let Some(tool_variant) = tool {
|
||||
api::Message {
|
||||
id: tool_use_id.to_string(),
|
||||
id: effective_tool_call_id.clone(),
|
||||
task_id: task_id.to_string(),
|
||||
request_id: String::new(),
|
||||
timestamp: None,
|
||||
server_message_data: String::new(),
|
||||
citations: vec![],
|
||||
message: Some(api::message::Message::ToolCall(api::message::ToolCall {
|
||||
tool_call_id: tool_use_id.to_string(),
|
||||
tool_call_id: effective_tool_call_id,
|
||||
tool: Some(tool_variant),
|
||||
})),
|
||||
}
|
||||
@@ -1275,6 +1283,13 @@ const KNOWN_TOOLS: &[&str] = &[
|
||||
"write_to_long_running_shell_command",
|
||||
"read_shell_command_output",
|
||||
"read_mcp_resource",
|
||||
"read_plan",
|
||||
"create_plan",
|
||||
"edit_plan",
|
||||
"read_notebook",
|
||||
"create_notebook",
|
||||
"edit_notebook",
|
||||
// Legacy aliases — accept old names so in-flight conversations don't break.
|
||||
"read_documents",
|
||||
"create_documents",
|
||||
"edit_documents",
|
||||
@@ -1291,6 +1306,10 @@ fn is_known_tool(name: &str) -> bool {
|
||||
KNOWN_TOOLS.contains(&name) || name.starts_with("mcp__")
|
||||
}
|
||||
|
||||
fn is_notebook_tool(name: &str) -> bool {
|
||||
matches!(name, "create_notebook" | "read_notebook" | "edit_notebook")
|
||||
}
|
||||
|
||||
/// Searches conversation message history for tool call results matching the given criteria.
|
||||
fn recall_from_history(
|
||||
messages: &[ConversationMessage],
|
||||
|
||||
@@ -5,6 +5,7 @@ pub(super) mod edit_documents;
|
||||
pub(super) mod fetch_conversation;
|
||||
pub(super) mod file_glob;
|
||||
pub(super) mod grep;
|
||||
pub(super) mod notebooks;
|
||||
pub(super) mod read_documents;
|
||||
pub(super) mod read_files;
|
||||
pub(super) mod read_mcp_resource;
|
||||
@@ -31,6 +32,7 @@ use file_glob::FileGlobExecutor;
|
||||
use futures::{future::BoxFuture, FutureExt};
|
||||
use galaxy_core::{execution_mode::AppExecutionMode, features::FeatureFlag};
|
||||
use grep::GrepExecutor;
|
||||
use notebooks::NotebookExecutor;
|
||||
use parking_lot::FairMutex;
|
||||
use read_documents::ReadDocumentsExecutor;
|
||||
pub(super) use read_files::ReadFilesExecutor;
|
||||
@@ -253,6 +255,7 @@ pub struct BlocklistAIActionExecutor {
|
||||
read_documents_executor: ModelHandle<ReadDocumentsExecutor>,
|
||||
edit_documents_executor: ModelHandle<EditDocumentsExecutor>,
|
||||
create_documents_executor: ModelHandle<CreateDocumentsExecutor>,
|
||||
notebook_executor: ModelHandle<NotebookExecutor>,
|
||||
use_computer_executor: ModelHandle<UseComputerExecutor>,
|
||||
request_computer_use_executor: ModelHandle<RequestComputerUseExecutor>,
|
||||
read_skill_executor: ModelHandle<ReadSkillExecutor>,
|
||||
@@ -317,6 +320,7 @@ impl BlocklistAIActionExecutor {
|
||||
let edit_documents_executor = ctx.add_model(|_| EditDocumentsExecutor::new());
|
||||
let create_documents_executor = ctx
|
||||
.add_model(|_| CreateDocumentsExecutor::new(active_session.clone(), terminal_view_id));
|
||||
let notebook_executor = ctx.add_model(|_| NotebookExecutor::new());
|
||||
let use_computer_executor = ctx.add_model(|_| UseComputerExecutor::new());
|
||||
let request_computer_use_executor =
|
||||
ctx.add_model(|_| RequestComputerUseExecutor::new(terminal_view_id));
|
||||
@@ -341,6 +345,7 @@ impl BlocklistAIActionExecutor {
|
||||
read_documents_executor,
|
||||
edit_documents_executor,
|
||||
create_documents_executor,
|
||||
notebook_executor,
|
||||
use_computer_executor,
|
||||
request_computer_use_executor,
|
||||
async_executing_actions: Default::default(),
|
||||
@@ -487,15 +492,33 @@ impl BlocklistAIActionExecutor {
|
||||
AIAgentActionType::SuggestPrompt { .. } => self
|
||||
.suggest_prompt_executor
|
||||
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx)),
|
||||
AIAgentActionType::ReadDocuments(_) => self
|
||||
.read_documents_executor
|
||||
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx)),
|
||||
AIAgentActionType::EditDocuments(_) => self
|
||||
.edit_documents_executor
|
||||
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx)),
|
||||
AIAgentActionType::CreateDocuments(_) => self
|
||||
.create_documents_executor
|
||||
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx)),
|
||||
AIAgentActionType::ReadDocuments(_) => {
|
||||
if action.tool_name.as_deref() == Some("notebook") {
|
||||
self.notebook_executor
|
||||
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx))
|
||||
} else {
|
||||
self.read_documents_executor
|
||||
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx))
|
||||
}
|
||||
}
|
||||
AIAgentActionType::EditDocuments(_) => {
|
||||
if action.tool_name.as_deref() == Some("notebook") {
|
||||
self.notebook_executor
|
||||
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx))
|
||||
} else {
|
||||
self.edit_documents_executor
|
||||
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx))
|
||||
}
|
||||
}
|
||||
AIAgentActionType::CreateDocuments(_) => {
|
||||
if action.tool_name.as_deref() == Some("notebook") {
|
||||
self.notebook_executor
|
||||
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx))
|
||||
} else {
|
||||
self.create_documents_executor
|
||||
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx))
|
||||
}
|
||||
}
|
||||
AIAgentActionType::UseComputer(_) => self
|
||||
.use_computer_executor
|
||||
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx)),
|
||||
@@ -683,20 +706,41 @@ impl BlocklistAIActionExecutor {
|
||||
.suggest_prompt_executor
|
||||
.update(ctx, |executor, ctx| executor.execute(input, ctx))
|
||||
.into(),
|
||||
AIAgentActionType::ReadDocuments(_) => self
|
||||
.read_documents_executor
|
||||
AIAgentActionType::ReadDocuments(_) => {
|
||||
if action.tool_name.as_deref() == Some("notebook") {
|
||||
self.notebook_executor
|
||||
.update(ctx, |executor, ctx| executor.execute_read(input, ctx))
|
||||
.into()
|
||||
} else {
|
||||
self.read_documents_executor
|
||||
.update(ctx, |executor, ctx| executor.execute(input, ctx))
|
||||
.into(),
|
||||
AIAgentActionType::EditDocuments(_) => self
|
||||
.edit_documents_executor
|
||||
.into()
|
||||
}
|
||||
}
|
||||
AIAgentActionType::EditDocuments(_) => {
|
||||
if action.tool_name.as_deref() == Some("notebook") {
|
||||
self.notebook_executor
|
||||
.update(ctx, |executor, ctx| executor.execute_edit(input, ctx))
|
||||
.into()
|
||||
} else {
|
||||
self.edit_documents_executor
|
||||
.update(ctx, |executor, ctx| executor.execute(input, ctx))
|
||||
.into(),
|
||||
AIAgentActionType::CreateDocuments(_) => self
|
||||
.create_documents_executor
|
||||
.into()
|
||||
}
|
||||
}
|
||||
AIAgentActionType::CreateDocuments(_) => {
|
||||
if action.tool_name.as_deref() == Some("notebook") {
|
||||
self.notebook_executor
|
||||
.update(ctx, |executor, ctx| executor.execute_create(input, ctx))
|
||||
.into()
|
||||
} else {
|
||||
self.create_documents_executor
|
||||
.update(ctx, |executor, ctx| {
|
||||
executor.execute(input, conversation_id, ctx)
|
||||
})
|
||||
.into(),
|
||||
.into()
|
||||
}
|
||||
}
|
||||
AIAgentActionType::UseComputer(_) => self
|
||||
.use_computer_executor
|
||||
.update(ctx, |executor, ctx| executor.execute(input, ctx))
|
||||
@@ -924,15 +968,33 @@ impl BlocklistAIActionExecutor {
|
||||
AIAgentActionType::SuggestPrompt { .. } => self
|
||||
.suggest_prompt_executor
|
||||
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx)),
|
||||
AIAgentActionType::ReadDocuments(_) => self
|
||||
.read_documents_executor
|
||||
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx)),
|
||||
AIAgentActionType::EditDocuments(_) => self
|
||||
.edit_documents_executor
|
||||
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx)),
|
||||
AIAgentActionType::CreateDocuments(_) => self
|
||||
.create_documents_executor
|
||||
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx)),
|
||||
AIAgentActionType::ReadDocuments(_) => {
|
||||
if input.action.tool_name.as_deref() == Some("notebook") {
|
||||
self.notebook_executor
|
||||
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx))
|
||||
} else {
|
||||
self.read_documents_executor
|
||||
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx))
|
||||
}
|
||||
}
|
||||
AIAgentActionType::EditDocuments(_) => {
|
||||
if input.action.tool_name.as_deref() == Some("notebook") {
|
||||
self.notebook_executor
|
||||
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx))
|
||||
} else {
|
||||
self.edit_documents_executor
|
||||
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx))
|
||||
}
|
||||
}
|
||||
AIAgentActionType::CreateDocuments(_) => {
|
||||
if input.action.tool_name.as_deref() == Some("notebook") {
|
||||
self.notebook_executor
|
||||
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx))
|
||||
} else {
|
||||
self.create_documents_executor
|
||||
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx))
|
||||
}
|
||||
}
|
||||
AIAgentActionType::UseComputer(_) => self
|
||||
.use_computer_executor
|
||||
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx)),
|
||||
|
||||
@@ -10,7 +10,6 @@ use crate::{
|
||||
artifacts::Artifact,
|
||||
blocklist::BlocklistAIHistoryModel,
|
||||
document::ai_document_model::{AIDocumentModel, AIDocumentVersion},
|
||||
execution_profiles::profiles::AIExecutionProfilesModel,
|
||||
},
|
||||
notebooks::editor::model::FileLinkResolutionContext,
|
||||
terminal::model::session::active_session::ActiveSession,
|
||||
@@ -102,15 +101,10 @@ impl CreateDocumentsExecutor {
|
||||
})
|
||||
};
|
||||
|
||||
let profile = AIExecutionProfilesModel::as_ref(ctx)
|
||||
.active_profile(Some(self.terminal_view_id), ctx);
|
||||
let should_autosync = profile.data().autosync_plans_to_warp_drive;
|
||||
|
||||
if should_autosync {
|
||||
// Plans always sync to the Plans folder in Galaxy Drive.
|
||||
model.update(ctx, |model, model_ctx| {
|
||||
model.sync_to_warp_drive(id, model_ctx);
|
||||
});
|
||||
}
|
||||
|
||||
// Add plan artifact to the conversation.
|
||||
let artifact = Artifact::Plan {
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
//! Executors for the `create_notebook`, `read_notebook`, and `edit_notebook` tools.
|
||||
//!
|
||||
//! These tools interact with Galaxy Drive CloudNotebook objects directly,
|
||||
//! as opposed to the plan tools which work through AIDocumentModel.
|
||||
|
||||
use futures::{future::BoxFuture, FutureExt};
|
||||
use galaxyui::{Entity, ModelContext, SingletonEntity};
|
||||
|
||||
use crate::{
|
||||
ai::{
|
||||
agent::{
|
||||
AIAgentAction, AIAgentActionType, CreateDocumentsRequest, CreateDocumentsResult,
|
||||
DocumentContext, EditDocumentsRequest, EditDocumentsResult, ReadDocumentsRequest,
|
||||
ReadDocumentsResult,
|
||||
},
|
||||
document::ai_document_model::AIDocumentVersion,
|
||||
},
|
||||
cloud_object::model::persistence::CloudModel,
|
||||
notebooks::CloudNotebookModel,
|
||||
server::{cloud_objects::update_manager::UpdateManager, ids::ClientId},
|
||||
workspaces::user_workspaces::UserWorkspaces,
|
||||
};
|
||||
|
||||
use super::{ActionExecution, AnyActionExecution, ExecuteActionInput, PreprocessActionInput};
|
||||
|
||||
pub struct NotebookExecutor;
|
||||
|
||||
impl NotebookExecutor {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
pub(super) fn should_autoexecute(
|
||||
&self,
|
||||
_input: ExecuteActionInput,
|
||||
_ctx: &mut ModelContext<Self>,
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) fn execute_create(
|
||||
&mut self,
|
||||
input: ExecuteActionInput,
|
||||
ctx: &mut ModelContext<Self>,
|
||||
) -> impl Into<AnyActionExecution> {
|
||||
let ExecuteActionInput { action, .. } = input;
|
||||
let AIAgentAction {
|
||||
action: AIAgentActionType::CreateDocuments(CreateDocumentsRequest { documents }),
|
||||
..
|
||||
} = action
|
||||
else {
|
||||
return ActionExecution::<CreateDocumentsResult>::InvalidAction;
|
||||
};
|
||||
|
||||
let Some(owner) = UserWorkspaces::as_ref(ctx).personal_drive(ctx) else {
|
||||
return ActionExecution::Sync(
|
||||
CreateDocumentsResult::Error("No personal drive available.".to_string()).into(),
|
||||
);
|
||||
};
|
||||
|
||||
let mut created = Vec::new();
|
||||
|
||||
for document in documents {
|
||||
let client_id = ClientId::new();
|
||||
let model = CloudNotebookModel {
|
||||
title: document.title.clone(),
|
||||
data: document.content.clone(),
|
||||
ai_document_id: None,
|
||||
conversation_id: None,
|
||||
};
|
||||
|
||||
UpdateManager::handle(ctx).update(ctx, |update_manager, ctx| {
|
||||
update_manager.create_notebook(
|
||||
client_id,
|
||||
owner,
|
||||
None, // root of personal space
|
||||
model,
|
||||
crate::cloud_object::CloudObjectEventEntrypoint::Unknown,
|
||||
false,
|
||||
ctx,
|
||||
);
|
||||
});
|
||||
|
||||
created.push(DocumentContext {
|
||||
document_id: crate::ai::document::ai_document_model::AIDocumentId::new(),
|
||||
document_version: AIDocumentVersion::default(),
|
||||
content: document.content.clone(),
|
||||
line_ranges: vec![],
|
||||
});
|
||||
}
|
||||
|
||||
ActionExecution::Sync(
|
||||
CreateDocumentsResult::Success {
|
||||
created_documents: created,
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn execute_read(
|
||||
&mut self,
|
||||
input: ExecuteActionInput,
|
||||
ctx: &mut ModelContext<Self>,
|
||||
) -> impl Into<AnyActionExecution> {
|
||||
let ExecuteActionInput { action, .. } = input;
|
||||
let AIAgentAction {
|
||||
action: AIAgentActionType::ReadDocuments(ReadDocumentsRequest { document_ids }),
|
||||
..
|
||||
} = action
|
||||
else {
|
||||
return ActionExecution::<ReadDocumentsResult>::InvalidAction;
|
||||
};
|
||||
|
||||
let cloud_model = CloudModel::as_ref(ctx);
|
||||
let mut documents = Vec::new();
|
||||
|
||||
for id in document_ids {
|
||||
// Try to find the notebook by treating the ID as a SyncId string
|
||||
let notebook = cloud_model
|
||||
.get_all_active_notebooks()
|
||||
.find(|nb| nb.id.uid() == id.to_string());
|
||||
|
||||
if let Some(notebook) = notebook {
|
||||
documents.push(DocumentContext {
|
||||
document_id: *id,
|
||||
document_version: AIDocumentVersion::default(),
|
||||
content: format!("# {}\n\n{}", notebook.model().title, notebook.model().data),
|
||||
line_ranges: vec![],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ActionExecution::Sync(ReadDocumentsResult::Success { documents }.into())
|
||||
}
|
||||
|
||||
pub(super) fn execute_edit(
|
||||
&mut self,
|
||||
input: ExecuteActionInput,
|
||||
ctx: &mut ModelContext<Self>,
|
||||
) -> impl Into<AnyActionExecution> {
|
||||
let ExecuteActionInput { action, .. } = input;
|
||||
let AIAgentAction {
|
||||
action: AIAgentActionType::EditDocuments(EditDocumentsRequest { diffs }),
|
||||
..
|
||||
} = action
|
||||
else {
|
||||
return ActionExecution::<EditDocumentsResult>::InvalidAction;
|
||||
};
|
||||
|
||||
let mut updated_documents = Vec::new();
|
||||
let mut errors = Vec::new();
|
||||
|
||||
for diff in diffs {
|
||||
let cloud_model = CloudModel::as_ref(ctx);
|
||||
let notebook_data = cloud_model
|
||||
.get_all_active_notebooks()
|
||||
.find(|nb| nb.id.uid() == diff.document_id.to_string())
|
||||
.map(|nb| (nb.id, nb.model().data.clone()));
|
||||
|
||||
let Some((notebook_id, current_data)) = notebook_data else {
|
||||
errors.push(format!("Notebook {} not found.", diff.document_id));
|
||||
continue;
|
||||
};
|
||||
|
||||
// Simple search/replace on the notebook data
|
||||
if !current_data.contains(&diff.search) {
|
||||
errors.push(format!(
|
||||
"Could not find search text in notebook {}.",
|
||||
diff.document_id
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
let new_data = current_data.replacen(&diff.search, &diff.replace, 1);
|
||||
let new_data_arc = std::sync::Arc::new(new_data.clone());
|
||||
|
||||
UpdateManager::handle(ctx).update(ctx, |update_manager, ctx| {
|
||||
update_manager.update_notebook_data(new_data_arc, notebook_id, ctx);
|
||||
});
|
||||
|
||||
updated_documents.push(DocumentContext {
|
||||
document_id: diff.document_id,
|
||||
document_version: AIDocumentVersion::default(),
|
||||
content: new_data,
|
||||
line_ranges: vec![],
|
||||
});
|
||||
}
|
||||
|
||||
if !errors.is_empty() {
|
||||
return ActionExecution::Sync(EditDocumentsResult::Error(errors.join("\n")).into());
|
||||
}
|
||||
|
||||
ActionExecution::Sync(EditDocumentsResult::Success { updated_documents }.into())
|
||||
}
|
||||
|
||||
pub(super) fn preprocess_action(
|
||||
&mut self,
|
||||
_input: PreprocessActionInput,
|
||||
_ctx: &mut ModelContext<Self>,
|
||||
) -> BoxFuture<'static, ()> {
|
||||
futures::future::ready(()).boxed()
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for NotebookExecutor {
|
||||
type Event = ();
|
||||
}
|
||||
@@ -2226,11 +2226,9 @@ impl BlocklistAIController {
|
||||
{
|
||||
let history_model = BlocklistAIHistoryModel::as_ref(ctx);
|
||||
if let Some(conversation) = history_model.conversation(&conversation_id) {
|
||||
let has_optimistic_cli_subagent =
|
||||
conversation.has_active_subagent();
|
||||
let has_optimistic_cli_subagent = conversation.has_active_subagent();
|
||||
if !has_optimistic_cli_subagent {
|
||||
request_params.root_task_id =
|
||||
Some(conversation.get_root_task_id().to_string());
|
||||
request_params.root_task_id = Some(conversation.get_root_task_id().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2435,8 +2433,7 @@ impl BlocklistAIController {
|
||||
{
|
||||
let terminal_view_id = self.terminal_view_id;
|
||||
history_model.update(ctx, |history_model, ctx| {
|
||||
if let Some(conversation) =
|
||||
history_model.conversation_mut(&conversation_id)
|
||||
if let Some(conversation) = history_model.conversation_mut(&conversation_id)
|
||||
{
|
||||
conversation.force_cancel_all_streaming_exchanges(
|
||||
terminal_view_id,
|
||||
|
||||
@@ -242,23 +242,59 @@ impl AIDocumentModel {
|
||||
return false;
|
||||
};
|
||||
|
||||
let Some(plan_folder_id) = self.get_or_create_plan_folder(owner, ctx).into_server() else {
|
||||
let plan_folder_sync_id = self.get_or_create_plan_folder(owner, ctx);
|
||||
match plan_folder_sync_id.into_server() {
|
||||
Some(plan_folder_id) => {
|
||||
// Plan folder already exists on server — create notebook directly.
|
||||
self.create_notebook_in_plan_folder(
|
||||
id,
|
||||
&title,
|
||||
&content,
|
||||
owner,
|
||||
plan_folder_id,
|
||||
ctx,
|
||||
);
|
||||
ctx.emit(AIDocumentModelEvent::DocumentSaveStatusUpdated(id));
|
||||
}
|
||||
None => {
|
||||
// Plan folder is still being created (has ClientId only).
|
||||
// If we save using the ClientId as the parent folder, the document
|
||||
// will end up in a broken state once the folder is saved.
|
||||
// Queue the document for creation until the folder gets a ServerId.
|
||||
self.pending_document_queue
|
||||
.push(PendingDocument { id, title, content });
|
||||
// Also create the notebook at the root level so it appears in Drive
|
||||
// immediately in local/offline mode.
|
||||
self.pending_document_queue.push(PendingDocument {
|
||||
id,
|
||||
title: title.clone(),
|
||||
content: content.clone(),
|
||||
});
|
||||
|
||||
if let Some(document) = self.documents.get_mut(&id) {
|
||||
// Create a notebook at the root level so it's visible in Drive immediately.
|
||||
let client_id = ClientId::new();
|
||||
if let Some(document) = self.documents.get_mut(&id) {
|
||||
document.sync_id = Some(SyncId::ClientId(client_id));
|
||||
}
|
||||
return true;
|
||||
|
||||
let notebook_model = CloudNotebookModel {
|
||||
title,
|
||||
data: content,
|
||||
ai_document_id: Some(id),
|
||||
conversation_id: self.get_server_conversation_id(&id, ctx),
|
||||
};
|
||||
|
||||
self.create_notebook_in_plan_folder(id, &title, &content, owner, plan_folder_id, ctx);
|
||||
UpdateManager::handle(ctx).update(ctx, |update_manager, ctx| {
|
||||
update_manager.create_notebook(
|
||||
client_id,
|
||||
owner,
|
||||
Some(plan_folder_sync_id),
|
||||
notebook_model,
|
||||
CloudObjectEventEntrypoint::Unknown,
|
||||
true,
|
||||
ctx,
|
||||
);
|
||||
});
|
||||
|
||||
ctx.emit(AIDocumentModelEvent::DocumentSaveStatusUpdated(id));
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
+2
-5
@@ -870,8 +870,7 @@ impl LLMPreferences {
|
||||
Some(key)
|
||||
}
|
||||
};
|
||||
let name =
|
||||
if base_url.contains("localhost") || base_url.contains("127.0.0.1") {
|
||||
let name = if base_url.contains("localhost") || base_url.contains("127.0.0.1") {
|
||||
"LiteLLM (local)".to_string()
|
||||
} else {
|
||||
"LiteLLM".to_string()
|
||||
@@ -931,9 +930,7 @@ impl LLMPreferences {
|
||||
}
|
||||
}
|
||||
|
||||
log::info!(
|
||||
"[openai/litellm] Injected {total_injected} model(s) into available choices"
|
||||
);
|
||||
log::info!("[openai/litellm] Injected {total_injected} model(s) into available choices");
|
||||
}
|
||||
|
||||
/// Returns the OpenAI client config for a given model ID, if it was injected
|
||||
|
||||
@@ -498,6 +498,13 @@ const KNOWN_TOOLS: &[&str] = &[
|
||||
"write_to_long_running_shell_command",
|
||||
"read_shell_command_output",
|
||||
"read_mcp_resource",
|
||||
"read_plan",
|
||||
"create_plan",
|
||||
"edit_plan",
|
||||
"read_notebook",
|
||||
"create_notebook",
|
||||
"edit_notebook",
|
||||
// Legacy aliases — accept old names so in-flight conversations don't break.
|
||||
"read_documents",
|
||||
"create_documents",
|
||||
"edit_documents",
|
||||
|
||||
@@ -39,7 +39,7 @@ mod prompt_builder_tests {
|
||||
|
||||
assert!(prompt.system_prompt.contains("code review mode"));
|
||||
assert!(!prompt.tools.iter().any(|t| t.name == "apply_file_diffs"));
|
||||
assert!(!prompt.tools.iter().any(|t| t.name == "create_documents"));
|
||||
assert!(!prompt.tools.iter().any(|t| t.name == "create_plan"));
|
||||
assert!(prompt.tools.iter().any(|t| t.name == "read_files"));
|
||||
assert!(prompt.tools.iter().any(|t| t.name == "grep"));
|
||||
}
|
||||
|
||||
@@ -32,6 +32,9 @@ fn code_tools() -> Vec<ToolDefinition> {
|
||||
read_documents(),
|
||||
create_documents(),
|
||||
edit_documents(),
|
||||
read_notebook(),
|
||||
create_notebook(),
|
||||
edit_notebook(),
|
||||
start_agent(),
|
||||
send_message_to_agent(),
|
||||
ask_user_question(),
|
||||
@@ -217,13 +220,13 @@ fn read_mcp_resource() -> ToolDefinition {
|
||||
|
||||
fn read_documents() -> ToolDefinition {
|
||||
ToolDefinition {
|
||||
name: "read_documents".to_string(),
|
||||
description: "Read the contents of one or more Galaxy notebook documents by their IDs."
|
||||
name: "read_plan".to_string(),
|
||||
description: "Read the contents of one or more Galaxy plan documents by their IDs. Plans are rich-text documents that appear in the Plans folder of Galaxy Drive."
|
||||
.to_string(),
|
||||
input_schema: serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"document_ids": { "type": "array", "items": { "type": "string" }, "description": "Document IDs to read" }
|
||||
"document_ids": { "type": "array", "items": { "type": "string" }, "description": "Plan document IDs to read" }
|
||||
},
|
||||
"required": ["document_ids"]
|
||||
}),
|
||||
@@ -232,13 +235,13 @@ fn read_documents() -> ToolDefinition {
|
||||
|
||||
fn create_documents() -> ToolDefinition {
|
||||
ToolDefinition {
|
||||
name: "create_documents".to_string(),
|
||||
description: "Create new Galaxy notebook documents with the specified title and content."
|
||||
name: "create_plan".to_string(),
|
||||
description: "Create a new plan document in Galaxy Drive's Plans folder. Plans are rich-text documents for tracking tasks, architecture decisions, and project notes."
|
||||
.to_string(),
|
||||
input_schema: serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"documents": { "type": "array", "items": { "type": "object", "properties": { "title": { "type": "string" }, "content": { "type": "string" } }, "required": ["title", "content"] }, "description": "Documents to create" }
|
||||
"documents": { "type": "array", "items": { "type": "object", "properties": { "title": { "type": "string" }, "content": { "type": "string" } }, "required": ["title", "content"] }, "description": "Plan documents to create" }
|
||||
},
|
||||
"required": ["documents"]
|
||||
}),
|
||||
@@ -247,13 +250,13 @@ fn create_documents() -> ToolDefinition {
|
||||
|
||||
fn edit_documents() -> ToolDefinition {
|
||||
ToolDefinition {
|
||||
name: "edit_documents".to_string(),
|
||||
description: "Edit existing Galaxy notebook documents using search/replace diffs."
|
||||
name: "edit_plan".to_string(),
|
||||
description: "Edit an existing plan document in Galaxy Drive using search/replace diffs."
|
||||
.to_string(),
|
||||
input_schema: serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"diffs": { "type": "array", "items": { "type": "object", "properties": { "document_id": { "type": "string" }, "search": { "type": "string" }, "replace": { "type": "string" } }, "required": ["document_id", "search", "replace"] }, "description": "Edits to apply to documents" }
|
||||
"diffs": { "type": "array", "items": { "type": "object", "properties": { "document_id": { "type": "string" }, "search": { "type": "string" }, "replace": { "type": "string" } }, "required": ["document_id", "search", "replace"] }, "description": "Edits to apply to plan documents" }
|
||||
},
|
||||
"required": ["diffs"]
|
||||
}),
|
||||
@@ -334,3 +337,46 @@ fn fetch_conversation() -> ToolDefinition {
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_notebook() -> ToolDefinition {
|
||||
ToolDefinition {
|
||||
name: "read_notebook".to_string(),
|
||||
description: "Read the contents of one or more Galaxy Drive notebooks by their IDs. Notebooks are user-created rich-text documents stored in Galaxy Drive.".to_string(),
|
||||
input_schema: serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"document_ids": { "type": "array", "items": { "type": "string" }, "description": "Notebook IDs to read" }
|
||||
},
|
||||
"required": ["document_ids"]
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_notebook() -> ToolDefinition {
|
||||
ToolDefinition {
|
||||
name: "create_notebook".to_string(),
|
||||
description: "Create a new notebook in Galaxy Drive. Notebooks are rich-text documents for general notes, documentation, and reference material.".to_string(),
|
||||
input_schema: serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"documents": { "type": "array", "items": { "type": "object", "properties": { "title": { "type": "string" }, "content": { "type": "string" } }, "required": ["title", "content"] }, "description": "Notebooks to create" }
|
||||
},
|
||||
"required": ["documents"]
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn edit_notebook() -> ToolDefinition {
|
||||
ToolDefinition {
|
||||
name: "edit_notebook".to_string(),
|
||||
description: "Edit an existing Galaxy Drive notebook using search/replace diffs."
|
||||
.to_string(),
|
||||
input_schema: serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"diffs": { "type": "array", "items": { "type": "object", "properties": { "document_id": { "type": "string" }, "search": { "type": "string" }, "replace": { "type": "string" } }, "required": ["document_id", "search", "replace"] }, "description": "Edits to apply to notebooks" }
|
||||
},
|
||||
"required": ["diffs"]
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use chrono::Utc;
|
||||
use galaxyui::{App, ModelHandle};
|
||||
use lazy_static::lazy_static;
|
||||
use settings::{SyncToCloud};
|
||||
use settings::SyncToCloud;
|
||||
|
||||
use crate::auth::auth_manager::AuthManager;
|
||||
use crate::auth::user::TEST_USER_UID;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use galaxy_core::features::FeatureFlag;
|
||||
use settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
use super::DriveSortOrder;
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
define_settings_group!(CommandSearchSettings, settings: [
|
||||
show_global_workflows_in_universal_search: ShowGlobalWorkflowsInUniversalSearch {
|
||||
|
||||
@@ -5,7 +5,7 @@ use futures_lite::future;
|
||||
use galaxy_core::features::FeatureFlag;
|
||||
use galaxy_graphql::{object_permissions::AccessLevel, scalars::time::ServerTimestamp};
|
||||
use galaxyui::{App, ModelHandle, SingletonEntity};
|
||||
use settings::{SyncToCloud};
|
||||
use settings::SyncToCloud;
|
||||
|
||||
#[cfg(test)]
|
||||
use crate::server::server_api::object::MockObjectClient;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use galaxyui::accessibility::AccessibilityVerbosity;
|
||||
use settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
define_settings_group!(AccessibilitySettings, settings: [
|
||||
a11y_verbosity: AccessibilityVerbosityState {
|
||||
|
||||
@@ -24,9 +24,7 @@ use regex::Regex;
|
||||
|
||||
use galaxy_core::execution_mode::AppExecutionMode;
|
||||
use galaxy_core::features::FeatureFlag;
|
||||
use settings::{
|
||||
define_settings_group, Setting, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{define_settings_group, Setting, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
use serde::{de::Deserializer, Deserialize, Serialize};
|
||||
use strum::IntoEnumIterator;
|
||||
@@ -466,7 +464,9 @@ impl settings_value::SettingsValue for BedrockModelConfig {}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, schemars::JsonSchema)]
|
||||
#[schemars(description = "Configuration for a single OpenAI-compatible model (e.g. via LiteLLM).")]
|
||||
pub struct OpenAIModelConfig {
|
||||
#[schemars(description = "The model ID to send in the API request (e.g. claude-sonnet-4-20250514).")]
|
||||
#[schemars(
|
||||
description = "The model ID to send in the API request (e.g. claude-sonnet-4-20250514)."
|
||||
)]
|
||||
pub model_id: String,
|
||||
#[schemars(description = "Display name shown in the model picker.")]
|
||||
pub display_name: String,
|
||||
@@ -477,7 +477,9 @@ pub struct OpenAIModelConfig {
|
||||
#[schemars(description = "Maximum context window size in tokens.")]
|
||||
pub context_size: u32,
|
||||
#[serde(default)]
|
||||
#[schemars(description = "Optional provider hint (e.g. anthropic, openai, google) for icon display.")]
|
||||
#[schemars(
|
||||
description = "Optional provider hint (e.g. anthropic, openai, google) for icon display."
|
||||
)]
|
||||
pub provider: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
define_settings_group!(AliasExpansionSettings, settings: [
|
||||
alias_expansion_enabled: AliasExpansionEnabled {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
// Settings for visibility of non-user command blocks like the bootstrap block
|
||||
// and in-band command blocks.
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
define_settings_group!(ChangelogSettings, settings: [
|
||||
show_changelog_after_update: ShowChangelogAfterUpdate {
|
||||
|
||||
@@ -14,9 +14,7 @@ use crate::{
|
||||
server::sync_queue::QueueItem,
|
||||
};
|
||||
|
||||
use settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
define_settings_group!(CloudPreferencesSettings, settings: [
|
||||
settings_sync_enabled: IsSettingsSyncEnabled {
|
||||
type: bool,
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
define_settings_group!(CodeSettings, settings: [
|
||||
code_as_default_editor: CodeAsDefaultEditor {
|
||||
|
||||
@@ -3,10 +3,7 @@ use std::fmt::{Display, Formatter};
|
||||
use enum_iterator::{all, Sequence};
|
||||
use galaxyui::ModelContext;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{
|
||||
macros::define_settings_group, Setting as _, SupportedPlatforms,
|
||||
SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, Setting as _, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use crate::banner::BannerState;
|
||||
use settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
// This isn't exactly a setting, but rather a record of a
|
||||
// user action that should be persisted the same way we would a setting.
|
||||
|
||||
@@ -2,9 +2,7 @@ use galaxy_core::ui::builder::MIN_FONT_SIZE;
|
||||
use galaxyui::{fonts::Weight, rendering::ThinStrokes, AppContext, SingletonEntity};
|
||||
|
||||
use galaxyui::elements::DEFAULT_UI_LINE_HEIGHT_RATIO;
|
||||
use settings::{
|
||||
macros::define_settings_group, Setting, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, Setting, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
use super::EnforceMinimumContrast as EnforceMinimumContrastEnum;
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use crate::terminal::block_list_viewport::InputMode;
|
||||
use settings::{
|
||||
macros::define_settings_group, Setting, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, Setting, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
define_settings_group!(InputModeSettings, settings: [
|
||||
input_mode: InputModeState {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
define_settings_group!(PaneSettings, settings: [
|
||||
should_dim_inactive_panes: ShouldDimInactivePanes {
|
||||
|
||||
@@ -2,9 +2,7 @@ use std::ops::Not;
|
||||
|
||||
use galaxyui::{clipboard::ClipboardContent, AppContext};
|
||||
|
||||
use settings::{
|
||||
macros::define_settings_group, Setting, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, Setting, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
define_settings_group!(SelectionSettings, settings: [
|
||||
copy_on_select: CopyOnSelect {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
define_settings_group!(SshSettings,
|
||||
settings: [
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use galaxyui::{platform::SystemTheme, AppContext};
|
||||
|
||||
use crate::themes::theme::{RespectSystemTheme, SelectedSystemThemes, ThemeKind};
|
||||
use settings::{
|
||||
macros::define_settings_group, Setting, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, Setting, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
// Settings group for themes related settings.
|
||||
// Note that we store just the information needed to derive the current
|
||||
|
||||
@@ -1429,10 +1429,7 @@ impl AISettingsPageView {
|
||||
};
|
||||
|
||||
if !response.status().is_success() {
|
||||
log::error!(
|
||||
"[litellm] Model fetch returned HTTP {}",
|
||||
response.status()
|
||||
);
|
||||
log::error!("[litellm] Model fetch returned HTTP {}", response.status());
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
@@ -1473,9 +1470,7 @@ impl AISettingsPageView {
|
||||
let mut chars = word.chars();
|
||||
match chars.next() {
|
||||
None => String::new(),
|
||||
Some(c) => {
|
||||
c.to_uppercase().to_string() + chars.as_str()
|
||||
}
|
||||
Some(c) => c.to_uppercase().to_string() + chars.as_str(),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
@@ -1502,11 +1497,7 @@ impl AISettingsPageView {
|
||||
})
|
||||
.collect();
|
||||
|
||||
log::info!(
|
||||
"[litellm] Fetched {} model(s) from {}",
|
||||
models.len(),
|
||||
url
|
||||
);
|
||||
log::info!("[litellm] Fetched {} model(s) from {}", models.len(), url);
|
||||
models
|
||||
},
|
||||
|_view, models, ctx| {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
define_settings_group!(AltScreenReporting, settings: [
|
||||
mouse_reporting_enabled: MouseReportingEnabled {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
// Settings for controlling the behavior of the block list.
|
||||
define_settings_group!(BlockListSettings, settings: [
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::{banner::BannerState, resource_center::Tip};
|
||||
use galaxy_core::settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use galaxy_core::settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
define_settings_group!(GeneralSettings, settings: [
|
||||
show_warning_before_quitting: ShowWarningBeforeQuitting {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use galaxyui::{keymap::Keystroke, AppContext, DisplayIdx, ModelContext};
|
||||
use settings::{
|
||||
macros::define_settings_group, Setting, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, Setting, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
use crate::{
|
||||
report_if_error,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use crate::features::FeatureFlag;
|
||||
|
||||
use galaxyui::{AppContext, SingletonEntity};
|
||||
use settings::{
|
||||
macros::define_settings_group, Setting, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, Setting, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
define_settings_group!(LigatureSettings, settings: [
|
||||
ligature_rendering_enabled: LigatureRenderingEnabled {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use galaxyui::{AppContext, SingletonEntity};
|
||||
use settings::{
|
||||
macros::define_settings_group, Setting, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, Setting, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
use crate::{terminal::model::ObfuscateSecrets, workspaces::user_workspaces::UserWorkspaces};
|
||||
|
||||
|
||||
@@ -9,9 +9,7 @@ use serde::{Deserialize, Serialize};
|
||||
pub use startup_shell::*;
|
||||
pub use working_directory_config::*;
|
||||
|
||||
use galaxy_core::settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use galaxy_core::settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
use crate::ai::blocklist::agent_view::toolbar_item::AgentToolbarItemKind;
|
||||
use crate::context_chips::prompt::PromptSelection;
|
||||
|
||||
@@ -2,9 +2,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::settings::{AISettings, InputSettings, TerminalSpacing};
|
||||
use galaxyui::{units::Pixels, AppContext, SingletonEntity};
|
||||
use settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use settings::{
|
||||
macros::define_settings_group, Setting, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, Setting, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
define_settings_group!(SharedSessionSettings, settings: [
|
||||
onboarding_block_shown: SessionSharingOnboardingBlockShown {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
define_settings_group!(UndoCloseSettings, settings: [
|
||||
enabled: UndoCloseEnabled {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
pub use crate::util::openable_file_type::EditorLayout;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use galaxyui::{AppContext, WindowId};
|
||||
use settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
define_settings_group!(WindowSettings, settings: [
|
||||
background_blur_radius: BackgroundBlurRadius {
|
||||
|
||||
@@ -2,9 +2,7 @@ use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
use galaxy_core::ui::theme::AnsiColorIdentifier;
|
||||
use settings::{
|
||||
macros::define_settings_group, SupportedPlatforms, SyncToCloud,
|
||||
};
|
||||
use settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
|
||||
|
||||
#[derive(
|
||||
Default,
|
||||
|
||||
Reference in New Issue
Block a user