sim-lib-openai-server 0.1.3

OpenAI-compatible gateway skeleton for SIM.
Documentation
use std::sync::Arc;

use serde_json::{Value, json};
use sim_kernel::{Args, Callable, Error};

use crate::routes::admin::admin_expr_json;
use crate::{
    ADMIN_CACHE_STATS_PATH, ADMIN_CAPABILITY_REPORT_PATH, ADMIN_EVENTS_PATH,
    ADMIN_MODEL_HEALTH_PATH, ADMIN_RUN_RETRIEVAL_PREFIX, ADMIN_RUNS_PATH, ADMIN_STORAGE_STATS_PATH,
    GatewayRequest, GatewayResponse, GatewayRoutesValue, HEALTH_PATH,
    OPENAI_GATEWAY_ADMIN_CAPABILITY, OpenAiGatewayFunction, RESPONSES_PATH, cache_stats_symbol,
    capability_report_symbol, configure_routes, events_symbol, install_openai_gateway_lib,
    model_health_symbol, openai_gateway_admin_capability, run_get_symbol, runs_symbol,
    storage_stats_symbol,
};

#[test]
fn public_health_does_not_leak_host_details() {
    let response = configure_routes().handle(&GatewayRequest::get(HEALTH_PATH));
    let json = response_json(&response);

    assert_eq!(response.status(), 200);
    assert_eq!(json["status"], "ok");
    for leaked in ["host", "hostname", "pid", "address", "uptime", "cwd"] {
        assert!(json.get(leaked).is_none(), "{leaked} leaked from health");
    }
}

#[test]
fn admin_routes_require_capability_and_return_json_metrics() {
    let routes = configure_routes();
    let response = routes.handle(&json_request(
        "POST",
        RESPONSES_PATH,
        &json!({
            "model": "fixture/slow-echo",
            "input": "admin metrics",
            "store": true,
        })
        .to_string(),
    ));
    assert_eq!(response.status(), 200);

    let denied = routes.handle(&GatewayRequest::get(ADMIN_STORAGE_STATS_PATH));
    assert_eq!(denied.status(), 403);
    assert_eq!(response_json(&denied)["error"]["code"], "capability_denied");

    let stats = response_json(&routes.handle(&admin_get(ADMIN_STORAGE_STATS_PATH)));
    assert_eq!(stats["object"], "openai-gateway/storage-stats");
    assert_eq!(stats["counters"]["request-count"], 1);
    assert_eq!(stats["counters"]["run-count"], 1);
    assert_eq!(stats["counters"]["event-count"], 6);
    assert_eq!(stats["storage-object-counts"]["response-objects"], 1);
    assert_eq!(
        stats["average-latency-ms-by-model-id"]["fixture/slow-echo"],
        1000
    );

    let runs = response_json(&routes.handle(&admin_get(ADMIN_RUNS_PATH)));
    let run_id = runs["data"][0]["id"].as_str().unwrap();
    let run =
        response_json(&routes.handle(&admin_get(&format!("{ADMIN_RUN_RETRIEVAL_PREFIX}{run_id}"))));
    assert_eq!(run["object"], "openai-gateway/run");
    assert_eq!(run["id"], run_id);

    let events = response_json(&routes.handle(&admin_get(ADMIN_EVENTS_PATH)));
    assert_eq!(events["object"], "openai-gateway/events");
    assert_eq!(events["data"].as_array().unwrap().len(), 6);
    assert!(
        events["data"]
            .as_array()
            .unwrap()
            .iter()
            .any(|event| event["event-kind"] == "request-start")
    );

    let health = response_json(&routes.handle(&admin_get(ADMIN_MODEL_HEALTH_PATH)));
    assert_eq!(health["object"], "openai-gateway/model-health");
    assert_eq!(health["backend"], "sim");
    let cache = response_json(&routes.handle(&admin_get(ADMIN_CACHE_STATS_PATH)));
    assert_eq!(cache["object"], "openai-gateway/cache-stats");
    assert_eq!(cache["hit-rate"], 0.0);
    let report = response_json(&routes.handle(&admin_get(ADMIN_CAPABILITY_REPORT_PATH)));
    assert_eq!(report["object"], "openai-gateway/capability-report");
    assert_eq!(report["admin-capability"], OPENAI_GATEWAY_ADMIN_CAPABILITY);
}

#[test]
fn admin_sim_exports_require_admin_and_return_sim_values() {
    let mut cx = super::cx();
    install_openai_gateway_lib(&mut cx).unwrap();
    for symbol in [
        runs_symbol(),
        run_get_symbol(),
        events_symbol(),
        storage_stats_symbol(),
        model_health_symbol(),
        cache_stats_symbol(),
        capability_report_symbol(),
    ] {
        assert!(cx.resolve_function(&symbol).is_ok());
    }

    let err = OpenAiGatewayFunction::storage_stats()
        .call(&mut cx, Args::new(Vec::new()))
        .unwrap_err();
    assert!(matches!(
        err,
        Error::CapabilityDenied { capability } if capability == openai_gateway_admin_capability()
    ));

    cx.grant(openai_gateway_admin_capability());
    let routes_value = cx
        .factory()
        .opaque(Arc::new(GatewayRoutesValue::new(configure_routes())))
        .unwrap();
    let value = OpenAiGatewayFunction::storage_stats()
        .call(&mut cx, Args::new(vec![routes_value]))
        .unwrap();
    let expr = value.object().as_expr(&mut cx).unwrap();
    let json = admin_expr_json(&expr);

    assert_eq!(json["object"], "openai-gateway/storage-stats");
    assert_eq!(json["counters"]["request-count"], 0);
}

fn admin_get(path: &str) -> GatewayRequest {
    GatewayRequest::new(
        "GET",
        path,
        vec![(
            "X-SIM-Capability".to_owned(),
            OPENAI_GATEWAY_ADMIN_CAPABILITY.to_owned(),
        )],
        Vec::new(),
    )
}

fn json_request(method: &str, path: &str, body: &str) -> GatewayRequest {
    GatewayRequest::new(
        method,
        path,
        vec![("Content-Type".to_owned(), "application/json".to_owned())],
        body.as_bytes().to_vec(),
    )
}

fn response_json(response: &GatewayResponse) -> Value {
    serde_json::from_slice(response.body()).unwrap()
}