Skip to content

Commit dd23f80

Browse files
Implemented GenAI Additional Operations (#1724)
* Implemented GenAI Additional Operations * added vendor files * Added flags --------- Co-authored-by: anup-deka <[email protected]>
1 parent 7962c1e commit dd23f80

12 files changed

+792
-4
lines changed

commands/command.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ func cmdBuilderWithInit(parent *Command, cr CmdRunner, cliText, shortdesc string
100100
)
101101
checkErr(err)
102102

103+
c.Command = cmd
104+
103105
err = cr(c)
104106
checkErr(err)
105107
}

commands/command_config.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"fmt"
1818
"io"
1919

20+
"github.com/spf13/cobra"
2021
"github.com/spf13/viper"
2122

2223
"github.com/digitalocean/doctl"
@@ -27,10 +28,11 @@ import (
2728

2829
// CmdConfig is a command configuration.
2930
type CmdConfig struct {
30-
NS string
31-
Doit doctl.Config
32-
Out io.Writer
33-
Args []string
31+
NS string
32+
Doit doctl.Config
33+
Out io.Writer
34+
Args []string
35+
Command *cobra.Command
3436

3537
initServices func(*CmdConfig) error
3638
getContextAccessToken func() string

commands/displayers/genai.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package displayers
22

33
import (
4+
"fmt"
45
"io"
56

67
"github.com/digitalocean/doctl/do"
@@ -453,3 +454,123 @@ func (o *OpenAiApiKey) KV() []map[string]any {
453454
}
454455
return out
455456
}
457+
458+
type Model struct {
459+
Models []do.Model
460+
}
461+
462+
var _ Displayable = &Model{}
463+
464+
func (m *Model) JSON(out io.Writer) error {
465+
return writeJSON(m.Models, out)
466+
}
467+
func (m *Model) Cols() []string {
468+
return []string{
469+
"Id",
470+
"Name",
471+
"Agreement",
472+
"CreatedAt",
473+
"UpdatedAt",
474+
"isFoundational",
475+
"ParentId",
476+
"UploadComplete",
477+
"URL",
478+
"Version",
479+
}
480+
}
481+
482+
func (m *Model) ColMap() map[string]string {
483+
return map[string]string{
484+
"Id": "ID",
485+
"Name": "Name",
486+
"Agreement": "Agreement",
487+
"CreatedAt": "Created At",
488+
"UpdatedAt": "Updated At",
489+
"isFoundational": "Is Foundational",
490+
"ParentId": "Parent ID",
491+
"UploadComplete": "Upload Complete",
492+
"URL": "URL",
493+
"Version": "Version",
494+
}
495+
}
496+
497+
func (m *Model) KV() []map[string]any {
498+
if m == nil {
499+
return []map[string]any{}
500+
}
501+
out := make([]map[string]any, 0, len(m.Models))
502+
for _, model := range m.Models {
503+
// Format Agreement field
504+
agreementName := ""
505+
if model.Agreement != nil {
506+
agreementName = model.Agreement.Name
507+
}
508+
509+
// Format Version field
510+
versionString := ""
511+
if model.Version != nil {
512+
versionString = fmt.Sprintf("%d.%d.%d", model.Version.Major, model.Version.Minor, model.Version.Patch)
513+
}
514+
515+
out = append(out, map[string]any{
516+
"Id": model.Uuid,
517+
"Name": model.Name,
518+
"Agreement": agreementName,
519+
"CreatedAt": model.CreatedAt,
520+
"UpdatedAt": model.UpdatedAt,
521+
"isFoundational": model.IsFoundational,
522+
"ParentId": model.ParentUuid,
523+
"UploadComplete": model.UploadComplete,
524+
"URL": model.Url,
525+
"Version": versionString,
526+
})
527+
}
528+
return out
529+
}
530+
531+
type DatacenterRegion struct {
532+
DatacenterRegions do.DatacenterRegions
533+
}
534+
535+
var _ Displayable = &DatacenterRegion{}
536+
537+
func (d *DatacenterRegion) JSON(out io.Writer) error {
538+
return writeJSON(d.DatacenterRegions, out)
539+
}
540+
541+
func (d *DatacenterRegion) Cols() []string {
542+
return []string{
543+
"InferenceURL",
544+
"Region",
545+
"ServesBatch",
546+
"ServesInference",
547+
"StreamInferenceUrl",
548+
}
549+
}
550+
551+
func (d *DatacenterRegion) ColMap() map[string]string {
552+
return map[string]string{
553+
"InferenceURL": "Inference URL",
554+
"Region": "Region",
555+
"ServesBatch": "Serves Batch",
556+
"ServesInference": "Serves Inference",
557+
"StreamInferenceUrl": "Stream Inference URL",
558+
}
559+
}
560+
561+
func (d *DatacenterRegion) KV() []map[string]any {
562+
if d == nil || d.DatacenterRegions == nil {
563+
return []map[string]any{}
564+
}
565+
out := make([]map[string]any, 0, len(d.DatacenterRegions))
566+
for _, region := range d.DatacenterRegions {
567+
out = append(out, map[string]any{
568+
"InferenceURL": region.InferenceUrl,
569+
"Region": region.Region,
570+
"ServesBatch": region.ServesBatch,
571+
"ServesInference": region.ServesInference,
572+
"StreamInferenceUrl": region.StreamInferenceUrl,
573+
})
574+
}
575+
return out
576+
}

commands/genai.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ func GenAI() *Command {
3131
cmd.AddCommand(AgentCmd())
3232
// Add the knowledgebase command as a subcommand to genai
3333
cmd.AddCommand(KnowledgeBaseCmd())
34+
// Add the model command as a subcommand to genai
35+
cmd.AddCommand(ListModelsCmd())
36+
// Add the region command as a subcommand to genai
37+
cmd.AddCommand(ListRegionsCmd())
3438
// Add the OpenAI keys command as a subcommand to genai
3539
cmd.AddCommand(OpenAIKeyCmd())
3640

commands/genai_list_models.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package commands
2+
3+
import (
4+
"github.com/digitalocean/doctl/commands/displayers"
5+
)
6+
7+
func ListModelsCmd() *Command {
8+
cmd := CmdBuilder(nil, RunGenAIListModels, "list-models", "List GenAI models", `The `+"`doctl genai list-models`"+` command lists all available GenAI models.
9+
10+
The command returns the following details for each model:
11+
- The model ID
12+
- The model name
13+
- Agreement name
14+
- The model creation date, in ISO8601 combined date and time format
15+
- The model update date, in ISO8601 combined date and time format
16+
- Parent ID of the model, this model is based on
17+
- Model has been fully uploaded
18+
- Download URL for the model
19+
- Version information about a model
20+
- is_foundational: True if it is a foundational model provided by DigitalOcean`, Writer, displayerType(&displayers.Model{}), aliasOpt("models", "lm"))
21+
22+
cmd.Example = `doctl genai list-models`
23+
24+
return cmd
25+
}
26+
27+
func RunGenAIListModels(c *CmdConfig) error {
28+
models, err := c.GenAI().ListAvailableModels()
29+
if err != nil {
30+
return err
31+
}
32+
33+
return c.Display(&displayers.Model{Models: models})
34+
}

commands/genai_list_models_test.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
Copyright 2018 The Doctl Authors All rights reserved.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package commands
15+
16+
import (
17+
"testing"
18+
"time"
19+
20+
"github.com/digitalocean/doctl/do"
21+
"github.com/digitalocean/godo"
22+
"github.com/stretchr/testify/assert"
23+
)
24+
25+
// Test data
26+
var (
27+
testModel1 = do.Model{
28+
Model: &godo.Model{
29+
Uuid: "model-1",
30+
Name: "GPT-4 Turbo",
31+
IsFoundational: true,
32+
Provider: "OpenAI",
33+
InferenceName: "gpt-4-turbo",
34+
InferenceVersion: "2024-04-09",
35+
UploadComplete: true,
36+
Url: "https://api.openai.com/v1/models/gpt-4-turbo",
37+
CreatedAt: &godo.Timestamp{Time: time.Now().AddDate(0, -2, 0)},
38+
UpdatedAt: &godo.Timestamp{Time: time.Now().AddDate(0, -1, 0)},
39+
Usecases: []string{"text-generation", "chat"},
40+
Agreement: &godo.Agreement{
41+
Name: "OpenAI Terms of Service",
42+
Description: "Standard OpenAI API terms and conditions",
43+
},
44+
Version: &godo.ModelVersion{
45+
Major: 4,
46+
Minor: 0,
47+
Patch: 0,
48+
},
49+
},
50+
}
51+
52+
testModel2 = do.Model{
53+
Model: &godo.Model{
54+
Uuid: "model-2",
55+
Name: "Claude 3.5 Sonnet",
56+
IsFoundational: true,
57+
Provider: "Anthropic",
58+
InferenceName: "claude-3-5-sonnet",
59+
InferenceVersion: "20240620",
60+
UploadComplete: true,
61+
Url: "https://api.anthropic.com/v1/models/claude-3-5-sonnet",
62+
CreatedAt: &godo.Timestamp{Time: time.Now().AddDate(0, -1, -15)},
63+
UpdatedAt: &godo.Timestamp{Time: time.Now().AddDate(0, 0, -10)},
64+
Usecases: []string{"text-generation", "analysis", "coding"},
65+
Agreement: &godo.Agreement{
66+
Name: "Anthropic Service Terms",
67+
Description: "Anthropic API service agreement",
68+
},
69+
Version: &godo.ModelVersion{
70+
Major: 3,
71+
Minor: 5,
72+
Patch: 0,
73+
},
74+
},
75+
}
76+
77+
testModels = do.Models{testModel1, testModel2}
78+
)
79+
80+
func TestListModelsCommand(t *testing.T) {
81+
cmd := ListModelsCmd()
82+
assert.NotNil(t, cmd)
83+
assert.Equal(t, "list-models", cmd.Use)
84+
assert.Contains(t, cmd.Aliases, "models")
85+
assert.Contains(t, cmd.Aliases, "lm")
86+
assert.Equal(t, "List GenAI models", cmd.Short)
87+
assert.Contains(t, cmd.Long, "doctl genai list-models")
88+
}
89+
90+
func TestRunGenAIListModels(t *testing.T) {
91+
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
92+
tm.genAI.EXPECT().ListAvailableModels().Return(testModels, nil)
93+
94+
err := RunGenAIListModels(config)
95+
assert.NoError(t, err)
96+
})
97+
}
98+
99+
func TestRunGenAIListModelsError(t *testing.T) {
100+
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
101+
tm.genAI.EXPECT().ListAvailableModels().Return(nil, assert.AnError)
102+
103+
err := RunGenAIListModels(config)
104+
assert.Error(t, err)
105+
})
106+
}

commands/genai_list_regions.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package commands
2+
3+
import (
4+
"github.com/digitalocean/doctl/commands/displayers"
5+
)
6+
7+
func ListRegionsCmd() *Command {
8+
cmd := CmdBuilder(nil, RunGenAIListRegions, "list-regions", "List GenAI regions", `The `+"`doctl genai list-regions`"+` command lists all available GenAI regions.
9+
10+
The command returns the following details for each region:
11+
- Inference URL: The URL for the inference server
12+
- Region: The region code
13+
- Serves Batch: Whether this datacenter is capable of running batch jobs
14+
- Serves Inference: Whether this datacenter is capable of serving inference
15+
- Stream Inference URL: The URL for the inference streaming server`, Writer, displayerType(&displayers.DatacenterRegion{}), aliasOpt("regions", "lr"))
16+
17+
cmd.Example = `doctl genai list-regions`
18+
19+
cmd.Flags().Bool("serves-inference", false, "Filter regions that serve inference")
20+
cmd.Flags().Bool("serves-batch", false, "Filter regions that serve batch jobs")
21+
22+
return cmd
23+
}
24+
25+
func RunGenAIListRegions(c *CmdConfig) error {
26+
var servesInferencePtr, servesBatchPtr *bool
27+
28+
// Only set pointer if user passed the flag
29+
if c.Command.Flags().Changed("serves-inference") {
30+
val, _ := c.Command.Flags().GetBool("serves-inference")
31+
servesInferencePtr = &val
32+
}
33+
if c.Command.Flags().Changed("serves-batch") {
34+
val, _ := c.Command.Flags().GetBool("serves-batch")
35+
servesBatchPtr = &val
36+
}
37+
38+
DatacenterRegions, err := c.GenAI().ListDatacenterRegions(servesInferencePtr, servesBatchPtr)
39+
if err != nil {
40+
return err
41+
}
42+
43+
return c.Display(&displayers.DatacenterRegion{DatacenterRegions: DatacenterRegions})
44+
}

0 commit comments

Comments
 (0)