diff --git a/Cargo.lock b/Cargo.lock index 5291d19..efa3d84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -144,6 +144,7 @@ dependencies = [ "libc", "model-connector", "rmp-serde", + "rmpv", "serde", "serde_json", "sha2", diff --git a/crates/claude-connector/src/backend/tests.rs b/crates/claude-connector/src/backend/tests.rs index e3449fe..e53b9f7 100644 --- a/crates/claude-connector/src/backend/tests.rs +++ b/crates/claude-connector/src/backend/tests.rs @@ -5,8 +5,9 @@ use std::sync::{Arc, Mutex}; use std::time::Duration; use model_connector::{ - CallerAdmission, PlanQuotaSnapshot, ReasoningEffort, StopReason, ToolDefinition, TransportAdmission, TransportDisposition, TransportInvocation, - TransportKey, TransportReceipt, TransportService, decrypt_result, encrypt_invocation, + CallerAdmission, PlanQuotaSnapshot, ReasoningEffort, StopReason, ToolDefinition, + TransportAdmission, TransportDisposition, TransportInvocation, TransportKey, TransportReceipt, + TransportService, decrypt_result, encrypt_invocation, }; use super::*; @@ -480,8 +481,7 @@ impl Door { .unwrap() }); let service = - TransportService::open(&dir.join("replay.cc"), PROVIDER, callers, 5_000) - .unwrap(); + TransportService::open(&dir.join("replay.cc"), PROVIDER, callers, 5_000).unwrap(); Self { dir, service } } @@ -498,13 +498,13 @@ impl Door { request: ProviderRequest, ) -> (model_connector::ExecutionClaim, TransportInvocation) { let key = TransportKey::from_connection_secret(&format!("{caller}-key")).unwrap(); - let invocation = - TransportInvocation::new(caller, 2_000, [7; 32], request).unwrap(); + let invocation = TransportInvocation::new(caller, 2_000, [7; 32], request).unwrap(); let envelope = encrypt_invocation(&invocation, &key).unwrap(); match self .service .begin(&envelope, 1_000, &PlanQuotaSnapshot::default(), FRAME) - .unwrap() { + .unwrap() + { TransportAdmission::Execute(claim) => (*claim, invocation), TransportAdmission::Reply(_) => panic!("the door answered instead of admitting"), } diff --git a/crates/claude-connector/src/daemon.rs b/crates/claude-connector/src/daemon.rs index eaf26d0..1a1a80b 100644 --- a/crates/claude-connector/src/daemon.rs +++ b/crates/claude-connector/src/daemon.rs @@ -11,8 +11,8 @@ use std::time::Duration; use cultcache_rs::{CultCache, DatabaseEntry, SingleFileMessagePackBackingStore}; use model_connector::{ - CallerAdmission, CallerConfig, LeaseTurn, MIN_PAYLOAD_BYTES, ProviderBackend, - ServeError, ServeLimits, ServiceError, TransportService, load_admissions, serve_service, + CallerAdmission, CallerConfig, LeaseTurn, MIN_PAYLOAD_BYTES, ProviderBackend, ServeError, + ServeLimits, ServiceError, TransportService, load_admissions, serve_service, }; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -230,9 +230,7 @@ impl ClaudeConnectorConfig { } let mut seen = BTreeSet::new(); for model in &caller.allowed_models { - if !has_model_id_form(model) - || !seen.insert(model.as_str()) - { + if !has_model_id_form(model) || !seen.insert(model.as_str()) { return Err(ConfigError::Invalid("allowed_models")); } } @@ -344,7 +342,6 @@ fn has_model_id_form(model: &str) -> bool { && after_ok } - /// One file name under `$CREDENTIALS_DIRECTORY`, never a path. fn is_credential_name(name: &str) -> bool { !name.is_empty() diff --git a/crates/claude-connector/src/lease.rs b/crates/claude-connector/src/lease.rs index 78b19ac..2974a42 100644 --- a/crates/claude-connector/src/lease.rs +++ b/crates/claude-connector/src/lease.rs @@ -671,9 +671,9 @@ mod tests { fn a_frame_that_trickles_past_its_deadline_is_refused_however_small_each_read() { let (mut reader, mut writer) = UnixStream::pair().unwrap(); let frame = encode_frame(&LeaseMessage::ClaudeLeaseRefusal(LeaseRefused { - kind: LeaseRefusalKind::Busy, - detail: "lease:x".to_string(), - })); + kind: LeaseRefusalKind::Busy, + detail: "lease:x".to_string(), + })); let trickle = thread::spawn(move || { for byte in frame { if writer.write_all(&[byte]).is_err() { diff --git a/crates/claude-connector/src/lib.rs b/crates/claude-connector/src/lib.rs index 4a9a42b..70a2e53 100644 --- a/crates/claude-connector/src/lib.rs +++ b/crates/claude-connector/src/lib.rs @@ -28,8 +28,8 @@ pub use launch::ChildEnv; #[cfg(target_os = "linux")] pub use lease::{ LeaseCaller, LeaseGranted, LeaseLimits, LeaseMessage, LeaseRefusalKind, LeaseRefused, - LeaseRequest, MAX_LEASE_FRAME_BYTES, PeerCredentials, PeerUnit, - PeerUnits, decode_frame, encode_frame, peer_credentials, serve_leases, user_uid, + LeaseRequest, MAX_LEASE_FRAME_BYTES, PeerCredentials, PeerUnit, PeerUnits, decode_frame, + encode_frame, peer_credentials, serve_leases, user_uid, }; #[cfg(unix)] pub use process::StdProcessPort; diff --git a/crates/claude-connector/tests/config_document.rs b/crates/claude-connector/tests/config_document.rs index 865ea00..d2af150 100644 --- a/crates/claude-connector/tests/config_document.rs +++ b/crates/claude-connector/tests/config_document.rs @@ -280,6 +280,9 @@ fn the_dollar_flags_are_not_flags() { let scratch = Scratch::new(); let made = init(&scratch, &init_flags(&scratch, extra)); assert!(!made.status.success(), "{extra:?} was accepted"); - assert!(!Path::new(&scratch.path("connector.cc")).exists(), "{extra:?}"); + assert!( + !Path::new(&scratch.path("connector.cc")).exists(), + "{extra:?}" + ); } } diff --git a/crates/claude-connector/tests/lease.rs b/crates/claude-connector/tests/lease.rs index ce52048..4bd3941 100644 --- a/crates/claude-connector/tests/lease.rs +++ b/crates/claude-connector/tests/lease.rs @@ -279,7 +279,12 @@ fn start(setup: Setup) -> Harness { }); let quota = Arc::new(QuotaCell::new()); let listener = UnixListener::bind(&socket).unwrap(); - let serving = (service.clone(), quota.clone(), tokens.clone(), units.clone()); + let serving = ( + service.clone(), + quota.clone(), + tokens.clone(), + units.clone(), + ); let caller = LeaseCaller { uid: setup.uid, caller_id: CALLER.to_string(), @@ -343,7 +348,10 @@ fn ask_until_granted(harness: &Harness, first_invocation: u64) -> Answer { fn a_grant_passes_one_fd_holding_the_token_then_end_of_file() { let harness = start(setup()); let mut answer = ask(&harness, 60_000); - assert_eq!(granted(&answer).lease_id, format!("lease:{}", invocation(1))); + assert_eq!( + granted(&answer).lease_id, + format!("lease:{}", invocation(1)) + ); assert_eq!(answer.fds.len(), 1); let mut read = Vec::new(); File::from(answer.fds.remove(0)) @@ -351,7 +359,10 @@ fn a_grant_passes_one_fd_holding_the_token_then_end_of_file() { .unwrap(); assert_eq!(read, TOKEN.as_bytes()); assert_eq!(harness.tokens.reads.load(Ordering::SeqCst), 1); - assert!(!contains(&answer.raw, TOKEN), "the token is in the pipe only"); + assert!( + !contains(&answer.raw, TOKEN), + "the token is in the pipe only" + ); } #[test] @@ -416,7 +427,10 @@ fn a_rejected_window_refuses_the_lease_until_its_reset() { .quota .observe([window("five_hour", 1_000, "rejected", reset)]); let refused = ask(&harness, 60_000); - assert_eq!(refusal_of(&refused), Some(LeaseRefusalKind::ProviderRejected)); + assert_eq!( + refusal_of(&refused), + Some(LeaseRefusalKind::ProviderRejected) + ); assert!(refused.fds.is_empty()); assert_eq!(harness.tokens.reads.load(Ordering::SeqCst), 0); // At the provider's own reset the refusal lifts, with no one lifting it. diff --git a/crates/model-connector/src/contract.rs b/crates/model-connector/src/contract.rs index d039774..0a83d4e 100644 --- a/crates/model-connector/src/contract.rs +++ b/crates/model-connector/src/contract.rs @@ -762,8 +762,15 @@ mod tests { #[test] fn the_invocations_identity_covers_its_request() { let one = invocation(); - let mut other = one.clone(); - other.request.max_output_tokens += 1; + let mut request = one.request.clone(); + request.max_output_tokens += 1; + let other = TransportInvocation::new( + one.caller_runtime_id.clone(), + one.expires_at_unix_ms, + one.native_request_sha256, + request, + ) + .unwrap(); assert_ne!( canonical_invocation_bytes(&one).unwrap(), canonical_invocation_bytes(&other).unwrap() diff --git a/crates/model-connector/src/fixtures.rs b/crates/model-connector/src/fixtures.rs index 1bfc37a..19ab522 100644 --- a/crates/model-connector/src/fixtures.rs +++ b/crates/model-connector/src/fixtures.rs @@ -67,14 +67,6 @@ impl FlakyStore { pub(crate) fn allowance(&self) -> Arc { self.pushes_allowed.clone() } - - /// A store over the same records that accepts `pushes_allowed` more writes. - pub(crate) fn reopened(&self, pushes_allowed: usize) -> Self { - Self { - held: self.held.clone(), - pushes_allowed: Arc::new(AtomicUsize::new(pushes_allowed)), - } - } } #[cfg(feature = "daemon")] @@ -133,7 +125,6 @@ pub(crate) fn request() -> ProviderRequest { request } - pub(crate) fn invocation() -> TransportInvocation { TransportInvocation::new("epiphany-yggdrasil", 2_000, [7; 32], request()).unwrap() } diff --git a/crates/model-connector/src/readout.rs b/crates/model-connector/src/readout.rs index 197e65a..5efa324 100644 --- a/crates/model-connector/src/readout.rs +++ b/crates/model-connector/src/readout.rs @@ -9,7 +9,6 @@ use crate::contract::ContractError; pub const USAGE_READOUT_SCHEMA_ID: &str = "gamecult.model.usage_readout.v1"; - /// One window of the subscription plan, as the provider reported it. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[serde(deny_unknown_fields)] diff --git a/crates/model-connector/src/serve.rs b/crates/model-connector/src/serve.rs index 57edfa9..2c2ac91 100644 --- a/crates/model-connector/src/serve.rs +++ b/crates/model-connector/src/serve.rs @@ -382,10 +382,7 @@ mod tests { FailureKind, Refusal, TransportDisposition, TransportEvent, TransportEventPayload, TransportInvocation, TransportOutcome, TransportResult, }; - use crate::fixtures::{ - TestStore, caller, invocation_for, receipt, - request, - }; + use crate::fixtures::{TestStore, caller, invocation_for, receipt, request}; const CALLER: &str = "epiphany-yggdrasil"; const KEY: &str = "epiphany-distinct-test-key"; @@ -461,13 +458,7 @@ mod tests { let (sender, receiver) = std::sync::mpsc::channel(); let path = path.to_path_buf(); thread::spawn(move || { - let _ = sender.send(serve( - listener, - limits, - admissions, - &path, - backend, - )); + let _ = sender.send(serve(listener, limits, admissions, &path, backend)); }); receiver.recv_timeout(Duration::from_secs(2)).ok() } @@ -493,15 +484,7 @@ mod tests { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); let endpoint = listener.local_addr().unwrap(); let path = store.path.clone(); - thread::spawn(move || { - serve( - listener, - limits(), - admissions, - &path, - backend, - ) - }); + thread::spawn(move || serve(listener, limits(), admissions, &path, backend)); let client = ConnectorClient::new(endpoint, KEY, FRAME, Some(Duration::from_secs(5))).unwrap(); (client, endpoint) @@ -770,10 +753,9 @@ mod tests { let envelope = crate::envelope::encrypt_invocation(&invocation, &crate::fixtures::security(KEY)) .unwrap(); - let TransportAdmission::Execute(claim) = - service - .begin(&envelope, now_ms(), &PlanQuotaSnapshot::default(), 1 << 20) - .unwrap() + let TransportAdmission::Execute(claim) = service + .begin(&envelope, now_ms(), &PlanQuotaSnapshot::default(), 1 << 20) + .unwrap() else { panic!("a fresh request must execute"); }; @@ -881,12 +863,7 @@ mod tests { fn the_smallest_frame_also_carries_the_failure_that_stands_in_for_a_result() { let long = "x".repeat(crate::envelope::MAX_IDENTITY_BYTES); let plaintext = crate::contract::wire_encode(&crate::service::result_size_failure( - &long, - &long, - &long, - [0xff; 32], - [0xff; 32], - 0, + &long, &long, &long, [0xff; 32], [0xff; 32], 0, )) .unwrap() .len(); @@ -994,16 +971,7 @@ mod tests { } fn admission(id: &str, key: &str, slots: usize, payload: usize) -> CallerAdmission { - CallerAdmission::new( - id, - key, - 1, - ["gpt-5.4".to_string()], - slots, - payload, - 32_768, - ) - .unwrap() + CallerAdmission::new(id, key, 1, ["gpt-5.4".to_string()], slots, payload, 32_768).unwrap() } /// `Some(field)` when serve refused the configuration, `None` while it serves. @@ -1233,10 +1201,9 @@ mod tests { &crate::fixtures::security(KEY), ) .unwrap(); - let TransportAdmission::Execute(claim) = - service - .begin(&envelope, now_ms(), &PlanQuotaSnapshot::default(), 1 << 20) - .unwrap() + let TransportAdmission::Execute(claim) = service + .begin(&envelope, now_ms(), &PlanQuotaSnapshot::default(), 1 << 20) + .unwrap() else { panic!("a fresh request must execute"); }; @@ -1385,15 +1352,7 @@ mod tests { let backend = CountingBackend::new(true); let path = store.path.clone(); let served = backend.clone(); - thread::spawn(move || { - serve( - listener, - limits(), - admissions, - &path, - served, - ) - }); + thread::spawn(move || serve(listener, limits(), admissions, &path, served)); let client = ConnectorClient::new(endpoint, KEY, FRAME, Some(Duration::from_secs(5))).unwrap(); client.execute(&fresh("request-key")).unwrap(); diff --git a/crates/model-connector/src/service.rs b/crates/model-connector/src/service.rs index c17802d..5fe2db8 100644 --- a/crates/model-connector/src/service.rs +++ b/crates/model-connector/src/service.rs @@ -524,7 +524,8 @@ impl TransportService { .get::(&replay_key(&identity)) .map_err(replay_store_error)? .ok_or(ServiceError::NoActiveRequest)?; - if active.state != ReplayState::Active || active.invocation_sha256 != claim.invocation_sha256 + if active.state != ReplayState::Active + || active.invocation_sha256 != claim.invocation_sha256 { return Err(ServiceError::ActiveRequestMismatch); } @@ -897,13 +898,9 @@ mod tests { let mut changed_request = request(); changed_request.instructions.push_str(" Different cargo."); - let conflicting = TransportInvocation::new( - "epiphany-yggdrasil", - 2_000, - [7; 32], - changed_request, - ) - .unwrap(); + let conflicting = + TransportInvocation::new("epiphany-yggdrasil", 2_000, [7; 32], changed_request) + .unwrap(); let refusal = match service .begin_unplanned( &encrypt_invocation(&conflicting, &security).unwrap(), @@ -932,7 +929,9 @@ mod tests { let mut service = open_service(&store, 1); let first_envelope = encrypt_invocation(&first, &security).unwrap(); assert!(matches!( - service.begin_unplanned(&first_envelope, 1_000, FRAME).unwrap(), + service + .begin_unplanned(&first_envelope, 1_000, FRAME) + .unwrap(), TransportAdmission::Execute(_) )); @@ -1035,10 +1034,12 @@ mod tests { ) -> (TransportAdmission, TransportInvocation) { let mut request = invocation_for(CALLER, request_id).request; edit(&mut request); - let invocation = - TransportInvocation::new(CALLER, 2_000, [7; 32], request).unwrap(); + let invocation = TransportInvocation::new(CALLER, 2_000, [7; 32], request).unwrap(); let envelope = encrypt_invocation(&invocation, &security(KEY)).unwrap(); - (service.begin_unplanned(&envelope, 1_000, FRAME).unwrap(), invocation) + ( + service.begin_unplanned(&envelope, 1_000, FRAME).unwrap(), + invocation, + ) } fn refusal_of(admission: TransportAdmission, invocation: &TransportInvocation) -> Refusal { @@ -1118,14 +1119,9 @@ mod tests { 32_768, ) .unwrap(); - TransportService::open( - &store.path, - "fixture-sse", - [admission], - 5_000, - ) - .unwrap() - .begin_unplanned(&envelope, 1_000, FRAME) + TransportService::open(&store.path, "fixture-sse", [admission], 5_000) + .unwrap() + .begin_unplanned(&envelope, 1_000, FRAME) }; assert!(matches!(admit(size), Ok(TransportAdmission::Execute(_)))); let refused = admit(size - 1).unwrap(); @@ -1272,15 +1268,7 @@ mod tests { #[test] fn identities_are_bounded_at_admission_and_on_the_request() { let admit = |caller_id: String, model: String| { - CallerAdmission::new( - caller_id, - KEY, - 1, - [model], - 4, - 64 * 1024, - 32_768, - ) + CallerAdmission::new(caller_id, KEY, 1, [model], 4, 64 * 1024, 32_768) }; let at = "i".repeat(MAX_IDENTITY_BYTES); let over = "i".repeat(MAX_IDENTITY_BYTES + 1); @@ -1298,7 +1286,9 @@ mod tests { let mut service = open_service(&store, 8); let ok = invocation_for(CALLER, &at); assert!(matches!( - service.begin_unplanned(&raw_envelope(&ok), 1_000, FRAME).unwrap(), + service + .begin_unplanned(&raw_envelope(&ok), 1_000, FRAME) + .unwrap(), TransportAdmission::Execute(_) )); } @@ -1315,8 +1305,9 @@ mod tests { let mut service = open_service(&store, 1); let id = "i".repeat(length); let long = invocation_for(CALLER, &id); - let TransportAdmission::Reply(reply) = - service.begin_unplanned(&raw_envelope(&long), 1_000, FRAME).unwrap() + let TransportAdmission::Reply(reply) = service + .begin_unplanned(&raw_envelope(&long), 1_000, FRAME) + .unwrap() else { panic!("an over-long id must be refused, not executed ({length})"); }; @@ -1353,7 +1344,9 @@ mod tests { let store = TestStore::new(); let mut service = open_service(&store, 1); assert!(matches!( - service.begin_unplanned(&raw_envelope(&long), 1_000, frame_cap).unwrap(), + service + .begin_unplanned(&raw_envelope(&long), 1_000, frame_cap) + .unwrap(), TransportAdmission::Reply(_) )); assert_eq!( @@ -1543,7 +1536,9 @@ mod tests { let big = text_result(claim.call(), "y"); service.complete(*claim, big, FRAME).unwrap(); assert_eq!( - service.begin_unplanned(&raw_envelope(&invocation), 1_000, 10).err(), + service + .begin_unplanned(&raw_envelope(&invocation), 1_000, 10) + .err(), Some(ServiceError::FrameBound) ); } @@ -1758,25 +1753,15 @@ mod tests { for tag in ["", " ", STAND_IN_PROVIDER] { assert!( matches!( - TransportService::open( - &store.path, - tag, - [caller(CALLER, KEY, 1)], - 5_000 - ), + TransportService::open(&store.path, tag, [caller(CALLER, KEY, 1)], 5_000), Err(ServiceError::InvalidAdmission) ), "{tag:?}" ); } assert!( - TransportService::open( - &store.path, - "fixture-sse", - [caller(CALLER, KEY, 1)], - 5_000 - ) - .is_ok() + TransportService::open(&store.path, "fixture-sse", [caller(CALLER, KEY, 1)], 5_000) + .is_ok() ); } @@ -2060,12 +2045,7 @@ mod tests { .unwrap(); } assert!(matches!( - TransportService::open( - &store.path, - "fixture-sse", - [caller(CALLER, KEY, 1)], - 5_000 - ), + TransportService::open(&store.path, "fixture-sse", [caller(CALLER, KEY, 1)], 5_000), Err(ServiceError::ReplayStore(_)) )); } diff --git a/crates/model-connector/src/service/lease.rs b/crates/model-connector/src/service/lease.rs index 0e2c37e..5d55bf3 100644 --- a/crates/model-connector/src/service/lease.rs +++ b/crates/model-connector/src/service/lease.rs @@ -128,16 +128,10 @@ mod tests { TransportService::open( &store.path, "fixture-sse", - [CallerAdmission::new( - CALLER, - KEY, - 1, - ["gpt-5.4".to_string()], - 1, - 65_536, - 32_768, - ) - .unwrap()], + [ + CallerAdmission::new(CALLER, KEY, 1, ["gpt-5.4".to_string()], 1, 65_536, 32_768) + .unwrap(), + ], 5_000, ) .unwrap() @@ -185,7 +179,10 @@ mod tests { fn a_lease_is_named_by_its_invocation() { let store = TestStore::new(); let mut service = open(&store); - assert_eq!(granted(lease(&mut service, "run-1")).lease_id, "lease:run-1"); + assert_eq!( + granted(lease(&mut service, "run-1")).lease_id, + "lease:run-1" + ); } #[test] @@ -231,14 +228,28 @@ mod tests { ); assert_eq!( service - .begin_lease(CALLER, LeaseTurn::SelfRun, "run-2", NOW + 99, NOW + 9, &none()) + .begin_lease( + CALLER, + LeaseTurn::SelfRun, + "run-2", + NOW + 99, + NOW + 9, + &none() + ) .unwrap(), LeaseAdmission::Refused(LeaseRefusal::Busy), "one millisecond before its expiry the lease still holds" ); granted( service - .begin_lease(CALLER, LeaseTurn::SelfRun, "run-2", NOW + 99, NOW + 10, &none()) + .begin_lease( + CALLER, + LeaseTurn::SelfRun, + "run-2", + NOW + 99, + NOW + 10, + &none(), + ) .unwrap(), ); } @@ -249,7 +260,14 @@ mod tests { let mut service = open(&store); assert_eq!( service - .begin_lease("stranger", LeaseTurn::SelfRun, "run-1", NOW + 1, NOW, &none()) + .begin_lease( + "stranger", + LeaseTurn::SelfRun, + "run-1", + NOW + 1, + NOW, + &none() + ) .unwrap(), LeaseAdmission::Refused(LeaseRefusal::UnknownCaller) ); @@ -286,8 +304,7 @@ mod tests { let call = |service: &mut TransportService, now: u64, plan: &PlanQuotaSnapshot| { let mut asked = request(); asked.request_id = format!("call-{now}"); - let invocation = - TransportInvocation::new(CALLER, now + 1_000, [7; 32], asked).unwrap(); + let invocation = TransportInvocation::new(CALLER, now + 1_000, [7; 32], asked).unwrap(); let sealed = crate::envelope::encrypt_message( CALLER, invocation.request_id(), @@ -301,7 +318,8 @@ mod tests { else { return None; }; - let result = crate::envelope::decrypt_result(&reply, &security(KEY), &invocation).unwrap(); + let result = + crate::envelope::decrypt_result(&reply, &security(KEY), &invocation).unwrap(); match result.disposition { TransportDisposition::Refused(refusal) => Some(refusal), other => panic!("unexpected {other:?}"), @@ -361,7 +379,12 @@ mod tests { let store = TestStore::new(); let mut service = open(&store); let longest = "r".repeat(MAX_IDENTITY_BYTES - LEASE_PREFIX.len() - PERSONA_SUFFIX.len()); - let grant = granted(lease_in(&mut service, LeaseTurn::PersonaTurn, &longest, &none())); + let grant = granted(lease_in( + &mut service, + LeaseTurn::PersonaTurn, + &longest, + &none(), + )); assert_eq!(grant.lease_id.len(), MAX_IDENTITY_BYTES); assert!(lease_try(&mut service, LeaseTurn::PersonaTurn, &format!("{longest}r")).is_err()); } diff --git a/crates/model-connector/tests/fixtures/wire_v4/carriage_invocation.frame b/crates/model-connector/tests/fixtures/wire_v4/carriage_invocation.frame index b531c71..355d501 100644 --- a/crates/model-connector/tests/fixtures/wire_v4/carriage_invocation.frame +++ b/crates/model-connector/tests/fixtures/wire_v4/carriage_invocation.frame @@ -1 +1 @@ -‡©messageId®golden-request©operation¦invoke§payloadÚDhqlzY2hlbWFfaWTZJGdhbWVjdWx0Lm1vZGVsLnRyYW5zcG9ydF9lbnZlbG9wZS52M7FjYWxsZXJfcnVudGltZV9pZK1nb2xkZW4tY2FsbGVyqnJlcXVlc3RfaWSuZ29sZGVuLXJlcXVlc3SsbWVzc2FnZV9raW5kqkludm9jYXRpb26lbm9uY2WczO7MxRrMxw7MhTh7SMzwzNB2qmNpcGhlcnRleHTFA0d65EskLG6kIwJ/G+XDK03gZOGtLFi6vZHJbI+/KpRLwnLT0TPF7RQa0KD98CrW+VwkKY3sRa4+Z9+/5OdPq1YmDFwbxvMVjZsFjXXw84tzvuLKHsb/3kQSEvAlFE4K6FXHvznLcMo4CnGUzoXwNGALf+xDTqFG0aFbdG4xGynV/kttsL6f34F4+l6xiXMUy87Q8/1UyOK/YaVxMMmlT3tdTllhq8YU8Dvfq0KuMaj/f9Anya8A5bJwlxafZPtc53RoH1/oNjLdaDqvwrTEnTB9TvKlnEOxKyyKhmmwGaC03qqPJXRaj8cDQO6EHDbML4J0zMN8TvssQISd5UCeVMkvDGjmvEOmRUoPk1/SnSrIHdha4+6rU4lUSumloLmlx5vv++Mq4AqzOdd/vPB6Ov/AoF32X27o4RI6H3xCKKHjKC4m3+cVjXCrOcGojuRf9yeDFkgbiPaC31hmSc8rAm6HO/NcaIHvx+G+6AaurAEhDLdF6DdrXb4xwjuv6yWQKihNvLXpnwmhRFkSeD5lS6FHW+CoOIdq/RBnFFyDrboTPWFoxQYIrBuqFoHDOajIz7dXYif0sf9sB9JBrDhlcbTpiGcyURfzDhW55NOPqKFGi6ropaJ1/SyLP3f7E57kA22aQ2Op7o36Tu06Pnzo2wI7UsL/Qz27F+AGAukQCvTJyjxARiGr3TRpChjVXx6GXhLrG1T9P+Mrhyrt9vwwhboN8qz1uvDZwKhGWBKXPXkxluq/NY1DwB3JTCCZfEwukWjMZbqiINLEKvW9BStcBR1kfXcK6sQvn/F0g9r81e9brbe+w3U24GERnHwgruOqytTeax/xPbQ7uISV48g5L8DAYf788KhUXwI43QDHNBdBUyo0rCsgoWJKeO05fcAgKgYYGsyLtMW10Vstw3q4POyn3o7rrBz4S2cnl2iZgr0ixg/KgF0V+loEVuj/6rYrcW0SLCHwV1+w1ClFMn0ZPsrz28EoZMA617G0Rrch66cS5uI9/Zf/wLX3z6n7xoPLNXk51ZsLSZ2bqEQkcBJbW/dT5MdH6anWVogRDvIW1coUbkIq9l+pkQSuelWsfakX4UQwYuUD0UwNbqxHiGwlEFj0f1bdhEj26Q==¯payloadEncoding²messagepack-base64­payloadSchemaÙ$gamecult.model.transport_envelope.v3­schemaVersion¼cultnet.operation_request.v0©serviceId¸gamecult.model.connector \ No newline at end of file +‡©messageId®golden-request©operation¦invoke§payloadÚ(hqlzY2hlbWFfaWTZJGdhbWVjdWx0Lm1vZGVsLnRyYW5zcG9ydF9lbnZlbG9wZS52NLFjYWxsZXJfcnVudGltZV9pZK1nb2xkZW4tY2FsbGVyqnJlcXVlc3RfaWSuZ29sZGVuLXJlcXVlc3SsbWVzc2FnZV9raW5kqkludm9jYXRpb26lbm9uY2WcXSbMzU3MhsyCzLAtzKLMhcy8zMSqY2lwaGVydGV4dMUDMY816tn0FGQ9jqxE+KyDVcxmhufaQI7vzEXO8NXTQPOYpRuMcRDM/j/QzNF2Lb3Fak0G+hHRKrXDxJ3MeKpoELNYOEyxbKfxn88mjAyyc7BVPM7+0Bk9ye3m5oBF6hoyCgOHSTwESIfq0bim+9oQh1IDp+HdFjztK/XjM731GRzKJmGgxsKqQsz42zq8y9mjAXl3V1RR9ssdKvk2k5U56/3liOeHDU96kOa7IlVCBm6qnn06hg2DTtnBF4xGj6PihHj4KufVkAcuQhRCZWOKsploToXo7wy+jcQDrnhHIUCMN6sk3P9YJh1ejuleDM9rp3RYiXcnzRwMNXK1AGpNraTgWn91d6L/1Ff+gaQBgPxSjW6FATshJf9Iqk9pEhT8xeCQMmNj40CdlVFYNz/TsRJYW45BXm8g83Jnh3Wj0Z90WJ80bwwgZbjPWx6wCOCID6Jo8wiubF6411ygi+GUHTR9rZFhrsGo6TSmsbvIAuabUJcskqKHW7tqVkfpz5JdjyeSSDvKcZ3PQyVne7Lyo8nzNh0g06a7CEUvq9kvr4zfTCfwk/1YVSrZ+7U8ePGj27V3D8+BH/pafiLR5FxxBKWBTYe7n5irVP5gLw4WlzOrSriXpolmaPWujCm3apml45mhlYARdfJJbpQxnQGb6cI3D/vAxxI50BPLB82JqCjNFex9xwfP5jSRDYah9wLm4yVymBqZ8Wvn0maLz4OvF2EonJBwEtN73UBULkdeX6g9GTiu2MKtCjSMm33SkwiHfvDdwMDS11sINK+YQLF/3HobbnAk2n963JahlddNRg79T96GksjN0br8u1PNMC+/uBUHEQvUBfRnR4tKp+SeLF6zMn228orgHgWr7sxilMwTOP1DgXpbEE19M0P8XC0CA/pTeHr4eFHzakPLotxF8avQfPKAXbXWgNGK6uWj1NGPshD6EN3pLvePOc4uObqHMKz/dg52/7m1VTHhYMivSRenG6H/BrWfUXQvcgCPalg7C4KCPYINb3nnRxP0NIOBP+KJ4S2haU94ZNHVr2HB038xNzUm2HrONVBsxuuTckYDmHfCa6FmvVJv4she5wyuPHk=¯payloadEncoding²messagepack-base64­payloadSchemaÙ$gamecult.model.transport_envelope.v4­schemaVersion¼cultnet.operation_request.v0©serviceId¸gamecult.model.connector \ No newline at end of file diff --git a/crates/model-connector/tests/fixtures/wire_v4/provider_request.mp b/crates/model-connector/tests/fixtures/wire_v4/provider_request.mp index 9ff75e4..26d45cb 100644 Binary files a/crates/model-connector/tests/fixtures/wire_v4/provider_request.mp and b/crates/model-connector/tests/fixtures/wire_v4/provider_request.mp differ diff --git a/crates/model-connector/tests/fixtures/wire_v4/transport_envelope.mp b/crates/model-connector/tests/fixtures/wire_v4/transport_envelope.mp index cc0d7fe..9b361fa 100644 Binary files a/crates/model-connector/tests/fixtures/wire_v4/transport_envelope.mp and b/crates/model-connector/tests/fixtures/wire_v4/transport_envelope.mp differ diff --git a/crates/model-connector/tests/fixtures/wire_v4/transport_invocation.mp b/crates/model-connector/tests/fixtures/wire_v4/transport_invocation.mp index dff0ca2..4981058 100644 Binary files a/crates/model-connector/tests/fixtures/wire_v4/transport_invocation.mp and b/crates/model-connector/tests/fixtures/wire_v4/transport_invocation.mp differ diff --git a/crates/model-connector/tests/fixtures/wire_v4/transport_receipt_failed.mp b/crates/model-connector/tests/fixtures/wire_v4/transport_receipt_failed.mp index 385a318..2a6635f 100644 Binary files a/crates/model-connector/tests/fixtures/wire_v4/transport_receipt_failed.mp and b/crates/model-connector/tests/fixtures/wire_v4/transport_receipt_failed.mp differ diff --git a/crates/model-connector/tests/fixtures/wire_v4/transport_result_refused.mp b/crates/model-connector/tests/fixtures/wire_v4/transport_result_refused.mp index 0c4f7b1..3bd43b9 100644 --- a/crates/model-connector/tests/fixtures/wire_v4/transport_result_refused.mp +++ b/crates/model-connector/tests/fixtures/wire_v4/transport_result_refused.mp @@ -1 +1 @@ -„©schema_idÙ"gamecult.model.transport_result.v3ªrequest_id®golden-request±caller_runtime_id­golden-caller«disposition�§Refused«OutputBound \ No newline at end of file +„©schema_idÙ"gamecult.model.transport_result.v4ªrequest_id®golden-request±caller_runtime_id­golden-caller«disposition�§Refused«OutputBound \ No newline at end of file diff --git a/crates/model-connector/tests/fixtures/wire_v4/transport_result_transported.mp b/crates/model-connector/tests/fixtures/wire_v4/transport_result_transported.mp index 79e6ebd..8ccef86 100644 Binary files a/crates/model-connector/tests/fixtures/wire_v4/transport_result_transported.mp and b/crates/model-connector/tests/fixtures/wire_v4/transport_result_transported.mp differ diff --git a/schemas/transport_invocation.json b/schemas/transport_invocation.json index 7c32147..5435b2e 100644 --- a/schemas/transport_invocation.json +++ b/schemas/transport_invocation.json @@ -11,12 +11,6 @@ "format": "uint64", "minimum": 0 }, - "max_cost_micro_usd": { - "description": "The most this request may cost, in micro-USD: the caller's own bound\non it. Required nonzero. It is part of the invocation's identity, so a\nchanged budget under a recorded request id is a `ReplayConflict`.", - "type": "integer", - "format": "uint64", - "minimum": 0 - }, "native_request_sha256": { "type": "array", "items": { @@ -53,7 +47,6 @@ "expires_at_unix_ms", "native_request_sha256", "provider_request_sha256", - "max_cost_micro_usd", "request" ], "$defs": { diff --git a/schemas/transport_receipt.json b/schemas/transport_receipt.json index 01ff920..0011d79 100644 --- a/schemas/transport_receipt.json +++ b/schemas/transport_receipt.json @@ -6,12 +6,6 @@ "caller_runtime_id": { "type": "string" }, - "cost_micro_usd": { - "description": "What the request cost, in micro-USD. The service derives it from the\noutcome's usage and its price table; a backend never writes it.", - "type": "integer", - "format": "uint64", - "minimum": 0 - }, "foreign_reasoning_dropped": { "description": "How many foreign `ReasoningState` items the service removed from the\nrequest before the backend saw it.", "type": "integer", @@ -35,12 +29,6 @@ "outcome": { "$ref": "#/$defs/TransportOutcome" }, - "overrun_micro_usd": { - "description": "How far `cost_micro_usd` exceeds what the service reserved for the\nrequest: zero unless the backend reported usage above the worst case\nthe request allowed, which is a breach of the backend's contract. The\ncost is still the true one and the caller's window is charged all of\nit. The service derives this; a backend never writes it.", - "type": "integer", - "format": "uint64", - "minimum": 0 - }, "provider": { "type": "string" }, @@ -72,8 +60,6 @@ "model", "provider", "foreign_reasoning_dropped", - "cost_micro_usd", - "overrun_micro_usd", "outcome" ], "$defs": { @@ -174,7 +160,7 @@ ] }, "Usage": { - "description": "Token accounting for one request, as the backend reports it; the service\nprices it. Cache reads and writes are counted apart from plain input.\n`output_tokens` includes reasoning tokens: `reasoning_tokens` is a detail\n(absent when the provider does not report it) and is never priced apart.", + "description": "Token accounting for one request, as the backend reports it. Cache reads\nand writes are counted apart from plain input.\n`output_tokens` includes reasoning tokens: `reasoning_tokens` is a detail\n(absent when the provider does not report it) and is never counted apart.", "type": "object", "properties": { "cache_read_tokens": { diff --git a/schemas/transport_result.json b/schemas/transport_result.json index 39e010f..4ba4597 100644 --- a/schemas/transport_result.json +++ b/schemas/transport_result.json @@ -46,15 +46,13 @@ "ModelNotAllowed", "OutputBound", "PayloadBound", - "CostBound", - "SpendCeiling", "Capacity", "InFlight", "Indeterminate", "ReplayConflict", "Malformed", "ForeignReasoningState", - "ClockBehindAdmissionWindow" + "ProviderRejected" ] }, "StopReason": { @@ -286,12 +284,6 @@ "caller_runtime_id": { "type": "string" }, - "cost_micro_usd": { - "description": "What the request cost, in micro-USD. The service derives it from the\noutcome's usage and its price table; a backend never writes it.", - "type": "integer", - "format": "uint64", - "minimum": 0 - }, "foreign_reasoning_dropped": { "description": "How many foreign `ReasoningState` items the service removed from the\nrequest before the backend saw it.", "type": "integer", @@ -315,12 +307,6 @@ "outcome": { "$ref": "#/$defs/TransportOutcome" }, - "overrun_micro_usd": { - "description": "How far `cost_micro_usd` exceeds what the service reserved for the\nrequest: zero unless the backend reported usage above the worst case\nthe request allowed, which is a breach of the backend's contract. The\ncost is still the true one and the caller's window is charged all of\nit. The service derives this; a backend never writes it.", - "type": "integer", - "format": "uint64", - "minimum": 0 - }, "provider": { "type": "string" }, @@ -352,13 +338,11 @@ "model", "provider", "foreign_reasoning_dropped", - "cost_micro_usd", - "overrun_micro_usd", "outcome" ] }, "Usage": { - "description": "Token accounting for one request, as the backend reports it; the service\nprices it. Cache reads and writes are counted apart from plain input.\n`output_tokens` includes reasoning tokens: `reasoning_tokens` is a detail\n(absent when the provider does not report it) and is never priced apart.", + "description": "Token accounting for one request, as the backend reports it. Cache reads\nand writes are counted apart from plain input.\n`output_tokens` includes reasoning tokens: `reasoning_tokens` is a detail\n(absent when the provider does not report it) and is never counted apart.", "type": "object", "properties": { "cache_read_tokens": { diff --git a/schemas/usage_readout.json b/schemas/usage_readout.json new file mode 100644 index 0000000..8f321fa --- /dev/null +++ b/schemas/usage_readout.json @@ -0,0 +1,68 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "UsageReadout", + "type": "object", + "properties": { + "at_unix_ms": { + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "plan_quota": { + "type": "array", + "items": { + "$ref": "#/$defs/PlanQuota" + } + }, + "schema_id": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "schema_id", + "at_unix_ms", + "plan_quota" + ], + "$defs": { + "PlanQuota": { + "description": "One window of the subscription plan, as the provider reported it.", + "type": "object", + "properties": { + "observed_at_unix_ms": { + "description": "When this window was last reported. Its age in a readout is the\nreadout's `at_unix_ms` minus this.", + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "resets_at_unix_ms": { + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "status": { + "type": "string" + }, + "utilization_permille": { + "description": "How much of the window was used when it was observed, in thousandths;\n`None` once the window has reset (`status` is then `expired`), because\nthe old reading says nothing about the new window.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0 + }, + "window": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "window", + "resets_at_unix_ms", + "status", + "observed_at_unix_ms" + ] + } + } +}