roder-api 0.3.0

Agentic software development tools and SDKs for Roder.
Documentation
use roder_api::events::{EventEnvelope, EventSource, RoderEvent, TurnInterrupted};
use roder_api::inference::TokenUsage;
use roder_api::thread::project_turns_from_events;
use serde_json::json;
use time::OffsetDateTime;

fn interrupted(usage: TokenUsage) -> TurnInterrupted {
    serde_json::from_value(json!({
        "thread_id": "thread",
        "turn_id": "interrupted",
        "usage": usage,
        "timestamp": "1970-01-01T00:00:00Z"
    }))
    .unwrap()
}

#[test]
fn interrupted_usage_survives_event_serialization_and_turn_projection() {
    let usage = TokenUsage {
        prompt_tokens: 100,
        completion_tokens: 20,
        total_tokens: 120,
        cached_prompt_tokens: 30,
        cache_creation_prompt_tokens: 10,
        service_tier: Some("priority".into()),
        ..TokenUsage::default()
    };
    let event = RoderEvent::TurnInterrupted(interrupted(usage.clone()));
    let persisted = serde_json::to_vec(&event).unwrap();
    let event: RoderEvent = serde_json::from_slice(&persisted).unwrap();
    let envelope = EventEnvelope {
        event_id: "interruption".into(),
        seq: 1,
        timestamp: OffsetDateTime::UNIX_EPOCH,
        source: EventSource::Core,
        kind: event.kind().into(),
        thread_id: event.thread_id().cloned(),
        turn_id: event.turn_id().cloned(),
        event,
    };

    let turns = project_turns_from_events(&"thread".into(), &[envelope]);

    assert_eq!(turns.len(), 1);
    assert_eq!(turns[0].turn_id, "interrupted");
    assert_eq!(turns[0].completed_at, Some(OffsetDateTime::UNIX_EPOCH));
    assert_eq!(turns[0].usage, Some(usage));
}

#[test]
fn interruption_without_reported_usage_remains_unknown() {
    let event: TurnInterrupted = serde_json::from_value(json!({
        "thread_id": "thread",
        "turn_id": "interrupted",
        "timestamp": "1970-01-01T00:00:00Z"
    }))
    .unwrap();
    assert_eq!(event.usage, None);
    let serialized = serde_json::to_value(event).unwrap();

    assert!(serialized.get("usage").is_none());
}