Skip to content

Commit

Permalink
Merge pull request #53 from docker/cm/0.1.9
Browse files Browse the repository at this point in the history
0.1.9 Release
  • Loading branch information
ColinMcNeil authored Nov 15, 2024
2 parents 6bbcb9b + c56345e commit 5f13c51
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 69 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ This project is a research prototype. It is ready to try and will give results f
*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. Execute command `>Set OpenAI API key...` and enter your OpenAI secret key.
2. Execute command `>Docker AI: 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

Expand Down Expand Up @@ -49,7 +49,7 @@ My project has the following files:

```

Run command `>Run current file as prompt`
Run command `>Docker AI: Run this prompt`

## Docs
https://vonwig.github.io/prompts.docs
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.8-ghu-preview",
"version": "0.1.9",
"publisher": "docker",
"repository": {
"type": "git",
Expand Down Expand Up @@ -84,6 +84,10 @@
{
"command": "docker.labs-ai-tools-vscode.toggle-debug",
"title": "Docker AI: Toggle debug mode"
},
{
"command": "docker.labs-ai-tools-vscode.kill-active-prompts",
"title": "Docker AI: Kill active prompts"
}
]
},
Expand Down
2 changes: 2 additions & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { runHotCommand } from './runHotCommand';
import { deletePrompt, savePrompt } from './manageSavedPrompts';
import { setProjectDir } from './setProjectDir';
import { setThreadId } from './setThreadId';
import killActivePrompts from './killActivePrompts';

type CTX = { secrets: any }

Check warning on line 10 in src/commands/index.ts

View workflow job for this annotation

GitHub Actions / test

Missing semicolon

Expand All @@ -25,6 +26,7 @@ const commands = (context: CTX) => [
vscode.window.showInformationMessage(`Debug mode is now ${currentValue ? 'disabled' : 'enabled'}.`);
}
},
{ id: 'docker.labs-ai-tools-vscode.kill-active-prompts', callback: killActivePrompts },
]

Check warning on line 30 in src/commands/index.ts

View workflow job for this annotation

GitHub Actions / test

Missing semicolon

export default (context: CTX) => commands(context).map((comm) => vscode.commands.registerCommand(comm.id, comm.callback))

Check warning on line 32 in src/commands/index.ts

View workflow job for this annotation

GitHub Actions / test

Missing semicolon
13 changes: 13 additions & 0 deletions src/commands/killActivePrompts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { window } from "vscode";
import { sendKillSignalToActivePrompts } from "../utils/promptRunner";

const killActivePrompts = () => {
const result = sendKillSignalToActivePrompts();
if (result.length > 0) {
window.showInformationMessage(`Sent kill signal to ${result.length} active prompts.`);
} else {
window.showInformationMessage(`No active prompts to kill.`);
}
}

Check warning on line 11 in src/commands/killActivePrompts.ts

View workflow job for this annotation

GitHub Actions / test

Missing semicolon

export default killActivePrompts;
53 changes: 1 addition & 52 deletions src/commands/runPrompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,51 +14,6 @@ import { randomUUID } from "crypto";

type PromptOption = 'local-dir' | 'local-file' | 'remote';

const START_DOCKER_COMMAND = {
'win32': 'Start-Process -NoNewWindow -Wait -FilePath "C:\\Program Files\\Docker\\Docker\\Docker Desktop.exe"',
'darwin': 'open -a Docker',
'linux': 'systemctl --user start docker-desktop',
};

const checkDockerDesktop = () => {

// Coerce the error to have an exit code
type DockerSpawnError = Error & { code: number };

try {
const res = spawnSync("docker", ["version"]);

if (res.error) {
// Using -1 to indicate docker is not installed
(res.error as DockerSpawnError).code = -1;
throw res.error;
}

if (res.status !== 0) {
const err = new Error(`Docker command exited with code ${res.status} and output the following error: ${res.error || res.stderr.toString()}`);
// Using -1 as a fallback, should have already been caught by res.error
(err as DockerSpawnError).code = res.status || -1;
throw err;
}

// @ts-expect-error
} catch (e: DockerSpawnError) {
const platform = process.platform;
const actionItems = e.code !== -1 ? [(platform in START_DOCKER_COMMAND ? "Start Docker" : "Try again")] : ["Install Docker Desktop", "Try again"];
return vscode.window.showErrorMessage("Error starting Docker", { modal: true, detail: (e as DockerSpawnError).toString() }, ...actionItems).then(async (value) => {
switch (value) {
case "Start Docker":
spawnSync(START_DOCKER_COMMAND[platform as keyof typeof START_DOCKER_COMMAND], { shell: true });
case "Install Docker Desktop":
vscode.env.openExternal(vscode.Uri.parse("https://www.docker.com/products/docker-desktop"));
return;
case "Try again":
return 'RETRY';
}
});
}
};

const getWorkspaceFolder = async () => {
const workspaceFolders = vscode.workspace.workspaceFolders;

Expand Down Expand Up @@ -87,12 +42,6 @@ const getWorkspaceFolder = async () => {
export const runPrompt: (secrets: vscode.SecretStorage, mode: PromptOption) => void = (secrets: vscode.SecretStorage, mode: PromptOption) => vscode.window.withProgress({ location: vscode.ProgressLocation.Window, cancellable: true }, async (progress, token) => {
progress.report({ increment: 1, message: "Starting..." });
postToBackendSocket({ event: 'eventLabsPromptRunPrepare', properties: { mode } });
const result = await checkDockerDesktop();
if (result === 'RETRY') {
return runPrompt(secrets, mode);
}


progress.report({ increment: 5, message: "Checking for OpenAI key..." });

const hasOpenAIKey = await verifyHasOpenAIKey(secrets, true);
Expand Down Expand Up @@ -200,7 +149,7 @@ export const runPrompt: (secrets: vscode.SecretStorage, mode: PromptOption) => v
await writeToEditor(`${header} ROLE ${role}${content ? ` (${content})` : ''}\n\n`);
break;
case 'functions-done':
await writeToEditor('\n```'+`\n\n*entering tool*\n\n`);
await writeToEditor('\n```' + `\n\n*entering tool*\n\n`);
break;
case 'message':
await writeToEditor(json.params.content);
Expand Down
5 changes: 5 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { spawn, spawnSync } from 'child_process';
import semver from 'semver';
import commands from './commands';
import { postToBackendSocket, setDefaultProperties } from './utils/ddSocket';
import { checkDockerDesktop } from './utils/dockerDesktop';

export let ctx: vscode.ExtensionContext;

Expand Down Expand Up @@ -74,6 +75,10 @@ const checkOutdatedVersionInstalled = async () => {
};

export async function activate(context: vscode.ExtensionContext) {
const result = await checkDockerDesktop();
if (result === 'RETRY') {
return vscode.commands.executeCommand('workbench.action.reloadWindow');
}
checkOutdatedVersionInstalled();
checkVersion();
setDefaultProperties(context);
Expand Down
2 changes: 1 addition & 1 deletion src/promptmetadatagrammar.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
],
"repository": {
"prompt-metadata-keyword": {
"match": "(?i)(functions)|(extractors)|(model)|(stream)|(url)(?-i)",
"match": "(?i)(functions)|(extractors)|(model)|(stream)|(url)|(tools)(?-i)",
"name": "keyword.function.prompt.metadata"
}
}
Expand Down
47 changes: 47 additions & 0 deletions src/utils/dockerDesktop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { spawnSync } from "child_process";
import * as vscode from "vscode";

const START_DOCKER_COMMAND = {
'win32': 'Start-Process -NoNewWindow -Wait -FilePath "C:\\Program Files\\Docker\\Docker\\Docker Desktop.exe"',
'darwin': 'open -a Docker',
'linux': 'systemctl --user start docker-desktop',
};

export const checkDockerDesktop = () => {

// Coerce the error to have an exit code
type DockerSpawnError = Error & { code: number };

try {
const res = spawnSync("docker", ["version"]);

if (res.error) {
// Using -1 to indicate docker is not installed
(res.error as DockerSpawnError).code = -1;
throw res.error;
}

if (res.status !== 0) {
const err = new Error(`Docker command exited with code ${res.status} and output the following error: ${res.error || res.stderr.toString()}`);
// Using -1 as a fallback, should have already been caught by res.error
(err as DockerSpawnError).code = res.status || -1;
throw err;
}

// @ts-expect-error
} catch (e: DockerSpawnError) {
const platform = process.platform;
const actionItems = e.code !== -1 ? [(platform in START_DOCKER_COMMAND ? "Start Docker" : "Try again")] : ["Install Docker Desktop", "Try again"];
return vscode.window.showErrorMessage("Error starting Docker", { modal: true, detail: (e as DockerSpawnError).toString() }, ...actionItems).then(async (value) => {
switch (value) {
case "Start Docker":
spawnSync(START_DOCKER_COMMAND[platform as keyof typeof START_DOCKER_COMMAND], { shell: true });
case "Install Docker Desktop":
vscode.env.openExternal(vscode.Uri.parse("https://www.docker.com/products/docker-desktop"));
return;
case "Try again":
return 'RETRY';
}
});
}
};
39 changes: 26 additions & 13 deletions src/utils/promptRunner.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { spawn } from "child_process";
import { ChildProcessWithoutNullStreams, spawn } from "child_process";
import { CancellationToken, commands, window, workspace } from "vscode";
import { setThreadId } from "../commands/setThreadId";
import { notifications } from "./notifications";
import { extensionOutput } from "../extension";
import * as rpc from 'vscode-jsonrpc/node';

const output = window.createOutputChannel("Docker Labs: AI Tools");
const activePrompts: { [key: string]: Function } = {};

export const sendKillSignalToActivePrompts = () =>
Object.values(activePrompts).map(kill => kill());


export const getRunArgs = async (promptRef: string, projectDir: string, username: string, pat: string, platform: string, render = false) => {
const isLocal = promptRef.startsWith('local://');
Expand Down Expand Up @@ -52,12 +56,19 @@ export const spawnPromptImage = async (promptArg: string, projectDir: string, us
const args = await getRunArgs(promptArg!, projectDir!, username, platform, pat);
callback({ method: 'message', params: { debug: `Running ${args.join(' ')}` } });
const childProcess = spawn("docker", args);
const pid = childProcess.pid;

if (pid) {
activePrompts[pid] = childProcess.kill;
childProcess.on('exit', () => {
delete activePrompts[pid];
});
}

let connection = rpc.createMessageConnection(
new rpc.StreamMessageReader(childProcess.stdout),
new rpc.StreamMessageWriter(childProcess.stdin)
);

const notificationBuffer: { method: string, params: object }[] = []

let processingBuffer = false;
Expand All @@ -78,9 +89,9 @@ export const spawnPromptImage = async (promptArg: string, projectDir: string, us
}
}

for (const [type, properties] of Object.entries(notifications)){
for (const [type, properties] of Object.entries(notifications)) {
// @ts-expect-error
connection.onNotification(properties, (params)=> pushNotification(type, params))
connection.onNotification(properties, (params) => pushNotification(type, params))
}

connection.listen();
Expand All @@ -98,7 +109,7 @@ export const spawnPromptImage = async (promptArg: string, projectDir: string, us

};

const getJSONArgForPlatform = (json: object) =>{
const getJSONArgForPlatform = (json: object) => {
if (process.platform === 'win32') {
return `"` + JSON.stringify(json).replace(/"/g, '\\"') + `"`
}
Expand All @@ -120,26 +131,28 @@ export const writeKeyToVolume = async (key: string) => {
getJSONArgForPlatform({ files: [{ path: ".openai-api-key", content: key, executable: false }] })
];

extensionOutput.appendLine(JSON.stringify({"write-open-ai-key-to-volume": {
args1, args2
}}));
extensionOutput.appendLine(JSON.stringify({
"write-open-ai-key-to-volume": {
args1, args2
}
}));

const child1 = spawn("docker", args1);

child1.stdout.on('data', (data) => {
extensionOutput.appendLine(JSON.stringify({stdout:data.toString()}));
extensionOutput.appendLine(JSON.stringify({ stdout: data.toString() }));
});
child1.stderr.on('data', (data) => {
extensionOutput.appendLine(JSON.stringify({stderr:data.toString()}));
extensionOutput.appendLine(JSON.stringify({ stderr: data.toString() }));
});

const child2 = spawn("docker", args2, {
shell: true
});
child2.stdout.on('data', (data) => {
extensionOutput.appendLine(JSON.stringify({stdout:data.toString()}));
extensionOutput.appendLine(JSON.stringify({ stdout: data.toString() }));
});
child2.stderr.on('data', (data) => {
extensionOutput.appendLine(JSON.stringify({stderr:data.toString()}));
extensionOutput.appendLine(JSON.stringify({ stderr: data.toString() }));
});
};

0 comments on commit 5f13c51

Please sign in to comment.