Skip to content

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.

  • 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 server feature.
  • 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 the SmartChatStore trait), and the desktop console’s src-tauri gRPC 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.

  • The SmartChatService gRPC surface — its own smartchat.v1 proto: CreateConversation, OpenConversation (a bidi stream), ListConversations, GetConversation, RenameConversation, DeleteConversation. The proto imports apomesh.proto and maps apomesh.v1 onto the already-generated apomesh-protocol types via extern_path — no regenerated or duplicated wire symbols.
  • The conversation / turn session model — a Conversation (kind, title, timestamps, turn count) holding an ordered list of Turns; each turn’s TurnOutcome is the closed set Pending | Assistant | Dispatch | Failed.
  • The persistence portSmartChatStore, 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 crate apomesh-state-store-smartchat-postgres (no reference page yet).
  • The live bridgeOpenConversation replays stored turns as substrate OperatorChatInitialTurn seed history, opens an internal OperatorChatSession, forwards each substrate OperatorChatResponse verbatim as substrate_passthrough, and commits the outcome on TurnComplete.
  • Tenant scoping — every store call carries &TenantContext; a cross-tenant read surfaces as ConversationNotFound so 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.

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.