c47cc2f5bf
feat(03-05): mount Phase 3 API routers on gateway FastAPI app
...
- Import all 6 Phase 3 routers from shared.api (portal, billing, channels, llm_keys, usage, webhook)
- Add include_router() calls after existing whatsapp_router
- Update module docstring to document portal API endpoints
2026-03-24 00:53:32 -06:00
9dd7c481a3
feat(02-05): Slack file_share extraction and channel-aware outbound routing
...
- Add gateway/channels/slack_media.py with is_file_share_event, media_type_from_mime,
build_slack_storage_key, build_attachment_from_slack_file, download_and_store_slack_file
- Add _send_response() helper to orchestrator/tasks.py for channel-aware dispatch
(Slack -> chat.update, WhatsApp -> send_whatsapp_message)
- Add send_whatsapp_message import to orchestrator/tasks.py for WhatsApp outbound
- Add boto3>=1.35.0 to gateway dependencies for MinIO S3 client
- Add 23 unit tests in test_slack_media.py (TDD)
2026-03-23 15:06:45 -06:00
6fea34db28
feat(02-03): WhatsApp adapter with business-function scoping and router registration
...
- Register whatsapp_router in gateway main.py (GET + POST /whatsapp/webhook)
- Implement is_clearly_off_topic() tier 1 keyword scoping gate
- Implement build_off_topic_reply() canned redirect message builder
- Full webhook handler: verify -> normalize -> tenant -> rate limit -> dedup -> scope -> media -> dispatch
- Outbound delivery via send_whatsapp_message() and send_whatsapp_media()
- Media download from Meta API and storage in MinIO with tenant-prefixed keys
- 14 new passing scoping tests
2026-03-23 14:43:04 -06:00
370a860622
feat(02-03): add MediaAttachment model, WhatsApp normalizer, and signature verification
...
- Add MediaType(StrEnum) and MediaAttachment(BaseModel) to shared/models/message.py
- Add media: list[MediaAttachment] field to MessageContent
- Add whatsapp_app_secret, whatsapp_verify_token, and MinIO settings to shared/config.py
- Add normalize_whatsapp_event() to gateway/normalize.py (text, image, document support)
- Create whatsapp.py adapter with verify_whatsapp_signature() and verify_hub_challenge()
- 30 new passing tests (signature verification + normalizer)
2026-03-23 14:41:48 -06:00
6f30705e1a
feat(01-03): Channel Gateway (Slack adapter) and Message Router
...
- gateway/normalize.py: normalize_slack_event -> KonstructMessage (strips bot mention)
- gateway/channels/slack.py: register_slack_handlers for app_mention + DM events
- rate limit check -> ephemeral rejection on exceeded
- idempotency dedup (Slack retry protection)
- placeholder 'Thinking...' message posted in-thread before Celery dispatch
- auto-follow engaged threads with 30-minute TTL
- HTTP 200 returned immediately; all LLM work dispatched to Celery
- gateway/main.py: FastAPI on port 8001, /slack/events + /health
- router/tenant.py: resolve_tenant workspace_id -> tenant_id (RLS-bypass query)
- router/ratelimit.py: check_rate_limit Redis token bucket, RateLimitExceeded exception
- router/idempotency.py: is_duplicate + mark_processed (SET NX, 24h TTL)
- router/context.py: load_agent_for_tenant with RLS ContextVar setup
- orchestrator/tasks.py: handle_message now extracts placeholder_ts/channel_id,
calls _update_slack_placeholder via chat.update after LLM response
- docker-compose.yml: gateway service on port 8001
- pyproject.toml: added redis, konstruct-router, konstruct-orchestrator deps
2026-03-23 10:27:59 -06:00
5714acf741
feat(01-foundation-01): monorepo scaffolding, Docker Compose, and shared data models
...
- pyproject.toml: uv workspace with 5 member packages (shared, gateway, router, orchestrator, llm-pool)
- docker-compose.yml: PostgreSQL 16 + Redis 7 + Ollama services on konstruct-net
- .env.example: all required env vars documented, konstruct_app role (not superuser)
- scripts/init-db.sh: creates konstruct_app role at DB init time
- packages/shared/shared/config.py: Pydantic Settings loading all env vars
- packages/shared/shared/models/message.py: KonstructMessage, ChannelType, SenderInfo, MessageContent
- packages/shared/shared/models/tenant.py: Tenant, Agent, ChannelConnection SQLAlchemy 2.0 models
- packages/shared/shared/models/auth.py: PortalUser model for admin portal auth
- packages/shared/shared/db.py: async SQLAlchemy engine, session factory, get_session dependency
- packages/shared/shared/rls.py: current_tenant_id ContextVar and configure_rls_hook with parameterized SET LOCAL
- packages/shared/shared/redis_keys.py: tenant-namespaced key constructors (rate_limit, idempotency, session, engaged_thread)
2026-03-23 09:49:28 -06:00