diff --git a/Cargo.lock b/Cargo.lock index 3ecdc74..1e30f52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -477,7 +477,6 @@ dependencies = [ "cultnet-rs", "huginn-mind", "rmp-serde", - "schemars", "serde", "serde_json", "sha2", @@ -491,6 +490,7 @@ name = "huginn-mind" version = "0.1.0" dependencies = [ "anyhow", + "base64", "chrono", "cultcache-rs", "cultnet-rs", diff --git a/crates/huginn-daemon/Cargo.toml b/crates/huginn-daemon/Cargo.toml index 428588e..5890c67 100644 --- a/crates/huginn-daemon/Cargo.toml +++ b/crates/huginn-daemon/Cargo.toml @@ -24,7 +24,6 @@ chrono = "0.4.44" cultnet-rs = { git = "https://github.com/GameCult/CultLib.git", rev = "6251cbcd37c12ef3f0c1cc50063a03369225b7ac", package = "cultnet-rs" } huginn-mind = { path = "../huginn-mind" } rmp-serde = "1" -schemars = "1" serde = { version = "1", features = ["derive"] } # The vector store's and the embedder's HTTP APIs are the xenos boundary, and # JSON exists in `index/ollama.rs` and `index/qdrant.rs` only. `ureq` 3 with diff --git a/crates/huginn-daemon/src/lib.rs b/crates/huginn-daemon/src/lib.rs index a9fbb9f..90ae5e8 100644 --- a/crates/huginn-daemon/src/lib.rs +++ b/crates/huginn-daemon/src/lib.rs @@ -1,14 +1,12 @@ //! `huginn-daemon`: one process that serves one mind over CultNet. //! -//! The crate owns five things and no rule. `startup` owns "may this process +//! The crate owns four things and no rule. `startup` owns "may this process //! serve this mind", by delegating to `Mind::open` and refusing before //! anything listens. `index` owns when the mind's documents are embedded and //! written to the vector store, off the serving path. `Daemon::handle` owns "which `Mind` method answers this //! request": it dispatches, it asks `Mind::require_instance` for every read -//! that names an instance, and it compares nothing itself. `envelope` owns "is -//! this a request at all": a message it cannot decode is answered with a typed -//! failure and touches no mind. `serve` owns "which session gets which reply" -//! and whether an answer is delivered whole, deferred or refused; `bodies` +//! that names an instance, and it compares nothing itself. `serve` owns "which +//! session gets which reply" and whether an answer is delivered whole, deferred or refused; `bodies` //! owns how long and how much of a deferred answer is held. //! //! Every admission rule, the instance check, the receipt, the derived status @@ -17,7 +15,6 @@ pub mod bodies; pub mod daemon; -pub mod envelope; pub mod index; pub mod serve; @@ -25,5 +22,4 @@ pub mod serve; mod semantic_tests; pub use daemon::{Daemon, Handled, Hits, IndexSink, Search, SearchTicket, runtime_id}; -pub use envelope::{FAILURE_SCHEMA, OperationFailure}; pub use serve::{Options, ServeOptions, parse_options, run, startup}; diff --git a/crates/huginn-daemon/src/semantic_tests.rs b/crates/huginn-daemon/src/semantic_tests.rs index 513f0e9..f72c5b4 100644 --- a/crates/huginn-daemon/src/semantic_tests.rs +++ b/crates/huginn-daemon/src/semantic_tests.rs @@ -20,7 +20,7 @@ use tempfile::TempDir; use crate::daemon::Daemon; use crate::daemon::tests::{CAMPAIGN, INSTANCE, batch, campaign_seed, now, slug}; -use crate::envelope::{decode_response, encode_request}; +use huginn_mind::envelope::{decode_response, encode_request}; use crate::index::fakes::{FakeEmbedder, FakeIndex, Gate}; use crate::index::{Backoff, Described, Hit, WorkerSink, collection_name}; use crate::serve::{ServeOptions, bind, run, schema_registry}; diff --git a/crates/huginn-daemon/src/serve.rs b/crates/huginn-daemon/src/serve.rs index a5efc02..bfc54b8 100644 --- a/crates/huginn-daemon/src/serve.rs +++ b/crates/huginn-daemon/src/serve.rs @@ -79,7 +79,7 @@ use crate::bodies::{ DEFERRED_BUDGET_BYTES, DEFERRED_CHUNK_BYTES, DeferredBodies, MAX_DEFERRED_BODY_BYTES, }; use crate::daemon::{Daemon, Handled, IndexSink, Search, SearchTicket, runtime_id}; -use crate::envelope::{OperationFailure, decode_request, encode_failure, encode_response}; +use huginn_mind::envelope::{OperationFailure, decode_request, encode_failure, encode_response}; use crate::index::{Backoff, Embedder, VectorIndex, WorkerSink}; /// What the loop does when nothing is waiting. A deferred body untouched for @@ -345,7 +345,7 @@ fn encode_or_fail( Err(error) => encode_failure( message_id, operation, - &crate::envelope::OperationFailure { + &OperationFailure { code: "response-not-encodable".into(), message: format!("{error:#}"), }, @@ -548,7 +548,7 @@ mod tests { open_unindexed, seeded_wide, slug, }; use huginn_mind::epiphany_pipeline::{PipelineKind, PipelineRef}; - use crate::envelope::{FAILURE_SCHEMA, decode_response, encode_request}; + use huginn_mind::envelope::{FAILURE_SCHEMA, decode_response, encode_request}; use crate::index::Backoff; use base64::Engine; use base64::engine::general_purpose::STANDARD; @@ -632,7 +632,7 @@ mod tests { assert_eq!(rejected(&reply).1, code); } // The sixth code, `not-an-operation-request`, is the codec's and is - // pinned in `envelope::tests`: `answer` never produces it, because a + // pinned in `huginn_mind::envelope::tests`: `answer` never produces it, because a // message of another family is answered with `Error` instead. let reply = answer(&mut daemon, ®istry, &mut fresh_bodies(), CultNetMessage::Error { error: "hello".into(), code: None, details: None }, now()).reply(); let CultNetMessage::Error { error, .. } = &reply else { panic!("expected an error, got {reply:?}") }; diff --git a/crates/huginn-mind/Cargo.toml b/crates/huginn-mind/Cargo.toml index a2e60d4..ccf4e94 100644 --- a/crates/huginn-mind/Cargo.toml +++ b/crates/huginn-mind/Cargo.toml @@ -15,6 +15,7 @@ publish.workspace = true # daemon's (Cut 10, Cut 11). [dependencies] anyhow = "1" +base64 = "0.22" chrono = "0.4.44" cultcache-rs = { git = "https://github.com/GameCult/CultLib.git", rev = "6251cbcd37c12ef3f0c1cc50063a03369225b7ac", package = "cultcache-rs" } cultnet-rs = { git = "https://github.com/GameCult/CultLib.git", rev = "6251cbcd37c12ef3f0c1cc50063a03369225b7ac", package = "cultnet-rs" } diff --git a/crates/huginn-daemon/src/envelope.rs b/crates/huginn-mind/src/envelope.rs similarity index 93% rename from crates/huginn-daemon/src/envelope.rs rename to crates/huginn-mind/src/envelope.rs index fecbde2..8152a54 100644 --- a/crates/huginn-daemon/src/envelope.rs +++ b/crates/huginn-mind/src/envelope.rs @@ -14,7 +14,7 @@ use anyhow::Result; use base64::Engine; use base64::engine::general_purpose::STANDARD; use cultnet_rs::CultNetMessage; -use huginn_mind::wire::{HuginnMindRequest, HuginnMindResponse, MIND_REQUEST_SCHEMA, MIND_RESPONSE_SCHEMA, MIND_SERVICE_ID}; +use crate::wire::{HuginnMindRequest, HuginnMindResponse, MIND_REQUEST_SCHEMA, MIND_RESPONSE_SCHEMA, MIND_SERVICE_ID}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -151,9 +151,9 @@ pub fn decode_response(message: &CultNetMessage) -> Result<(String, Result MindRefusal { MindRefusal::ForeignInstance { declared: OTHER.into(), mind: INSTANCE.into() } @@ -183,7 +183,7 @@ mod tests { schema_epoch: "epiphany.pipeline.epoch.v2".into(), documents: 0, receipts: 0, - index: huginn_mind::wire::IndexStatus::Current, + index: crate::wire::IndexStatus::Current, }), "accepted", ), @@ -214,7 +214,11 @@ mod tests { /// `Error` instead. #[test] fn a_request_round_trips_and_a_foreign_message_is_not_a_request() { - let request = HuginnMindRequest::Admit(batch(INSTANCE, vec![identity(INSTANCE)])); + let request = HuginnMindRequest::Admit(PipelineAdmissionBatch { + instance: slug(INSTANCE), + provenance: provenance(Faculty::Hands), + documents: vec![instance(INSTANCE)], + }); let message = encode_request("m-3", &request, Some("eureka-state".into())).unwrap(); assert_eq!(decode_request(&message).unwrap(), ("m-3".into(), request)); diff --git a/crates/huginn-mind/src/lib.rs b/crates/huginn-mind/src/lib.rs index 0fd85e7..b791aac 100644 --- a/crates/huginn-mind/src/lib.rs +++ b/crates/huginn-mind/src/lib.rs @@ -14,7 +14,8 @@ //! selection (CultNet's, over rows this crate supplies) through the same //! `docs` the rules use; nothing is stored for it. //! -//! The wire vocabulary is here too, so the daemon and the client share one set +//! The wire vocabulary and its operation envelope (`envelope`) are here too, +//! so the daemon and the client share one set //! of types. //! //! Document shape, bounds, formats and keys are `epiphany-pipeline`'s; the @@ -26,6 +27,7 @@ pub mod admission; mod docs; +pub mod envelope; pub mod index; pub mod mind; mod rows;