first commit
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
# @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
|
||||
|
||||
```bash
|
||||
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.
|
||||
|
||||
```ts
|
||||
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.
|
||||
|
||||
```ts
|
||||
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.
|
||||
|
||||
```ts
|
||||
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.
|
||||
|
||||
```ts
|
||||
// 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"`).
|
||||
|
||||
```ts
|
||||
import { AIError } from "@wrnexus/ai";
|
||||
try {
|
||||
await ai.generate("...");
|
||||
} catch (e) {
|
||||
if (e instanceof AIError && e.type === "rate_limit_error") {
|
||||
/* back off */
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
// 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 });
|
||||
};
|
||||
```
|
||||
|
||||
## 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).
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "@wrnexus/ai",
|
||||
"version": "0.2.12",
|
||||
"type": "module",
|
||||
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"engines": {
|
||||
"bun": ">=1.1.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"access": "restricted"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user