add FIM streaming + instructions

This commit is contained in:
Andrew Pareles 2024-12-30 15:20:55 -08:00
parent a9b2ff631d
commit 6e40a92315
5 changed files with 151 additions and 44 deletions

View file

@ -27,13 +27,14 @@ import * as dom from '../../../../base/browser/dom.js';
import { Widget } from '../../../../base/browser/ui/widget.js';
import { URI } from '../../../../base/common/uri.js';
import { IConsistentItemService } from './helperServices/consistentItemService.js';
import { inlineDiff_systemMessage } from './prompt/prompts.js';
import { ctrlKStream_prefixAndSuffix, ctrlKStream_prompt, ctrlKStream_systemMessage, ctrlLStream_prompt, ctrlLStream_systemMessage } from './prompt/prompts.js';
import { ILLMMessageService } from '../../../../platform/void/common/llmMessageService.js';
import { IPosition } from '../../../../editor/common/core/position.js';
import { mountCtrlK } from '../browser/react/out/ctrl-k-tsx/index.js'
import { QuickEditPropsType } from './quickEditActions.js';
import { InputBox } from '../../../../base/browser/ui/inputbox/inputBox.js';
import { LLMMessage } from '../../../../platform/void/common/llmMessageTypes.js';
const configOfBG = (color: Color) => {
return { dark: color, light: color, hcDark: color, hcLight: color, }
@ -302,6 +303,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
const viewZone: IViewZone = {
afterLineNumber: ctrlKZone.startLine - 1,
domNode: domNode,
heightInPx: 0,
suppressMouseDown: false,
};
@ -640,7 +642,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
// changes the start/line locations of all DiffAreas on the page (adjust their start/end based on the change) based on the change that was recently made
private _realignAllDiffAreasLines(uri: URI, text: string, recentChange: { startLineNumber: number; endLineNumber: number }) {
console.log('recent change', recentChange)
// console.log('recent change', recentChange)
const model = this._getModel(uri)
if (!model) return
@ -930,9 +932,9 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
const adding: Omit<DiffZone, 'diffareaid'> = {
type: 'DiffZone',
originalCode: originalCode,
startLine: startLine,
endLine: endLine, // starts out the same as the current file
originalCode,
startLine,
endLine,
_URI: uri,
_streamState: {
isStreaming: true,
@ -944,40 +946,38 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
}
const diffZone = this._addDiffArea(adding)
// actually call the LLM
const userContent = featureName === 'Ctrl+L' ? `\
ORIGINAL_CODE
\`\`\`
${originalCode}
\`\`\`
DIFF
\`\`\`
${userMessage}
\`\`\`
INSTRUCTIONS
Please finish writing the new file by applying the diff to the original file. Return ONLY the completion of the file, without any explanation.
`
: `\
CTRL K MESSAGE GOES HERE __TODO__!
${userMessage}
`
let messages: LLMMessage[]
if (featureName === 'Ctrl+L') {
const userContent = ctrlLStream_prompt({ originalCode, userMessage })
messages = [
// TODO include more context too
{ role: 'system', content: ctrlLStream_systemMessage, },
{ role: 'user', content: userContent, }
]
}
else if (featureName === 'Ctrl+K') {
const { prefix, suffix } = ctrlKStream_prefixAndSuffix({ fullFileStr: currentFileStr, startLine, endLine })
const userContent = ctrlKStream_prompt({ selection: originalCode, userMessage, prefix, suffix })
console.log('PREFIX:\n', prefix)
console.log('SUFFIX:\n', suffix)
console.log('USER CONTENT:\n', userContent)
messages = [
// TODO include more context too (LSP, file history, etc)
{ role: 'system', content: ctrlKStream_systemMessage, },
{ role: 'user', content: userContent, }
]
}
else { throw new Error(`featureName ${featureName} is invalid`) }
// __TODO__ make these only move forward
const latestCurrentFileEnd: IPosition = { lineNumber: 1, column: 1 }
const latestOriginalFileStart: IPosition = { lineNumber: 1, column: 1 }
streamRequestIdRef.current = this._llmMessageService.sendLLMMessage({
featureName,
logging: { loggingName: 'streamChunk' },
messages: [
{ role: 'system', content: inlineDiff_systemMessage, },
// TODO include more context too
{ role: 'user', content: userContent, }
],
messages,
onText: ({ newText, fullText }) => {
this._writeDiffZoneLLMText(diffZone, fullText, latestCurrentFileEnd, latestOriginalFileStart)
this._refreshStylesAndDiffsInURI(uri)

View file

@ -121,10 +121,7 @@ export const chat_prompt = (instructions: string, selections: CodeSelection[] |
export const inlineDiff_systemMessage = `
export const ctrlLStream_systemMessage = `
You are a coding assistant that applies a diff to a file. You are given the original file \`original_file\`, a diff \`diff\`, and a new file that you are applying the diff to \`new_file\`.
Please finish writing the new file \`new_file\`, according to the diff \`diff\`. You must completely re-write the whole file, using the diff.
@ -255,14 +252,106 @@ export default Sidebar;\`\`\`
export const generateCtrlKPrompt = ({ selection, prefix, suffix, instructions, }: { selection: string, prefix: string, suffix: string, instructions: string, }) => `\
export const ctrlLStream_prompt = ({ originalCode, userMessage }: { originalCode: string, userMessage: string }) => {
return `\
ORIGINAL_CODE
\`\`\`
${originalCode}
\`\`\`
DIFF
\`\`\`
${userMessage}
\`\`\`
INSTRUCTIONS
Please finish writing the new file by applying the diff to the original file. Return ONLY the completion of the file, without any explanation.
`
}
export const ctrlKStream_systemMessage = `\
`
export const ctrlKStream_prefixAndSuffix = ({ fullFileStr, startLine, endLine }: { fullFileStr: string, startLine: number, endLine: number }) => {
const fullFileLines = fullFileStr.split('\n')
// we can optimize this later
const MAX_CHARS = 1024
/*
a
a
a <-- final i (prefix = a\na\n)
a
|b <-- startLine-1 (middle = b\nc\nd\n) <-- initial i (moves up)
c
d| <-- endLine-1 <-- initial j (moves down)
e
e <-- final j (suffix = e\ne\n)
e
e
*/
let prefix = ''
let i = startLine - 1 // 0-indexed exclusive
// we'll include fullFileLines[i...(startLine-1)-1].join('\n') in the prefix.
while (i !== 0) {
const newLine = fullFileLines[i - 1]
if (newLine.length + 1 + prefix.length <= MAX_CHARS) { // +1 to include the \n
prefix = `${newLine}\n${prefix}`
i -= 1
}
else break
}
let suffix = ''
let j = endLine - 1
while (j !== fullFileLines.length - 1) {
const newLine = fullFileLines[j + 1]
if (newLine.length + 1 + suffix.length <= MAX_CHARS) { // +1 to include the \n
suffix = `${suffix}\n${newLine}`
j += 1
}
else break
}
return { prefix, suffix }
}
export const ctrlKStream_prompt = ({ selection, prefix, suffix, userMessage }: { selection: string, prefix: string, suffix: string, userMessage: string, }) => {
const onlySpeaksFIM = false
if (onlySpeaksFIM) {
const preTag = 'PRE'
const sufTag = 'SUF'
const midTag = 'MID'
return `\
<${preTag}>
/* Original Selection:
${selection}*/
/* Instructions: ${userMessage}*/
${prefix}</${preTag}>
<${sufTag}>${suffix}</${sufTag}>
<${midTag}>`
}
// prompt the model on how to do FIM
else {
const preTag = 'PRE'
const sufTag = 'SUF'
const midTag = 'MID'
return `\
Here is the user's original selection:
\`\`\`
<MID>${selection}</MID>
<${midTag}>${selection}</${midTag}>
\`\`\`
The user wants to apply the following instructions to the selection:
${instructions}
${userMessage}
Please rewrite the selection following the user's instructions.
@ -273,10 +362,11 @@ Instructions to follow:
3. Be careful not to duplicate or remove variables, comments, or other syntax by mistake
Complete the following:
\`\`\`
<PRE>${prefix}</PRE>
<SUF>${suffix}</SUF>
<MID>`;
<${preTag}>${prefix}</${preTag}>
<${sufTag}>${suffix}</${sufTag}>
<${midTag}>`
}
};

View file

@ -1,3 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
import { useEffect, useState } from 'react'
import { useIsDark, useSidebarState } from '../util/services.js'
import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js'

View file

@ -1,3 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
import React, { FormEvent, useCallback, useEffect, useRef, useState } from 'react';
import { useSettingsState, useSidebarState, useThreadsState, useQuickEditState, useAccessor } from '../util/services.js';
@ -20,9 +24,13 @@ export const CtrlKChat = ({ diffareaid, onGetInputBox, onUserUpdateText, onChang
const inputContainer = sizerRef.current
if (!inputContainer) return;
// inputBoxRef.current?.onDidHeightChange(height => {
// console.log('NEW HEIGHT',height)
// onChangeHeight(height + 40)
// })
// only observing 1 element
let resizeObserver: ResizeObserver | undefined
resizeObserver = new ResizeObserver((entries) => {
const height = entries[0].borderBoxSize[0].blockSize
console.log('NEW HEIGHT', height)
@ -105,7 +113,7 @@ export const CtrlKChat = ({ diffareaid, onGetInputBox, onUserUpdateText, onChang
>
<div // this div is used to position the input box properly
className={`w-full m-2 z-[999]`}
className={`w-full p-2 z-[999]`}
>
<div className='flex flex-row justify-between items-end gap-1'>
{/* left (input) */}
@ -126,7 +134,7 @@ export const CtrlKChat = ({ diffareaid, onGetInputBox, onUserUpdateText, onChang
</div>
{/* right (button) */}
<div className='flex flex-row items-end w-10'>
<div className='flex flex-row items-end'>
{/* submit / stop button */}
{isStreaming ?
// stop button

View file

@ -1,3 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
import { mountFnGenerator } from '../util/mountFnGenerator.js'
import { CtrlK } from './CtrlK.js'