Call a tool from your app
Named AI tools, the kinds and providers, the files a tool takes, and model aliases.
Call a tool from your app
A tool file at gemmein/ai/tools/deep-research.json:
{
"label": "Deep Research",
"provider": "openai",
"model": "gpt-4o",
"credits": 20,
"requires": "access:pro-max",
"instructions": "You are a careful research assistant. Answer with sources.",
"promptTemplate": "Research this for a {{audience}} reader:\n\n{{question}}",
"inputs": [
{ "name": "question", "type": "text", "required": true, "maxLength": 2000 },
{ "name": "audience", "type": "text" }
],
"bounds": { "maxOutputTokens": 4000 }
}
The app never sends a prompt or a provider request. It names the tool and passes the inputs the tool declares. Gemmein composes the request in the provider's own format, from the instructions and the template on the server. The answer comes back in the provider's own shape, so a streaming call streams:
const res = await g.ai.run("deep-research", { question: text, audience: "beginner" }, { stream: true })
for await (const chunk of res.body) render(chunk) // the provider's SSE, byte for byte
res.headers.get("x-gemmein-credits-remaining") // the balance after this call
What it is. A named AI operation, defined on the server, that you price in credits and restrict by access.
Does.
- Composes the provider request from the tool's own instructions and template, with the inputs the app sent.
- Runs it on your key with the pinned model, or the model alias the tool names, and the output cap.
- Charges the tool's price, per call or per tokens (see Pricing).
- Records every call on
ai_calls(who, tool, tokens, units and unitCount, credits, outcome). It keeps the prompt and answer only when you switch that on for the tool.
Does not. Does not let the browser compose the request or see the prompt (raw calls are off unless the owner switches them on for a provider key); does not let the browser set a price, a model or the access it requires; does not run from a secret key; does not rename a tool after creation.
Needs something else when. You want a plan-dependent price for the same
operation → make two tools, each with its own access requirement; you meter something that is not an AI call
→ spendCredits from your server; you need an image, audio, video or a
transcript → a job tool and a run; you need embeddings → your server
calls the provider directly.
Example. "Deep Research", openai, gpt-4o, 20 credits, requires access:pro-max, with
instructions and a question input.
For a non-stream answer as one string, whichever provider answered, and for the person's own history:
const answer = await g.ai.runText("deep-research", { question: text })
const { calls } = await g.ai.calls() // what they ran, when, what it cost, how it ended
g.ai.runText reads choices[0].message.content from OpenAI, joins
content[].text from Anthropic, and joins
candidates[0].content.parts[].text from Google.
A provider's non-2xx answer is returned as it came by g.ai.run, and is not
thrown. g.ai.runText, like g.ai.text, throws it as
provider_error with the provider's status and message. A refusal by Gemmein
throws GemmeinError with one of the codes below. An unknown
input, a missing required one, a wrong type or a value over its cap is refused by name before
anything is spent (invalid_inputs).
What the person sees:
| When | Message |
|---|---|
| They lack the access | "Deep Research requires Pro Max." |
| They are short of the price | "Deep Research costs 20 credits. You have 7." |
| They are short of a per-token tool's ceiling | "Deep Research needs up to 12 credits. You have 7." |
| The ledger line after the call | "20 credits spent · Deep Research · 87 remaining." |
Tool kinds and providers
A tool is one of two kinds. A chat tool answers now. A job tool (generate or transcribe) answers with a run. A provider serves every kind its key sells:
| Kind | Answers with | Providers |
|---|---|---|
| chat (the default) | An answer now | openai, anthropic, google, openai-compatible |
| generate | A run: an image, a video or audio out | openai, google, replicate, fal, elevenlabs, runway, external |
| transcribe | A run: audio in, text out | openai, google, deepgram, replicate, fal, assemblyai, elevenlabs, external |
An OpenAI key makes chat, image, speech, transcription and video tools. A Google key makes
chat, image, video and audio-transcription tools. external is
your own pipeline.
When your own server makes the model call, or the call is neither chat nor a job (such as
embeddings), call the provider directly with your key. Ask Gemmein's server methods in the
SDK reference who the person is and what they hold. A chat model on
any server that speaks the OpenAI Chat Completions format runs as provider
openai-compatible (see Any OpenAI-compatible provider);
a server that speaks another format runs as your own pipeline.
Files a tool takes
A tool says which kinds of file a person may hand it: image, audio,
video and document. Each is a tick in the tool's file
("accepts": ["image", "document"]) and on the dashboard's AI page. A
file input takes the ref of a file the person already uploaded, to any
collection: g.ai.run("describe-photo", { photo: upload.ref }).
Gemmein checks the file's kind, the tool's model and the file's size before any credit moves, then hands the file to the provider. Most providers fetch a short-lived signed link made for that one call. Small files ride inline. Gemini files over 14 MB stream from storage into Gemini's own file store. A file is never sent as a public link, never converted, and never held whole in Gemmein's memory.
A tick is offered only where the tool's provider and model read that kind:
| Provider | Takes | Models |
|---|---|---|
| image, audio, video, PDF | Gemini 1.5, 2.x and 3.x | |
| openai | image · audio | image: gpt-4o, gpt-4.1, gpt-5, o1, o3, o4-mini · audio: gpt-4o-audio models (mp3 or wav); transcription: whisper-1, gpt-4o-transcribe |
| anthropic | image · PDF | image: Claude 3 and later · PDF: Claude 3.5 Sonnet and later |
| assemblyai, deepgram, elevenlabs | audio, video | transcription models (ElevenLabs Scribe) |
| replicate, fal | image, audio, video | models that take a file link |
| runway | image | image-to-video models |
| external | all four | your own pipeline, by a short-lived signed link |
npx gemmein dev and npx gemmein sync refuse a tick the model
does not read and name the models that do. A file may be up to 500 MB of video, 100 MB of
audio, or 25 MB of image or document, or the provider's own limit when lower.
"inputMaxMb": { "video": 200 } in the tool's file sets a lower limit for one tool;
the AI page has no size field. A tool that takes files is priced per run: a flat credit price
per call, held and charged as that price whatever the file's size. A video input can cost
more on your own provider key. A tool charged per token that ticks a kind is refused at
gemmein dev, gemmein sync and on the AI page: "A tool that takes files
is priced per run — set a credit price per call, and lower its file size limit if you want to
cap your cost." Text-only tools keep per-token pricing. Files a person hands a tool count
toward your app's storage; video fills it fastest, and you can add 50 GB storage packs under
Usage. With ticks set, a file input takes only a Gemmein file ref, never a link. A tool
created before ticks takes exactly the files it took before.
Create a tool
Create a tool on the dashboard's AI page, or have your AI write a file at
gemmein/ai/tools/<name>.json. npx gemmein sync pushes it to
Development. npx gemmein sync --live pushes it to your live app on its own
approval: you approve the push in the browser, item by item, and anything different from
what you approved is refused.
The file owns the implementation on every sync. After creation, the dashboard owns the commerce: label, credits, unit and per, access, on/off, record calls, and a transcribe tool's delete-after-run. Every field is in the field table.
A tool can be defined before its provider key exists. Runs are refused
ai_not_configured until the key is added on the AI page, and the dashboard shows
the tool as waiting on a key until then. A tool's name is fixed once created, because it is
what the app calls.
Model aliases
What it is. A name of your own, such as fast or writer,
that stands for one provider and one model. A tool names the alias in place of a model.
Does.
- Resolves at call time to the alias's provider and model. Repointing the alias moves every tool on it, with no edit to any tool and no deploy.
- Lives on the AI page or in
gemmein/ai/aliases.json(one object of name → { provider, model }), and syncs with the tools. - Refuses at save a model the provider key's allowlist does not admit.
- Keeps a tool and its alias on one provider.
Does not.
- Span providers. An alias is on one provider, and a tool on another cannot name it.
- Exist on
external(your pipeline has no model). - Change a tool's price, access or inputs.
- Sit beside a pinned model. A tool names a model or an alias, never both.
- Stop an alias being removed while tools still name it. Such a tool answers
409 tool_incompleteuntil it is repointed.
Needs something else when.
- You want a different model per plan → two tools, each with its own access requirement.
- You want the app to choose the model → not offered; the model is the server's.
- You want a model the allowlist lacks → add it to the provider key's models on the AI page first.
Example. fast → openai, gpt-4o-mini. "Quick answer" and
"Summarise" both name fast. Move fast to gpt-4.1-mini
on the AI page and both tools run on it from their next call.
Thanks for telling us.
Thanks for telling us.
Last updated