Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
ee4dbd5
docs: plan Klicker MCP server and chat integration (#5080)
rschlaefli May 4, 2026
b38895a
feat(mcp-student): add student practice MCP (#5090)
rschlaefli May 9, 2026
2e3caa2
feat(apps/chat): LTI semi-anonymous guest access (#5083)
rschlaefli May 10, 2026
079a691
Merge branch 'v3' of https://github.com/uzh-bf/klicker-uzh into v3-ai
TheHummel May 11, 2026
c0fc217
docs: add Mastra framework evaluation for the chat platform
rschlaefli Jun 13, 2026
fcb5be5
docs: add Mastra chat prototype plan (Scope A+ evaluation harness)
rschlaefli Jun 13, 2026
23823bf
chore(prototype): add S0.5 measurement queries + execution runbook
rschlaefli Jun 13, 2026
0718c45
docs(prototype): add build-session kickoff runbook
rschlaefli Jun 13, 2026
6f96c25
feat(prototype): scaffold S0 engine project (env, db, harness)
rschlaefli Jun 13, 2026
003f971
feat(prototype): S0 engine spine + S0.5 measurement + synthetic fixture
rschlaefli Jun 13, 2026
36272bc
feat(prototype): S1 retrieval + guardrails (code; live validation pen…
rschlaefli Jun 13, 2026
27ee1b2
feat(prototype): S3/S4/S5 DIY branch-correct memory (offline-proven)
rschlaefli Jun 13, 2026
91ea6af
feat(prototype): S2 DB-backed skill source (offline-proven) + model-t…
rschlaefli Jun 13, 2026
d96e2a1
feat(prototype): S7 eval dataset (model-independent fixture)
rschlaefli Jun 13, 2026
b6d0aa3
fix(prototype): use Chat Completions API (S1 live-validated)
rschlaefli Jun 14, 2026
65ce15b
feat(prototype): S3 update_profile tool + context injection (live-val…
rschlaefli Jun 14, 2026
098a1e2
feat(prototype): S4 embedding ranking on branch-restricted candidates…
rschlaefli Jun 14, 2026
82548c7
feat(prototype): S6 two-level supervisor + DB roster (live-validated)
rschlaefli Jun 14, 2026
ef7d71f
feat(prototype): S7 eval runner over course-question dataset (live)
rschlaefli Jun 14, 2026
798f276
feat(prototype): S2 skills applied live (progressive disclosure)
rschlaefli Jun 14, 2026
64b8022
feat(prototype): S5 live summarize + measured token savings
rschlaefli Jun 14, 2026
88e24ad
docs(prototype): backfill S0.5-S7 verdicts + final matrix and go/no-go
rschlaefli Jun 14, 2026
26a4ef0
docs(prototype): record S0.5 measurement numbers (fixture-only)
rschlaefli Jun 14, 2026
f3e7f3b
merge: #5118 Mastra evaluation report into prototype branch
rschlaefli Jun 14, 2026
4f1c0d0
docs: append prototype outcome (§12) to Mastra evaluation report
rschlaefli Jun 14, 2026
76ac1af
docs(prototype): add post-prototype next-steps engineering plan
rschlaefli Jun 14, 2026
579269d
feat(prototype): A1 real credit accounting (cost.ts + part.totalUsage)
rschlaefli Jun 14, 2026
a632df2
feat(prototype): A1 observability — native Mastra tracing via container
rschlaefli Jun 14, 2026
a49308f
feat(prototype): A3 background cost attribution (embeddings + summari…
rschlaefli Jun 14, 2026
ff04426
feat(proto/mastra-chat): A2 — route reasoning models to OpenRouter pr…
rschlaefli Jun 14, 2026
c2dbc0f
fix(proto/mastra-chat): skip observability container when tracing is off
rschlaefli Jun 14, 2026
72036c0
feat(prototype/mastra-chat): standard OpenAI/Azure Responses API for …
rschlaefli Jun 14, 2026
ff673d8
docs(chat-mastra): add A2 findings + Hono integration plan, reconcile…
rschlaefli Jun 15, 2026
85792ee
feat(chat-api): scaffold parallel Hono chat service + flag/Traefik wi…
rschlaefli Jun 15, 2026
6e2ac0f
feat(chat-engine): extract DB-free Mastra chat engine from prototype
rschlaefli Jun 15, 2026
14e5f21
feat(chat-api): Hono service hosting the Mastra chat engine (core cha…
rschlaefli Jun 15, 2026
4cb046a
feat(chat): flag-gated streaming proxy to the chat-api Mastra service
rschlaefli Jun 15, 2026
20d3033
fix(chat-api): make Prisma import type-only to clean the rollup bundle
rschlaefli Jun 15, 2026
dd4eb40
fix(chat-api): use --env-file-if-exists so the service boots under in…
rschlaefli Jun 15, 2026
b37e261
docs(plan): record Phase 2-4 progress + Phase 4 manual-setup next steps
rschlaefli Jun 15, 2026
b45cd6e
fix(chat-api): add a request body-size limit to the chat route (DoS g…
rschlaefli Jun 15, 2026
51c539b
docs(plan): record final security review (1 fixed, 2 pre-existing fla…
rschlaefli Jun 15, 2026
6146ebf
docs(chat): record verified Phase 4 local test-drive results
rschlaefli Jun 16, 2026
d754a90
test(chat-api): add openrouter smoke workflow
rschlaefli Jun 17, 2026
1392f31
Merge remote-tracking branch 'origin/v3' into v3-ai
rschlaefli Jun 18, 2026
1c97210
merge(v3): update v3-ai
rschlaefli Jun 29, 2026
09dab97
merge(v3-ai): update mastra chat PR
rschlaefli Jun 29, 2026
fabe64f
fix(security): avoid token-looking test placeholder
rschlaefli Jun 29, 2026
7717572
docs(project): add production-readiness review for the Mastra chat-ap…
rschlaefli Jul 6, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ test('completes registration with email verification', async ({ page }) => {

```typescript
test('verifies email with mocked endpoints', async ({ page }) => {
const mockToken = 'test-verification-abc123'
const mockToken = 'mock_verification_token'

await page.route('**/api/auth/register', async (route) => {
await route.fulfill({
Expand Down
127 changes: 127 additions & 0 deletions .github/workflows/chat-api-openrouter-smoke.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
name: Chat API OpenRouter smoke

on:
workflow_dispatch:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths:
- 'apps/chat-api/**'
- 'packages/chat-engine/**'
- 'packages/prisma/**'
- 'packages/prisma-data/**'
- 'pnpm-lock.yaml'
- '.github/workflows/chat-api-openrouter-smoke.yml'

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}
cancel-in-progress: true

jobs:
smoke:
runs-on: ubuntu-latest
permissions:
contents: read

services:
postgres:
image: postgres:15
env:
POSTGRES_USER: klicker
POSTGRES_DB: klicker-prod
POSTGRES_HOST_AUTH_METHOD: trust
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432

env:
APP_SECRET: local-dev-only
CHAT_MODEL_REGISTRY_JSON: >-
[{"id":"deepseek-v4-flash","deploymentId":"deepseek/deepseek-v4-flash","name":"DeepSeek V4 Flash","description":"OpenRouter smoke test model","fallback":true,"supportsReasoning":false,"supportsImageAttachments":false,"maxOutputTokens":256,"cost":{"input":0.27,"output":0.41}}]
CHAT_OPENAI_STORE_RESPONSES: 'false'
CHAT_SMOKE_API_BASE_URL: http://127.0.0.1:3305
CHAT_SMOKE_TIMEOUT_MS: '120000'
DATABASE_URL: postgresql://klicker@localhost:5432/klicker-prod?sslmode=disable
FALLBACK_MODEL_ID: deepseek/deepseek-v4-flash
OPENAI_API_KEY: ${{ secrets.OPENROUTER_API_KEY || 'ci-placeholder' }}
OPENAI_BASE_URL: https://openrouter.ai/api/v1
PORT: '3305'

steps:
- name: Checkout repository
uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 10.15.0

- uses: actions/setup-node@v4
with:
node-version: 20.19.4
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build smoke dependencies
run: |
pnpm --filter @klicker-uzh/prisma build
pnpm --filter @klicker-uzh/types build
pnpm --filter @klicker-uzh/grading build
pnpm --filter @klicker-uzh/util build
pnpm --filter @klicker-uzh/chat-engine build

- name: Prepare seeded database
run: |
pnpm --filter @klicker-uzh/prisma prisma:push:raw
pnpm --filter @klicker-uzh/prisma-data seed:raw

- name: Typecheck chat-api
run: pnpm --filter @klicker-uzh/chat-api check

- name: Start chat-api
run: |
pnpm --filter @klicker-uzh/chat-api exec tsx src/index.ts > /tmp/chat-api-openrouter-smoke.log 2>&1 &
echo "$!" > /tmp/chat-api-openrouter-smoke.pid

for attempt in {1..30}; do
if curl -fsS "http://127.0.0.1:${PORT}/health" >/dev/null; then
exit 0
fi
sleep 2
done

cat /tmp/chat-api-openrouter-smoke.log
exit 1

- name: Run OpenRouter DeepSeek V4 Flash smoke
continue-on-error: true
run: pnpm --filter @klicker-uzh/chat-api smoke:openrouter
Comment on lines +100 to +102

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 continue-on-error: true silently masks smoke regressions

The smoke step is non-blocking regardless of exit code, so any regression in streaming, persistence, or credit decrement will cause the workflow to succeed and produce a green check on the PR. PRs that break the live SSE path will merge undetected until the OPENROUTER_API_KEY secret is wired and continue-on-error is removed. Consider using if: ${{ secrets.OPENROUTER_API_KEY != '' }} to skip the step entirely when the secret is absent, and removing continue-on-error so a present secret always produces a hard gate.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Codex Fix in Claude Code


- name: Publish smoke status note
if: always()
run: |
{
echo '### Chat API OpenRouter smoke'
echo
echo 'This workflow executes the real chat-api OpenRouter smoke path against DeepSeek V4 Flash.'
echo
echo 'The smoke step is currently non-blocking because the workflow falls back to an invalid placeholder when `OPENROUTER_API_KEY` is not configured as a repository secret.'
} >> "$GITHUB_STEP_SUMMARY"

- name: Print chat-api logs
if: always()
run: |
if [[ -f /tmp/chat-api-openrouter-smoke.log ]]; then
cat /tmp/chat-api-openrouter-smoke.log
fi

- name: Stop chat-api
if: always()
run: |
if [[ -f /tmp/chat-api-openrouter-smoke.pid ]]; then
kill "$(cat /tmp/chat-api-openrouter-smoke.pid)" >/dev/null 2>&1 || true
fi
91 changes: 91 additions & 0 deletions .github/workflows/v3_mcp-student-prd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: Build Docker image for mcp-student (prd)

on:
push:
tags: ['v*.*.*']

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}
cancel-in-progress: true

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}/mcp-student

jobs:
build-arm:
runs-on: ubuntu-24.04-arm
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-arm

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: apps/mcp-student/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
platforms: linux/arm64
no-cache: true
labels: ${{ steps.meta.outputs.labels }}

build-amd:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-amd

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: apps/mcp-student/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
platforms: linux/amd64
labels: ${{ steps.meta.outputs.labels }}
106 changes: 106 additions & 0 deletions .github/workflows/v3_mcp-student-stg.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
name: Build Docker image for mcp-student (stg)

on:
push:
branches:
- 'v3'
- 'v3*'
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths:
- 'apps/mcp-student/**'
- 'packages/graphql/**'
- 'packages/types/**'
- 'packages/util/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'turbo.json'
- '.github/workflows/v3_mcp-student**'

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}
cancel-in-progress: true

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}/mcp-student

jobs:
build-arm:
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
runs-on: ubuntu-24.04-arm
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-arm

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: apps/mcp-student/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
platforms: linux/arm64
no-cache: true
labels: ${{ steps.meta.outputs.labels }}

build-amd:
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-amd

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: apps/mcp-student/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
platforms: linux/amd64
labels: ${{ steps.meta.outputs.labels }}
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.pnpm-store/
**/__pycache__/
**/.venv/
**/node_modules/
Expand Down Expand Up @@ -59,6 +60,7 @@ output/
/apps/docs/docs/.obsidian/

.claude/worktrees
.claude/scheduled_tasks.lock

playwright/playwright-report/
playwright/test-results/
playwright/test-results/
Loading