Skip to content

Commit

Permalink
refactor: move Ask api calls into llm pkg.
Browse files Browse the repository at this point in the history
Future commit will impl custom llm support for savvy ask.
  • Loading branch information
joshi4 committed Dec 21, 2024
1 parent f5e3bd0 commit 9b4f0ed
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 58 deletions.
47 changes: 10 additions & 37 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 35,7 @@ type Client interface {
GenerateRunbookV2(ctx context.Context, commands []model.RecordedCommand) (*GeneratedRunbook, error)
// Deprecated. Use GenerateRunbookV2 instead
GenerateRunbook(ctx context.Context, commands []string) (*GeneratedRunbook, error)
Ask(ctx context.Context, question QuestionInfo) (*Runbook, error)
Ask(ctx context.Context, question model.QuestionInfo) (*Runbook, error)
Explain(ctx context.Context, code CodeInfo) (<-chan string, error)
StepContentByStepID(ctx context.Context, stepID string) (*StepContent, error)
}
Expand Down Expand Up @@ -89,11 89,13 @@ func New() (Client, error) {
return nil, fmt.Errorf("%w: %w", ErrInvalidClient, err)
}

cl := &http.Client{
Transport: authz.NewRoundTripper(cfg.Token, config.Version()),
}

c := &client{
cl: &http.Client{
Transport: authz.NewRoundTripper(cfg.Token, config.Version()),
},
llmSvc: service.New(cfg),
cl: cl,
llmSvc: service.New(cfg, cl),
apiHost: config.APIHost(),
}

Expand Down Expand Up @@ -298,41 300,12 @@ func (c *client) Runbooks(ctx context.Context, opts RunbooksOpt) ([]RunbookInfo,
return runbooks, nil
}

type QuestionInfo struct {
Question string `json:"question"`
Tags map[string]string `json:"tags,omitempty"`
FileData []byte `json:"file_data,omitempty"`
FileName string `json:"file_name,omitempty"`
PreviousQuestions []string `json:"previous_questions,omitempty"`
PreviousCommands []string `json:"previous_commands,omitempty"`
}

func (c *client) Ask(ctx context.Context, question QuestionInfo) (*Runbook, error) {
return ask(ctx, c.cl, c.apiURL("/api/v1/public/ask"), question)
}

func ask(ctx context.Context, cl *http.Client, apiURL string, question QuestionInfo) (*Runbook, error) {
bs, err := json.Marshal(question)
func (c *client) Ask(ctx context.Context, question model.QuestionInfo) (*Runbook, error) {
answer, err := c.llmSvc.Ask(ctx, question)
if err != nil {
return nil, err
}

req, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, bytes.NewReader(bs))
if err != nil {
return nil, err
}

resp, err := cl.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()

var runbook Runbook
if err := json.NewDecoder(resp.Body).Decode(&runbook); err != nil {
return nil, err
}
return &runbook, nil
return toClientRunbook(answer), nil
}

type CodeInfo struct {
Expand Down
28 changes: 21 additions & 7 deletions client/guest.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 8,28 @@ import (
"strings"

"github.com/getsavvyinc/savvy-cli/config"
"github.com/getsavvyinc/savvy-cli/llm/service"
"github.com/getsavvyinc/savvy-cli/login"
"github.com/getsavvyinc/savvy-cli/model"
)

var _ Client = (*guest)(nil)

func NewGuest() Client {
func NewGuest() (Client, error) {
cfg, err := config.LoadFromFile()
if err != nil {
return nil, fmt.Errorf("%w: %w", ErrInvalidClient, err)
}

cl := &http.Client{
Transport: &GuestRoundTripper{savvyVersion: config.Version()},
}

return &guest{
cl: &http.Client{
Transport: &GuestRoundTripper{savvyVersion: config.Version()},
},
cl: cl,
llmSvc: service.New(cfg, cl),
apiHost: config.APIHost(),
}
}, nil
}

type GuestRoundTripper struct {
Expand All @@ -43,6 52,7 @@ func (g *GuestRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)

type guest struct {
cl *http.Client
llmSvc service.Service
apiHost string
}

Expand Down Expand Up @@ -125,8 135,12 @@ func (g *guest) Runbooks(ctx context.Context, opt RunbooksOpt) ([]RunbookInfo, e
return cl.Runbooks(ctx, opt)
}

func (g *guest) Ask(ctx context.Context, question QuestionInfo) (*Runbook, error) {
return ask(ctx, g.cl, g.apiURL("/api/v1/public/ask"), question)
func (g *guest) Ask(ctx context.Context, question model.QuestionInfo) (*Runbook, error) {
answer, err := g.llmSvc.Ask(ctx, question)
if err != nil {
return nil, err
}
return toClientRunbook(answer), nil
}

func (g *guest) Explain(ctx context.Context, code CodeInfo) (<-chan string, error) {
Expand Down
10 changes: 8 additions & 2 deletions cmd/ask.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 19,7 @@ import (
"github.com/getsavvyinc/savvy-cli/cmd/component"
"github.com/getsavvyinc/savvy-cli/cmd/component/list"
"github.com/getsavvyinc/savvy-cli/display"
"github.com/getsavvyinc/savvy-cli/model"
"github.com/getsavvyinc/savvy-cli/server"
"github.com/getsavvyinc/savvy-cli/slice"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -51,7 52,12 @@ var askCmd = &cobra.Command{
cl, err = client.New()
if err != nil {
logger.Debug("error creating client", "error", err, "message", "falling back to guest client")
cl = client.NewGuest()
cl, err = client.NewGuest()
if err != nil {
err = fmt.Errorf("error creating guest client: %w", err)
display.ErrorWithSupportCTA(err)
os.Exit(1)
}
}

// get info about the os from os pkg: mac/darwin, linux, windows
Expand Down Expand Up @@ -195,7 201,7 @@ func runAsk(ctx context.Context, cl client.Client, question string, askParams *A
return nil
}

qi := client.QuestionInfo{
qi := model.QuestionInfo{
Question: question,
Tags: map[string]string{
"os": askParams.goos,
Expand Down
8 changes: 7 additions & 1 deletion cmd/explain.go
Original file line number Diff line number Diff line change
@@ -1,6 1,7 @@
package cmd

import (
"fmt"
"os"
"runtime"
"strings"
Expand Down Expand Up @@ -37,7 38,12 @@ var explainCmd = &cobra.Command{
cl, err = client.New()
if err != nil {
logger.Debug("error creating client", "error", err, "message", "falling back to guest client")
cl = client.NewGuest()
cl, err = client.NewGuest()
if err != nil {
err = fmt.Errorf("error creating guest client: %w", err)
display.ErrorWithSupportCTA(err)
os.Exit(1)
}
}

var code string
Expand Down
7 changes: 6 additions & 1 deletion cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 68,12 @@ func savvyRun(cmd *cobra.Command, args []string) {
cl, err = client.New()
if err != nil {
logger.Debug("error creating client", "error", err, "message", "falling back to guest client")
cl = client.NewGuest()
cl, err = client.NewGuest()
if err != nil {
err = fmt.Errorf("error creating guest client: %w", err)
display.ErrorWithSupportCTA(err)
os.Exit(1)
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions llm/service/custom_llm.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,3 251,7 @@ func (c *customSvc) generateRunbookTitleAndDescriptions(ctx context.Context, com
}
return &runbook, nil
}

func (c *customSvc) Ask(ctx context.Context, question model.QuestionInfo) (*llm.Runbook, error) {
return nil, nil
}
41 changes: 31 additions & 10 deletions llm/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 7,19 @@ import (
"net/http"
"strings"

"github.com/getsavvyinc/savvy-cli/authz"
"github.com/getsavvyinc/savvy-cli/config"
"github.com/getsavvyinc/savvy-cli/llm"
"github.com/getsavvyinc/savvy-cli/model"
)

type Service interface {
GenerateRunbook(ctx context.Context, commands []model.RecordedCommand) (*llm.Runbook, error)
Ask(ctx context.Context, question model.QuestionInfo) (*llm.Runbook, error)
}

func New(cfg *config.Config) Service {
func New(cfg *config.Config, savvyClient *http.Client) Service {
if cfg.LLMBaseURL == "" {
return newDefaultService(cfg)
return newDefaultService(cfg, savvyClient)
}
return newCustomService(cfg)
}
Expand All @@ -29,11 29,9 @@ type defaultLLM struct {
apiHost string
}

func newDefaultService(cfg *config.Config) Service {
func newDefaultService(cfg *config.Config, savvyClient *http.Client) Service {
return &defaultLLM{
cl: &http.Client{
Transport: authz.NewRoundTripper(cfg.Token, config.Version()),
},
cl: savvyClient,
apiHost: config.APIHost(),
}
}
Expand Down Expand Up @@ -69,8 67,31 @@ func genAPIURL(host, path string) string {
return host path
}

type service struct{}
func (d *defaultLLM) Ask(ctx context.Context, question model.QuestionInfo) (*llm.Runbook, error) {
apiURL := genAPIURL(d.apiHost, "/api/v1/public/ask")
return ask(ctx, d.cl, apiURL, question)
}

func ask(ctx context.Context, cl *http.Client, apiURL string, question model.QuestionInfo) (*llm.Runbook, error) {
bs, err := json.Marshal(question)
if err != nil {
return nil, err
}

req, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, bytes.NewReader(bs))
if err != nil {
return nil, err
}

func (s *service) GenerateRunbook(ctx context.Context, commands []model.RecordedCommand) (*llm.Runbook, error) {
return nil, nil
resp, err := cl.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()

var runbook llm.Runbook
if err := json.NewDecoder(resp.Body).Decode(&runbook); err != nil {
return nil, err
}
return &runbook, nil
}
10 changes: 10 additions & 0 deletions model/question_info.go
Original file line number Diff line number Diff line change
@@ -0,0 1,10 @@
package model

type QuestionInfo struct {
Question string `json:"question"`
Tags map[string]string `json:"tags,omitempty"`
FileData []byte `json:"file_data,omitempty"`
FileName string `json:"file_name,omitempty"`
PreviousQuestions []string `json:"previous_questions,omitempty"`
PreviousCommands []string `json:"previous_commands,omitempty"`
}

0 comments on commit 9b4f0ed

Please sign in to comment.