dodo: save config tool
Change-Id: Ia8c8639cda093a3776ff018c2b77e63449cbc78c
diff --git a/dodo_tools/dodo.go b/dodo_tools/dodo.go
index 41127df..fdb7c8f 100644
--- a/dodo_tools/dodo.go
+++ b/dodo_tools/dodo.go
@@ -1,8 +1,8 @@
package dodo_tools
import (
- "context"
"bytes"
+ "context"
"encoding/json"
"fmt"
"io"
@@ -16,7 +16,6 @@
projectId string
}
-
func NewGetProjectConfigTool(apiBaseAddress string, projectId string) *llm.Tool {
tool := &GetProjectConfigTool{
apiBaseAddress: apiBaseAddress,
@@ -99,11 +98,10 @@
type ValidateConfigOutput struct {
Success bool `json:"success"`
- Errors any `json:"errors,omitempty"`
+ 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
@@ -171,7 +169,7 @@
type DeployProjectOutput struct {
Success bool `json:"success"`
- Errors any `json:"errors,omitempty"`
+ Errors any `json:"errors,omitempty"`
}
type deployProjectReq struct {
@@ -187,7 +185,6 @@
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
@@ -207,10 +204,91 @@
return llm.TextContent(string("Project deployed successfully")), nil
}
+// Save
+
+type SaveProjectTool struct {
+ apiBaseAddress string
+ projectId string
+}
+
+const (
+ saveProjectSchemaInputSchema = `
+{
+ "type": "object",
+ "properties": {
+ "config": {
+ "type": "string",
+ "description": "Serialized dodo-app configuration to save"
+ }
+ }
+}
+`
+)
+
+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 SaveProjectOutput struct {
+ Success bool `json:"success"`
+ Errors any `json:"errors,omitempty"`
+}
+
+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),
NewDeployProjectTool(apiBaseAddress, projectId),
+ NewSaveProjectTool(apiBaseAddress, projectId),
}
-}
\ No newline at end of file
+}
diff --git a/loop/agent_system_prompt.txt b/loop/agent_system_prompt.txt
index 840fcde..473431d 100644
--- a/loop/agent_system_prompt.txt
+++ b/loop/agent_system_prompt.txt
@@ -83,6 +83,7 @@
Use following tools to interact with dodo:
1. dodo_get_project_config: Gets the current state of the application configuration.
2. dodo_validate_config: Takes dodo-app configuration and validates it. Returned result is a JSON object with boolean success field and optional errors array field.
+3. dodo_save_config: Takes dodo-app configuration and saves it as a current working draft. Always use this tool before actually deploying the new configuration, so that user can verify the changes first.
3. dodo_deploy_project: Takes new configuration and deployes it.
You might want to use dodo tools in following scenarios:
@@ -90,8 +91,11 @@
2. User explicitely asks to add new infrastructure pieces or modify existing ones.
3. User asks you to implement new feature which requires new infrastucture piece.
-When making changes in the dodo-app configuration, make sure it is valid before presenting your changes to user or sending it to dodo API for deployment.
-When validating inspect success and error fields and fix all errors. Use the todo_read and todo_write tools to organize and track your dodo-app configuration changes.
+When making changes in the dodo-app configuration, make sure that:
+1. It is valid before presenting your changes to user or sending it to dodo API for deployment.
+2. Carefully inspect validation result and fix all the errors.
+3. Make sure to save the new configuration before deploying it.
+4. Use the todo_read and todo_write tools to organize and track your dodo-app configuration changes.
Always pretty print dodo-app config before presenting it to the user.