diff --git a/.gitignore b/.gitignore index 2624516..0f14950 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ node_modules /prompts/functions/write_file/blah.txt /prompts/functions/write_files/.direnv/ /prompts/functions/write_files/result +**/.DS_Store diff --git a/README.md b/README.md index df7b1ec..b0eef51 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,58 @@ -# Docker AI Prompts +# AI Prompt Runner for VSCode +Docker Labs +## What is this? -## What is this project? +If you aren't familiar with our experiments and work, please check out https://github.com/docker/labs-ai-tools-for-devs +If you are familiar with our projects, then this is simply a VSCode extension to run prompts. + +This project is a research prototype. It is ready to try and will give results for any project you try it on. ## Getting started -Docker internal users: You must be opted-out of mandatory sign-in. +*Docker internal users: You must be opted-out of mandatory sign-in.* 1. Install latest VSIX file https://github.com/docker/labs-ai-tools-vscode/releases -2. Open your workspace -3. Execute command `>Set OpenAI API key...` and enter your OpenAI secret key. -4. Execute command `>save-prompt` and enter your prompt URL/ref. - Example prompt: `https://github.com/docker/labs-ai-tools-for-devs/tree/main/prompts/poem` -5. Execute command `>run-prompt` +2. Execute command `>Set OpenAI API key...` and enter your OpenAI secret key. + You can run a prompt with a local model. Docs coming soon. +3. Run a prompt -This project is a research prototype. It is ready to try and will give results for any project you try it on. +### Local Prompt: + +Create file test.md + +`test.md` + +```md +--- +extractors: + - name: project-facts +functions: + - name: write_files +--- + +# Improv Test +This is a test prompt... + +# Prompt system +You are Dwight Schrute. + +# Prompt user +Tell me about my project. + +My project uses the following languages: +{{project-facts.languages}} + +My project has the following files: +{{project-facts.files}} + +``` + +Run command `>Run current file as prompt` + +## Docs +https://vonwig.github.io/prompts.docs ## Development diff --git a/package.json b/package.json index c87e355..ffaeef6 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "labs-ai-tools-vscode", "displayName": "Labs: AI Tools for VSCode", "description": "Run & Debug AI Prompts with Dockerized tools", - "version": "0.1.0", + "version": "0.1.1", "publisher": "docker", "repository": { "type": "git", @@ -20,19 +20,6 @@ ], "main": "./out/extension.js", "contributes": { - "configuration": { - "title": "Docker AI Prompts", - "properties": { - "docker.labs-ai-tools-vscode.project_dir": { - "type": "string", - "description": "Project directory to run AI prompts within" - }, - "docker.labs-ai-tools-vscode.thread_id": { - "type": "string", - "description": "If set, will persist the conversation's Docker volume tagged with this thread ID." - } - } - }, "commands": [ { "command": "docker.labs-ai-tools-vscode.run-prompt", @@ -63,6 +50,15 @@ "command": "docker.labs-ai-tools-vscode.run-file-as-prompt", "title": "Run current file as a prompt", "when": "editorLangId == markdown" + }, + { + "command": "docker.labs-ai-tools-vscode.set-project-dir", + "title": "Set local prompt project directory", + "when": "editorLangId == markdown" + }, + { + "command": "docker.labs-ai-tools-vscode.set-thread-id", + "title": "Set prompt thread ID" } ] }, diff --git a/src/commands/index.ts b/src/commands/index.ts new file mode 100644 index 0000000..11e81bb --- /dev/null +++ b/src/commands/index.ts @@ -0,0 +1,22 @@ +// barreli boi +import * as vscode from 'vscode' +import { runPrompt } from './runPrompt'; +import { runHotCommand } from './runHotCommand'; +import { deletePrompt, savePrompt } from './manageSavedPrompts'; +import { setProjectDir } from './setProjectDir'; +import { setThreadId } from './setThreadId'; + +type CTX = { secrets: any } + +const commands = (context: CTX) => [ + { id: 'docker.labs-ai-tools-vscode.run-commands', callback: runHotCommand }, + { id: 'docker.labs-ai-tools-vscode.save-prompt', callback: savePrompt }, + { id: 'docker.labs-ai-tools-vscode.delete-prompt', callback: deletePrompt }, + { id: 'docker.labs-ai-tools-vscode.run-workspace-as-prompt', callback: () => runPrompt(context.secrets, 'local-dir') }, + { id: 'docker.labs-ai-tools-vscode.run-file-as-prompt', callback: () => runPrompt(context.secrets, 'local-file') }, + { id: 'docker.labs-ai-tools-vscode.run-prompt', callback: () => runPrompt(context.secrets, 'remote') }, + { id: 'docker.labs-ai-tools-vscode.project-dir', callback: setProjectDir }, + { id: 'docker.labs-ai-tools-vscode.thread-id', callback: setThreadId }, +] + +export default (context: CTX) => commands(context).map((comm) => vscode.commands.registerCommand(comm.id, comm.callback)) \ No newline at end of file diff --git a/src/commands/runPrompt.ts b/src/commands/runPrompt.ts index 1332718..3ebef53 100644 --- a/src/commands/runPrompt.ts +++ b/src/commands/runPrompt.ts @@ -7,6 +7,7 @@ import { createOutputBuffer } from "../utils/promptFilename"; import { spawnPromptImage, writeKeyToVolume } from "../utils/promptRunner"; import { verifyHasOpenAIKey } from "./setOpenAIKey"; import { getCredential } from "../utils/credential"; +import { setProjectDir } from "./setProjectDir"; type PromptOption = 'local-dir' | 'local-file' | 'remote'; @@ -79,29 +80,6 @@ const getWorkspaceFolder = async () => { return workspaceFolder; }; -// When running local workspace as a prompt, we need to use a config value to determine the project path -const checkHasInputWorkspace = async () => { - const existingPath = vscode.workspace.getConfiguration('docker.labs-ai-tools-vscode').get('project_dir') as string; - if (!existingPath) { - const resp = await vscode.window.showErrorMessage("No project path set in settings", "Set project path", "Cancel"); - if (resp === "Set project path") { - const resp = await vscode.window.showOpenDialog({ - canSelectFiles: false, - canSelectFolders: true, - canSelectMany: false, - title: "Select project path", - }); - if (resp) { - await vscode.workspace.getConfiguration('docker.labs-ai-tools-vscode').update('project_dir', resp[0].fsPath, true); - vscode.window.showInformationMessage(`Project path set to ${resp[0].fsPath}. You can change this in settings.`); - return resp[0].fsPath; - } - } - return; - } - return existingPath; -}; - export const runPrompt: (secrets: vscode.SecretStorage, mode: PromptOption) => void = (secrets: vscode.SecretStorage, mode: PromptOption) => vscode.window.withProgress({ location: vscode.ProgressLocation.Window }, async progress => { @@ -124,7 +102,7 @@ export const runPrompt: (secrets: vscode.SecretStorage, mode: PromptOption) => v const workspaceFolder = await getWorkspaceFolder(); - const inputWorkspace = await checkHasInputWorkspace(); + const inputWorkspace = await vscode.commands.executeCommand>('docker.labs-ai-tools-vscode.project-dir', false); const promptOption = mode === 'remote' ? await showPromptPicker() : { id: `${mode === 'local-dir' ? `local://${inputWorkspace}` : `local://${vscode.window.activeTextEditor?.document.uri.fsPath}`}`, name: `Local Prompt (${mode})` }; diff --git a/src/commands/setProjectDir.ts b/src/commands/setProjectDir.ts new file mode 100644 index 0000000..0e50b8a --- /dev/null +++ b/src/commands/setProjectDir.ts @@ -0,0 +1,23 @@ +import { window } from "vscode" +import { ctx } from "../extension"; + +export const setProjectDir = async (overwrite = true) => { + const existingVal = ctx.workspaceState.get('project_dir') + if (!overwrite && existingVal) { + return existingVal + } + const directory = await window.showOpenDialog({ + openLabel: 'Use this project', + canSelectFiles: false, + canSelectFolders: true, + canSelectMany: false + }) + if (!directory) { + window.showErrorMessage('Project directory not set.') + return undefined; + } + const path = directory[0].fsPath + await ctx.workspaceState.update('project_dir', path) + window.showInformationMessage(`Project directory set to ${path}`) + return path; +} \ No newline at end of file diff --git a/src/commands/setThreadId.ts b/src/commands/setThreadId.ts new file mode 100644 index 0000000..6aed824 --- /dev/null +++ b/src/commands/setThreadId.ts @@ -0,0 +1,20 @@ +import { window } from "vscode" +import { ctx } from "../extension" + +export const setThreadId = async (overwrite = true) => { + const existingVal = ctx.workspaceState.get('thread_id') + if (!overwrite && existingVal) { + return existingVal + } + const resp = await window.showInputBox({ + title: 'Thread ID', + prompt: 'Enter a simple string to tag the thread volume.' + }) + if (!resp) { + window.showErrorMessage('No thread ID set.') + return undefined; + } + await ctx.workspaceState.update('thread_id', resp) + window.showInformationMessage(`Thread ID set to ${resp}`) + return resp; +} \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index 177254f..4aa50ff 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -7,6 +7,7 @@ import { nativeClient } from './utils/lsp'; import { deletePrompt, savePrompt } from './commands/manageSavedPrompts'; import { spawnSync } from 'child_process'; import semver from 'semver'; +import commands from './commands'; export let ctx: vscode.ExtensionContext; @@ -83,29 +84,9 @@ export async function activate(context: vscode.ExtensionContext) { spawnSync('docker', ['pull', "vonwig/prompts:latest"]); - let runPromptCommand = vscode.commands.registerCommand('docker.labs-ai-tools-vscode.run-prompt', () => runPrompt(context.secrets, 'remote')); + const registeredCommands = commands(context) - context.subscriptions.push(runPromptCommand); - - let runBoundCommands = vscode.commands.registerCommand('docker.labs-ai-tools-vscode.run-commands', runHotCommand); - - context.subscriptions.push(runBoundCommands); - - let savePromptCommand = vscode.commands.registerCommand('docker.labs-ai-tools-vscode.save-prompt', savePrompt); - - context.subscriptions.push(savePromptCommand); - - let deletePromptCommand = vscode.commands.registerCommand('docker.labs-ai-tools-vscode.delete-prompt', deletePrompt); - - context.subscriptions.push(deletePromptCommand); - - let runWorkspaceAsPrompt = vscode.commands.registerCommand('docker.labs-ai-tools-vscode.run-workspace-as-prompt', () => runPrompt(context.secrets, 'local-dir')); - - context.subscriptions.push(runWorkspaceAsPrompt); - - let runFileAsPrompt = vscode.commands.registerCommand('docker.labs-ai-tools-vscode.run-file-as-prompt', () => runPrompt(context.secrets, 'local-file')); - - context.subscriptions.push(runFileAsPrompt); + context.subscriptions.push(...registeredCommands) nativeClient.onNotification("$bind/register", async (args: { uri: string, blocks: { diff --git a/src/utils/promptRunner.ts b/src/utils/promptRunner.ts index 6dbdd0b..a78e00c 100644 --- a/src/utils/promptRunner.ts +++ b/src/utils/promptRunner.ts @@ -1,11 +1,12 @@ import { spawn } from "child_process"; -import { window, workspace } from "vscode"; +import { commands, window, workspace } from "vscode"; +import { setThreadId } from "../commands/setThreadId"; const output = window.createOutputChannel("Docker Labs: AI Tools"); -export const getRunArgs = (promptRef: string, projectDir: string, username: string, platform: string, pat: string, render = false) => { +export const getRunArgs = async (promptRef: string, projectDir: string, username: string, platform: string, pat: string, render = false) => { const isLocal = promptRef.startsWith('local://'); const isMarkdown = promptRef.toLowerCase().endsWith('.md'); - const threadId = workspace.getConfiguration('docker.labs-ai-tools-vscode').get('thread_id') as string | undefined; + const threadId = await commands.executeCommand>('docker.labs-ai-tools-vscode.thread-id', false) let promptArgs: string[] = ["--prompts", promptRef]; let mountArgs: string[] = ["--mount", `type=bind,source=${projectDir},target=/app/${promptRef}`]; @@ -89,7 +90,7 @@ const runAndStream = async (command: string, args: string[], callback: (json: an }; export const spawnPromptImage = async (promptArg: string, projectDir: string, username: string, platform: string, pat: string, callback: (json: any) => Promise) => { - const args = getRunArgs(promptArg!, projectDir!, username, platform, pat); + const args = await getRunArgs(promptArg!, projectDir!, username, platform, pat); callback({ method: 'message', params: { debug: `Running ${args.join(' ')}` } }); return runAndStream("docker", args, callback); };