Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions managed-chatkit/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
node_modules
.git
.gitignore
*.md
.env*
!.env.example
__pycache__
*.pyc
*.pyo
*.pyd
.Python
*.so
*.egg
*.egg-info
dist
build
.venv
.venv/*
venv
venv/*
.ruff_cache
.pytest_cache
.vscode
.idea
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
Dockerfile
.dockerignore
docker-compose*
39 changes: 39 additions & 0 deletions managed-chatkit/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# syntax=docker/dockerfile:1

FROM node:20-slim AS builder

WORKDIR /app

COPY frontend/package*.json ./

RUN npm ci

COPY frontend/ ./

ARG VITE_CHATKIT_WORKFLOW_ID
ENV VITE_CHATKIT_WORKFLOW_ID=${VITE_CHATKIT_WORKFLOW_ID}

RUN npm run build

FROM python:3.12-slim AS runtime

WORKDIR /app

RUN pip install --no-cache-dir uv

COPY --from=builder /app/dist ./dist

COPY backend/pyproject.toml ./

RUN uv pip install --system -e .

COPY backend/app ./app

ENV PYTHONUNBUFFERED=1

EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD python -c "import httpx; httpx.get('http://localhost:8000/health').raise_for_status()" || exit 1

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4", "--log-level", "info"]
39 changes: 39 additions & 0 deletions managed-chatkit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,42 @@ same project and organization.

- UI: `frontend/src/components/ChatKitPanel.tsx`
- Session logic: `backend/app/main.py`

## Deploy with Docker

### Build

```bash
docker build \
--build-arg VITE_CHATKIT_WORKFLOW_ID=wf_xxxxx \
-t managed-chatkit .
```

### Run

```bash
docker run -d \
-e OPENAI_API_KEY=sk-proj-xxxxx \
-p 8000:8000 \
--name managed-chatkit \
managed-chatkit
```

The app will be available at `http://localhost:8000`.

### Environment variables

| Variable | Build-time | Runtime | Description |
|----------|------------|---------|-------------|
| `VITE_CHATKIT_WORKFLOW_ID` | Required | Required | Your ChatKit workflow ID |
| `OPENAI_API_KEY` | - | Required | OpenAI API key |
| `CHATKIT_API_BASE` | - | Optional | ChatKit API base URL |

### Production deployment

For production, pass `OPENAI_API_KEY` via your orchestrator's secret management:

- **Kubernetes**: Use Secrets
- **Cloud Run**: Use `--set-secrets`
- **ECS**: Use Task Definition secrets
- **Azure Container Apps**: Use secret references
14 changes: 13 additions & 1 deletion managed-chatkit/backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@
import httpx
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.staticfiles import StaticFiles

DEFAULT_CHATKIT_BASE = "https://api.openai.com"
SESSION_COOKIE_NAME = "chatkit_session_id"
SESSION_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 30 # 30 days

app = FastAPI(title="Managed ChatKit Session API")

app.mount("/static", StaticFiles(directory="dist/assets"), name="static")

app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
Expand Down Expand Up @@ -164,3 +167,12 @@ def parse_json(response: httpx.Response) -> Mapping[str, Any]:
return parsed if isinstance(parsed, Mapping) else {}
except (json.JSONDecodeError, httpx.DecodingError):
return {}


@app.get("/{full_path:path}")
async def serve_spa(full_path: str) -> HTMLResponse:
index_path = os.path.join("dist", "index.html")
if os.path.exists(index_path):
with open(index_path, "r") as f:
return HTMLResponse(content=f.read())
return HTMLResponse(content="<h1>Not Found</h1>", status_code=404)