-
Notifications
You must be signed in to change notification settings - Fork 4
Move the wire vocabulary into tinymemory-bus, beneath tinymemory-api #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 11 commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
9de5007
chore(workspace): add tinymemory-bus crate to default members
senamakel ad5e3ec
chore(deps): add tinymemory-bus crate to workspace lockfile
senamakel 7cc9a5d
refactor(calls): replace external API types with local re-exports
senamakel 9630cd3
chore(tinymemory-bus): reformat long serde_json::to_value and use sta…
senamakel d3df87b
chore(tinymemory-bus): suppress clippy expect and panic warnings in t…
senamakel f768595
fix(calls): correct example values in module documentation
senamakel 1a0794a
chore(tinymemory-module): add tinymemory-bus as a dev-dependency
senamakel cf9ebed
chore(tinymemory-module): update Cargo.lock and test file
senamakel 4d696f4
docs(tinymemory-bus): add README with crate overview and usage
senamakel c9dd9ca
docs(README): add description for tinymemory-bus crate in project ove…
senamakel 792aa62
feat(tinymemory-bus): add dependency guard comments to Cargo.toml
senamakel 2922ce5
chore(tinymemory-bus): remove unused calls module and its submodules
senamakel 3fbdec7
chore(tinymemory): move source files from tinymemory-api to tinymemor…
senamakel 2c1ac19
chore(tinymemory): move provider types from api crate to bus crate
senamakel 297209a
refactor(provider): move type definitions from api to bus crate
senamakel 4dc1816
chore(tinymemory-bus): rewrite module-level documentation and reorgan…
senamakel 918686d
feat(provider): re-export bus value types in each provider module
senamakel f5eea1d
refactor(tinymemory-api): re-export wire vocabulary from tinymemory-bus
senamakel 3abc985
feat(tinymemory-api): re-export types from tinymemory-bus crate
senamakel b3574ac
feat(tinymemory-bus): replace tinymemory-api dependency with standalo…
senamakel 0692e32
fix(provider): remove unused imports across multiple provider files
senamakel da2629b
chore(provider): remove unused imports
senamakel 0372ee7
fix(evidence): add doc comments to EvidenceRef variant fields
senamakel d490e0c
fix(provider): remove unused SourceKind import
senamakel 1603238
fix: correct test module path in names.rs
senamakel 2d86499
chore(tinymemory): clean up re-exports and whitespace
senamakel 0913ca0
chore(tinymemory-bus): disable pedantic clippy lint to match sibling …
senamakel da9560d
test: add missing test files for tinymemory-bus crate
senamakel 1c6c787
chore: files changed clippy.toml,crates/tinymemory-bus/src/chunks.rs
senamakel 4b2d416
fix: correct outdated crate name in doc examples
senamakel 9e92ae5
chore: remove intra-doc links to types that are no longer re-exported
senamakel 40f7709
chore(deps): update Cargo.lock for tinymemory-module
senamakel 0024ef7
chore(tinymemory-api): remove unused dependencies
senamakel bf97eec
feat(tinymemory-api): clarify module documentation and re-export stru…
senamakel 8612196
docs: clarify tinymemory-bus as the vocabulary crate beneath tinymemo…
senamakel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| [package] | ||
| name = "tinymemory-bus" | ||
| # Not published, for the same reason `tinymemory-api` is not: the graph below it | ||
| # reaches crates that are not on crates.io. A host takes this by git or by path. | ||
| publish = false | ||
| version = "0.1.0" | ||
| edition = "2021" | ||
| rust-version = "1.96" | ||
| license = "MIT" | ||
| repository = "https://github.com/tinyhumansai/tinymemory" | ||
| description = "The TinyBus wire contract for the TinyMemory module: member names, payload types, and typed calls" | ||
|
|
||
| # Three dependencies, and the ceiling is low on purpose. | ||
| # | ||
| # This crate is what a *host* compiles against to talk to the loaded module, so | ||
| # it must cost that host almost nothing: no engine, no storage, no async | ||
| # runtime, and — importantly — no `tinybus`. See `src/lib.rs` for why the | ||
| # transport is deliberately absent, and the root manifest's note on | ||
| # `crates/tinymemory-module` for what depending on the vendored `tinybus` from a | ||
| # workspace member would do to this workspace. | ||
| # | ||
| # Nothing here may pull in `rusqlite`, `git2`, `reqwest`, `regex`, an async | ||
| # runtime, or `tinybus`. Guard with the FORWARD form, which is scoped to this | ||
| # package — `cargo tree -i` discards the `-p` scope and exits clean even when | ||
| # this crate is the one pulling the dependency in: | ||
| # | ||
| # cargo tree -p tinymemory-bus -e normal,build --prefix none \ | ||
| # | grep -Ei 'rusqlite|libsqlite|git2|reqwest|regex|tokio|tinybus' # expect no match | ||
| [dependencies] | ||
| # The single definition of every type on the wire. Re-exported, never | ||
| # redefined — see `src/types/mod.rs`. | ||
| tinymemory-api = { path = "../tinymemory-api" } | ||
| # The call structs derive both halves: `Serialize` to build an argument array, | ||
| # `Deserialize` so a module-side test can decode one back. | ||
| serde = { version = "1", features = ["derive"] } | ||
| # A tinybus frame body is JSON, so an encoded argument list is a | ||
| # `serde_json::Value` and nothing here needs a different representation. | ||
| serde_json = "1" | ||
| thiserror = "2" | ||
|
|
||
| [lints.rust] | ||
| unsafe_code = "forbid" | ||
| missing_docs = "warn" | ||
| missing_debug_implementations = "warn" | ||
| unreachable_pub = "warn" | ||
| rust_2018_idioms = { level = "warn", priority = -1 } | ||
|
|
||
| [lints.clippy] | ||
| all = { level = "warn", priority = -1 } | ||
| pedantic = { level = "warn", priority = -1 } | ||
| unwrap_used = "warn" | ||
| expect_used = "warn" | ||
| panic = "warn" | ||
| todo = "warn" | ||
| unimplemented = "warn" | ||
| missing_errors_doc = "warn" | ||
| missing_panics_doc = "warn" | ||
| doc_markdown = "warn" | ||
|
|
||
| [lints.rustdoc] | ||
| broken_intra_doc_links = "warn" | ||
| private_intra_doc_links = "warn" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| # tinymemory-bus | ||
|
|
||
| The wire contract for the TinyMemory `TinyBus` module, as a library a host | ||
| links. | ||
|
|
||
| TinyMemory ships as a loadable module so a host does not compile the engine. | ||
| `crates/tinymemory-module` exports one object with 89 members on it, and it | ||
| ships as a `cdylib` — a host can load it, but it cannot `use` anything out of | ||
| it. This crate is what the host compiles against instead: | ||
|
|
||
| | module | what it holds | | ||
| | -------- | -------------------------------------------------------------- | | ||
| | `names` | the bus name, the object path, one constant per member | | ||
| | `types` | every value type that crosses a frame | | ||
| | `calls` | one struct per member: arguments in wire order, plus reply type | | ||
| | `wire` | the error names, and the mapping back to `MemoryError` | | ||
|
|
||
| Four dependencies, none of them heavy: `tinymemory-api` for the types, `serde` | ||
| and `serde_json` for the encoding, `thiserror` for one small error enum. No | ||
| engine, no storage, no async runtime — and no `tinybus`. | ||
|
|
||
| ## Why the types are re-exported, not defined | ||
|
|
||
| The obvious reading of "a crate that holds the bus types" is a crate that | ||
| *defines* them. That would be a mistake, and the repository has already made | ||
| the equivalent one once: when `tinymemory-api` was resolved twice, by git and | ||
| by path, `MemoryCategory` from one copy was not the same type as | ||
| `MemoryCategory` from the other, and the mismatch only surfaced at the seam. | ||
| The root `Cargo.toml`'s `[patch]` table exists to prevent exactly that. | ||
|
|
||
| Defining structurally identical types here would reproduce it deliberately: the | ||
| module would serve `tinymemory_api::` types, the host would hold | ||
| `tinymemory_bus::` ones, and every call site would need a conversion whose | ||
| correctness nothing checks. So there is one definition, in `tinymemory-api`, | ||
| surfaced here. A host gets the types the module serves — the same types, not | ||
| equivalents. | ||
|
|
||
| ## Why not just depend on `tinymemory-api` | ||
|
|
||
| It would compile. But `tinymemory-api` is the **driver** contract: it also | ||
| carries `MemoryProvider` and its eighteen capability traits, the | ||
| mandatory-family composition, the null driver, and the `host::` config sections | ||
| a host persists in `config.toml`. A host that loads the module implements none | ||
| of that — it makes calls. | ||
|
|
||
| This crate is the subset that crosses a frame. What a host compiles against is | ||
| what it can actually send and receive, and a trait method that is not exported | ||
| on the bus is absent here rather than tempting. | ||
|
|
||
| ## Why arguments get a struct | ||
|
|
||
| `#[tinybus::interface]` puts a method's arguments on the wire as a positional | ||
| JSON array, decoded into a tuple on the far side. That is a fine encoding and a | ||
| bad thing to write by hand. `Store` takes six arguments: | ||
|
|
||
| ```json | ||
| ["work", "standup", "…", "core", null, "internal"] | ||
| ``` | ||
|
|
||
| Two are `Option`s, two are enums that serialize as strings, and swapping | ||
| `namespace` with `key` produces a call that succeeds and writes the entry to the | ||
| wrong place. Nothing on the module side can catch it — both are `String`, in | ||
| the right position count, and the engine has no way to know which one the caller | ||
| meant. | ||
|
|
||
| So a caller fills in named fields and `BusCall::into_args` does the positioning. | ||
| The reply type travels with the call for the same reason: `Get` answers | ||
| `Option<MemoryEntry>` and `Forget` answers `bool`, both are perfectly good JSON, | ||
| and decoding one as the other fails somewhere far from the call. | ||
|
|
||
| ## There is no client here | ||
|
|
||
| This crate holds no connection and no `call()` that sends anything. Two reasons. | ||
|
|
||
| A host already owns its connection — its reconnect policy, its timeouts, its | ||
| tracing, its own idea of what a memory call costs it. A client here would either | ||
| duplicate that or fight it, and the useful part is already in `calls` and | ||
| `types`. | ||
|
|
||
| And structurally it could not work anyway: `tinybus` is a vendored submodule | ||
| whose manifest inherits fields from its own nested `[workspace.package]`, so a | ||
| member of this workspace that depends on it makes cargo resolve that inheritance | ||
| against the wrong root and fail. That is why `crates/tinymemory-module` is its | ||
| own workspace root — see the note on `exclude` in the root `Cargo.toml`. A | ||
| contract crate a host links has no business being a separate workspace, so it | ||
| stays transport-free. | ||
|
|
||
| Wiring it up host-side is small: | ||
|
|
||
| ```rust,ignore | ||
| use tinymemory_bus::calls::BusCall; | ||
| use tinymemory_bus::names::{BUS_NAME, OBJECT_PATH}; | ||
| use tinymemory_bus::{types::MemoryError, wire}; | ||
|
|
||
| /// Make one call, and give a failure back as the driver's own error type. | ||
| async fn call<C: BusCall>( | ||
| connection: &tinybus::Connection, | ||
| call: C, | ||
| ) -> Result<C::Response, MemoryError> { | ||
| let args = call | ||
| .into_args() | ||
| .map_err(|e| MemoryError::Invalid(e.to_string()))?; | ||
|
|
||
| match connection | ||
| .call(BUS_NAME, OBJECT_PATH, C::METHOD, args) | ||
| .await | ||
| { | ||
| Ok(body) => C::decode_response(body).map_err(|e| MemoryError::Other(e.into())), | ||
| // The name is the contract; `from_wire` is the same table the module | ||
| // mapped out through, so the variant survives the round trip. | ||
| Err(tinybus::Error::MethodFailed { name, message }) => { | ||
| Err(wire::from_wire(&name, &message)) | ||
| } | ||
| Err(other) => Err(MemoryError::Other(other.into())), | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| `OpenStore` is the one member that needs more than that: it returns an object | ||
| *path*, not a value, and calls against that path use the same `BUS_NAME` and the | ||
| same member names. Treat `OBJECT_PATH` as the root object rather than the only | ||
| one. | ||
|
|
||
| ## Staying in step with the module | ||
|
|
||
| `names::METHODS` lists every member. `crates/tinymemory-module` asserts its | ||
| served members against that list, in order, in | ||
| `the_served_members_are_exactly_the_published_contract`. Nothing else links the | ||
| two — this crate lists members by hand, the module derives them from its | ||
| `#[tinybus::interface]` block — so that test is what turns a drift into a | ||
| `cargo test` failure instead of an `UnknownMethod` in a host at runtime. | ||
|
|
||
| Adding a member is therefore three edits in this crate: a constant in | ||
| `names::methods`, an entry in `names::METHODS`, and a call struct in the | ||
| matching `calls` family (which `calls::test::COVERED` also lists). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| //! The persisted chunk model and its embeddings. | ||
| //! | ||
| //! One [`BusCall`] per member; see [`crate::calls`] for how they are used. | ||
|
|
||
| use serde::{Deserialize, Serialize}; | ||
| use serde_json::Value; | ||
|
|
||
| use crate::calls::BusCall; | ||
| use crate::error::Error; | ||
| use crate::names::methods; | ||
| use crate::types; | ||
|
|
||
| /// Arguments for `ListChunks`. | ||
| /// | ||
| /// Chunks matching the query, size-checked. | ||
| /// | ||
| /// `ChunkQuery::limit` bounds rows, not bytes, and a chunk carries full | ||
| /// content — so this is one of the methods where the ceiling matters most. | ||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct ListChunks { | ||
| /// The `query` argument — wire position 0. | ||
| pub query: types::ChunkQuery, | ||
| /// The `scope` argument — wire position 1. | ||
| pub scope: Option<types::SourceScope>, | ||
| } | ||
|
|
||
| impl BusCall for ListChunks { | ||
| const METHOD: &'static str = methods::LIST_CHUNKS; | ||
|
|
||
| type Response = Vec<types::Chunk>; | ||
|
|
||
| fn into_args(self) -> crate::Result<Value> { | ||
| serde_json::to_value((self.query, self.scope)).map_err(Error::Encode) | ||
| } | ||
| } | ||
|
|
||
| /// Arguments for `GetChunk`. | ||
| /// | ||
| /// One chunk, size-checked. | ||
| /// | ||
| /// A single object is checked for the same reason a list is: the ceiling is | ||
| /// a property of the frame, not of the row count, and one chunk carries | ||
| /// full content with no bound of its own. A list of one that is refused | ||
| /// while the singular read of the same chunk succeeds would be an odd | ||
| /// contract to explain. | ||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct GetChunk { | ||
| /// The `chunk_id` argument — wire position 0. | ||
| pub chunk_id: String, | ||
| } | ||
|
|
||
| impl BusCall for GetChunk { | ||
| const METHOD: &'static str = methods::GET_CHUNK; | ||
|
|
||
| type Response = Option<types::Chunk>; | ||
|
|
||
| fn into_args(self) -> crate::Result<Value> { | ||
| serde_json::to_value((self.chunk_id,)).map_err(Error::Encode) | ||
| } | ||
| } | ||
|
|
||
| /// Arguments for `ChunkDetail`. | ||
| /// | ||
| /// One chunk plus its metadata, size-checked. | ||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct ChunkDetail { | ||
| /// The `chunk_id` argument — wire position 0. | ||
| pub chunk_id: String, | ||
| } | ||
|
|
||
| impl BusCall for ChunkDetail { | ||
| const METHOD: &'static str = methods::CHUNK_DETAIL; | ||
|
|
||
| type Response = Option<types::ChunkDetail>; | ||
|
|
||
| fn into_args(self) -> crate::Result<Value> { | ||
| serde_json::to_value((self.chunk_id,)).map_err(Error::Encode) | ||
| } | ||
| } | ||
|
|
||
| /// Arguments for `StorageKinds`. | ||
| /// | ||
| /// Takes no arguments, so it encodes as an empty positional array. | ||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct StorageKinds; | ||
|
|
||
| impl BusCall for StorageKinds { | ||
| const METHOD: &'static str = methods::STORAGE_KINDS; | ||
|
|
||
| type Response = Vec<String>; | ||
|
|
||
| fn into_args(self) -> crate::Result<Value> { | ||
| Ok(Value::Array(Vec::new())) | ||
| } | ||
| } | ||
|
|
||
| /// Arguments for `ChunkEmbeddings`. | ||
| /// | ||
| /// Embedding vectors are the largest thing this interface returns. | ||
| /// | ||
| /// A 1536-dimension vector encodes to roughly 10 KiB of JSON, so a few | ||
| /// hundred chunks reach the frame ceiling on their own. Checked for the same | ||
| /// reason `List` is, and refused by name rather than truncated — a short | ||
| /// batch is indistinguishable from "those chunks have no vector". | ||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct ChunkEmbeddings { | ||
| /// The `chunk_ids` argument — wire position 0. | ||
| pub chunk_ids: Vec<String>, | ||
| /// The `model_signature` argument — wire position 1. | ||
| pub model_signature: String, | ||
| } | ||
|
|
||
| impl BusCall for ChunkEmbeddings { | ||
| const METHOD: &'static str = methods::CHUNK_EMBEDDINGS; | ||
|
|
||
| type Response = Vec<types::ChunkEmbedding>; | ||
|
|
||
| fn into_args(self) -> crate::Result<Value> { | ||
| serde_json::to_value((self.chunk_ids, self.model_signature)).map_err(Error::Encode) | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: tinyhumansai/tinymemory
Length of output: 204
🏁 Script executed:
Repository: tinyhumansai/tinymemory
Length of output: 12176
🏁 Script executed:
Repository: tinyhumansai/tinymemory
Length of output: 3579
🏁 Script executed:
Repository: tinyhumansai/tinymemory
Length of output: 4186
Use Rust 2024 for this crate.
Set
edition = "2024"incrates/tinymemory-bus/Cargo.tomlto follow the repository coding guidelines.🤖 Prompt for AI Agents
Source: Coding guidelines