npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@agentskit/adapters

v0.19.0

Published

Provider adapters for AgentsKit.

Downloads

9,143

Readme

@agentskit/adapters

Profile: major-package

Connect to any LLM provider — and swap between them — without touching your app code.

npm version npm downloads bundle size license stability GitHub stars

Tags: ai · agents · llm · agentskit · openai · anthropic · claude · gemini · chatgpt · ollama · embeddings · providers

Verified proof

  • Package metadata and tests live under packages/adapters/.
  • Package guide: https://www.agentskit.io/docs/reference/packages/adapters
  • Stability map: docs/STABILITY.md

How this fits the ecosystem

@agentskit/adapters is the provider layer: swap OpenAI, Anthropic, Gemini, Ollama, local models, and embedding providers without rewriting your agent.

  • AgentsKit: compose it with the other packages in this repo to build agents from small, swappable parts.
  • Registry: look for ready agents and templates that already use this layer at registry.agentskit.io.
  • Playbook: learn the production patterns behind this layer at playbook.agentskit.io.

Docs: package guide · agent handoff

Why adapters

  • Vendor independence — switch from OpenAI to Anthropic to a local Ollama model by changing one line; your hooks, runtime, and tools stay untouched
  • 25 native adapters in the catalog — Anthropic, OpenAI, Gemini, Ollama, DeepSeek, Grok, Kimi, Mistral, Cohere, Together, Groq, Fireworks, OpenRouter, Hugging Face, LM Studio, vLLM, llama.cpp, LangChain, Vercel AI SDK, and additional compatible providers
  • Embedder functions built in — the same adapter pattern covers text embeddings, so you can reuse provider config for both chat and RAG
  • One-line local AI — ollama({ model: 'llama3.1' }) for fully offline agents with no API key required
  • CLI-backed agents — @agentskit/adapters/cli normalizes text, JSON, and ACP-based local LLM CLIs
  • LangChain bridge — @agentskit/adapters/langchain-bridge turns any adapter into a LangChain BaseChatModel for createAgent

Gateway and multimodal transport

openaiCompatible({ model, baseUrl, path, headers, fetch }) supports custom OpenAI-compatible endpoints; apiKey is optional and headers override defaults. openRouter aliases openrouter; cloudflareAiGateway and workersAi supply Cloudflare REST URLs without runtime bindings. Catalog URL variables are supplied explicitly through env, including CLOUDFLARE_ACCOUNT_ID.

OpenAI-compatible, Anthropic, Gemini/Vertex and Ollama serializers read message.parts. Unsupported sources/modalities raise CapabilityUnsupportedError with code: CAPABILITY_UNSUPPORTED when multimodal input is enabled. Models without multimodal capability fall back to text parts and short attachment markers, omitting binary data and data URLs, and log at most one warning per request. OpenAI and Anthropic assistant binary parts also use this text fallback. Explicit capabilities.multiModal takes precedence over model-name heuristics (GPT-4/5, o-series, vision, Gemini and Claude 3+ names, including provider prefixes). Set it explicitly for custom models or providers whose capabilities differ. See the tested modality matrix and configuration guide. These are local wire-format tests; live provider acceptance remains unverified.

Install

npm install @agentskit/adapters

The runtime example below also needs @agentskit/runtime. The RAG example also needs @agentskit/rag, @agentskit/memory, and the optional vectra peer (npm install @agentskit/rag @agentskit/memory vectra).

Quick example

import { anthropic } from '@agentskit/adapters'
import { createRuntime } from '@agentskit/runtime'

// Switch provider by swapping one import
const adapter = anthropic({ apiKey: process.env.ANTHROPIC_API_KEY, model: 'claude-sonnet-4-6' })
// const adapter = openai({ apiKey: process.env.OPENAI_API_KEY, model: 'gpt-4o' })
// const adapter = ollama({ model: 'llama3.1' })

const runtime = createRuntime({ adapter })
const result = await runtime.run('Summarize the latest AI news')
console.log(result.content)

Embeddings (for RAG)

Use the same package for vector embeddings — wire openaiEmbedder, geminiEmbedder, or ollamaEmbedder into @agentskit/rag:

import { openaiEmbedder } from '@agentskit/adapters'
import { createRAG } from '@agentskit/rag'
import { fileVectorMemory } from '@agentskit/memory'

const rag = createRAG({
  embed: openaiEmbedder({ apiKey: process.env.OPENAI_API_KEY! }),
  store: fileVectorMemory({ path: './vectors' }),
})

Features

  • Providers: Anthropic, OpenAI, Gemini, Ollama, DeepSeek, Grok, Kimi, Mistral, Cohere, Together, Groq, Fireworks, OpenRouter, Hugging Face, LM Studio, vLLM, llama.cpp, LangChain, LangGraph, Vercel AI SDK, generic ReadableStream
  • Embedders: openaiEmbedder, geminiEmbedder, ollamaEmbedder, deepseekEmbedder, grokEmbedder, kimiEmbedder, createOpenAICompatibleEmbedder
  • Fetch-backed adapters run against the shared Adapter contract v1 suite (ADR 0001); SDK-backed adapters have provider-specific contract and resilience coverage
  • Custom adapter authoring via createAdapter()
  • Higher-order adapters: createRouter (cost/latency/classifier), createEnsembleAdapter (fan-out + merge), createFallbackAdapter (ordered try-next)

CLI-backed adapters

The Node-only @agentskit/adapters/cli subpath runs an explicitly selected local LLM executable without a shell:

import { createCliAdapter, getCliProviderManifest, resolveCliManifest } from '@agentskit/adapters/cli'

const manifest = getCliProviderManifest('codex')
if (!manifest) throw new Error('provider manifest is unavailable')
const adapter = createCliAdapter(resolveCliManifest(manifest, { mode: 'review-safe' }))

The generic factories are createCliAdapter (exec-text), createJsonCliAdapter (exec-json), and createAcpCliAdapter (ACP v1 over JSON lines). The built-in manifests cover Codex, Claude Code (text and JSON output), Grok CLI, and OpenCode. resolveCliManifest keeps command, argv, protocol, provider id, and mode explicit; diagnoseCliProviderManifest verifies availability and an optional version pattern. review-safe is the default: no shell, automatic installation, native login, MCP, plugins, or terminal tools. Use trusted-local explicitly when a developer intentionally wants the CLI's local authentication and environment. requiredCapabilities is checked before spawning, and onDiagnostic receives redacted exit, timeout, abort, and output-limit data. Structured output fails closed; timeouts, aborts, output limits, and non-zero exits produce terminal adapter errors. Process termination is awaited before the adapter finishes, including when input or output fails. restricted-environment is an explicit environment allowlist mode; it is not an OS, filesystem, process, or network sandbox. Use @agentskit/sandbox when a real isolation boundary is required.

exec-text vs exec-json: what each protocol can return

The protocol decides which stream chunks a manifest can produce. Pick the manifest by what you need back, not by the CLI name:

| Protocol | Factory | Chunks emitted | Not available | | ----------- | ---------------------- | --------------------------------------- | ---------------------------------------------- | | exec-text | createCliAdapter | text (streamed), done | structuredOutput, reasoning, tool_call, usage | | exec-json | createJsonCliAdapter | text, reasoning, tool_call, usage, done | streaming (one response per process) | | acp | createAcpCliAdapter | text, reasoning (streamed), done | tool calls (rejected), MCP, plugins, terminal |

validateCliProviderManifest rejects a manifest that claims a capability its protocol cannot deliver, and requiredCapabilities fails before spawning for the same reason. In practice: the claude-code and codex manifests are exec-text and only ever yield plain text. For tool calls, usage, or structured output from Claude Code use claude-code-json instead.

The same table is enforced at compile time. CliProviderManifest is a union discriminated by protocol, capabilities is typed as CliCapabilitiesFor<protocol>, and getCliProviderManifest('claude-code') returns CliProviderManifestFor<'exec-text'>, so this does not type-check:

const manifest = getCliProviderManifest('claude-code')!
resolveCliManifest(manifest, { requiredCapabilities: { tools: true } })
//                                                    ^^^^^ not assignable: exec-text has no tools/structuredOutput

Claude Code

Two first-party manifests wrap the claude executable:

  • claude-code — claude -p, exec-text. Streams the final answer as text.
  • claude-code-json — claude -p --output-format json, exec-json. Parses the result envelope into text, tool_call, and usage chunks with session_id, total_cost_usd, duration_ms, and num_turns as metadata. is_error or a non-success subtype fails closed.
import { createJsonCliAdapter, getCliProviderManifest, resolveCliManifest } from '@agentskit/adapters/cli'

const manifest = getCliProviderManifest('claude-code-json')
if (!manifest) throw new Error('provider manifest is unavailable')
const adapter = createJsonCliAdapter(resolveCliManifest(manifest, { mode: 'trusted-local' }))

When the request carries context.tools, the tool list and a { text?, toolCalls } reply contract are appended to Claude Code's system prompt via --append-system-prompt; claude-code-json recognizes that object (fenced or bare) in the result string and emits tool_call chunks. Plain prose results stay text. structured_output from --json-schema is honored the same way. Tool execution itself remains the consumer's responsibility: the adapter never enables Claude Code's own tools, MCP, or plugins.

Prompt serialization

createCliAdapter and createJsonCliAdapter default to writing the raw AdapterRequest as one JSON line on stdin. That is the right contract for a purpose-built CLI, but agentic CLIs read stdin as a user prompt, and Claude Code refuses a raw JSON request containing systemPrompt as an apparent prompt-injection attempt. The first-party manifests therefore use two exported serializers:

  • serializeCliPrompt (used by codex) writes labelled blocks:

    [system]
    You are a reviewer.
    
    [user]
    review this
  • serializeCliMessages plus claudeCodeRequestArgs (used by claude-code and claude-code-json) writes only the conversation to stdin (a single user message is written bare) and passes the system prompt and the tool contract through --append-system-prompt. Verified live against Claude Code 2.1: the same instructions inside the user prompt, even as labelled [system] blocks, are flagged as an injection attempt, whereas through the system flag a request with context.tools comes back as a tool_call chunk.

A manifest may declare serializeRequest, requestArgs (extra per-request argv, appended after args), parseOutput, and parse; resolveCliManifest forwards them to the factory. buildCliSystemPrompt(request) returns the system prompt plus tool contract for other CLIs with a system-prompt flag.

Use buildArgs(request) only for CLIs that require the prompt in argv; it is request-aware and still uses direct, shell-free spawning. Set serializeRequest: () => '' when the provider does not consume stdin. For JSONL or event-wrapped output, parseOutput(stdout) can decode raw stdout before the normal parse(value) callback runs. For CLIs that write the final response to a file, outputFile reads that file after process completion with the same byte limit and abort handling.

LangChain bridge: use any adapter as a LangChain chat model

langchain() and langgraph() expose a LangChain runnable as an AdapterFactory. The @agentskit/adapters/langchain-bridge subpath goes the other way: adapterToLangChainModel(adapter) wraps any AdapterFactory (mock, Ollama, a CLI-backed adapter, ...) as a real BaseChatModel, so it can replace ChatAnthropic/ChatOpenAI in createAgent, AgentNode, or a plain chain. It needs @langchain/core (optional peer dependency).

import { createAgent } from 'langchain'
import { tool } from '@langchain/core/tools'
import { mockAdapter } from '@agentskit/adapters'
import { adapterToLangChainModel } from '@agentskit/adapters/langchain-bridge'

const add = tool(async ({ a, b }) => String(a + b), {
  name: 'add',
  description: 'Add two numbers',
  schema: { type: 'object', properties: { a: { type: 'number' }, b: { type: 'number' } }, required: ['a', 'b'] },
})
const model = adapterToLangChainModel(mockAdapter({ response: [/* ... */] }), { modelName: 'mock' })
const agent = createAgent({ model, tools: [add] })
  • bindTools() forwards LangChain tools (structured tools, OpenAI-format definitions, runnable tools) as AgentsKit ToolDefinitions on context.tools; tool_choice lands in context.metadata.toolChoice.
  • System messages become both a system message and context.systemPrompt; AI messages keep tool_calls; tool messages keep tool_call_id.
  • tool_call chunks become AIMessage.tool_calls (args parsed from JSON), usage chunks become usage_metadata, reasoning chunks land in additional_kwargs.reasoning, error chunks throw, and .stream() yields AIMessageChunks with tool_call_chunks.
  • Every response is a real AIMessage/AIMessageChunk instance, so wrapModelCall middleware and responseFormat work together. The default profile reports toolCalling and no native structuredOutput, so createAgent uses its tool strategy, which any tool-calling adapter can satisfy. Pass profile: { structuredOutput: true } only for adapters that honour a provider-side JSON schema.

Stream guarantees

  • A stream terminates exactly once with done or error; terminal errors carry an Error in metadata.error.
  • Fetch retries use @agentskit/net backoff and Retry-After parsing; the fetchWithRetry and adapter retry options keep their 3-attempt defaults. readSSELines remains as a deprecated compatibility wrapper; new parsers should consume parseSSE events directly.
  • SSE event data preserves whitespace and joins multiple data: fields with newlines. NDJSON readers flush a final UTF-8 chunk and accept a last record without a newline.
  • Provider streams that close before their native completion marker are treated as truncated, not successful.
  • abort(reason) propagates to active fetch readers and SDK requests and terminates with the same error semantics.
  • Native tool histories preserve call/result correlation. Parallel tool results are encoded in a single provider turn where required.
  • Credentials stay in provider headers when the protocol supports them; Gemini API keys are never placed in request URLs.
  • vercelAI consumes the Vercel AI SDK UI message stream v1 protocol, including its required response header and [DONE] marker.

Higher-order adapters

import { createRouter, anthropic, openai } from '@agentskit/adapters'

// Auto-pick cheapest capable candidate per request.
const router = createRouter({
  candidates: [
    { id: 'haiku', adapter: anthropic({ model: 'claude-haiku-4-5' }), cost: 0.25 },
    { id: 'sonnet', adapter: anthropic({ model: 'claude-sonnet-4-6' }), cost: 3 },
    { id: 'gpt-mini', adapter: openai({ model: 'gpt-4o-mini' }), cost: 0.15 },
  ],
})

See Adapter router, Ensemble, and Fallback chain.

Ecosystem

| Package | Role | |---------|------| | @agentskit/core | Adapter, EmbedFn, types | | @agentskit/runtime | Headless createRuntime | | @agentskit/rag | createRAG + embedders | | @agentskit/memory | Vector + chat memory backends |

Testing Adapters

Three built-in utilities let you test agents without hitting a real LLM.

mockAdapter — deterministic responses

import { mockAdapter } from '@agentskit/adapters'

const adapter = mockAdapter({
  response: [
    { type: 'text', content: 'Hello!' },
    { type: 'done' },
  ],
})

Pass a function to make responses request-aware, or pass an array of arrays to return different chunks on each call (sequenced mode). Use the optional history array to capture every request for assertions.

recordingAdapter + inMemorySink — capture real calls

import { recordingAdapter, inMemorySink, anthropic } from '@agentskit/adapters'

const sink = inMemorySink()
const adapter = recordingAdapter(
  anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-sonnet-4-6' }),
  sink,
)
// Runs the real LLM and captures every chunk to sink.fixture

replayAdapter — replay captured fixtures

import { replayAdapter } from '@agentskit/adapters'
import fixture from './fixture.json'

const adapter = replayAdapter(fixture) // no network calls

Typical workflow: record once in dev → commit JSON fixture → replay in CI.

Contributors

License

MIT — see LICENSE.

Docs

Full documentation · GitHub

Maturity and compatibility

  • Stability: beta — see docs/STABILITY.md
  • The implementation is hardened for a future freeze, but promotion still requires the 90-day beta window, two released minor lines, accepted package RFC, and repository evidence required by ADR 0024.
  • Node.js 20+ and TypeScript strict mode
  • Published as @agentskit/adapters

Contributing

See CONTRIBUTING.md and the monorepo LICENSE.