Design Document for SUTE Tenant Profile Implementation
Design Document: Tenant Profile for SUTE
Status: Draft for implementation
Tenant scope: sute (single organization)
Audience: Engineering
Related systems: Agent kernel, smart chains, web chat, voice, knowledge base, BusinessInfo
1. Executive summary
Introduce a Tenant Profile—one canonical, structured record per tenant (tenant_id = "sute") describing the organization for question answering across all communication channels. The profile is always injected into agent prompts (same pattern as formatted_business_info). The existing knowledge base remains responsible for long-form and semantic content (documents, crawled pages, meeting transcripts).
This avoids vector search and multiple LLM rerank/filter steps for simple org facts (hours, contact, “what is SUTE?”).
2. Goals
ID | Goal |
|---|---|
G1 | One authoritative org profile per SUTE tenant |
G2 | Agents on all channels (web chat, voice, SMS, WhatsApp, intake, etc.) use the same profile data |
G3 | SUTE admins and business users can view and edit the profile via API + UI |
G4 | Predictable answers: profile facts take precedence over generic LLM knowledge |
G5 | Implement using existing patterns (BusinessInfo, TenantSetting, smart chain data providers) |
3. Non-goals (this phase)
Ecommerce / multi-store / merchant-specific profiles
Replacing the knowledge base or
fetch_knowledgePer-visitor or per-conversation profiles
Automatic website scraping into profile (optional later)
Full audit/version history UI (metadata only in v1)
i18n of profile content (agents already translate replies)
4. Background
4.1 Current behavior
BusinessInfo: Per
user_id; name, phone, address,product_information; auto-injected viaBusinessInfoSmartChainDataProvider→formatted_business_info.Knowledge base: Per
user_id; vector search + smart-chain rerank/filter; used when the LLM callsfetch_knowledge.Web chat: Conversations use
web_chat_configuration.user_idfor KB scope; visitors are anonymous (conversation_tokenonly).SUTE:
tenant_id == "sute"with dedicated features (SSO, integrations, meeting content in KB, web chat mapping). Single-org model.
4.2 Problem
Org facts are split across BusinessInfo (user-scoped), KB (heavy retrieval), and prompts.
KB path is slow and non-deterministic for simple questions (“What are your hours?”).
No tenant-scoped object representing “the organization.”
4.3 Design principle
Layer | Content | Retrieval |
|---|---|---|
Tenant profile | Short, curated, org-wide facts | Always auto-injected in prompt |
Knowledge base | Documents, webpages, meeting notes | On-demand |
5. Requirements
5.1 Functional
CRUD for exactly one profile document per tenant (
sute).Profile readable/writable by authenticated SUTE users (admins and non-admins). Anonymous web chat visitors cannot edit.
Profile exposed to smart chains as
tenant_profileandformatted_tenant_profile().SUTE inbound agents enable
tenant_profileinenabled_auto_injected_datas.Agent prompts: use profile for org facts; use
fetch_knowledgefor documents/meetings.Empty profile must not break agents.
5.2 Non-functional
Profile load cached (TTL similar to BusinessInfo: ~30–150s).
Profile text bounded (≤ ~8 KB total in prompt).
PUT idempotent; create-on-first-GET if missing.
Tenant isolation via existing multitenant Mongo adapters.
6. Architecture
┌─────────────────┐ GET/PUT ┌──────────────────────┐
│ SUTE UI │ ───────────────► │ TenantProfile API │
└─────────────────┘ └──────────┬───────────┘
▼
┌──────────────────────┐
│ Mongo: tenant_profile │
│ (unique tenant_id) │
└──────────┬───────────┘
│
User message (any channel) │
▼ │
┌──────────────────┐ │
│ AgentKernel │ │
└────────┬─────────┘ │
▼ │
┌──────────────────┐ get_by_tenant_id │
│ SmartChainEngine │ ◄────────────────────┘
│ + data providers │
└────────┬─────────┘
▼
LLM → fetch_knowledge (KB) / send_messageRule: TenantProfileSmartChainDataProvider loads by self.tenant_id, not prompt_context.user_id.
7. Data model
7.1 TenantProfile
Collection: tenant_profile
Unique index: (tenant_id)
Field | Type | Notes |
|---|---|---|
| string | Mongo id |
| string | e.g. |
| string |
|
| string |
|
| string |
|
| string | normalize like BusinessInfo |
| string |
|
| string |
|
| string |
|
| string | free text |
| string | default |
| string | max ~2000 chars |
| string | max ~2000 chars |
| string | max ~2000 chars |
| list of | max 50 entries |
| datetime |
|
| string |
|
| int | optimistic locking |
7.2 Repository
get_by_tenant_id(tenant_id) -> TenantProfile | Nonecreate(tenant_profile) -> TenantProfilesave(tenant_profile) -> TenantProfile
Pattern: MongoTenantProfileRepository, mirror MongoBusinessInfoRepository / TenantSetting.
8. API
Method | Path | Auth |
|---|---|---|
GET |
| Logged in |
PUT |
| Logged in |
Resolve tenant from request domain (existing multitenant helpers).
v1 allowlist:
tenant_id == "sute"only; else 404/403.GET: create empty profile if missing.
PUT: whitelist content fields; set
updated_at,updated_by_user_id, bumpversion; optional 409 on version mismatch.
Permissions: Any authenticated user on SUTE tenant (not admin-only like TenantSetting).
9. Smart chain integration
9.1 TenantProfileSmartChainDataProvider
module_id:tenant_profileauto_inject_smart_chain_binding_name:formatted_tenant_profileCache keyed by
tenant_id
9.2 formatted_tenant_profile.json
Chain type:
prompt_templateJinja template: emit non-empty fields + FAQs (mirror
formatted_business_info.json)
9.3 DI
Register in multitenant.py and testapp.py:
tenant_profile_repositorytenant_profile_smart_chain_data_providerinsmart_chain_data_providerslist
9.4 Agents (SUTE)
"enabled_auto_injected_datas": {
"tenant_profile": true,
"business_info": true,
"current_date_time": true
}Update: web chat inbound agent, knowledge_bot, q_and_a_agent, default_agent, SUTE voice inbound agents, other SUTE cores.
Prompt addition:
For questions about the organization (contact, hours, services, policies), use the tenant profile first. If not found, use fetch_knowledge for documents or meetings. Do not invent facts.
10. Channels
No channel-specific code. All use AgentKernel + auto-inject.
Channel | Note |
|---|---|
Web chat | Profile by tenant; KB still by conversation |
Voice / SMS / WhatsApp | Same agents + inject |
11. Caching
Provider:
AsynchronouslyReloadingMemoryCachekeyed bytenant_idOn PUT: invalidate tenant profile cache (not
clear_smart_chain_caches_for_user_idalone)
12. Coexistence
System | v1 policy |
|---|---|
TenantProfile | Source of truth for org Q&A |
BusinessInfo | Keep; profile wins for org questions in prompt |
Knowledge base | Unchanged |
TenantSetting | UI menu only |
13. Frontend
Page: Organization Profile (all logged-in SUTE users)
GET/PUT
/tenant_profileMenu entry on SUTE nav
14. Implementation phases
Phase 1 — Backend
Model, mongo repo, API, data provider, formatted smart chain, DI, tests, seed empty
suteprofile
Phase 2 — Agents
Enable auto-inject on SUTE agents; prompt updates; integration tests
Phase 3 — UI
React page + manual E2E via web chat
Phase 4 — Optional
Strict optimistic locking, audit log, expand beyond
sute
15. Testing
Unit: repo uniqueness, API auth, provider uses tenant_id not user_id
Integration: formatted chain, agent kernel with inject enabled
Manual: set phone in profile → web chat cites it; meeting facts still via KB
16. Rollout
Deploy backend (gated to
sute)Seed / fill profile in UI
Staging agent validation
Production enable
Rollback: disable
tenant_profilein agentenabled_auto_injected_datas
17. Open questions
# | Question | Default |
|---|---|---|
Q1 | All SUTE users can PUT? | Yes |
Q2 | Keep business_info inject? | Yes; profile precedence in prompt |
Q3 | API allowlist sute only? | Yes v1 |
Q4 | Auto-migrate BusinessInfo? | Manual v1 |
18. File checklist
prospera/tenant_profile/
data_models/tenant_profile.py
data_repository/tenant_profile_repository_base.py
data_repository/mongo/mongo_tenant_profile_repository.py
components/tenant_profile_smart_chain_data_provider.py
data/smart_chains/formatted_tenant_profile.json
apis/tenant_profile_api.py
frontend/TenantProfilePage.js
frontend/apis.jsAlso: multitenant.py, SUTE agent JSON, smart chain prompt JSON.
19. Success criteria
SUTE staff can edit org profile in UI
Web chat answers contact/hours/about from profile without KB in typical cases
Voice and other channels use same profile
KB still used for meetings/documents
Empty profile does not break agents