mirror of
https://github.com/voideditor/void
synced 2026-05-24 01:48:25 +00:00
tool pages work, improve prompt
This commit is contained in:
parent
5699cf19f4
commit
ac1788ae9a
5 changed files with 64 additions and 29 deletions
|
|
@ -356,7 +356,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
|
||||
// add user's message to chat history
|
||||
const instructions = userMessage
|
||||
const userMessageContent = await chat_userMessageContent(instructions, currSelns, currSelns)
|
||||
const userMessageContent = await chat_userMessageContent(instructions, currSelns)
|
||||
const selectionsStr = await chat_selectionsString(prevSelns, currSelns, this._modelService, this._fileService)
|
||||
const userMessageFullContent = chat_userMessageContentWithAllFiles(userMessageContent, selectionsStr)
|
||||
|
||||
|
|
|
|||
|
|
@ -33,10 +33,12 @@ For example, if the user asks you to "make this file look nicer", make sure your
|
|||
|
||||
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.
|
||||
If you are given tools:
|
||||
- Only use tools if the user asks you to do something. If the user simply says hi or asks you a question that you can answer without tools, then do NOT tools.
|
||||
- You are allowed to use tools without asking for permission.
|
||||
- Feel free to use tools to gather context, make suggestions, etc.
|
||||
- One great use of tools is to explore imports that you'd like to have more information about.
|
||||
- NEVER refer to a tool by name when speaking with the user. For example, do NOT say to the user user "I'm going to use \`list_dir\`". Instead, say "I'm going to list all files in ___ directory", etc.
|
||||
- Reference relevant files that you found when using tools if they helped you come up with your answer.
|
||||
- NEVER refer to a tool by name when speaking with the user. For example, do NOT say to the user user "I'm going to use \`list_dir\`". Instead, say "I'm going to list all files in ___ directory", etc. Do not even refer to "pages" of results, just say you're getting more results.
|
||||
|
||||
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. Do not assume the user is talking about any of the examples below.
|
||||
|
|
@ -176,14 +178,14 @@ const stringifyFileSelections = async (fileSelections: FileSelection[], modelSer
|
|||
return fileSlns.map(sel => stringifyFileSelection(sel)).join('\n')
|
||||
}
|
||||
const stringifyCodeSelections = (codeSelections: CodeSelection[]) => {
|
||||
return codeSelections.map(sel => stringifyCodeSelection(sel)).join('\n')
|
||||
return codeSelections.map(sel => stringifyCodeSelection(sel)).join('\n') || null
|
||||
}
|
||||
const stringifySelectionNames = (currSelns: StagingSelectionItem[] | null): string => {
|
||||
if (!currSelns) return ''
|
||||
return currSelns.map(s => `${s.fileURI.fsPath}${s.range ? ` (lines ${s.range.startLineNumber}:${s.range.endLineNumber})` : ''}`).join('\n')
|
||||
}
|
||||
|
||||
export const chat_userMessageContent = async (instructions: string, prevSelns: StagingSelectionItem[] | null, currSelns: StagingSelectionItem[] | null) => {
|
||||
export const chat_userMessageContent = async (instructions: string, currSelns: StagingSelectionItem[] | null) => {
|
||||
|
||||
const selnsStr = stringifySelectionNames(currSelns)
|
||||
|
||||
|
|
@ -198,6 +200,8 @@ export const chat_selectionsString = async (prevSelns: StagingSelectionItem[] |
|
|||
// ADD IN FILES AT TOP
|
||||
const allSelections = [...currSelns || [], ...prevSelns || []]
|
||||
|
||||
if (allSelections.length === 0) return null
|
||||
|
||||
const codeSelections: CodeSelection[] = []
|
||||
const fileSelections: FileSelection[] = []
|
||||
const filesURIs = new Set<string>()
|
||||
|
|
@ -219,17 +223,17 @@ export const chat_selectionsString = async (prevSelns: StagingSelectionItem[] |
|
|||
const filesStr = await stringifyFileSelections(fileSelections, modelService, fileService)
|
||||
const selnsStr = stringifyCodeSelections(codeSelections)
|
||||
|
||||
let str = ''
|
||||
|
||||
str += 'ALL FILE CONTENTS\n'
|
||||
if (filesStr) str += `${filesStr}\n`
|
||||
if (selnsStr) str += `${selnsStr}\n`
|
||||
if (filesStr || selnsStr) return `\
|
||||
ALL FILE CONTENTS
|
||||
${filesStr}
|
||||
${selnsStr}`
|
||||
|
||||
return str;
|
||||
return null
|
||||
}
|
||||
|
||||
export const chat_userMessageContentWithAllFilesToo = (userMessage: string, selectionsString: string | undefined) => {
|
||||
if (userMessage) return `${userMessage}\n${selectionsString}\n`
|
||||
export const chat_userMessageContentWithAllFilesToo = (userMessage: string, selectionsString: string | null) => {
|
||||
if (userMessage) return `${userMessage}${selectionsString ? `\n${selectionsString}` : ''}`
|
||||
else return userMessage
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ const getApplyBoxId = ({ threadId, messageIdx, tokenIdx }: ApplyBoxLocation) =>
|
|||
|
||||
|
||||
|
||||
const ApplyButtonsOnHover = ({ applyStr, applyBoxId }: { applyStr: string, applyBoxId: string }) => {
|
||||
const ApplyButtonsOnHover = ({ applyStr }: { applyStr: string }) => {
|
||||
const accessor = useAccessor()
|
||||
|
||||
const [copyButtonState, setCopyButtonState] = useState(CopyButtonState.Copy)
|
||||
|
|
@ -120,7 +120,7 @@ const RenderToken = ({ token, nested = false, noSpace = false, chatMessageLocati
|
|||
return <BlockCode
|
||||
initValue={t.text}
|
||||
language={t.lang === undefined ? undefined : nameToVscodeLanguage[t.lang]}
|
||||
buttonsOnHover={applyBoxId && <ApplyButtonsOnHover applyStr={t.text} applyBoxId={applyBoxId} />}
|
||||
buttonsOnHover={applyBoxId && <ApplyButtonsOnHover applyStr={t.text} />}
|
||||
/>
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { CancellationToken } from '../../../../base/common/cancellation.js'
|
||||
import { URI } from '../../../../base/common/uri.js'
|
||||
import { IModelService } from '../../../../editor/common/services/model.js'
|
||||
import { IFileService, IFileStat } from '../../../../platform/files/common/files.js'
|
||||
import { IFileService } from '../../../../platform/files/common/files.js'
|
||||
import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js'
|
||||
import { createDecorator, IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'
|
||||
import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'
|
||||
|
|
@ -93,22 +93,46 @@ export type ToolCallReturnType<T extends ToolName>
|
|||
export type ToolFns = { [T in ToolName]: (p: string) => Promise<ToolCallReturnType<T>> }
|
||||
export type ToolResultToString = { [T in ToolName]: (result: ToolCallReturnType<T>) => string }
|
||||
|
||||
const MAX_DEPTH = 1
|
||||
const MAX_CHILDREN = 500
|
||||
async function generateDirectoryTreeMd(fileService: IFileService, rootURI: URI, pageNumber: number): Promise<string> {
|
||||
let output = '';
|
||||
|
||||
async function generateDirectoryTreeMd(fileService: IFileService, rootURI: URI): Promise<string> {
|
||||
let output = ''
|
||||
function traverseChildren(children: IFileStat[], depth: number) {
|
||||
const indentation = ' '.repeat(depth);
|
||||
for (const child of children) {
|
||||
output += `${indentation}- ${child.name}\n`;
|
||||
traverseChildren(child.children ?? [], depth + 1);
|
||||
const indentation = (depth: number, isLast: boolean): string => {
|
||||
if (depth === 0) return '';
|
||||
return `${'| '.repeat(depth - 1)}${isLast ? '└── ' : '├── '}`;
|
||||
};
|
||||
|
||||
async function traverseChildren(uri: URI, depth: number, isLast: boolean) {
|
||||
const stat = await fileService.resolve(uri, { resolveMetadata: false });
|
||||
|
||||
if ((depth === 0 && pageNumber === 1) || depth !== 0)
|
||||
output += `${indentation(depth, isLast)}${stat.name}${stat.isDirectory ? '/' : ''}${stat.isSymbolicLink ? ` (symbolic link)` : ''}\n`; // TODO say where symlink links to
|
||||
|
||||
// list children
|
||||
const originalChildrenLength = stat.children?.length ?? 0
|
||||
const fromChildIdx = MAX_CHILDREN * (pageNumber - 1)
|
||||
const toChildIdx = MAX_CHILDREN * pageNumber - 1 // INCLUSIVE
|
||||
const listChildren = stat.children?.slice(fromChildIdx, toChildIdx + 1) ?? [];
|
||||
|
||||
if (!stat.isDirectory) return;
|
||||
|
||||
if (listChildren.length === 0) return
|
||||
if (depth === MAX_DEPTH) return // right now MAX_DEPTH=1 to make pagination work nicely
|
||||
|
||||
for (let i = 0; i < Math.min(listChildren.length, MAX_CHILDREN); i++) {
|
||||
await traverseChildren(listChildren[i].resource, depth + 1, i === listChildren.length - 1);
|
||||
}
|
||||
const nCutoffChildren = (originalChildrenLength - 1) - toChildIdx
|
||||
if (nCutoffChildren > 0) {
|
||||
output += `${indentation(depth + 1, true)}(${nCutoffChildren} results remaining...)\n`
|
||||
}
|
||||
|
||||
}
|
||||
const stat = await fileService.resolve(rootURI, { resolveMetadata: false });
|
||||
|
||||
// kickstart recursion
|
||||
output += `${stat.name}\n`;
|
||||
traverseChildren(stat.children ?? [], 1);
|
||||
await traverseChildren(rootURI, 0, false);
|
||||
|
||||
console.log('OUTPUT', output);
|
||||
return output;
|
||||
}
|
||||
|
||||
|
|
@ -119,6 +143,12 @@ const validateURI = (uriStr: unknown) => {
|
|||
return uri
|
||||
}
|
||||
|
||||
const validatePageNum = (pageNumberUnknown: unknown) => {
|
||||
const proposedPageNum = Number.parseInt(pageNumberUnknown + '')
|
||||
const num = Number.isInteger(proposedPageNum) ? proposedPageNum : 1
|
||||
const pageNumber = num < 1 ? 1 : num
|
||||
return pageNumber
|
||||
}
|
||||
export interface IToolsService {
|
||||
readonly _serviceBrand: undefined;
|
||||
toolFns: ToolFns;
|
||||
|
|
@ -170,12 +200,13 @@ export class ToolsService implements IToolsService {
|
|||
list_dir: async (s: string) => {
|
||||
const o = parseObj(s)
|
||||
if (!o) return invalidToolParamMsg
|
||||
const { uri: uriStr } = o
|
||||
const { uri: uriStr, pageNumber: pageNumberUnknown } = o
|
||||
|
||||
const uri = validateURI(uriStr)
|
||||
const pageNumber = validatePageNum(pageNumberUnknown)
|
||||
|
||||
// TODO!!!! check to make sure in workspace
|
||||
// TODO check to make sure is not gitignored
|
||||
const treeStr = await generateDirectoryTreeMd(fileService, uri)
|
||||
const treeStr = await generateDirectoryTreeMd(fileService, uri, pageNumber)
|
||||
return treeStr
|
||||
},
|
||||
pathname_search: async (s: string) => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ToolName, toolNames } from '../../common/toolsService';
|
||||
import { ToolName, toolNames } from '../../common/toolsService.js';
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue