Call any AI provider easily

Claude, Gemini, OpenAI and Claude Code — one call, and the same shape coming back from all four.

Every provider hands you its own SDK, its own option names, and its own shape coming back. Add a second provider and you write the same feature twice.

Splendid AI gives you one call. Name the provider, send the prompt, read the answer off the same fields every time.

import { ai } from '@splendidlabz/ai'
const answer = await ai({
provider: 'gemini',
model: 'gemini-3.6-flash',
systemPrompt,
prompt: 'When are you free?',
})
console.log(answer.text)

Swap provider to claude, openai or claudep and the rest of the call stays where it is. Pass an option the new provider can’t take and it throws, naming what broke — rather than quietly sending a different call.

Headless calls

Run Claude Code from your own code, on the subscription you already pay for

claudep runs Claude Code in non-interactive mode and authenticates with the login already on the machine. The call draws on that subscription rather than metered API tokens.

import { claudep } from '@splendidlabz/ai'
const answer = await claudep({
model: 'sonnet',
systemPrompt,
prompt: 'When are you free?',
})

It takes the same options as the other three and answers in the same shape, with the things only the CLI reports gathered under meta — every tool the run used, in order, and why the loop ended.

When a provider is out

Name the standbys and the call keeps going

A provider goes down and every feature behind it goes with it. fallbacks is a list of standby calls, walked in order when the one before it is out.

const answer = await ai({
provider: 'claude',
model: 'claude-sonnet-4-5',
systemPrompt,
prompt,
fallbacks: [
{ provider: 'gemini', model: 'gemini-3.6-flash' },
{ provider: 'openai', model: 'gpt-5.6-sol', maxRetries: 0 },
],
})

A fallback is a whole call rather than a provider bolted onto the first one, so it takes everything its own sender takes — down to that provider’s own spelling for reasoning and thinking config.

Prompts live in markdown

One place to read them, one place to change them

Prompts spread across JavaScript files get hard to read and harder to change. The same rule ends up in three modules and two of them go stale.

Splendid AI reads them off disk instead. Wording lives in .md beside the code that sends it, and the filename is the address.

import { claudep, loadPrompts, renderPrompt } from '@splendidlabz/ai'
const prompts = loadPrompts(import.meta.url)
const answer = await claudep({
model: 'sonnet',
systemPrompt: prompts.job,
prompt: renderPrompt(prompts.turn, {
name: 'Wei Ling',
standing: prompts.asking,
}),
})

renderPrompt fills the slots, and it takes conditions — so a prompt that changes with the data stays in the file rather than in an if statement three modules away.

## The chat
{{#if chat}}
{{chat.lines}}
{{:else}}
Nothing yet — this is your first message to them.
{{/if}}

The two halves are what keep the bill down. systemPrompt holds the stable half and prompt holds the per-call half. Keeping them apart is what lets the stable half match a cached prefix, which is the next section.

Cache each half on its own clock

A chat that also runs tools has two clocks going in one call

The system prefix waits on a person deciding what to type. The conversation gets rewritten every round. Those are different clocks, and most libraries give you one switch for both.

cache: { system: '1h', rounds: '5m' }

An hour pays wherever the gap between calls is a person — a chat whose replies land minutes apart reads a warm prefix that a five-minute entry would already have dropped, and the write it saves is the whole system half plus every tool declaration. Inside a tool loop, where rounds land seconds apart, that same hour is 60% over the odds for nothing.

cache: true and cache: false are free and do what they always did. The lifetimes and the split are what you’re buying.

Know what every call cost

Providers give you token counts. They don’t give you dollars.

cost_usd sits on every result, priced from a rate table kept inside the package and read against the model the call actually resolved to. Switch on logUsage and each call prints its own line:

const answer = await claudep({
model: 'sonnet',
systemPrompt,
prompt,
logUsage: true,
})
[claudep] 0 cached, 0 written, 511 fresh, ~$0.0011 list, 2.4s

Writes sit in that line alongside reads because they’re the tell. A prefix that stopped matching shows up as a write where there used to be a read, and nothing else in the system reports it — so the caching in the last section reads as a number instead of a feeling.

It says list because a claudep call runs on your subscription, and a bare figure there would read as a bill.

The same numbers are on the result whether you log them or not, as answer.usage and answer.cost_usd. A model with no rate on file answers undefined rather than zero — a zero reads as a free call, and something downstream adds it to a total.

Tools without writing the schema twice

Describe the arguments once, and act on what comes back

buildToolParams takes the arguments a tool accepts as a flat list and writes the parameters object from it — the wrapper, the required array, additionalProperties: false.

import { buildToolParams } from '@splendidlabz/ai'
buildToolParams([
{
name: 'email',
type: 'string',
description: 'Their work email',
required: true,
},
])

invokeOutputHandlers is the other half of the same job. With tools, the model picks the function and every pick costs a round. Here the schema does the picking — one response, and you decide what runs from the values in it.

const { parsed_output } = await ai({
provider: 'claude',
model,
prompt,
schema,
})
await invokeOutputHandlers(parsed_output, {
summary: value => saveSummary(value),
notify: (value, output) => sendEmail(value, output.summary),
})

A handler gets its own field first and the whole output second, so one that needs a sibling field has it. A field the model left null is one it declined, and that handler doesn’t run.

Why not the Vercel AI SDK?

Use it. It’s good, it’s free, and it does the thing this page opened with — one interface over several providers, with streaming, tools and structured output.

Splendid AI is free for all of that too. The difference is what sits past it:

  • Running Claude Code headlessly, on the subscription you already pay for
  • Dollars per call, not just token counts
  • Prompts read off disk, with the conditions in the file
  • Two cache clocks named apart in one call
  • A Gemini cache you hold as a resource, with a lifetime you set
  • Tool parameters written once
  • Handlers that run off the schema instead of costing a round each

Some of those are free here and some need a key. The next section draws that line exactly.

If none of them are problems you have yet, take the free half and come back when they are.

What’s free, and what isn’t

Most of the package is free and stays free — all four senders, the routing between them, schemas, files, tools with toolChoice and maxRounds, streaming, sessions, token counts on every result, and cache: true / cache: false.

tools is free deliberately. It’s how you build something real on this.

Seven things need a licence key:

loadPrompts, readPrompt, renderPromptprompts read off disk
fallbacksthe standby chain
buildToolParamstool parameters from a list
invokeOutputHandlershandlers run off the schema
cache past a booleana lifetime, or a { system, rounds } split
createCache, deleteCache, listCachesholding a Gemini cache yourself
cost_usddollars per call

The line is drawn on capability rather than on sender, so what you pay has nothing to do with which provider you picked.

One key covers every Splendid Labz package you hold. Put it in .env as SPLENDID_LICENSE_KEY and it’s read on each call.

Getting access to Splendid AI

Two ways in.

Take Splendid AI if this package is the one you need. Take Splendid Pro if you want every Splendid Labz library on one key — components, layouts, styles, utilities and the rest.

Splendid AI

$99 only
Four providers behind one call, with the prompt, cache and cost engineering already done.
  • Prompts as markdown files on disk
  • A fallback chain across providers when the one you named is out
  • Tool parameters built from a schema
  • Output handlers that act on what came back
  • Cache control past a boolean, and Gemini caches you hold yourself
  • Per-call cost in USD at list rates

Splendid Pro

$199 only
Artfully crafted tools to speed up your web development workflow — includes components, layouts, styles, and utilities.
  • Everything in Splendid AI
  • All other Splendid libraries
    • Splendid Astro
    • Splendid Svelte
    • Splendid Layouts
    • Splendid Styles
    • Splendid Utils
    • Splendid Tracking

Once you’ve decided:

  • Click the button
  • Make payment
  • You’ll get an email with your licence key and a login for the docs

Put the key in .env as SPLENDID_LICENSE_KEY and every paid feature above unlocks on the next call. Nothing to reinstall.

One call, four providers

And the seven things you only find out you need once it’s running

I built this because I was running all four providers across my own projects and kept writing the same five things again in every one — the prompt loading, the fallback, the cache split, the cost line.

Try it risk-free for 30 days. If it doesn’t earn back what you paid, ask and you get every cent back.

Splendid AI

$99 only
Four providers behind one call, with the prompt, cache and cost engineering already done.
  • Prompts as markdown files on disk
  • A fallback chain across providers when the one you named is out
  • Tool parameters built from a schema
  • Output handlers that act on what came back
  • Cache control past a boolean, and Gemini caches you hold yourself
  • Per-call cost in USD at list rates

Splendid Pro

$199 only
Artfully crafted tools to speed up your web development workflow — includes components, layouts, styles, and utilities.
  • Everything in Splendid AI
  • All other Splendid libraries
    • Splendid Astro
    • Splendid Svelte
    • Splendid Layouts
    • Splendid Styles
    • Splendid Utils
    • Splendid Tracking

Stay awesome,
Zell