AI Module#
Built-in AI integration with streaming support, response caching, and prompt injection. Works with OpenAI, Anthropic, and any OpenAI-compatible API.
streamAI#
Transform any AI stream into a Vercel AI SDK compatible response (Data Stream Protocol v1):
typescript
import { streamAI } from "@buntok/core";
import OpenAI from "openai";
const openai = new OpenAI();
app.post("/chat", async (ctx) => {
const { message } = await ctx.body();
const stream = await openai.chat.completions.create({
model: "gpt-4",
messages: [{ role: "user", content: message }],
stream: true,
});
return streamAI(ctx, stream, {
onCompletion: (fullText) => {
console.log("Full response:", fullText);
},
});
});streamAI API#
| Parameter | Type | Description |
|---|---|---|
| ctx | Context | Buntok request context |
| stream | AsyncIterable | AI response stream (OpenAI, Anthropic, etc.) |
| options? | object | onCompletion callback |
Info
Supported chunk shapes:
chunk.choices[0].delta.content (OpenAI), chunk.message.content (Anthropic), or plain string. If your AI provider uses a different chunk shape, content extraction may fail silently (empty chunks).injectSystemPrompt#
Safely inject a system prompt while removing any existing system messages from user input (prevents prompt injection):
typescript
import { injectSystemPrompt } from "@buntok/core";
const userMessages = [
{ role: "user", content: "Hello!" },
];
// Inject system prompt (prepends it, removes existing system messages)
const messages = injectSystemPrompt(
userMessages,
"You are a helpful assistant. Be concise."
);
// Result:
// [
// { role: "system", content: "You are a helpful assistant. Be concise." },
// { role: "user", content: "Hello!" }
// ]Warning
injectSystemPrompt removes any role: "system" messages from user input. This prevents users from overriding your system prompt.AICache#
Cache AI responses to save costs. Hashes the last few messages to generate cache keys:
typescript
import { AICache } from "@buntok/core";
import { Cache } from "@buntok/core/cache";
const aiCache = new AICache(new Cache());
app.post("/chat", async (ctx) => {
const { messages } = await ctx.body();
// Check cache first
const cached = await aiCache.get(messages);
if (cached) {
return ctx.json({ response: cached, cached: true });
}
// Call AI
const response = await callAI(messages);
// Cache result (default TTL: 3600s)
await aiCache.set(messages, response, 3600);
return ctx.json({ response, cached: false });
});AICache API#
| Method | Description |
|---|---|
| aiCache.get(messages) | Get cached response (returns null if miss) |
| aiCache.set(messages, response, ttl?) | Cache a response |
Full Example#
typescript
import { Buntok } from "@buntok/core";
import { streamAI, injectSystemPrompt, AICache } from "@buntok/core";
import { Cache } from "@buntok/core/cache";
import OpenAI from "openai";
const app = new Buntok();
const openai = new OpenAI();
const aiCache = new AICache(new Cache());
const SYSTEM_PROMPT = "You are a helpful coding assistant. Be concise.";
app.post("/chat", async (ctx) => {
const { messages } = await ctx.body();
// Inject system prompt safely
const safeMessages = injectSystemPrompt(messages, SYSTEM_PROMPT);
// Check cache
const cached = await aiCache.get(safeMessages);
if (cached) {
return ctx.json({ response: cached, cached: true });
}
// Stream response
const stream = await openai.chat.completions.create({
model: "gpt-4",
messages: safeMessages,
stream: true,
});
return streamAI(ctx, stream, {
onCompletion: async (fullText) => {
await aiCache.set(safeMessages, fullText);
},
});
});
app.listen(1212);