Skip to content

msflib.ai_api

Public modules of the msflib-ai-api package.

msflib.ai_api.adapter_registry

msflib.ai_api.agent_builder

msflib.ai_api.agent_runtime

Shared LangGraph runtime-context schema for the ai_api agent.

Set once as the agent's context_schema and passed fresh on every invoke/astream call (see router/agent.py), never checkpointed — unlike graph state, context is documented by LangGraph as "static context for the graph run" and is never persisted/rehydrated across turns, which is exactly the property authorization-boundary data like scope needs: it must come from the live request, never resurface from a stored checkpoint.

Tool registries that need per-request identity (e.g. msflib.knowledge's KnowledgeToolRegistry) read it via langgraph.runtime.get_runtime() inside their own tool bodies — they don't import this module directly, they just expect context.scope to exist by convention, the same way they already avoid a hard dependency on ai_core's orchestration layer.

vector_store_resolver, when set, is a zero-arg callable ai_core's rag_search tool calls per-invocation instead of the vector store it was constructed with — how router/agent.py opts individual requests into VectorStoreRegistryService-backed per-tenant routing without rebuilding the (cached, router-lifetime) tool registries themselves.

web_search_backend, when set, is the tenant/workspace-tiered WEB_SEARCH_BACKEND value ai_core's web_search tool reads per-invocation instead of the backend it was constructed with — same "override a cached, router-lifetime tool without rebuilding it" seam as vector_store_resolver, but a plain resolved value rather than a callable since router/agent.py already resolves it eagerly via a FastAPI dependency (unlike the vector store, resolving it isn't deferred to only when the tool actually runs). web_search_api_key is the matching tiered WEB_SEARCH_API_KEY, needed so an override to a key-requiring backend (e.g. "tavily") doesn't fall back to the web_search tool's construction-time key, which may be absent when the tool was built for the (keyless) default backend.

msflib.ai_api.contracts

msflib.ai_api.deps

Request-scoped FastAPI dependencies for ai_api's transport endpoints.

Follows the module deps convention (see ai_core.deps.get_provider_dependencies and ai_core.router, which wires get_provider_dependencies and msflib.scope.get_scope_dependencies as two separate factory calls): scope building and settings/service resolution are two distinct factories here too. Both now take get_session (scope building needs it to resolve the caller's tenant id via get_current_tenant/resolve_default_tenant_id).

get_ai_api_scope_dependencies(*, get_current_account: Callable, get_current_workspace: Callable | None = None, get_current_tenant: Callable | None = None, get_session: Callable | None = None) -> DependencyNamespace

Return reusable FastAPI dependencies yielding the caller's ai_api ScopeEnvelope.

get_current_account is always required (ai_api has no anonymous surface); get_current_workspace is optional. get_current_tenant is resolved once at the endpoint boundary, the same way documents.router()/ingestion.router() resolve it (see msflib.tenancy.deps.get_tenant_dependencies) -- yields a Tenant row. When omitted, falls back to resolve_default_tenant_id (today's single-tenant resolution) if get_session is wired, else the scope's tenant dimension is set to _NO_TENANT_SENTINEL (0) -- mirrors get_current_tenant itself being optional: a host app that supplies neither gets a scope that still satisfies profiles requiring a non-null tenant_id.

Returns:

Type Description
DependencyNamespace exposing:
  • get_search_scope / get_ask_scope — the caller's ScopeEnvelope for /search and /ask-shaped operations respectively, including the aiapi.retrieval.search / aiapi.ask constraint evaluation (403 on violation). Each declares its request body model (SearchRequest / AskRequest) as a parameter so the conversation/sub-thread dimensions come from the same parsed body FastAPI hands the endpoint — not a second inline scope build.

get_ai_api_settings_dependencies(settings: SettingsBase, *, get_session: Callable, get_current_account: Callable, get_current_workspace: Callable | None = None, get_policy_resolver: Callable | None = None) -> DependencyNamespace

Return reusable FastAPI dependencies for ai_api's settings/service resolution.

Delegates tiered AICoreSettings/ProviderRegistryService resolution to ai_core (see :func:msflib.ai_core.deps.get_provider_dependencies) rather than re-implementing it; only adds the request-level k -> RETRIEVER_K override on top.

Returns:

Type Description
DependencyNamespace exposing:
  • get_search_ai_settings / get_ask_ai_settings — AICoreSettings resolved for the request's tenant/workspace/account tiers via ai_core's get_provider_dependencies, with the request's k (if set) layered on as RETRIEVER_K.
  • get_registry_service — ProviderRegistryService bound to the resolved settings (delegates to ai_core; see :func:msflib.ai_core.deps.get_provider_dependencies).

msflib.ai_api.protocol_adapter

msflib.ai_api.router

TieredResolution(vector_store: bool = False, web_search_backend: bool = False) dataclass

Which settings a router should re-resolve per-request through policy tiers.

Shared by create_llm_router and create_agent_router (via the top-level router()'s single tiered param) so both stay in sync rather than each accumulating its own flat resolve_x_per_request boolean. "Tiered" means resolved through PolicyResolutionService's tenant -> workspace -> user chain (see msflib.ai_core.deps._resolve_tiered_ai_settings) instead of the module's static base AICoreSettings — each field defaults to False (static settings, byte-for-byte unchanged from before this class existed), since resolving through that chain means a live DB lookup on every request, not something to switch on by default.

  • vector_store — re-resolve the vector store backend per request (VectorStoreRegistryService, ranked user < workspace < tenant < global). Read by both routers.
  • web_search_backend — re-resolve WEB_SEARCH_BACKEND per request, tenant/workspace tiers only (the user tier is deliberately skipped — see create_agent_router). Read by the agent router only.

agent

TranscriptWriter

Bases: Protocol

Subset of msflib.conversation.contracts.transcript.TranscriptWriter this router calls -- declared locally so this module never imports msflib.conversation.

msflib.ai_api.schema

msflib.ai_api.scope

build_request_envelope(*, tenant_id: int | None, account: AccountBase, workspace_id: int | None, channel: str, conversation_id: str | None = None, sub_thread_id: str | None = None) -> ScopeEnvelope

Build a per-request ScopeEnvelope from the authenticated account context.

tenant_id is caller-supplied (the endpoint boundary's already-resolved real tenant id -- see ai_api.deps.get_ai_api_scope_dependencies) and never derived from account: accounts can belong to multiple tenants. account.id is the individual user identity and goes into PrincipalContext only.

msflib.ai_api.scope_profiles

msflib.ai_api.services

rag

resolve_scoped_vector_store(*, settings: SettingsBase, session: Session, scope: ScopeEnvelope, ai_settings: AICoreSettings, collection_name: str | None = None) -> ScopedSearchable

Resolve a vector store from the caller's scope via the DB-backed registry.

Unlike resolve_default_vector_store (static AICoreSettings.VECTOR_STORE_*, cached for the router's lifetime), this re-resolves per call against VectorStoreProfile rows ranked user < workspace < tenant < global for scope, falling back to the static settings when none match or DB_VECTOR_STORE_REGISTRY_ENABLED is off. ai_settings should already be the per-request tiered settings from resolve_ai_settings so a workspace-level VECTOR_STORE_BACKEND override is honored too.

answer_with_rag(*, question: str, hits: list[Document], llm: BaseLanguageModel | None) -> str

Build a retrieval-grounded answer using the provided LLM client.

scope_evaluator