@tjoc/ai-core
v3.0.1
Published
AI provider adapters (Anthropic, OpenAI, Gemini) and prompt utilities for the tjoc.dev portfolio
Readme
@tjoc/ai-core
AI provider adapters for the tjoc.dev portfolio. Unified AIService interface over Anthropic (primary), OpenAI, and Gemini. ESM-only, zero config boilerplate.
Installation
pnpm add @tjoc/ai-coreNode ≥20. Published to npm.
Quick start — Anthropic (default)
import { AnthropicService } from '@tjoc/ai-core';
const ai = new AnthropicService(process.env.ANTHROPIC_API_KEY!);
// Single completion — defaults to claude-sonnet-4-6, 8192 max tokens
const { text } = await ai.complete('Summarise this CV in three bullet points.');
// With system prompt and conversation history
const { text } = await ai.complete('What is the next step?', {
system: 'You are a career coach. Be concise.',
messages: [
{ role: 'user', content: 'I just uploaded my CV.' },
{ role: 'assistant', content: 'Great, I can see it. What would you like to improve?' },
],
});
// Streaming
for await (const chunk of ai.stream('Write a cover letter for this role.', { system: 'Be professional.' })) {
process.stdout.write(chunk);
}AIService interface
Every provider implements the same interface. Swap providers by changing the constructor.
interface AIService {
complete(prompt: string, options?: AIRequestOptions): Promise<AIResponse>;
stream(prompt: string, options?: AIRequestOptions): AsyncIterable<string>;
}
interface AIRequestOptions {
model?: string; // overrides provider default
temperature?: number;
maxTokens?: number;
system?: string; // system prompt
messages?: AIMessage[]; // conversation history (replaces single-turn prompt if provided)
}
interface AIMessage {
role: 'user' | 'assistant';
content: string;
}
interface AIResponse {
text: string; // assembled text response
raw?: unknown; // raw provider response object
}Other providers
import { OpenAIService } from '@tjoc/ai-core';
const ai = new OpenAIService(process.env.OPENAI_API_KEY!);
const { text } = await ai.complete('Hello');import { GeminiService, FallbackAIService } from '@tjoc/ai-core';
// Fallback: try primary, use secondary on failure
const ai = new FallbackAIService(
new AnthropicService(process.env.ANTHROPIC_API_KEY!),
new GeminiService(process.env.GEMINI_API_KEY!),
);Provider defaults
| Provider | Default model | Notes |
|---|---|---|
| AnthropicService | claude-sonnet-4-6 | @anthropic-ai/sdk bundled |
| OpenAIService | provider default | bring your own openai dep |
| GeminiService | provider default | bring your own Gemini dep |
Prompt utilities
import { formatPrompt, sanitizePrompt, validatePrompt } from '@tjoc/ai-core';
const raw = ' What is 2 + 2?\r\n';
const formatted = formatPrompt(raw); // normalises whitespace and line breaks
const sanitized = sanitizePrompt(formatted); // removes control characters
const { isValid, error } = validatePrompt(sanitized);
if (!isValid) throw new Error(error);Adoption
wisecv is the first consumer — AnthropicService powers the 6-stage CV analysis pipeline (ADR 0007).
Development
pnpm build # tsup → dist/
pnpm typecheck # tsc --noEmit