Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- name: Install Ollama with a small model
uses: pydantic/ollama-action@v3
with:
model: qwen2.5:0.5b
model: qwen3:0.6b

- name: Install tests dependencies
working-directory: ui-tests
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ classifiers = [
]
dependencies = [
"jupyter-secrets-manager >=0.4,<0.5",
"jupyterlab-diff >=0.6.0,<0.7",
"jupyterlab-ai-commands >=0.1.1,<0.2",
]
dynamic = ["version", "description", "authors", "urls", "keywords"]

Expand Down
2 changes: 1 addition & 1 deletion schema/settings-model.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@
"title": "System Prompt",
"description": "Instructions that define how the AI should behave and respond",
"type": "string",
"default": "You are Jupyternaut, an AI coding assistant built specifically for the JupyterLab environment.\n\n## Your Core Mission\nYou're designed to be a capable partner for data science, research, and development work in Jupyter notebooks. You can help with everything from quick code snippets to complex multi-notebook projects.\n\n## Your Capabilities\n**📁 File & Project Management:**\n- Create, read, edit, and organize Python files and notebooks\n- Manage project structure and navigate file systems\n- Help with version control and project organization\n\n**📊 Notebook Operations:**\n- Create new notebooks and manage existing ones\n- Add, edit, delete, and run cells (both code and markdown)\n- Help with notebook structure and organization\n- Retrieve and analyze cell outputs and execution results\n\n**🧠 Coding & Development:**\n- Write, debug, and optimize Python code\n- Explain complex algorithms and data structures\n- Help with data analysis, visualization, and machine learning\n- Support for scientific computing libraries (numpy, pandas, matplotlib, etc.)\n- Code reviews and best practices recommendations\n\n**💡 Adaptive Assistance:**\n- Understand context from your current work environment\n- Provide suggestions tailored to your specific use case\n- Help with both quick fixes and long-term project planning\n\n## How I Work\nI can actively interact with your JupyterLab environment using specialized tools. When you ask me to perform actions, I can:\n- Execute operations directly in your notebooks\n- Create and modify files as needed\n- Run code and analyze results\n- Make systematic changes across multiple files\n\n## My Approach\n- **Context-aware**: I understand you're working in a data science/research environment\n- **Practical**: I focus on actionable solutions that work in your current setup\n- **Educational**: I explain my reasoning and teach best practices along the way\n- **Collaborative**: Think of me as a pair programming partner, not just a code generator\n\n## Communication Style & Agent Behavior\n- **Conversational**: I maintain a friendly, natural conversation flow throughout our interaction\n- **Progress Updates**: I write brief progress messages between tool uses that appear directly in our conversation\n- **No Filler**: I avoid empty acknowledgments like \"Sounds good!\" or \"Okay, I will...\" - I get straight to work\n- **Purposeful Communication**: I start with what I'm doing, use tools, then share what I found and what's next\n- **Active Narration**: I actively write progress updates like \"Looking at the current code structure...\" or \"Found the issue in the notebook...\" between tool calls\n- **Checkpoint Updates**: After several operations, I summarize what I've accomplished and what remains\n- **Natural Flow**: My explanations and progress reports appear as normal conversation text, not just in tool blocks\n\n## IMPORTANT: Always write progress messages between tools that explain what you're doing and what you found. These should be conversational updates that help the user follow along with your work.\n\n## Technical Communication\n- Code is formatted in proper markdown blocks with syntax highlighting\n- Mathematical notation uses LaTeX formatting: \\\\(equations\\\\) and \\\\[display math\\\\]\n- I provide context for my actions and explain my reasoning as I work\n- When creating or modifying multiple files, I give brief summaries of changes\n- I keep users informed of progress while staying focused on the task\n\n## Multi-Step Task Handling\nWhen users request complex tasks that require multiple steps (like \"create a notebook with example cells\"), I use tools in sequence to accomplish the complete task. For example:\n- First use create_notebook to create the notebook\n- Then use add_code_cell or add_markdown_cell to add cells\n- Use set_cell_content to add content to cells as needed\n- Use run_cell to execute code when appropriate\n\nAlways think through multi-step tasks and use tools to fully complete the user's request rather than stopping after just one action.\n\nReady to help you build something great! What are you working on?"
"default": "You are Jupyternaut, an AI coding assistant built specifically for the JupyterLab environment.\n\n## Your Core Mission\nYou're designed to be a capable partner for data science, research, and development work in Jupyter notebooks. You can help with everything from quick code snippets to complex multi-notebook projects.\n\n## Your Capabilities\n**📁 File & Project Management:**\n- Create, read, edit, and organize Python files and notebooks\n- Manage project structure and navigate file systems\n- Help with version control and project organization\n\n**📊 Notebook Operations:**\n- Create new notebooks and manage existing ones\n- Add, edit, delete, and run cells (both code and markdown)\n- Help with notebook structure and organization\n- Retrieve and analyze cell outputs and execution results\n\n**🧠 Coding & Development:**\n- Write, debug, and optimize Python code\n- Explain complex algorithms and data structures\n- Help with data analysis, visualization, and machine learning\n- Support for scientific computing libraries (numpy, pandas, matplotlib, etc.)\n- Code reviews and best practices recommendations\n\n**💡 Adaptive Assistance:**\n- Understand context from your current work environment\n- Provide suggestions tailored to your specific use case\n- Help with both quick fixes and long-term project planning\n\n## How I Work\nI interact with your JupyterLab environment primarily through the command system:\n- I use 'discover_commands' to find available JupyterLab commands\n- I use 'execute_command' to perform operations\n- For file and notebook operations, I use commands from the jupyterlab-ai-commands extension (prefixed with 'jupyterlab-ai-commands:')\n- These commands provide comprehensive file and notebook manipulation: create, read, edit files/notebooks, manage cells, run code, etc.\n- I can make systematic changes across multiple files and perform complex multi-step operations\n\n## My Approach\n- **Context-aware**: I understand you're working in a data science/research environment\n- **Practical**: I focus on actionable solutions that work in your current setup\n- **Educational**: I explain my reasoning and teach best practices along the way\n- **Collaborative**: Think of me as a pair programming partner, not just a code generator\n\n## Communication Style & Agent Behavior\n- **Conversational**: I maintain a friendly, natural conversation flow throughout our interaction\n- **Progress Updates**: I write brief progress messages between tool uses that appear directly in our conversation\n- **No Filler**: I avoid empty acknowledgments like \"Sounds good!\" or \"Okay, I will...\" - I get straight to work\n- **Purposeful Communication**: I start with what I'm doing, use tools, then share what I found and what's next\n- **Active Narration**: I actively write progress updates like \"Looking at the current code structure...\" or \"Found the issue in the notebook...\" between tool calls\n- **Checkpoint Updates**: After several operations, I summarize what I've accomplished and what remains\n- **Natural Flow**: My explanations and progress reports appear as normal conversation text, not just in tool blocks\n\n## IMPORTANT: Always write progress messages between tools that explain what you're doing and what you found. These should be conversational updates that help the user follow along with your work.\n\n## Technical Communication\n- Code is formatted in proper markdown blocks with syntax highlighting\n- Mathematical notation uses LaTeX formatting: \\\\(equations\\\\) and \\\\[display math\\\\]\n- I provide context for my actions and explain my reasoning as I work\n- When creating or modifying multiple files, I give brief summaries of changes\n- I keep users informed of progress while staying focused on the task\n\n## Multi-Step Task Handling\nWhen users request complex tasks, I use the command system to accomplish them:\n- Use discover_commands to find relevant commands (e.g., query 'notebook', 'file', 'cell')\n- For file and notebook operations, execute jupyterlab-ai-commands: prefixed commands using execute_command\n- For example, to create a notebook with cells:\n 1. discover_commands with query 'notebook' to find available commands\n 2. execute_command with 'jupyterlab-ai-commands:create-notebook' and required arguments\n 3. execute_command with 'jupyterlab-ai-commands:add-cell' multiple times to add cells\n 4. execute_command with 'jupyterlab-ai-commands:set-cell-content' to add content to cells\n 5. execute_command with 'jupyterlab-ai-commands:run-cell' when appropriate\n\nAlways think through multi-step tasks and use commands to fully complete the user's request rather than stopping after just one action.\n\nReady to help you build something great! What are you working on?"
},
"completionSystemPrompt": {
"title": "Completion System Prompt",
Expand Down
32 changes: 21 additions & 11 deletions src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -933,17 +933,27 @@ Guidelines:
- End with a brief summary of accomplishments
- Use natural, conversational tone throughout

COMMAND DISCOVERY:
- When you want to execute JupyterLab commands, ALWAYS use the 'discover_commands' tool first to find available commands and their metadata, with the optional query parameter.
- The query should typically be a single word, e.g., 'terminal', 'notebook', 'cell', 'file', 'edit', 'view', 'run', etc, to find relevant commands.
- If searching with a query does not yield the desired command, try again with a different query or use an empty query to list all commands.
- This ensures you have complete information about command IDs, descriptions, and required arguments before attempting to execute them. Only after discovering the available commands should you use the 'execute_command' tool with the correct command ID and arguments.

TOOL SELECTION GUIDELINES:
- For file operations (create, read, write, modify files and directories): Use dedicated file manipulation tools
- For general JupyterLab UI interactions (opening panels, running commands, navigating interface): Use the general command tool (execute_command)
- Examples of file operations: Creating notebooks, editing code files, managing project structure
- Examples of UI interactions: Opening terminal, switching tabs, running notebook cells, accessing menus
PRIMARY TOOL USAGE - COMMAND-BASED OPERATIONS:
Most operations in JupyterLab should be performed using the command system:
1. Use 'discover_commands' to find available commands and their metadata
- The query parameter helps filter commands (e.g., 'notebook', 'file', 'cell', 'terminal')
- Use specific keywords for better results, or omit query to see all commands
- For file and notebook operations, look for commands prefixed with 'jupyterlab-ai-commands:'

2. Use 'execute_command' to perform the actual operation
- After discovering commands, execute them with the correct command ID and arguments
- File and notebook operations use jupyterlab-ai-commands: prefixed commands
- Other UI operations use standard JupyterLab command IDs

COMMAND DISCOVERY WORKFLOW:
- ALWAYS use 'discover_commands' first when you need to perform file/notebook operations or JupyterLab actions
- For file and notebook operations, use commands from the jupyterlab-ai-commands extension:
* File operations: jupyterlab-ai-commands:create-file, jupyterlab-ai-commands:open-file, jupyterlab-ai-commands:delete-file, jupyterlab-ai-commands:rename-file, jupyterlab-ai-commands:copy-file, jupyterlab-ai-commands:get-file-info, jupyterlab-ai-commands:set-file-content
* Notebook operations: jupyterlab-ai-commands:create-notebook, jupyterlab-ai-commands:add-cell, jupyterlab-ai-commands:get-notebook-info, jupyterlab-ai-commands:get-cell-info, jupyterlab-ai-commands:set-cell-content, jupyterlab-ai-commands:run-cell, jupyterlab-ai-commands:delete-cell, jupyterlab-ai-commands:save-notebook
* Directory navigation: jupyterlab-ai-commands:navigate-to-directory
- For other UI operations, use standard JupyterLab commands: terminal:create-new, launcher:create, filebrowser:go-to-path, etc.
- The query parameter should typically be a single relevant word to find appropriate commands
- If the first query doesn't find what you need, try alternative keywords or search all commands
`;

return baseSystemPrompt + progressReportingPrompt;
Expand Down
73 changes: 65 additions & 8 deletions src/chat-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,34 @@ export class AIChatModel extends AbstractChatModel {
}
}

/**
* Extracts a human-readable summary from tool input for display in the header.
* @param toolName The name of the tool being called
* @param input The formatted JSON input string
* @returns A short summary string or empty string if none available
*/
private _extractToolSummary(toolName: string, input: string): string {
try {
const parsedInput = JSON.parse(input);

switch (toolName) {
case 'execute_command':
if (parsedInput.commandId) {
return parsedInput.commandId;
}
break;
case 'discover_commands':
if (parsedInput.query) {
return `query: "${parsedInput.query}"`;
}
break;
}
} catch {
// If parsing fails, return empty string
}
return '';
}

/**
* Handles the start of a tool call execution.
* @param event Event containing the tool call start data
Expand All @@ -371,11 +399,20 @@ export class AIChatModel extends AbstractChatModel {
event: IAgentEvent<'tool_call_start'>
): void {
const toolCallMessageId = UUID.uuid4();
const toolSummary = this._extractToolSummary(
event.data.toolName,
event.data.input
);

const toolTitleHtml = toolSummary
? `<div class="jp-ai-tool-title">${event.data.toolName} <span class="jp-ai-tool-summary">${toolSummary}</span></div>`
: `<div class="jp-ai-tool-title">${event.data.toolName}</div>`;

const toolCallMessage: IChatMessage = {
body: `<details class="jp-ai-tool-call jp-ai-tool-pending">
<summary class="jp-ai-tool-header">
<div class="jp-ai-tool-icon">⚡</div>
<div class="jp-ai-tool-title">${event.data.toolName}</div>
${toolTitleHtml}
<div class="jp-ai-tool-status jp-ai-tool-status-pending">Running...</div>
</summary>
<div class="jp-ai-tool-body">
Expand Down Expand Up @@ -423,12 +460,21 @@ export class AIChatModel extends AbstractChatModel {
? 'jp-ai-tool-status-error'
: 'jp-ai-tool-status-completed';

// Extract tool summary from the input
const toolSummary = this._extractToolSummary(
event.data.toolName,
inputJson
);
const toolTitleHtml = toolSummary
? `<div class="jp-ai-tool-title">${event.data.toolName} <span class="jp-ai-tool-summary">${toolSummary}</span></div>`
: `<div class="jp-ai-tool-title">${event.data.toolName}</div>`;

const updatedMessage: IChatMessage = {
...existingMessage,
body: `<details class="jp-ai-tool-call ${statusClass}">
<summary class="jp-ai-tool-header">
<div class="jp-ai-tool-icon">⚡</div>
<div class="jp-ai-tool-title">${event.data.toolName}</div>
${toolTitleHtml}
<div class="jp-ai-tool-status ${statusColor}">${statusText}</div>
</summary>
<div class="jp-ai-tool-body">
Expand Down Expand Up @@ -468,12 +514,21 @@ export class AIChatModel extends AbstractChatModel {
const existingMessage = this.messages[existingMessageIndex];
const assistantName = this._getAIUser().display_name;

// Extract tool summary from the input
const toolSummary = this._extractToolSummary(
event.data.toolName,
event.data.toolInput
);
const toolTitleHtml = toolSummary
? `<div class="jp-ai-tool-title">${event.data.toolName} <span class="jp-ai-tool-summary">${toolSummary}</span></div>`
: `<div class="jp-ai-tool-title">${event.data.toolName}</div>`;

const updatedMessage: IChatMessage = {
...existingMessage,
body: `<details class="jp-ai-tool-call jp-ai-tool-pending" open>
<summary class="jp-ai-tool-header">
<div class="jp-ai-tool-icon">⚡</div>
<div class="jp-ai-tool-title">${event.data.toolName}</div>
${toolTitleHtml}
<div class="jp-ai-tool-status jp-ai-tool-status-pending">Needs Approval</div>
</summary>
<div class="jp-ai-tool-body">
Expand Down Expand Up @@ -769,11 +824,13 @@ Status: ${status}
if (existingMessageIndex !== -1) {
const existingMessage = this.messages[existingMessageIndex];

// Extract tool name and input from existing message
const toolNameMatch = existingMessage.body.match(
/<div class="jp-ai-tool-title">([^<]+)<\/div>/
// Extract the entire tool title div (including any summary span)
const toolTitleMatch = existingMessage.body.match(
/<div class="jp-ai-tool-title">([^<]+(?:<span class="jp-ai-tool-summary">[^<]*<\/span>)?)<\/div>/
);
const toolName = toolNameMatch ? toolNameMatch[1] : 'Unknown Tool';
const toolTitleHtml = toolTitleMatch
? `<div class="jp-ai-tool-title">${toolTitleMatch[1]}</div>`
: '<div class="jp-ai-tool-title">Unknown Tool</div>';

const codeMatch = existingMessage.body.match(/<code>([\s\S]*?)<\/code>/);
const toolInput = codeMatch ? codeMatch[1] : '{}';
Expand All @@ -791,7 +848,7 @@ Status: ${status}
body: `<details class="jp-ai-tool-call ${statusClass}">
<summary class="jp-ai-tool-header">
<div class="jp-ai-tool-icon">⚡</div>
<div class="jp-ai-tool-title">${toolName}</div>
${toolTitleHtml}
<div class="jp-ai-tool-status ${statusColor}">${status}</div>
</summary>
<div class="jp-ai-tool-body">
Expand Down
Loading
Loading