Path: blob/main/extensions/copilot/src/extension/chatSessions/claude/node/claudePromptResolver.ts
13405 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import Anthropic from '@anthropic-ai/sdk';6import type * as vscode from 'vscode';7import { isLocation } from '../../../../util/common/types';8import { URI } from '../../../../util/vs/base/common/uri';9import { ChatReferenceBinaryData } from '../../../../vscodeTypes';10import { toAnthropicImageMediaType } from './sessionParser/claudeSessionSchema';1112// #region Prompt Resolution1314function uriToString(uri: URI): string {15return uri.scheme === 'file' ? uri.fsPath : uri.toString();16}1718/**19* Converts a `vscode.ChatRequest` into an array of Anthropic content blocks.20*21* - Inline references (`ref.range`) are substituted directly into the prompt text.22* - Non-inline references are appended as a `<system-reminder>` text block.23* - Binary image references become `image` content blocks.24* - Slash-command prompts (starting with `/`) are passed through unmodified.25*/26export async function resolvePromptToContentBlocks(request: vscode.ChatRequest): Promise<Anthropic.ContentBlockParam[]> {27if (request.prompt.startsWith('/')) {28return [{ type: 'text', text: request.prompt }];29}3031let prompt = request.prompt;32const imageBlocks: Anthropic.ContentBlockParam[] = [];33const extraRefsTexts: string[] = [];3435// Sort references with inline ranges by descending start position so that36// earlier replacements don't shift the indices of later ones.37const sortedRefs = [...request.references].sort((a, b) => {38const aStart = a.range?.[0] ?? -1;39const bStart = b.range?.[0] ?? -1;40return bStart - aStart;41});4243for (const ref of sortedRefs) {44let refValue = ref.value;45if (refValue instanceof ChatReferenceBinaryData) {46const mediaType = toAnthropicImageMediaType(refValue.mimeType);47if (mediaType) {48const data = await refValue.data();49imageBlocks.push({50type: 'image',51source: {52type: 'base64',53data: Buffer.from(data).toString('base64'),54media_type: mediaType55}56});57continue;58}59if (!refValue.reference) {60continue;61}62refValue = refValue.reference;63}6465const valueText = URI.isUri(refValue)66? uriToString(refValue)67: isLocation(refValue)68? `${uriToString(refValue.uri)}:${refValue.range.start.line + 1}`69: undefined;70if (valueText) {71if (ref.range) {72prompt = prompt.slice(0, ref.range[0]) + valueText + prompt.slice(ref.range[1]);73} else {74extraRefsTexts.push(`- ${valueText}`);75}76}77}7879const contentBlocks: Anthropic.ContentBlockParam[] = [80{ type: 'text', text: request.command ? `/${request.command} ${prompt}` : prompt },81...imageBlocks,82];8384if (extraRefsTexts.length > 0) {85contentBlocks.push({86type: 'text',87text: `<system-reminder>\nThe user provided the following references:\n${extraRefsTexts.join('\n')}\n\nIMPORTANT: this context may or may not be relevant to your tasks. You should not respond to this context unless it is highly relevant to your task.\n</system-reminder>`88});89}9091return contentBlocks;92}9394// #endregion959697