feat: V1.0 (#734)

Co-authored-by: Michael Neale <michael.neale@gmail.com>
Co-authored-by: Wendy Tang <wendytang@squareup.com>
Co-authored-by: Jarrod Sibbison <72240382+jsibbison-square@users.noreply.github.com>
Co-authored-by: Alex Hancock <alex.hancock@example.com>
Co-authored-by: Alex Hancock <alexhancock@block.xyz>
Co-authored-by: Lifei Zhou <lifei@squareup.com>
Co-authored-by: Wes <141185334+wesrblock@users.noreply.github.com>
Co-authored-by: Max Novich <maksymstepanenko1990@gmail.com>
Co-authored-by: Zaki Ali <zaki@squareup.com>
Co-authored-by: Salman Mohammed <smohammed@squareup.com>
Co-authored-by: Kalvin C <kalvinnchau@users.noreply.github.com>
Co-authored-by: Alec Thomas <alec@swapoff.org>
Co-authored-by: lily-de <119957291+lily-de@users.noreply.github.com>
Co-authored-by: kalvinnchau <kalvin@block.xyz>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Rizel Scarlett <rizel@squareup.com>
Co-authored-by: bwrage <bwrage@squareup.com>
Co-authored-by: Kalvin Chau <kalvin@squareup.com>
Co-authored-by: Alice Hau <110418948+ahau-square@users.noreply.github.com>
Co-authored-by: Alistair Gray <ajgray@stripe.com>
Co-authored-by: Nahiyan Khan <nahiyan.khan@gmail.com>
Co-authored-by: Alex Hancock <alexhancock@squareup.com>
Co-authored-by: Nahiyan Khan <nahiyan@squareup.com>
Co-authored-by: marcelle <1852848+laanak08@users.noreply.github.com>
Co-authored-by: Yingjie He <yingjiehe@block.xyz>
Co-authored-by: Yingjie He <yingjiehe@squareup.com>
Co-authored-by: Lily Delalande <ldelalande@block.xyz>
Co-authored-by: Adewale Abati <acekyd01@gmail.com>
Co-authored-by: Ebony Louis <ebony774@gmail.com>
Co-authored-by: Angie Jones <jones.angie@gmail.com>
Co-authored-by: Ebony Louis <55366651+EbonyLouis@users.noreply.github.com>
This commit is contained in:
Bradley Axen
2025-01-24 13:04:43 -08:00
committed by GitHub
parent eccb1b2261
commit 1c9a7c0b05
688 changed files with 71147 additions and 19132 deletions

View File

@@ -0,0 +1,8 @@
{
"label": "Architecture Overview",
"position": 5,
"link": {
"type": "generated-index",
"description": "Extend Goose functionalities with extensions and custom configurations"
}
}

View File

@@ -0,0 +1,35 @@
---
title: Error Handling
---
# Error Handling in Goose
Error handling is a key performance-driving part of Goose. There are many ways that the non-determinism
in the LLM can introduce an error that it can in turn recover from. In a typical Goose session, it's expected for there
to be several agent errors that the model can see directly and correct, perhaps entirely behind the scenes.
## Traditional Errors
While the agent is operating, there can be intermittent issues in the network, availability of the
foundational model, etc. These are raised as errors in the agent API to the caller, who can decide
how to handle that. We generally handle these with [anyhow::Error][anyhow-error].
## Agent Errors
There are several types of errors where everything is working correctly, but the model generations
themselves are somehow causing errors. Things like generating an unknown tool name, incorrect parameters,
or a well formed tool call that results in an error in the tool itself. All of these can be surfaced to
the LLM to have it attempt to recover.
The error messages are in some ways prompting - they give instructions to the LLM on how it might go
about recovering. We handle these with [thiserror::Error][this-error] and carefully maintain a collection.
To cover all these cases, both `ToolUse` and `ToolResult` are typically passed through the API as part of a
`Result<T, AgentError>`. An error in a `ToolUse` will immediately become an error in a `ToolResult` and
passed back to the LLM. A valid `ToolUse` might still end up in an error `ToolResult`, which is also passed
back to the LLM.
The providers then handle translating the agent errors into the various API specs as valid messages.
[anyhow-error]: https://docs.rs/anyhow/latest/anyhow/
[this-error]: https://docs.rs/thiserror/latest/thiserror/

View File

@@ -0,0 +1,123 @@
---
sidebar_position: 1
---
# Extensions Design
This document describes the design and implementation of the Extensions framework in Goose, which enables AI agents to interact with different extensions through a unified tool-based interface.
## Core Concepts
### Extension
An Extension represents any component that can be operated by an AI agent. Extensions expose their capabilities through Tools and maintain their own state. The core interface is defined by the `Extension` trait:
```rust
#[async_trait]
pub trait Extension: Send + Sync {
fn name(&self) -> &str;
fn description(&self) -> &str;
fn instructions(&self) -> &str;
fn tools(&self) -> &[Tool];
async fn status(&self) -> AnyhowResult<HashMap<String, Value>>;
async fn call_tool(&self, tool_name: &str, parameters: HashMap<String, Value>) -> ToolResult<Value>;
}
```
### Tools
Tools are the primary way Extensions expose functionality to agents. Each tool has:
- A name
- A description
- A set of parameters
- An implementation that executes the tool's functionality
A tool must take a Value and return an `AgentResult<Value>` (it must also be async). This
is what makes it compatible with the tool calling framework from the agent.
```rust
async fn echo(&self, params: Value) -> AgentResult<Value>
```
## Architecture
### Component Overview
1. **Extension Trait**: The core interface that all extensions must implement
2. **Error Handling**: Specialized error types for tool execution
3. **Proc Macros**: Simplify tool definition and registration [*not yet implemented*]
### Error Handling
The system uses two main error types:
- `ToolError`: Specific errors related to tool execution
- `anyhow::Error`: General purpose errors for extension status and other operations
This split allows precise error handling for tool execution while maintaining flexibility for general extension operations.
## Best Practices
### Tool Design
1. **Clear Names**: Use clear, action-oriented names for tools (e.g., "create_user" not "user")
2. **Descriptive Parameters**: Each parameter should have a clear description
3. **Error Handling**: Return specific errors when possible, the errors become "prompts"
4. **State Management**: Be explicit about state modifications
### Extension Implementation
1. **State Encapsulation**: Keep extension state private and controlled
2. **Error Propagation**: Use `?` operator with `ToolError` for tool execution
3. **Status Clarity**: Provide clear, structured status information
4. **Documentation**: Document all tools and their effects
### Example Implementation
Here's a complete example of a simple extension:
```rust
use goose_macros::tool;
struct FileSystem {
registry: ToolRegistry,
root_path: PathBuf,
}
impl FileSystem {
#[tool(
name = "read_file",
description = "Read contents of a file"
)]
async fn read_file(&self, path: String) -> ToolResult<Value> {
let full_path = self.root_path.join(path);
let content = tokio::fs::read_to_string(full_path)
.await
.map_err(|e| ToolError::ExecutionError(e.to_string()))?;
Ok(json!({ "content": content }))
}
}
#[async_trait]
impl Extension for FileSystem {
// ... implement trait methods ...
}
```
## Testing
Extensions should be tested at multiple levels:
1. Unit tests for individual tools
2. Integration tests for extension behavior
3. Property tests for tool invariants
Example test:
```rust
#[tokio::test]
async fn test_echo_tool() {
let extension = TestExtension::new();
let result = extension.call_tool(
"echo",
hashmap!{ "message" => json!("hello") }
).await;
assert_eq!(result.unwrap(), json!({ "response": "hello" }));
}
```