Design Document for SUTE Tenant Profile Implementation

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

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_knowledge

  • Per-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 via BusinessInfoSmartChainDataProviderformatted_business_info.

  • Knowledge base: Per user_id; vector search + smart-chain rerank/filter; used when the LLM calls fetch_knowledge.

  • Web chat: Conversations use web_chat_configuration.user_id for KB scope; visitors are anonymous (conversation_token only).

  • 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

Layer

Content

Retrieval

Tenant profile

Short, curated, org-wide facts

Always auto-injected in prompt

Knowledge base

Documents, webpages, meeting notes

On-demand fetch_knowledge


5. Requirements

5.1 Functional

  1. CRUD for exactly one profile document per tenant (sute).

  2. Profile readable/writable by authenticated SUTE users (admins and non-admins). Anonymous web chat visitors cannot edit.

  3. Profile exposed to smart chains as tenant_profile and formatted_tenant_profile().

  4. SUTE inbound agents enable tenant_profile in enabled_auto_injected_datas.

  5. Agent prompts: use profile for org facts; use fetch_knowledge for documents/meetings.

  6. 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_message

Rule: 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

Field

Type

Notes

id

string

Mongo id

tenant_id

string

e.g. "sute"

display_name

string

 

legal_name

string

 

tagline

string

 

primary_phone

string

normalize like BusinessInfo

primary_email

string

 

address

string

 

website_url

string

 

support_hours

string

free text

timezone

string

default America/Toronto

about

string

max ~2000 chars

services_summary

string

max ~2000 chars

policies_summary

string

max ~2000 chars

faq_entries

list of {question, answer}

max 50 entries

updated_at

datetime

 

updated_by_user_id

string

 

version

int

optimistic locking

7.2 Repository

  • get_by_tenant_id(tenant_id) -> TenantProfile | None

  • create(tenant_profile) -> TenantProfile

  • save(tenant_profile) -> TenantProfile

Pattern: MongoTenantProfileRepository, mirror MongoBusinessInfoRepository / TenantSetting.


8. API

Method

Path

Auth

Method

Path

Auth

GET

/tenant_profile

Logged in

PUT

/tenant_profile

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, bump version; 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_profile

  • auto_inject_smart_chain_binding_name: formatted_tenant_profile

  • Cache keyed by tenant_id

9.2 formatted_tenant_profile.json

  • Chain type: prompt_template

  • Jinja template: emit non-empty fields + FAQs (mirror formatted_business_info.json)

9.3 DI

Register in multitenant.py and testapp.py:

  • tenant_profile_repository

  • tenant_profile_smart_chain_data_provider in smart_chain_data_providers list

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

Channel

Note

Web chat

Profile by tenant; KB still by conversation user_id

Voice / SMS / WhatsApp

Same agents + inject


11. Caching

  • Provider: AsynchronouslyReloadingMemoryCache keyed by tenant_id

  • On PUT: invalidate tenant profile cache (not clear_smart_chain_caches_for_user_id alone)


12. Coexistence

System

v1 policy

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_profile

  • Menu entry on SUTE nav


14. Implementation phases

Phase 1 — Backend

  • Model, mongo repo, API, data provider, formatted smart chain, DI, tests, seed empty sute profile

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

  1. Deploy backend (gated to sute)

  2. Seed / fill profile in UI

  3. Staging agent validation

  4. Production enable

  5. Rollback: disable tenant_profile in agent enabled_auto_injected_datas


17. Open questions

#

Question

Default

#

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.js

Also: 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