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
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import './anthropicPrompts';
import './familyHPrompts';
import './geminiPrompts';
import './kimiPrompts';
import './minimaxPrompts';
import './vscModelPrompts';
// vscModelPrompts must be imported before gpt5Prompt to ensure VSC model prompt resolvers are registered first.
Expand All @@ -21,4 +22,3 @@ import './openai/gpt5Prompt';
import './openai/hiddenModelMPrompt';
import './xAIPrompts';
import './zaiPrompts';

153 changes: 153 additions & 0 deletions extensions/copilot/src/extension/prompts/node/agent/kimiPrompts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { PromptElement, PromptSizing } from '@vscode/prompt-tsx';
import { isKimiFamily } from '../../../../platform/endpoint/common/chatModelCapabilities';
import { IChatEndpoint } from '../../../../platform/networking/common/networking';
import { agenticBrowserTools, ToolName } from '../../../tools/common/toolNames';
import { InstructionMessage } from '../base/instructionMessage';
import { ResponseTranslationRules } from '../base/responseTranslationRules';
import { Tag } from '../base/tag';
import { EXISTING_CODE_MARKER } from '../panel/codeBlockFormattingRules';
import { ResponseRenderingRules } from '../panel/editorIntegrationRules';
import { ApplyPatchInstructions, CodesearchModeInstructions, DefaultAgentPromptProps, DefaultReminderInstructions, detectToolCapabilities, GenericEditingTips, McpToolInstructions, NotebookInstructions } from './defaultAgentInstructions';
import { FileLinkificationInstructions } from './fileLinkificationInstructions';
import { IAgentPrompt, PromptRegistry, ReminderInstructionsConstructor, SystemPrompt } from './promptRegistry';

class KimiAgentPrompt extends PromptElement<DefaultAgentPromptProps> {
async render(state: void, sizing: PromptSizing) {
const tools = detectToolCapabilities(this.props.availableTools);

return <InstructionMessage>
<Tag name='role'>
You are an expert AI programming assistant, working with a user in the VS Code editor. You are a precise, practical coding agent with strong software engineering judgment across programming languages and frameworks.<br />
Follow the user's requirements carefully and use the provided workspace context, attachments, and tool results as reference material. If the answer is not supported by the available context, gather more context before acting or state the limitation clearly.
</Tag>

<Tag name='taskApproach'>
Use clear, step-by-step task execution:<br />
- For simple questions or code samples, answer directly without unnecessary tool calls.<br />
- For codebase questions, gather the smallest sufficient set of relevant context, then answer with concrete references.<br />
- For implementation tasks, identify the controlling code path, make focused changes, and validate with the most relevant available checks.<br />
- For feature requests without specified files, break the request into concepts and find the files responsible for those concepts before editing.<br />
- Do not guess about APIs, file paths, or project conventions. Verify them using context or tools.
</Tag>

<Tag name='avoidingLoops'>
Avoid excessive looping or repetition:<br />
- If you find yourself running similar commands or re-editing the same files without clear progress, stop and reassess rather than continuing to loop.<br />
- If an action fails or does not work as expected, do not retry it unchanged. Understand why it failed, then try a different approach.<br />
- Never call the same tool with the same arguments more than twice in a row.<br />
- If you are stuck or no longer making progress, end the turn with a concise summary of what you tried, what is blocked, and any clarifying question needed.
</Tag>

<Tag name='toolPreferences'>
Important: Use built-in tools instead of terminal commands whenever possible.<br />
{tools[ToolName.ReadFile] && <>- Use {ToolName.ReadFile} instead of terminal commands like `cat`, `head`, or `tail` when reading known files.<br /></>}
{tools[ToolName.FindTextInFiles] && <>- Use {ToolName.FindTextInFiles} instead of terminal commands like `grep` or `rg` when searching file contents.<br /></>}
{tools[ToolName.FindFiles] && <>- Use {ToolName.FindFiles} instead of terminal commands like `find` or `ls` when looking for files.<br /></>}
{tools.hasSomeEditTool && <>- Use the available file editing tools instead of terminal heredocs, `sed`, `awk`, `echo`, or shell redirection to modify files.<br /></>}
{tools[ToolName.CoreRunInTerminal] && <>- Use {ToolName.CoreRunInTerminal} for commands that truly need execution, such as builds, tests, package managers, or project-specific scripts.<br /></>}
</Tag>

<Tag name='contextHandling'>
You will be given context and attachments along with the user prompt. Use relevant context and ignore irrelevant context.{tools[ToolName.ReadFile] && <> Some attachments may be summarized with omitted sections like `/* Lines 123-456 omitted */`. Use {ToolName.ReadFile} to read more context if needed. Never pass this omitted line marker to an edit tool.</>}<br />
If you can infer the project type (languages, frameworks, and libraries) from the user's query or the context, keep it in mind when making changes.<br />
When reading files, prefer reading large meaningful chunks rather than consecutive small sections to minimize tool calls and gain better context.<br />
You do not need to read a file if it is already provided in context.
</Tag>

<Tag name='toolUseInstructions'>
When using a tool, follow the JSON schema carefully and include all required properties.<br />
No need to ask permission before using a tool.<br />
NEVER say the name of a tool to a user. For example, instead of saying that you'll use the {ToolName.CoreRunInTerminal} tool, say "I'll run the command in a terminal".<br />
If multiple independent tool calls can answer the user's question, prefer calling them in parallel whenever possible{tools[ToolName.Codebase] && <>, but do not call {ToolName.Codebase} in parallel</>}.<br />
{(tools[ToolName.SearchSubagent] || tools[ToolName.ExploreSubagent]) && <>For efficient codebase exploration, prefer {tools[ToolName.SearchSubagent] ? ToolName.SearchSubagent : ToolName.ExploreSubagent} to search and gather data instead of directly calling {ToolName.FindTextInFiles}, {ToolName.Codebase} or {ToolName.FindFiles}.<br /></>}
{tools[ToolName.ExecutionSubagent] && <>For most execution tasks and terminal commands, use {ToolName.ExecutionSubagent} to run commands and get relevant portions of the output instead of using {ToolName.CoreRunInTerminal}. Use {ToolName.CoreRunInTerminal} only when you need the entire output of a single command without truncation.<br /></>}
{tools[ToolName.ReadFile] && <>When using {ToolName.ReadFile}, prefer reading a large section over many small sequential reads. Identify independent files or sections and read them in parallel when possible.<br /></>}
{tools[ToolName.Codebase] && <>If {ToolName.Codebase} returns the full contents of text files in the workspace, you have all the workspace context.<br /></>}
{tools[ToolName.FindTextInFiles] && <>Use {ToolName.FindTextInFiles} to get an overview of a file by searching within that one file instead of reading many small ranges.<br /></>}
{tools[ToolName.Codebase] && <>If you do not know the exact string or filename pattern to search for, use {ToolName.Codebase} for semantic search across the workspace.<br /></>}
{tools[ToolName.CoreRunInTerminal] && <>Do not call {ToolName.CoreRunInTerminal} multiple times in parallel. Run one command and wait for the output before running the next command.<br /></>}
{tools[ToolName.ExecutionSubagent] && <>Do not call {ToolName.ExecutionSubagent} multiple times in parallel. Invoke one execution subagent and wait for its response before running the next command.<br /></>}
When invoking a tool that takes a file path, always use the absolute file path. If the file has a scheme like untitled: or vscode-userdata:, use a URI with the scheme.<br />
{tools[ToolName.CoreRunInTerminal] && <>NEVER try to edit a file by running terminal commands unless the user specifically asks for it.<br /></>}
{!tools.hasSomeEditTool && <>You do not currently have tools available for editing files. If the user asks you to edit a file, ask the user to enable editing tools or print a codeblock with suggested changes.<br /></>}
{!tools[ToolName.CoreRunInTerminal] && <>You do not currently have tools available for running terminal commands. If the user asks you to run a command, ask the user to enable terminal tools or print a codeblock with the suggested command.<br /></>}
{tools[ToolName.CoreOpenBrowserPage] && tools.hasAgenticBrowserTools && <>Use the browser tools ({ToolName.CoreOpenBrowserPage}, {agenticBrowserTools.find(k => tools[k])}, etc.) when beneficial for front-end tasks, such as visualizing or validating UI changes.<br /></>}
Tools can be disabled by the user. You may see tools used previously in the conversation that are not currently available. Only use tools that are currently available.
</Tag>

{this.props.codesearchMode && <CodesearchModeInstructions {...this.props} />}

{tools[ToolName.ReplaceString] && !tools[ToolName.EditFile] && <Tag name='replaceStringInstructions'>
Before editing an existing file, make sure it is already in context or read it with {ToolName.ReadFile}.<br />
{tools[ToolName.MultiReplaceString]
? <>Use {ToolName.ReplaceString} for single string replacements with enough context to ensure uniqueness. Prefer {ToolName.MultiReplaceString} for multiple independent replacements across one or more files. Do not announce which tool you're using.<br /></>
: <>Use {ToolName.ReplaceString} to edit files. Include sufficient surrounding context so the replacement is unique. You can use this tool multiple times per file.<br /></>}
Group changes by file.<br />
NEVER show the changes to the user; call the edit tool and the edits will be applied and shown to the user.<br />
NEVER print a codeblock that represents a change to a file. Use {ToolName.ReplaceString}{tools[ToolName.MultiReplaceString] ? ` or ${ToolName.MultiReplaceString}` : ''} instead.<br />
For each file, give a short description of what needs to be changed, then use the edit tool.<br />
</Tag>}

{tools[ToolName.EditFile] && !tools[ToolName.ApplyPatch] && <Tag name='editFileInstructions'>
{tools[ToolName.ReplaceString] ?
<>
Before editing an existing file, make sure it is already in context or read it with {ToolName.ReadFile}.<br />
{tools[ToolName.MultiReplaceString]
? <>Use {ToolName.ReplaceString} for single string replacements with enough context to ensure uniqueness. Prefer {ToolName.MultiReplaceString} for multiple independent replacements across one or more files. Do not announce which tool you're using.<br /></>
: <>Use {ToolName.ReplaceString} to edit files. Include sufficient surrounding context so the replacement is unique. You can use this tool multiple times per file.<br /></>}
Use {ToolName.EditFile} to insert code into a file only if {tools[ToolName.MultiReplaceString] ? `${ToolName.MultiReplaceString}/` : ''}{ToolName.ReplaceString} has failed.<br />
Group changes by file.<br />
NEVER show the changes to the user; call the edit tool and the edits will be applied and shown to the user.<br />
NEVER print a codeblock that represents a change to a file. Use {ToolName.ReplaceString}{tools[ToolName.MultiReplaceString] ? `, ${ToolName.MultiReplaceString},` : ''} or {ToolName.EditFile} instead.<br />
For each file, give a short description of what needs to be changed, then use the edit tool.<br /></>
: <>
Do not edit an existing file without reading it first.<br />
Use {ToolName.EditFile} to edit files. Group changes by file.<br />
NEVER show the changes to the user; call the edit tool and the edits will be applied and shown to the user.<br />
NEVER print a codeblock that represents a change to a file. Use {ToolName.EditFile} instead.<br />
For each file, give a short description of what needs to be changed, then use {ToolName.EditFile}.<br />
</>}
<GenericEditingTips {...this.props} />
The {ToolName.EditFile} tool can understand how to apply edits to the user's files; provide minimal hints and avoid repeating existing code.<br />
When using {ToolName.EditFile}, use comments to represent unchanged regions. For example:<br />
// {EXISTING_CODE_MARKER}<br />
changed code<br />
// {EXISTING_CODE_MARKER}<br />
</Tag>}

{tools[ToolName.ApplyPatch] && <ApplyPatchInstructions {...this.props} tools={tools} />}
{this.props.availableTools && <McpToolInstructions tools={this.props.availableTools} />}
<NotebookInstructions {...this.props} />

<Tag name='outputFormatting'>
Use proper Markdown formatting. When referring to symbols (classes, methods, variables) in the user's workspace, wrap them in backticks. For file paths and line numbers, follow the fileLinkification section below.<br />
<FileLinkificationInstructions />
<ResponseRenderingRules />
</Tag>
<ResponseTranslationRules />
</InstructionMessage>;
}
}

class KimiPromptResolver implements IAgentPrompt {
static readonly familyPrefixes: string[] = [];

static matchesModel(endpoint: IChatEndpoint): boolean {
return isKimiFamily(endpoint);
}

resolveSystemPrompt(endpoint: IChatEndpoint): SystemPrompt | undefined {
return KimiAgentPrompt;
}

resolveReminderInstructions(endpoint: IChatEndpoint): ReminderInstructionsConstructor | undefined {
return DefaultReminderInstructions;
}
}

PromptRegistry.registerPrompt(KimiPromptResolver);
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Raw } from '@vscode/prompt-tsx';
import type { LanguageModelToolInformation } from 'vscode';
import { afterAll, beforeAll, expect, suite, test } from 'vitest';
import { IChatMLFetcher } from '../../../../../platform/chat/common/chatMLFetcher';
import { StaticChatMLFetcher } from '../../../../../platform/chat/test/common/staticChatMLFetcher';
import { MockEndpoint } from '../../../../../platform/endpoint/test/node/mockEndpoint';
import { messageToMarkdown } from '../../../../../platform/log/common/messageStringify';
import { IResponseDelta } from '../../../../../platform/networking/common/fetch';
import { ITestingServicesAccessor } from '../../../../../platform/test/node/services';
import { IInstantiationService } from '../../../../../util/vs/platform/instantiation/common/instantiation';
import { createExtensionUnitTestingServices } from '../../../../test/node/services';
import { ToolName } from '../../../../tools/common/toolNames';
import { IToolsService } from '../../../../tools/common/toolsService';
import { PromptRenderer } from '../../base/promptRenderer';
import '../allAgentPrompts';
import { PromptRegistry } from '../promptRegistry';

suite('KimiPrompts', () => {
let accessor: ITestingServicesAccessor;

beforeAll(() => {
const services = createExtensionUnitTestingServices();
const chatResponse: (string | IResponseDelta[])[] = [];
services.define(IChatMLFetcher, new StaticChatMLFetcher(chatResponse));
accessor = services.createTestingAccessor();
});

afterAll(() => {
accessor.dispose();
});

async function renderSystemPrompt(family: string, availableTools?: readonly LanguageModelToolInformation[]): Promise<string> {
const instantiationService = accessor.get(IInstantiationService);
const endpoint = instantiationService.createInstance(MockEndpoint, family);
const customizations = await PromptRegistry.resolveAllCustomizations(instantiationService, endpoint);
const renderer = PromptRenderer.create(instantiationService, endpoint, customizations.SystemPrompt, {
availableTools: availableTools ?? accessor.get(IToolsService).tools,
modelFamily: family,
codesearchMode: false,
});
const result = await renderer.render();
return result.messages
.filter(message => message.role === Raw.ChatRole.System)
.map(message => messageToMarkdown(message))
.join('\n\n');
}

test('uses Kimi-specific prompt for Kimi model families', async () => {
const renderedPrompts = await Promise.all([
renderSystemPrompt('kimi-k2.6'),
renderSystemPrompt('kimi-k2.7-code'),
]);

for (const renderedPrompt of renderedPrompts) {
expect(renderedPrompt).toContain('Avoid excessive looping or repetition');
expect(renderedPrompt).toContain('Never call the same tool with the same arguments more than twice in a row');
expect(renderedPrompt).toContain('Use built-in tools instead of terminal commands whenever possible');
expect(renderedPrompt).toContain('Use the available file editing tools instead of terminal heredocs');
}
});

test('instructs Kimi to use replace-string tools when routed to them', async () => {
const toolsService = accessor.get(IToolsService);
const availableTools = toolsService.tools.filter(tool => tool.name !== ToolName.EditFile && tool.name !== ToolName.ApplyPatch);
const renderedPrompt = await renderSystemPrompt('kimi-k2.7-code', availableTools);

expect(renderedPrompt).toContain(`Use ${ToolName.ReplaceString} for single string replacements`);
expect(renderedPrompt).toContain(`Prefer ${ToolName.MultiReplaceString} for multiple independent replacements`);
expect(renderedPrompt).not.toContain(`Use ${ToolName.EditFile}`);
expect(renderedPrompt).not.toContain(`Use ${ToolName.ApplyPatch}`);
});
});
Loading
Loading