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

Support for direct call of genaiscript from js #925

Merged
merged 14 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/anthropic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ jobs:
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: cache
run: yarn test:scripts summarize-cached --model anthropic:claude-3-5-sonnet-20240620
run: yarn run:script summarize-cached --model anthropic:claude-3-5-sonnet-20240620 --small-model anthropic:claude-3-5-sonnet-20240620
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
64 changes: 64 additions & 0 deletions docs/src/content/docs/reference/cli/api.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
title: Node.JS API
sidebar:
order: 50
---
Copy link

Choose a reason for hiding this comment

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

Front matter is missing at the beginning of the file. It should include a title and sidebar order.

generated by pr-docs-review-commit missing_frontmatter


import { Tabs, TabItem } from "@astrojs/starlight/components"

This page describes how to import and use the [cli](/genaiscript/reference/cli) as an API in your Node.JS application.

Assuming you have have added the cli as a dependency in your project, you can import the cli as follows:

<Tabs>
<TabItem value="npm" label="npm">

```sh
npm install -D genaiscript
```

</TabItem>

<TabItem value="yarn" label="yarn">

```sh
yarn add -D genaiscript
```

</TabItem>

</Tabs>

The API can be imported using imports from **"genaiscript/api"**.

```javascript
import { runScript } from "genaiscript/api"
```

The imported `api.mjs` wrapper is a tiny, zero dependency loader that
spawns a [Node.JS worker thread](https://nodejs.org/api/worker_threads.html) to run GenAIScript.

- No pollutation of the globals
- No side effects on the process

## `run`

The `run` function wraps the [cli run](/genaiscript/reference/cli/run) command.

```javascript
import { run } from "genaiscript/api"

const results = await run("summarize", ["myfile.txt"])
```

### Environment variables

You can set the environment variables for the GenAIScript process by passing an object as the `env` field in the options. By default, the worker will inherit `process.env`.

```javascript
const results = await run("summarize", ["myfile.txt"], {
env: {
MY_ENV_VAR: "value",
},
})
```
Copy link

Choose a reason for hiding this comment

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

Front matter is missing at the beginning of the file. It should include a title and sidebar order.

generated by pr-docs-review-commit missing_frontmatter

16 changes: 10 additions & 6 deletions docs/src/content/docs/reference/cli/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ where `--yes` skips the confirmation prompt to install the package.

The CLI is a Node.JS package hosted on [npm](https://www.npmjs.com/package/genaiscript).

- Install [Node.JS LTS](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) (Node.JS includes npm and npx).
- Install [Node.JS LTS](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) (Node.JS includes npm and npx).

## Installation

- Install locally as a `devDependency` in your project.
- Install locally as a `devDependency` in your project.

<Tabs>
<TabItem label="npm" icon="seti:npm">
Expand All @@ -46,13 +46,13 @@ yarn add -D genaiscript
</TabItem>
</Tabs>

- Install it globally.
- Install it globally.

```sh "-g"
npm install -g genaiscript
```

- Check that your node version is at least 20._ and npm 10._ by running this command.
- Check that your node version is at least 20._ and npm 10._ by running this command.

```sh
node -v
Expand All @@ -77,13 +77,13 @@ system issues where the tool is not found in the path.
npx genaiscript ...
```

- Add `--yes` to skip the confirmation prompt, which is useful in a CI scenario.
- Add `--yes` to skip the confirmation prompt, which is useful in a CI scenario.

```sh "--yes"
npx --yes genaiscript ...
```

- Specify the version range to avoid unexpected behavior with cached installations of the CLI using npx.
- Specify the version range to avoid unexpected behavior with cached installations of the CLI using npx.

```sh "@^1.16.0"
npx --yes genaiscript@^1.16.0 ...
Expand Down Expand Up @@ -132,6 +132,10 @@ npx genaiscript scripts model [script]

where [script] can be a script id or a file path.

## Using a the CLI as a Node.JS API

The CLI can be imported and [used as an API in your Node.JS application](/genaiscript/reference/cli/api).

## Topics

Copy link

Choose a reason for hiding this comment

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

The link to the API documentation should be more descriptive than "Using a the CLI as a Node.JS API". Consider using a more specific title.

generated by pr-docs-review-commit link_to_api_doc

<DirectoryLinks directory="reference/cli" />
7 changes: 6 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
"bin": {
"genaiscript": "built/genaiscript.cjs"
},
"exports": {
".": "./built/genaiscript.cjs",
"./package.json": "./package.json"
},
"files": [
"built/api.mjs",
"built/genaiscript.cjs"
],
"publisher": "Microsoft",
Expand Down Expand Up @@ -98,7 +103,7 @@
"zx": "^8.2.4"
},
"scripts": {
"compile": "esbuild src/main.ts --metafile=./esbuild.meta.json --bundle --platform=node --target=node20 --outfile=built/genaiscript.cjs --external:tsx --external:esbuild --external:get-tsconfig --external:resolve-pkg-maps --external:dockerode --external:pdfjs-dist --external:web-tree-sitter --external:tree-sitter-wasms --external:promptfoo --external:typescript --external:@lvce-editor/ripgrep --external:gpt-3-encoder --external:mammoth --external:xlsx --external:mathjs --external:@azure/identity --external:gpt-tokenizer --external:playwright --external:@inquirer/prompts --external:jimp --external:turndown --external:turndown-plugin-gfm --external:vectra --external:tabletojson --external:html-to-text --external:@octokit/rest --external:@octokit/plugin-throttling --external:@octokit/plugin-retry --external:@octokit/plugin-paginate-rest --external:skia-canvas --external:@huggingface/transformers --external:@modelcontextprotocol/sdk --external:@anthropic-ai/sdk && node ../../scripts/patch-cli.mjs",
"compile": "esbuild src/api.ts --outfile=built/api.mjs && esbuild src/main.ts --metafile=./esbuild.meta.json --bundle --platform=node --target=node20 --outfile=built/genaiscript.cjs --external:tsx --external:esbuild --external:get-tsconfig --external:resolve-pkg-maps --external:dockerode --external:pdfjs-dist --external:web-tree-sitter --external:tree-sitter-wasms --external:promptfoo --external:typescript --external:@lvce-editor/ripgrep --external:gpt-3-encoder --external:mammoth --external:xlsx --external:mathjs --external:@azure/identity --external:gpt-tokenizer --external:playwright --external:@inquirer/prompts --external:jimp --external:turndown --external:turndown-plugin-gfm --external:vectra --external:tabletojson --external:html-to-text --external:@octokit/rest --external:@octokit/plugin-throttling --external:@octokit/plugin-retry --external:@octokit/plugin-paginate-rest --external:skia-canvas --external:@huggingface/transformers --external:@modelcontextprotocol/sdk --external:@anthropic-ai/sdk && node ../../scripts/patch-cli.mjs",
"compile-debug": "esbuild src/main.ts --sourcemap --metafile=./esbuild.meta.json --bundle --platform=node --target=node20 --outfile=built/genaiscript.cjs --external:tsx --external:esbuild --external:get-tsconfig --external:resolve-pkg-maps --external:dockerode --external:pdfjs-dist --external:web-tree-sitter --external:tree-sitter-wasms --external:promptfoo --external:typescript --external:@lvce-editor/ripgrep --external:gpt-3-encoder --external:mammoth --external:xlsx --external:mathjs --external:@azure/identity --external:gpt-tokenizer --external:playwright --external:@inquirer/prompts --external:jimp --external:turndown --external:turndown-plugin-gfm --external:vectra --external:tabletojson --external:html-to-text --external:@octokit/rest --external:@octokit/plugin-throttling --external:@octokit/plugin-retry --external:@octokit/plugin-paginate-rest --external:skia-canvas --external:@huggingface/transformers --external:@modelcontextprotocol/sdk --external:@anthropic-ai/sdk",
"postcompile": "node built/genaiscript.cjs info help > ../../docs/src/content/docs/reference/cli/commands.md",
"vis:treemap": "npx --yes esbuild-visualizer --metadata esbuild.meta.json --filename esbuild.treemap.html",
Expand Down
43 changes: 43 additions & 0 deletions packages/cli/src/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { GenerationResult } from "../../core/src/generation"
import type { PromptScriptRunOptions } from "../../core/src/server/messages"
import { Worker } from "node:worker_threads"
import { fileURLToPath } from "url"
import { dirname, join } from "node:path"

/**
* Runs a GenAIScript script with the given files and options.
* This function acts similarly to the `run` command in the CLI.
* @param scriptId script identifier or full file path
* @param files list of file paths to run the script on, leave empty if not needed
* @param options
* @returns
*/
export async function run(
scriptId: string,
files?: string[],
options?: Partial<PromptScriptRunOptions> & {
env?: Record<string, string>
}
): Promise<{
exitCode: number
result?: GenerationResult
}> {
const { env, ...rest } = options || {}
const workerData = {
type: "run",
scriptId,
files: files || [],
options: rest,
}

const filename =
typeof __filename === "undefined"
? join(dirname(fileURLToPath(import.meta.url)), "genaiscript.cjs") // ignore esbuild warning
: __filename
const worker = new Worker(filename, { workerData, name: options?.label })
return new Promise((resolve, reject) => {
worker.on("online", () => process.stderr.write(`worker: online\n`))
worker.on("message", resolve)
worker.on("error", reject)
})
}
29 changes: 20 additions & 9 deletions packages/cli/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
// Main entry point for the CLI application

// Import necessary modules and functions
import { installGlobals } from "../../core/src/globals"
import { cli } from "./cli"
import { workerData } from "node:worker_threads"
import { worker } from "./worker"
import { run } from "./api"
import { PromptScriptRunOptions } from "../../core/src/server/messages"
import { GenerationResult } from "../../core/src/generation"

// Initialize global settings or variables for the application
// This might include setting up global error handlers or configurations
installGlobals()
export { run, type PromptScriptRunOptions, type GenerationResult }

// Execute the command-line interface logic
// This function likely handles parsing input arguments and executing commands
cli()
// if this file is not the entry point, skip cli
if (require.main === module) {
// Initialize global settings or variables for the application
// This might include setting up global error handlers or configurations
installGlobals()
if (workerData) {
// Executes a worker
worker()
} else {
// Execute the command-line interface logic
// This function likely handles parsing input arguments and executing commands
cli()
}
}
2 changes: 1 addition & 1 deletion packages/cli/src/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export async function parseAnyToJSON(
else if (YAML_REGEX.test(file)) data = YAMLParse(src)
else if (XML_REGEX.test(file)) data = XMLParse(src)
else if (MD_REGEX.test(file) || MDX_REGEX.test(file))
data = YAML.parse(splitMarkdown(src).frontmatter)
data = YAMLParse(splitMarkdown(src).frontmatter)
else throw new Error("Unsupported file format")
}

Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export async function runScriptWithExitCode(
host.path.basename(scriptId).replace(GENAI_ANYTS_REGEX, ""),
`${new Date().toISOString().replace(/[:.]/g, "-")}.trace.md`
)
const res = await runScript(scriptId, files, { ...options, outTrace })
const res = await runScriptInternal(scriptId, files, { ...options, outTrace })
exitCode = res.exitCode
if (
exitCode === SUCCESS_ERROR_CODE ||
Expand All @@ -162,7 +162,7 @@ export async function runScriptWithExitCode(
process.exit(exitCode)
}

export async function runScript(
export async function runScriptInternal(
scriptId: string,
files: string[],
options: Partial<PromptScriptRunOptions> &
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { WebSocketServer } from "ws"
import { runPromptScriptTests } from "./test"
import { PROMPTFOO_VERSION } from "./version"
import { runScript } from "./run"
import { runScriptInternal } from "./run"
import { AbortSignalCancellationController } from "../../core/src/cancellation"
import {
SERVER_PORT,
Expand Down Expand Up @@ -304,7 +304,7 @@ export async function startServer(options: { port: string; apiKey?: string }) {
)
})
logVerbose(`run ${runId}: starting ${script}`)
const runner = runScript(script, files, {
const runner = runScriptInternal(script, files, {
...options,
trace,
cancellationToken: canceller.token,
Expand Down
4 changes: 3 additions & 1 deletion packages/cli/src/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"compilerOptions": {
"types": ["node"],
"declarationDir": "../built/types",
"outDir": "../built"
"outDir": "../built",
"skipLibCheck": true,
"emitDeclarationOnly": true
},
"include": [".", "../../core/src/types/*.d.ts"]
}
22 changes: 22 additions & 0 deletions packages/cli/src/worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { workerData, parentPort } from "node:worker_threads"
import { runScriptInternal } from "./run"
import { NodeHost } from "./nodehost"

export async function worker() {
await NodeHost.install(undefined) // Install NodeHost with environment options

const { type, ...data } = workerData as {
type: string
} & object
switch (type) {
case "run": {
const { scriptId, files, ...options } = data as {
scriptId: string
files: string[]
} & object
const { result } = await runScriptInternal(scriptId, files, options)
parentPort.postMessage(result)
break
}
}
}
2 changes: 1 addition & 1 deletion packages/core/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export const DEFAULT_SMALL_MODEL_CANDIDATES = [
"azure_serverless:gpt-4o-mini",
DEFAULT_SMALL_MODEL,
"google:gemini-1.5-flash-002",
"anthropic:claude-instant-1",
"anthropic:claude-instant-1.2",
"mistral:mistral-small-latest",
"github:gpt-4o-mini",
"client:gpt-4-mini",
Expand Down
Loading
Loading