Skip to content

AI Assistant ​

@laikacms/server/ai provides the chat and session endpoints behind the Decap CMS editor assistant. It authenticates a Bearer token, streams a model response through the Vercel AI SDK, and persists conversations through consumer-supplied callbacks.

bash
pnpm add @laikacms/server ai @ai-sdk/anthropic

ai and the @ai-sdk/* provider are only needed when you mount these endpoints; swap @ai-sdk/anthropic for whichever provider you use.

Wiring example ​

typescript
import { decapAi } from '@laikacms/server/ai';
import { anthropic } from '@laikacms/server/ai/providers';
import { resolveBearer } from 'laikacms/auth';

const ai = decapAi({
  model: anthropic('claude-sonnet-5-5'),
  basePath: '/api/ai',
  authenticateAccessToken: async token => {
    const ctx = await resolveBearer(token, { verifySessionToken, lookupPatByHash });
    if (!ctx) throw new Error('Unauthorized');
    return { ...ctx.user, scopes: ctx.scopes };
  },
  callbacks: {
    createSession,
    getSession,
    getSessionsByDocument,
    updateSession,
    deleteSession,
  },
});

export default { fetch: ai.fetch.bind(ai) };

Re-export tool, jsonSchema, and the model factories from @laikacms/server/ai rather than importing ai directly — this keeps a single physical ai package and ensures branded tool/schema types match.

Endpoints ​

All paths are relative to basePath (default /api/ai). Every endpoint except /health requires Authorization: Bearer <token> and the requiredScope.

MethodPathPurpose
GET/healthLiveness probe; no auth
POST/chatStreams a response; body { messages, sessionId?, document }
GET/sessionsSessions for ?documentSlug=, scoped to the caller
GET/sessions/:idOne session with its full message history
DELETE/sessions/:idDeletes a session the caller owns

POST /chat replies with the AI SDK's UI message stream and an X-Session-Id header.

DecapAiConfig options ​

OptionTypeRequiredDefaultDescription
modelLanguageModel✓—Vercel AI SDK model (e.g. anthropic('claude-sonnet-5-5'))
authenticateAccessToken(token) => User✓—Validate a Bearer token; return { ...ctx.user, scopes: ctx.scopes } from resolveBearer
callbacksAiSessionCallbacks✓—Session persistence; see below
basePathstring'/api/ai'URL prefix for all endpoints
requiredScopeScope'content:write'Scope checked against user.scopes; users with no scopes (legacy full-admin sessions) are always allowed
systemPromptstringEnglish CMS promptReplaces the default system prompt entirely; the default getDocumentData/updateDocument guidance is discarded when this is set
toolsToolSet—Additional server-side tools (with execute); see client-side tools note below
maxOutputTokensnumber4096Maximum tokens in the model response
temperaturenumber0.7Sampling temperature
logger{ error(...): void }—Receives internal errors
messagesTranslationEnglishLocalized error responses and system prompt

AiSessionCallbacks interface ​

Consumer-supplied callbacks that persist conversations to your storage (KV, D1, Postgres, etc.).

typescript
interface AiSessionCallbacks {
  createSession(session: AiSession): Promise<void>;
  getSession(sessionId: string): Promise<AiSession | null>;
  getSessionsByDocument(documentSlug: string, userId: string): Promise<AiSession[]>;
  updateSession(
    sessionId: string,
    updates: Partial<Pick<AiSession, 'messages' | 'title' | 'updatedAt'>>,
  ): Promise<void>;
  deleteSession(sessionId: string): Promise<void>;
}

Client-side tools ​

getDocumentData and updateDocument are declared with no execute, so the SDK ships them to the browser: the CMS client (the Dulla LlmTransport) applies document edits locally and reports the result back as the tool output. Add your own server-side tools through config.tools — those should have an execute.

Authorization ​

requiredScope defaults to content:write because the assistant can edit entries. A User with no scopes is treated as full access, matching resolveBearer's convention for legacy sessions; populate scopes to gate AI access separately from content access.

Released under the MIT License.