Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [".", "api", "core", "adapters/tinycortex", "adapters/remote", "conformance"]
default-members = [".", "api", "core", "adapters/tinycortex", "adapters/remote", "conformance"]
# `sync` is the engine-neutral Composio normalisers (issue #18 §B3).
members = [".", "api", "core", "sync", "adapters/tinycortex", "adapters/remote", "conformance"]
default-members = [".", "api", "core", "sync", "adapters/tinycortex", "adapters/remote", "conformance"]
# `vendor/` holds engine submodules (tinycortex, tinybus, tinyagents), each of
# which is its own workspace with its own lockfile. Same exclusion
# `vendor/tinycortex` uses for its own nested vendor directory.
Expand Down Expand Up @@ -79,6 +80,9 @@ tinymemory-api = { path = "api", features = ["test-support"] }
# integration tests. A dev-dependency only: the facade must not carry a test
# harness into a consumer's dependency graph.
tinymemory-conformance = { path = "conformance" }
# The extracted Composio normalisers, for the integration test that runs them
# against a driver that is not TinyCortex (issue #18 §B3).
tinymemory-sync = { path = "sync" }

[features]
default = []
Expand Down
19 changes: 12 additions & 7 deletions conformance/src/suite/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ pub async fn assert_awkward_content_round_trips(provider: &dyn MemoryProvider) {
("large", "x".repeat(64 * 1024)),
("newlines", "a\nb\r\nc\0d".to_string()),
];
let mut accepted = 0usize;
let mut accepted: Vec<&str> = Vec::new();
for (key, content) in &cases {
// A driver may refuse a shape outright — `MemoryCore::store` documents
// `Invalid` "for caller input the driver rejects", and the TinyCortex
Expand Down Expand Up @@ -629,7 +629,7 @@ pub async fn assert_awkward_content_round_trips(provider: &dyn MemoryProvider) {
{
continue;
}
accepted += 1;
accepted.push(key);
if let Some(got) = provider
.get(&ns, key)
.await
Expand All @@ -638,12 +638,17 @@ pub async fn assert_awkward_content_round_trips(provider: &dyn MemoryProvider) {
assert_eq!(&got.content, content, "{who}: `{key}` content was mangled");
}
}
// Without this a driver that refused every shape would pass having stored
// nothing, which is the vacuous reading of "may refuse".
// "May refuse" needs a floor, or a driver that refused everything would pass
// having stored nothing. The floor is `unicode` specifically rather than a
// count: refusing `empty` is documented validation, and refusing `large` is
// a defensible size limit, but refusing ordinary UTF-8 text is a broken
// driver — and unicode is the case where mangling actually shows, since
// truncation and re-encoding are invisible on ASCII.
assert!(
accepted > 0,
"{who}: refused every content shape — unicode, empty, large and \
newlines were all rejected, so this assertion proved nothing"
accepted.contains(&"unicode"),
"{who}: refused ordinary UTF-8 content — accepted {accepted:?}. A driver \
may refuse a shape, but not this one; every assertion about content \
surviving a round trip rests on it."
);
let keys: Vec<&str> = cases.iter().map(|(k, _)| *k).collect();
cleanup(provider, &ns, &keys).await;
Expand Down
5 changes: 5 additions & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ readme = "../README.md"
# The contract. `tinymemory-core` implements and consumes it; the host seam
# traits (config, event sink, embeddings, chat) live in `tinymemory_api::host`.
tinymemory-api = { path = "../api" }
# Composio payload normalisers, extracted out of the engine (issue #18 §B3).
# They were reached through `tinycortex` until now, which meant a host binding a
# different engine could not have Composio sync despite none of this code
# caring which engine is bound.
tinymemory-sync = { path = "../sync" }
tinymemory = { path = ".." }

# The default embedded engine. `store/`, `tree/` and `sync/` drive it directly;
Expand Down
2 changes: 1 addition & 1 deletion core/src/sync/composio/providers/clickup/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
// The payload normalisers moved to tinycortex (they are pure Value
// transforms, i.e. driver-side). Aliased under the old module name so
// every `normalization::extract_*` call site below stays unchanged.
use crate::engine::backend::sync::composio::providers::normalize::clickup as normalization;
use tinymemory_sync::clickup as normalization;
mod provider;
#[cfg(test)]
mod tests;
Expand Down
2 changes: 1 addition & 1 deletion core/src/sync/composio/providers/github/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
// The payload normalisers moved to tinycortex (they are pure Value
// transforms, i.e. driver-side). Aliased under the old module name so
// every `normalization::extract_*` call site below stays unchanged.
use crate::engine::backend::sync::composio::providers::normalize::github as normalization;
use tinymemory_sync::github as normalization;
mod provider;
#[cfg(test)]
mod tests;
Expand Down
2 changes: 1 addition & 1 deletion core/src/sync/composio/providers/gmail/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// The Gmail post-processor moved to tinycortex (a pure Value transform, i.e.
// driver-side). Aliased under the old module name so the single call site in
// `provider.rs` stays unchanged.
use crate::engine::backend::sync::composio::providers::normalize::gmail_post_process as post_process;
use tinymemory_sync::gmail_post_process as post_process;
mod provider;
#[cfg(test)]
mod tests;
Expand Down
4 changes: 2 additions & 2 deletions core/src/sync/composio/providers/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! Shared helpers for Composio provider implementations.
//!
//! `pick_str` used to live here. It is a provider payload normaliser, so it
//! moved to `crate::engine::backend::sync::composio::providers::normalize::helpers`
//! moved to `tinymemory_sync::helpers`
//! and is re-exported from this module's parent. The helpers that remain are
//! request-building rather than normalisation, and stay host-side.

use crate::engine::backend::sync::composio::providers::normalize::helpers::pick_str;
use tinymemory_sync::helpers::pick_str;

/// Shallow-merge an `extra` JSON object into a (mutable) action-args
/// object. Only object-typed extras are merged; non-object `extra`
Expand Down
2 changes: 1 addition & 1 deletion core/src/sync/composio/providers/linear/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// The payload normalisers moved to tinycortex (they are pure Value
// transforms, i.e. driver-side). Aliased under the old module name so
// every `normalization::extract_*` call site below stays unchanged.
use crate::engine::backend::sync::composio::providers::normalize::linear as normalization;
use tinymemory_sync::linear as normalization;
mod provider;
#[cfg(test)]
mod tests;
Expand Down
2 changes: 1 addition & 1 deletion core/src/sync/composio/providers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,11 +281,11 @@ pub(crate) use helpers::{first_array_str, merge_extra};
// re-exported here so the ~40 in-tree call sites keep resolving unchanged.
// Note this is deliberately NOT `providers::common::pick_str`, which coerces
// numbers to strings — see the doc comments on both definitions.
pub(crate) use crate::engine::backend::sync::composio::providers::normalize::helpers::pick_str;
pub use registry::{
all_providers, get_provider, init_default_providers, register_provider, ProviderArc,
};
pub use scope_lookup::{curated_scope_for, toolkit_has_scope};
pub(crate) use tinymemory_sync::helpers::pick_str;
pub use tool_scope::{classify_unknown, find_curated, toolkit_from_slug, CuratedTool, ToolScope};
pub use traits::{resolve_sync_interval_secs, sync_interval_env_var, ComposioProvider};
pub use types::{
Expand Down
2 changes: 1 addition & 1 deletion core/src/sync/composio/providers/notion/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// The payload normalisers moved to tinycortex (they are pure Value
// transforms, i.e. driver-side). Aliased under the old module name so
// every `normalization::extract_*` call site below stays unchanged.
use crate::engine::backend::sync::composio::providers::normalize::notion as normalization;
use tinymemory_sync::notion as normalization;
mod provider;
#[cfg(test)]
mod tests;
Expand Down
2 changes: 1 addition & 1 deletion core/src/sync/composio/providers/slack/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
// driver-side). Re-exported under the old module name — `pub`, not a plain
// `use`, because `tests/raw_coverage/memory_threads_raw_coverage_e2e.rs`
// imports this path directly.
pub use crate::engine::backend::sync::composio::providers::normalize::slack_post_process as post_process;
pub use tinymemory_sync::slack_post_process as post_process;
pub mod types;

mod provider;
Expand Down
11 changes: 11 additions & 0 deletions crates/tinymemory-module/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions scripts/ci/dependency-budget.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,26 @@ printf '%-52s %s\n' "tinymemory-api" "$(count -p tinymemory-api)"
printf '%-52s %s\n' "tinymemory-tinycortex (default)" "$(count -p tinymemory-tinycortex --no-default-features)"
printf '%-52s %s\n' "tinymemory-tinycortex --features memory-git" "$(count -p tinymemory-tinycortex --features memory-git)"
printf '%-52s %s\n' "tinymemory-remote" "$(count -p tinymemory-remote)"
printf '%-52s %s\n' "tinymemory-sync" "$(count -p tinymemory-sync)"

# The sync crate exists because it has no engine behind it (issue #18 §B3):
# the Composio normalisers lived inside TinyCortex, and a host binding a
# different engine could not run them. Nothing else enforces that, and a
# dependency added two crates away would reintroduce the coupling silently —
# the build would still be green, and the property would just quietly stop
# being true.
sync_engine="$(
cargo tree -p tinymemory-sync -e normal --prefix none 2>/dev/null \
| grep -Ei '^(tinycortex|rusqlite|libsqlite|tinymemory-core|tinymemory-api)' || true
)"
if [ -n "$sync_engine" ]; then
echo "tinymemory-sync reached an engine, a store, or the contract:" >&2
echo "$sync_engine" >&2
echo >&2
echo "That crate is the one piece of Composio sync a non-TinyCortex host can" >&2
echo "use. A dependency on any of the above puts it back behind an engine." >&2
exit 1
fi

echo
if [ "$minimal" -gt "$MINIMAL_CEILING" ]; then
Expand Down
45 changes: 45 additions & 0 deletions sync/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
[package]
name = "tinymemory-sync"
publish = false
version = "0.1.0"
edition = "2021"
rust-version = "1.96"
license = "MIT"
repository = "https://github.com/tinyhumansai/tinymemory"
description = "Engine-neutral Composio payload normalisers for TinyMemory"

# The whole dependency list, and it is the point of the crate. These are pure
# `Value -> Value` transforms: no engine, no storage, no network, no async
# runtime. A dependency added here should have to argue for itself against that
# sentence (issue #18 §B3).
[dependencies]
serde_json = "1"
# Two logging facades, neither an implementation, both carried over from the
# engine layout this crate was extracted from: `gmail_post_process` traces
# through `tracing`, `slack_post_process` through `log`. Preserved rather than
# unified, because §B3 is a *move* and swapping a facade would change where a
# host's log lines surface — a behaviour change hiding inside a relocation.
# Worth reconciling in its own change.
tracing = "0.1"
log = "0.4"
# RFC 2822/3339 date handling for Gmail `Date:` headers.
#
# `clock` is on, and it is the one place this crate is not a pure function of
# its input: `format_email_local_time` renders in `chrono::Local`, so it reads
# the host's timezone. That is deliberate upstream — the agent presents local
# times without doing UTC arithmetic, and the raw UTC field is preserved
# alongside — but it means "pure `Value -> Value`" is true of every normaliser
# here except that one. Better said out loud than discovered by someone whose
# output moved when they changed TZ.
chrono = { version = "0.4", features = ["clock"] }

[lints.rust]
unsafe_code = "forbid"
missing_docs = "warn"
unreachable_pub = "warn"

[lints.clippy]
all = { level = "warn", priority = -1 }
unwrap_used = "warn"
expect_used = "warn"
panic = "warn"
Loading
Loading