apomesh-smartchat
apomesh-smartchat is an application crate, not substrate. It is the
operator-facing chat layer that sits above the orchestrator: the substrate is
a runtime, and SmartChat is a feature built on it. The crate serves its own
gRPC surface (SmartChatService) and persists the operator’s conversations and
their turns, then composes the substrate’s in-process OperatorChat loop
underneath to actually drive the LLM tool-use. Reach for this crate for the
durable operator-chat surface — creating, listing, and streaming a live
conversation, and the persistence port behind it.
The distinction from OperatorChat matters: the substrate’s OperatorChat
bidi RPC and its in-memory OperatorChatSession tool-use loop (owned by
apomesh-orchestrator) stay
substrate-internal transport mechanisms. SmartChat wraps them — it owns
persistence and its own typed wire; the orchestrator’s loop owns driving the
turn.
Position in the workspace
Section titled “Position in the workspace”- Layer: transports / applications — an application beside the substrate, not part of it. See Control & execution planes.
- Depends on: apomesh-protocol (the
substrate wire types its proto re-uses),
apomesh-orchestrator and
apomesh-substrate (the loop and
primitives it composes) — the latter two behind the crate’s default-on
serverfeature. - Consumed by:
apomesh-orchestrator-bin(mounts the service beside the daemon),apomesh-state-store-smartchat-postgres(the durable adapter, which depends on this crate for theSmartChatStoretrait), and the desktop console’ssrc-taurigRPC client. Verified by grep across the workspace.
The daemon gates the whole mount behind its own smartchat Cargo feature,
which is default-on; building apomesh-orchestrator-bin with
--no-default-features drops the SmartChat service entirely.
What it owns
Section titled “What it owns”- The
SmartChatServicegRPC surface — its ownsmartchat.v1proto:CreateConversation,OpenConversation(a bidi stream),ListConversations,GetConversation,RenameConversation,DeleteConversation. The proto importsapomesh.protoand mapsapomesh.v1onto the already-generatedapomesh-protocoltypes viaextern_path— no regenerated or duplicated wire symbols. - The conversation / turn session model — a
Conversation(kind, title, timestamps, turn count) holding an ordered list ofTurns; each turn’sTurnOutcomeis the closed setPending | Assistant | Dispatch | Failed. - The persistence port —
SmartChatStore, a hexagonal-port trait with an in-memory adapter (InMemorySmartChatStore) here for tests and Postgres-less dev. The durable Postgres adapter lives in the sibling crateapomesh-state-store-smartchat-postgres(no reference page yet). - The live bridge —
OpenConversationreplays stored turns as substrateOperatorChatInitialTurnseed history, opens an internalOperatorChatSession, forwards each substrateOperatorChatResponseverbatim assubstrate_passthrough, and commits the outcome onTurnComplete. - Tenant scoping — every store call carries
&TenantContext; a cross-tenant read surfaces asConversationNotFoundso existence never leaks across tenants.
It does not own the LLM tool-use loop (that is the orchestrator’s
OperatorChatSession, composed as SmartChat’s internal transport), the durable
store’s implementation (the sibling Postgres adapter), or the substrate
workflows a turn drives — dispatch, cancel, tail all reach the orchestrator
daemon through the operator-tools.
Public surface
Section titled “Public surface”SmartChatGrpcService— the tonic service;new(store, deps)theninto_server(), constructed once and registered besideApomeshDaemonServer.SmartChatDeps— the substrate handles the bridge composes (session,router,catalog,daemon); the daemon hands over the sameArcs it already built.SmartChatStore/InMemorySmartChatStore— the async persistence-port trait (create/get/list/rename/delete conversation, append/set/list turn) and its in-memory adapter.Conversation/ConversationKind— the conversation row and its closed kind enum (SmartRouter).Turn/TurnOutcome— one turn and its outcome variant.ConversationId/TurnId— the opaque UUIDv7 identity types (IdentityParseErroron bad wire input).SmartChatError/SmartChatResult— the crate’s typed error, mapped to a gRPCStatusat the service boundary.pb— the generatedsmartchat.v1wire module.
The daemon builds SmartChatDeps from the same Arc-shared substrate handles
it already holds, then registers the service as a sibling on the one gRPC
Server::builder — same listener as ApomeshDaemonServer, not a second port:
use std::sync::Arc;use apomesh_smartchat::{InMemorySmartChatStore, SmartChatDeps, SmartChatGrpcService};
// `session`, `router`, `catalog`, `daemon` are the daemon's existing handles.let deps = SmartChatDeps { session, router, catalog, daemon: daemon.clone() };let smartchat = SmartChatGrpcService::new(Arc::new(InMemorySmartChatStore::new()), deps) .into_server();
Server::builder() .add_service(ApomeshDaemonServer::new(daemon)) .add_service(smartchat) // registered beside the daemon service .serve(addr) .await?;The operator meets SmartChat through the desktop console: its src-tauri
SmartChat command family (smartchat_create_conversation,
smartchat_open_conversation, smartchat_send_turn, and peers) is a gRPC
client of this service. See the console guide.
Related
Section titled “Related”- Operate: Console — where the operator drives SmartChat.
- Reference: apomesh-orchestrator
(the
OperatorChatloop SmartChat composes), apomesh-control-plane (the desktop console that is its client), apomesh-protocol (the substrate wire its proto re-uses). Its durable store is the sibling crateapomesh-state-store-smartchat-postgres. - Concepts: Control & execution planes — how applications sit beside the substrate runtime.