Add experimental thread job scheduler for /loop

This commit is contained in:
Eric Traut
2026-03-30 15:30:34 -06:00
parent 716f7b0428
commit a02c2cd50c
52 changed files with 2291 additions and 113 deletions
+89
View File
@@ -0,0 +1,89 @@
use crate::JsonSchema;
use crate::ResponsesApiTool;
use crate::ToolSpec;
use std::collections::BTreeMap;
pub fn create_job_create_tool() -> ToolSpec {
let properties = BTreeMap::from([
(
"cron_expression".to_string(),
JsonSchema::String {
description: Some(
"Scheduler expression for the job. Supported values are scheduler-specific."
.to_string(),
),
},
),
(
"prompt".to_string(),
JsonSchema::String {
description: Some("Prompt to execute when the job fires.".to_string()),
},
),
(
"run_once".to_string(),
JsonSchema::Boolean {
description: Some(
"Optional. When true, delete the job after its next execution is claimed."
.to_string(),
),
},
),
]);
ToolSpec::Function(ResponsesApiTool {
name: "JobCreate".to_string(),
description:
"Create a runtime-only thread job using a structured scheduler expression and prompt."
.to_string(),
strict: false,
defer_loading: None,
parameters: JsonSchema::Object {
properties,
required: Some(vec!["cron_expression".to_string(), "prompt".to_string()]),
additional_properties: Some(false.into()),
},
output_schema: None,
})
}
pub fn create_job_delete_tool() -> ToolSpec {
let properties = BTreeMap::from([(
"id".to_string(),
JsonSchema::String {
description: Some("Identifier of the job to delete.".to_string()),
},
)]);
ToolSpec::Function(ResponsesApiTool {
name: "JobDelete".to_string(),
description: "Delete a runtime-only thread job by id.".to_string(),
strict: false,
defer_loading: None,
parameters: JsonSchema::Object {
properties,
required: Some(vec!["id".to_string()]),
additional_properties: Some(false.into()),
},
output_schema: None,
})
}
pub fn create_job_list_tool() -> ToolSpec {
ToolSpec::Function(ResponsesApiTool {
name: "JobList".to_string(),
description: "List runtime-only thread jobs for the current thread.".to_string(),
strict: false,
defer_loading: None,
parameters: JsonSchema::Object {
properties: BTreeMap::new(),
required: None,
additional_properties: Some(false.into()),
},
output_schema: None,
})
}
#[cfg(test)]
#[path = "job_tool_tests.rs"]
mod tests;
+31
View File
@@ -0,0 +1,31 @@
use crate::ResponsesApiTool;
use crate::ToolSpec;
use pretty_assertions::assert_eq;
use super::create_job_create_tool;
use super::create_job_delete_tool;
use super::create_job_list_tool;
#[test]
fn job_create_tool_uses_expected_name() {
let ToolSpec::Function(ResponsesApiTool { name, .. }) = create_job_create_tool() else {
panic!("expected function tool");
};
assert_eq!(name, "JobCreate");
}
#[test]
fn job_delete_tool_uses_expected_name() {
let ToolSpec::Function(ResponsesApiTool { name, .. }) = create_job_delete_tool() else {
panic!("expected function tool");
};
assert_eq!(name, "JobDelete");
}
#[test]
fn job_list_tool_uses_expected_name() {
let ToolSpec::Function(ResponsesApiTool { name, .. }) = create_job_list_tool() else {
panic!("expected function tool");
};
assert_eq!(name, "JobList");
}
+4
View File
@@ -5,6 +5,7 @@ mod agent_job_tool;
mod agent_tool;
mod code_mode;
mod dynamic_tool;
mod job_tool;
mod js_repl_tool;
mod json_schema;
mod local_tool;
@@ -38,6 +39,9 @@ pub use code_mode::create_code_mode_tool;
pub use code_mode::create_wait_tool;
pub use code_mode::tool_spec_to_code_mode_tool_definition;
pub use dynamic_tool::parse_dynamic_tool;
pub use job_tool::create_job_create_tool;
pub use job_tool::create_job_delete_tool;
pub use job_tool::create_job_list_tool;
pub use js_repl_tool::create_js_repl_reset_tool;
pub use js_repl_tool::create_js_repl_tool;
pub use json_schema::AdditionalProperties;