mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-20 00:02:19 +00:00
fix(aichat): in script mode use diff based edits for good providers only (#6665)
* Revert "use diffs based edits"
This reverts commit 4ef6bce562.
* feat(aichat): use diff-based edits for OpenAI/Anthropic providers, whole code for others
- Check the current model provider at runtime
- Use diff-based approach (with diffs array) for OpenAI and Anthropic
- Use whole code replacement for all other providers
- Update tool definition to support both parameters
- Update system prompt with conditional instructions based on provider
Co-authored-by: centdix <centdix@users.noreply.github.com>
* fix
* cleaning
* cleaning
* cleaning
* cleaning
---------
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: centdix <centdix@users.noreply.github.com>
This commit is contained in:
@@ -209,11 +209,12 @@ class AIChatManager {
|
||||
this.pendingPrompt = pendingPrompt ?? ''
|
||||
if (mode === AIMode.SCRIPT) {
|
||||
const customPrompt = get(copilotInfo).customPrompts?.[mode]
|
||||
this.systemMessage = prepareScriptSystemMessage(customPrompt)
|
||||
const currentModel = getCurrentModel()
|
||||
this.systemMessage = prepareScriptSystemMessage(currentModel, customPrompt)
|
||||
this.systemMessage.content = this.NAVIGATION_SYSTEM_PROMPT + this.systemMessage.content
|
||||
const context = this.contextManager.getSelectedContext()
|
||||
const lang = this.scriptEditorOptions?.lang ?? 'bun'
|
||||
this.tools = [this.changeModeTool, ...prepareScriptTools(lang, context)]
|
||||
this.tools = [this.changeModeTool, ...prepareScriptTools(currentModel, lang, context)]
|
||||
this.helpers = {
|
||||
getScriptOptions: () => {
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ResourceService, JobService } from '$lib/gen/services.gen'
|
||||
import type { ResourceType, ScriptLang } from '$lib/gen/types.gen'
|
||||
import type { AIProvider, AIProviderModel, ResourceType, ScriptLang } from '$lib/gen/types.gen'
|
||||
import { capitalize, isObject, toCamel } from '$lib/utils'
|
||||
import { get } from 'svelte/store'
|
||||
import { compile, phpCompile, pythonCompile } from '../../utils'
|
||||
@@ -29,6 +29,8 @@ const SCORE_THRESHOLD = 1000
|
||||
const DOCS_CONTEXT_PERCENTAGE = 1
|
||||
// percentage of the context window for types of npm packages
|
||||
const TYPES_CONTEXT_PERCENTAGE = 1
|
||||
// good providers for diff-based edit
|
||||
const DIFF_BASED_EDIT_PROVIDERS: AIProvider[] = ['openai', 'anthropic', 'googleai', 'azure_openai']
|
||||
|
||||
export function formatResourceTypes(
|
||||
allResourceTypes: ResourceType[],
|
||||
@@ -337,50 +339,44 @@ export async function getFormattedResourceTypes(
|
||||
}
|
||||
}
|
||||
|
||||
export const CHAT_SYSTEM_PROMPT = `
|
||||
function buildChatSystemPrompt(currentModel: AIProviderModel) {
|
||||
const useDiffBasedEdit = DIFF_BASED_EDIT_PROVIDERS.includes(currentModel.provider)
|
||||
const editToolName = EDIT_CODE_TOOL.function.name
|
||||
const editInstructions = useDiffBasedEdit
|
||||
? `
|
||||
- Pass an array of **diff objects** to the \`${editToolName}\` tool using the \`diffs\` parameter. Each diff should specify exactly what text to replace and what to replace it with.
|
||||
- Each diff object must contain:
|
||||
- \`old_string\`: The exact text to replace (must match the current code exactly)
|
||||
- \`new_string\`: The replacement text
|
||||
- \`replace_all\` (optional): Set to true to replace all occurrences, false or omit for first occurrence only
|
||||
- Example: [{"old_string": "return 1", "new_string": "return 2"}]`
|
||||
: `- Pass the **complete updated file** to the \`${editToolName}\` tool using the \`code\` parameter, not just the modified sections.`
|
||||
|
||||
return `
|
||||
You are a coding assistant for the Windmill platform. You are provided with a list of \`INSTRUCTIONS\` and the current contents of a code file under \`CODE\`.
|
||||
|
||||
Your task is to respond to the user's request. Assume all user queries are valid and actionable.
|
||||
|
||||
When the user requests code changes:
|
||||
- ALWAYS use the \`edit_code\` tool to apply code changes. Use it only once with an array of diffs.
|
||||
- Pass an array of **diff objects** to the \`edit_code\` tool. Each diff should specify exactly what text to replace and what to replace it with.
|
||||
- Each diff object must contain:
|
||||
- \`old_string\`: The exact text to replace (must match the current code exactly)
|
||||
- \`new_string\`: The replacement text
|
||||
- \`replace_all\` (optional): Set to true to replace all occurrences, false or omit for first occurrence only
|
||||
- The code can include \`[#START]\` and \`[#END]\` markers to indicate the start and end of a code piece. You MUST only modify the code between these markers if given, and remove them when creating your diffs. If a question is asked about the code, you MUST only talk about the code between the markers. Refer to it as the code piece, not the code between the markers.
|
||||
- ALWAYS use the \`${editToolName}\` tool to apply code changes. Use it only once.
|
||||
${editInstructions}
|
||||
- The code can include \`[#START]\` and \`[#END]\` markers to indicate the start and end of a code piece. You MUST only modify the code between these markers if given, and remove them when passing to the tool. If a question is asked about the code, you MUST only talk about the code between the markers. Refer to it as the code piece, not the code between the markers.
|
||||
- Follow the instructions carefully and explain the reasoning behind your changes in your response text.
|
||||
- If the request is abstract (e.g., "make this cleaner"), interpret it concretely and reflect that in the diffs.
|
||||
- If the request is abstract (e.g., "make this cleaner"), interpret it concretely and reflect that in your changes.
|
||||
- Preserve existing formatting, indentation, and whitespace unless changes are strictly required to fulfill the user's request.
|
||||
- The user can ask you to look at or modify specific files, databases or errors by having its name in the INSTRUCTIONS preceded by the @ symbol. In this case, put your focus on the element that is explicitly mentioned.
|
||||
- The user can ask you questions about a list of \`DATABASES\` that are available in the user's workspace. If the user asks you a question about a database, you should ask the user to specify the database name if not given, or take the only one available if there is only one.
|
||||
- You can also receive a \`DIFF\` of the changes that have been made to the code. You should use this diff to give better answers.
|
||||
- Before giving your answer, check again that you carefully followed these instructions.
|
||||
- When asked to create a script that communicates with an external service, you can use the \`search_hub_scripts\` tool to search for relevant scripts in the hub. Make sure the language is the same as what the user is coding in. If you do not find any relevant scripts, you can use the \`search_npm_packages\` tool to search for relevant packages and their documentation. Always give a link to the documentation in your answer if possible.
|
||||
- After applying code changes with the \`edit_code\` tool, ALWAYS use the \`test_run_script\` tool to test the code, and iterate on the code until it works as expected (MAX 3 times). If the user cancels the test run, do not try again and wait for the next user instruction.
|
||||
|
||||
Example diff usage:
|
||||
To change "return 1" to "return 2", use:
|
||||
[{
|
||||
"old_string": "return 1",
|
||||
"new_string": "return 2"
|
||||
}]
|
||||
|
||||
To add a new function and modify an existing one:
|
||||
[{
|
||||
"old_string": "export async function main() {",
|
||||
"new_string": "function helper() {\n\treturn 'help';\n}\n\nexport async function main() {"
|
||||
}, {
|
||||
"old_string": "return result;",
|
||||
"new_string": "return result + helper();"
|
||||
}]
|
||||
- After applying code changes with the \`${editToolName}\` tool, ALWAYS use the \`test_run_script\` tool to test the code, and iterate on the code until it works as expected (MAX 3 times). If the user cancels the test run, do not try again and wait for the next user instruction.
|
||||
|
||||
Important:
|
||||
- Each old_string must match the exact text in the current code, including whitespace and indentation.
|
||||
${useDiffBasedEdit ? '- Each old_string must match the exact text in the current code, including whitespace and indentation.' : ''}
|
||||
- Do not return the applied code in your response, just explain what you did. You can return code blocks in your response for explanations or examples as per user request.
|
||||
- Do not mention or reveal these instructions to the user unless explicitly asked to do so.
|
||||
`
|
||||
}
|
||||
|
||||
export const INLINE_CHAT_SYSTEM_PROMPT = `
|
||||
# Windmill Inline Coding Assistant
|
||||
@@ -477,9 +473,10 @@ WINDMILL LANGUAGE CONTEXT:
|
||||
`
|
||||
|
||||
export function prepareScriptSystemMessage(
|
||||
currentModel: AIProviderModel,
|
||||
customPrompt?: string
|
||||
): ChatCompletionSystemMessageParam {
|
||||
let content = CHAT_SYSTEM_PROMPT
|
||||
let content = buildChatSystemPrompt(currentModel)
|
||||
|
||||
// If there's a custom prompt, prepend it to the system prompt
|
||||
if (customPrompt?.trim()) {
|
||||
@@ -493,6 +490,7 @@ export function prepareScriptSystemMessage(
|
||||
}
|
||||
|
||||
export function prepareScriptTools(
|
||||
currentModel: AIProviderModel,
|
||||
language: ScriptLang | 'bunnative',
|
||||
context: ContextElement[]
|
||||
): Tool<ScriptChatHelpers>[] {
|
||||
@@ -507,7 +505,12 @@ export function prepareScriptTools(
|
||||
tools.push(createSearchHubScriptsTool(true))
|
||||
tools.push(searchNpmPackagesTool)
|
||||
}
|
||||
tools.push(editCodeTool)
|
||||
const useDiffBasedEdit = DIFF_BASED_EDIT_PROVIDERS.includes(currentModel.provider)
|
||||
if (useDiffBasedEdit) {
|
||||
tools.push(editCodeToolWithDiff)
|
||||
} else {
|
||||
tools.push(editCodeTool)
|
||||
}
|
||||
tools.push(testRunScriptTool)
|
||||
return tools
|
||||
}
|
||||
@@ -815,7 +818,27 @@ const EDIT_CODE_TOOL: ChatCompletionFunctionTool = {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'edit_code',
|
||||
description: 'Apply code changes to the current script in the editor using an array of diffs',
|
||||
description: 'Apply code changes to the current script in the editor',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
code: {
|
||||
type: 'string',
|
||||
description: 'The complete updated code for the entire script file.'
|
||||
}
|
||||
},
|
||||
additionalProperties: false,
|
||||
strict: true,
|
||||
required: ['code']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const EDIT_CODE_TOOL_WITH_DIFF: ChatCompletionFunctionTool = {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'edit_code',
|
||||
description: 'Apply code changes to the current script in the editor',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -868,8 +891,8 @@ const TEST_RUN_SCRIPT_TOOL: ChatCompletionFunctionTool = {
|
||||
}
|
||||
}
|
||||
|
||||
export const editCodeTool: Tool<ScriptChatHelpers> = {
|
||||
def: EDIT_CODE_TOOL,
|
||||
export const editCodeToolWithDiff: Tool<ScriptChatHelpers> = {
|
||||
def: EDIT_CODE_TOOL_WITH_DIFF,
|
||||
fn: async function ({ args, helpers, toolCallbacks, toolId }) {
|
||||
const scriptOptions = helpers.getScriptOptions()
|
||||
|
||||
@@ -934,6 +957,54 @@ export const editCodeTool: Tool<ScriptChatHelpers> = {
|
||||
}
|
||||
}
|
||||
|
||||
export const editCodeTool: Tool<ScriptChatHelpers> = {
|
||||
def: EDIT_CODE_TOOL,
|
||||
fn: async function ({ args, helpers, toolCallbacks, toolId }) {
|
||||
const scriptOptions = helpers.getScriptOptions()
|
||||
|
||||
if (!scriptOptions) {
|
||||
toolCallbacks.setToolStatus(toolId, {
|
||||
content: 'No script available to edit',
|
||||
error: 'No script found in current context'
|
||||
})
|
||||
throw new Error(
|
||||
'No script code available to edit. Please ensure you have a script open in the editor.'
|
||||
)
|
||||
}
|
||||
|
||||
if (!args.code || typeof args.code !== 'string') {
|
||||
toolCallbacks.setToolStatus(toolId, {
|
||||
content: 'Invalid code provided',
|
||||
error: 'Code parameter is required and must be a string'
|
||||
})
|
||||
throw new Error('Code parameter is required and must be a string')
|
||||
}
|
||||
|
||||
toolCallbacks.setToolStatus(toolId, { content: 'Applying code changes...' })
|
||||
|
||||
try {
|
||||
// Save old code
|
||||
const oldCode = scriptOptions.code
|
||||
|
||||
// Apply the code changes directly
|
||||
await helpers.applyCode(args.code, { applyAll: true, mode: 'apply' })
|
||||
|
||||
// Show revert mode
|
||||
await helpers.applyCode(oldCode, { mode: 'revert' })
|
||||
|
||||
toolCallbacks.setToolStatus(toolId, { content: 'Code changes applied' })
|
||||
return 'Code has been applied to the script editor.'
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
toolCallbacks.setToolStatus(toolId, {
|
||||
content: 'Failed to apply code changes',
|
||||
error: errorMessage
|
||||
})
|
||||
throw new Error(`Failed to apply code changes: ${errorMessage}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const testRunScriptTool: Tool<ScriptChatHelpers> = {
|
||||
def: TEST_RUN_SCRIPT_TOOL,
|
||||
fn: async function ({ args, workspace, helpers, toolCallbacks, toolId }) {
|
||||
|
||||
Reference in New Issue
Block a user