Skip to content
Open
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
6 changes: 6 additions & 0 deletions args.go
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,12 @@ const (
// ArgOpenAIKeyAPIKey is the API key for the OpenAI API Key
ArgOpenAIKeyAPIKey = "api-key"

// ArgAnthropicKeyName is the name of the Anthropic API Key
ArgAnthropicKeyName = "name"

// ArgAnthropicKeyAPIKey is the API key for the Anthropic API Key
ArgAnthropicKeyAPIKey = "api-key"

// Dedicated Inference Args

// ArgDedicatedInferenceSpec is the path to a dedicated inference spec file.
Expand Down
50 changes: 50 additions & 0 deletions commands/displayers/gradientai.go
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,56 @@ func (o *OpenAiApiKey) KV() []map[string]any {
return out
}

type AnthropicApiKey struct {
AnthropicApiKeys do.AnthropicApiKeys
}

var _ Displayable = &AnthropicApiKey{}

func (a *AnthropicApiKey) JSON(out io.Writer) error {
return writeJSON(a.AnthropicApiKeys, out)
}

func (a *AnthropicApiKey) Cols() []string {
return []string{
"Name",
"UUID",
"CreatedAt",
"CreatedBy",
"UpdatedAt",
"DeletedAt",
}
}

func (a *AnthropicApiKey) ColMap() map[string]string {
return map[string]string{
"Name": "Name",
"UUID": "UUID",
"CreatedAt": "Created At",
"CreatedBy": "Created By",
"UpdatedAt": "Updated At",
"DeletedAt": "Deleted At",
}
}

func (a *AnthropicApiKey) KV() []map[string]any {
if a == nil || a.AnthropicApiKeys == nil {
return []map[string]any{}
}
out := make([]map[string]any, 0, len(a.AnthropicApiKeys))
for _, key := range a.AnthropicApiKeys {
out = append(out, map[string]any{
"Name": key.Name,
"UUID": key.Uuid,
"CreatedAt": key.CreatedAt,
"CreatedBy": key.CreatedBy,
"UpdatedAt": key.UpdatedAt,
"DeletedAt": key.DeletedAt,
})
}
return out
}

type Model struct {
Models []do.Model
}
Expand Down
2 changes: 2 additions & 0 deletions commands/gradientai.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ func GradientAI() *Command {
cmd.AddCommand(ListRegionsCmd())
// Add the OpenAI keys command as a subcommand to Gradient AI
cmd.AddCommand(OpenAIKeyCmd())
// Add the Anthropic keys command as a subcommand to Gradient AI
cmd.AddCommand(AnthropicKeyCmd())

return cmd
}
211 changes: 211 additions & 0 deletions commands/gradientai_anthropic_key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package commands

import (
"fmt"

"github.com/digitalocean/doctl"
"github.com/digitalocean/doctl/commands/displayers"
"github.com/digitalocean/doctl/do"
"github.com/digitalocean/godo"
"github.com/spf13/cobra"
)

func AnthropicKeyCmd() *Command {
cmd := &Command{
Command: &cobra.Command{
Use: "anthropic-key",
Aliases: []string{"ak"},
Short: "Display commands that manage DigitalOcean Anthropic API Keys.",
Long: "The subcommands of `doctl gradient anthropic-key` allow you to access and manage Anthropic API keys.",
},
}

cmdAnthropicKeyList := CmdBuilder(
cmd,
RunAnthropicKeyList,
"list",
"Lists all Anthropic API Keys",
"Lists all Anthropic API Keys available in your account",
Writer, aliasOpt("ls"),
displayerType(&displayers.AnthropicApiKey{}),
)
cmdAnthropicKeyList.Example = `The following example lists information about all Anthropic API Keys ` + "\n" +
` doctl gradient anthropic-key list `

cmdAnthropicKeyGet := CmdBuilder(
cmd,
RunAnthropicKeyGet,
"get <anthropic-api-key-uuid>",
"Retrieves an Anthropic API Key by its UUID",
"Retrieves information about an Anthropic API Key",
Writer, aliasOpt("g"),
displayerType(&displayers.AnthropicApiKey{}),
)
cmdAnthropicKeyGet.Example = `The following example retrieves information about an Anthropic API Key with ID - f81d4fae-0000-11d0-a765-000000000000` + "\n" +
` doctl gradient anthropic-key get f81d4fae-0000-11d0-a765-000000000000`

cmdAnthropicKeyGetAgents := CmdBuilder(
cmd,
RunAnthropicKeyGetAgents,
"get-agents <anthropic-api-key-uuid>",
"Lists agents using an Anthropic API Key",
"Lists all agents that are using the specified Anthropic API Key",
Writer, aliasOpt("ga"),
displayerType(&displayers.Agent{}),
)
cmdAnthropicKeyGetAgents.Example = `The following example retrieves information about an Anthropic API Key with ID - f81d4fae-0000-11d0-a765-000000000000` + "\n" +
` doctl gradient anthropic-key get-agents f81d4fae-0000-11d0-a765-000000000000 `

cmdAnthropicKeyCreate := CmdBuilder(
cmd,
RunAnthropicKeyCreate,
"create",
"Creates an Anthropic API Key",
"Creates a new Anthropic API Key with the specified name and API key.",
Writer, aliasOpt("c"),
displayerType(&displayers.AnthropicApiKey{}),
)
cmdAnthropicKeyCreate.Example = `The following example creates an Anthropic API Key ` + "\n" +
` doctl gradient anthropic-key create --name my-key --api-key sk-ant-1234567890abcdef1234567890abcdef `
AddStringFlag(cmdAnthropicKeyCreate, "name", "", "", "The name of the Anthropic API Key.", requiredOpt())
AddStringFlag(cmdAnthropicKeyCreate, "api-key", "", "", "The API key for the Anthropic API Key.", requiredOpt())

cmdAnthropicKeyUpdate := CmdBuilder(
cmd,
RunAnthropicKeyUpdate,
"update <anthropic-api-key-uuid>",
"Updates an Anthropic API Key by its UUID",
"Updates an existing Anthropic API Key with the specified name and API key.",
Writer, aliasOpt("u"),
displayerType(&displayers.AnthropicApiKey{}),
)
cmdAnthropicKeyUpdate.Example = `The following example updates an Anthropic API Key with ID - f81d4fae-0000-11d0-a765-000000000000 ` + "\n" +
` doctl gradient anthropic-key update f81d4fae-0000-11d0-a765-000000000000 --name my-key --api-key sk-ant-1234567890abcdef1234567890abcdef `
AddStringFlag(cmdAnthropicKeyUpdate, "name", "", "", "The name of the Anthropic API Key.")
AddStringFlag(cmdAnthropicKeyUpdate, "api-key", "", "", "The API key for the Anthropic API Key.")

cmdAnthropicKeyDelete := CmdBuilder(
cmd,
RunAnthropicKeyDelete,
"delete <anthropic-api-key-uuid>",
"Deletes an Anthropic API Key by its UUID",
"Deletes an Anthropic API Key by its UUID.",
Writer, aliasOpt("rm"),
)
cmdAnthropicKeyDelete.Example = `The following example deletes an Anthropic API Key with ID - f81d4fae-0000-11d0-a765-000000000000 ` + "\n" +
` doctl gradient anthropic-key delete f81d4fae-0000-11d0-a765-000000000000 ` + "\n" +
`Note - Anthropic Keys linked to DO Agents cannot be deleted unless you change it from agent`
AddBoolFlag(cmdAnthropicKeyDelete, doctl.ArgForce, "f", false, "Forces deletion without confirmation.")

return cmd
}

func RunAnthropicKeyList(c *CmdConfig) error {
anthropicApiKeys, err := c.GradientAI().ListAnthropicAPIKeys()
if err != nil {
return err
}
return c.Display(&displayers.AnthropicApiKey{AnthropicApiKeys: anthropicApiKeys})
}

func RunAnthropicKeyGet(c *CmdConfig) error {
if len(c.Args) < 1 {
return doctl.NewMissingArgsErr(c.NS)
}

anthropicApiKey, err := c.GradientAI().GetAnthropicAPIKey(c.Args[0])
if err != nil {
return err
}
return c.Display(&displayers.AnthropicApiKey{AnthropicApiKeys: do.AnthropicApiKeys{*anthropicApiKey}})
}

func RunAnthropicKeyGetAgents(c *CmdConfig) error {
if len(c.Args) < 1 {
return doctl.NewMissingArgsErr(c.NS)
}

anthropicApiKeyID := c.Args[0]
agents, err := c.GradientAI().ListAgentsByAnthropicAPIKey(anthropicApiKeyID)
if err != nil {
return err
}
return c.Display(&displayers.Agent{Agents: agents})
}

func RunAnthropicKeyCreate(c *CmdConfig) error {
name, err := c.Doit.GetString(c.NS, doctl.ArgAnthropicKeyName)
if err != nil {
return err
}

apiKey, err := c.Doit.GetString(c.NS, doctl.ArgAnthropicKeyAPIKey)
if err != nil {
return err
}

anthropicApiKeyCreate := &godo.AnthropicAPIKeyCreateRequest{
Name: name,
ApiKey: apiKey,
}

anthropicApiKey, err := c.GradientAI().CreateAnthropicAPIKey(anthropicApiKeyCreate)
if err != nil {
return err
}

return c.Display(&displayers.AnthropicApiKey{AnthropicApiKeys: do.AnthropicApiKeys{*anthropicApiKey}})
}

func RunAnthropicKeyUpdate(c *CmdConfig) error {
if len(c.Args) < 1 {
return doctl.NewMissingArgsErr(c.NS)
}

anthropicApiKeyID := c.Args[0]

name, err := c.Doit.GetString(c.NS, doctl.ArgAnthropicKeyName)
if err != nil {
return err
}

apiKey, err := c.Doit.GetString(c.NS, doctl.ArgAnthropicKeyAPIKey)
if err != nil {
return err
}

anthropicApiKeyUpdate := &godo.AnthropicAPIKeyUpdateRequest{
Name: name,
ApiKey: apiKey,
ApiKeyUuid: anthropicApiKeyID,
}

anthropicApiKey, err := c.GradientAI().UpdateAnthropicAPIKey(anthropicApiKeyID, anthropicApiKeyUpdate)
if err != nil {
return err
}

return c.Display(&displayers.AnthropicApiKey{AnthropicApiKeys: do.AnthropicApiKeys{*anthropicApiKey}})
}

func RunAnthropicKeyDelete(c *CmdConfig) error {
if len(c.Args) < 1 {
return doctl.NewMissingArgsErr(c.NS)
}

anthropicApiKeyID := c.Args[0]
force, err := c.Doit.GetBool(c.NS, doctl.ArgForce)
if err != nil {
return err
}
if force || AskForConfirmDelete("Anthropic API Key", 1) == nil {
_, err := c.GradientAI().DeleteAnthropicAPIKey(anthropicApiKeyID)
if err != nil {
return err
}
notice("Anthropic API Key deleted successfully")
} else {
return fmt.Errorf("operation aborted")
}
return nil
}
99 changes: 99 additions & 0 deletions commands/gradientai_anthropic_key_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package commands

import (
"testing"

"github.com/digitalocean/doctl"
"github.com/digitalocean/doctl/do"
"github.com/digitalocean/godo"
"github.com/stretchr/testify/assert"
)

var (
testAnthropicKey = do.AnthropicApiKey{
AnthropicApiKeyInfo: &godo.AnthropicApiKeyInfo{
Uuid: "d35e5cb7-7957-4643-8e3a-1ab4eb3a494c",
Name: "Test Anthropic Key",
},
}
)

func TestAnthropicKeyCommand(t *testing.T) {
cmd := AnthropicKeyCmd()
assert.NotNil(t, cmd)
assertCommandNames(t, cmd, "create", "delete", "get", "get-agents", "list", "update")
}

func TestAnthropicKeyGet(t *testing.T) {
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
anthropic_key_id := "00000000-0000-4000-8000-000000000000"
config.Args = append(config.Args, anthropic_key_id)
tm.gradientAI.EXPECT().GetAnthropicAPIKey("00000000-0000-4000-8000-000000000000").Return(&testAnthropicKey, nil)
err := RunAnthropicKeyGet(config)
assert.NoError(t, err)
})
}

func TestAnthropicKeyList(t *testing.T) {
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
tm.gradientAI.EXPECT().ListAnthropicAPIKeys().Return(do.AnthropicApiKeys{testAnthropicKey}, nil)
err := RunAnthropicKeyList(config)
assert.NoError(t, err)
})
}

func TestAnthropicKeyCreate(t *testing.T) {
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {

config.Doit.Set(config.NS, doctl.ArgAnthropicKeyName, "Test Anthropic Key")
config.Doit.Set(config.NS, doctl.ArgAnthropicKeyAPIKey, "sk-ant-proddfsefac")

tm.gradientAI.EXPECT().CreateAnthropicAPIKey(&godo.AnthropicAPIKeyCreateRequest{
Name: "Test Anthropic Key",
ApiKey: "sk-ant-proddfsefac",
}).Return(&testAnthropicKey, nil)

err := RunAnthropicKeyCreate(config)
assert.NoError(t, err)
})
}

func TestAnthropicKeyDelete(t *testing.T) {
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
anthropic_api_id := "00000000-0000-4000-8000-000000000000"
config.Args = append(config.Args, anthropic_api_id)
config.Doit.Set(config.NS, doctl.ArgForce, true)
tm.gradientAI.EXPECT().DeleteAnthropicAPIKey("00000000-0000-4000-8000-000000000000").Return(&testAnthropicKey, nil)
err := RunAnthropicKeyDelete(config)
assert.NoError(t, err)
})
}

func TestAnthropicKeyUpdate(t *testing.T) {
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
anthropic_api_id := "00000000-0000-4000-8000-000000000000"
config.Args = append(config.Args, anthropic_api_id)

config.Doit.Set(config.NS, doctl.ArgAnthropicKeyName, "Updated Anthropic Key")
config.Doit.Set(config.NS, doctl.ArgAnthropicKeyAPIKey, "updated-api-key")

tm.gradientAI.EXPECT().UpdateAnthropicAPIKey("00000000-0000-4000-8000-000000000000", &godo.AnthropicAPIKeyUpdateRequest{
Name: "Updated Anthropic Key",
ApiKey: "updated-api-key",
ApiKeyUuid: "00000000-0000-4000-8000-000000000000",
}).Return(&testAnthropicKey, nil)

err := RunAnthropicKeyUpdate(config)
assert.NoError(t, err)
})
}

func TestAnthropicKeyGetAgents(t *testing.T) {
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
anthropic_key_id := "00000000-0000-4000-8000-000000000000"
config.Args = append(config.Args, anthropic_key_id)
tm.gradientAI.EXPECT().ListAgentsByAnthropicAPIKey("00000000-0000-4000-8000-000000000000").Return(do.Agents{}, nil)
err := RunAnthropicKeyGetAgents(config)
assert.NoError(t, err)
})
}
Loading
Loading