mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-20 00:02:19 +00:00
feat: add wac ai context for frontend chat (#9021)
* feat: add wac ai context * fix: limit wac context languages * fix: pass wac auto kind in flow script drawer
This commit is contained in:
@@ -6165,12 +6165,12 @@ Use this guide when writing or modifying Windmill Workflow-as-Code (WAC) scripts
|
||||
WAC is authored as a Windmill script and deployed with the normal script workflow. It is not an OpenFlow YAML flow.
|
||||
|
||||
Supported WAC authoring targets:
|
||||
- TypeScript scripts that import from \`windmill-client\`
|
||||
- Bun TypeScript scripts that import from \`windmill-client\`
|
||||
- Python 3 scripts that import from \`wmill\`
|
||||
|
||||
## File Shape
|
||||
|
||||
TypeScript:
|
||||
Bun TypeScript:
|
||||
|
||||
\`\`\`typescript
|
||||
import {
|
||||
@@ -6212,7 +6212,7 @@ async def main(x: str):
|
||||
|
||||
Rules:
|
||||
- Do not call \`main\`.
|
||||
- TypeScript should export the workflow entrypoint, preferably \`export const main = workflow(async (...) => { ... })\`.
|
||||
- Bun TypeScript should export the workflow entrypoint, preferably \`export const main = workflow(async (...) => { ... })\`.
|
||||
- Python must use \`@workflow\` on an async top-level function, usually \`main\`.
|
||||
- Define task functions and \`taskScript\`/\`task_script\` or \`taskFlow\`/\`task_flow\` assignments at module top level with stable names.
|
||||
- Use the exact SDK names. Do not alias \`workflow\`, \`task\`, \`taskScript\`, \`taskFlow\`, \`step\`, \`sleep\`, \`waitForApproval\`, \`task_script\`, \`task_flow\`, or \`wait_for_approval\`; the WAC parser recognizes these names directly.
|
||||
|
||||
@@ -129,6 +129,7 @@
|
||||
useWebsockets?: boolean
|
||||
small?: boolean
|
||||
scriptLang: Preview['language'] | 'bunnative' | 'tsx' | 'jsx' | 'json' | undefined
|
||||
workflowAsCode?: boolean
|
||||
disabled?: boolean
|
||||
lineNumbersMinChars?: number
|
||||
files?: Record<string, { code: string; readonly?: boolean }> | undefined
|
||||
@@ -163,6 +164,7 @@
|
||||
useWebsockets = true,
|
||||
small = false,
|
||||
scriptLang,
|
||||
workflowAsCode = false,
|
||||
disabled = false,
|
||||
lineNumbersMinChars = 3,
|
||||
files = {},
|
||||
@@ -819,12 +821,13 @@
|
||||
|
||||
function addAutoCompletor(
|
||||
editor: meditor.IStandaloneCodeEditor,
|
||||
scriptLang: ScriptLang | 'bunnative' | 'jsx' | 'tsx' | 'json'
|
||||
scriptLang: ScriptLang | 'bunnative' | 'jsx' | 'tsx' | 'json',
|
||||
workflowAsCode: boolean
|
||||
) {
|
||||
if (autocompletor) {
|
||||
autocompletor.dispose()
|
||||
}
|
||||
autocompletor = new Autocompletor(editor, scriptLang)
|
||||
autocompletor = new Autocompletor(editor, scriptLang, { workflowAsCode })
|
||||
}
|
||||
|
||||
const outputChannel = {
|
||||
@@ -1875,13 +1878,14 @@
|
||||
;(!dbSchema || lang !== 'graphql') && untrack(() => disposeGaphqlService())
|
||||
})
|
||||
$effect(() => {
|
||||
const currentWorkflowAsCode = workflowAsCode
|
||||
$copilotInfo.enabled &&
|
||||
$codeCompletionSessionEnabled &&
|
||||
Autocompletor.isProviderModelSupported($copilotInfo.codeCompletionModel) &&
|
||||
initialized &&
|
||||
editor &&
|
||||
scriptLang &&
|
||||
untrack(() => editor && addAutoCompletor(editor, scriptLang))
|
||||
untrack(() => editor && addAutoCompletor(editor, scriptLang, currentWorkflowAsCode))
|
||||
})
|
||||
$effect(() => {
|
||||
$copilotInfo.enabled && initialized && editor && untrack(() => editor && addChatHandler(editor))
|
||||
|
||||
@@ -2117,6 +2117,7 @@
|
||||
bind:code={script.content}
|
||||
lang={script.language}
|
||||
kind={script.kind}
|
||||
autoKind={script.auto_kind}
|
||||
{template}
|
||||
tag={script.tag}
|
||||
lastSavedCode={savedScript?.draft?.content}
|
||||
|
||||
@@ -116,6 +116,7 @@
|
||||
path: string | undefined
|
||||
lang: Preview['language']
|
||||
kind?: string | undefined
|
||||
autoKind?: string | undefined
|
||||
template?:
|
||||
| 'pgsql'
|
||||
| 'mysql'
|
||||
@@ -161,6 +162,7 @@
|
||||
path,
|
||||
lang,
|
||||
kind = undefined,
|
||||
autoKind = undefined,
|
||||
template = 'script',
|
||||
tag,
|
||||
fixedOverflowWidgets = true,
|
||||
@@ -1234,7 +1236,16 @@
|
||||
)
|
||||
}
|
||||
|
||||
let isWac = $derived(code && lang ? isWorkflowAsCode(code, lang) : false)
|
||||
const WAC_CONTEXT_LANGUAGES = ['python3', 'bun']
|
||||
let isWac = $derived(
|
||||
template === 'wac_python' ||
|
||||
template === 'wac_typescript' ||
|
||||
autoKind === 'wac' ||
|
||||
(code && lang ? isWorkflowAsCode(code, lang) : false)
|
||||
)
|
||||
let workflowAsCodeAiContext = $derived(
|
||||
activeModuleTab === null && isWac && WAC_CONTEXT_LANGUAGES.includes(lang ?? '')
|
||||
)
|
||||
let showTabs = $derived(hasPreprocessor || isWac)
|
||||
$effect(() => {
|
||||
!hasPreprocessor && (selectedTab = 'main')
|
||||
@@ -1304,7 +1315,8 @@
|
||||
path,
|
||||
lastSavedCode,
|
||||
lastDeployedCode,
|
||||
diffMode
|
||||
diffMode,
|
||||
workflowAsCode: workflowAsCodeAiContext
|
||||
}
|
||||
untrack(() => {
|
||||
aiChatManager.scriptEditorOptions = options
|
||||
@@ -2075,6 +2087,7 @@
|
||||
automaticLayout={true}
|
||||
{fixedOverflowWidgets}
|
||||
{args}
|
||||
workflowAsCode={workflowAsCodeAiContext}
|
||||
{enablePreprocessorSnippet}
|
||||
preparedAssetsSqlQueries={preparedSqlQueries.current}
|
||||
customTag={tag}
|
||||
|
||||
@@ -66,6 +66,7 @@ export class Autocompletor {
|
||||
max: 10
|
||||
})
|
||||
#scriptLang: ScriptLang | 'bunnative' | 'jsx' | 'tsx' | 'json'
|
||||
#workflowAsCode: boolean
|
||||
#abortController: AbortController = new AbortController()
|
||||
#completionDisposable: IDisposable
|
||||
#cursorDisposable: IDisposable
|
||||
@@ -76,7 +77,8 @@ export class Autocompletor {
|
||||
|
||||
constructor(
|
||||
editor: meditor.IStandaloneCodeEditor,
|
||||
scriptLang: ScriptLang | 'bunnative' | 'jsx' | 'tsx' | 'json'
|
||||
scriptLang: ScriptLang | 'bunnative' | 'jsx' | 'tsx' | 'json',
|
||||
options: { workflowAsCode?: boolean } = {}
|
||||
) {
|
||||
setGlobalCSS(
|
||||
'ai-chat-autocomplete',
|
||||
@@ -87,6 +89,7 @@ export class Autocompletor {
|
||||
`
|
||||
)
|
||||
this.#scriptLang = scriptLang
|
||||
this.#workflowAsCode = options.workflowAsCode ?? false
|
||||
|
||||
const deletionsCues = editor.createDecorationsCollection()
|
||||
|
||||
@@ -520,7 +523,8 @@ export class Autocompletor {
|
||||
suffix,
|
||||
scriptLang: this.#scriptLang,
|
||||
markers: markersAtCursor,
|
||||
libraries: librariesCompletions
|
||||
libraries: librariesCompletions,
|
||||
workflowAsCode: this.#workflowAsCode
|
||||
},
|
||||
this.#abortController
|
||||
)
|
||||
|
||||
@@ -20,6 +20,7 @@ export async function autocompleteRequest(
|
||||
scriptLang: ScriptLang | 'bunnative' | 'jsx' | 'tsx' | 'json'
|
||||
markers: editor.IMarker[]
|
||||
libraries: string
|
||||
workflowAsCode?: boolean
|
||||
},
|
||||
abortController: AbortController
|
||||
) {
|
||||
@@ -38,7 +39,10 @@ export async function autocompleteRequest(
|
||||
'You are a code completion assistant. You are given three important contexts (<LANGUAGE CONTEXT>, <DIAGNOSTICS>, <LIBRARY METHODS>) to help you complete the code.\n'
|
||||
)
|
||||
contextLines += comment(commentSymbol, 'LANGUAGE CONTEXT:\n')
|
||||
contextLines += comment(commentSymbol, getLangContext(context.scriptLang) + '\n')
|
||||
contextLines += comment(
|
||||
commentSymbol,
|
||||
getLangContext(context.scriptLang, { workflowAsCode: context.workflowAsCode }) + '\n'
|
||||
)
|
||||
contextLines += comment(commentSymbol, 'DIAGNOSTICS:\n')
|
||||
contextLines += comment(commentSymbol, context.markers.map((m) => m.message).join('\n') + '\n')
|
||||
contextLines += comment(commentSymbol, 'LIBRARY METHODS:\n')
|
||||
|
||||
@@ -256,6 +256,8 @@ class AIChatManager {
|
||||
options?: {
|
||||
closeScriptSettings?: boolean
|
||||
lang?: ScriptLang | 'bunnative'
|
||||
isPreprocessor?: boolean
|
||||
workflowAsCode?: boolean
|
||||
}
|
||||
) {
|
||||
if (mode === AIMode.SCRIPT && !tryGetCurrentModel()) return
|
||||
@@ -265,8 +267,16 @@ class AIChatManager {
|
||||
const currentModel = getCurrentModel()
|
||||
const customPrompt = getCombinedCustomPrompt(mode)
|
||||
const lang = options?.lang ?? this.scriptEditorOptions?.lang ?? 'bun'
|
||||
const workflowAsCode =
|
||||
options?.workflowAsCode ??
|
||||
(options?.lang ? false : (this.scriptEditorOptions?.workflowAsCode ?? false))
|
||||
const context = this.contextManager.getSelectedContext()
|
||||
this.systemMessage = prepareScriptSystemMessage(currentModel, lang, {}, customPrompt)
|
||||
this.systemMessage = prepareScriptSystemMessage(
|
||||
currentModel,
|
||||
lang,
|
||||
{ isPreprocessor: options?.isPreprocessor, workflowAsCode },
|
||||
customPrompt
|
||||
)
|
||||
this.systemMessage.content = this.systemMessage.content
|
||||
this.tools = [...prepareScriptTools(currentModel, lang, context)]
|
||||
this.helpers = {
|
||||
@@ -533,7 +543,9 @@ class AIChatManager {
|
||||
|
||||
const systemMessage: ChatCompletionSystemMessageParam = {
|
||||
role: 'system',
|
||||
content: prepareInlineChatSystemPrompt(lang)
|
||||
content: prepareInlineChatSystemPrompt(lang, {
|
||||
workflowAsCode: this.scriptEditorOptions?.workflowAsCode ?? false
|
||||
})
|
||||
}
|
||||
|
||||
let reply = ''
|
||||
@@ -606,9 +618,15 @@ class AIChatManager {
|
||||
} = {}
|
||||
) => {
|
||||
if (options.mode) {
|
||||
this.changeMode(options.mode, undefined, { lang: options.lang })
|
||||
this.changeMode(options.mode, undefined, {
|
||||
lang: options.lang,
|
||||
isPreprocessor: options.isPreprocessor
|
||||
})
|
||||
} else {
|
||||
this.changeMode(this.mode, undefined, { lang: options.lang })
|
||||
this.changeMode(this.mode, undefined, {
|
||||
lang: options.lang,
|
||||
isPreprocessor: options.isPreprocessor
|
||||
})
|
||||
}
|
||||
if (options.instructions) {
|
||||
this.instructions = options.instructions
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface ScriptOptions {
|
||||
lastSavedCode?: string
|
||||
lastDeployedCode?: string
|
||||
diffMode: boolean
|
||||
workflowAsCode?: boolean
|
||||
}
|
||||
|
||||
export interface FlowOptions {
|
||||
|
||||
@@ -27,7 +27,7 @@ import { getModelContextWindow } from '../../lib'
|
||||
import type { ReviewChangesOpts } from '../monaco-adapter'
|
||||
import { getCurrentModel } from '$lib/aiStore'
|
||||
import { getDbSchemas } from '$lib/components/apps/components/display/dbtable/metadata'
|
||||
import { getScriptPrompt } from '$system_prompts'
|
||||
import { getScriptPrompt, getWorkflowAsCodePrompt } from '$system_prompts'
|
||||
|
||||
// Score threshold for npm packages search filtering
|
||||
const SCORE_THRESHOLD = 1000
|
||||
@@ -104,11 +104,22 @@ export function getLangContext(
|
||||
lang: ScriptLang | 'bunnative' | 'jsx' | 'tsx' | 'json',
|
||||
{
|
||||
allowResourcesFetch = false,
|
||||
isPreprocessor = false
|
||||
}: { allowResourcesFetch?: boolean; isPreprocessor?: boolean; isFailure?: boolean } = {}
|
||||
isPreprocessor = false,
|
||||
workflowAsCode = false
|
||||
}: {
|
||||
allowResourcesFetch?: boolean
|
||||
isPreprocessor?: boolean
|
||||
isFailure?: boolean
|
||||
workflowAsCode?: boolean
|
||||
} = {}
|
||||
): string {
|
||||
// Get base language context from centralized prompts
|
||||
let context = getScriptPrompt(lang)
|
||||
let context = workflowAsCode ? getWorkflowAsCodePrompt(lang) : getScriptPrompt(lang)
|
||||
|
||||
// Fallback to the regular script prompt if WAC context is requested for an unsupported language.
|
||||
if (!context) {
|
||||
context = getScriptPrompt(lang)
|
||||
}
|
||||
|
||||
// Add tool usage instructions for applicable languages
|
||||
if (['python3', 'php', 'bun', 'deno', 'nativets', 'bunnative'].includes(lang)) {
|
||||
@@ -117,8 +128,8 @@ export function getLangContext(
|
||||
}
|
||||
}
|
||||
|
||||
// Note preprocessor function naming if applicable
|
||||
if (isPreprocessor) {
|
||||
// Note preprocessor function naming if applicable. WAC scripts are not preprocessors.
|
||||
if (isPreprocessor && !workflowAsCode) {
|
||||
context +=
|
||||
'\n\nThe main function for this script should be named `preprocessor` instead of `main`.'
|
||||
}
|
||||
@@ -277,8 +288,15 @@ export async function main() {
|
||||
\`\`\`
|
||||
`
|
||||
|
||||
export function prepareInlineChatSystemPrompt(lang: ScriptLang | 'bunnative') {
|
||||
return INLINE_CHAT_SYSTEM_PROMPT + getLangContext(lang, { allowResourcesFetch: true })
|
||||
export function prepareInlineChatSystemPrompt(
|
||||
lang: ScriptLang | 'bunnative',
|
||||
options: { workflowAsCode?: boolean } = {}
|
||||
) {
|
||||
return (
|
||||
INLINE_CHAT_SYSTEM_PROMPT +
|
||||
'\n\n' +
|
||||
getLangContext(lang, { allowResourcesFetch: true, workflowAsCode: options.workflowAsCode })
|
||||
)
|
||||
}
|
||||
|
||||
export const CHAT_USER_PROMPT = `
|
||||
@@ -290,7 +308,7 @@ INSTRUCTIONS:
|
||||
export function prepareScriptSystemMessage(
|
||||
currentModel: AIProviderModel,
|
||||
language: ScriptLang | 'bunnative',
|
||||
options: { isPreprocessor?: boolean; allowResourcesFetch?: boolean } = {},
|
||||
options: { isPreprocessor?: boolean; allowResourcesFetch?: boolean; workflowAsCode?: boolean } = {},
|
||||
customPrompt?: string
|
||||
): ChatCompletionSystemMessageParam {
|
||||
let content = buildChatSystemPrompt(currentModel)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { getScriptPrompt, getWorkflowAsCodePrompt } from '$system_prompts'
|
||||
|
||||
describe('Workflow-as-Code prompt helpers', () => {
|
||||
it('injects only the TypeScript WAC SDK for TypeScript runtimes', () => {
|
||||
const prompt = getWorkflowAsCodePrompt('bun')
|
||||
|
||||
expect(prompt).toContain('Windmill Workflow-as-Code Writing Guide')
|
||||
expect(prompt).toContain('## TypeScript Workflow-as-Code API')
|
||||
expect(prompt).not.toContain('## Python Workflow-as-Code API')
|
||||
})
|
||||
|
||||
it('injects only the Python WAC SDK for Python runtimes', () => {
|
||||
const prompt = getWorkflowAsCodePrompt('python3')
|
||||
|
||||
expect(prompt).toContain('Windmill Workflow-as-Code Writing Guide')
|
||||
expect(prompt).toContain('## Python Workflow-as-Code API')
|
||||
expect(prompt).not.toContain('## TypeScript Workflow-as-Code API')
|
||||
})
|
||||
|
||||
it('does not support non-Bun TypeScript runtimes as WAC targets', () => {
|
||||
expect(getWorkflowAsCodePrompt('deno')).toBe('')
|
||||
expect(getWorkflowAsCodePrompt('nativets')).toBe('')
|
||||
expect(getWorkflowAsCodePrompt('bunnative')).toBe('')
|
||||
})
|
||||
|
||||
it('does not change normal script prompts', () => {
|
||||
expect(getWorkflowAsCodePrompt('go')).toBe('')
|
||||
expect(getScriptPrompt('bun')).not.toContain('Windmill Workflow-as-Code Writing Guide')
|
||||
})
|
||||
})
|
||||
@@ -207,6 +207,7 @@
|
||||
noSyncFromGithub
|
||||
lang={script.language}
|
||||
path={script.path}
|
||||
autoKind={script.auto_kind}
|
||||
tag={script.tag}
|
||||
fixedOverflowWidgets={false}
|
||||
bind:code={script.content}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { isWorkflowAsCode } from './wacToFlow'
|
||||
|
||||
const tsWac = `
|
||||
import { workflow, task } from "windmill-client"
|
||||
|
||||
const process = task(async () => "ok")
|
||||
|
||||
export const main = workflow(async () => await process())
|
||||
`
|
||||
|
||||
describe('isWorkflowAsCode', () => {
|
||||
it('detects WAC only for Bun TypeScript and Python', () => {
|
||||
expect(isWorkflowAsCode(tsWac, 'bun')).toBe(true)
|
||||
expect(isWorkflowAsCode(tsWac, 'deno')).toBe(false)
|
||||
expect(isWorkflowAsCode(tsWac, 'nativets')).toBe(false)
|
||||
expect(isWorkflowAsCode('@workflow\nasync def main():\n return None\n', 'python3')).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -5,7 +5,7 @@ export function isWorkflowAsCode(code: string, language: string): boolean {
|
||||
if (language === 'python3') {
|
||||
return /^\s*@workflow\s*$/m.test(code) || /from\s+wmill\s+import.*workflow/.test(code)
|
||||
}
|
||||
if (language === 'bun' || language === 'deno') {
|
||||
if (language === 'bun') {
|
||||
return (
|
||||
/workflow\s*\(/.test(code) &&
|
||||
/task\s*\(/.test(code) &&
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@ export * from './prompts';
|
||||
export declare function getScriptPrompt(language: string): string;
|
||||
export declare function getFlowPrompt(): string;
|
||||
export declare function getDatatableSdkReference(): string;
|
||||
export declare function getWorkflowAsCodePrompt(): string;
|
||||
export declare function getWorkflowAsCodePrompt(language?: string): string;
|
||||
|
||||
@@ -10,6 +10,12 @@ const TS_SDK_LANGUAGES = ['bun', 'deno', 'nativets', 'bunnative'];
|
||||
// Languages that use the Python SDK
|
||||
const PY_SDK_LANGUAGES = ['python3'];
|
||||
|
||||
// Languages that use the TypeScript Workflow-as-Code SDK
|
||||
const WAC_TS_SDK_LANGUAGES = ['bun'];
|
||||
|
||||
// Languages that use the Python Workflow-as-Code SDK
|
||||
const WAC_PY_SDK_LANGUAGES = PY_SDK_LANGUAGES;
|
||||
|
||||
// Helper to combine prompts for scripts
|
||||
export function getScriptPrompt(language: string): string {
|
||||
const langKey = `LANG_${language.toUpperCase()}` as keyof typeof prompts;
|
||||
@@ -47,10 +53,24 @@ export function getDatatableSdkReference(): string {
|
||||
}
|
||||
|
||||
// Helper to combine prompts for Workflow-as-Code scripts
|
||||
export function getWorkflowAsCodePrompt(): string {
|
||||
export function getWorkflowAsCodePrompt(language?: string): string {
|
||||
let sdkPrompt = '';
|
||||
|
||||
if (language == null) {
|
||||
sdkPrompt = [
|
||||
prompts.WAC_SDK_TYPESCRIPT,
|
||||
prompts.WAC_SDK_PYTHON
|
||||
].filter(Boolean).join('\n\n');
|
||||
} else if (WAC_TS_SDK_LANGUAGES.includes(language)) {
|
||||
sdkPrompt = prompts.WAC_SDK_TYPESCRIPT;
|
||||
} else if (WAC_PY_SDK_LANGUAGES.includes(language)) {
|
||||
sdkPrompt = prompts.WAC_SDK_PYTHON;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
||||
return [
|
||||
prompts.WORKFLOW_AS_CODE_BASE,
|
||||
prompts.WAC_SDK_TYPESCRIPT,
|
||||
prompts.WAC_SDK_PYTHON
|
||||
sdkPrompt
|
||||
].filter(Boolean).join('\n\n');
|
||||
}
|
||||
|
||||
@@ -332,12 +332,12 @@ Use this guide when writing or modifying Windmill Workflow-as-Code (WAC) scripts
|
||||
WAC is authored as a Windmill script and deployed with the normal script workflow. It is not an OpenFlow YAML flow.
|
||||
|
||||
Supported WAC authoring targets:
|
||||
- TypeScript scripts that import from \`windmill-client\`
|
||||
- Bun TypeScript scripts that import from \`windmill-client\`
|
||||
- Python 3 scripts that import from \`wmill\`
|
||||
|
||||
## File Shape
|
||||
|
||||
TypeScript:
|
||||
Bun TypeScript:
|
||||
|
||||
\`\`\`typescript
|
||||
import {
|
||||
@@ -379,7 +379,7 @@ async def main(x: str):
|
||||
|
||||
Rules:
|
||||
- Do not call \`main\`.
|
||||
- TypeScript should export the workflow entrypoint, preferably \`export const main = workflow(async (...) => { ... })\`.
|
||||
- Bun TypeScript should export the workflow entrypoint, preferably \`export const main = workflow(async (...) => { ... })\`.
|
||||
- Python must use \`@workflow\` on an async top-level function, usually \`main\`.
|
||||
- Define task functions and \`taskScript\`/\`task_script\` or \`taskFlow\`/\`task_flow\` assignments at module top level with stable names.
|
||||
- Use the exact SDK names. Do not alias \`workflow\`, \`task\`, \`taskScript\`, \`taskFlow\`, \`step\`, \`sleep\`, \`waitForApproval\`, \`task_script\`, \`task_flow\`, or \`wait_for_approval\`; the WAC parser recognizes these names directly.
|
||||
|
||||
@@ -48,12 +48,12 @@ Use this guide when writing or modifying Windmill Workflow-as-Code (WAC) scripts
|
||||
WAC is authored as a Windmill script and deployed with the normal script workflow. It is not an OpenFlow YAML flow.
|
||||
|
||||
Supported WAC authoring targets:
|
||||
- TypeScript scripts that import from `windmill-client`
|
||||
- Bun TypeScript scripts that import from `windmill-client`
|
||||
- Python 3 scripts that import from `wmill`
|
||||
|
||||
## File Shape
|
||||
|
||||
TypeScript:
|
||||
Bun TypeScript:
|
||||
|
||||
```typescript
|
||||
import {
|
||||
@@ -95,7 +95,7 @@ async def main(x: str):
|
||||
|
||||
Rules:
|
||||
- Do not call `main`.
|
||||
- TypeScript should export the workflow entrypoint, preferably `export const main = workflow(async (...) => { ... })`.
|
||||
- Bun TypeScript should export the workflow entrypoint, preferably `export const main = workflow(async (...) => { ... })`.
|
||||
- Python must use `@workflow` on an async top-level function, usually `main`.
|
||||
- Define task functions and `taskScript`/`task_script` or `taskFlow`/`task_flow` assignments at module top level with stable names.
|
||||
- Use the exact SDK names. Do not alias `workflow`, `task`, `taskScript`, `taskFlow`, `step`, `sleep`, `waitForApproval`, `task_script`, `task_flow`, or `wait_for_approval`; the WAC parser recognizes these names directly.
|
||||
|
||||
@@ -6,12 +6,12 @@ Use this guide when writing or modifying Windmill Workflow-as-Code (WAC) scripts
|
||||
WAC is authored as a Windmill script and deployed with the normal script workflow. It is not an OpenFlow YAML flow.
|
||||
|
||||
Supported WAC authoring targets:
|
||||
- TypeScript scripts that import from `windmill-client`
|
||||
- Bun TypeScript scripts that import from `windmill-client`
|
||||
- Python 3 scripts that import from `wmill`
|
||||
|
||||
## File Shape
|
||||
|
||||
TypeScript:
|
||||
Bun TypeScript:
|
||||
|
||||
```typescript
|
||||
import {
|
||||
@@ -53,7 +53,7 @@ async def main(x: str):
|
||||
|
||||
Rules:
|
||||
- Do not call `main`.
|
||||
- TypeScript should export the workflow entrypoint, preferably `export const main = workflow(async (...) => { ... })`.
|
||||
- Bun TypeScript should export the workflow entrypoint, preferably `export const main = workflow(async (...) => { ... })`.
|
||||
- Python must use `@workflow` on an async top-level function, usually `main`.
|
||||
- Define task functions and `taskScript`/`task_script` or `taskFlow`/`task_flow` assignments at module top level with stable names.
|
||||
- Use the exact SDK names. Do not alias `workflow`, `task`, `taskScript`, `taskFlow`, `step`, `sleep`, `waitForApproval`, `task_script`, `task_flow`, or `wait_for_approval`; the WAC parser recognizes these names directly.
|
||||
|
||||
@@ -1967,6 +1967,12 @@ const TS_SDK_LANGUAGES = ['bun', 'deno', 'nativets', 'bunnative'];
|
||||
// Languages that use the Python SDK
|
||||
const PY_SDK_LANGUAGES = ['python3'];
|
||||
|
||||
// Languages that use the TypeScript Workflow-as-Code SDK
|
||||
const WAC_TS_SDK_LANGUAGES = ['bun'];
|
||||
|
||||
// Languages that use the Python Workflow-as-Code SDK
|
||||
const WAC_PY_SDK_LANGUAGES = PY_SDK_LANGUAGES;
|
||||
|
||||
// Helper to combine prompts for scripts
|
||||
export function getScriptPrompt(language: string): string {
|
||||
const langKey = `LANG_${language.toUpperCase()}` as keyof typeof prompts;
|
||||
@@ -2004,16 +2010,38 @@ export function getDatatableSdkReference(): string {
|
||||
}
|
||||
|
||||
// Helper to combine prompts for Workflow-as-Code scripts
|
||||
export function getWorkflowAsCodePrompt(): string {
|
||||
export function getWorkflowAsCodePrompt(language?: string): string {
|
||||
let sdkPrompt = '';
|
||||
|
||||
if (language == null) {
|
||||
sdkPrompt = [
|
||||
prompts.WAC_SDK_TYPESCRIPT,
|
||||
prompts.WAC_SDK_PYTHON
|
||||
].filter(Boolean).join('\\n\\n');
|
||||
} else if (WAC_TS_SDK_LANGUAGES.includes(language)) {
|
||||
sdkPrompt = prompts.WAC_SDK_TYPESCRIPT;
|
||||
} else if (WAC_PY_SDK_LANGUAGES.includes(language)) {
|
||||
sdkPrompt = prompts.WAC_SDK_PYTHON;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
||||
return [
|
||||
prompts.WORKFLOW_AS_CODE_BASE,
|
||||
prompts.WAC_SDK_TYPESCRIPT,
|
||||
prompts.WAC_SDK_PYTHON
|
||||
sdkPrompt
|
||||
].filter(Boolean).join('\\n\\n');
|
||||
}
|
||||
"""
|
||||
(OUTPUT_GENERATED_DIR / "index.ts").write_text(index_content)
|
||||
|
||||
index_dts_content = """export * from './prompts';
|
||||
export declare function getScriptPrompt(language: string): string;
|
||||
export declare function getFlowPrompt(): string;
|
||||
export declare function getDatatableSdkReference(): string;
|
||||
export declare function getWorkflowAsCodePrompt(language?: string): string;
|
||||
"""
|
||||
(OUTPUT_GENERATED_DIR / "index.d.ts").write_text(index_dts_content)
|
||||
|
||||
# Generate skill files for Claude Code
|
||||
CLI_GUIDANCE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
skills = generate_skills(
|
||||
|
||||
Reference in New Issue
Block a user