sim-lib-openai-server 0.1.2

OpenAI-compatible gateway skeleton for SIM.
Documentation
use serde_json::Value;

use crate::{
    AUDIO_SPEECH_PATH, AUDIO_TRANSCRIPTIONS_PATH, DeterministicGatewayClock, GatewayRequest,
    GatewayResponse, GatewayRouteState, GatewayStateStore, GatewayStore, GatewayVectorStore,
    GatewayVectorStoreItem, IMAGES_GENERATIONS_PATH, MemoryGatewayStore, TableGatewayStore,
    VECTOR_STORE_SEARCH_ROUTE, VECTOR_STORES_PATH, configure_routes, configure_routes_with_state,
    routes::{
        audio::execute_audio_transcription_request, run_record::RouteRunIdGenerators,
        vector_stores::VECTOR_STORE_SEARCH_SUFFIX,
    },
};

#[test]
fn phase21_routes_are_registered() {
    let paths = configure_routes().paths();

    for path in [
        AUDIO_TRANSCRIPTIONS_PATH,
        AUDIO_SPEECH_PATH,
        IMAGES_GENERATIONS_PATH,
        VECTOR_STORES_PATH,
        VECTOR_STORE_SEARCH_ROUTE,
    ] {
        assert!(paths.contains(&path), "missing route {path}");
    }
}

#[test]
fn audio_transcription_records_replayable_fixture_run() {
    let mut store = MemoryGatewayStore::new();
    let mut ids = RouteRunIdGenerators::deterministic(7);
    let mut clock = DeterministicGatewayClock::new(1_000, 10);
    let request = json_request(
        AUDIO_TRANSCRIPTIONS_PATH,
        r#"{"input":"hello   SIM audio","store":true}"#,
    );

    let execution = execute_audio_transcription_request(&mut store, &mut ids, &mut clock, &request);
    let json = response_json(execution.response());

    assert_eq!(execution.response().status(), 200);
    assert_eq!(json["text"], "transcribed: hello SIM audio");
    assert!(
        store
            .request(execution.request_content_id().unwrap())
            .is_some()
    );
    assert!(store.run(execution.run_content_id().unwrap()).is_some());
    assert_eq!(execution.event_content_ids().len(), 4);
    assert_eq!(execution.events().len(), 4);
    assert!(
        store
            .response(execution.response_content_id().unwrap())
            .is_some()
    );
}

#[test]
fn audio_and_image_fixtures_are_deterministic() {
    let routes = configure_routes();
    let transcription = json_request(
        AUDIO_TRANSCRIPTIONS_PATH,
        r#"{"input":"deterministic audio"}"#,
    );
    let speech = json_request(
        AUDIO_SPEECH_PATH,
        r#"{"input":"hello","voice":"verse","response_format":"wav"}"#,
    );
    let image = json_request(IMAGES_GENERATIONS_PATH, r#"{"prompt":"unit square","n":2}"#);

    let transcription_first = routes.handle(&transcription);
    let transcription_second = routes.handle(&transcription);
    let speech_first = routes.handle(&speech);
    let speech_second = routes.handle(&speech);
    let image_first = routes.handle(&image);
    let image_second = routes.handle(&image);

    assert_eq!(transcription_first.status(), 200);
    assert_eq!(transcription_first.body(), transcription_second.body());
    assert_eq!(speech_first.status(), 200);
    assert_eq!(speech_first.header("Content-Type"), Some("audio/wav"));
    assert_eq!(speech_first.body(), speech_second.body());
    assert_eq!(image_first.status(), 200);
    assert_eq!(image_first.body(), image_second.body());
    assert_eq!(
        response_json(&image_first)["data"]
            .as_array()
            .unwrap()
            .len(),
        2
    );
}

#[test]
fn vector_store_search_returns_deterministic_nearest_items() {
    let state = GatewayRouteState::memory();
    let routes = configure_routes_with_state(state.clone());
    let create = routes.handle(&json_request(
        VECTOR_STORES_PATH,
        r#"{"name":"fixture","items":["alpha","beta","gamma"]}"#,
    ));
    let create_json = response_json(&create);
    let vector_store_id = create_json["id"].as_str().unwrap();
    let search_path = format!(
        "/v1/vector_stores/{vector_store_id}{}",
        VECTOR_STORE_SEARCH_SUFFIX
    );

    assert_eq!(create.status(), 200);
    assert!(
        state
            .store()
            .lock()
            .unwrap()
            .vector_store(vector_store_id)
            .is_some()
    );

    let request = json_request(&search_path, r#"{"query":"alpha","limit":2}"#);
    let first = routes.handle(&request);
    let second = routes.handle(&request);
    let json = response_json(&first);

    assert_eq!(first.status(), 200);
    assert_eq!(first.body(), second.body());
    assert_eq!(json["data"].as_array().unwrap().len(), 2);
    assert_eq!(json["data"][0]["text"], "alpha");
    assert_eq!(json["data"][0]["score"], 1.0);
}

#[test]
fn vector_store_objects_round_trip_through_table_store() {
    let mut store = TableGatewayStore::new().unwrap();
    let vector_store = GatewayVectorStore::new(
        "vs_table",
        "table-backed",
        1,
        vec![GatewayVectorStoreItem::new(
            "vsi_table",
            "alpha",
            vec![1.0, 0.0],
        )],
    );

    store.put_vector_store(vector_store.clone()).unwrap();

    assert_eq!(store.vector_store("vs_table"), Some(vector_store));
}

fn json_request(path: &str, body: &str) -> GatewayRequest {
    GatewayRequest::new(
        "POST",
        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()
}