]> git.giacomolli.uk - onechat/blobdiff - agent.go
Return known failures without redundant checks
[onechat] / agent.go
index 8226a43fb0e4deebc20cef17c9ff2997ef7f281e..da4b0b7be7eb5c65bc474095cd5eb00f189c4840 100644 (file)
--- a/agent.go
+++ b/agent.go
@@ -61,7 +61,12 @@ func openAgent(dir string) (*Agent, error) {
        path := filepath.Join(dir, "config.json")
        data, err := os.ReadFile(path)
        if errors.Is(err, os.ErrNotExist) {
        path := filepath.Join(dir, "config.json")
        data, err := os.ReadFile(path)
        if errors.Is(err, os.ErrNotExist) {
-               return createAgent(dir)
+               agent, err := createAgent(dir)
+               if err != nil {
+                       return agent, fmt.Errorf("create agent: %w", err)
+               }
+
+               return agent, nil
        }
 
        if err != nil {
        }
 
        if err != nil {
@@ -70,7 +75,7 @@ func openAgent(dir string) (*Agent, error) {
 
        config, err := defaultAgentConfig()
        if err != nil {
 
        config, err := defaultAgentConfig()
        if err != nil {
-               return nil, err
+               return nil, fmt.Errorf("prepare agent defaults: %w", err)
        }
 
        decoder := json.NewDecoder(bytes.NewReader(data))
        }
 
        decoder := json.NewDecoder(bytes.NewReader(data))
@@ -115,11 +120,6 @@ func (a *Agent) Voices() *VoiceStore {
        return &VoiceStore{dir: filepath.Join(filepath.Dir(root), voiceDirName)}
 }
 
        return &VoiceStore{dir: filepath.Join(filepath.Dir(root), voiceDirName)}
 }
 
-// CacheKey returns the prompt cache key of every model call of the agent.
-func (a *Agent) CacheKey() string {
-       return "onechat-" + a.config.ID
-}
-
 // Prompt returns the system prompt of the agent with the user's instructions
 // from instructions.md. It reads the file on each call, so a change applies at
 // the next call. A missing file means no instructions.
 // Prompt returns the system prompt of the agent with the user's instructions
 // from instructions.md. It reads the file on each call, so a change applies at
 // the next call. A missing file means no instructions.
@@ -141,6 +141,17 @@ func (a *Agent) Prompt() (string, error) {
        return prompt(a.config.Name, path, instructions), nil
 }
 
        return prompt(a.config.Name, path, instructions), nil
 }
 
+// instructions reads the user's instructions from instructions.md. A missing
+// file means no instructions.
+func (a *Agent) instructions() (string, error) {
+       data, err := os.ReadFile(filepath.Join(a.dir, agentInstructionsFileName))
+       if err != nil && !errors.Is(err, os.ErrNotExist) {
+               return "", fmt.Errorf("read instructions: %w", err)
+       }
+
+       return string(data), nil
+}
+
 // Tools returns the tools shared by the turns and compactions of this agent. A
 // child has no image output, WhatsApp, child creation, or route tools.
 func (a *Agent) Tools() []ModelTool {
 // Tools returns the tools shared by the turns and compactions of this agent. A
 // child has no image output, WhatsApp, child creation, or route tools.
 func (a *Agent) Tools() []ModelTool {
@@ -213,6 +224,11 @@ func (a *Agent) Children() ([]*Agent, error) {
        return children, nil
 }
 
        return children, nil
 }
 
+// CacheKey returns the prompt cache key of every model call of the agent.
+func (a *Agent) CacheKey() string {
+       return "onechat-" + a.config.ID
+}
+
 // CreateChild writes a new direct child with its own ID and the model, effort,
 // and working directory of the root. Child and subagent names share a
 // namespace. The config is complete before an atomic rename publishes the
 // CreateChild writes a new direct child with its own ID and the model, effort,
 // and working directory of the root. Child and subagent names share a
 // namespace. The config is complete before an atomic rename publishes the
@@ -299,17 +315,6 @@ func (a *Agent) SubagentPrompt() (string, error) {
        return promptSubagent(a.config.Name, instructions), nil
 }
 
        return promptSubagent(a.config.Name, instructions), nil
 }
 
-// instructions reads the user's instructions from instructions.md. A missing
-// file means no instructions.
-func (a *Agent) instructions() (string, error) {
-       data, err := os.ReadFile(filepath.Join(a.dir, agentInstructionsFileName))
-       if err != nil && !errors.Is(err, os.ErrNotExist) {
-               return "", fmt.Errorf("read instructions: %w", err)
-       }
-
-       return string(data), nil
-}
-
 // validate makes sure that each setting has a valid value.
 func (c AgentConfig) validate() error {
        if c.Name == "" || strings.ContainsAny(c.Name, "\r\n") {
 // validate makes sure that each setting has a valid value.
 func (c AgentConfig) validate() error {
        if c.Name == "" || strings.ContainsAny(c.Name, "\r\n") {
@@ -340,6 +345,38 @@ func (c AgentConfig) validate() error {
        return nil
 }
 
        return nil
 }
 
+// createAgent writes the config.json of a new agent in dir with the defaults
+// and a new ID.
+func createAgent(dir string) (*Agent, error) {
+       config, err := defaultAgentConfig()
+       if err != nil {
+               return nil, fmt.Errorf("prepare agent defaults: %w", err)
+       }
+
+       agent, err := writeAgent(dir, config)
+       if err != nil {
+               return agent, fmt.Errorf("write new agent: %w", err)
+       }
+
+       return agent, nil
+}
+
+// defaultAgentConfig returns the settings of a new agent, without an ID. The
+// commands run in the home directory of the user.
+func defaultAgentConfig() (AgentConfig, error) {
+       home, err := os.UserHomeDir()
+       if err != nil {
+               return AgentConfig{}, fmt.Errorf("find working directory: %w", err)
+       }
+
+       return AgentConfig{
+               Name:    "OneChat",
+               Model:   "gpt-6.1-sol",
+               Effort:  "medium",
+               Workdir: home,
+       }, nil
+}
+
 // agentChildNameValid accepts one word of 2 to 32 ASCII letters with an initial
 // capital letter. Child names describe their area.
 func agentChildNameValid(name string) bool {
 // agentChildNameValid accepts one word of 2 to 32 ASCII letters with an initial
 // capital letter. Child names describe their area.
 func agentChildNameValid(name string) bool {
@@ -357,17 +394,6 @@ func agentChildNameValid(name string) bool {
        return true
 }
 
        return true
 }
 
-// createAgent writes the config.json of a new agent in dir with the defaults
-// and a new ID.
-func createAgent(dir string) (*Agent, error) {
-       config, err := defaultAgentConfig()
-       if err != nil {
-               return nil, err
-       }
-
-       return writeAgent(dir, config)
-}
-
 // writeAgent gives config a new ID and writes it in dir.
 func writeAgent(dir string, config AgentConfig) (*Agent, error) {
        id := make([]byte, agentIDBytes)
 // writeAgent gives config a new ID and writes it in dir.
 func writeAgent(dir string, config AgentConfig) (*Agent, error) {
        id := make([]byte, agentIDBytes)
@@ -392,19 +418,3 @@ func writeAgent(dir string, config AgentConfig) (*Agent, error) {
                config: config,
        }, nil
 }
                config: config,
        }, nil
 }
-
-// defaultAgentConfig returns the settings of a new agent, without an ID. The
-// commands run in the home directory of the user.
-func defaultAgentConfig() (AgentConfig, error) {
-       home, err := os.UserHomeDir()
-       if err != nil {
-               return AgentConfig{}, fmt.Errorf("find working directory: %w", err)
-       }
-
-       return AgentConfig{
-               Name:    "OneChat",
-               Model:   "gpt-6.1-sol",
-               Effort:  "medium",
-               Workdir: home,
-       }, nil
-}