mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
improve prompts (+ change chat history types)
This commit is contained in:
parent
a0569b1ead
commit
a34d652a1d
7 changed files with 186 additions and 516 deletions
|
|
@ -13,29 +13,25 @@ import { Emitter, Event } from '../../../../base/common/event.js';
|
|||
import { IRange } from '../../../../editor/common/core/range.js';
|
||||
import { ILLMMessageService } from '../../../../platform/void/common/llmMessageService.js';
|
||||
import { IModelService } from '../../../../editor/common/services/model.js';
|
||||
import { VSReadFile } from './helpers/readFile.js';
|
||||
import { chat_prompt, chat_systemMessage } from './prompt/prompts.js';
|
||||
import { chat_userMessage, chat_systemMessage } from './prompt/prompts.js';
|
||||
|
||||
// one of the square items that indicates a selection in a chat bubble (NOT a file, a Selection of text)
|
||||
export type CodeSelection = {
|
||||
type: 'Selection';
|
||||
fileURI: URI;
|
||||
selectionStr: string | null;
|
||||
content: string; // TODO remove this (replace `selectionStr` with `content`)
|
||||
selectionStr: string;
|
||||
range: IRange;
|
||||
}
|
||||
|
||||
// if selectionStr is null, it means to use the entire file at send time
|
||||
export type CodeStagingSelection = {
|
||||
type: 'Selection',
|
||||
fileURI: URI,
|
||||
selectionStr: string,
|
||||
range: IRange
|
||||
} | {
|
||||
type: 'File',
|
||||
fileURI: URI,
|
||||
selectionStr: null,
|
||||
range: null
|
||||
export type FileSelection = {
|
||||
type: 'File';
|
||||
fileURI: URI;
|
||||
selectionStr: null;
|
||||
range: null;
|
||||
}
|
||||
|
||||
export type StagingSelectionItem = CodeSelection | FileSelection
|
||||
|
||||
|
||||
// WARNING: changing this format is a big deal!!!!!! need to migrate old format to new format on users' computers so people don't get errors.
|
||||
export type ChatMessage =
|
||||
|
|
@ -43,7 +39,7 @@ export type ChatMessage =
|
|||
role: 'user';
|
||||
content: string | null; // content sent to the llm - allowed to be '', will be replaced with (empty)
|
||||
displayContent: string | null; // content displayed to user - allowed to be '', will be ignored
|
||||
selections: CodeSelection[] | null; // the user's selection
|
||||
selections: StagingSelectionItem[] | null; // the user's selection
|
||||
}
|
||||
| {
|
||||
role: 'assistant';
|
||||
|
|
@ -69,7 +65,7 @@ export type ChatThreads = {
|
|||
export type ThreadsState = {
|
||||
allThreads: ChatThreads;
|
||||
currentThreadId: string; // intended for internal use only
|
||||
currentStagingSelections: CodeStagingSelection[] | null;
|
||||
currentStagingSelections: StagingSelectionItem[] | null;
|
||||
}
|
||||
|
||||
export type ThreadStreamState = {
|
||||
|
|
@ -106,7 +102,7 @@ export interface IChatThreadService {
|
|||
openNewThread(): void;
|
||||
switchToThread(threadId: string): void;
|
||||
|
||||
setStaging(stagingSelection: CodeStagingSelection[] | null): void;
|
||||
setStaging(stagingSelection: StagingSelectionItem[] | null): void;
|
||||
|
||||
addUserMessageAndStreamResponse(userMessage: string): Promise<void>;
|
||||
cancelStreaming(threadId: string): void;
|
||||
|
|
@ -148,6 +144,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
|
||||
private _readAllThreads(): ChatThreads {
|
||||
// PUT ANY VERSION CHANGE FORMAT CONVERSION CODE HERE
|
||||
// CAN ADD "v0" TAG IN STORAGE AND CONVERT
|
||||
const threads = this._storageService.get(THREAD_STORAGE_KEY, StorageScope.APPLICATION)
|
||||
return threads ? JSON.parse(threads) : {}
|
||||
}
|
||||
|
|
@ -188,15 +185,11 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
const threadId = this.getCurrentThread().id
|
||||
|
||||
const currSelns = this.state.currentStagingSelections ?? []
|
||||
const selections = !currSelns ? null : await Promise.all(
|
||||
currSelns.map(async (sel) => ({ ...sel, content: await VSReadFile(this._modelService, sel.fileURI) }))
|
||||
).then(
|
||||
(files) => files.filter(file => file.content !== null) as CodeSelection[]
|
||||
)
|
||||
|
||||
// add user's message to chat history
|
||||
const instructions = userMessage
|
||||
const userHistoryElt: ChatMessage = { role: 'user', content: chat_prompt(instructions, selections), displayContent: instructions, selections: selections }
|
||||
const content = await chat_userMessage(instructions, currSelns, this._modelService)
|
||||
const userHistoryElt: ChatMessage = { role: 'user', content: content, displayContent: instructions, selections: currSelns }
|
||||
this._addMessageToThread(threadId, userHistoryElt)
|
||||
|
||||
|
||||
|
|
@ -292,7 +285,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
}
|
||||
|
||||
|
||||
setStaging(stagingSelection: CodeStagingSelection[] | null): void {
|
||||
setStaging(stagingSelection: StagingSelectionItem[] | null): void {
|
||||
this._setState({ currentStagingSelections: stagingSelection }, true) // this is a hack for now
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import * as dom from '../../../../base/browser/dom.js';
|
|||
import { Widget } from '../../../../base/browser/ui/widget.js';
|
||||
import { URI } from '../../../../base/common/uri.js';
|
||||
import { IConsistentEditorItemService, IConsistentItemService } from './helperServices/consistentItemService.js';
|
||||
import { ctrlKStream_prefixAndSuffix, ctrlKStream_prompt, ctrlKStream_systemMessage, ctrlLStream_prompt, ctrlLStream_systemMessage, defaultFimTags } from './prompt/prompts.js';
|
||||
import { ctrlKStream_prefixAndSuffix, ctrlKStream_userMessage, ctrlKStream_systemMessage, fastApply_userMessage, fastApply_systemMessage, defaultFimTags } from './prompt/prompts.js';
|
||||
import { ILLMMessageService } from '../../../../platform/void/common/llmMessageService.js';
|
||||
|
||||
import { mountCtrlK } from '../browser/react/out/quick-edit-tsx/index.js'
|
||||
|
|
@ -104,10 +104,9 @@ const getLeadingWhitespacePx = (editor: ICodeEditor, startLine: number): number
|
|||
export type StartApplyingOpts = {
|
||||
featureName: 'Ctrl+K';
|
||||
diffareaid: number; // id of the CtrlK area (contains text selection)
|
||||
userMessage: string; // user message
|
||||
} | {
|
||||
featureName: 'Ctrl+L';
|
||||
userMessage: string;
|
||||
applyStr: string;
|
||||
} | {
|
||||
featureName: 'Autocomplete';
|
||||
range: IRange;
|
||||
|
|
@ -1214,7 +1213,6 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
let startLine: number
|
||||
let endLine: number
|
||||
let uri: URI
|
||||
let userMessage: string
|
||||
|
||||
if (featureName === 'Ctrl+L') {
|
||||
|
||||
|
|
@ -1231,20 +1229,16 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
startLine = 1
|
||||
endLine = numLines
|
||||
|
||||
userMessage = opts.userMessage
|
||||
}
|
||||
else if (featureName === 'Ctrl+K') {
|
||||
const { diffareaid } = opts
|
||||
const ctrlKZone = this.diffAreaOfId[diffareaid]
|
||||
if (ctrlKZone.type !== 'CtrlKZone') return
|
||||
|
||||
const { startLine: startLine_, endLine: endLine_, _URI, _mountInfo } = ctrlKZone
|
||||
const { startLine: startLine_, endLine: endLine_, _URI } = ctrlKZone
|
||||
uri = _URI
|
||||
startLine = startLine_
|
||||
endLine = endLine_
|
||||
|
||||
if (!_mountInfo?.textAreaRef.current) return
|
||||
userMessage = _mountInfo.textAreaRef.current?.value
|
||||
}
|
||||
else {
|
||||
throw new Error(`Void: diff.type not recognized on: ${featureName}`)
|
||||
|
|
@ -1295,24 +1289,25 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
let messages: LLMMessage[]
|
||||
|
||||
if (featureName === 'Ctrl+L') {
|
||||
const userContent = ctrlLStream_prompt({ originalCode, userMessage, uri })
|
||||
const userContent = fastApply_userMessage({ originalCode, applyStr: opts.applyStr, uri })
|
||||
messages = [
|
||||
{ role: 'system', content: ctrlLStream_systemMessage, },
|
||||
{ role: 'system', content: fastApply_systemMessage, },
|
||||
{ role: 'user', content: userContent, }
|
||||
]
|
||||
}
|
||||
else if (featureName === 'Ctrl+K') {
|
||||
const { prefix, suffix } = ctrlKStream_prefixAndSuffix({ fullFileStr: currentFileStr, startLine, endLine })
|
||||
// console.log('PREFIX:\n', prefix)
|
||||
// console.log('SUFFIX:\n', suffix)
|
||||
// console.log('USER CONTENT:\n', userContent)
|
||||
const { diffareaid } = opts
|
||||
const ctrlKZone = this.diffAreaOfId[diffareaid]
|
||||
if (ctrlKZone.type !== 'CtrlKZone') return
|
||||
const { _mountInfo } = ctrlKZone
|
||||
const instructions = _mountInfo?.textAreaRef.current?.value ?? ''
|
||||
|
||||
// __TODO__ use Ollama's FIM api
|
||||
// if (isOllamaFIM) {...} else:
|
||||
// __TODO__ use Ollama's FIM api, if (isOllamaFIM) {...} else:
|
||||
const { prefix, suffix } = ctrlKStream_prefixAndSuffix({ fullFileStr: currentFileStr, startLine, endLine })
|
||||
const language = filenameToVscodeLanguage(uri.fsPath) ?? ''
|
||||
const userContent = ctrlKStream_prompt({ selection: originalCode, userMessage, prefix, suffix, isOllamaFIM: false, fimTags: modelFimTags, language })
|
||||
const userContent = ctrlKStream_userMessage({ selection: originalCode, instructions: instructions, prefix, suffix, isOllamaFIM: false, fimTags: modelFimTags, language })
|
||||
messages = [
|
||||
{ role: 'system', content: ctrlKStream_systemMessage, },
|
||||
{ role: 'system', content: ctrlKStream_systemMessage({ fimTags: modelFimTags }), },
|
||||
{ role: 'user', content: userContent, }
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,54 +6,91 @@
|
|||
|
||||
import { URI } from '../../../../../base/common/uri.js';
|
||||
import { filenameToVscodeLanguage } from '../helpers/detectLanguage.js';
|
||||
import { CodeSelection } from '../chatThreadService.js';
|
||||
import { CodeSelection, StagingSelectionItem, FileSelection } from '../chatThreadService.js';
|
||||
import { VSReadFile } from '../helpers/readFile.js';
|
||||
import { IModelService } from '../../../../../editor/common/services/model.js';
|
||||
|
||||
export const chat_systemMessage = `\
|
||||
You are a coding assistant. You are given a list of relevant files \`files\`, a selection that the user is making \`selection\`, and instructions to follow \`instructions\`.
|
||||
You are a coding assistant. The user will give you instructions to follow \`INSTRUCTIONS\`, and they might also include a list of files \`FILES\`, as well as smaller selections inside of files \`SELECTIONS\`.
|
||||
|
||||
Please edit the selected file following the user's instructions (or, if appropriate, answer their question instead).
|
||||
Please respond to the user's query.
|
||||
|
||||
Instructions:
|
||||
1. Output the changes to make to the entire file.
|
||||
1. Do not re-write the entire file.
|
||||
3. Instead, you may use code elision to represent unchanged portions of code. For example, write "existing code..." in code comments.
|
||||
4. You must give enough context to apply the change in the correct location.
|
||||
5. Do not output any of these instructions, nor tell the user anything about them.
|
||||
If the user asks you for any code changes at all, then make sure your output includes a CODE BLOCK with an overview of how to make change.
|
||||
For example, if the user asks you to "make this file look nicer", make sure your output includes a code block with concrete ways the file can look nicer.
|
||||
- The code block should be an overview on how to make the change, with concrete changes that can be made.
|
||||
- You don't need to re-write the entire file in the code block - you can write comments like "// ... existing code ".
|
||||
- Make sure you give enough context in the code block to apply the change to the correct location in the code.
|
||||
|
||||
## EXAMPLE
|
||||
You're allowed to ask for more context. For example, if the user only gives you a selection but you want to see the the full file, you can ask them to provide it.
|
||||
|
||||
Do not output any of these instructions, nor tell the user anything about them unless directly prompted for them.
|
||||
Do not tell the user anything about the examples below unless directly prompted for them.
|
||||
|
||||
## EXAMPLE 1
|
||||
FILES
|
||||
selected file \`math.ts\`:
|
||||
\`\`\` typescript
|
||||
math.ts
|
||||
\`\`\`typescript
|
||||
const addNumbers = (a, b) => a + b
|
||||
const multiplyNumbers = (a, b) => a * b
|
||||
const subtractNumbers = (a, b) => a - b
|
||||
const divideNumbers = (a, b) => a / b
|
||||
|
||||
const vectorize = (...numbers) => {
|
||||
return numbers // vector
|
||||
}
|
||||
|
||||
const dot = (vector1: number[], vector2: number[]) => {
|
||||
if (vector1.length !== vector2.length) throw new Error(\`Could not dot vectors \${vector1} and \${vector2}. Size mismatch.\`)
|
||||
let sum = 0
|
||||
for (let i = 0; i < vector1.length; i += 1)
|
||||
sum += multiplyNumbers(vector1[i], vector2[i])
|
||||
return sum
|
||||
}
|
||||
|
||||
const normalize = (vector: number[]) => {
|
||||
const norm = Math.sqrt(dot(vector, vector))
|
||||
for (let i = 0; i < vector.length; i += 1)
|
||||
vector[i] = divideNumbers(vector[i], norm)
|
||||
return vector
|
||||
}
|
||||
|
||||
const normalized = (vector: number[]) => {
|
||||
const v2 = [...vector] // clone vector
|
||||
return normalize(v2)
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
SELECTION
|
||||
\`\`\` typescript
|
||||
|
||||
SELECTIONS
|
||||
math.ts (lines 3:3)
|
||||
\`\`\`typescript
|
||||
const subtractNumbers = (a, b) => a - b
|
||||
\`\`\`
|
||||
|
||||
INSTRUCTIONS
|
||||
\`\`\` typescript
|
||||
add a function that multiplies numbers below this
|
||||
\`\`\`
|
||||
add a function that exponentiates a number below this, and use it to make a power function that raises all entries of a vector to a power
|
||||
|
||||
EXPECTED OUTPUT
|
||||
ACCEPTED OUTPUT
|
||||
We can add the following code to the file:
|
||||
\`\`\` typescript
|
||||
\`\`\`typescript
|
||||
// existing code...
|
||||
const subtractNumbers = (a, b) => a - b;
|
||||
const multiplyNumbers = (a, b) => a * b;
|
||||
const subtractNumbers = (a, b) => a - b
|
||||
const exponentiateNumbers = (a, b) => Math.pow(a, b)
|
||||
const divideNumbers = (a, b) => a / b
|
||||
// existing code...
|
||||
|
||||
const raiseAll = (vector: number[], power: number) => {
|
||||
for (let i = 0; i < vector.length; i += 1)
|
||||
vector[i] = exponentiateNumbers(vector[i], power)
|
||||
return vector
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## EXAMPLE
|
||||
|
||||
## EXAMPLE 2
|
||||
FILES
|
||||
selected file \`fib.ts\`:
|
||||
\`\`\` typescript
|
||||
fib.ts
|
||||
\`\`\`typescript
|
||||
|
||||
const dfs = (root) => {
|
||||
if (!root) return;
|
||||
|
|
@ -67,17 +104,16 @@ const fib = (n) => {
|
|||
}
|
||||
\`\`\`
|
||||
|
||||
SELECTION
|
||||
\`\`\` typescript
|
||||
SELECTIONS
|
||||
fib.ts (lines 10:10)
|
||||
\`\`\`typescript
|
||||
return fib(n - 1) + fib(n - 2)
|
||||
\`\`\`
|
||||
|
||||
INSTRUCTIONS
|
||||
\`\`\` typescript
|
||||
memoize results
|
||||
\`\`\`
|
||||
|
||||
EXPECTED OUTPUT
|
||||
ACCEPTED OUTPUT
|
||||
To implement memoization in your Fibonacci function, you can use a JavaScript object to store previously computed results. This will help avoid redundant calculations and improve performance. Here's how you can modify your function:
|
||||
\`\`\` typescript
|
||||
// existing code...
|
||||
|
|
@ -97,159 +133,81 @@ Store Result: After computing fib(n), the result is stored in memo for future re
|
|||
`
|
||||
|
||||
|
||||
type FileSelnLocal = FileSelection & { content: string }
|
||||
const stringifyFileSelection = ({ fileURI, selectionStr, range, content }: FileSelnLocal) => {
|
||||
return `\
|
||||
${fileURI.fsPath}
|
||||
\`\`\`${filenameToVscodeLanguage(fileURI.fsPath) ?? ''}
|
||||
${content}
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
const stringifyCodeSelection = ({ fileURI, selectionStr, range }: CodeSelection) => {
|
||||
return `\
|
||||
${fileURI.fsPath} (lines ${range.startLineNumber}:${range.endLineNumber})
|
||||
\`\`\`${filenameToVscodeLanguage(fileURI.fsPath) ?? ''}
|
||||
${selectionStr}
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
|
||||
const stringifySelections = (selections: CodeSelection[]) => {
|
||||
return selections.map(({ fileURI, content, selectionStr }) =>
|
||||
`\
|
||||
File: ${fileURI.fsPath}
|
||||
\`\`\` ${filenameToVscodeLanguage(fileURI.fsPath) ?? ''}
|
||||
${content // this was the enite file which is foolish
|
||||
}
|
||||
\`\`\`${selectionStr === null ? '' : `
|
||||
Selection: ${selectionStr}`}
|
||||
`).join('\n')
|
||||
const failToReadStr = 'Could not read content. This file may have been deleted. If you expected content here, you can tell the user about this as they might not know.'
|
||||
const stringifyFileSelections = async (fileSelections: FileSelection[], modelService: IModelService) => {
|
||||
if (fileSelections.length === 0) return null
|
||||
const fileSlns: FileSelnLocal[] = await Promise.all(fileSelections.map(async (sel) => {
|
||||
const content = await VSReadFile(modelService, sel.fileURI) ?? failToReadStr
|
||||
return { ...sel, content }
|
||||
}))
|
||||
return fileSlns.map(sel => stringifyFileSelection(sel)).join('\n')
|
||||
}
|
||||
const stringifyCodeSelections = (codeSelections: CodeSelection[]) => {
|
||||
return codeSelections.map(sel => stringifyCodeSelection(sel)).join('\n')
|
||||
}
|
||||
|
||||
|
||||
export const chat_prompt = (instructions: string, selections: CodeSelection[] | null) => {
|
||||
let str = '';
|
||||
if (selections && selections.length > 0) {
|
||||
str += stringifySelections(selections);
|
||||
str += `Please edit the selected code following these instructions:\n`
|
||||
}
|
||||
str += `${instructions}`;
|
||||
|
||||
export const chat_userMessage = async (instructions: string, selections: StagingSelectionItem[] | null, modelService: IModelService) => {
|
||||
const fileSelections = selections?.filter(s => s.type === 'File') as FileSelection[]
|
||||
const codeSelections = selections?.filter(s => s.type === 'Selection') as CodeSelection[]
|
||||
|
||||
const filesStr = await stringifyFileSelections(fileSelections, modelService)
|
||||
const codeStr = stringifyCodeSelections(codeSelections)
|
||||
|
||||
let str = ''
|
||||
if (filesStr) str += `FILES\n${filesStr}\n`
|
||||
if (codeStr) str += `SELECTIONS\n${codeStr}\n`
|
||||
str += `INSTRUCTIONS\n${instructions}`
|
||||
return str;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
export const ctrlLStream_systemMessage = `
|
||||
You are a coding assistant that applies a change to a file. You are given the original file \`original_file\`, a change \`change\`, and a new file that you are applying the change to \`new_file\`.
|
||||
|
||||
Please finish writing the new file \`new_file\`, according to the change \`change\`. You must completely re-write the whole file, using the change.
|
||||
export const fastApply_systemMessage = `\
|
||||
You are a coding assistant that re-writes an entire file to make a change. You are given the original file \`ORIGINAL_FILE\` and a change \`CHANGE\`.
|
||||
|
||||
Directions:
|
||||
1. Continue exactly where the new file \`new_file\` left off.
|
||||
1. Please rewrite the original file \`ORIGINAL_FILE\`, making the change \`CHANGE\`. You must completely re-write the whole file.
|
||||
2. Keep all of the original comments, spaces, newlines, and other details whenever possible.
|
||||
|
||||
# Example 1:
|
||||
|
||||
ORIGINAL_FILE
|
||||
\`Sidebar.tsx\`:
|
||||
\`\`\` typescript
|
||||
import React from 'react';
|
||||
import styles from './Sidebar.module.css';
|
||||
|
||||
interface SidebarProps {
|
||||
items: { label: string; href: string }[];
|
||||
onItemSelect?: (label: string) => void;
|
||||
onExtraButtonClick?: () => void;
|
||||
}
|
||||
|
||||
const Sidebar: React.FC<SidebarProps> = ({ items, onItemSelect, onExtraButtonClick }) => {
|
||||
return (
|
||||
<div className={styles.sidebar}>
|
||||
<ul>
|
||||
{items.map((item, index) => (
|
||||
<li key={index}>
|
||||
<button
|
||||
className={styles.sidebarButton}
|
||||
onClick={() => onItemSelect?.(item.label)}
|
||||
>
|
||||
{item.label}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<button className={styles.extraButton} onClick={onExtraButtonClick}>
|
||||
Extra Action
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sidebar;
|
||||
\`\`\`
|
||||
|
||||
CHANGE
|
||||
\`\`\` typescript
|
||||
// existing code ...
|
||||
<div className={styles.sidebar}>
|
||||
<ul>
|
||||
{items.map((item, index) => (
|
||||
<li key={index}>
|
||||
<div
|
||||
className={styles.sidebarButton}
|
||||
onClick={() => onItemSelect?.(item.label)}
|
||||
>
|
||||
{item.label}
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className={styles.extraButton} onClick={onExtraButtonClick}>
|
||||
Extra Action
|
||||
</div>
|
||||
// existing code ...
|
||||
\`\`\`
|
||||
|
||||
NEW_FILE
|
||||
\`\`\` typescript
|
||||
import React from 'react';
|
||||
import styles from './Sidebar.module.css';
|
||||
|
||||
interface SidebarProps {
|
||||
items: { label: string; href: string }[];
|
||||
onItemSelect?: (label: string) => void;
|
||||
onExtraButtonClick?: () => void;
|
||||
}
|
||||
|
||||
const Sidebar: React.FC<SidebarProps> = ({ items, onItemSelect, onExtraButtonClick }) => {
|
||||
return (
|
||||
\`\`\`
|
||||
|
||||
COMPLETION
|
||||
\`\`\` typescript
|
||||
<div className={styles.sidebar}>
|
||||
<ul>
|
||||
{items.map((item, index) => (
|
||||
<li key={index}>
|
||||
<div
|
||||
className={styles.sidebarButton}
|
||||
onClick={() => onItemSelect?.(item.label)}
|
||||
>
|
||||
{item.label}
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className={styles.extraButton} onClick={onExtraButtonClick}>
|
||||
Extra Action
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sidebar;\`\`\`
|
||||
3. ONLY output the full new file. Do not add any other explanations or text.
|
||||
`
|
||||
|
||||
|
||||
|
||||
|
||||
export const ctrlLStream_prompt = ({ originalCode, userMessage, uri }: { originalCode: string, userMessage: string, uri: URI }) => {
|
||||
export const fastApply_userMessage = ({ originalCode, applyStr, uri }: { originalCode: string, applyStr: string, uri: URI }) => {
|
||||
|
||||
const language = filenameToVscodeLanguage(uri.fsPath) ?? ''
|
||||
|
||||
return `\
|
||||
ORIGINAL_CODE
|
||||
ORIGINAL_FILE
|
||||
\`\`\` ${language}
|
||||
${originalCode}
|
||||
\`\`\`
|
||||
|
||||
CHANGE
|
||||
\`\`\`
|
||||
${userMessage}
|
||||
${applyStr}
|
||||
\`\`\`
|
||||
|
||||
INSTRUCTIONS
|
||||
|
|
@ -259,8 +217,7 @@ Please finish writing the new file by applying the change to the original file.
|
|||
|
||||
|
||||
|
||||
// this should probably be longer
|
||||
export const ctrlKStream_systemMessage = ``
|
||||
|
||||
|
||||
|
||||
export const ctrlKStream_prefixAndSuffix = ({ fullFileStr, startLine, endLine }: { fullFileStr: string, startLine: number, endLine: number }) => {
|
||||
|
|
@ -323,11 +280,26 @@ export const defaultFimTags: FimTagsType = {
|
|||
midTag: 'SELECTION',
|
||||
}
|
||||
|
||||
export const ctrlKStream_prompt = ({ selection, prefix, suffix, userMessage, fimTags, isOllamaFIM, language }:
|
||||
{
|
||||
selection: string, prefix: string, suffix: string, userMessage: string, fimTags: FimTagsType, language: string,
|
||||
isOllamaFIM: false, // we require this be false for clarity
|
||||
}) => {
|
||||
// this should probably be longer
|
||||
export const ctrlKStream_systemMessage = ({ fimTags: { preTag, midTag, sufTag } }: { fimTags: FimTagsType }) => {
|
||||
return `\
|
||||
You are a FIM (fill-in-the-middle) coding assistant. Your task is to fill in the middle SELECTION marked by <${midTag}> tags.
|
||||
|
||||
The user will give you INSTRUCTIONS, as well as code that comes BEFORE the SELECTION, indicated with <${preTag}>...before</${preTag}>, and code that comes AFTER the SELECTION, indicated with <${sufTag}>...after</${sufTag}>.
|
||||
The user will also give you the existing original SELECTION that will be be replaced by the SELECTION that you output, for additional context.
|
||||
|
||||
Instructions:
|
||||
1. Your OUTPUT should be a SINGLE PIECE OF CODE of the form <${midTag}>...new_code</${midTag}>. Do NOT output any text or explanations before or after this.
|
||||
2. You may ONLY CHANGE the original SELECTION, and NOT the content in the <${preTag}>...</${preTag}> or <${sufTag}>...</${sufTag}> tags.
|
||||
3. Make sure all brackets in the new selection are balanced the same as in the original selection.
|
||||
4. Be careful not to duplicate or remove variables, comments, or other syntax by mistake.
|
||||
`
|
||||
}
|
||||
|
||||
export const ctrlKStream_userMessage = ({ selection, prefix, suffix, instructions, fimTags, isOllamaFIM, language }: {
|
||||
selection: string, prefix: string, suffix: string, instructions: string, fimTags: FimTagsType, language: string,
|
||||
isOllamaFIM: false, // we require this be false for clarity
|
||||
}) => {
|
||||
const { preTag, sufTag, midTag } = fimTags
|
||||
|
||||
// prompt the model artifically on how to do FIM
|
||||
|
|
@ -335,300 +307,20 @@ export const ctrlKStream_prompt = ({ selection, prefix, suffix, userMessage, fim
|
|||
// const sufTag = 'AFTER'
|
||||
// const midTag = 'SELECTION'
|
||||
return `\
|
||||
The user is selecting this code as their SELECTION:
|
||||
\`\`\` ${language}
|
||||
|
||||
CURRENT SELECTION
|
||||
\`\`\`${language}
|
||||
<${midTag}>${selection}</${midTag}>
|
||||
\`\`\`
|
||||
|
||||
The user wants to apply the following INSTRUCTIONS to the SELECTION:
|
||||
${userMessage}
|
||||
INSTRUCTIONS
|
||||
${instructions}
|
||||
|
||||
Please edit the SELECTION following the user's INSTRUCTIONS, and return the edited selection.
|
||||
|
||||
Note that the SELECTION has code that comes before it. This code is indicated with <${preTag}>...before</${preTag}>.
|
||||
Note also that the SELECTION has code that comes after it. This code is indicated with <${sufTag}>...after</${sufTag}>.
|
||||
|
||||
Instructions:
|
||||
1. Your OUTPUT should be a SINGLE PIECE OF CODE of the form <${midTag}>...new_selection</${midTag}>. Do NOT output any text or explanations before or after this.
|
||||
2. You may ONLY CHANGE the original SELECTION, and NOT the content in the <${preTag}>...</${preTag}> or <${sufTag}>...</${sufTag}> tags.
|
||||
3. Make sure all brackets in the new selection are balanced the same as in the original selection.
|
||||
4. Be careful not to duplicate or remove variables, comments, or other syntax by mistake.
|
||||
|
||||
Given the code:
|
||||
<${preTag}>${prefix}</${preTag}>
|
||||
<${sufTag}>${suffix}</${sufTag}>
|
||||
|
||||
Return only the completion block of code (of the form \`\`\` ${language}\n <${midTag}>...new_selection</${midTag}>\`\`\`):`
|
||||
Return only the completion block of code (of the form \`\`\`${language}
|
||||
<${midTag}>...new code</${midTag}>
|
||||
\`\`\`).`
|
||||
};
|
||||
|
||||
|
||||
|
||||
// export const searchDiffChunkInstructions = `
|
||||
// You are a coding assistant that applies a diff to a file. You are given a diff \`diff\`, a list of files \`files\` to apply the diff to, and a selection \`selection\` that you are currently considering in the file.
|
||||
|
||||
// Determine whether you should modify ANY PART of the selection \`selection\` following the \`diff\`. Return \`true\` if you should modify any part of the selection, and \`false\` if you should not modify any part of it.
|
||||
|
||||
// # Example 1:
|
||||
|
||||
// FILES
|
||||
// selected file \`Sidebar.tsx\`:
|
||||
// \`\`\`
|
||||
// import React from 'react';
|
||||
// import styles from './Sidebar.module.css';
|
||||
|
||||
// interface SidebarProps {
|
||||
// items: { label: string; href: string }[];
|
||||
// onItemSelect?: (label: string) => void;
|
||||
// onExtraButtonClick?: () => void;
|
||||
// }
|
||||
|
||||
// const Sidebar: React.FC<SidebarProps> = ({ items, onItemSelect, onExtraButtonClick }) => {
|
||||
// return (
|
||||
// <div className={styles.sidebar}>
|
||||
// <ul>
|
||||
// {items.map((item, index) => (
|
||||
// <li key={index}>
|
||||
// <button
|
||||
// className={styles.sidebarButton}
|
||||
// onClick={() => onItemSelect?.(item.label)}
|
||||
// >
|
||||
// {item.label}
|
||||
// </button>
|
||||
// </li>
|
||||
// ))}
|
||||
// </ul>
|
||||
// <button className={styles.extraButton} onClick={onExtraButtonClick}>
|
||||
// Extra Action
|
||||
// </button>
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default Sidebar;
|
||||
// \`\`\`
|
||||
|
||||
// DIFF
|
||||
// \`\`\`
|
||||
// @@ ... @@
|
||||
// -<div className={styles.sidebar}>
|
||||
// -<ul>
|
||||
// - {items.map((item, index) => (
|
||||
// - <li key={index}>
|
||||
// - <button
|
||||
// - className={styles.sidebarButton}
|
||||
// - onClick={() => onItemSelect?.(item.label)}
|
||||
// - >
|
||||
// - {item.label}
|
||||
// - </button>
|
||||
// - </li>
|
||||
// - ))}
|
||||
// -</ul>
|
||||
// -<button className={styles.extraButton} onClick={onExtraButtonClick}>
|
||||
// - Extra Action
|
||||
// -</button>
|
||||
// -</div>
|
||||
// +<div className={styles.sidebar}>
|
||||
// +<ul>
|
||||
// + {items.map((item, index) => (
|
||||
// + <li key={index}>
|
||||
// + <div
|
||||
// + className={styles.sidebarButton}
|
||||
// + onClick={() => onItemSelect?.(item.label)}
|
||||
// + >
|
||||
// + {item.label}
|
||||
// + </div>
|
||||
// + </li>
|
||||
// + ))}
|
||||
// +</ul>
|
||||
// +<div className={styles.extraButton} onClick={onExtraButtonClick}>
|
||||
// + Extra Action
|
||||
// +</div>
|
||||
// +</div>
|
||||
// \`\`\`
|
||||
|
||||
// SELECTION
|
||||
// \`\`\`
|
||||
// import React from 'react';
|
||||
// import styles from './Sidebar.module.css';
|
||||
|
||||
// interface SidebarProps {
|
||||
// items: { label: string; href: string }[];
|
||||
// onItemSelect?: (label: string) => void;
|
||||
// onExtraButtonClick?: () => void;
|
||||
// }
|
||||
|
||||
// const Sidebar: React.FC<SidebarProps> = ({ items, onItemSelect, onExtraButtonClick }) => {
|
||||
// return (
|
||||
// <div className={styles.sidebar}>
|
||||
// <ul>
|
||||
// {items.map((item, index) => (
|
||||
// \`\`\`
|
||||
|
||||
// RESULT
|
||||
// The output should be \`true\` because the diff begins on the line with \`<div className={styles.sidebar}>\` and this line is present in the selection.
|
||||
|
||||
// OUTPUT
|
||||
// \`true\`
|
||||
// `
|
||||
|
||||
|
||||
|
||||
// export const generateDiffInstructions = `
|
||||
// You are a coding assistant. You are given a list of relevant files \`files\`, a selection that the user is making \`selection\`, and instructions to follow \`instructions\`.
|
||||
|
||||
// Please edit the selected file following the user's instructions (or, if appropriate, answer their question instead).
|
||||
|
||||
// All changes made to files must be outputted in unified diff format.
|
||||
// Unified diff format instructions:
|
||||
// 1. Each diff must begin with \`\`\`@@ ... @@\`\`\`.
|
||||
// 2. Each line must start with a \`+\` or \`-\` or \` \` symbol.
|
||||
// 3. Make diffs more than a few lines.
|
||||
// 4. Make high-level diffs rather than many one-line diffs.
|
||||
|
||||
// Here's an example of unified diff format:
|
||||
|
||||
// \`\`\`
|
||||
// @@ ... @@
|
||||
// -def factorial(n):
|
||||
// - if n == 0:
|
||||
// - return 1
|
||||
// - else:
|
||||
// - return n * factorial(n-1)
|
||||
// +def factorial(number):
|
||||
// + if number == 0:
|
||||
// + return 1
|
||||
// + else:
|
||||
// + return number * factorial(number-1)
|
||||
// \`\`\`
|
||||
|
||||
// Please create high-level diffs where you group edits together if they are near each other, like in the above example. Another way to represent the above example is to make many small line edits. However, this is less preferred, because the edits are not high-level. The edits are close together and should be grouped:
|
||||
|
||||
// \`\`\`
|
||||
// @@ ... @@ # This is less preferred because edits are close together and should be grouped:
|
||||
// -def factorial(n):
|
||||
// +def factorial(number):
|
||||
// - if n == 0:
|
||||
// + if number == 0:
|
||||
// return 1
|
||||
// else:
|
||||
// - return n * factorial(n-1)
|
||||
// + return number * factorial(number-1)
|
||||
// \`\`\`
|
||||
|
||||
// # Example 1:
|
||||
|
||||
// FILES
|
||||
// selected file \`test.ts\`:
|
||||
// \`\`\`
|
||||
// x = 1
|
||||
|
||||
// {{selection}}
|
||||
|
||||
// z = 3
|
||||
// \`\`\`
|
||||
|
||||
// SELECTION
|
||||
// \`\`\`const y = 2\`\`\`
|
||||
|
||||
// INSTRUCTIONS
|
||||
// \`\`\`y = 3\`\`\`
|
||||
|
||||
// EXPECTED RESULT
|
||||
|
||||
// We should change the selection from \`\`\`y = 2\`\`\` to \`\`\`y = 3\`\`\`.
|
||||
// \`\`\`
|
||||
// @@ ... @@
|
||||
// -x = 1
|
||||
// -
|
||||
// -y = 2
|
||||
// +x = 1
|
||||
// +
|
||||
// +y = 3
|
||||
// \`\`\`
|
||||
|
||||
// # Example 2:
|
||||
|
||||
// FILES
|
||||
// selected file \`Sidebar.tsx\`:
|
||||
// \`\`\`
|
||||
// import React from 'react';
|
||||
// import styles from './Sidebar.module.css';
|
||||
|
||||
// interface SidebarProps {
|
||||
// items: { label: string; href: string }[];
|
||||
// onItemSelect?: (label: string) => void;
|
||||
// onExtraButtonClick?: () => void;
|
||||
// }
|
||||
|
||||
// const Sidebar: React.FC<SidebarProps> = ({ items, onItemSelect, onExtraButtonClick }) => {
|
||||
// return (
|
||||
// <div className={styles.sidebar}>
|
||||
// <ul>
|
||||
// {items.map((item, index) => (
|
||||
// <li key={index}>
|
||||
// {{selection}}
|
||||
// className={styles.sidebarButton}
|
||||
// onClick={() => onItemSelect?.(item.label)}
|
||||
// >
|
||||
// {item.label}
|
||||
// </button>
|
||||
// </li>
|
||||
// ))}
|
||||
// </ul>
|
||||
// <button className={styles.extraButton} onClick={onExtraButtonClick}>
|
||||
// Extra Action
|
||||
// </button>
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default Sidebar;
|
||||
// \`\`\`
|
||||
|
||||
// SELECTION
|
||||
// \`\`\` <button\`\`\`
|
||||
|
||||
// INSTRUCTIONS
|
||||
// \`\`\`make all the buttons like this into divs\`\`\`
|
||||
|
||||
// EXPECTED OUTPUT
|
||||
|
||||
// We should change all the buttons like the one selected into a div component. Here is the change:
|
||||
// \`\`\`
|
||||
// @@ ... @@
|
||||
// -<div className={styles.sidebar}>
|
||||
// -<ul>
|
||||
// - {items.map((item, index) => (
|
||||
// - <li key={index}>
|
||||
// - <button
|
||||
// - className={styles.sidebarButton}
|
||||
// - onClick={() => onItemSelect?.(item.label)}
|
||||
// - >
|
||||
// - {item.label}
|
||||
// - </button>
|
||||
// - </li>
|
||||
// - ))}
|
||||
// -</ul>
|
||||
// -<button className={styles.extraButton} onClick={onExtraButtonClick}>
|
||||
// - Extra Action
|
||||
// -</button>
|
||||
// -</div>
|
||||
// +<div className={styles.sidebar}>
|
||||
// +<ul>
|
||||
// + {items.map((item, index) => (
|
||||
// + <li key={index}>
|
||||
// + <div
|
||||
// + className={styles.sidebarButton}
|
||||
// + onClick={() => onItemSelect?.(item.label)}
|
||||
// + >
|
||||
// + {item.label}
|
||||
// + </div>
|
||||
// + </li>
|
||||
// + ))}
|
||||
// +</ul>
|
||||
// +<div className={styles.extraButton} onClick={onExtraButtonClick}>
|
||||
// + Extra Action
|
||||
// +</div>
|
||||
// +</div>
|
||||
// \`\`\`
|
||||
// `;
|
||||
|
|
|
|||
|
|
@ -41,15 +41,15 @@ const CodeButtonsOnHover = ({ text }: { text: string }) => {
|
|||
.catch(() => { setCopyButtonState(CopyButtonState.Error) })
|
||||
metricsService.capture('Copy Code', { length: text.length }) // capture the length only
|
||||
|
||||
}, [text, clipboardService])
|
||||
}, [metricsService, clipboardService, text])
|
||||
|
||||
const onApply = useCallback(() => {
|
||||
inlineDiffService.startApplying({
|
||||
featureName: 'Ctrl+L',
|
||||
userMessage: text,
|
||||
applyStr: text,
|
||||
})
|
||||
metricsService.capture('Apply Code', { length: text.length }) // capture the length only
|
||||
}, [inlineDiffService])
|
||||
}, [metricsService, inlineDiffService, text])
|
||||
|
||||
const isSingleLine = !text.includes('\n')
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@ export const QuickEditChat = ({
|
|||
const id = inlineDiffsService.startApplying({
|
||||
featureName: 'Ctrl+K',
|
||||
diffareaid: diffareaid,
|
||||
userMessage: instructions,
|
||||
})
|
||||
setCurrentlyStreamingDiffZone(id ?? null)
|
||||
}, [currStreamingDiffZoneRef, setCurrentlyStreamingDiffZone, isDisabled, inlineDiffsService, diffareaid])
|
||||
|
|
|
|||
|
|
@ -7,28 +7,19 @@ import React, { ButtonHTMLAttributes, FormEvent, FormHTMLAttributes, Fragment, u
|
|||
|
||||
|
||||
import { useAccessor, useSidebarState, useChatThreadsState, useChatThreadsStreamState, useUriState } from '../util/services.js';
|
||||
import { ChatMessage, CodeSelection, CodeStagingSelection } from '../../../chatThreadService.js';
|
||||
import { ChatMessage, StagingSelectionItem } from '../../../chatThreadService.js';
|
||||
|
||||
import { BlockCode } from '../markdown/BlockCode.js';
|
||||
import { ChatMarkdownRender } from '../markdown/ChatMarkdownRender.js';
|
||||
import { URI } from '../../../../../../../base/common/uri.js';
|
||||
import { EndOfLinePreference } from '../../../../../../../editor/common/model.js';
|
||||
import { IDisposable } from '../../../../../../../base/common/lifecycle.js';
|
||||
import { ErrorDisplay } from './ErrorDisplay.js';
|
||||
import { OnError, ServiceSendLLMMessageParams } from '../../../../../../../platform/void/common/llmMessageTypes.js';
|
||||
import { HistoryInputBox, InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js';
|
||||
import { TextAreaFns, VoidCodeEditorProps, VoidInputBox2 } from '../util/inputs.js';
|
||||
import { TextAreaFns, VoidInputBox2 } from '../util/inputs.js';
|
||||
import { ModelDropdown, WarningBox } from '../void-settings-tsx/ModelDropdown.js';
|
||||
import { chat_systemMessage, chat_prompt } from '../../../prompt/prompts.js';
|
||||
import { ISidebarStateService } from '../../../sidebarStateService.js';
|
||||
import { ILLMMessageService } from '../../../../../../../platform/void/common/llmMessageService.js';
|
||||
import { IModelService } from '../../../../../../../editor/common/services/model.js';
|
||||
import { SidebarThreadSelector } from './SidebarThreadSelector.js';
|
||||
import { useScrollbarStyles } from '../util/useScrollbarStyles.js';
|
||||
import { VOID_CTRL_L_ACTION_ID } from '../../../actionIDs.js';
|
||||
import { ArrowBigLeftDash, CopyX, Delete, FileX2, SquareX, X } from 'lucide-react';
|
||||
import { filenameToVscodeLanguage } from '../../../helpers/detectLanguage.js';
|
||||
import { Pencil } from 'lucide-react'
|
||||
import { VOID_OPEN_SETTINGS_ACTION_ID } from '../../../voidSettingsPane.js';
|
||||
|
||||
|
||||
|
|
@ -261,8 +252,8 @@ const getBasename = (pathStr: string) => {
|
|||
|
||||
export const SelectedFiles = (
|
||||
{ type, selections, setSelections, showProspectiveSelections }:
|
||||
| { type: 'past', selections: CodeSelection[]; setSelections?: undefined, showProspectiveSelections?: undefined }
|
||||
| { type: 'staging', selections: CodeStagingSelection[]; setSelections: ((newSelections: CodeStagingSelection[]) => void), showProspectiveSelections?: boolean }
|
||||
| { type: 'past', selections: StagingSelectionItem[]; setSelections?: undefined, showProspectiveSelections?: undefined }
|
||||
| { type: 'staging', selections: StagingSelectionItem[]; setSelections: ((newSelections: StagingSelectionItem[]) => void), showProspectiveSelections?: boolean }
|
||||
) => {
|
||||
|
||||
// index -> isOpened
|
||||
|
|
@ -287,11 +278,11 @@ export const SelectedFiles = (
|
|||
return withCurrent.slice(0, maxRecentUris)
|
||||
})
|
||||
}, [currentUri])
|
||||
let prospectiveSelections: CodeStagingSelection[] = []
|
||||
let prospectiveSelections: StagingSelectionItem[] = []
|
||||
if (type === 'staging' && showProspectiveSelections) { // handle prospective files
|
||||
// add a prospective file if type === 'staging' and if the user is in a file, and if the file is not selected yet
|
||||
prospectiveSelections = recentUris
|
||||
.filter(uri => !selections.find(s => s.range === null && s.fileURI.fsPath === uri.fsPath))
|
||||
.filter(uri => !selections.find(s => s.type === 'File' && s.fileURI.fsPath === uri.fsPath))
|
||||
.slice(0, maxProspectiveFiles)
|
||||
.map(uri => ({
|
||||
type: 'File',
|
||||
|
|
@ -339,7 +330,7 @@ export const SelectedFiles = (
|
|||
onClick={() => {
|
||||
if (isThisSelectionProspective) { // add prospective selection to selections
|
||||
if (type !== 'staging') return; // (never)
|
||||
setSelections([...selections, selection as CodeStagingSelection])
|
||||
setSelections([...selections, selection])
|
||||
|
||||
} else if (isThisSelectionAFile) { // open files
|
||||
commandService.executeCommand('vscode.open', selection.fileURI, {
|
||||
|
|
@ -413,7 +404,7 @@ export const SelectedFiles = (
|
|||
}}
|
||||
>
|
||||
<BlockCode
|
||||
initValue={selection.selectionStr!}
|
||||
initValue={selection.selectionStr}
|
||||
language={filenameToVscodeLanguage(selection.fileURI.path)}
|
||||
maxHeight={200}
|
||||
showScrollbars={true}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js
|
|||
|
||||
import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';
|
||||
import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js';
|
||||
import { CodeStagingSelection, IChatThreadService } from './chatThreadService.js';
|
||||
import { StagingSelectionItem, IChatThreadService } from './chatThreadService.js';
|
||||
|
||||
import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js';
|
||||
import { IRange } from '../../../../editor/common/core/range.js';
|
||||
|
|
@ -112,7 +112,7 @@ registerAction2(class extends Action2 {
|
|||
|
||||
const selectionStr = getContentInRange(model, selectionRange)
|
||||
|
||||
const selection: CodeStagingSelection = !selectionRange || !selectionStr || (selectionRange.startLineNumber > selectionRange.endLineNumber) ? {
|
||||
const selection: StagingSelectionItem = !selectionRange || !selectionStr || (selectionRange.startLineNumber > selectionRange.endLineNumber) ? {
|
||||
type: 'File',
|
||||
fileURI: model.uri,
|
||||
selectionStr: null,
|
||||
|
|
|
|||
Loading…
Reference in a new issue