Spaces:
Running
Author RAG β Architecture Reference
π€ AI AGENT MANDATORY PROTOCOL
THIS BLOCK IS FOR AI AGENTS (including Antigravity / Gemini). FOLLOW ON EVERY PROMPT.
On EVERY new request you must:
- READ this file first before reading any source file or writing any code.
- CHECK Β§9 Invariants β verify your planned change does not violate any rule.
- CHECK Β§8 Where to Add Features β confirm you are editing the correct file for the task.
- After completing any change that adds a file, route, model column, config value, or service β UPDATE this document to reflect the new state.
- Push the updated
ARCHITECTURE.mdin the same commit as the code change. They must never go out of sync.
What counts as a required update:
| Change made | Section to update |
|---|---|
| New file created | Β§4 Full File Tree |
| New route added | Β§4 (correct sub-section) |
| New DB column added | Β§4 Models table + Β§8 |
| New config value added | Β§7 Configuration |
| New invariant established | Β§9 Invariants |
| New feature area added | Β§8 Where to Add Features |
| Architecture restructured | Β§3 Layer diagram + Β§4 |
NEVER do this:
- Edit source files without checking Β§9 first.
- Add a new file without adding it to Β§4.
- Define a new guard/detector outside
guards.py. - Add prompt text outside
prompter.py. - Skip updating this document after a structural change.
Purpose: This document is the single source of truth for any AI agent or human developer joining this codebase. Read this file first before opening any source file. It answers: What does each file do? How does a request flow? Where do I add X?
1. What This System Does
Author RAG is a multi-tenant SaaS platform. Each customer (an author) gets an AI-powered chatbot that lives on their website and answers questions about their books. The chatbot is a persuasive sales tool β it understands reader intent, retrieves precise passages from the book, and surfaces purchase links at the right moment.
Key numbers:
- Runtime: Python 3.11, FastAPI async
- Database: SQLite (dev) / PostgreSQL (prod) via SQLAlchemy async
- Vector store: ChromaDB (local persistent)
- LLM: OpenAI GPT-4o-mini (configurable)
- Embeddings:
text-embedding-3-small
2. Top-Level Directory Layout
Author RAG/
βββ app/ β All application code (see Β§4)
βββ static/ β Compiled frontend (admin SPA, widget JS)
βββ requirements.txt β Python dependencies
βββ Dockerfile β HuggingFace Spaces deployment
βββ ARCHITECTURE.md β THIS FILE
βββ .env β Secrets (never committed)
3. Layer Architecture
The codebase follows a strict 5-layer architecture. Data only flows downward. A layer may never import from a layer above it.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Layer 1 β API / Routers β
β app/api/, app/admin/routers/, app/superadmin/ β
β β’ Accepts HTTP requests β
β β’ Validates input (Pydantic schemas) β
β β’ Delegates to services β no business logic here β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Layer 2 β Services / Pipeline β
β app/services/ β
β β’ All business logic lives here β
β β’ The RAG pipeline is the core of this layer β
β β’ May call repositories and other services β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Layer 3 β Repositories β
β app/repositories/ β
β β’ All database queries live here β
β β’ Returns model objects β never raw SQL β
β β’ No business logic β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Layer 4 β Models β
β app/models/ β
β β’ SQLAlchemy ORM table definitions only β
β β’ No methods with logic β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Layer 5 β Core / Infrastructure β
β app/core/, app/config.py, app/dependencies.py β
β β’ DB engine, Redis, ChromaDB clients β
β β’ JWT / token crypto β
β β’ No domain logic β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
4. Full File Tree with Descriptions
app/ β Root
| File | Role |
|---|---|
main.py |
FastAPI app factory. Registers middleware, exception handlers, routers. Startup delegates to core/startup/. |
config.py |
All env-var settings via Pydantic BaseSettings. One source of truth for every config value. |
dependencies.py |
FastAPI Depends() providers: DB session, Redis, auth guards (get_current_author_scoped, get_subscription_author, get_current_superadmin). |
app/core/ β Infrastructure
| File | Role |
|---|---|
core/startup/db.py |
init_db() β creates all tables + runs incremental ALTER TABLE migrations on startup. |
core/startup/seeder.py |
seed_superadmin() β bootstraps the superadmin account from env vars on first run. |
core/access/token_crypto.py |
Creates and verifies signed subscription tokens (widget embed). |
core/access/subscription.py |
Subscription validation logic (expiry, budget, revocation). |
core/access/jwt_blacklist.py |
Redis-backed JWT revocation list. |
core/access/totp.py |
TOTP 2FA for SuperAdmin login. |
core/chroma_client.py |
Singleton ChromaDB client. |
core/security.py |
hash_password(), verify_password(). |
app/api/ β Public Widget API
These routes are called by the embedded widget on the author's website.
All require X-Subscription-Token header (validated via get_subscription_author).
| File | Routes | Role |
|---|---|---|
api/chat.py |
POST /api/chat/{slug} |
Main chat endpoint β calls run_pipeline(). |
POST /api/chat/{slug}/session/init |
Creates a new visitor session. | |
GET /api/chat/{slug}/session/history/{id} |
Returns chat history. | |
POST /api/chat/{slug}/session/farewell |
Graceful session close + summarization. | |
POST /api/chat/{slug}/session/rate |
Star rating. | |
POST /api/chat/{slug}/track-click |
Records buy-link click. | |
POST /api/chat/{slug}/feedback |
π/π message feedback. | |
GET /api/chat/{slug}/events |
SSE stream for ingestion progress. | |
api/ingest.py |
POST /api/admin/{slug}/books/upload |
Book PDF upload + async embedding pipeline. |
api/widget.py |
POST /api/widget/token |
Returns a signed subscription token for the embed script. |
api/schemas_router.py |
/api/auth/* |
Login, register, token refresh. |
app/admin/ β Author Admin API
Prefix: /api/admin/{author_slug}/
Auth: Bearer JWT (get_current_author_scoped)
| File | Routes | Role |
|---|---|---|
admin/router.py |
β | 20-line aggregator. Mounts all 7 sub-routers. |
admin/routers/dashboard.py |
/sessions, /sessions/search, /sessions/{id}/transcript, /sessions/{id}/block, /sessions/{id}/messages/{mid}/annotate |
Session management and message review. |
admin/routers/books.py |
/books, /books/{id}, /books/{id}/cover |
Book list, delete, cover upload. |
admin/routers/analytics.py |
/analytics, /analytics/funnel, /analytics/intents, /analytics/visitors, /analytics/geo, /analytics/devices, /analytics/sessions/stats |
Dashboard charts, funnel data, visitor analytics. |
admin/routers/settings.py |
/password, /widget-config, /profile, /personality, /notifications, /embed-token, /token-usage |
All author account settings. |
admin/routers/links.py |
/smart-links/{book_id} |
Buy/preview URL management. |
admin/routers/qa.py |
/qa, /qa/import, /qa/export, /qa/{id} |
Custom Q&A training data (CRUD + CSV). |
admin/routers/exports.py |
/export/sessions, /export/analytics, /export/conversations |
CSV data exports. |
app/superadmin/ β SuperAdmin API
Prefix: /api/super/
Auth: Bearer JWT + TOTP (get_current_superadmin)
| File | Routes | Role |
|---|---|---|
superadmin/router.py |
β | 15-line aggregator. Mounts 3 sub-routers. |
superadmin/routers/platform.py |
/, /diag, /health, /audit, /announce, /backup |
Platform diagnostics, audit log, announcements. |
superadmin/routers/authors.py |
/authors, /authors/{id}, /authors/{id}/suspend, /authors/{id}/grant, /authors/{id}/embed-token |
Full author account lifecycle. |
superadmin/routers/grants.py |
/grants, /grants/{id}/revoke, /grants/{id}/bonus-tokens, /grants/{id}/extend, /grants/{id}/reset-tokens, /grant |
Subscription token budget management. |
superadmin/routers/_utils.py |
β | Shared _err() error handler (no routes). |
app/services/pipeline/ β The RAG Pipeline β
This is the core of the product. Every visitor message flows through run_pipeline().
app/services/pipeline/
βββ __init__.py β Public API: run_pipeline, PipelineResult, invalidate_book_cache
βββ core.py β 12-step orchestrator (the conductor, ~200 lines)
βββ generation.py β Steps 8β10: LLM call + faithfulness retry + safety scrub
βββ handlers.py β Short-circuit response functions (greeting, catalog, piracy...)
βββ helpers.py β Pure stateless utilities (call_llm, format_history, get_book_links...)
βββ guards.py β Boolean detectors (is_greeting, is_full_story_request, ...) β SINGLE SOURCE OF TRUTH
βββ cache.py β LRU answer cache (256 slots, MD5 key)
βββ dedup.py β Chunk deduplication by Jaccard overlap
The 12-Step Pipeline (core.py)
Step 0 sanitize_user_input() Strip dangerous/empty input
Step 1 check_boundary() Jailbreak / piracy / off-topic guard
Step 1.5 check_custom_qa() Short-circuit: exact Q&A match (no LLM)
Step 2 classify_intent() 3-tier: rules β cache β LLM-as-fallback
Step 3 Book resolution Greeting / catalog / book-select short-circuits
LRU cache lookup Skip steps 4-12 on cache hit
Step 4 rewrite_query() Generate 3 query variations for multi-pass retrieval
Step 5 retrieve_chunks() ChromaDB vector search (top_k=15 default)
Step 6 rerank_chunks() Cross-encoder re-ranking (top_n=5 default)
Step 6.5 deduplicate_chunks() Remove near-duplicate overlapping windows
Step 7 build_context() Token-aware context assembly (max 2000 tokens)
Step 8 MASTER_SYSTEM_PROMPT.format Assemble full prompt with history + interest + context
call_llm() OpenAI chat completions
Step 9 check_faithfulness() NLI guardrail β retry with stricter prompt if hallucinating
Step 10 scrub_unsafe_response() Output safety scrub + is_response_safe() check
Step 11 UpsellEngine.select_strategy() Choose upsell strategy based on intent + session state
Step 12 ResponseFormatter.format() Assemble final JSON response with optional buy button
cache_set() Store result for future identical questions
Where to look for what:
| Task | File |
|---|---|
| Change the LLM model or temperature | config.py β OPENAI_CHAT_MODEL, RAG_TEMPERATURE |
| Edit the system prompt | services/prompter.py |
| Change what counts as a greeting | services/pipeline/guards.py β _GREETINGS |
| Change upsell logic | services/upsell_engine.py |
| Change faithfulness threshold | services/faithfulness.py |
| Change how queries are rewritten | services/rewriter.py |
| Change intent labels / rules | services/intent.py |
| Change chunking strategy | services/chunker.py |
| Change re-ranking | services/reranker.py |
| Change context token limit | config.py β RAG_CONTEXT_MAX_TOKENS |
app/services/ β All Other Services
| File | Role |
|---|---|
rag_pipeline.py |
Compatibility shim only. Imports from pipeline/ and re-exports. |
intent.py |
3-tier intent classifier: keyword rules β session cache β LLM fallback. |
rewriter.py |
Query rewriter β generates 3 variations using conversation context. |
guardrails.py |
Input sanitization, boundary checks, output safety, response scrubbing. |
prompter.py |
All prompt templates: MASTER_SYSTEM_PROMPT, boundary responses, upsell hooks. |
faithfulness.py |
NLI-based faithfulness scoring against retrieved context. |
formatter.py |
ResponseFormatter β assembles the final JSON {text, links, has_links} dict. |
upsell_engine.py |
UpsellEngine β selects upsell strategy, decides if link should show. |
context_builder.py |
Token-aware context string builder from ranked chunks. |
vector_store.py |
ChromaDB retrieval β multi-query search + score filtering. |
reranker.py |
Cross-encoder re-ranking of retrieved chunks. |
embeddings.py |
Generates and stores OpenAI embeddings into ChromaDB. |
chunker.py |
Splits book text into overlapping sliding-window chunks. |
parser.py |
Extracts clean text from PDF uploads. |
auth_service.py |
Register, login, token refresh logic. |
superadmin_service.py |
Business logic for author lifecycle (suspend, delete, grant). |
token_budget.py |
Token usage tracking and budget enforcement. |
email_service.py |
Transactional email sending. |
rate_limiter.py |
Per-IP / per-author rate limiting. |
file_validator.py |
PDF upload validation (size, MIME type, header). |
session_store.py |
Redis-backed session state persistence. |
summarizer.py |
End-of-session conversation summarizer. |
notifications.py |
In-app notification helpers. |
analytics.py |
Analytics event recording facade. |
services/analytics_core/
| File | Role |
|------||------|
| tracker.py | Records AnalyticsEvent rows after each chat turn. |
| aggregator.py | Pre-aggregates daily analytics for dashboard charts. |
| geo.py | IP β country/region/city lookup (MaxMind GeoLite2). Exposes get_real_ip() (proxy-aware). |
| visitor_tracker.py | record_visitor() β upserts Visitor table on every session/init. Deduplicates via localStorage UUID then fingerprint fallback. |
services/session_core/
| File | Role |
|---|---|
manager.py |
SessionManager + SessionContext β the session state object passed through the pipeline. |
context.py |
Interest score and tag tracking across turns. |
fingerprint.py |
Visitor fingerprint generation (SHA-256, no PII). |
app/models/ β Database Tables
| File | Table | Key columns |
|---|---|---|
user.py |
users |
id, email, role (author/superadmin), bot_name, chatbot_is_active |
book.py |
books |
id, author_id, title, status, chroma_collection_id, chunk_count, buy_url |
chat_session.py |
chat_sessions, chat_messages |
Session: visitor_fingerprint, visitor_uid, turn_count, rating, blocked. Message: role, intent, faithfulness_score, hallucination_detected |
client_access.py |
client_access |
author_id, plan, token_budget, tokens_used, expires_at, is_revoked |
custom_qa.py |
custom_qa |
author_id, question, answer, match_threshold, priority, match_count |
analytics.py |
analytics_events |
author_id, session_id, intent, link_clicked, prompt_tokens, response_ms |
link.py |
links |
author_id, book_id, purchase_url, preview_url |
document.py |
documents |
book_id, filename, status, pages |
visitor.py |
visitors |
author_id, visitor_uid (UUID, unique per author), fingerprint, first_seen, last_seen, page_views, country_code, region, city, device_type, browser, os |
app/repositories/ β Database Access Layer
| File | Role |
|------||------|
| base.py | BaseRepository β shared get_by_id, save, delete methods. |
| user_repo.py | UserRepository β lookup by email/slug, list all authors. |
| book_repo.py | BookRepository β list_active_for_author(), list_for_author(). |
| access_repo.py | AccessRepository β get_active_for_author() β used everywhere for budget checks. |
| link_repo.py | LinkRepository β get_for_book(), upsert_for_book(). |
| audit_repo.py | AuditRepository β append-only log(), list_recent(). |
| document_repo.py | DocumentRepository β book document tracking. |
| visitor_repo.py | VisitorRepository β get_by_uid(), get_by_fingerprint(), create(), touch(), distribution queries for analytics. |
app/schemas/ β Pydantic Request/Response Schemas
| File | Role |
|---|---|
chatbot.py |
ChatRequest, ChatResponse, SessionInitResponse, FarewellRequest |
admin.py |
AnnotateRequest, FlagRequest, PasswordChangeRequest, ProfileUpdate, WidgetConfigUpdate |
superadmin.py |
CreateAuthorRequest, GrantAccessRequest, RevokeAccessRequest, AddBonusTokensRequest |
auth.py |
LoginRequest, RegisterRequest, TokenResponse |
app/middleware/ β Request Middleware
| File | Role |
|---|---|
logging_middleware.py |
Structured request/response logging with structlog. |
rate_limit_middleware.py |
Sliding-window rate limiting (configurable per route type). |
security_headers.py |
CSP, HSTS, X-Frame-Options headers. |
metrics.py |
Prometheus metrics collection (optional). |
app/tasks/ β Background Tasks
| File | Role |
|---|---|
ingestion_task.py |
Async book processing: parse β chunk β embed β store in ChromaDB. |
analytics_task.py |
Periodic analytics aggregation. |
email_task.py |
Queued email sending. |
backup_task.py |
SQLite + ChromaDB backup. |
expiry_check_task.py |
Subscription expiry warnings. |
geo_update_task.py |
Retroactive geo enrichment for sessions. |
link_health_task.py |
Checks buy links for 404s. |
celery_app.py |
Celery app instance (optional β tasks can run inline). |
5. A Complete Request Trace
Scenario: A reader on an author's website asks "Is the ending happy?"
Browser (widget JS)
β POST /api/chat/{author_slug}
β Headers: X-Subscription-Token: <signed JWT>
β Body: { "message": "Is the ending happy?", "session_id": "..." }
βΌ
app/api/chat.py β chat()
β 1. get_subscription_author() Verify token, check budget, load author
β 2. SessionManager.load() Load session from Redis (history, book selection)
β 3. run_pipeline(query, author, session_context, db)
βΌ
app/services/pipeline/core.py β run_pipeline()
β Step 0: sanitize_user_input() β "Is the ending happy?"
β Step 1: check_boundary() β no violation
β Step 1.5: check_custom_qa() β no match
β Step 2: classify_intent() β intent="question", confidence=0.92
β Step 3: BookRepository.list_active_for_author() β [Book("The Last Signal")]
β is_greeting() β False
β is_catalog_question() β False
β LRU cache lookup β miss
β Step 4: rewrite_query() β ["Is the ending happy?", "How does it end?", "resolution of the story"]
β Step 5: retrieve_chunks() β 15 chunks from ChromaDB
β Step 6: rerank_chunks() β top 5 chunks scored by cross-encoder
β Step 6.5: deduplicate_chunks() β 4 unique chunks
β Step 7: build_context() β 1,840 tokens of context
βΌ
app/services/pipeline/generation.py β generate_response()
β Step 8: MASTER_SYSTEM_PROMPT.format(...)
β call_llm() β "Without giving anything away, I can say..."
β Step 9: check_faithfulness() β faithful=True, score=0.91
β Step 10: is_response_safe() β True
β return (response, 0.91, False, 312, 87)
βΌ
core.py (continues)
β Step 11: UpsellEngine.select_strategy() β "SOFT_MENTION"
β should_include_link() β True (turn 4, high interest)
β get_book_links() β purchase_url="https://amazon.com/..."
β Step 12: ResponseFormatter.format() β {text, links:[{label:"Get the book", url:...}], has_links:True}
β cache_set() β stored for next identical query
βΌ
app/api/chat.py (continues)
β SessionManager.save() Update session: turn_count++, history appended
β analytics.record_event() Log intent, tokens, latency, link_shown
β token_budget.deduct() Deduct prompt+completion tokens from grant
βΌ
HTTP 200 { "message": "Without giving anything away...", "links": [...] }
βΌ
Browser (widget JS)
Renders response + "Get the book" button
6. Authentication Model
Three distinct auth tiers with separate dependencies:
Tier 1: Visitor (Widget)
β Header: X-Subscription-Token (signed JWT, no login needed)
β Dependency: get_subscription_author()
β Checks: token signature, expiry, token budget, author active
Tier 2: Author (Admin)
β Header: Authorization: Bearer <JWT>
β Dependency: get_current_author_scoped()
β Checks: JWT validity, author role, slug matches token
Tier 3: SuperAdmin
β Header: Authorization: Bearer <JWT>
β Dependency: get_current_superadmin()
β Checks: JWT validity, superadmin role, TOTP verified
7. Key Configuration Values
All in app/config.py (loaded from .env):
| Variable | Default | Effect |
|---|---|---|
OPENAI_CHAT_MODEL |
gpt-4o-mini |
LLM used for response generation |
OPENAI_EMBED_MODEL |
text-embedding-3-small |
Embedding model |
RAG_RETRIEVAL_TOP_K |
15 |
Chunks fetched from ChromaDB |
RAG_RERANK_TOP_N |
5 |
Chunks kept after re-ranking |
RAG_RERANK_MIN_SCORE |
0.3 |
Min cross-encoder score to keep |
RAG_CONTEXT_MAX_TOKENS |
2000 |
Max tokens in assembled context |
RAG_MAX_RESPONSE_TOKENS |
300 |
Max tokens in LLM response |
RAG_TEMPERATURE |
0.4 |
LLM temperature |
RAG_BOOK_CONFIDENCE_THRESHOLD |
0.7 |
Min score to route to a specific book |
SUPERADMIN_EMAIL |
β | Seeded on first startup |
SUPERADMIN_PASSWORD |
β | Seeded on first startup |
8. Where to Add New Features
| Task | Where to add it | Notes |
|---|---|---|
| New author admin route | app/admin/routers/<closest>.py |
Or create a new file + mount in admin/router.py |
| New superadmin route | `app/superadmin/routers/<platform | authors |
| New intent label | app/services/intent.py β _RULE_MAP |
Add keyword rules first; LLM only as fallback |
| New guard/detector | app/services/pipeline/guards.py |
Never define is_greeting elsewhere |
| New short-circuit response | app/services/pipeline/handlers.py |
Must return PipelineResult |
| New prompt template | app/services/prompter.py |
Keep ALL prompt text in one file |
| New DB column | app/models/<model>.py + app/core/startup/db.py β _NEW_COLUMNS |
No Alembic needed |
| New background task | app/tasks/<name>_task.py |
Register in tasks/celery_app.py if async |
| New config value | app/config.py |
Always typed, always has a default |
9. Invariants β Rules That Must Never Be Broken
- Services never import from routers. Data flows down only.
guards.pyis the single source of truth for all boolean detectors. Never re-defineis_greeting()elsewhere.- All prompt text lives in
prompter.py. No inline f-strings with user-facing text in other files. - All pipeline results return
PipelineResult. Never return a raw dict fromrun_pipeline(). - Repositories never contain business logic. If it's a decision, it belongs in a service.
- Never skip a pipeline step. Steps 0β12 run for every message; short-circuits return early from
core.py, not by skipping steps. - Cache only cacheable intents.
purchase_intent,complaint,greetingare never cached (time/person-sensitive). - Token usage must always be recorded. Every LLM call goes through
call_llm()inhelpers.pywhich returns token counts. - All new DB columns go into
core/startup/db.py β _NEW_COLUMNS. This keeps migrations consistent across environments. - All exceptions have typed exception classes in
app/exceptions/. Neverraise HTTPExceptiondirectly in service layer. - This document must be updated on every structural change. See Β§AI AGENT MANDATORY PROTOCOL above.
10. Change Log
AI agents: append an entry here after every significant change. Format:
YYYY-MM-DD | What changed | Files affected.
| Date | Change | Files Affected |
|---|---|---|
| 2026-06-19 | Initial project build β auth, chat API, admin panel, ingestion pipeline | All |
| 2026-06-22 | Intelligence overhaul β Python-first intent classifier, hybrid rewriter, LRU cache, chunk dedup, natural upsell | intent.py, rewriter.py, upsell_engine.py, rag_pipeline.py |
| 2026-06-23 | Modularity refactor Phase 1 β admin/router.py split into 7 sub-routers, rag_pipeline.py split into pipeline/ package |
admin/router.py, admin/routers/*, services/pipeline/*, services/rag_pipeline.py |
| 2026-06-23 | Modularity refactor Phase 2 β superadmin/router.py split into 3 sub-routers, main.py startup extracted to core/startup/, pipeline/core.py generation extracted to pipeline/generation.py |
superadmin/router.py, superadmin/routers/*, core/startup/*, services/pipeline/generation.py, main.py |
| 2026-06-23 | Added ARCHITECTURE.md with AI Agent Mandatory Protocol |
ARCHITECTURE.md |
| 2026-06-23 | Production Improvements Phases 1β5: 8 bugs fixed, 7 features added. B1 fingerprint null crash, B2 silent truncation, B3 OpenAI client per-call, B4 no LLM retry, B5 piracyβjailbreak wrong intent, B6 history only 3 turns, B8 no budget warning, B9 sync ChromaDB in async loop. New: per-visitor rate limit, input cap, tenacity retry, intent rule expansion (+30% coverage), history fix, budget email warning, async health check. | config.py, middleware/rate_limit_middleware.py, api/chat.py, services/pipeline/core.py, services/pipeline/helpers.py, services/intent.py, dependencies.py, services/token_budget.py, requirements.txt |
| 2026-06-23 | Visitor Analytics System: Replaced session-based visitor counting with proper deduplication. New Visitor model (one row per browser per author), VisitorRepository, visitor_tracker.py service. session/init now accepts visitor_uid (localStorage UUID) in request body β HF-Spaces-safe (no cookies). 4 new analytics endpoints: /visitors, /geo, /devices, /sessions/stats. geo.py updated with region extraction and X-Real-IP header support. |
models/visitor.py, repositories/visitor_repo.py, services/analytics_core/visitor_tracker.py, services/analytics_core/geo.py, api/chat.py, admin/routers/analytics.py, schemas/chatbot.py, models/chat_session.py, core/startup/db.py, models/__init__.py |