Skip to content

chore: remove third override syntax #585

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

Merged
merged 2 commits into from
Jun 28, 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
43 changes: 11 additions & 32 deletions docs/docs/03-tools/04-credential-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,6 @@ This is useful when working with credential overrides.

## Credential Overrides (Advanced)

:::note
The syntax for this will change at some point in the future.
:::

You can bypass credential tools and stored credentials by setting the `--credential-override` argument (or the
`GPTSCRIPT_CREDENTIAL_OVERRIDE` environment variable) when running GPTScript. To set up a credential override, you
need to be aware of which environment variables the credential tool sets. You can find this out by running the
Expand All @@ -166,41 +162,24 @@ This can be overridden with a credential alias, i.e. `credential: my-cred-tool.g
If the credential has an alias, use it instead of the tool name when you specify an override.
:::

The `--credential-override` argument must be formatted in one of the following three ways:
The `--credential-override` argument must be formatted in one of the following two ways:

#### 1. Key-Value Pairs

`toolA:ENV_VAR_1=value1,ENV_VAR_2=value2;toolB:ENV_VAR_1=value3,ENV_VAR_2=value4`
`toolA:ENV_VAR_1=value1,ENV_VAR_2=value2`

In this example, both `toolA` provides the variables `ENV_VAR_1` and `ENV_VAR_2`.
This will set the environment variables `ENV_VAR_1` and `ENV_VAR_2` to the specific values `value1` and `value2`.

In this example, both `toolA` and `toolB` provide the variables `ENV_VAR_1` and `ENV_VAR_2`.
This will set the environment variables `ENV_VAR_1` and `ENV_VAR_2` to the specific values provided for each tool.
:::info
To override more than one credential, use `;` as a separator. For example, `toolA:ENV_VAR_1=value1;toolB:ENV_VAR_2=value2`.
:::

#### 2. Environment Variables

Copy link
Member

Choose a reason for hiding this comment

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

remove toolB and its env vars here to match the updated example

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for catching this. Just pushed the fix

`toolA:ENV_VAR_1,ENV_VAR_2;toolB:ENV_VAR_3,ENV_VAR_4`
`toolA:ENV_VAR_1,ENV_VAR_2`

In this example, `toolA` provides the variables `ENV_VAR_1` and `ENV_VAR_2`, and `toolB` provides the variables `ENV_VAR_3` and `ENV_VAR_4`.
This will read the values of `ENV_VAR_1` through `ENV_VAR_4` from the current environment and set them for each tool.
In this example, `toolA` provides the variables `ENV_VAR_1` and `ENV_VAR_2`,
This will read the values of `ENV_VAR_1` through `ENV_VAR_4` from the current environment and set them for the credential.
This is a direct mapping of environment variable names. **This is not recommended when overriding credentials for
multiple tools that use the same environment variable names.**

#### 3. Environment Variable Mapping

`toolA:ENV_VAR_1->TOOL_A_ENV_VAR_1,ENV_VAR_2->TOOL_A_ENV_VAR_2;toolB:ENV_VAR_1->TOOL_B_ENV_VAR_1,ENV_VAR_2->TOOL_B_ENV_VAR_2`

In this example, `toolA` and `toolB` both provide the variables `ENV_VAR_1` and `ENV_VAR_2`.
This will set the environment variables `ENV_VAR_1` and `ENV_VAR_2` to the values of `TOOL_A_ENV_VAR_1` and
`TOOL_A_ENV_VAR_2` from the current environment for `toolA`. The same applies for `toolB`, but with the values of
`TOOL_B_ENV_VAR_1` and `TOOL_B_ENV_VAR_2`. This is a mapping of one environment variable name to another.

### Real-World Example

Here is an example of how you can use a credential override to skip running the credential tool for the Brave Search tool:

```bash
gptscript --credential-override "github.com/gptscript-ai/search/brave-credential:GPTSCRIPT_BRAVE_SEARCH_TOKEN->MY_BRAVE_SEARCH_TOKEN" github.com/gptscript-ai/search/brave '{"q": "cute cats"}'
```

If you run this command, rather than being prompted by the credential tool for your token, GPTScript will read the contents
of the environment variable `MY_BRAVE_SEARCH_TOKEN` and set that as the variable `GPTSCRIPT_BRAVE_SEARCH_TOKEN` when it runs
the script.
14 changes: 3 additions & 11 deletions pkg/runner/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ import (
)

// parseCredentialOverrides parses a string of credential overrides that the user provided as a command line arg.
// The format of credential overrides can be one of three things:
// The format of credential overrides can be one of two things:
// cred1:ENV1,ENV2;cred2:ENV1,ENV2 (direct mapping of environment variables)
// cred1:ENV1=VALUE1,ENV2=VALUE2;cred2:ENV1=VALUE1,ENV2=VALUE2 (key-value pairs)
// cred1:ENV1->OTHER_ENV1,ENV2->OTHER_ENV2;cred2:ENV1->OTHER_ENV1,ENV2->OTHER_ENV2 (mapping to other environment variables)
//
// This function turns it into a map[string]map[string]string like this:
//
Expand All @@ -36,15 +35,8 @@ func parseCredentialOverrides(override string) (map[string]map[string]string, er
for _, env := range strings.Split(envs, ",") {
key, value, found := strings.Cut(env, "=")
if !found {
var envVar string
key, envVar, found = strings.Cut(env, "->")
if found {
// User did a mapping of key -> other env var, so look up the value.
value = os.Getenv(envVar)
} else {
// User just passed an env var name as the key, so look up the value.
value = os.Getenv(key)
}
// User just passed an env var name as the key, so look up the value.
value = os.Getenv(key)
}
envMap[key] = value
}
Expand Down
128 changes: 128 additions & 0 deletions pkg/runner/credentials_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package runner

import (
"os"
"testing"

"github.com/stretchr/testify/require"
)

func TestParseCredentialOverrides(t *testing.T) {
cases := []struct {
name string
envs map[string]string
in string
out map[string]map[string]string
expectErr bool
}{
{
name: "empty",
in: "",
expectErr: true,
},
{
name: "single cred, single env",
envs: map[string]string{
"ENV1": "VALUE1",
},
in: "cred1:ENV1",
out: map[string]map[string]string{
"cred1": {
"ENV1": "VALUE1",
},
},
},
{
name: "single cred, multiple envs",
envs: map[string]string{
"ENV1": "VALUE1",
"ENV2": "VALUE2",
},
in: "cred1:ENV1,ENV2",
out: map[string]map[string]string{
"cred1": {
"ENV1": "VALUE1",
"ENV2": "VALUE2",
},
},
},
{
name: "single cred, key value pairs",
envs: map[string]string{
"ENV1": "VALUE1",
"ENV2": "VALUE2",
},
in: "cred1:ENV1=OTHERVALUE1,ENV2=OTHERVALUE2",
out: map[string]map[string]string{
"cred1": {
"ENV1": "OTHERVALUE1",
"ENV2": "OTHERVALUE2",
},
},
},
{
name: "multiple creds, multiple envs",
envs: map[string]string{
"ENV1": "VALUE1",
"ENV2": "VALUE2",
},
in: "cred1:ENV1,ENV2;cred2:ENV1,ENV2",
out: map[string]map[string]string{
"cred1": {
"ENV1": "VALUE1",
"ENV2": "VALUE2",
},
"cred2": {
"ENV1": "VALUE1",
"ENV2": "VALUE2",
},
},
},
{
name: "multiple creds, key value pairs",
envs: map[string]string{
"ENV1": "VALUE1",
"ENV2": "VALUE2",
},
in: "cred1:ENV1=OTHERVALUE1,ENV2=OTHERVALUE2;cred2:ENV1=OTHERVALUE3,ENV2=OTHERVALUE4",
out: map[string]map[string]string{
"cred1": {
"ENV1": "OTHERVALUE1",
"ENV2": "OTHERVALUE2",
},
"cred2": {
"ENV1": "OTHERVALUE3",
"ENV2": "OTHERVALUE4",
},
},
},
{
name: "invalid format",
in: "cred1=ENV1,ENV2",
expectErr: true,
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
envs := tc.envs
if envs == nil {
envs = map[string]string{}
}

for k, v := range envs {
_ = os.Setenv(k, v)
}

out, err := parseCredentialOverrides(tc.in)
if tc.expectErr {
require.Error(t, err)
return
}
require.NoError(t, err)

require.Equal(t, len(tc.out), len(out), "expected %d creds, but got %d", len(tc.out), len(out))
require.Equal(t, tc.out, out, "expected output %v, but got %v", tc.out, out)
})
}
}