Files
WRNexusJS/packages/ai
2026-07-19 17:14:12 +05:30
..
2026-07-12 15:55:18 +05:30
2026-07-12 15:55:18 +05:30
2026-07-19 17:14:12 +05:30

@wrnexus/ai

A tiny, zero-dependency Claude (Anthropic) client for WrNexus apps — generate and stream text with Claude from any server-side code.

Part of the WrNexus framework — an SSR-first, Bun-native full-stack web framework.

Overview

@wrnexus/ai is a thin, dependency-free wrapper over the Anthropic Messages API, built on fetch (Bun-native, no SDK). Use it in API routes, jobs, or middleware to call Claude. It defaults to the most capable model, claude-opus-4-8, reads your key from ANTHROPIC_API_KEY, and supports both one-shot generation and streaming.

Installation

bun add @wrnexus/ai

Private package — the machine must be authenticated to the wrnexus npm org (a read token in ~/.npmrc). Requires Bun (Node is not supported).

Set your key in the environment (e.g. .env):

ANTHROPIC_API_KEY=sk-ant-...

API

createAI(config?)

Creates a client. The key is read at call time, so it's safe to create at import.

import { createAI } from "@wrnexus/ai";
const ai = createAI(); // or createAI({ apiKey, model, maxTokens, baseURL, version })

AIConfig fields (all optional):

Field Default Description
apiKey ANTHROPIC_API_KEY Anthropic API key
model "claude-opus-4-8" Model id
maxTokens 4096 Default max output tokens
baseURL https://api.anthropic.com API base URL
version "2023-06-01" anthropic-version header

ai.generate(prompt, opts?): Promise<string>

One-shot text generation. prompt is a string or a Message[] history.

const text = await ai.generate("Write a haiku about Bun.");

const reply = await ai.generate(
  [
    { role: "user", content: "My name is Ada." },
    { role: "assistant", content: "Hi Ada!" },
    { role: "user", content: "What's my name?" },
  ],
  { system: "You are concise." },
);

ai.stream(prompt, opts?): AsyncGenerator<string>

Yields text deltas as they arrive.

for await (const chunk of ai.stream("Tell me a story.")) {
  process.stdout.write(chunk);
}

ai.streamResponse(prompt, opts?): Response

Returns a streaming text/plain Response — drop it straight into an API route.

// app/api/chat.ts
import { createAI } from "@wrnexus/ai";
const ai = createAI();

export const POST = async (ctx) => {
  const { prompt } = await ctx.req.json();
  return ai.streamResponse(prompt);
};

GenerateOptions

Option Type Description
system string System prompt
model string Override the model for this call
maxTokens number Override max output tokens
thinking boolean Enable adaptive extended thinking (deeper reasoning)
effort "low" | "medium" | "high" | "xhigh" | "max" Reasoning effort / token spend
messages Message[] Full history — supersedes prompt
signal AbortSignal Cancel the request

temperature / top_p are intentionally not exposed — the current Claude models reject them (400). Steer output with prompting instead.

AIError

Thrown on non-2xx responses or a model refusal. Carries .status and .type (e.g. "authentication_error", "rate_limit_error", "refusal").

import { AIError } from "@wrnexus/ai";
try {
  await ai.generate("...");
} catch (e) {
  if (e instanceof AIError && e.type === "rate_limit_error") {
    /* back off */
  }
}

Usage

Return generated JSON from an API route

// app/api/summarize.ts — summarize posted text
import { createAI } from "@wrnexus/ai";
const ai = createAI();

export const POST = async (ctx) => {
  const { text } = await ctx.req.json().catch(() => ({}));
  if (!text) return Response.json({ error: "Provide 'text'." }, { status: 400 });
  const summary = await ai.generate(`Summarize in one sentence:\n\n${text}`, {
    system: "You are a precise summarizer.",
  });
  return Response.json({ summary });
};

Stream a chat response to the browser

// app/api/chat.ts
import { createAI } from "@wrnexus/ai";

const ai = createAI({ model: "claude-sonnet-5" });

export const POST = async (ctx) => {
  const { messages } = await ctx.req.json();
  return ai.streamResponse(messages, {
    system: "Answer using concise Markdown.",
    maxTokens: 1_500,
  });
};

Requirements / Notes

  • Bun-only. Uses fetch, ReadableStream, TextDecoder/TextEncoder, and reads ANTHROPIC_API_KEY from Bun.env (falls back to process.env).
  • Zero dependencies — no @anthropic-ai/sdk; talks to the Messages API directly.
  • Defaults to claude-opus-4-8. Pass { model } for a different model (e.g. "claude-sonnet-5" for speed/cost, "claude-haiku-4-5" for the fastest).