Add Health and Spend for Admins
This commit is contained in:
@@ -1,58 +1,31 @@
|
||||
# Session 6.6 — Items 3 & 4: AGENTS.md / TOOLS.md in Wizard + Default Templates
|
||||
# Session 6.6 — Items 5 & 6: System Health + Spend Column
|
||||
|
||||
## Manual Steps (in order)
|
||||
## Files
|
||||
|
||||
### 1. Deploy the updated portal code
|
||||
Copy the files from this ZIP into your `pieced-portal` repo, overwriting existing files.
|
||||
All paths match the project structure — drop-in replacements.
|
||||
| File | Action | What |
|
||||
|------|--------|------|
|
||||
| `src/lib/litellm.ts` | REPLACE | Added `listTeams()`, `getLitellmHealth()`, `getGlobalSpend()`, `getPerTeamSpend()` |
|
||||
| `src/app/api/admin/health/route.ts` | **NEW** | Returns tenant phase counts, aggregate + per-tenant spend, vLLM & LiteLLM health |
|
||||
| `src/components/admin/admin-panel.tsx` | REPLACE | Added "Health" tab (service indicators, tenant overview, spend cards) + "Spend (CHF)" column in tenants table |
|
||||
| `patch-i18n-admin-health.mjs` | **RUN ONCE** | Patches all 4 i18n files with new admin keys |
|
||||
|
||||
### 2. The DB migration is automatic
|
||||
The updated `db.ts` adds these idempotently on first query:
|
||||
- Column `agents_md TEXT` on `tenant_requests`
|
||||
- Table `workspace_templates` (file_key TEXT PK, content TEXT, updated_at TIMESTAMPTZ)
|
||||
## Steps
|
||||
|
||||
No manual `ALTER TABLE` needed.
|
||||
1. Drop in the 3 source files (overwrite existing)
|
||||
2. Run the i18n patcher from the portal root:
|
||||
```bash
|
||||
node patch-i18n-admin-health.mjs
|
||||
```
|
||||
3. Build and deploy
|
||||
|
||||
### 3. Seed the workspace templates
|
||||
After the portal has started (so the table exists):
|
||||
## Environment Variables (optional)
|
||||
|
||||
```bash
|
||||
kubectl exec -i portal-db-1 -n portal -- psql -U portal -d portal < seed-workspace-templates.sql
|
||||
```
|
||||
- `VLLM_HEALTH_URL` — defaults to `http://vllm.inference.svc:8000`. Set if your vLLM is elsewhere.
|
||||
- `LITELLM_INTERNAL_URL` / `LITELLM_MASTER_KEY` — already configured.
|
||||
|
||||
Or connect interactively and paste the SQL.
|
||||
## Notes
|
||||
|
||||
### 4. Edit templates as needed
|
||||
To update a template later, just UPDATE the row:
|
||||
|
||||
```sql
|
||||
UPDATE workspace_templates
|
||||
SET content = '# Your new SOUL.md content here...', updated_at = now()
|
||||
WHERE file_key = 'SOUL.md';
|
||||
```
|
||||
|
||||
The portal reads templates on every wizard load / approval — no restart needed.
|
||||
|
||||
---
|
||||
|
||||
## File Manifest
|
||||
|
||||
| File | Action | What changed |
|
||||
|------|--------|-------------|
|
||||
| `src/lib/workspace-defaults.ts` | **NEW** | Default content fetching from DB + TOOLS.md generation |
|
||||
| `src/lib/db.ts` | REPLACE | Added `agents_md` column, `workspace_templates` table + CRUD |
|
||||
| `src/types/index.ts` | REPLACE | Added `agentsMd` to `TenantRequest` and `OnboardingInput` |
|
||||
| `src/app/api/onboarding/route.ts` | REPLACE | Accepts `agentsMd` field |
|
||||
| `src/app/api/admin/requests/[id]/approve/route.ts` | REPLACE | Builds all 3 workspace files (SOUL/AGENTS/TOOLS) |
|
||||
| `src/app/api/workspace-defaults/route.ts` | **NEW** | API to fetch defaults for wizard pre-fill |
|
||||
| `src/components/onboarding/wizard.tsx` | REPLACE | "Advanced Configuration" accordion with AGENTS.md textarea + readonly TOOLS.md preview |
|
||||
| `src/messages/{de,en,fr,it}.json` | REPLACE | Added `agentsMd`, `agentsMdHint`, `toolsMd`, `toolsMdHint`, `advancedConfig`, `readonlyNote` |
|
||||
| `seed-workspace-templates.sql` | **NEW** | SQL to seed default templates |
|
||||
|
||||
## Design Decisions
|
||||
|
||||
- **TOOLS.md is readonly** in both the wizard and the tenant detail page. It's auto-generated from the base template + per-package sections. Users see it but can't edit it.
|
||||
- **AGENTS.md is editable** in the wizard (under "Advanced Configuration" accordion) and on the tenant detail workspace editor.
|
||||
- **Templates live in the DB** (`workspace_templates` table) so you can edit them without redeploying. Hardcoded fallbacks exist in `workspace-defaults.ts` in case the DB rows are missing.
|
||||
- **TOOLS.md is regenerated on approval** based on the packages selected, so it's always consistent with what's actually enabled.
|
||||
- The workspace editor on the tenant detail page already supports arbitrary `workspaceFiles` keys from the CR spec, so AGENTS.md and TOOLS.md will appear there automatically. TOOLS.md should be made readonly there too — that's a separate small change to the workspace editor component (mark `TOOLS.md` as readonly based on the filename).
|
||||
- The health API uses `Promise.allSettled` so a single service being down won't break the whole page.
|
||||
- Per-tenant spend is fetched from LiteLLM's `/team/list` which returns the cumulative `spend` per team. This is mapped to tenant names via `status.litellmTeamId` on the PiecedTenant CR.
|
||||
- The spend column in the tenants table piggybacks on the same health data — fetched once when switching to the tenants tab.
|
||||
- If LiteLLM's `/team/list` or `/global/spend` response format differs from what I assumed, you may need to adjust the parsing in `litellm.ts`. The functions have fallbacks for common response shapes.
|
||||
|
||||
58
deploy/README_sql.md
Normal file
58
deploy/README_sql.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# Session 6.6 — Items 3 & 4: AGENTS.md / TOOLS.md in Wizard + Default Templates
|
||||
|
||||
## Manual Steps (in order)
|
||||
|
||||
### 1. Deploy the updated portal code
|
||||
Copy the files from this ZIP into your `pieced-portal` repo, overwriting existing files.
|
||||
All paths match the project structure — drop-in replacements.
|
||||
|
||||
### 2. The DB migration is automatic
|
||||
The updated `db.ts` adds these idempotently on first query:
|
||||
- Column `agents_md TEXT` on `tenant_requests`
|
||||
- Table `workspace_templates` (file_key TEXT PK, content TEXT, updated_at TIMESTAMPTZ)
|
||||
|
||||
No manual `ALTER TABLE` needed.
|
||||
|
||||
### 3. Seed the workspace templates
|
||||
After the portal has started (so the table exists):
|
||||
|
||||
```bash
|
||||
kubectl exec -i portal-db-1 -n portal -- psql -U portal -d portal < seed-workspace-templates.sql
|
||||
```
|
||||
|
||||
Or connect interactively and paste the SQL.
|
||||
|
||||
### 4. Edit templates as needed
|
||||
To update a template later, just UPDATE the row:
|
||||
|
||||
```sql
|
||||
UPDATE workspace_templates
|
||||
SET content = '# Your new SOUL.md content here...', updated_at = now()
|
||||
WHERE file_key = 'SOUL.md';
|
||||
```
|
||||
|
||||
The portal reads templates on every wizard load / approval — no restart needed.
|
||||
|
||||
---
|
||||
|
||||
## File Manifest
|
||||
|
||||
| File | Action | What changed |
|
||||
|------|--------|-------------|
|
||||
| `src/lib/workspace-defaults.ts` | **NEW** | Default content fetching from DB + TOOLS.md generation |
|
||||
| `src/lib/db.ts` | REPLACE | Added `agents_md` column, `workspace_templates` table + CRUD |
|
||||
| `src/types/index.ts` | REPLACE | Added `agentsMd` to `TenantRequest` and `OnboardingInput` |
|
||||
| `src/app/api/onboarding/route.ts` | REPLACE | Accepts `agentsMd` field |
|
||||
| `src/app/api/admin/requests/[id]/approve/route.ts` | REPLACE | Builds all 3 workspace files (SOUL/AGENTS/TOOLS) |
|
||||
| `src/app/api/workspace-defaults/route.ts` | **NEW** | API to fetch defaults for wizard pre-fill |
|
||||
| `src/components/onboarding/wizard.tsx` | REPLACE | "Advanced Configuration" accordion with AGENTS.md textarea + readonly TOOLS.md preview |
|
||||
| `src/messages/{de,en,fr,it}.json` | REPLACE | Added `agentsMd`, `agentsMdHint`, `toolsMd`, `toolsMdHint`, `advancedConfig`, `readonlyNote` |
|
||||
| `seed-workspace-templates.sql` | **NEW** | SQL to seed default templates |
|
||||
|
||||
## Design Decisions
|
||||
|
||||
- **TOOLS.md is readonly** in both the wizard and the tenant detail page. It's auto-generated from the base template + per-package sections. Users see it but can't edit it.
|
||||
- **AGENTS.md is editable** in the wizard (under "Advanced Configuration" accordion) and on the tenant detail workspace editor.
|
||||
- **Templates live in the DB** (`workspace_templates` table) so you can edit them without redeploying. Hardcoded fallbacks exist in `workspace-defaults.ts` in case the DB rows are missing.
|
||||
- **TOOLS.md is regenerated on approval** based on the packages selected, so it's always consistent with what's actually enabled.
|
||||
- The workspace editor on the tenant detail page already supports arbitrary `workspaceFiles` keys from the CR spec, so AGENTS.md and TOOLS.md will appear there automatically. TOOLS.md should be made readonly there too — that's a separate small change to the workspace editor component (mark `TOOLS.md` as readonly based on the filename).
|
||||
86
deploy/patch-i18n-admin-health.mjs
Normal file
86
deploy/patch-i18n-admin-health.mjs
Normal file
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Run: node patch-i18n-admin-health.mjs
|
||||
* Adds health/spend keys to all 4 message files.
|
||||
* Run from the pieced-portal root.
|
||||
*/
|
||||
import { readFileSync, writeFileSync } from "fs";
|
||||
|
||||
const newKeys = {
|
||||
en: {
|
||||
health: "Health",
|
||||
serviceHealth: "Service Health",
|
||||
vllmDescription: "GPU inference engine",
|
||||
litellmDescription: "LLM proxy & spend tracking",
|
||||
tenantOverview: "Tenant Overview",
|
||||
spendOverview: "Spend Overview",
|
||||
globalSpend: "Global Spend (CHF)",
|
||||
activeTenants: "Active Tenants",
|
||||
tenantsWithSpend: "tenants with recorded spend",
|
||||
refresh: "Refresh",
|
||||
healthUnavailable: "Health data unavailable.",
|
||||
loadingHealth: "Loading health data…",
|
||||
statusHealthy: "Healthy",
|
||||
statusDown: "Down",
|
||||
spendChf: "Spend (CHF)",
|
||||
},
|
||||
de: {
|
||||
health: "Status",
|
||||
serviceHealth: "Dienststatus",
|
||||
vllmDescription: "GPU-Inferenz-Engine",
|
||||
litellmDescription: "LLM-Proxy & Kostenerfassung",
|
||||
tenantOverview: "Mandanten-Übersicht",
|
||||
spendOverview: "Kostenübersicht",
|
||||
globalSpend: "Gesamtkosten (CHF)",
|
||||
activeTenants: "Aktive Mandanten",
|
||||
tenantsWithSpend: "Mandanten mit erfassten Kosten",
|
||||
refresh: "Aktualisieren",
|
||||
healthUnavailable: "Statusdaten nicht verfügbar.",
|
||||
loadingHealth: "Statusdaten werden geladen…",
|
||||
statusHealthy: "OK",
|
||||
statusDown: "Ausgefallen",
|
||||
spendChf: "Kosten (CHF)",
|
||||
},
|
||||
fr: {
|
||||
health: "Santé",
|
||||
serviceHealth: "Santé des services",
|
||||
vllmDescription: "Moteur d'inférence GPU",
|
||||
litellmDescription: "Proxy LLM & suivi des coûts",
|
||||
tenantOverview: "Aperçu des locataires",
|
||||
spendOverview: "Aperçu des coûts",
|
||||
globalSpend: "Coûts globaux (CHF)",
|
||||
activeTenants: "Locataires actifs",
|
||||
tenantsWithSpend: "locataires avec dépenses enregistrées",
|
||||
refresh: "Actualiser",
|
||||
healthUnavailable: "Données de santé indisponibles.",
|
||||
loadingHealth: "Chargement des données de santé…",
|
||||
statusHealthy: "OK",
|
||||
statusDown: "Hors service",
|
||||
spendChf: "Coûts (CHF)",
|
||||
},
|
||||
it: {
|
||||
health: "Stato",
|
||||
serviceHealth: "Stato dei servizi",
|
||||
vllmDescription: "Motore di inferenza GPU",
|
||||
litellmDescription: "Proxy LLM & monitoraggio costi",
|
||||
tenantOverview: "Panoramica tenant",
|
||||
spendOverview: "Panoramica costi",
|
||||
globalSpend: "Costi globali (CHF)",
|
||||
activeTenants: "Tenant attivi",
|
||||
tenantsWithSpend: "tenant con spese registrate",
|
||||
refresh: "Aggiorna",
|
||||
healthUnavailable: "Dati di stato non disponibili.",
|
||||
loadingHealth: "Caricamento dati di stato…",
|
||||
statusHealthy: "OK",
|
||||
statusDown: "Non disponibile",
|
||||
spendChf: "Costi (CHF)",
|
||||
},
|
||||
};
|
||||
|
||||
for (const [lang, keys] of Object.entries(newKeys)) {
|
||||
const path = `src/messages/${lang}.json`;
|
||||
const json = JSON.parse(readFileSync(path, "utf8"));
|
||||
Object.assign(json.admin, keys);
|
||||
writeFileSync(path, JSON.stringify(json, null, 2) + "\n");
|
||||
console.log(`Patched ${path} — added ${Object.keys(keys).length} keys`);
|
||||
}
|
||||
Reference in New Issue
Block a user