AI Integration
:::info Source
Sourced from services/catalog-service/AI_INTEGRATION.md in the documentation repo.
:::
Companion: ../../docs/03-microservices/ai-gateway-service.md · APPLICATION_LOGIC
Catalog is not an AI-producing service. It consumes the ai-gateway-service only for narrowly scoped metadata-assist tasks. No direct model calls.
1. AI Surface Summary
| Capability | Status | Trigger | Model family |
|---|---|---|---|
| Metadata localisation (translate title + description into missing locales) | S5 | Admin action POST /courses/{id}/metadata/localize | translation-grade chat model |
| Taxonomy suggestion (given a course, suggest nodes) | S5 | Admin action POST /courses/{id}/taxonomy/suggest | embedding-based kNN + LLM |
| Slug suggestion (given a title) | S2 | Authoring-service call GET /internal/v1/catalog/slug/suggest | small LLM or heuristic |
| Tag suggestion (given metadata) | S5 | Admin action POST /courses/{id}/tags/suggest | embeddings + LLM |
| Listing blurb suggestion (for marketplace) | S5 | Marketplace delegates to authoring/ai-gateway | LLM |
2. Invocation Contract (ai-gateway)
All AI calls use the platform AIRequest envelope defined in ai-gateway-service:
interface AIRequest<T> {
requestId: ULID;
correlationId: ULID;
tenantId: TenantId;
actor: { type: 'user'|'system'; id: string };
capability: string; // 'catalog.metadata.localize' | 'catalog.tags.suggest' | …
input: T;
constraints: {
maxLatencyMs: number;
maxCostMicroUSD: number;
dataResidency: 'us'|'eu'|'me'|'ap';
piiPolicy: 'none'|'strict';
localOnly?: boolean;
};
hitl?: { required: boolean; approverRole?: string };
}
AI gateway returns:
interface AIResponse<R> {
requestId: ULID;
output: R;
provenance: AIProvenance; // see 12-data-models
usage: { microUSD: number; tokens: { in: number; out: number } };
status: 'ok'|'partial'|'refused'|'error';
}
Catalog stores provenance alongside any AI-assisted field in course_audit.diff.
3. Capability: catalog.metadata.localize
Purpose: Given a Course with title + description in defaultLocale, produce translations for the target locales.
Request input
{
"courseId": "crs_01H…",
"defaultLocale": "en",
"targetLocales": ["ar","fr","es"],
"title": { "en": "Intro Physics" },
"description": { "en": "A first course in mechanics, waves, and thermodynamics." },
"domainHint": "/science/physics"
}
Response output
{
"translations": {
"ar": { "title": "مقدمة الفيزياء", "description": "…" },
"fr": { "title": "Introduction à la physique", "description": "…" },
"es": { "title": "Introducción a la física", "description": "…" }
}
}
HITL
Translation output is tagged hitl.required=false by default, but the admin UI surfaces the AI-generated copy as draft until the user confirms. Confirmation writes both the text and the AIProvenance.reviewedBy + reviewedAt fields.
Safety
piiPolicy: 'strict'— gateway redacts any detected PII before model call (unlikely in titles but enforced).- Cost cap: 10,000 μUSD per request.
- Rate limit: 100 rpm per tenant.
4. Capability: catalog.tags.suggest
Purpose: Propose 5–15 tags for a course.
Input:
{ "title": { "en":"…" }, "description": { "en":"…" }, "moduleSummaries": [ "…" ] }
Output:
{ "suggestions": [ { "tag":"kinematics","confidence":0.92 }, { "tag":"newtonian-mechanics","confidence":0.88 } ] }
- Suggestions below confidence
0.6are filtered client-side. - All applied tags require user click-through (HITL enforced at UI).
5. Capability: catalog.taxonomy.suggest
Purpose: Given a course, suggest taxonomy nodes from a given taxonomy.
Flow:
1. catalog builds { course metadata + taxonomy tree }.
2. ai-gateway embeds taxonomy nodes (cached) and course text.
3. ai-gateway returns top-N nodes by cosine similarity, then LLM re-ranks.
Output:
{
"suggestions": [
{ "taxonomyId":"tax_01H…", "nodePath":"/science/physics/mechanics", "score":0.94 },
{ "taxonomyId":"tax_01H…", "nodePath":"/science/physics", "score":0.89 }
]
}
6. Capability: catalog.slug.suggest (S2)
Deterministic by default; AI as fallback when the heuristic yields collisions.
Algorithm:
- Normalise title to ASCII, lowercase, kebab-case.
- If unused within tenant → return.
- Else append a disambiguator via AI (
-intro,-2026,-advanced) up to 3 attempts. - Else fall back to
base-<shortULID>.
Cost cap per call: 500 μUSD.
7. No Direct Model Access
Catalog service MUST NOT import SDKs for OpenAI, Anthropic, Vertex, Azure OpenAI, Bedrock, or any on-prem model server. All calls are via ai-gateway-service HTTP API. Violation is a build-time lint failure (eslint custom rule no-direct-ai-sdk).
8. Cost & Budget
| Capability | Avg cost / call | Budget line (per tenant/mo) |
|---|---|---|
| Localize (3 locales) | ~3000 μUSD | included in tenant.ai.budget |
| Tags suggest | ~200 μUSD | included |
| Taxonomy suggest | ~500 μUSD | included |
| Slug suggest | ~50 μUSD | included |
Catalog publishes ai.usage.reported.v1 via the gateway side (catalog does not emit directly).
9. Failure Modes
| Gateway result | Catalog behavior |
|---|---|
status=error | Return 502 upstream to admin UI; do NOT persist partial output |
status=refused | Return 422 with reason (safety/policy) |
status=partial | Persist what's returned; flag missing locales in UI |
timeout (> maxLatencyMs) | Return 504; user can retry |
10. Audit & Provenance
Every AI-assisted field write creates a course_audit row with:
{
"action": "ai_assist_apply",
"diff": { "title": {"en":"old","ar":"new"} },
"provenance": {
"model": "claude-4.6",
"version": "2026-02-01",
"promptId": "catalog.metadata.localize.v3",
"promptVersion": "3.0.1",
"traceId": "00-…-…-01",
"decisionId": "dec_01H…",
"local": false,
"generatedAt": "2026-03-02T09:00Z",
"reviewedBy": "usr_01H…",
"reviewedAt": "2026-03-02T09:03Z",
"cost": { "microUSD": 2840, "tokens": { "in": 812, "out": 610 } }
}
}
This trail is surfaced in the admin UI as an "AI badge" on the field.
11. Offline & On-device AI
Catalog has no offline AI path. All suggestion endpoints are online-only and return 503 with Retry-After when the client is offline.
12. Testing
- AI calls mocked in CI; contract fixtures for each capability.
- Canary tests on staging: localize 10 sample courses → BLEU score ≥ threshold.
- HITL UX test: AI suggestion must not auto-apply without user confirmation.