pub struct AppState {Show 19 fields
pub storage: Arc<dyn StorageAdapter>,
pub config: Arc<ServerConfig>,
pub auth: Arc<dyn AuthVerifier>,
pub indexing: Arc<dyn IndexingStore>,
pub connector_configs: Arc<dyn ConnectorConfigStore>,
pub settings: Arc<dyn SettingsStore>,
pub tool_provider: Option<Arc<dyn ToolProvider>>,
pub widget_auth: Arc<dyn WidgetAuthProvider>,
pub agent_config: Arc<dyn AgentConfigResolver>,
pub backplane: Arc<dyn Backplane>,
pub chat_provider: Option<Arc<dyn LlmProvider>>,
pub gateway_key_resolver: Arc<dyn GatewayKeyResolver>,
pub otp_service: Option<Arc<dyn OtpService>>,
pub shutdown: CancellationToken,
pub serve_widget: bool,
pub widget_token: Option<String>,
pub strict_auth: bool,
pub default_persona: Option<String>,
pub model_costs_cache: Arc<OnceCell<Value>>,
/* private fields */
}Expand description
Shared, cloneable application state handed to every WebSocket connection + every admin HTTP request.
Fields§
§storage: Arc<dyn StorageAdapter>The single storage seam (conversations / participants / messages / sessions / checkpoints / knowledge).
config: Arc<ServerConfig>Resolved server configuration (gateway, model, limits).
auth: Arc<dyn AuthVerifier>The configured auth verifier (jwt / smoo / none). Used by the admin API’s
require_role extractor to turn a bearer token into a Principal.
indexing: Arc<dyn IndexingStore>Indexing-run status store, surfaced by GET /admin/indexing/runs.
connector_configs: Arc<dyn ConnectorConfigStore>Connector-configuration store, CRUD’d by the admin write API
(/admin/connectors). Org-scoped; holds an auth_ref (secret name), not
the secret itself.
settings: Arc<dyn SettingsStore>Per-org agent settings store, read/written by /admin/settings.
tool_provider: Option<Arc<dyn ToolProvider>>Host tool-injection seam. When Some, the runner asks this provider
for EXTRA tools and merges them into every turn’s ToolRegistry
alongside the built-ins. Defaults to None (built-ins only); a host
installs one via with_tools to contribute its own
per-org tool catalog without forking the runner.
widget_auth: Arc<dyn WidgetAuthProvider>Embeddable-widget auth hook: resolves an agent’s origin-allowlist +
public-key policy for <smooth-agent-chat> connections. Defaults to
PermissiveWidgetAuth (no enforcement) until a host installs a real
provider via with_widget_auth.
agent_config: Arc<dyn AgentConfigResolver>Per-agent behavior config hook. Resolves an agent’s instructions
(system prompt), personality, greeting, and conversation_workflow
from its agent_id so a public chat agent behaves as its owner configured
— not as the generic org-default persona. Defaults to
StaticAgentConfigResolver (empty ⇒ no
per-agent config → the org default persona is used, unchanged); a host
installs a real provider (backed by the monorepo agents table) via
with_agent_config.
backplane: Arc<dyn Backplane>Connection backplane: per-pod sink registry + cross-pod event delivery.
Defaults to InMemoryBackplane (single-process); a host installs a
Redis/NATS impl via with_backplane to scale out
and to let non-AI publishers push realtime events to connected clients.
chat_provider: Option<Arc<dyn LlmProvider>>Test-only injected LLM surface. When Some, every send_message turn
runs the engine against this provider (a
MockLlmClient)
instead of building a live gateway client from config — exactly the
ServerState(chat_client=mock) seam the Python reference uses to drive the
scenario-parity corpus deterministically offline. None in production
(a live client is built from the gateway config), so the /ws path is
byte-for-byte unchanged for real deployments. Installed via
with_chat_provider.
gateway_key_resolver: Arc<dyn GatewayKeyResolver>Per-org LLM gateway-key resolver: maps a turn’s org_id to the gateway
key it should bill/scope to. Defaults to EnvGatewayKeyResolver (the
single SMOOAI_GATEWAY_KEY for every org — unchanged local behavior); a
multi-tenant host installs a per-org resolver via
with_gateway_key_resolver so each
tenant’s usage is attributed to its own key. The per-turn LLM-config build
falls back to the env key whenever the resolver returns None.
otp_service: Option<Arc<dyn OtpService>>End-user OTP identity-verification seam. When Some, a turn whose
auth gate refuses an end_user tool on an unverified session triggers the
OTP flow: the server emits otp_verification_required, calls
send_otp, and emits
otp_sent; a later verify_otp action calls
verify_otp and, on
success, marks the session authenticated. None (the default) keeps the
current fail-closed behavior — the end_user tool is refused and no OTP is
offered. Installed via with_otp_service. The
reference server never holds a code; the host owns generation/expiry.
shutdown: CancellationTokenGraceful-shutdown signal, shared across every per-connection clone of this
state. On SIGTERM/ctrl_c the serve loop cancels this token; each
connection’s reader loop selects on CancellationToken::cancelled so it
finishes its in-flight turn, exits, and detaches from the Backplane —
no in-flight turn dropped, no stale registry entry left behind. A fresh
token from new is never cancelled, so the /ws path and
tests are unaffected until a run/serve path wires the signal.
serve_widget: boolWhen true, the router mounts the embedded widget host page at / and
the widget bundle at /chat-widget.iife.js. Off by default (the
K8s/Lambda flavors never serve the widget); the local flavor opts in via
with_widget.
widget_token: Option<String>The auth token injected into the served widget host page (same-origin), so
the embedded widget connects to this server’s /ws?token=…. None ⇒ no
token injected (a no-auth local server).
strict_auth: boolStrict auth. When true, the /ws connect path rejects a
missing/invalid token (HTTP 401) instead of degrading to an anonymous
connection. Off by default (K8s/widget anonymous flows unchanged); a
single-tenant local/tailnet deployment opts in via
with_strict_auth so a tokenless peer can’t
drive the agent.
default_persona: Option<String>Default agent persona / system prompt. When Some, it is used as the
turn’s system prompt whenever the per-org [AgentSettings::persona] is
None — i.e. a host-supplied default that replaces the built-in
customer-support KNOWLEDGE_CHAT_SYSTEM_PROMPT when no
per-org override exists. The single-tenant local daemon installs its
“Big Smooth” personal-assistant persona here via
with_default_persona. None (the default)
keeps the const prompt, so the cloud flavor is byte-for-byte unchanged.
model_costs_cache: Arc<OnceCell<Value>>Model-pricing cache for GET /admin/model-costs. The gateway’s
/v1/model/info pricing is stable, so it’s fetched at most once per
process and reused for every subsequent request (the admin handler sets
this on the first successful fetch; a gateway error is NOT cached, so a
transient failure is retried on the next request). Shared across clones so
every connection/request sees the same cached map.
Implementations§
Source§impl AppState
impl AppState
Sourcepub fn new(storage: Arc<dyn StorageAdapter>, config: ServerConfig) -> Self
pub fn new(storage: Arc<dyn StorageAdapter>, config: ServerConfig) -> Self
Construct shared state over a storage adapter and config.
Defaults the admin-API collaborators: a NoAuthVerifier (overridden via
with_auth) and an empty InMemoryIndexingStore
(overridden via with_indexing). The /ws path
uses none of these, so existing callers are unaffected.
Sourcepub fn with_auth(self, auth: Arc<dyn AuthVerifier>) -> Self
pub fn with_auth(self, auth: Arc<dyn AuthVerifier>) -> Self
Install the configured auth verifier (builder).
Sourcepub fn with_storage(self, storage: Arc<dyn StorageAdapter>) -> Self
pub fn with_storage(self, storage: Arc<dyn StorageAdapter>) -> Self
Replace the storage adapter (builder).
Lets an embedder (e.g. the local-flavor daemon) swap the default in-memory store for a durable local adapter — the seam an always-on, self-hosted deployment needs so conversations/sessions/checkpoints survive a restart without standing up Postgres.
Sourcepub fn with_indexing(self, indexing: Arc<dyn IndexingStore>) -> Self
pub fn with_indexing(self, indexing: Arc<dyn IndexingStore>) -> Self
Install the indexing store (builder).
Sourcepub fn with_connector_configs(
self,
store: Arc<dyn ConnectorConfigStore>,
) -> Self
pub fn with_connector_configs( self, store: Arc<dyn ConnectorConfigStore>, ) -> Self
Install the connector-configuration store (builder).
Sourcepub fn with_settings(self, store: Arc<dyn SettingsStore>) -> Self
pub fn with_settings(self, store: Arc<dyn SettingsStore>) -> Self
Install the agent-settings store (builder).
Sourcepub fn with_tools(self, provider: Arc<dyn ToolProvider>) -> Self
pub fn with_tools(self, provider: Arc<dyn ToolProvider>) -> Self
Install a host ToolProvider (builder). The runner merges the
provider’s per-turn tools into every turn’s registry alongside the
built-ins. Without this, the registry is exactly the built-ins, so the
default/local flavor is unaffected.
Sourcepub fn with_strict_auth(self, strict: bool) -> Self
pub fn with_strict_auth(self, strict: bool) -> Self
Enable strict auth (builder): reject /ws connections with a
missing/invalid token (HTTP 401) instead of degrading to anonymous. Pair
with a real with_auth verifier. Off by default.
Sourcepub fn with_default_persona(self, persona: impl Into<String>) -> Self
pub fn with_default_persona(self, persona: impl Into<String>) -> Self
Install a default agent persona (builder): the system prompt used for
a turn when the per-org [AgentSettings::persona] is unset. A single-tenant
host (the local daemon) installs its own personality here so every turn
runs as that agent rather than the built-in customer-support prompt. None
(the default) keeps the const prompt, so the cloud flavor is unchanged. An
empty/whitespace-only string is treated as no default.
Sourcepub fn with_widget(self, token: Option<String>) -> Self
pub fn with_widget(self, token: Option<String>) -> Self
Serve the embedded official widget (host page at /, bundle at
/chat-widget.iife.js), injecting token into the page so the widget
connects to this server’s /ws?token=… (builder). The local deployment
flavor opts in; other flavors never mount the widget routes.
Sourcepub fn with_widget_auth(self, provider: Arc<dyn WidgetAuthProvider>) -> Self
pub fn with_widget_auth(self, provider: Arc<dyn WidgetAuthProvider>) -> Self
Install the embeddable-widget auth provider (builder). A host backs this with its agent store so embed origins + public keys are enforced.
Sourcepub fn with_agent_config(self, provider: Arc<dyn AgentConfigResolver>) -> Self
pub fn with_agent_config(self, provider: Arc<dyn AgentConfigResolver>) -> Self
Install the per-agent behavior-config provider (builder). A host backs
this with its agents store so each agent’s instructions /
conversation_workflow drive its conversations. Without it, the runner
falls back to the org-default persona (unchanged behavior).
Sourcepub fn with_backplane(self, backplane: Arc<dyn Backplane>) -> Self
pub fn with_backplane(self, backplane: Arc<dyn Backplane>) -> Self
Install the connection backplane (builder). A host installs a Redis/NATS
impl to scale the WS service horizontally and to let other services push
realtime events to connected clients via Backplane::publish.
Sourcepub fn with_chat_provider(self, provider: Arc<dyn LlmProvider>) -> Self
pub fn with_chat_provider(self, provider: Arc<dyn LlmProvider>) -> Self
Install a test-injected LLM provider (builder). Every send_message turn
then runs the engine against this provider instead of a live gateway
client — the MockLlmClient
seam the scenario-parity corpus drives. Production never calls this, so the
live path is unchanged. See chat_provider.
Sourcepub fn with_gateway_key_resolver(
self,
resolver: Arc<dyn GatewayKeyResolver>,
) -> Self
pub fn with_gateway_key_resolver( self, resolver: Arc<dyn GatewayKeyResolver>, ) -> Self
Install a per-org gateway-key resolver (builder). A multi-tenant host
installs a resolver backed by its per-org key store (e.g. one LiteLLM
virtual key per tenant) so each org’s turns are billed/scoped to its own
key. The per-turn LLM-config build falls back to the env key whenever the
resolver returns None, so a resolver covering only some orgs is safe.
Leaving this unset keeps the default EnvGatewayKeyResolver (single env
key for every org — unchanged local behavior).
Sourcepub fn with_otp_service(self, service: Arc<dyn OtpService>) -> Self
pub fn with_otp_service(self, service: Arc<dyn OtpService>) -> Self
Install the end-user OTP identity-verification service (builder). Wires the
end_user auth gate to an OTP flow (see otp_service);
leaving it unset keeps the fail-closed default (refuse, no OTP offered).
Sourcepub fn with_shutdown(self, shutdown: CancellationToken) -> Self
pub fn with_shutdown(self, shutdown: CancellationToken) -> Self
Install the graceful-shutdown signal (builder). The serve loop owns a
clone of this token and cancels it on SIGTERM/ctrl_c; every per-connection
clone observes the cancellation and drains. Defaulted to a fresh token in
new, so this is only needed when a caller wants to drive
shutdown from its own token.
Sourcepub fn insert_session(&self, session: Session)
pub fn insert_session(&self, session: Session)
Register a freshly created session.
Sourcepub fn get_session(&self, session_id: &str) -> Option<Session>
pub fn get_session(&self, session_id: &str) -> Option<Session>
Look up a session by id.
Sourcepub fn session_current_step(&self, session_id: &str) -> Option<String>
pub fn session_current_step(&self, session_id: &str) -> Option<String>
The conversation-workflow step this session is currently on, read from the
session’s metadata.currentStepId. None = no workflow / fresh start (the
runner then resolves to the workflow’s first step).
Sourcepub fn set_session_current_step(&self, session_id: &str, step_id: Option<&str>)
pub fn set_session_current_step(&self, session_id: &str, step_id: Option<&str>)
Persist the workflow step pointer onto the in-memory session’s
metadata.currentStepId. Matches the session registry’s durability (the
pointer lives as long as the session does, on the pod that owns it). A
None step clears the pointer. No-op for an unknown session.
Sourcepub fn session_authenticated(&self, session_id: &str) -> bool
pub fn session_authenticated(&self, session_id: &str) -> bool
Whether this session’s caller has completed OTP identity verification,
read from the session’s metadata.otpVerified. false for an unknown or
unverified session. Threaded into the end_user auth gate so a verified
session’s gated tools run. Same durability as the session registry (lives
as long as the session, on the pod that owns it).
Sourcepub fn set_session_authenticated(&self, session_id: &str, verified: bool)
pub fn set_session_authenticated(&self, session_id: &str, verified: bool)
Mark this session identity-verified (or clear it) by setting
metadata.otpVerified. Called after a successful verify_otp. No-op for
an unknown session. Coexists with the workflow step pointer (both live in
the session’s metadata map).
Sourcepub fn session_contact(&self, session_id: &str) -> OtpContact
pub fn session_contact(&self, session_id: &str) -> OtpContact
The caller’s OTP contact points for this session, read from the session’s
metadata.contactEmail / metadata.contactPhone (stashed at
create-session time). Empty when the session is unknown or captured no
contact — the server then can’t offer OTP. The reference create-session
path captures only an email.
Sourcepub fn record_document_set(
&self,
org_id: impl Into<String>,
set: impl Into<String>,
)
pub fn record_document_set( &self, org_id: impl Into<String>, set: impl Into<String>, )
Record that a document was added to a named document set within an org
(increments its count). Used by seeding + the ingest path so
GET /admin/document-sets can report set names + counts despite the
in-memory backend dropping document metadata. Org-scoped so org A’s sets
are never reported to an org-B caller.
Sourcepub fn document_sets(&self, org_id: &str) -> Vec<(String, usize)>
pub fn document_sets(&self, org_id: &str) -> Vec<(String, usize)>
Snapshot one org’s document-set registry as (name, count) pairs,
sorted by name for a stable response. Never returns another org’s sets.
Sourcepub fn record_connector(
&self,
org_id: impl Into<String>,
name: impl Into<String>,
)
pub fn record_connector( &self, org_id: impl Into<String>, name: impl Into<String>, )
Record a connector (within an org) whose indexing runs should be listed
(idempotent). Org-scoped so a same-named connector in two orgs records
separately and GET /admin/indexing/runs only lists the caller’s org’s.
Sourcepub fn connectors(&self, org_id: &str) -> Vec<String>
pub fn connectors(&self, org_id: &str) -> Vec<String>
Snapshot one org’s recorded connector names (sorted, stable). Never returns another org’s connectors.
Sourcepub fn register_confirmation(
&self,
session_id: impl Into<String>,
responder: UnboundedSender<HumanResponse>,
)
pub fn register_confirmation( &self, session_id: impl Into<String>, responder: UnboundedSender<HumanResponse>, )
Register a parked turn’s HumanResponse sender for session_id, so a
later confirm_tool_action can resume it. Any prior pending sender for
the same session is replaced (one outstanding confirmation per session).
Called by the runner’s confirmation bridge when a write tool emits a
HumanRequest::Confirm.
Sourcepub fn take_confirmation(
&self,
session_id: &str,
) -> Option<UnboundedSender<HumanResponse>>
pub fn take_confirmation( &self, session_id: &str, ) -> Option<UnboundedSender<HumanResponse>>
Take (remove + return) the pending HumanResponse sender for
session_id, if a turn is parked on a confirmation. Returns None when
no turn awaits confirmation for that session (the common case). Taking it
out — rather than cloning — guarantees a single confirmation resolves a
single parked tool call, and a duplicate confirm_tool_action is a no-op.
Sourcepub fn clear_confirmation(&self, session_id: &str)
pub fn clear_confirmation(&self, session_id: &str)
Drop any pending confirmation registered for session_id without
resolving it. Called when a parked turn ends (the bridge task finishes)
so a stale sender can’t linger and mis-route a later confirmation.
Trait Implementations§
Source§impl<const MIN: u8> FromRequestParts<AppState> for RequireRole<MIN>
impl<const MIN: u8> FromRequestParts<AppState> for RequireRole<MIN>
Auto Trait Implementations§
impl !RefUnwindSafe for AppState
impl !UnwindSafe for AppState
impl Freeze for AppState
impl Send for AppState
impl Sync for AppState
impl Unpin for AppState
impl UnsafeUnpin for AppState
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> FutureExt for T
impl<T> FutureExt for T
Source§fn with_context(self, otel_cx: Context) -> WithContext<Self>
fn with_context(self, otel_cx: Context) -> WithContext<Self>
Source§fn with_current_context(self) -> WithContext<Self>
fn with_current_context(self) -> WithContext<Self>
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request