Commit Graph
21 Commits
Author SHA1 Message Date
westfarn 6e34fd95ec Gate self-serve registration behind ENABLE_ACCOUNT_REGISTRATION (default false).
CI / test (pull_request) Successful in 10s
Unit Tests / test (pull_request) Successful in 10s
Supports chat_web_app#31: public settings + /user/create/ create company/user and return JWTs for Stripe Checkout. Document flag in env/secret templates.
2026-07-27 06:17:42 -05:00
westfarn ad44359804 Add finance app with Stripe Checkout subscriptions (#21) (#23)
Unit Tests / test (push) Successful in 10s
## Summary
- Closes #21 — new Django `finance` app with Stripe as payment provider
- Subscription price defaults to **$10 USD / month** via `SUBSCRIPTION_PRICE_AMOUNT_CENTS = 1000` in `settings.py` (env-overridable)
- Persists **Invoice** and **Payment** rows; both registered in Django admin (with payment inline on invoices)
- Checkout Session API redirects users to Stripe hosted payment; webhook verifies signatures and upserts ledger idempotently

## API
- `POST /api/finance/checkout/` — JWT auth → `{ checkout_url, session_id }`
- `GET /api/finance/invoices/` / `GET /api/finance/payments/` — own records
- `POST /api/finance/webhooks/stripe/` — Stripe signature-verified webhook

## Config
Documented in `.env.example` / `.env.prod.example`:
`STRIPE_SECRET_KEY`, `STRIPE_PUBLISHABLE_KEY`, `STRIPE_WEBHOOK_SECRET`, optional `STRIPE_PRICE_ID`, `FRONTEND_BASE_URL`

## Test plan
- [x] `uv run python manage.py test finance` (17 tests)
- [ ] Set Stripe test keys locally; create checkout session; complete payment in Stripe test mode
- [ ] Confirm Invoice/Payment appear in `/admin/`
- [ ] Point Stripe webhook to `/api/finance/webhooks/stripe/` and verify `checkout.session.completed` / `invoice.paid`Reviewed-on: #23
2026-07-26 17:35:06 -07:00
westfarn 9984d1c340 Name AI assistant Hesychia in system prompts (#22)
Unit Tests / test (push) Successful in 9s
## Summary
- Closes #20 — name the AI assistant **Hesychia** in system / generation prompts
- Add shared `assistant_identity.py` with `ASSISTANT_NAME` + concise calm/stillness tone
- Prepend identity to chat (`llm_service`), RAG, data analysis, and the views system message
- Document that identity lives in code (not env); add unit coverage

## Test plan
- [x] `uv run python manage.py test chat_backend.tests.test_assistant_identity chat_backend.tests.test_services_llm chat_backend.tests.test_services_data_analysis`
- [ ] Fresh chat: ask "who are you?" → responds as Hesychia
- [ ] Confirm classifiers/moderators/title generator unchanged (not assistant identity)

Related: companion frontend rebrand `chat_web_app#29`Reviewed-on: #22
2026-07-26 17:08:08 -07:00
westfarn 92aa277a37 Allow Capacitor WebView origins in CORS/CSRF (#22) (#19)
Unit Tests / test (push) Successful in 9s
## Summary
- Supports [chat_web_app#22](ai_ml_operations/chat_web_app#22)
- Always merge Capacitor WebView origins (`https://localhost`, `capacitor://localhost`, `http://localhost`) into `CORS_ALLOWED_ORIGINS` and `CSRF_TRUSTED_ORIGINS`
- Keep `CORS_ALLOW_CREDENTIALS = False` (JWT header-only; no cookie credentials)

## Test plan
- [x] `uv run python manage.py test chat_backend.tests.test_settings_cors`
- [ ] OPTIONS preflight from `https://localhost` / `capacitor://localhost` against prod/beta with `CORS_ORIGIN_ALLOW_ALL=false`
- [ ] Authenticated JWT GET/POST from Capacitor shell after FE #22 landsReviewed-on: #19
2026-07-26 14:04:09 -07:00
westfarn a049e4f685 Track token in/out per prompt on PromptMetric (#18)
Unit Tests / test (push) Successful in 9s
Closes #15

## Summary
- Add nullable `tokens_in` / `tokens_out` `IntegerField`s to `PromptMetric` to record real prompt/completion token counts per turn.
- New `extract_token_usage()` helper parses provider usage payloads (LangChain `usage_metadata`, OpenAI-style `prompt_tokens`/`completion_tokens`, Ollama `prompt_eval_count`/`eval_count`). When a provider reports no usage, the fields stay **null** — counts are never estimated/fabricated.
- `create_prompt_metric` / `finish_prompt_metric` in both `consumers.py` and `consumers_graph.py` accept and persist optional `tokens_in` / `tokens_out` (added to `update_fields` only when present).
- Admin panel (this ticket's deliverable):
  - `PromptMetricAdmin` lists `tokens_in` / `tokens_out` and adds `event` / `model_name` / `has_file` filters.
  - `ConversationAdmin` shows summed `tokens_in` / `tokens_out` / `tokens_total` per conversation.
- Migration `0023_promptmetric_tokens_in_promptmetric_tokens_out` (existing rows remain valid — null).

## Note on live capture
The streaming chat path uses LangChain `StrOutputParser`, which yields plain string chunks with no usage metadata, so live turns currently persist `null` tokens (honest, per acceptance criteria — no fabricated counts). The plumbing + helper are in place so wiring real provider usage is a drop-in once the services expose it.

## Follow-ups
- #16 — Show token in/out in chat web app UI (FE + API exposure)
- #17 — Token-based billing, quotas, and enforcement

## Test plan
- [x] `uv run python manage.py test` — full suite green (266 tests, 6 skipped)
- [x] Model: token fields default null + persist when set
- [x] `extract_token_usage`: LangChain / OpenAI / Ollama key variants, attribute sources, bool/float handling, missing usage → (None, None)
- [x] Metric lifecycle: tokens persist when provided, stay null when absent (both consumers)
- [x] Admin: conversation token totals sum across metrics and ignore other conversationsReviewed-on: #18
2026-07-26 08:22:34 -07:00
westfarn 85637e3db6 Unpin langgraph stack; upgrade langchain-core instead (#14)
Unit Tests / test (push) Successful in 9s
Closes #10

## Summary
- Remove force-pins on `langgraph==1.0.4` / `langgraph-checkpoint==3.0.1` / `langgraph-prebuilt==1.0.5` / `langgraph-sdk==0.2.14`
- Upgrade langchain stack so current langgraph-checkpoint (4.x) works with `Reviver(allowed_objects=...)`
- Keep direct `langgraph>=1.2.5,<1.3.0` (matches langchain 1.3.x); checkpoint/prebuilt/sdk resolve transitively
- Adapt `BaseMessage.text()` → `.text` property for langchain-core 1.5.x

## Resolved versions (uv.lock)
| Package | Before | After |
|---|---|---|
| langchain-core | 1.1.1 | 1.5.1 |
| langchain | 1.1.2 | 1.3.14 |
| langgraph | 1.0.4 | 1.2.9 |
| langgraph-checkpoint | 3.0.1 | 4.1.1 |
| langgraph-prebuilt | 1.0.5 | 1.1.0 |
| langgraph-sdk | 0.2.14 | 0.4.2 |

## Test plan
- [x] `uv sync --frozen`
- [x] `uv run python manage.py test` — 248 OK (6 skipped)
- [x] Import `consumers_graph` CompiledStateGraph OK
- [x] Confirm `Reviver.__init__` accepts `allowed_objects`
- [ ] Manual smoke: WebSocket chat + graph path (`consumers_graph`)

## References
- Issue: #10
- Prior pin: #9Reviewed-on: #14
2026-07-26 05:11:39 -07:00
westfarn 0525f9559b Add offline unit test suite for chat_backend (#12)
Unit Tests / test (push) Successful in 9s
Closes #5

## Summary

- Replaces the three scattered test modules (`chat_backend/tests.py`, `services/tests.py`, `services/prompt_classifier/tests.py`) with a `chat_backend/tests/` package: **242 deterministic tests plus 6 opt-in live-Ollama checks**, up from 10 tests (3 of which were skipped and 4 of which were never even discovered).
- The suite runs fully offline — no Ollama, Chroma, SMTP or network access. LangChain runnables are replaced by a small `FakeChain`, Chroma/embeddings are mocked, email uses Django's locmem backend, and blobs go through `DatabaseStorage`.
- New `llm_be/test_runner.py` (wired via `TEST_RUNNER`) sets `SKIP_RAG_INIT=1` and an MD5 password hasher, so the suite cannot accidentally reach a model server and finishes in ~5s on SQLite (~17s on Postgres) instead of ~30s.

## Coverage

| Area | File |
|------|------|
| `TimeInfoBase.save`, slugs, `get_duration`, `file_exists`, cascades | `test_models.py` |
| `DatabaseStorage` save/open/exists/size/listdir/delete/times | `test_storage.py` |
| JWT claim, prompt/user/feedback/document serializers | `test_serializers.py` |
| auth + token, invite, feedback, company users, set-password, TOS | `test_views_users.py` |
| conversation list/order/create/detail/soft-delete | `test_views_conversations.py` |
| all four analytics endpoints, including empty-month behaviour | `test_views_analytics.py` |
| workspace + document upload/list/detail, 404 and 400 paths | `test_views_documents.py` |
| prompt classifier rules/parsing, moderation fail-safe, title cleanup | `test_services_classifiers.py` |
| CSV/XLSX/DOCX/PDF analysis, plot generation, error payloads | `test_services_data_analysis.py` |
| loader selection, filename sanitising, ingest, temp-file cleanup, search filters | `test_services_rag.py` |
| history formatting and streaming | `test_services_llm.py` |
| document re-index on create/delete, `SKIP_RAG_INIT` guard | `test_signals.py` |
| consumer DB helpers, LangGraph nodes (moderation, classification, generation, search flags), websocket routes | `test_consumers.py` |

Live checks (non-deterministic, need a model server):

```bash
cd llm_be
RUN_LIVE_OLLAMA_TESTS=1 uv run python manage.py test chat_backend.tests.test_live_ollama
```

## Bugs the tests surfaced (fixed here)

1. **`ConversationDetailView.post` silently dropped every prompt.** `import datetime` shadowed `from datetime import datetime`, so `datetime.now()` raised `AttributeError` inside a bare `except` and the endpoint returned 200 without saving. Now uses `timezone.now()`.
2. **Prompt attachments never reached the LLM.** `get_conversation_file_async` (both consumers) did `sync_to_async(prompt.file.read)` — with `DatabaseStorage` the attribute access itself opens the blob, i.e. a DB query in async context, raising `SynchronousOnlyOperation` that was swallowed and returned `(None, None)`. The read now happens inside the thread.
3. **`DatabaseStorage._save` crashed on a str-backed `ContentFile`** (`TypeError: sequence item 0: expected a bytes-like object`); chunks are encoded when needed.
4. **`services/prompt_classifier/__init__.,py`** (note the comma) meant the directory was only an implicit namespace package, which is why its test module was never collected. Renamed, and its duplicate live-Ollama tests folded into `test_live_ollama.py`.

Known-broken paths deliberately left untested and unchanged: `reset_password` / `ResetUserPassword` reference an unimported `requests` plus undefined locals, and `DocumentDetailView.get` references an undefined `workspaces` on its success path. Worth a follow-up ticket.

## Test plan

- [x] `cd llm_be && uv run python manage.py test` → 248 tests, OK (6 skipped, all opt-in live)
- [x] Same suite against Postgres 16 (`DATABASE_URL=postgres://…`) → OK, matching the containerized run in `deploy.yml`
- [x] `uv run black` clean on all added files
- [ ] Gitea Actions **Unit Tests** + **CI** green on this PRReviewed-on: #12
2026-07-26 05:00:17 -07:00
westfarn 383c571137 update requirements (#9)
Unit Tests / test (push) Successful in 13s
Reviewed-on: #9
2026-07-25 19:06:16 -07:00
westfarn ad81257ecb fixing deploymennt (#8)
Unit Tests / test (push) Successful in 14s
Reviewed-on: #8
2026-07-25 06:17:46 -07:00
westfarn d1660792ad Dockerize chat_backend + Ollama LAN + DB file storage (#6) (#7)
Unit Tests / test (push) Successful in 13s
## Summary

Implements [chat_backend#6](#6) Part A:

- **uv** packaging (`pyproject.toml` + `uv.lock`), Docker/compose (dev + prod), entrypoint/validate-env, Gitea unit-test + auto-deploy workflows (mirror `scha`)
- Env-driven Django settings (`DJANGO_*`, `DATABASE_URL`, CSRF/CORS)
- **`OLLAMA_BASE_URL`** wired through all Ollama/LangChain clients (prod → `http://10.0.0.128:11434`)
- **DatabaseStorage** — prompt/document file blobs in Postgres (`StoredFile`), not container FS; RAG materializes temp paths for loaders
- ASGI via `gunicorn` + `UvicornWorker` (HTTP + WebSockets)

Companion server-infra PR registers `app_catalog` / `host_apps` (port **8003**).

## Test plan

- [ ] `uv sync && cd llm_be && SKIP_RAG_INIT=1 uv run python manage.py test`
- [ ] `docker compose build && docker compose up` against bundled Postgres
- [ ] Confirm Ollama calls use `OLLAMA_BASE_URL` (not hardcoded localhost)
- [ ] Upload a document / prompt file → row in `chat_backend_storedfile`, no disk under `media/`
- [ ] After server-infra merge + secret/Postgres/NPM: deploy via `deploy.sh --app chat_backend --env prod`Reviewed-on: #7
2026-07-25 05:23:33 -07:00
westfarn 77d7edd0dc Closes #4
Added site tracking
Can pick the model the use
Better handle llm model based on debug or not
2025-12-08 13:52:30 -06:00
westfarn eed1abedc8 updates 2025-12-07 06:31:06 -06:00
westfarn 91bdb2fd2d Merging from prod 2025-09-24 12:05:22 -05:00
westfarn 8a259158c8 Updated data analysis to generate images to perform data analysis 2025-09-24 11:49:08 -05:00
westfarn 14d8211715 Allow for data analysis 2025-09-08 12:29:20 -05:00
westfarn 951a58f2fa fixed chat service 2025-05-28 03:25:14 -05:00
westfarn a85f1222eb Syncing with updates from prod and formatted 2025-05-18 06:15:07 -05:00
westfarn d8a912e2c3 updated the requirements.txt 2025-05-14 03:28:16 -05:00
westfarn f5d29166a6 RAG implementation, content moderation, prompt classification, new LLM chain, document storage 2025-05-14 03:27:38 -05:00
westfarn 57695353d0 temp checkin 2025-03-07 12:23:00 -06:00
westfarn 058f961b0d Initial commit 2025-03-03 10:53:23 +00:00