{"_id":"@animica/sdk","name":"@animica/sdk","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@animica/sdk","version":"0.1.0","private":false,"license":"MIT","description":"Official Animica TypeScript/JavaScript SDK — an OpenAI-compatible client for the Animica inference API (chat, completions, embeddings, models, usage) with streaming and webhook signature verification. Zero runtime dependencies.","type":"commonjs","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"sideEffects":false,"publishConfig":{"access":"public"},"engines":{"node":">=18"},"keywords":["animica","ai","llm","inference","openai-compatible","chat","completions","embeddings","sdk","bittensor"],"homepage":"https://api.animica.org","repository":{"type":"git","url":"git+https://github.com/animicaorg/all.git","directory":"packages/sdk"},"bugs":{"url":"https://github.com/animicaorg/all/issues"},"author":{"name":"Animica"},"scripts":{"typecheck":"tsc --noEmit","build":"tsc -p tsconfig.json","lint":"echo no-lint","test":"echo \"no tests\"","prepublishOnly":"npm run build"},"devDependencies":{"@types/node":"^20.16.0","typescript":"^5.6.3"},"_id":"@animica/sdk@0.1.0","gitHead":"bbf1fc08b162008b1618a02465293dd396cab618","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-d3aeNPLeJmKchHeAq3UsUlRQUNV3AcHoJuc34NypoVvBWJ/nYRGHfPKjvM2ynxvCGe2bOnnwH3sfHyhtzkfF3A==","shasum":"523569934b5066f83e934f5f2cb1fd6901f8390f","tarball":"https://registry.npmjs.org/@animica/sdk/-/sdk-0.1.0.tgz","fileCount":12,"unpackedSize":34182,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCq3YnfCKMWamsAmHS8qv/amlHsECcc4CrL5N8DFlgtNAIgQZra+p1Penq5HfTNud+oF1SGhg19u7naAD6MTGsjUGI="}]},"_npmUser":{"name":"animica","email":"animicaorg@gmail.com"},"directories":{},"maintainers":[{"name":"animica","email":"animicaorg@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sdk_0.1.0_1782453901183_0.2643543217417572"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-26T06:05:01.066Z","0.1.0":"2026-06-26T06:05:01.331Z","modified":"2026-06-26T06:05:01.592Z"},"maintainers":[{"name":"animica","email":"animicaorg@gmail.com"}],"description":"Official Animica TypeScript/JavaScript SDK — an OpenAI-compatible client for the Animica inference API (chat, completions, embeddings, models, usage) with streaming and webhook signature verification. Zero runtime dependencies.","homepage":"https://api.animica.org","keywords":["animica","ai","llm","inference","openai-compatible","chat","completions","embeddings","sdk","bittensor"],"repository":{"type":"git","url":"git+https://github.com/animicaorg/all.git","directory":"packages/sdk"},"author":{"name":"Animica"},"bugs":{"url":"https://github.com/animicaorg/all/issues"},"license":"MIT","readme":"# @animica/sdk\n\nOfficial TypeScript / JavaScript SDK for the **Animica API** — a fast, OpenAI-compatible inference platform (chat, completions, embeddings, models, usage) with streaming and signed webhooks.\n\n- Zero runtime dependencies — uses the global `fetch`, works in Node 18+, Deno, Bun, Cloudflare Workers, and the browser.\n- Fully typed requests and responses.\n- Server-Sent Events streaming for chat.\n- Built-in webhook signature verification (`node:crypto` is loaded lazily, so the browser inference path stays dependency-free).\n\n```bash\nnpm install @animica/sdk\n# or: pnpm add @animica/sdk   /   yarn add @animica/sdk\n```\n\n> Base URL: `https://api.animica.org/v1` (also reachable at `https://console.animica.org/v1`).\n> Auth: send your key as `Authorization: Bearer anm_live_...` (use `anm_test_...` for test mode). The SDK does this for you.\n\n---\n\n## Quickstart\n\n```ts\nimport { Animica } from \"@animica/sdk\";\n\nconst client = new Animica({\n  apiKey: process.env.ANIMICA_API_KEY, // or pass it explicitly\n  // baseURL: \"https://api.animica.org/v1\", // default\n});\n\nconst completion = await client.chat.completions.create({\n  model: \"anm-fast-8b\",\n  messages: [\n    { role: \"system\", content: \"You are a helpful assistant.\" },\n    { role: \"user\", content: \"Write a haiku about GPUs.\" },\n  ],\n});\n\nconsole.log(completion.choices[0].message.content);\nconsole.log(completion.usage); // { prompt_tokens, completion_tokens, total_tokens }\n```\n\nIf `apiKey` is omitted, the SDK reads `ANIMICA_API_KEY` from the environment (Node only).\n\n### Available models\n\n```ts\nconst { data } = await client.models.list();\n// anm-fast-8b, anm-code-7b, anm-pro-70b, anm-bittensor-router,\n// anm-embed, anm-worker-small, anm-worker-code\n```\n\n| Model | Use |\n| ----- | --- |\n| `anm-fast-8b` | Low-latency general chat |\n| `anm-code-7b` / `anm-worker-code` | Code generation |\n| `anm-pro-70b` | Highest quality reasoning |\n| `anm-bittensor-router` | Routed to the Bittensor subnet |\n| `anm-embed` | Embeddings |\n| `anm-worker-small` | Cheap background tasks |\n\n---\n\n## Streaming\n\nPass `stream: true` and `await` the call — it resolves to an `AsyncIterable` of\n`chat.completion.chunk` objects. Iteration ends automatically at the terminal\n`[DONE]` sentinel.\n\n```ts\nconst stream = await client.chat.completions.create({\n  model: \"anm-pro-70b\",\n  stream: true,\n  messages: [{ role: \"user\", content: \"Stream a short story.\" }],\n});\n\nfor await (const chunk of stream) {\n  const delta = chunk.choices[0]?.delta?.content;\n  if (delta) process.stdout.write(delta);\n}\n```\n\nTypeScript narrows the return type by the `stream` flag: with `stream: true` you\nget `AsyncIterable<ChatCompletionChunk>`, otherwise a `ChatCompletion`.\n\n---\n\n## Text completions\n\n```ts\nconst res = await client.completions.create({\n  model: \"anm-code-7b\",\n  prompt: \"// a TypeScript function that reverses a string\\n\",\n  max_tokens: 128,\n});\nconsole.log(res.choices[0].text);\n```\n\n## Embeddings\n\n```ts\nconst res = await client.embeddings.create({\n  model: \"anm-embed\",\n  input: [\"hello world\", \"goodbye world\"],\n});\nconsole.log(res.data[0].embedding.length);\nconsole.log(res.usage); // { prompt_tokens, total_tokens }\n```\n\n## Usage\n\n```ts\nconst usage = await client.usage();\nconsole.log(usage.totalSpentUsd, usage.count);\nconsole.log(usage.data); // recent requests for the authenticated key\n```\n\n---\n\n## Safe retries (idempotency)\n\nSet `idempotencyKey` on any create call. The SDK sends it as the\n`Idempotency-Key` header (and strips it from the JSON body); the server replays\nthe stored response if the same key is seen again, so retries never double-bill\nor double-execute.\n\n```ts\nawait client.chat.completions.create({\n  model: \"anm-fast-8b\",\n  messages: [{ role: \"user\", content: \"charge me once\" }],\n  idempotencyKey: crypto.randomUUID(),\n});\n```\n\n---\n\n## Error handling\n\nEvery non-2xx response throws an `AnimicaError` with the OpenAI-shaped fields\nplus the `x-request-id` for support. Transport failures throw with `status: 0`.\n\n```ts\nimport { Animica, AnimicaError } from \"@animica/sdk\";\n\ntry {\n  await client.chat.completions.create({\n    model: \"anm-fast-8b\",\n    messages: [{ role: \"user\", content: \"hi\" }],\n  });\n} catch (err) {\n  if (err instanceof AnimicaError) {\n    console.error(err.status); // 401, 429, 400, 500, ...\n    console.error(err.type); // authentication_error | invalid_request_error |\n    //                          insufficient_quota | rate_limit_error |\n    //                          permission_error | api_error\n    console.error(err.code); // machine-readable code (may be null)\n    console.error(err.param); // offending parameter (may be null)\n    console.error(err.requestId); // x-request-id, include in support tickets\n    console.error(err.retryAfter); // seconds, present on 429\n    console.error(err.message);\n  } else {\n    throw err;\n  }\n}\n```\n\nRate-limit headers (`x-ratelimit-limit-requests`,\n`x-ratelimit-remaining-requests`, `x-ratelimit-reset-requests`) are returned on\nevery response; on `429` the SDK also parses `retry-after` into\n`err.retryAfter`.\n\n---\n\n## Webhook verification\n\nAnimica signs every webhook delivery with the header\n`X-Animica-Signature: t=<unixSeconds>,v1=<hex>`, where\n`hex = HMAC_SHA256(endpointSecret, `${t}.${rawRequestBody}`)`.\n\nUse the static `Animica.verifyWebhook` helper. **Pass the raw request body\nexactly as received** — do not re-serialize parsed JSON, or the signature will\nnot match.\n\n```ts\nimport { Animica } from \"@animica/sdk\";\nimport express from \"express\";\n\nconst app = express();\n\n// Capture the raw body so the signature can be recomputed verbatim.\napp.post(\n  \"/webhooks/animica\",\n  express.raw({ type: \"application/json\" }),\n  (req, res) => {\n    const signature = req.header(\"X-Animica-Signature\");\n    const rawBody = req.body.toString(\"utf8\");\n\n    const ok = Animica.verifyWebhook(\n      process.env.ANIMICA_WEBHOOK_SECRET!,\n      rawBody,\n      signature,\n      // toleranceSec = 300 (default): reject if |now - t| exceeds this window\n    );\n    if (!ok) return res.status(400).send(\"invalid signature\");\n\n    const event = JSON.parse(rawBody); // { id: \"evt_...\", type, created, data }\n    switch (event.type) {\n      case \"usage.updated\":\n        // ...\n        break;\n    }\n    res.sendStatus(200);\n  },\n);\n```\n\n`verifyWebhook` recomputes the HMAC, compares it in constant time, and rejects\ndeliveries whose timestamp is outside the tolerance window — defending against\nboth tampering and replay. It only touches `node:crypto` when called, so simply\nimporting the SDK in a browser bundle pulls in no Node built-ins.\n\n---\n\n## Drop-in for the official `openai` package\n\nThe API is OpenAI-compatible, so you can keep using the official `openai` SDK by\npointing it at Animica:\n\n```ts\nimport OpenAI from \"openai\";\n\nconst openai = new OpenAI({\n  apiKey: process.env.ANIMICA_API_KEY,\n  baseURL: \"https://api.animica.org/v1\",\n});\n\nconst res = await openai.chat.completions.create({\n  model: \"anm-fast-8b\",\n  messages: [{ role: \"user\", content: \"Hello from the openai package!\" }],\n});\n```\n\n`@animica/sdk` is the lighter, dependency-free option with first-class webhook\nverification; the `openai` package is handy when you want to share one client\nacross multiple providers.\n\n---\n\n## Configuration\n\n```ts\nnew Animica({\n  apiKey: \"anm_live_...\",            // required (or ANIMICA_API_KEY)\n  baseURL: \"https://api.animica.org/v1\", // optional override\n  fetch: customFetch,                // optional fetch polyfill (Node < 18)\n  defaultHeaders: { \"X-App\": \"my-app\" }, // merged into every request\n});\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-582abdec9dab71447d2c684934c28202"}