dodo: implement validate and deploy tools
Change-Id: I026cff45159dd1e6fa6a9bcef7c843564bcf9c8b
diff --git a/dodo_tools/dodo.go b/dodo_tools/dodo.go
index a5228de..41127df 100644
--- a/dodo_tools/dodo.go
+++ b/dodo_tools/dodo.go
@@ -2,6 +2,7 @@
import (
"context"
+ "bytes"
"encoding/json"
"fmt"
"io"
@@ -11,41 +12,26 @@
)
type GetProjectConfigTool struct {
+ apiBaseAddress string
+ projectId string
}
-const (
- getProjectSchemaInputSchema = `
-{
- "type": "object",
- "properties": {
- "apiBaseAddress": {
- "type": "string",
- "description": "The base address of the dodo API"
- },
- "projectId": {
- "type": "string",
- "description": "The ID of the dodo project to get infrastructure configuration."
- }
- },
- "required": ["apiBaseAddress", "projectId"]
-}
-`
-)
-func NewGetProjectConfigTool() *llm.Tool {
- tool := &GetProjectConfigTool{}
+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.MustSchema(getProjectSchemaInputSchema),
+ InputSchema: llm.EmptySchema(),
Run: tool.Run,
EndsTurn: true,
}
}
type GetProjectConfigInput struct {
- ApiBaseAddress string `json:"apiBaseAddress"`
- ProjectId string `json:"projectId"`
}
type GetProjectConfigOutput struct {
@@ -53,11 +39,7 @@
}
func (d *GetProjectConfigTool) Run(ctx context.Context, m json.RawMessage) ([]llm.Content, error) {
- var input GetProjectConfigInput
- if err := json.Unmarshal(m, &input); err != nil {
- return nil, err
- }
- resp, err := http.Get(fmt.Sprintf("%s/api/project/%s/config", input.ApiBaseAddress, input.ProjectId))
+ resp, err := http.Get(fmt.Sprintf("%s/api/project/%s/config", d.apiBaseAddress, d.projectId))
if err != nil {
return nil, err
}
@@ -69,10 +51,6 @@
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get project config: %s", string(body))
}
- var config map[string]interface{}
- if err := json.Unmarshal(body, &config); err != nil {
- return nil, fmt.Errorf("got invalid project config: %s %s", err, string(body))
- }
output := GetProjectConfigOutput{
Config: string(body),
}
@@ -82,3 +60,157 @@
}
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) {
+ fmt.Printf("%s\n", string(m))
+ 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
+}
+
+const (
+ deployProjectSchemaInputSchema = `
+{
+ "type": "object",
+ "properties": {
+ "config": {
+ "type": "string",
+ "description": "Serialized dodo-app configuration to deploy"
+ }
+ }
+}
+`
+)
+
+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.MustSchema(deployProjectSchemaInputSchema),
+ Run: tool.Run,
+ EndsTurn: true,
+ }
+}
+
+type DeployProjectInput struct {
+ Config string `json:"config"`
+}
+
+type DeployProjectOutput struct {
+ Success bool `json:"success"`
+ Errors any `json:"errors,omitempty"`
+}
+
+type deployProjectReq struct {
+ Config map[string]any `json:"config"`
+}
+
+func (d *DeployProjectTool) Run(ctx context.Context, m json.RawMessage) ([]llm.Content, error) {
+ var input DeployProjectInput
+ if err := json.Unmarshal(m, &input); err != nil {
+ return nil, err
+ }
+ req := deployProjectReq{}
+ if err := json.Unmarshal([]byte(input.Config), &req.Config); err != nil {
+ return nil, err
+ }
+ fmt.Printf("### %+v\n", req)
+ 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
+}
+
+func NewDodoTools(apiBaseAddress string, projectId string) []*llm.Tool {
+ return []*llm.Tool{
+ NewGetProjectConfigTool(apiBaseAddress, projectId),
+ NewValidateConfigTool(apiBaseAddress),
+ NewDeployProjectTool(apiBaseAddress, projectId),
+ }
+}
\ No newline at end of file