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

cli configure #1107

Merged
merged 8 commits into from
Feb 8, 2025
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: 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
Copy link

Choose a reason for hiding this comment

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

Missing header for new section. Consider adding a brief description or summary of the configure command.

AI-generated content by pr-docs-review-commit missing_header may be incorrect


`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
```
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. Add front matter to provide metadata and structure for the document.

AI-generated content by pr-docs-review-commit frontmatter_missing may be incorrect

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
Loading