{"id":48278333,"url":"https://github.com/ericgitangu/pawacloud-assistant","last_synced_at":"2026-04-04T22:41:14.956Z","repository":{"id":348124817,"uuid":"1182819317","full_name":"ericgitangu/pawacloud-assistant","owner":"ericgitangu","description":"AI-powered GCP advisory chatbot — FastAPI + Gemini 2.5 Flash + Next.js 16 + Rust/PyO3 + TailwindCSS v4. Pawa IT Solutions pre-screening assessment.","archived":false,"fork":false,"pushed_at":"2026-03-30T21:30:55.000Z","size":18423,"stargazers_count":0,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-30T23:40:30.735Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://pawacloud-web.fly.dev","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ericgitangu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-16T01:41:32.000Z","updated_at":"2026-03-30T21:30:59.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ericgitangu/pawacloud-assistant","commit_stats":null,"previous_names":["ericgitangu/pawacloud-assistant"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/ericgitangu/pawacloud-assistant","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericgitangu%2Fpawacloud-assistant","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericgitangu%2Fpawacloud-assistant/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericgitangu%2Fpawacloud-assistant/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericgitangu%2Fpawacloud-assistant/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ericgitangu","download_url":"https://codeload.github.com/ericgitangu/pawacloud-assistant/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericgitangu%2Fpawacloud-assistant/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31417656,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T20:09:54.854Z","status":"ssl_error","status_checked_at":"2026-04-04T20:09:44.350Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-04T22:41:12.512Z","updated_at":"2026-04-04T22:41:14.946Z","avatar_url":"https://github.com/ericgitangu.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PawaCloud Assistant - Pre-screening assessment\n\nAI-powered chatbot with Gemini, Rust+PyO3 text processing, and GCP deployment.\n\n[![CI](https://github.com/ericgitangu/pawacloud-assistant/actions/workflows/ci.yml/badge.svg)](https://github.com/ericgitangu/pawacloud-assistant/actions/workflows/ci.yml)\n![Python](https://img.shields.io/badge/Python-3.12-3776AB?logo=python\u0026logoColor=white)\n![FastAPI](https://img.shields.io/badge/FastAPI-0.115-009688?logo=fastapi\u0026logoColor=white)\n![Rust](https://img.shields.io/badge/Rust-PyO3-DEA584?logo=rust\u0026logoColor=white)\n![Next.js](https://img.shields.io/badge/Next.js-16-000?logo=next.js\u0026logoColor=white)\n![TailwindCSS](https://img.shields.io/badge/TailwindCSS-4.0-06B6D4?logo=tailwindcss\u0026logoColor=white)\n![Gemini](https://img.shields.io/badge/Gemini-2.5_Flash-4285F4?logo=google\u0026logoColor=white)\n![Redis](https://img.shields.io/badge/Redis-Sessions-DC382D?logo=redis\u0026logoColor=white)\n![PostgreSQL](https://img.shields.io/badge/PostgreSQL-16-4169E1?logo=postgresql\u0026logoColor=white)\n![Docker](https://img.shields.io/badge/Docker-Compose-2496ED?logo=docker\u0026logoColor=white)\n![Terraform](https://img.shields.io/badge/Terraform-IaC-7B42BC?logo=terraform\u0026logoColor=white)\n\n**Live demo**: [pawacloud-web.fly.dev](https://pawacloud-web.fly.dev) · **API docs**: [/docs](https://pawacloud-api-904401126919.africa-south1.run.app/docs)\n\n## Screenshots\n\nScreenshots are available in the `public/screenshots` directory. Here are some of them showing several functional units:\n\n### Login — auth options + assessment checklist\n\n![Login](public/screenshots/login.png)\n\n### Chat — the assessment example query with formatted response\n\n![Chat](public/screenshots/chat.png)\n\n### Chat History — persistent conversations across sessions\n\n![Chat History](public/screenshots/chat_history.png)\n\n### Status — live metrics, latency chart, Cloud Run infrastructure\n\n![Status](public/screenshots/status.png)\n\n---\n\n## Live\n\n| Service | URL |\n|---------|-----|\n| Frontend | [pawacloud-web.fly.dev](https://pawacloud-web.fly.dev) |\n| Backend API | [pawacloud-api](https://pawacloud-api-904401126919.africa-south1.run.app) |\n| Swagger | [/docs](https://pawacloud-api-904401126919.africa-south1.run.app/docs) |\n| ReDoc | [/redoc](https://pawacloud-api-904401126919.africa-south1.run.app/redoc) |\n| Status | [/status](https://pawacloud-web.fly.dev/status) |\n\n---\n\n## Quick Start\n\n**Prerequisites**: Python 3.12+, [pnpm](https://pnpm.io/installation), Docker (for compose method)\n\n**Optional**: [Rust toolchain](https://rustup.rs) (PyO3 — falls back to Python), [gcloud CLI](https://cloud.google.com/sdk/docs/install) (Cloud Run deploy), [flyctl](https://fly.io/docs/flyctl/install/) (frontend deploy), [Terraform](https://developer.hashicorp.com/terraform/install) (IaC), [Gemini API key](https://aistudio.google.com/apikey) (LLM — app runs without it, returns error on chat)\n\n### Docker Compose (recommended)\n\n```bash\ngit clone https://github.com/ericgitangu/pawacloud-assistant.git\ncd pawacloud-assistant\ncp backend/.env.example backend/.env   # add GEMINI_API_KEY\nmake dev\n```\n\nStarts PostgreSQL, Redis, backend, and frontend. Hit \u003chttp://localhost:3000\u003e.\n\n### Manual\n\n```bash\n# backend\ncd backend\npython3 -m venv venv \u0026\u0026 source venv/bin/activate\npip install -r requirements.txt\ncp .env.example .env\nuvicorn app.main:app --reload --port 8000\n\n# frontend (new terminal)\ncd frontend\npnpm install\ncp .env.example .env.local\npnpm run dev\n```\n\n### With Rust PyO3\n\n```bash\ncd backend \u0026\u0026 python3 -m venv venv \u0026\u0026 source venv/bin/activate\ncd ../rust-core \u0026\u0026 pip install maturin \u0026\u0026 maturin develop --release\ncd ../backend \u0026\u0026 uvicorn app.main:app --reload --port 8000\n```\n\n### Tests\n\n```bash\nmake test                    # backend: 27 pytest cases + doctests\ncd frontend \u0026\u0026 pnpm build    # frontend: typecheck + build\ncd rust-core \u0026\u0026 cargo test   # rust (runs inside Docker build)\n```\n\n---\n\n## API\n\n| Method | Endpoint | Purpose |\n|--------|----------|---------|\n| `POST` | `/api/v1/chat` | Query → complete response |\n| `GET` | `/api/v1/chat/stream?q=` | Query → SSE stream |\n| `GET` | `/api/v1/chat/history` | Paginated history (per user) |\n| `DELETE` | `/api/v1/chat/history/{id}` | Delete single conversation |\n| `DELETE` | `/api/v1/chat/history` | Clear all history |\n| `GET` | `/health` | Health + model metadata |\n| `GET` | `/health/metrics` | PyO3 benchmarks + Redis stats |\n| `GET` | `/health/infra` | Cloud Run / Fly.io env info |\n| `GET` | `/health/llm` | Live Gemini connectivity test |\n| `GET` | `/auth/login` | → Google OAuth consent |\n| `GET` | `/auth/callback` | OAuth code → session |\n| `POST` | `/auth/signup` | Create email/password account |\n| `POST` | `/auth/login` | Email/password login |\n| `GET` | `/auth/me` | Current user |\n| `POST` | `/auth/logout` | Clear session |\n| `POST` | `/auth/guest-pass` | 60-min session for `@pawait.co.ke` |\n\nAll endpoints return typed Pydantic v2 models. Input sanitized via Rust PyO3 (Python fallback). See [Swagger](https://pawacloud-api-904401126919.africa-south1.run.app/docs) for request/response schemas.\n\n---\n\n## Auth\n\nThree methods, sessions via HMAC-signed tokens (Redis-independent):\n\n| Method | TTL | Notes |\n|--------|-----|-------|\n| Google OAuth | 24h | OpenID Connect |\n| Email + password | 24h | bcrypt, PostgreSQL |\n| Guest Pass | 60min | `@pawait.co.ke` — no signup needed |\n\nHistory is keyed by email, not session ID — persists across sign-out/sign-in and survives Cloud Run cold starts. Chat conversations stored in PostgreSQL (Neon.tech, `aws-eu-west-2`) with in-memory cache for fast reads.\n\n**For Pawa IT reviewers**: click \"Fast Pass\" on login, enter any `@pawait.co.ke` email.\n\n---\n\n## Architecture\n\n```mermaid\nsequenceDiagram\n    participant U as User\n    participant F as Next.js\n    participant B as FastAPI\n    participant R as Rust/PyO3\n    participant PG as Neon PG\n    participant G as Gemini\n\n    U-\u003e\u003eF: Submit question\n    F-\u003e\u003eB: GET /api/v1/chat/stream?q=...\n    B-\u003e\u003eR: sanitize_input(query)\n    B-\u003e\u003eG: generate_content(stream=True)\n    loop SSE chunks\n        G--\u003e\u003eB: chunk\n        B--\u003e\u003eF: data: chunk\n    end\n    B-\u003e\u003ePG: Persist conversation\n```\n\n```mermaid\ngraph LR\n    subgraph GCP[\"GCP — africa-south1\"]\n        CR[\"Cloud Run\u003cbr/\u003eFastAPI + Rust\"]\n        GEM[\"Gemini 2.5 Flash\"]\n    end\n    subgraph Fly[\"Fly.io — JNB\"]\n        WEB[\"Next.js 16\"]\n    end\n    UPS[\"Upstash Redis\"]\n    NEON[\"Neon PostgreSQL\u003cbr/\u003eeu-west-2\"]\n    WEB --\u003e CR\n    CR --\u003e GEM\n    CR --\u003e UPS\n    CR --\u003e NEON\n```\n\nBackend on Cloud Run (`africa-south1`), frontend on Fly.io (JNB). Terraform IaC in `infra/`. Details in [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).\n\n---\n\n## Rust PyO3\n\nText processing on every request and response. Optional — falls back to pure Python.\n\n| Function | Rust | Python | Speedup |\n|----------|------|--------|---------|\n| `sanitize_input` (1KB) | ~0.9us | ~45us | ~50x |\n| `estimate_tokens` (4KB) | ~1.2us | ~120us | ~100x |\n| `validate_markdown` (8KB) | ~2.1us | ~80us | ~38x |\n\nLive benchmarks at [`/health/metrics`](https://pawacloud-api-904401126919.africa-south1.run.app/health/metrics).\n\n---\n\n## Project Structure\n\n```yaml\npawacloud-assistant/\n├── backend/\n│   ├── app/\n│   │   ├── main.py              FastAPI + session middleware\n│   │   ├── core/                config, decorators, middleware\n│   │   ├── models/schemas.py    Pydantic v2 models\n│   │   ├── routers/             chat, auth, health\n│   │   └── services/            llm, history, text processing\n│   ├── tests/test_api.py\n│   └── Dockerfile\n├── rust-core/                   PyO3 bindings (7 functions)\n├── frontend/\n│   ├── app/                     pages (chat, status, login, signup)\n│   ├── components/              12 components\n│   ├── lib/api.ts               fetch + SSE + auth helpers\n│   └── Dockerfile\n├── infra/                       Terraform (Cloud Run + Artifact Registry)\n├── public/\n│   └── screenshots/             Login, Chat, History, Status PNGs\n├── docs/\n│   ├── ARCHITECTURE.md\n│   ├── PROMPTS.md\n│   └── EVALUATION.md            Assessment criteria mapping\n├── scripts/                     deploy-backend.sh, deploy-frontend.sh\n├── docker-compose.yml           Full stack (Postgres + Redis + backend + frontend)\n└── Makefile\n```\n\n---\n\n## Deployment\n\n```bash\n# backend → Cloud Run\nbash scripts/deploy-backend.sh\n\n# frontend → Fly.io\nbash scripts/deploy-frontend.sh\n\n# or Terraform\ncd infra \u0026\u0026 terraform init \u0026\u0026 terraform apply\n```\n\n---\n\n## Prompt Engineering\n\nSystem prompt in `services/llm_service.py`. Details in [docs/PROMPTS.md](docs/PROMPTS.md).\n\n- Persona: GCP specialist tied to Pawa IT\n- Africa-aware: regional availability, cost, connectivity\n- Multilingual: responds in user's language, keeps technical terms in English\n- Honesty: \"if you don't know, say so\"\n\n---\n\n## Troubleshooting\n\n| Issue | Fix |\n|-------|-----|\n| `GEMINI_API_KEY not set` | Copy `backend/.env.example` to `backend/.env` and add your key from [aistudio.google.com/apikey](https://aistudio.google.com/apikey) |\n| Frontend redirects to `/login` endlessly | The `pawacloud_auth` cookie isn't set — clear cookies and log in again, or check `NEXT_PUBLIC_API_URL` points to a running backend |\n| `pnpm: command not found` | `corepack enable \u0026\u0026 corepack prepare pnpm@9.15.0 --activate` (requires Node 18+) |\n| CORS errors in browser console | Ensure `ALLOWED_ORIGINS` in `backend/.env` includes your frontend URL (default: `http://localhost:3000`) |\n| `maturin develop` fails | Rust toolchain missing — `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \\| sh`. Or skip it entirely; Python fallback handles everything |\n| Docker compose: port 5432 in use | Local PostgreSQL is running — `brew services stop postgresql` or change the port in `docker-compose.yml` |\n| History empty after login | Set `DATABASE_URL` in `backend/.env` for persistent history. Without it, history is in-memory and lost on restart. Redis is optional (session cache only) |\n| OAuth callback fails locally | Set `OAUTH_REDIRECT_URI=http://localhost:8000/auth/callback` and `FRONTEND_URL=http://localhost:3000` in `backend/.env`. Google Console must list the redirect URI |\n\n---\n\n## Evaluation\n\nAssessment criteria mapping, what's beyond requirements, and what I'd do with more time: [docs/EVALUATION.md](docs/EVALUATION.md).\n\n---\n\n**Eric Gitangu** · [resume](https://resume.ericgitangu.com) · [linkedin](https://linkedin.com/in/ericgitangu) · [github](https://github.com/ericgitangu)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fericgitangu%2Fpawacloud-assistant","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fericgitangu%2Fpawacloud-assistant","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fericgitangu%2Fpawacloud-assistant/lists"}