Skip to content
Merged
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
27 changes: 25 additions & 2 deletions extensions/positron-assistant/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as xml from './xml.js';
import * as vscode from 'vscode';
import * as positron from 'positron';
import { isStreamingEditsEnabled, ParticipantID } from './participants.js';
import { hasAttachedNotebookContext } from './tools/notebookUtils.js';
import { hasAttachedNotebookContext, getAttachedNotebookContext, SerializedNotebookContext } 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 @@ -58,7 +58,11 @@ export class PositronAssistantApi {
const activeSessions = await positron.runtime.getActiveSessions();
const sessions = activeSessions.map(session => session.runtimeMetadata);
const streamingEdits = isStreamingEditsEnabled();
let prompt = PromptRenderer.renderModePrompt(mode, { sessions, request, streamingEdits }).content;

// Get notebook context if available
const notebookContext = await getAttachedNotebookContext(request);

let prompt = PromptRenderer.renderModePrompt(mode, { sessions, request, streamingEdits, notebookContext }).content;

// Get the IDE context for the request.
const positronContext = await positron.ai.getPositronChatContext(request);
Expand Down Expand Up @@ -133,6 +137,18 @@ export class PositronAssistantApi {
}
}

/**
* Copilot notebook tool names that should be disabled when Positron notebook mode is active.
* These tools conflict with Positron's specialized notebook tools.
*/
const COPILOT_NOTEBOOK_TOOLS = new Set([
'copilot_editNotebook',
'copilot_getNotebookSummary',
'copilot_runNotebookCell',
'copilot_readNotebookCellOutput',
'copilot_createNewJupyterNotebook',
]);

/**
* Gets the set of enabled tools for a chat request.
*
Expand Down Expand Up @@ -330,6 +346,13 @@ export function getEnabledTools(
const alwaysIncludeCopilotTools = vscode.workspace.getConfiguration('positron.assistant').get('alwaysIncludeCopilotTools', false);
// Check if the tool is provided by Copilot.
const copilotTool = tool.source instanceof vscode.LanguageModelToolExtensionSource && tool.source.id === 'GitHub.copilot-chat';

// Disable Copilot notebook tools when Positron notebook mode is active
// to avoid conflicts with Positron's specialized notebook tools.
if (copilotTool && hasActiveNotebook && COPILOT_NOTEBOOK_TOOLS.has(tool.name)) {
continue;
}

// Check if the user is signed into Copilot.
let copilotEnabled;
try {
Expand Down
9 changes: 2 additions & 7 deletions extensions/positron-assistant/src/participants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -760,13 +760,8 @@ export class PositronAssistantChatParticipant extends PositronAssistantParticipa
const activeSessions = await positron.runtime.getActiveSessions();
const sessions = activeSessions.map(session => session.runtimeMetadata);

// Get notebook context if available, with error handling
let notebookContext: SerializedNotebookContext | undefined;
try {
notebookContext = await getAttachedNotebookContext(request);
} catch (err) {
log.error('[PositronAssistantChatParticipant] Error checking notebook context:', err);
}
// Get notebook context if available
const notebookContext = await getAttachedNotebookContext(request);

// Render prompt with notebook context
const prompt = PromptRenderer.renderModePrompt(positron.PositronChatMode.Ask, {
Expand Down
26 changes: 20 additions & 6 deletions extensions/positron-assistant/src/tools/notebookUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as positron from 'positron';
import * as xml from '../xml.js';
import { calculateSlidingWindow, filterNotebookContext, MAX_CELLS_FOR_ALL_CELLS_CONTEXT } from '../notebookContextFilter.js';
import { isRuntimeSessionReference } from '../utils.js';
import { log } from '../extension.js';

/**
* Maximum preview length per cell for confirmations (characters)
Expand Down Expand Up @@ -537,16 +538,29 @@ export function hasAttachedNotebookContext(
* 2. That notebook's URI is attached as context
*
* Applies filtering to limit context size for large notebooks.
*
* This function handles errors internally and will return `undefined` if any error
* occurs during context retrieval, filtering, or serialization. Errors are logged
* automatically. Callers do not need to wrap this function in try-catch blocks.
*
* @param request The chat request to check for attached notebook context
* @returns The serialized notebook context if available, or `undefined` if no context
* is available or if an error occurs
*/
export async function getAttachedNotebookContext(
request: vscode.ChatRequest
): Promise<SerializedNotebookContext | undefined> {
const activeContext = await getRawAttachedNotebookContext(request);
if (!activeContext) {
try {
const activeContext = await getRawAttachedNotebookContext(request);
if (!activeContext) {
return undefined;
}

// Apply filtering before returning context
const filteredContext = filterNotebookContext(activeContext);
return serializeNotebookContext(filteredContext);
} catch (err) {
log.error('[getAttachedNotebookContext] Error getting notebook context:', err);
return undefined;
}

// Apply filtering before returning context
const filteredContext = filterNotebookContext(activeContext);
return serializeNotebookContext(filteredContext);
}
Loading