pub const JOBS_ROOT: &str = "/api/jobs";
pub const JOBS_CREATE_GEPA: &str = "/api/jobs/gepa";
pub const JOBS_CREATE_MIPRO: &str = "/api/jobs/mipro";
pub const JOBS_CREATE_EVAL: &str = "/api/jobs/eval";
pub const JOBS_CREATE_GRAPH: &str = "/api/jobs/graph";
pub const JOBS_CREATE_VERIFIER: &str = "/api/jobs/verifier";
pub fn jobs_status(job_id: &str) -> String {
format!("{}/{}", JOBS_ROOT, job_id)
}
pub fn jobs_events(job_id: &str) -> String {
format!("{}/{}/events", JOBS_ROOT, job_id)
}
pub fn jobs_events_stream(job_id: &str) -> String {
format!("{}/{}/events/stream", JOBS_ROOT, job_id)
}
pub fn jobs_artifacts(job_id: &str) -> String {
format!("{}/{}/artifacts", JOBS_ROOT, job_id)
}
pub fn jobs_cancel(job_id: &str) -> String {
format!("{}/{}/cancel", JOBS_ROOT, job_id)
}
pub fn jobs_metrics(job_id: &str) -> String {
format!("{}/{}/metrics", JOBS_ROOT, job_id)
}
pub const LEGACY_PROMPT_LEARNING_ROOT: &str = "/api/prompt-learning/online/jobs";
pub const LEGACY_POLICY_OPTIMIZATION_ROOT: &str = "/api/policy-optimization/online/jobs";
pub const LEGACY_EVAL_ROOT: &str = "/api/eval/jobs";
pub const LEGACY_LEARNING_ROOT: &str = "/api/learning/jobs";
pub const LEGACY_ORCHESTRATION_ROOT: &str = "/api/orchestration/jobs";
pub fn legacy_prompt_learning_status(job_id: &str) -> String {
format!("{}/{}", LEGACY_PROMPT_LEARNING_ROOT, job_id)
}
pub fn legacy_prompt_learning_events(job_id: &str) -> String {
format!("{}/{}/events", LEGACY_PROMPT_LEARNING_ROOT, job_id)
}
pub fn legacy_prompt_learning_events_stream(job_id: &str) -> String {
format!("{}/{}/events/stream", LEGACY_PROMPT_LEARNING_ROOT, job_id)
}
pub fn legacy_prompt_learning_metrics(job_id: &str) -> String {
format!("{}/{}/metrics", LEGACY_PROMPT_LEARNING_ROOT, job_id)
}
pub fn legacy_eval_status(job_id: &str) -> String {
format!("{}/{}", LEGACY_EVAL_ROOT, job_id)
}
pub fn legacy_eval_events(job_id: &str) -> String {
format!("{}/{}/events", LEGACY_EVAL_ROOT, job_id)
}
pub fn legacy_eval_events_stream(job_id: &str) -> String {
format!("{}/{}/events/stream", LEGACY_EVAL_ROOT, job_id)
}
pub const TERMINAL_VERB_COMPLETED: &str = "job.completed";
pub const TERMINAL_VERB_FAILED: &str = "job.failed";
pub const TERMINAL_VERB_CANCELLED: &str = "job.cancelled";
pub const TERMINAL_VERBS: &[&str] = &[
TERMINAL_VERB_COMPLETED,
TERMINAL_VERB_FAILED,
TERMINAL_VERB_CANCELLED,
];
pub fn is_terminal_event_type(event_type: &str) -> bool {
let lower = event_type.to_lowercase();
TERMINAL_VERBS.iter().any(|verb| lower.ends_with(verb))
}
pub fn terminal_event_to_status(event_type: &str) -> Option<&'static str> {
let lower = event_type.to_lowercase();
if lower.ends_with(TERMINAL_VERB_COMPLETED) {
Some("succeeded")
} else if lower.ends_with(TERMINAL_VERB_FAILED) {
Some("failed")
} else if lower.ends_with(TERMINAL_VERB_CANCELLED) {
Some("cancelled")
} else {
None
}
}
pub const TERMINAL_STATUSES: &[&str] = &[
"succeeded",
"failed",
"cancelled",
"canceled",
"completed",
];
pub fn is_terminal_status(status: &str) -> bool {
TERMINAL_STATUSES.contains(&status.to_lowercase().as_str())
}
pub const EVENT_REQUIRED_FIELDS: &[&str] = &["type"];
pub const EVENT_RECOMMENDED_FIELDS: &[&str] = &["job_id", "seq", "data"];
pub const EVENT_OPTIONAL_FIELDS: &[&str] = &["run_id", "timestamp", "message", "level", "ts"];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_canonical_endpoints() {
assert_eq!(jobs_status("abc"), "/api/jobs/abc");
assert_eq!(jobs_events("abc"), "/api/jobs/abc/events");
assert_eq!(jobs_events_stream("abc"), "/api/jobs/abc/events/stream");
assert_eq!(jobs_artifacts("abc"), "/api/jobs/abc/artifacts");
assert_eq!(jobs_cancel("abc"), "/api/jobs/abc/cancel");
}
#[test]
fn test_legacy_endpoints() {
assert_eq!(
legacy_prompt_learning_status("abc"),
"/api/prompt-learning/online/jobs/abc"
);
assert_eq!(
legacy_eval_status("abc"),
"/api/eval/jobs/abc"
);
}
#[test]
fn test_terminal_detection_by_suffix() {
assert!(is_terminal_event_type("learning.policy.gepa.job.completed"));
assert!(is_terminal_event_type("eval.policy.job.failed"));
assert!(is_terminal_event_type("some.new.domain.job.cancelled"));
assert!(!is_terminal_event_type("learning.policy.gepa.candidate.evaluated"));
assert!(!is_terminal_event_type("learning.policy.gepa.generation.completed"));
}
#[test]
fn test_terminal_event_to_status() {
assert_eq!(
terminal_event_to_status("learning.policy.gepa.job.completed"),
Some("succeeded")
);
assert_eq!(
terminal_event_to_status("eval.policy.job.failed"),
Some("failed")
);
assert_eq!(
terminal_event_to_status("some.job.cancelled"),
Some("cancelled")
);
assert_eq!(
terminal_event_to_status("learning.policy.gepa.candidate.evaluated"),
None
);
}
#[test]
fn test_terminal_status() {
assert!(is_terminal_status("succeeded"));
assert!(is_terminal_status("failed"));
assert!(is_terminal_status("cancelled"));
assert!(is_terminal_status("canceled"));
assert!(is_terminal_status("completed"));
assert!(is_terminal_status("Completed")); assert!(!is_terminal_status("running"));
assert!(!is_terminal_status("pending"));
}
#[test]
fn test_create_endpoints() {
assert_eq!(JOBS_CREATE_GEPA, "/api/jobs/gepa");
assert_eq!(JOBS_CREATE_EVAL, "/api/jobs/eval");
}
}