Feature Flags Reference¶
Curaway uses Flagsmith for feature flag management. Flags control which features are active in each environment and allow gradual rollouts without code deploys.
Architecture¶
- Backend: The Python backend queries Flagsmith on startup and caches flag values for 60 seconds (configurable via
FLAGSMITH_CACHE_TTL). This prevents excessive API calls and avoids the Flagsmith warning flood issue documented in the troubleshooting runbook. - Frontend: The React frontend uses the Flagsmith React SDK and evaluates flags client-side based on the current tenant identity.
- Targeting: Flags can be targeted by tenant ID, environment, or percentage rollout.
Flag Inventory¶
agent_enhanced_matching¶
Controls whether the AI agent uses the enhanced multi-signal matching pipeline that combines Qdrant semantic search, Neo4j graph traversal, and LLM re-ranking.
| Property | Value |
|---|---|
| Type | Boolean |
| Default | false |
| Targeting | Enabled for tenant-apollo-001 in production; enabled for all tenants in staging |
| Impact | When disabled, matching falls back to simple SQL-based filtering by country and procedure |
| Dependencies | Requires Qdrant and Neo4j to be available; falls back gracefully if either is down |
agent_explanations_enabled¶
Controls whether the AI agent generates natural-language explanations for why each provider was recommended.
| Property | Value |
|---|---|
| Type | Boolean |
| Default | false |
| Targeting | Enabled for tenant-apollo-001 |
| Impact | When enabled, each match result includes a match_explanation field with a 2-3 sentence LLM-generated rationale |
| Dependencies | Requires agent_enhanced_matching to also be enabled |
| Cost note | Each explanation costs ~200 tokens (gpt-4o-mini); with 5 providers per match, this adds ~1000 tokens per query |
guardrail_input_classifier_enabled¶
Controls whether inbound user messages are classified by the guardrail system before reaching the LLM.
| Property | Value |
|---|---|
| Type | Boolean |
| Default | true |
| Targeting | Enabled globally; can be disabled per-tenant for debugging |
| Impact | When disabled, all user messages pass directly to the LLM without safety classification |
| Risk | Disabling in production exposes the agent to prompt injection and off-topic abuse |
guardrail_output_validator_enabled¶
Controls whether LLM responses are scanned against forbidden output patterns before being sent to the user.
| Property | Value |
|---|---|
| Type | Boolean |
| Default | true |
| Targeting | Enabled globally |
| Impact | When disabled, LLM responses are returned to the user without output validation |
| Risk | Disabling in production may allow the agent to return medical advice, internal URLs, or other prohibited content |
guardrail_file_validation_enabled¶
Controls whether uploaded files are validated against the size, type, and content rules defined in config/guardrails.yaml.
| Property | Value |
|---|---|
| Type | Boolean |
| Default | true |
| Targeting | Enabled globally |
| Impact | When disabled, files are accepted without pre-upload validation (R2 still enforces its own size limits) |
| Risk | Disabling may allow oversized or unsupported file types to reach the processing pipeline |
DOCTORS_IN_MATCHING¶
Controls whether the matching pipeline considers individual doctors in addition to provider-level matches. When enabled, the system returns both the matched provider and the specific doctor(s) at that provider who are most relevant.
| Property | Value |
|---|---|
| Type | Boolean |
| Default | false |
| Targeting | Enabled for tenant-apollo-001 in production |
| Impact | When enabled, match results include a matched_doctors array with doctor profiles, specialties, languages, and procedure experience |
| Dependencies | Requires the doctors table to be seeded (see seed data runbook) |
PUBLIC_API_ENABLED¶
Controls the entire /api/v1/public/ router. When disabled, all public storefront endpoints return 503 Service Unavailable.
| Property | Value |
|---|---|
| Type | Boolean |
| Default | false |
| Targeting | Disabled globally; enable per-environment when storefront is ready |
| Impact | When disabled, all /api/v1/public/* endpoints return 503 |
STOREFRONT_PAGES_ENABLED¶
Controls frontend storefront routes. When disabled, storefront URLs redirect to the homepage.
| Property | Value |
|---|---|
| Type | Boolean |
| Default | false |
| Targeting | Disabled globally; enable when storefront UI is complete |
| Impact | When disabled, frontend storefront routes redirect to homepage |
| Dependencies | Requires PUBLIC_API_ENABLED for data fetching |
PUBLIC_SEARCH_ENABLED¶
Controls the unified search endpoint independently from the rest of the public API.
| Property | Value |
|---|---|
| Type | Boolean |
| Default | false |
| Targeting | Disabled globally; can be enabled independently of other storefront flags |
| Impact | When disabled, /api/v1/public/search returns 503 even if PUBLIC_API_ENABLED is on |
STOREFRONT_CACHE_ENABLED¶
Controls whether storefront responses are cached. When disabled, all requests hit the database directly (useful for debugging).
| Property | Value |
|---|---|
| Type | Boolean |
| Default | false |
| Targeting | Disabled globally; enable in production for performance |
| Impact | When disabled, every storefront request queries the database directly |
PROVIDER_COMPLETENESS_DISPLAY¶
Controls whether completeness badges are shown on provider listing cards in the storefront.
| Property | Value |
|---|---|
| Type | Boolean |
| Default | false |
| Targeting | Disabled globally; enable when completeness scoring is validated |
| Impact | When disabled, provider cards do not display completeness badges |
| Dependencies | Requires PUBLIC_API_ENABLED and STOREFRONT_PAGES_ENABLED |
embedding_document_matching¶
Controls whether uploaded patient documents (medical reports, imaging) are embedded and used as additional signals in provider matching.
| Property | Value |
|---|---|
| Type | Boolean |
| Default | false |
| Targeting | Disabled globally; experimental feature |
| Impact | When enabled, the system extracts text from uploaded documents via OCR, generates embeddings, and includes document-based similarity scores in the matching pipeline |
| Dependencies | Requires guardrail_file_validation_enabled to be true; requires Qdrant |
| Cost note | Each document embedding costs ~500-2000 tokens depending on document length |
Adding New Flags¶
- Create the flag in the Flagsmith dashboard with a descriptive name using snake_case.
- Set the default value and any tenant-level overrides.
- In the backend, access the flag via the
flagsmith_client:
from app.core.flagsmith import flagsmith_client
if flagsmith_client.get_flag_value("my_new_flag", tenant_id=tenant_id):
# Feature-flagged code path
...
- In the frontend, use the Flagsmith React hook:
import { useFlags } from 'flagsmith/react';
function MyComponent() {
const flags = useFlags(['my_new_flag']);
if (flags.my_new_flag.enabled) {
return <NewFeature />;
}
return <ExistingFeature />;
}
- Document the flag in this page following the format above.
Flag Lifecycle¶
| Stage | Description |
|---|---|
| Experimental | Flag defaults to false; enabled only for specific test tenants |
| Beta | Flag enabled for a subset of production tenants via targeting |
| GA (General Availability) | Flag defaults to true for all tenants |
| Cleanup | Flag is removed from code; the feature is permanently enabled |
Flags should not remain in the "Experimental" stage for more than 90 days. If a feature is not promoted to Beta within that window, it should be removed.