Skip to content

Commit

Permalink
cli configure (#1107)
Browse files Browse the repository at this point in the history
* towards configure support

* link to docs

* more config

* twekas

* helper flag

* configure mode

* configure docs

* add responseType, responseSchema to cache key
  • Loading branch information
pelikhan authored Feb 8, 2025
1 parent 0e212ce commit 0a08154
Show file tree
Hide file tree
Showing 10 changed files with 407 additions and 11 deletions.
2 changes: 2 additions & 0 deletions .gitignore.genai
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ package.json
package-lock.json
yarn.lock
**/package.lock
docs/dist/
docs/distasw/
27 changes: 27 additions & 0 deletions docs/public/schemas/llms.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,33 @@
"description": "Alias for transcription model"
}
}
},
"env": {
"type": "object",
"description": "Environment variables for the provider",
"additionalProperties": {
"^[a-zA-Z0-9:_-]+$": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"required": {
"type": "boolean",
"description": "Indicates if the variable is required"
},
"secret": {
"type": "boolean",
"description": "Indicates if the variable is a secret"
},
"format": {
"type": "string",
"description": "Format of the variable",
"enum": ["url"]
}
}
}
}
}
},
"additionalProperties": false,
Expand Down
8 changes: 8 additions & 0 deletions docs/src/content/docs/getting-started/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,14 @@ env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
```

## `configure` command

The [configure](/genaiscript/reference/cli/configure) command is an interactive command to configure and validate the LLM connections.

```sh
npx genaiscript configure
```

## OpenAI

`openai` is the OpenAI chat model provider.
Expand Down
18 changes: 18 additions & 0 deletions docs/src/content/docs/reference/cli/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@ sidebar:
<!-- autogenerated, do not edit -->
A full list of the CLI command and its respective help text.

## `configure`

```
Usage: genaiscript configure [options]
Interactive help to configure providers
Options:
-p, --provider <string> Preferred LLM provider aliases (choices: "openai",
"azure", "azure_serverless",
"azure_serverless_models", "anthropic",
"anthropic_bedrock", "google", "huggingface",
"mistral", "alibaba", "github", "deepseek",
"transformers", "ollama", "lmstudio", "jan",
"llamafile", "litellm", "whisperasr")
-h, --help display help for command
```

## `run`

```
Expand Down
12 changes: 12 additions & 0 deletions docs/src/content/docs/reference/cli/configure.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: configure
descript: Configure and validate the LLM connections.
sidebar:
order: 4
---

Interactice command to configure and validate the LLM connections.

```bash
npx genaiscript configure
```
21 changes: 17 additions & 4 deletions packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import { logVerbose } from "../../core/src/util" // Utility logging
import { semverSatisfies } from "../../core/src/semver" // Semantic version checking
import { convertFiles } from "./convert"
import { extractAudio, extractVideoFrames, probeVideo } from "./video"
import { configure } from "./configure"

/**
* Main function to initialize and run the CLI.
Expand Down Expand Up @@ -96,6 +97,21 @@ export async function cli() {
program.on("option:no-colors", () => setConsoleColors(false))
program.on("option:quiet", () => setQuiet(true))

program
.command("configure")
.description("Interactive help to configure providers")
.addOption(
new Option(
"-p, --provider <string>",
"Preferred LLM provider aliases"
).choices(
MODEL_PROVIDERS.filter(
({ id }) => id !== MODEL_PROVIDER_GITHUB_COPILOT_CHAT
).map(({ id }) => id)
)
)
.action(configure)

// Define 'run' command for executing scripts
const run = program
.command("run")
Expand Down Expand Up @@ -145,10 +161,7 @@ export async function cli() {
"-prr, --pull-request-reviews",
"create pull request reviews from annotations"
)
.option(
"-tm, --teams-message",
"Posts a message to the teams channel"
)
.option("-tm, --teams-message", "Posts a message to the teams channel")
.option("-j, --json", "emit full JSON response to output")
.option("-y, --yaml", "emit full YAML response to output")
.option(`-fe, --fail-on-errors`, `fails on detected annotation error`)
Expand Down
137 changes: 137 additions & 0 deletions packages/cli/src/configure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { select, input, confirm, password } from "@inquirer/prompts"
import { MODEL_PROVIDERS } from "../../core/src/constants"
import { resolveLanguageModelConfigurations } from "../../core/src/config"
import { parse } from "dotenv"
import { readFile } from "fs/promises"
import { writeFile } from "fs/promises"
import { runtimeHost } from "../../core/src/host"
import { deleteUndefinedValues } from "../../core/src/cleaners"
import { logInfo, logVerbose, logWarn } from "../../core/src/util"

export async function configure(options: { provider?: string }) {
while (true) {
const provider = options?.provider
? MODEL_PROVIDERS.find(({ id }) => options.provider === id)
: await select({
message: "Select a LLM provider to configure",
choices: MODEL_PROVIDERS.map((provider) => ({
name: provider.id,
value: provider,
description: provider.detail,
})),
})
if (!provider) break

logInfo(`configurating ${provider.id} (${provider.detail})`)
logVerbose(
`- docs: https://microsoft.github.io/genaiscript/getting-started/configuration#${provider.id}`
)
while (true) {
const config = await runtimeHost.readConfig()
logVerbose(`- env file: ${config.envFile}`)
const envText = await readFile(config.envFile, "utf-8")
const env = parse(envText)
const conn = (
await resolveLanguageModelConfigurations(provider.id, {
token: false,
error: true,
models: true,
})
)?.[0]
if (conn) {
const { error, models, ...rest } = conn
logInfo("")
logInfo(
YAML.stringify(
deleteUndefinedValues({
configuration: deleteUndefinedValues({
...rest,
models: models?.length ?? undefined,
}),
})
)
)
if (error) logWarn(`error: ${error}`)
else logInfo(`configured!`)
} else {
logWarn(`no configuration found`)
}
if (!provider.env) {
logInfo(
`sorry, this provider is not yet configurable through the cli`
)
break
}
const envVars = Object.entries(provider.env)
if (!envVars.length) {
logInfo(`this provider does not have configuration flags`)
break
}

if (!conn?.error) {
const edit = await confirm({
message: `do you want to edit the configuration?`,
})
if (!edit) break
}
for (const ev of envVars) {
const [name, info] = ev
const oldValue = env[name]
let value = oldValue
if (value) {
const edit = await confirm({
message: `found a value for ${name}, do you want to edit?`,
})
if (!edit) continue
}
if (info.secret) {
value = await password({
message: `enter a value for ${name}`,
mask: false,
})
} else {
value = await input({
message: `enter a value for ${name} (optional, leave empty to skip)`,
default: value,
required: info.required,
theme: {
validationFailureMode: "keep",
},
validate: (v) => {
console.log(v)
if (info.format === "url") {
if (v && !URL.canParse(v)) return "invalid url"
}
return true
},
})
}
if (value === "") continue
if (value !== oldValue)
await patchEnvFile(config.envFile, name, value)
}
}
}
}

async function patchEnvFile(filePath: string, key: string, value: string) {
logVerbose(`patching ${filePath}, ${key}`)

const fileContent = await readFile(filePath, "utf-8")
const lines = fileContent.split("\n")
let found = false

const updatedLines = lines.map((line) => {
if (line.startsWith(`${key}=`)) {
found = true
return `${key}=${value}`
}
return line
})

if (!found) {
updatedLines.push(`${key}=${value}`)
}

await writeFile(filePath, updatedLines.join("\n"), "utf-8")
}
2 changes: 2 additions & 0 deletions packages/core/src/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,8 @@ export async function executeChatSession(
const cachedKey = deleteUndefinedValues({
modelid: model,
...req,
responseType,
responseSchema,
...cfgNoToken,
}) satisfies ChatCompletionRequestCacheKey
const validator = (value: ChatCompletionResponse) => {
Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,15 @@ export const MODEL_PROVIDERS = Object.freeze<
speech?: boolean
tokenless?: boolean
aliases?: Record<string, string>
env?: Record<
string,
{
description?: string
secret?: boolean
required?: boolean
format?: string
}
>
}[]
>(CONFIGURATION_DATA.providers)
export const MODEL_PRICINGS = Object.freeze<
Expand Down
Loading

0 comments on commit 0a08154

Please sign in to comment.