Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@ jobs:
source ./bin/activate-hermit
export CARGO_INCREMENTAL=0
cargo clippy --jobs 2 -- -D warnings

- name: Install Node.js Dependencies for OpenAPI Check
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this move to the lint check (we can rename that) for front end that runs in parallel? (not sure if it has to wait for cargo to do its thing however)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally thought about keeping it in a different job. We do need cargo to compile to check. The check is adding about ~2 seconds (on my machine)
We can move it out if it becomes a problem.

run: source ../../bin/activate-hermit && npm ci
working-directory: ui/desktop

- name: Check OpenAPI Schema is Up-to-Date
run: |
source ./bin/activate-hermit
just check-openapi-schema

desktop-lint:
name: Lint Electron Desktop App
Expand All @@ -147,6 +156,7 @@ jobs:
run: source ../../bin/activate-hermit && npm run lint:check
working-directory: ui/desktop


# Faster Desktop App build for PRs only
bundle-desktop-unsigned:
uses: ./.github/workflows/bundle-desktop.yml
Expand Down
15 changes: 9 additions & 6 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ run-server:
@echo "Running server..."
cargo run -p goose-server

# Check if OpenAPI schema is up-to-date
check-openapi-schema: generate-openapi
./scripts/check-openapi-schema.sh

# Generate OpenAPI specification without starting the UI
generate-openapi:
@echo "Generating OpenAPI schema..."
Expand Down Expand Up @@ -368,16 +372,16 @@ set windows-shell := ["powershell.exe", "-NoLogo", "-Command"]
### Build the core code
### profile = --release or "" for debug
### allparam = OR/AND/ANY/NONE --workspace --all-features --all-targets
win-bld profile allparam:
win-bld profile allparam:
cargo run {{profile}} -p goose-server --bin generate_schema
cargo build {{profile}} {{allparam}}

### Build just debug
win-bld-dbg:
win-bld-dbg:
just win-bld " " " "

### Build debug and test, examples,...
win-bld-dbg-all:
win-bld-dbg-all:
just win-bld " " "--workspace --all-targets --all-features"

### Build just release
Expand Down Expand Up @@ -440,8 +444,8 @@ win-total-rls *allparam:
just win-bld-rls{{allparam}}
just win-run-rls

### Build and run the Kotlin example with
### auto-generated bindings for goose-llm
### Build and run the Kotlin example with
### auto-generated bindings for goose-llm
kotlin-example:
# Build Rust dylib and generate Kotlin bindings
cargo build -p goose-llm
Expand All @@ -460,4 +464,3 @@ kotlin-example:
-Djna.library.path=$HOME/Development/goose/target/debug \
-classpath "example.jar:libs/kotlin-stdlib-1.9.0.jar:libs/kotlinx-coroutines-core-jvm-1.7.3.jar:libs/jna-5.13.0.jar" \
UsageKt

2 changes: 2 additions & 0 deletions crates/goose-server/src/openapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,8 @@ derive_utoipa!(Annotations as AnnotationsSchema);
goose::recipe::RecipeParameterRequirement,
goose::recipe::Response,
goose::recipe::SubRecipe,
goose::agents::types::RetryConfig,
goose::agents::types::SuccessCheck,
))
)]
pub struct ApiDoc;
Expand Down
5 changes: 3 additions & 2 deletions crates/goose/src/agents/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::{mpsc, Mutex};
use utoipa::ToSchema;

/// Type alias for the tool result channel receiver
pub type ToolResultReceiver = Arc<Mutex<mpsc::Receiver<(String, ToolResult<Vec<Content>>)>>>;
Expand All @@ -16,7 +17,7 @@ pub const DEFAULT_RETRY_TIMEOUT_SECONDS: u64 = 300;
pub const DEFAULT_ON_FAILURE_TIMEOUT_SECONDS: u64 = 600;

/// Configuration for retry logic in recipe execution
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct RetryConfig {
/// Maximum number of retry attempts before giving up
pub max_retries: u32,
Expand Down Expand Up @@ -59,7 +60,7 @@ impl RetryConfig {
}

/// A single success check to validate recipe completion
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(tag = "type")]
pub enum SuccessCheck {
/// Execute a shell command and check its exit status
Expand Down
28 changes: 28 additions & 0 deletions scripts/check-openapi-schema.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -e

# Check if OpenAPI schema is up-to-date
# This script generates the OpenAPI schema and compares it with the committed version

echo "🔍 Checking OpenAPI schema is up-to-date..."

# Check if the generated schema differs from the committed version
echo "🔍 Comparing generated schema with committed version..."
if ! git diff --exit-code ui/desktop/openapi.json ui/desktop/src/api/; then
echo ""
echo "❌ OpenAPI schema is out of date!"
echo ""
echo "The generated OpenAPI schema differs from the committed version."
echo "This usually means that API types were added or modified without updating the schema."
echo ""
echo "To fix this issue:"
echo "1. Run 'just generate-openapi' locally"
echo "2. Commit the changes to ui/desktop/openapi.json and ui/desktop/src/api/"
echo "3. Push your changes"
echo ""
echo "Changes detected:"
git diff ui/desktop/openapi.json ui/desktop/src/api/
exit 1
fi

echo "✅ OpenAPI schema is up-to-date"
80 changes: 79 additions & 1 deletion ui/desktop/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2183,7 +2183,7 @@
},
"Recipe": {
"type": "object",
"description": "A Recipe represents a personalized, user-generated agent configuration that defines\nspecific behaviors and capabilities within the Goose system.\n\n# Fields\n\n## Required Fields\n* `version` - Semantic version of the Recipe file format (defaults to \"1.0.0\")\n* `title` - Short, descriptive name of the Recipe\n* `description` - Detailed description explaining the Recipe's purpose and functionality\n* `Instructions` - Instructions that defines the Recipe's behavior\n\n## Optional Fields\n* `prompt` - the initial prompt to the session to start with\n* `extensions` - List of extension configurations required by the Recipe\n* `context` - Supplementary context information for the Recipe\n* `activities` - Activity labels that appear when loading the Recipe\n* `author` - Information about the Recipe's creator and metadata\n* `parameters` - Additional parameters for the Recipe\n* `response` - Response configuration including JSON schema validation\n\n# Example\n\n\nuse goose::recipe::Recipe;\n\n// Using the builder pattern\nlet recipe = Recipe::builder()\n.title(\"Example Agent\")\n.description(\"An example Recipe configuration\")\n.instructions(\"Act as a helpful assistant\")\n.build()\n.expect(\"Missing required fields\");\n\n// Or using struct initialization\nlet recipe = Recipe {\nversion: \"1.0.0\".to_string(),\ntitle: \"Example Agent\".to_string(),\ndescription: \"An example Recipe configuration\".to_string(),\ninstructions: Some(\"Act as a helpful assistant\".to_string()),\nprompt: None,\nextensions: None,\ncontext: None,\nactivities: None,\nauthor: None,\nsettings: None,\nparameters: None,\nresponse: None,\nsub_recipes: None,\n};\n",
"description": "A Recipe represents a personalized, user-generated agent configuration that defines\nspecific behaviors and capabilities within the Goose system.\n\n# Fields\n\n## Required Fields\n* `version` - Semantic version of the Recipe file format (defaults to \"1.0.0\")\n* `title` - Short, descriptive name of the Recipe\n* `description` - Detailed description explaining the Recipe's purpose and functionality\n* `Instructions` - Instructions that defines the Recipe's behavior\n\n## Optional Fields\n* `prompt` - the initial prompt to the session to start with\n* `extensions` - List of extension configurations required by the Recipe\n* `context` - Supplementary context information for the Recipe\n* `activities` - Activity labels that appear when loading the Recipe\n* `author` - Information about the Recipe's creator and metadata\n* `parameters` - Additional parameters for the Recipe\n* `response` - Response configuration including JSON schema validation\n* `retry` - Retry configuration for automated validation and recovery\n# Example\n\n\nuse goose::recipe::Recipe;\n\n// Using the builder pattern\nlet recipe = Recipe::builder()\n.title(\"Example Agent\")\n.description(\"An example Recipe configuration\")\n.instructions(\"Act as a helpful assistant\")\n.build()\n.expect(\"Missing required fields\");\n\n// Or using struct initialization\nlet recipe = Recipe {\nversion: \"1.0.0\".to_string(),\ntitle: \"Example Agent\".to_string(),\ndescription: \"An example Recipe configuration\".to_string(),\ninstructions: Some(\"Act as a helpful assistant\".to_string()),\nprompt: None,\nextensions: None,\ncontext: None,\nactivities: None,\nauthor: None,\nsettings: None,\nparameters: None,\nresponse: None,\nsub_recipes: None,\nretry: None,\n};\n",
"required": [
"title",
"description"
Expand Down Expand Up @@ -2244,6 +2244,14 @@
],
"nullable": true
},
"retry": {
"allOf": [
{
"$ref": "#/components/schemas/RetryConfig"
}
],
"nullable": true
},
"settings": {
"allOf": [
{
Expand Down Expand Up @@ -2373,6 +2381,48 @@
}
}
},
"RetryConfig": {
"type": "object",
"description": "Configuration for retry logic in recipe execution",
"required": [
"max_retries",
"checks"
],
"properties": {
"checks": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SuccessCheck"
},
"description": "List of success checks to validate recipe completion"
},
"max_retries": {
"type": "integer",
"format": "int32",
"description": "Maximum number of retry attempts before giving up",
"minimum": 0
},
"on_failure": {
"type": "string",
"description": "Optional shell command to run on failure for cleanup",
"nullable": true
},
"on_failure_timeout_seconds": {
"type": "integer",
"format": "int64",
"description": "Timeout in seconds for on_failure commands (default: 600 seconds)",
"nullable": true,
"minimum": 0
},
"timeout_seconds": {
"type": "integer",
"format": "int64",
"description": "Timeout in seconds for individual shell commands (default: 300 seconds)",
"nullable": true,
"minimum": 0
}
}
},
"Role": {
"oneOf": [
{
Expand Down Expand Up @@ -2685,6 +2735,34 @@
}
}
},
"SuccessCheck": {
"oneOf": [
{
"type": "object",
"description": "Execute a shell command and check its exit status",
"required": [
"command",
"type"
],
"properties": {
"command": {
"type": "string",
"description": "The shell command to execute"
},
"type": {
"type": "string",
"enum": [
"Shell"
]
}
}
}
],
"description": "A single success check to validate recipe completion",
"discriminator": {
"propertyName": "type"
}
},
"SummarizationRequested": {
"type": "object",
"required": [
Expand Down
41 changes: 40 additions & 1 deletion ui/desktop/src/api/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ export type ProvidersResponse = {
* * `author` - Information about the Recipe's creator and metadata
* * `parameters` - Additional parameters for the Recipe
* * `response` - Response configuration including JSON schema validation
*
* * `retry` - Retry configuration for automated validation and recovery
* # Example
*
*
Expand Down Expand Up @@ -425,6 +425,7 @@ export type ProvidersResponse = {
* parameters: None,
* response: None,
* sub_recipes: None,
* retry: None,
* };
*
*/
Expand All @@ -438,6 +439,7 @@ export type Recipe = {
parameters?: Array<RecipeParameter> | null;
prompt?: string | null;
response?: Response | null;
retry?: RetryConfig | null;
settings?: Settings | null;
sub_recipes?: Array<SubRecipe> | null;
title: string;
Expand Down Expand Up @@ -474,6 +476,32 @@ export type Response = {
json_schema?: unknown;
};

/**
* Configuration for retry logic in recipe execution
*/
export type RetryConfig = {
/**
* List of success checks to validate recipe completion
*/
checks: Array<SuccessCheck>;
/**
* Maximum number of retry attempts before giving up
*/
max_retries: number;
/**
* Optional shell command to run on failure for cleanup
*/
on_failure?: string | null;
/**
* Timeout in seconds for on_failure commands (default: 600 seconds)
*/
on_failure_timeout_seconds?: number | null;
/**
* Timeout in seconds for individual shell commands (default: 300 seconds)
*/
timeout_seconds?: number | null;
};

export type Role = string;

export type RunNowResponse = {
Expand Down Expand Up @@ -602,6 +630,17 @@ export type SubRecipe = {
} | null;
};

/**
* Execute a shell command and check its exit status
*/
export type SuccessCheck = {
/**
* The shell command to execute
*/
command: string;
type: 'Shell';
};

export type SummarizationRequested = {
msg: string;
};
Expand Down
Loading