mirror of
https://github.com/aljazceru/goose.git
synced 2025-12-17 06:04:23 +01:00
fix: ensure retry-config and success-criteria are populated in openapi spec (#3575)
This commit is contained in:
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@@ -131,6 +131,15 @@ jobs:
|
|||||||
source ./bin/activate-hermit
|
source ./bin/activate-hermit
|
||||||
export CARGO_INCREMENTAL=0
|
export CARGO_INCREMENTAL=0
|
||||||
cargo clippy --jobs 2 -- -D warnings
|
cargo clippy --jobs 2 -- -D warnings
|
||||||
|
|
||||||
|
- name: Install Node.js Dependencies for OpenAPI Check
|
||||||
|
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:
|
desktop-lint:
|
||||||
name: Lint Electron Desktop App
|
name: Lint Electron Desktop App
|
||||||
@@ -147,6 +156,7 @@ jobs:
|
|||||||
run: source ../../bin/activate-hermit && npm run lint:check
|
run: source ../../bin/activate-hermit && npm run lint:check
|
||||||
working-directory: ui/desktop
|
working-directory: ui/desktop
|
||||||
|
|
||||||
|
|
||||||
# Faster Desktop App build for PRs only
|
# Faster Desktop App build for PRs only
|
||||||
bundle-desktop-unsigned:
|
bundle-desktop-unsigned:
|
||||||
uses: ./.github/workflows/bundle-desktop.yml
|
uses: ./.github/workflows/bundle-desktop.yml
|
||||||
|
|||||||
15
Justfile
15
Justfile
@@ -184,6 +184,10 @@ run-server:
|
|||||||
@echo "Running server..."
|
@echo "Running server..."
|
||||||
cargo run -p goose-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 specification without starting the UI
|
||||||
generate-openapi:
|
generate-openapi:
|
||||||
@echo "Generating OpenAPI schema..."
|
@echo "Generating OpenAPI schema..."
|
||||||
@@ -368,16 +372,16 @@ set windows-shell := ["powershell.exe", "-NoLogo", "-Command"]
|
|||||||
### Build the core code
|
### Build the core code
|
||||||
### profile = --release or "" for debug
|
### profile = --release or "" for debug
|
||||||
### allparam = OR/AND/ANY/NONE --workspace --all-features --all-targets
|
### 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 run {{profile}} -p goose-server --bin generate_schema
|
||||||
cargo build {{profile}} {{allparam}}
|
cargo build {{profile}} {{allparam}}
|
||||||
|
|
||||||
### Build just debug
|
### Build just debug
|
||||||
win-bld-dbg:
|
win-bld-dbg:
|
||||||
just win-bld " " " "
|
just win-bld " " " "
|
||||||
|
|
||||||
### Build debug and test, examples,...
|
### Build debug and test, examples,...
|
||||||
win-bld-dbg-all:
|
win-bld-dbg-all:
|
||||||
just win-bld " " "--workspace --all-targets --all-features"
|
just win-bld " " "--workspace --all-targets --all-features"
|
||||||
|
|
||||||
### Build just release
|
### Build just release
|
||||||
@@ -440,8 +444,8 @@ win-total-rls *allparam:
|
|||||||
just win-bld-rls{{allparam}}
|
just win-bld-rls{{allparam}}
|
||||||
just win-run-rls
|
just win-run-rls
|
||||||
|
|
||||||
### Build and run the Kotlin example with
|
### Build and run the Kotlin example with
|
||||||
### auto-generated bindings for goose-llm
|
### auto-generated bindings for goose-llm
|
||||||
kotlin-example:
|
kotlin-example:
|
||||||
# Build Rust dylib and generate Kotlin bindings
|
# Build Rust dylib and generate Kotlin bindings
|
||||||
cargo build -p goose-llm
|
cargo build -p goose-llm
|
||||||
@@ -460,4 +464,3 @@ kotlin-example:
|
|||||||
-Djna.library.path=$HOME/Development/goose/target/debug \
|
-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" \
|
-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
|
UsageKt
|
||||||
|
|
||||||
|
|||||||
@@ -393,6 +393,8 @@ derive_utoipa!(Annotations as AnnotationsSchema);
|
|||||||
goose::recipe::RecipeParameterRequirement,
|
goose::recipe::RecipeParameterRequirement,
|
||||||
goose::recipe::Response,
|
goose::recipe::Response,
|
||||||
goose::recipe::SubRecipe,
|
goose::recipe::SubRecipe,
|
||||||
|
goose::agents::types::RetryConfig,
|
||||||
|
goose::agents::types::SuccessCheck,
|
||||||
))
|
))
|
||||||
)]
|
)]
|
||||||
pub struct ApiDoc;
|
pub struct ApiDoc;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::{mpsc, Mutex};
|
use tokio::sync::{mpsc, Mutex};
|
||||||
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
/// Type alias for the tool result channel receiver
|
/// Type alias for the tool result channel receiver
|
||||||
pub type ToolResultReceiver = Arc<Mutex<mpsc::Receiver<(String, ToolResult<Vec<Content>>)>>>;
|
pub type ToolResultReceiver = Arc<Mutex<mpsc::Receiver<(String, ToolResult<Vec<Content>>)>>>;
|
||||||
@@ -16,7 +17,7 @@ pub const DEFAULT_RETRY_TIMEOUT_SECONDS: u64 = 300;
|
|||||||
pub const DEFAULT_ON_FAILURE_TIMEOUT_SECONDS: u64 = 600;
|
pub const DEFAULT_ON_FAILURE_TIMEOUT_SECONDS: u64 = 600;
|
||||||
|
|
||||||
/// Configuration for retry logic in recipe execution
|
/// Configuration for retry logic in recipe execution
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
||||||
pub struct RetryConfig {
|
pub struct RetryConfig {
|
||||||
/// Maximum number of retry attempts before giving up
|
/// Maximum number of retry attempts before giving up
|
||||||
pub max_retries: u32,
|
pub max_retries: u32,
|
||||||
@@ -59,7 +60,7 @@ impl RetryConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A single success check to validate recipe completion
|
/// A single success check to validate recipe completion
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum SuccessCheck {
|
pub enum SuccessCheck {
|
||||||
/// Execute a shell command and check its exit status
|
/// Execute a shell command and check its exit status
|
||||||
|
|||||||
28
scripts/check-openapi-schema.sh
Executable file
28
scripts/check-openapi-schema.sh
Executable file
@@ -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"
|
||||||
@@ -2183,7 +2183,7 @@
|
|||||||
},
|
},
|
||||||
"Recipe": {
|
"Recipe": {
|
||||||
"type": "object",
|
"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": [
|
"required": [
|
||||||
"title",
|
"title",
|
||||||
"description"
|
"description"
|
||||||
@@ -2244,6 +2244,14 @@
|
|||||||
],
|
],
|
||||||
"nullable": true
|
"nullable": true
|
||||||
},
|
},
|
||||||
|
"retry": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/RetryConfig"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"allOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
@@ -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": {
|
"Role": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
@@ -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": {
|
"SummarizationRequested": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
|||||||
@@ -396,7 +396,7 @@ export type ProvidersResponse = {
|
|||||||
* * `author` - Information about the Recipe's creator and metadata
|
* * `author` - Information about the Recipe's creator and metadata
|
||||||
* * `parameters` - Additional parameters for the Recipe
|
* * `parameters` - Additional parameters for the Recipe
|
||||||
* * `response` - Response configuration including JSON schema validation
|
* * `response` - Response configuration including JSON schema validation
|
||||||
*
|
* * `retry` - Retry configuration for automated validation and recovery
|
||||||
* # Example
|
* # Example
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
@@ -425,6 +425,7 @@ export type ProvidersResponse = {
|
|||||||
* parameters: None,
|
* parameters: None,
|
||||||
* response: None,
|
* response: None,
|
||||||
* sub_recipes: None,
|
* sub_recipes: None,
|
||||||
|
* retry: None,
|
||||||
* };
|
* };
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -438,6 +439,7 @@ export type Recipe = {
|
|||||||
parameters?: Array<RecipeParameter> | null;
|
parameters?: Array<RecipeParameter> | null;
|
||||||
prompt?: string | null;
|
prompt?: string | null;
|
||||||
response?: Response | null;
|
response?: Response | null;
|
||||||
|
retry?: RetryConfig | null;
|
||||||
settings?: Settings | null;
|
settings?: Settings | null;
|
||||||
sub_recipes?: Array<SubRecipe> | null;
|
sub_recipes?: Array<SubRecipe> | null;
|
||||||
title: string;
|
title: string;
|
||||||
@@ -474,6 +476,32 @@ export type Response = {
|
|||||||
json_schema?: unknown;
|
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 Role = string;
|
||||||
|
|
||||||
export type RunNowResponse = {
|
export type RunNowResponse = {
|
||||||
@@ -602,6 +630,17 @@ export type SubRecipe = {
|
|||||||
} | null;
|
} | 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 = {
|
export type SummarizationRequested = {
|
||||||
msg: string;
|
msg: string;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user