| package dodo_tools |
| |
| import ( |
| "bytes" |
| "context" |
| "encoding/json" |
| "fmt" |
| "io" |
| "net/http" |
| |
| "sketch.dev/llm" |
| ) |
| |
| type GetProjectConfigTool struct { |
| apiBaseAddress string |
| projectId string |
| } |
| |
| func NewGetProjectConfigTool(apiBaseAddress string, projectId string) *llm.Tool { |
| tool := &GetProjectConfigTool{ |
| apiBaseAddress: apiBaseAddress, |
| projectId: projectId, |
| } |
| return &llm.Tool{ |
| Name: "dodo_get_project_config", |
| Description: "A tool for getting current state of the infrastructure configuration of a dodo project", |
| InputSchema: llm.EmptySchema(), |
| Run: tool.Run, |
| EndsTurn: true, |
| } |
| } |
| |
| type GetProjectConfigInput struct { |
| } |
| |
| type GetProjectConfigOutput struct { |
| Config string `json:"config"` |
| } |
| |
| func (d *GetProjectConfigTool) Run(ctx context.Context, m json.RawMessage) ([]llm.Content, error) { |
| resp, err := http.Get(fmt.Sprintf("%s/api/project/%s/config", d.apiBaseAddress, d.projectId)) |
| if err != nil { |
| return nil, err |
| } |
| defer resp.Body.Close() |
| body, err := io.ReadAll(resp.Body) |
| if err != nil { |
| return nil, err |
| } |
| if resp.StatusCode != http.StatusOK { |
| return nil, fmt.Errorf("failed to get project config: %s", string(body)) |
| } |
| output := GetProjectConfigOutput{ |
| Config: string(body), |
| } |
| jsonOutput, err := json.Marshal(output) |
| if err != nil { |
| return nil, err |
| } |
| return llm.TextContent(string(jsonOutput)), nil |
| } |
| |
| type ValidateConfigTool struct { |
| apiBaseAddress string |
| } |
| |
| const ( |
| validateConfigSchemaInputSchema = ` |
| { |
| "type": "object", |
| "properties": { |
| "config": { |
| "type": "string", |
| "description": "The dodo-app configuration to validate" |
| } |
| }, |
| "required": ["config"] |
| } |
| ` |
| ) |
| |
| func NewValidateConfigTool(apiBaseAddress string) *llm.Tool { |
| tool := &ValidateConfigTool{ |
| apiBaseAddress: apiBaseAddress, |
| } |
| return &llm.Tool{ |
| Name: "dodo_validate_config", |
| Description: "A tool for validating the dodo-app configuration", |
| InputSchema: llm.MustSchema(validateConfigSchemaInputSchema), |
| Run: tool.Run, |
| EndsTurn: true, |
| } |
| } |
| |
| type ValidateConfigInput struct { |
| Config string `json:"config"` |
| } |
| |
| type ValidateConfigOutput struct { |
| Success bool `json:"success"` |
| Errors any `json:"errors,omitempty"` |
| } |
| |
| func (d *ValidateConfigTool) Run(ctx context.Context, m json.RawMessage) ([]llm.Content, error) { |
| var input ValidateConfigInput |
| if err := json.Unmarshal(m, &input); err != nil { |
| return nil, err |
| } |
| resp, err := http.Post(fmt.Sprintf("%s/api/validate-config", d.apiBaseAddress), "application/json", bytes.NewBuffer([]byte(input.Config))) |
| if err != nil { |
| return nil, err |
| } |
| defer resp.Body.Close() |
| body, err := io.ReadAll(resp.Body) |
| if err != nil { |
| return nil, err |
| } |
| if resp.StatusCode != http.StatusOK { |
| return nil, fmt.Errorf("failed to validate config: %s", string(body)) |
| } |
| var output ValidateConfigOutput |
| if err := json.Unmarshal(body, &output); err != nil { |
| return nil, err |
| } |
| jsonOutput, err := json.Marshal(output) |
| if err != nil { |
| return nil, err |
| } |
| return llm.TextContent(string(jsonOutput)), nil |
| } |
| |
| type DeployProjectTool struct { |
| apiBaseAddress string |
| projectId string |
| } |
| |
| func NewDeployProjectTool(apiBaseAddress string, projectId string) *llm.Tool { |
| tool := &DeployProjectTool{ |
| apiBaseAddress: apiBaseAddress, |
| projectId: projectId, |
| } |
| |
| return &llm.Tool{ |
| Name: "dodo_deploy_project", |
| Description: "A tool for deploying the dodo-app configuration", |
| InputSchema: llm.EmptySchema(), |
| Run: tool.Run, |
| EndsTurn: true, |
| } |
| } |
| |
| type deployProjectReq struct { |
| Type string `json:"type"` |
| } |
| |
| func (d *DeployProjectTool) Run(ctx context.Context, m json.RawMessage) ([]llm.Content, error) { |
| req := deployProjectReq{ |
| Type: "draft", |
| } |
| jsonReq, err := json.Marshal(req) |
| if err != nil { |
| return nil, err |
| } |
| resp, err := http.Post(fmt.Sprintf("%s/api/project/%s/deploy", d.apiBaseAddress, d.projectId), "application/json", bytes.NewBuffer(jsonReq)) |
| if err != nil { |
| return nil, err |
| } |
| defer resp.Body.Close() |
| body, err := io.ReadAll(resp.Body) |
| if err != nil { |
| return nil, err |
| } |
| if resp.StatusCode != http.StatusOK { |
| return nil, fmt.Errorf("failed to deploy project: %s", string(body)) |
| } |
| return llm.TextContent(string("Project deployed successfully")), nil |
| } |
| |
| // Save |
| |
| type SaveProjectTool struct { |
| apiBaseAddress string |
| projectId string |
| } |
| |
| const ( |
| saveProjectSchemaInputSchema = ` |
| { |
| "type": "object", |
| "properties": { |
| } |
| } |
| ` |
| ) |
| |
| func NewSaveProjectTool(apiBaseAddress string, projectId string) *llm.Tool { |
| tool := &SaveProjectTool{ |
| apiBaseAddress: apiBaseAddress, |
| projectId: projectId, |
| } |
| |
| return &llm.Tool{ |
| Name: "dodo_save_config", |
| Description: "A tool for saving the dodo-app configuration", |
| InputSchema: llm.MustSchema(saveProjectSchemaInputSchema), |
| Run: tool.Run, |
| EndsTurn: true, |
| } |
| } |
| |
| type SaveProjectInput struct { |
| Config string `json:"config"` |
| } |
| |
| type saveProjectReq struct { |
| Type string `json:"type"` |
| Config map[string]any `json:"config"` |
| } |
| |
| func (d *SaveProjectTool) Run(ctx context.Context, m json.RawMessage) ([]llm.Content, error) { |
| var input SaveProjectInput |
| if err := json.Unmarshal(m, &input); err != nil { |
| return nil, err |
| } |
| req := saveProjectReq{ |
| Type: "config", |
| } |
| if err := json.Unmarshal([]byte(input.Config), &req.Config); err != nil { |
| return nil, err |
| } |
| jsonReq, err := json.Marshal(req) |
| if err != nil { |
| return nil, err |
| } |
| resp, err := http.Post(fmt.Sprintf("%s/api/project/%s/saved", d.apiBaseAddress, d.projectId), "application/json", bytes.NewBuffer(jsonReq)) |
| if err != nil { |
| return nil, err |
| } |
| defer resp.Body.Close() |
| body, err := io.ReadAll(resp.Body) |
| if err != nil { |
| return nil, err |
| } |
| if resp.StatusCode != http.StatusOK { |
| return nil, fmt.Errorf("failed to save project: %s", string(body)) |
| } |
| return llm.TextContent(string("Project saved successfully")), nil |
| } |
| |
| func NewDodoTools(apiBaseAddress string, projectId string) []*llm.Tool { |
| return []*llm.Tool{ |
| NewGetProjectConfigTool(apiBaseAddress, projectId), |
| NewValidateConfigTool(apiBaseAddress), |
| NewSaveProjectTool(apiBaseAddress, projectId), |
| NewDeployProjectTool(apiBaseAddress, projectId), |
| } |
| } |