Run backend and web with a shared configurable port, add local webhook and secret-generation helpers with tests, and document current app-auth and fixture workflows.
AI Platform
AI Platform provides Lead Scoring with a Python backend and internal SvelteKit Admin Web. Local app-auth verification and integration limitations are recorded in docs/contracts/app-auth-provider-handoff.md; local checks do not establish production readiness.
Runtime layout
- Python 3.13 backend package:
src/ai_platform, served asai_platform.main:app. - PostgreSQL 16 with a new, project-specific Compose volume named
ai_platform_pgdata. - Alembic schema changes are applied as a one-shot Compose service before the backend starts.
- SvelteKit is a separate web image under
apps/web. old_repo/is archived source only and is excluded from Docker build contexts and local checks.
The local Compose stack runs one backend process. It is for development only; default credentials and loopback port mappings are not deployment settings. Keep production secrets outside .env and inject them through the deployment environment or a secrets manager.
Local setup
Copy the backend/Compose template and the separate SvelteKit server-only template. Both .env files are ignored by Git; the previous root .env is preserved under old_repo/.env as historical local data, not loaded by the rebuilt service.
cp .env.example .env
cp apps/web/.env.example apps/web/.env
make install
For a new database, prepare its schema once before starting the application:
make db-up
make migrate
Then start PostgreSQL and both native servers from one terminal:
make dev
Set BACKEND_PORT once in the root .env (default 8000; use 8002 to avoid a local conflict). make backend-dev uses that port, and make web-dev passes the matching server-only BACKEND_INTERNAL_URL to Web, overriding any old URL in apps/web/.env. An exported BACKEND_PORT or make dev BACKEND_PORT=8002 can override the root value. Other root secrets are not passed to Web. The web development server normally listens at 127.0.0.1:5173 (see its terminal output). Ctrl+C stops the native servers; PostgreSQL stays running. make dev does not run migrations or start Omni/AIHub. To run the servers in separate terminals instead, use make backend-dev and make web-dev.
The local web server should call the backend through server-only configuration. Do not expose BACKEND_INTERNAL_URL, database credentials, or provider keys as browser-visible PUBLIC_* variables.
Compose can instead build and start all services, including the one-shot migration step:
docker compose up --build
The database is reachable from the host at 127.0.0.1:55432, backend at 127.0.0.1:8000, and web at 127.0.0.1:3000 by default. To discard only this new database and its data, run docker compose down -v; this does not target archived or unrelated volumes.
The local database starts with only the development database. To run tests safely, create a separate database in that same new PostgreSQL instance, then pass its URL explicitly:
docker compose exec postgres createdb -U ai_platform ai_platform_test
AI_PLATFORM_DATABASE_URL=postgresql+asyncpg://ai_platform:local-change-this-password@127.0.0.1:55432/ai_platform_test make test
make test rejects a database URL that does not contain ai_platform_test. CI provisions its own isolated PostgreSQL 16 test database.
Chấm điểm AI qua CLIProxy khi phát triển local
Không cần chạy AIHub để thử adapter OpenAI-compatible. Cấu hình URL trên backend (trong môi trường hoặc .env local đã có):
AI_PLATFORM_ENVIRONMENT=development
AI_PLATFORM_OPENAI_BASE_URL=http://127.0.0.1:8317
# AI_PLATFORM_OPENAI_API_KEY=... # chỉ cần nếu CLIProxy yêu cầu xác thực
Chạy make backend-dev trên máy host: 127.0.0.1:8317 bên trong Docker không trỏ tới CLIProxy trên host. Trong Admin Web, tạo và kích hoạt một phiên bản Cấu hình AI mới, chọn OpenAI-compatible, nhập URL http://127.0.0.1:8317, model gemini-3.5-flash-lite, prompt và timeout. AI_PLATFORM_OPENAI_BASE_URL là URL mặc định cho profile cũ và là địa chỉ duy nhất nhận AI_PLATFORM_OPENAI_API_KEY; URL khác nhập trong Web không được gửi key đó. Profile cũ mặc định AIHub; URL AIHub vẫn cấu hình bằng AI_PLATFORM_AIHUB_BASE_URL. Lựa chọn này áp dụng cho các run mới của Lead Scoring, không đổi các run đã nhận trước đó. Gửi POST /v1/lead-scoring/runs với ai_profile=medium, ai_weight>0 và app key; OpenAI-compatible không dùng X-Tenant-VKey. Trang “Thử chấm điểm” trên Web cho xem trước hội thoại và quy tắc mẫu. Mặc định chỉ chấm quy tắc (ai_weight=0); bật “Chấm thêm bằng AI” để gửi ai_profile=medium với trọng số quy tắc/AI 0.5/0.5. Chế độ này dùng provider đang active và có thể phát sinh chi phí. Mẫu không gửi V-Key nên với AIHub sẽ dự phòng bằng điểm quy tắc; xem trang chi tiết run để kiểm tra kết quả thực tế.
Với endpoint OpenAI-compatible chạy ở production, URL phải dùng HTTPS. Key được cấu hình trên server chỉ gửi tới URL được gắn với key đó; thay URL trên Web không tự chuyển key. Không đặt key vào Web hoặc file versioned. CLIProxy local chỉ xác minh được đường gọi thử; chưa chứng minh các điều kiện phát hành production.
App credentials và terminal webhook
Mỗi installation/môi trường Omni dùng một app_id ổn định và một backend app key chung cho các account. Vận hành cấp/rotate/revoke qua CLI; raw key chỉ được xuất khi cấp/rotate, chuyển an toàn sang backend Omni, không qua browser/log/Git:
uv run --no-sync ai-platform create-app omni-local
uv run --no-sync ai-platform rotate-app-key omni-local
uv run --no-sync ai-platform revoke-app-key omni-local
create-app --no-lead-scoring cấp app chưa có quyền Lead Scoring. Admin Web /platform/apps chỉ xem trạng thái và thu hồi, không hiển thị hay cấp/rotate key. POST/GET dùng Authorization: Bearer <app_api_key>; POST có thêm account_id từ Omni. Omni dùng LEAD_SCORING_API_URL, AI_PLATFORM_APP_ID, AI_PLATFORM_APP_API_KEY ở backend. Đã bỏ tenant provisioning API/secret; không đăng ký account ở AI Platform.
Tạo secret webhook bằng uv run --no-sync python scripts/generate_secret.py LEAD_SCORING_WEBHOOK_SIGNING_SECRET. Script chỉ in TEN_BIEN=SECRET với secret ngẫu nhiên 64 ký tự, không sửa file hay tạo app key. Lưu cùng giá trị secret vào cấu hình Omni và signing_secret của app tương ứng ở AI Platform; không đưa output vào Git/chat/log dùng chung. Mỗi lần chạy tạo secret mới.
Cấu hình AI_PLATFORM_APP_WEBHOOKS bằng JSON mapping app ID tới URL/signing secret, ví dụ trong .env.example. Mẫu localhost chỉ áp dụng khi backend/Omni chạy trực tiếp cùng host; với Compose phải dùng HTTPS endpoint Omni truy cập được từ container, không dùng localhost để trỏ tới host. Thiếu mapping chặn run mới với 503, nhưng replay vẫn trả run cũ. URL HTTPS, chỉ HTTP loopback development; secret ít nhất 32 ký tự. Webhook body có đúng event_id, app_id, account_id, request_id, run_id, status, occurred_at. Chữ ký HMAC-SHA256 trên timestamp + "." + raw body trong X-Webhook-Signature, timestamp mới mỗi attempt trong X-Webhook-Timestamp; không follow redirect. Retry tối đa 5 attempts rồi dead. Vận hành redeliver cùng event, không rescore:
uv run --no-sync ai-platform redeliver-notification <event_id>
Generated OpenAPI và các fixture tại contracts/lead-scoring/v2/; chạy make contract để tái sinh và uv run --no-sync python scripts/export_contract_fixtures.py --check để kiểm tra drift fixture mà không sửa file. Omni cần đối chiếu callback/GET/source request trước writeback; provider checks chưa chứng minh tích hợp Omni/EMS.
Schema chưa phát hành hiện có một migration 0001_initial, tạo thẳng app credentials, admin, AI profiles, runs và outbox. Database mới chạy make migrate. Database local còn revision cũ không tự upgrade sang baseline: dùng database mới, hoặc xác nhận phạm vi dữ liệu cần giữ/bỏ trước khi dựng lại hay stamp sau kiểm tra schema; không chạy alembic stamp head để che schema khác biệt. Startup vẫn kiểm tra revision và không tự migrate. Lịch sử migrations sau lần phát hành đầu tiên phải được giữ.
scripts/seed_demo.py yêu cầu app key đã cấp qua AI_PLATFORM_DEMO_APP_API_KEY, không tự provisioning. Fixture runner nội bộ dùng LEAD_SCORING_FIXTURE_APP_API_KEY server-only, ưu tiên app test riêng có webhook mock.
Backend dọn run quá 90 ngày (gồm snapshot, fingerprint và thông báo liên quan) khi khởi động và mỗi ngày. Backup và app/account offboarding cần chính sách riêng trước production.
Quality checks
The opt-in pre-commit runner is installed by make install as a project-local development dependency. Run:
make check-fast
make check
make check-fast runs the pinned Ruff and pre-commit-hooks rules. make check additionally runs Python type/import checks and tests; tests must use a separate test database, never a developer or production database. CI uses the same fast hooks and the Makefile test/check commands with an isolated PostgreSQL 16 service.
Operations notes
- Apply migrations as a release step; application startup must not migrate the database.
/health/liveis liveness;/health/readychecks readiness and PostgreSQL. The Compose backend health check waits for readiness.- Avoid logging credentials, request payloads containing personal data, or provider responses with sensitive content.
- The Compose PostgreSQL volume is disposable development data. Backup, restore, retention, secrets, TLS, and alerting must be defined for the eventual deployment target.
The web container currently assumes package.json, bun.lock, a production SvelteKit build at build/index.js, and Bun compatibility with the selected SvelteKit adapter. Confirm those assumptions with the web implementation before treating the image as verified.