Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GenAIScript task provider and CLI configuration in VSCode extension #709

Merged
merged 8 commits into from
Sep 13, 2024
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"promptdom",
"promptfoo",
"prompty",
"quoteify",
"stringifying",
"sysr",
"tabletojson",
Expand Down
3 changes: 1 addition & 2 deletions packages/cli/src/nodehost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import {
ResponseStatus,
} from "../../core/src/host"
import { AbortSignalOptions, TraceOptions } from "../../core/src/trace"
import { logVerbose, unique } from "../../core/src/util"
import { logVerbose, quoteify, unique } from "../../core/src/util"
import { parseModelIdentifier } from "../../core/src/models"
import {
AuthenticationToken,
Expand Down Expand Up @@ -300,7 +300,6 @@ export class NodeHost implements RuntimeHost {
if (command === "python" && process.platform !== "win32")
command = "python3"

const quoteify = (a: string) => (/\s/.test(a) ? `"${a}"` : a)
logVerbose(
`${cwd ? `${cwd}> ` : ""}${quoteify(command)} ${args.map(quoteify).join(" ")}`
)
pelikhan marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ export function logError(msg: string | Error | SerializedError) {
if (!/^\s*\{\s*\}\s*$/.test(se)) host.log(LogLevel.Verbose, se)
}
}
export function quoteify(a: string) {
return /\s/.test(a) ? `"${a}"` : a
}

export function concatArrays<T>(...arrays: T[][]): T[] {
if (arrays.length == 0) return []
return arrays[0].concat(...arrays.slice(1))
Expand Down
14 changes: 14 additions & 0 deletions packages/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,20 @@
},
"contributes": {
"markdown.markdownItPlugins": true,
"taskDefinitions": [
{
"type": "genaiscript",
"required": [
"script"
],
"properties": {
"script": {
"type": "string",
"description": "GenAIScript script to run"
}
}
}
],
"walkthroughs": [
{
"id": "genaiscript.tutorial",
Expand Down
22 changes: 22 additions & 0 deletions packages/vscode/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as vscode from "vscode"
import {
TOOL_ID,
VSCODE_CONFIG_CLI_PATH,
VSCODE_CONFIG_CLI_VERSION,
} from "../../core/src/constants"
import { CORE_VERSION } from "../../core/src/version"
import { semverParse, semverSatisfies } from "../../core/src/semver"

export async function resolveCli() {
const config = vscode.workspace.getConfiguration(TOOL_ID)
const cliPath = config.get(VSCODE_CONFIG_CLI_PATH) as string
const cliVersion =
(config.get(VSCODE_CONFIG_CLI_VERSION) as string) || CORE_VERSION
const gv = semverParse(CORE_VERSION)
if (!semverSatisfies(cliVersion, ">=" + gv.major + "." + gv.minor))
vscode.window.showWarningMessage(
TOOL_ID +
` - genaiscript cli version (${cliVersion}) outdated, please update to ${CORE_VERSION}`
)
pelikhan marked this conversation as resolved.
Show resolved Hide resolved
return { cliPath, cliVersion }
}
2 changes: 2 additions & 0 deletions packages/vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { activateConnectionInfoTree } from "./connectioninfotree"
import { updateConnectionConfiguration } from "../../core/src/connection"
import { APIType } from "../../core/src/host"
import { openUrlInTab } from "./browser"
import { activeTaskProvider } from "./taskprovider"

export async function activate(context: ExtensionContext) {
const state = new ExtensionState(context)
Expand All @@ -36,6 +37,7 @@ export async function activate(context: ExtensionContext) {
activateTraceTreeDataProvider(state)
activateStatusBar(state)
activateDocsNotebook(state)
activeTaskProvider(state)

context.subscriptions.push(
registerCommand(
Expand Down
27 changes: 5 additions & 22 deletions packages/vscode/src/fragmentcommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,12 @@
import { checkDirectoryExists, checkFileExists } from "./fs"
import { registerCommand } from "./commands"
import { templateGroup } from "../../core/src/ast"
import {
GENAI_ANY_REGEX,
TOOL_ID,
TOOL_NAME,
VSCODE_CONFIG_CLI_PATH,
VSCODE_CONFIG_CLI_VERSION,
} from "../../core/src/constants"
import { GENAI_ANY_REGEX, TOOL_ID, TOOL_NAME } from "../../core/src/constants"
import { NotSupportedError } from "../../core/src/error"
import { promptParameterTypeToJSONSchema } from "../../core/src/parameters"
import { Fragment } from "../../core/src/generation"
import { assert, dotGenaiscriptPath, groupBy } from "../../core/src/util"
import { CORE_VERSION } from "../../core/src/version"
import { semverParse, semverSatisfies } from "../../core/src/semver"
import { resolveCli } from "./config"

type TemplateQuickPickItem = {
template?: PromptScript
Expand Down Expand Up @@ -188,28 +181,18 @@
files = [file]
}

const config = vscode.workspace.getConfiguration(TOOL_ID)
const program = config.get(VSCODE_CONFIG_CLI_PATH) as string
const { cliPath, cliVersion } = await resolveCli()

Check failure on line 184 in packages/vscode/src/fragmentcommands.ts

View workflow job for this annotation

GitHub Actions / build

The async function `resolveCli()` is called without error handling. If this function fails, it could cause the application to crash. Consider adding a try-catch block to handle potential errors. 😊
pelikhan marked this conversation as resolved.
Show resolved Hide resolved
const args = [
"run",
vscode.workspace.asRelativePath(template.filename),
...files.map((file) =>
vscode.workspace.asRelativePath(file.fsPath)
),
]
const cliVersion =
(config.get(VSCODE_CONFIG_CLI_VERSION) as string) || CORE_VERSION
const gv = semverParse(CORE_VERSION)
if (!semverSatisfies(cliVersion, ">=" + gv.major + "." + gv.minor))
vscode.window.showWarningMessage(
TOOL_ID +
` - genaiscript cli version (${cliVersion}) outdated, please update to ${CORE_VERSION}`
)

const configuration = program
const configuration = cliPath
? <vscode.DebugConfiguration>{
name: TOOL_NAME,
program,
cliPath,
request: "launch",
skipFiles: ["<node_internals>/**", dotGenaiscriptPath("**")],
type: "node",
Expand Down
14 changes: 3 additions & 11 deletions packages/vscode/src/servermanager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,15 @@
OPEN,
TOOL_NAME,
ICON_LOGO_NAME,
CLIENT_RECONNECT_MAX_ATTEMPTS,
TOOL_ID,
VSCODE_CONFIG_CLI_VERSION,
VSCODE_CONFIG_CLI_PATH,
} from "../../core/src/constants"
import { ServerManager, host } from "../../core/src/host"
import { logError, logVerbose } from "../../core/src/util"
import { WebSocketClient } from "../../core/src/server/client"
import { CORE_VERSION } from "../../core/src/version"
import { createChatModelRunner } from "./lmaccess"
import { semverParse, semverSatisfies } from "../../core/src/semver"
import { resolveCli } from "./config"

export class TerminalServerManager implements ServerManager {
private _terminal: vscode.Terminal
Expand Down Expand Up @@ -86,15 +84,9 @@
isTransient: true,
iconPath: new vscode.ThemeIcon(ICON_LOGO_NAME),
})
const config = vscode.workspace.getConfiguration(TOOL_ID)
const cliPath = config.get(VSCODE_CONFIG_CLI_PATH) as string
const { cliPath, cliVersion } = await resolveCli()
if (cliPath) this._terminal.sendText(`node "${cliPath}" serve`)
else {
const cliVersion =
(config.get(VSCODE_CONFIG_CLI_VERSION) as string) ||
CORE_VERSION
this._terminal.sendText(`npx --yes ${TOOL_ID}@${cliVersion} serve`)
}
else this._terminal.sendText(`npx --yes ${TOOL_ID}@${cliVersion} serve`)

Check failure on line 89 in packages/vscode/src/servermanager.ts

View workflow job for this annotation

GitHub Actions / build

The async function `resolveCli()` is called without error handling. If this function fails, it could cause the application to crash. Consider adding a try-catch block to handle potential errors. 😊
pelikhan marked this conversation as resolved.
Show resolved Hide resolved
this._terminal.show()
}

Expand Down
52 changes: 52 additions & 0 deletions packages/vscode/src/taskprovider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as vscode from "vscode"
import { ExtensionState } from "./state"
import { resolveCli } from "./config"
import { TOOL_ID } from "../../core/src/constants"
import { quoteify } from "../../core/src/util"

export async function activeTaskProvider(state: ExtensionState) {
const { context, host } = state
const { subscriptions } = context

const taskProvider: vscode.TaskProvider = {
provideTasks: async () => {
const { cliPath, cliVersion } = await resolveCli()

Check failure on line 13 in packages/vscode/src/taskprovider.ts

View workflow job for this annotation

GitHub Actions / build

There is no error handling for the async function `resolveCli()`. If this function fails, it could cause the application to crash. Consider adding a try-catch block to handle potential errors. 😊
pelikhan marked this conversation as resolved.
Show resolved Hide resolved
const exec = cliPath
? quoteify(cliPath)
: `npx --yes genaiscript@${cliVersion}`
const scripts = state.project.templates.filter((t) => !t.isSystem)
const tasks = scripts.map((script) => {
const scriptp = host.path.relative(
host.projectFolder(),
script.filename
)
const task = new vscode.Task(
{ type: TOOL_ID, script: script.filename },
vscode.TaskScope.Workspace,
script.id,
TOOL_ID,
new vscode.ShellExecution(exec, [
"run",
scriptp,
"${relativeFile}",
])
)
pelikhan marked this conversation as resolved.
Show resolved Hide resolved
task.detail = `${script.title ?? script.description} - ${scriptp}`
task.problemMatchers = ["$tsc"]
task.presentationOptions = {
echo: true,
focus: true,
showReuseMessage: false,
clear: true,
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no error handling for the case when the host.path.relative function fails or returns an unexpected result. This could lead to unexpected behavior if the relative path cannot be correctly determined. Consider adding error handling or validation to ensure the relative path can be correctly determined. 😊

generated by pr-review-commit missing_error_handling

return task
})
return tasks
},
async resolveTask(task): Promise<vscode.Task> {
return task
},
}

subscriptions.push(vscode.tasks.registerTaskProvider(TOOL_ID, taskProvider))
}