forge-guardrails 0.1.2

Foundation types for an LLM-agent workflow framework
Documentation
use serde_json::{json, Value};

use super::types::AlternativeProposal;
use crate::schema::{mutated_arguments_for_tool, tool_by_name, validate_candidate_call};

pub(crate) fn propose_targeted_alternatives(capture: &Value) -> Vec<AlternativeProposal> {
    let mut proposals = Vec::new();
    let available_tools = &capture["available_tools"];
    let original = &capture["candidate_call"];
    let Some(current_name) = original.get("name").and_then(Value::as_str) else {
        return proposals;
    };
    let example_group_id = capture
        .get("example_group_id")
        .and_then(Value::as_str)
        .unwrap_or("unknown-group")
        .to_string();

    if let Some(tool) = tool_by_name(available_tools, current_name) {
        if let Some(mutated_args) =
            mutated_arguments_for_tool(tool, original.get("arguments").unwrap_or(&Value::Null))
        {
            let candidate_call = json!({"name": current_name, "arguments": mutated_args});
            if validate_candidate_call(available_tools, &candidate_call).is_ok()
                && candidate_call != *original
            {
                proposals.push(AlternativeProposal {
                    capture: capture.clone(),
                    example_group_id: example_group_id.clone(),
                    candidate_call,
                    label: "wrong_arguments_semantic".to_string(),
                });
            }
        }
    }

    if validate_candidate_call(available_tools, original).is_ok() {
        if let Some(capture) = tool_not_needed_capture(capture, current_name) {
            proposals.push(AlternativeProposal {
                capture,
                example_group_id: example_group_id.clone(),
                candidate_call: original.clone(),
                label: "tool_not_needed".to_string(),
            });
        }
        if let Some(capture) = needs_clarification_capture(capture) {
            proposals.push(AlternativeProposal {
                capture,
                example_group_id: example_group_id.clone(),
                candidate_call: original.clone(),
                label: "needs_clarification".to_string(),
            });
        }
    }

    if let Some(candidate_call) = curated_wrong_tool_candidate(capture, current_name) {
        if validate_candidate_call(available_tools, &candidate_call).is_ok() {
            proposals.push(AlternativeProposal {
                capture: capture.clone(),
                example_group_id: example_group_id.clone(),
                candidate_call,
                label: "wrong_tool_semantic".to_string(),
            });
        }
    }
    rotate_proposals(&mut proposals, &example_group_id);
    proposals
}

fn tool_not_needed_capture(capture: &Value, current_name: &str) -> Option<Value> {
    if current_name == "respond" {
        return None;
    }
    let mut capture = capture.clone();
    let workflow_state = capture.get_mut("workflow_state")?.as_object_mut()?;
    workflow_state.insert("required_steps".to_string(), json!([]));
    workflow_state.insert("completed_steps".to_string(), json!([current_name]));
    workflow_state.insert("pending_steps".to_string(), json!([]));
    Some(capture)
}

fn needs_clarification_capture(capture: &Value) -> Option<Value> {
    let domain = capture
        .get("proxy_trace")
        .and_then(|trace| trace.get("domain"))
        .and_then(Value::as_str)
        .unwrap_or("forge_dataset");
    let request = match domain {
        "repo_docs" => "Find the relevant repository information, but I have not said what topic or file I need.",
        "shopping" => "Help me buy the thing I mentioned, but I have not provided the product or quantity.",
        "calendar" => "Schedule that meeting for me, but I have not provided a date, duration, or slot.",
        "support" => "Fix the issue on my account, but I have not provided a ticket, account, or problem details.",
        "forge_eval" => "Run the eval I meant, but I have not provided the suite, scenario, or run count.",
        _ => "Use the right tool for my request, but I have not provided enough details to choose arguments.",
    };
    let mut capture = capture.clone();
    capture["user_request"] = Value::String(request.to_string());
    if let Some(workflow_state) = capture
        .get_mut("workflow_state")
        .and_then(Value::as_object_mut)
    {
        workflow_state.insert("required_steps".to_string(), json!([]));
        workflow_state.insert("completed_steps".to_string(), json!([]));
        workflow_state.insert("pending_steps".to_string(), json!([]));
        workflow_state.insert("recent_errors".to_string(), json!([]));
    }
    Some(capture)
}

fn rotate_proposals(proposals: &mut [AlternativeProposal], key: &str) {
    if proposals.len() <= 1 {
        return;
    }
    let rotation = stable_hash(key) as usize % proposals.len();
    proposals.rotate_left(rotation);
}

fn stable_hash(key: &str) -> u64 {
    let mut hash = 0xcbf29ce484222325u64;
    for byte in key.bytes() {
        hash ^= byte as u64;
        hash = hash.wrapping_mul(0x100000001b3);
    }
    hash
}

pub(crate) fn curated_wrong_tool_candidate(capture: &Value, current_name: &str) -> Option<Value> {
    let domain = capture
        .get("proxy_trace")
        .and_then(|trace| trace.get("domain"))
        .and_then(Value::as_str)?;
    let scenario = capture
        .get("proxy_trace")
        .and_then(|trace| trace.get("scenario"))
        .and_then(Value::as_str)?;

    let candidate = match (domain, scenario) {
        ("shopping", "compare_headphones") => {
            json!({"name": "add_to_cart", "arguments": {"product_id": "SKU-HEADPHONES-1", "quantity": 1}})
        }
        ("shopping", "add_dock") => {
            json!({"name": "compare_products", "arguments": {"product_ids": ["SKU-DOCK-1", "SKU-HEADPHONES-1"]}})
        }
        ("shopping", "product_details") => {
            json!({"name": "add_to_cart", "arguments": {"product_id": "SKU-HEADPHONES-1", "quantity": 1}})
        }
        ("calendar", "find_and_hold") => {
            json!({"name": "list_events", "arguments": {"date": "2026-06-05"}})
        }
        ("calendar", "list_events") => {
            json!({"name": "create_calendar_hold", "arguments": {"slot_id": "slot-001"}})
        }
        ("calendar", "cancel_hold") => {
            json!({"name": "list_events", "arguments": {"date": "2026-06-05"}})
        }
        ("support", "lookup_ticket") => {
            json!({"name": "update_ticket", "arguments": {"ticket_id": "TCK-1001", "note": "Reviewed by dataset stub."}})
        }
        ("support", "search_kb") => {
            json!({"name": "lookup_ticket", "arguments": {"ticket_id": "TCK-1001"}})
        }
        ("support", "escalate_ticket") => {
            json!({"name": "update_ticket", "arguments": {"ticket_id": "TCK-2002", "note": "Customer is blocked from billing."}})
        }
        ("forge_eval", "smoke_eval") => {
            json!({"name": "run_release_eval", "arguments": {"scenario": "basic_2step", "runs": 1}})
        }
        ("forge_eval", "release_eval") => {
            json!({"name": "run_smoke_eval", "arguments": {"scenario": "basic_2step", "runs": 1}})
        }
        ("forge_eval", "diagnose_failure") => {
            json!({"name": "report_result", "arguments": {"summary": "Failure diagnosed."}})
        }
        ("forge_eval", "inspect_workflow_state") => {
            json!({"name": "report_result", "arguments": {"summary": "Workflow state checked."}})
        }
        ("forge_eval", "fetch_zero_padded") => {
            json!({"name": "summarize_records", "arguments": {"content": "Fetched 0010 records."}})
        }
        _ => return None,
    };

    if candidate.get("name").and_then(Value::as_str) == Some(current_name) {
        None
    } else {
        Some(candidate)
    }
}

pub(crate) fn alternative_cap(real_row_count: usize, ratio: f64) -> usize {
    ((real_row_count as f64 * ratio) + 1e-9).floor() as usize
}

#[cfg(test)]
mod tests {
    use super::*;

    fn capture_row() -> Value {
        json!({
            "schema_version": "forge-dataset-capture/v1",
            "example_group_id": "group-1",
            "user_request": "Compare two products.",
            "workflow_state": {
                "required_steps": [],
                "completed_steps": [],
                "pending_steps": [],
                "terminal_tools": ["respond"],
                "recent_errors": []
            },
            "available_tools": [
                {
                    "name": "compare_products",
                    "description": "Compare products.",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "product_ids": {"type": "array", "items": {"type": "string"}}
                        },
                        "required": ["product_ids"]
                    }
                },
                {
                    "name": "add_to_cart",
                    "description": "Add a product to cart.",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "product_id": {"type": "string"},
                            "quantity": {"type": "integer"}
                        },
                        "required": ["product_id", "quantity"]
                    }
                }
            ],
            "candidate_call": {
                "name": "compare_products",
                "arguments": {"product_ids": ["SKU-1", "SKU-2"]}
            },
            "tool_result": {"status": "ok", "content": {}},
            "proxy_trace": {"domain": "shopping", "scenario": "compare_headphones"}
        })
    }

    #[test]
    fn proposes_schema_valid_targeted_alternatives() {
        let capture = capture_row();
        let proposals = propose_targeted_alternatives(&capture);
        assert!(proposals
            .iter()
            .any(|proposal| proposal.label == "wrong_arguments_semantic"));
        assert!(proposals
            .iter()
            .any(|proposal| proposal.label == "wrong_tool_semantic"));
        assert!(proposals
            .iter()
            .any(|proposal| proposal.label == "tool_not_needed"));
        assert!(proposals
            .iter()
            .any(|proposal| proposal.label == "needs_clarification"));
        assert!(proposals.iter().any(|proposal| {
            proposal.label == "wrong_tool_semantic"
                && proposal.candidate_call["name"] == "add_to_cart"
        }));
        for proposal in proposals {
            validate_candidate_call(&capture["available_tools"], &proposal.candidate_call)
                .expect("schema-valid proposal");
        }
    }

    #[test]
    fn targeted_alternatives_cover_repeated_tool_and_ambiguous_request() {
        let capture = capture_row();
        let proposals = propose_targeted_alternatives(&capture);
        let repeated = proposals
            .iter()
            .find(|proposal| proposal.label == "tool_not_needed")
            .expect("tool_not_needed proposal");
        assert_eq!(
            repeated.capture["workflow_state"]["completed_steps"],
            json!(["compare_products"])
        );
        assert_eq!(
            repeated.capture["workflow_state"]["pending_steps"],
            json!([])
        );
        assert_eq!(repeated.candidate_call, capture["candidate_call"]);

        let ambiguous = proposals
            .iter()
            .find(|proposal| proposal.label == "needs_clarification")
            .expect("needs_clarification proposal");
        assert!(ambiguous.capture["user_request"]
            .as_str()
            .expect("request")
            .contains("not provided"));
        assert_eq!(ambiguous.candidate_call, capture["candidate_call"]);
    }

    #[test]
    fn alternative_cap_keeps_real_rows_as_backbone() {
        assert_eq!(alternative_cap(0, 1.0 / 3.0), 0);
        assert_eq!(alternative_cap(2, 1.0 / 3.0), 0);
        assert_eq!(alternative_cap(3, 1.0 / 3.0), 1);
        assert_eq!(alternative_cap(6, 1.0 / 3.0), 2);
    }
}