Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
4013b09
Initial predefined assistant actions
nstrayer Nov 13, 2025
4105128
Add option for user to write a custom prompt.
nstrayer Nov 13, 2025
1c3dd79
Add codicons to improve scannability
nstrayer Nov 13, 2025
4a10827
cleanup
nstrayer Nov 13, 2025
64f6b1e
Add error handling and also cleanup quick pick disposal pattern
nstrayer Nov 13, 2025
876e651
Move ask assistant action to its own file
nstrayer Nov 13, 2025
5bece06
Add AI generated suggestions on request
nstrayer Nov 13, 2025
0372e2a
Enhance AskAssistantAction to include loading state and improved erro…
nstrayer Nov 13, 2025
e02643a
Add cancelization token to avoid completing assistant request when th…
nstrayer Nov 13, 2025
2809183
fix bad typing
nstrayer Nov 13, 2025
b836c45
Update assistant action to use NotebookAction2 and make NotebookActio…
nstrayer Nov 13, 2025
b18ff84
Add common-sense limit to custom prompt length
nstrayer Nov 13, 2025
bb54ef4
Remove second ai sparkle
nstrayer Nov 13, 2025
08a3308
Centralized notebook context serialization so filtering and XML gener…
nstrayer Nov 13, 2025
9ef9e45
Fix disposable leak in the ask assistant action quickpick logic
nstrayer Nov 13, 2025
07b9958
Inline small function
nstrayer Nov 13, 2025
19af4af
Switch to same serialization protocol for inline chat as other notebo…
nstrayer Nov 13, 2025
0485a60
More type safety updates
nstrayer Nov 13, 2025
9d458df
Dont duplicate the MAX_CELLS_FOR_ALL_CELLS_CONTEXT constant
nstrayer Nov 14, 2025
0129be8
Properly cleanup CancellationToken
nstrayer Nov 14, 2025
1468778
Remove dead function
nstrayer Nov 14, 2025
efecf6e
Fix spacing
nstrayer Nov 14, 2025
69bad6c
No any, please!
nstrayer Nov 14, 2025
6f2549a
Update predesigned prompts
nstrayer Nov 14, 2025
87938c3
Add configurable preference for faster models for suggested actions
nstrayer Nov 14, 2025
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
24 changes: 24 additions & 0 deletions extensions/positron-assistant/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,30 @@
"experimental"
]
},
"positron.assistant.notebookSuggestions.model": {
"type": "array",
"default": [
"haiku",
"mini"
],
"markdownDescription": "%configuration.notebookSuggestions.model.description%",
"items": {
"type": "string"
},
"examples": [
[
"haiku",
"mini"
],
[
"Claude Sonnet 4.5",
"GPT-5"
]
],
"tags": [
"experimental"
]
},
"positron.assistant.providerVariables.bedrock": {
"type": "object",
"default": {},
Expand Down
1 change: 1 addition & 0 deletions extensions/positron-assistant/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,6 @@
"configuration.filterModels.description": "A list of [glob patterns](https://aka.ms/vscode-glob-patterns) to filter which language models are available in Positron Assistant across all providers.\n\nOnly models with an ID or name matching at least one of the specified patterns will appear as available options. Models that do not match any pattern will be hidden.\n\nExamples:\n- `Claude*` shows only models starting with 'Claude'\n- `GPT-5` shows only models named 'GPT-5'\n- `**/google/gemini-*` shows only models with '/google/gemini-' in the ID\n- `**/*` shows all models",
"configuration.preferredModel.description": "A preferred model ID or name (partial match supported) to use if available in Positron Assistant for the current provider.\n\nTakes precedence over `#positron.assistant.defaultModels#`.\n\nRequires a restart to take effect.\n\nExamples:\n- `Claude Sonnet 4.5` prefers the model named 'Claude Sonnet 4.5'\n- `GPT-5` prefers the model named 'GPT-5'",
"configuration.defaultModels.description": "A mapping of provider IDs to default model IDs or names (partial match supported) to use for that provider in Positron Assistant.\n\n`#positron.assistant.preferredModel#` takes precedence over this setting.\n\nRequires a restart to take effect.\n\nExample: Item `anthropic-api` and Value `Claude Sonnet 4.5` sets the default model for Anthropic to 'Claude Sonnet 4.5'",
"configuration.notebookSuggestions.model.description": "An ordered array of model patterns to try when generating AI suggestions for Positron Notebooks. Patterns are tried in order until a match is found.\n\nEach pattern supports partial matching on model ID or name (case-insensitive). Default is `[\"haiku\", \"mini\"]` which tries to find a model with \"haiku\" in the name first, then tries \"mini\" if not found. If no patterns match or the array is empty, falls back to the current chat session model, then the current provider's model, then the first available model.\n\nExamples:\n- `[\"haiku\", \"mini\"]` (default) tries to find a model with \"haiku\" in the name first, then tries \"mini\" if not found\n- `[\"Claude Sonnet 4.5\", \"GPT-5\"]` tries \"Claude Sonnet 4.5\" first, then \"GPT-5\"\n- `[]` disables pattern matching and uses the default fallback behavior",
"configuration.providerVariables.bedrock.description": "Variables used to configure advanced settings for Bedrock in Positron Assistant.\n\nRequires a restart to take effect.\n\nExample: to set the AWS region and profile for Amazon Bedrock, add items with keys `AWS_REGION` and `AWS_PROFILE`."
}
6 changes: 3 additions & 3 deletions extensions/positron-assistant/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
import * as xml from './xml.js';
import * as vscode from 'vscode';
import * as positron from 'positron';
import { getAttachedNotebookContext, isStreamingEditsEnabled, ParticipantID } from './participants.js';
import { isStreamingEditsEnabled, ParticipantID } from './participants.js';
import { hasAttachedNotebookContext } from './tools/notebookUtils.js';
import { MARKDOWN_DIR, TOOL_TAG_REQUIRES_ACTIVE_SESSION, TOOL_TAG_REQUIRES_WORKSPACE, TOOL_TAG_REQUIRES_NOTEBOOK } from './constants.js';
import { isWorkspaceOpen } from './utils.js';
import { PositronAssistantToolName } from './types.js';
Expand Down Expand Up @@ -174,8 +175,7 @@ export async function getEnabledTools(
}

// Check if a notebook is attached as context and has an active editor
const notebookContext = await getAttachedNotebookContext(request);
const hasActiveNotebook = !!notebookContext;
const hasActiveNotebook = await hasAttachedNotebookContext(request);

// Define more readable variables for filtering.
const inChatPane = request.location2 === undefined;
Expand Down
27 changes: 27 additions & 0 deletions extensions/positron-assistant/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { registerCopilotAuthProvider } from './authProvider.js';
import { ALL_DOCUMENTS_SELECTOR, DEFAULT_MAX_TOKEN_OUTPUT } from './constants.js';
import { registerCodeActionProvider } from './codeActions.js';
import { generateCommitMessage } from './git.js';
import { generateNotebookSuggestions } from './notebookSuggestions.js';
import { TokenUsage, TokenTracker } from './tokens.js';
import { exportChatToUserSpecifiedLocation, exportChatToFileInWorkspace } from './export.js';
import { AnthropicLanguageModel } from './anthropic.js';
Expand Down Expand Up @@ -229,6 +230,31 @@ function registerGenerateCommitMessageCommand(
);
}

function registerGenerateNotebookSuggestionsCommand(
context: vscode.ExtensionContext,
participantService: ParticipantService,
log: vscode.LogOutputChannel,
) {
context.subscriptions.push(
vscode.commands.registerCommand(
'positron-assistant.generateNotebookSuggestions',
async (notebookUri: string, token?: vscode.CancellationToken) => {
// Create a token source only if no token is provided
let tokenSource: vscode.CancellationTokenSource | undefined;
// If there is no provided token, create a new one and also
// assign it to the tokenSource so we know to dispose it later.
const cancellationToken = token || (tokenSource = new vscode.CancellationTokenSource()).token;
try {
return await generateNotebookSuggestions(notebookUri, participantService, log, cancellationToken);
} finally {
// We only want to dispose the token if we created it
tokenSource?.dispose();
}
}
)
);
}

function registerExportChatCommands(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('positron-assistant.exportChatToFileInWorkspace', async () => {
Expand Down Expand Up @@ -307,6 +333,7 @@ function registerAssistant(context: vscode.ExtensionContext) {
// Commands
registerConfigureModelsCommand(context, storage);
registerGenerateCommitMessageCommand(context, participantService, log);
registerGenerateNotebookSuggestionsCommand(context, participantService, log);
registerExportChatCommands(context);
registerToggleInlineCompletionsCommand(context);
registerPromptManagement(context);
Expand Down
113 changes: 113 additions & 0 deletions extensions/positron-assistant/src/md/prompts/notebook/suggestions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
You are an AI assistant for Jupyter notebooks in Positron. Your task is to analyze the provided notebook context and suggest 3-5 specific, actionable tasks that the user might want to perform with their notebook.

## Guidelines

1. **Be Contextual**: Base suggestions on the actual state of the notebook (execution status, errors, outputs, cell content, etc.)
2. **Be Specific**: Suggestions should reference specific aspects of the notebook (e.g., "Debug the error in cell 5" not just "Debug errors")
3. **Be Actionable**: Each suggestion should be something the assistant can help with immediately
4. **Vary Modes**: Use appropriate modes:
- `ask`: For questions, explanations, or information requests
- `edit`: For code modifications, refactoring, or adding content
- `agent`: For complex tasks that may require multiple steps or tool usage
5. **Prioritize Issues**: If there are errors or failed cells, prioritize debugging suggestions
6. **Consider Workflow**: Suggest next logical steps based on what has been executed

## Output Format

You MUST return only valid JSON in the output, and nothing else. Format the response as an array of objects with the following structure:

```json
[
{
"label": "Brief action title (max 50 chars)",
"detail": "Longer explanation of what this action will do",
"query": "The full prompt that will be sent to the assistant to execute this action",
"mode": "ask" | "edit" | "agent"
}
]
```

## Examples

### Example 1: Notebook with Failed Cell

Context: Notebook has 10 cells, cell 5 failed with a NameError, 3 cells selected

```json
[
{
"label": "Debug the NameError in cell 5",
"detail": "Investigate and fix the undefined variable causing the error",
"query": "Can you help me debug the NameError in cell 5 and suggest a fix?",
"mode": "agent"
},
{
"label": "Explain the selected cells",
"detail": "Get a detailed explanation of what the selected code does",
"query": "Can you explain what the code in the selected cells does?",
"mode": "ask"
},
{
"label": "Add error handling",
"detail": "Add try-catch blocks to make the code more robust",
"query": "Can you add error handling to the selected cells?",
"mode": "edit"
}
]
```

### Example 2: Empty Notebook

Context: Notebook has 0 cells, Python kernel

```json
[
{
"label": "Get started with data analysis",
"detail": "Create a basic data analysis workflow with pandas",
"query": "Can you help me set up a basic data analysis workflow with pandas? Please create cells for loading data, exploring it, and visualizing it.",
"mode": "agent"
},
{
"label": "Create a data science template",
"detail": "Set up a standard data science notebook structure",
"query": "Can you create a template notebook structure for data science work with sections for imports, data loading, exploration, modeling, and conclusions?",
"mode": "edit"
}
]
```

### Example 3: Notebook with Outputs

Context: Notebook has 15 cells, all executed successfully, last cell shows a matplotlib plot, 0 cells selected

```json
[
{
"label": "Explain the visualization",
"detail": "Get insights about the plot in the last cell",
"query": "Can you explain what the visualization in the last cell shows and what insights we can draw from it?",
"mode": "ask"
},
{
"label": "Improve the plot aesthetics",
"detail": "Enhance the visual appearance of the matplotlib plot",
"query": "Can you suggest improvements to make the plot in the last cell more visually appealing and publication-ready?",
"mode": "edit"
},
{
"label": "Add summary statistics",
"detail": "Create a new cell with statistical analysis of the plotted data",
"query": "Can you add a cell that calculates and displays summary statistics for the data shown in the plot?",
"mode": "agent"
},
{
"label": "Export results to file",
"detail": "Save the plot and data to files",
"query": "Can you help me export the visualization and underlying data to files?",
"mode": "agent"
}
]
```

Remember: Return ONLY valid JSON. Do not include any explanatory text, markdown formatting, or additional commentary.
3 changes: 2 additions & 1 deletion extensions/positron-assistant/src/notebookContextFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as positron from 'positron';
* Notebooks with more cells will have filtering applied to avoid consuming
* too much context space.
*/
const MAX_CELLS_FOR_ALL_CELLS_CONTEXT = 20;
export const MAX_CELLS_FOR_ALL_CELLS_CONTEXT = 20;

/**
* Default window size for sliding window filtering.
Expand Down Expand Up @@ -80,3 +80,4 @@ export function filterNotebookContext(
allCells: filteredCells
};
}

Loading