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

@agentiny/gemini

v0.2.0

Published

Google Gemini adapter for @agentiny/core

Readme

@agentiny/gemini

Google Gemini adapter for @agentiny/core. Turns a state-in / state-out generateContent call into an agent ActionFn.

Migration from 0.1.x

0.2.0 is a breaking change:

  • The peer dependency is @google/genai. @google/generative-ai is deprecated.
  • createGeminiAction(config, options) is now a single options object.
  • onResponse receives a result object. Use result.text for the previous string.
  • The client is created once and reused. Pass client to share one GoogleGenAI instance.
  • The default model is still the gemini-flash-latest alias.
// 0.1.x
createGeminiAction(
  { apiKey: process.env.GOOGLE_API_KEY! },
  {
    prompt: (state) => state.input,
    onResponse: (text, state) => {
      state.output = text;
    },
  },
);

// 0.2.0
createGeminiAction({
  apiKey: process.env.GOOGLE_API_KEY,
  prompt: (state) => state.input,
  onResponse: (result, state) => {
    state.output = result.text;
  },
});

Installation

npm install @agentiny/core @agentiny/gemini @google/genai

Requirements

  • Node.js 20+ (22+ when @google/genai reaches 3.x, which this package does not peer yet)
  • @agentiny/core (peer)
  • @google/genai >=1.9.0 <3 (peer, install separately)
  • zod ^3.25 || ^4.0 (optional peer, only if you pass a Zod schema)

Other adapters

Quick start

import { createGeminiAction } from '@agentiny/gemini';
import { Agent } from '@agentiny/core';

interface AnalysisState {
  data: string;
  analysis?: string;
}

const agent = new Agent<AnalysisState>({
  initialState: { data: '' },
});

const analyzeAction = createGeminiAction({
  apiKey: process.env.GOOGLE_API_KEY,
  prompt: (state) => `Analyze this: ${state.data}`,
  onResponse: (result, state) => {
    state.analysis = result.text;
  },
});

agent.addTrigger({
  id: 'analyze-trigger',
  check: (state) => !!state.data && !state.analysis,
  actions: [analyzeAction],
  repeat: false,
});

await agent.start();
agent.setState({ data: 'What is Gemini?' });

API

createGeminiAction<TState, TParsed>(options)

Creates an ActionFn<TState> that calls Gemini models.generateContent.

The client is created once in the factory (or taken from options.client) and reused. When the agent passes an ActionContext, its abort signal is forwarded as config.abortSignal.

Client

  • client (optional) — Existing GoogleGenAI instance or compatible client. When set, apiKey and baseURL are ignored.
  • apiKey (optional) — Used when client is omitted. The SDK also reads GOOGLE_API_KEY, then GEMINI_API_KEY. If both are set, the SDK uses GOOGLE_API_KEY.
  • baseURL (optional) — Custom endpoint. Sent as httpOptions.baseUrl.
  • model (optional) — Default: gemini-flash-latest.

Prompt (exactly one of prompt or messages)

  • prompt — (state) => string turned into a single user content.
  • messages — (state) => GeminiMessage[] for multi-turn history. Roles are user, model, or assistant. assistant is sent as model.
  • system — string or (state) => string. Sent as config.systemInstruction, not as a message.

Result

  • onResponse — (result, state) => void | Promise<void>. Always receives:

    | Field | Meaning | | -------------- | ------------------------------------------------------------------------- | | text | Concatenated text parts. Thought parts are skipped. Empty string if none. | | data | text when no schema; parsed value when schema is set | | usage | { promptTokens, completionTokens, totalTokens } from usageMetadata | | finishReason | API finishReason (STOP, MAX_TOKENS, …), or null | | model | modelVersion from the API, or the requested model | | raw | The response, or the last stream chunk |

  • onDelta — (delta, state) => void | Promise<void>. Uses models.generateContentStream. Each non-empty text chunk is emitted; onResponse still runs once at the end.

Generation

  • maxTokens — Sent as maxOutputTokens only when set. Gemini does not require it.
  • temperature — Optional passthrough.
  • schema — JSON Schema object, or a Zod-like object with safeParse. Enables structured output; result.data is the parsed value.
  • schemaName — Kept for adapter API parity. responseJsonSchema has no name field, so this value is not sent.

Examples

Injected client

import { GoogleGenAI } from '@google/genai';
import { createGeminiAction } from '@agentiny/gemini';

const client = new GoogleGenAI({});

const action = createGeminiAction({
  client,
  prompt: (state) => state.input,
  onResponse: (result, state) => {
    state.output = result.text;
  },
});

System prompt and message history

History uses user and model. assistant is accepted and sent as model. The system prompt is a separate field:

const reply = createGeminiAction({
  client,
  system: 'You are terse.',
  messages: (state) => state.history,
  onResponse: (result, state) => {
    state.history.push({ role: 'model', content: result.text });
  },
});

Temperature and max tokens

const creative = createGeminiAction({
  apiKey: process.env.GOOGLE_API_KEY,
  model: 'gemini-2.5-pro',
  prompt: (state) => `Write a short story about: ${state.topic}`,
  temperature: 1,
  maxTokens: 500,
  onResponse: (result, state) => {
    state.story = result.text;
  },
});

On thinking models, maxOutputTokens counts thoughts as well as visible text. A very small cap can come back with an empty result.text.

Structured output

JSON Schema (sent as responseMimeType: 'application/json' and responseJsonSchema):

const extract = createGeminiAction({
  prompt: (state) => state.email,
  schema: {
    type: 'object',
    properties: {
      tasks: { type: 'array', items: { type: 'string' } },
    },
    required: ['tasks'],
  },
  onResponse: (result, state) => {
    state.tasks = result.data.tasks;
  },
});

Zod (optional peer — install zod yourself). Zod 4's toJSONSchema is sent as responseJsonSchema. Older Zod still requests JSON and validates with safeParse.

import { z } from 'zod';

const TaskList = z.object({
  tasks: z.array(z.string()),
});

const extract = createGeminiAction({
  prompt: (state) => state.email,
  schema: TaskList,
  onResponse: (result, state) => {
    state.tasks = result.data.tasks;
  },
});

Streaming into state

const stream = createGeminiAction({
  prompt: (state) => state.prompt,
  onDelta: (delta, state) => {
    state.draft += delta;
  },
  onResponse: (result, state) => {
    state.draft = result.text;
  },
});

onDelta plus schema is allowed: deltas are the raw text (usually JSON), and result.data is parsed after the stream ends.

Chained triggers

interface ProcessState {
  text: string;
  summary?: string;
  sentiment?: string;
}

const agent = new Agent<ProcessState>({
  initialState: { text: 'Your text here' },
});

const client = new GoogleGenAI({});

const summarize = createGeminiAction({
  client,
  prompt: (state) => `Summarize: ${state.text}`,
  onResponse: (result, state) => {
    state.summary = result.text;
  },
});

const analyzeSentiment = createGeminiAction({
  client,
  prompt: (state) => `Analyze sentiment of: ${state.summary}`,
  onResponse: (result, state) => {
    state.sentiment = result.text;
  },
});

agent.addTrigger({
  id: 'summarize-trigger',
  check: (state) => !!state.text && !state.summary,
  actions: [summarize],
});

agent.addTrigger({
  id: 'analyze-trigger',
  check: (state) => !!state.summary && !state.sentiment,
  actions: [analyzeSentiment],
});

Prefer one shared client when several actions hit the same account.

Custom endpoint

const custom = createGeminiAction({
  apiKey: process.env.GEMINI_API_KEY,
  baseURL: 'https://your-gemini-compatible-endpoint.example',
  prompt: (state) => `Process: ${state.data}`,
  onResponse: (result, state) => {
    state.result = result.text;
  },
});

Errors and cancellation

API errors and abort errors propagate to the agent's onError. Long-running calls stop when the agent is paused or stopped because ctx.signal is forwarded.

const agent = new Agent<TextState>({
  initialState: { input: '' },
  onError: (error) => {
    console.error('Agent error:', error.message);
  },
});

For retries, wrap the action with @agentiny/utils withRetry.

Best practices

  1. Keep API keys in the environment; pass a shared client when you have more than one action.
  2. Use a current model alias (gemini-flash-latest, or a newer flash/pro model) unless you need a pinned snapshot.
  3. Set maxTokens when you want a cap. Leave it unset when you want the model's default.
  4. Handle failures with the agent's onError.
  5. Use schema when the next trigger needs structured state, not a blob of prose.

Troubleshooting

"API key is missing or invalid"

Set GOOGLE_API_KEY or GEMINI_API_KEY, pass apiKey, or inject a client that already has credentials.

"Cannot find module '@google/genai'"

@google/genai is a peer dependency: npm install @google/genai. This package does not use @google/generative-ai.

"Model not found"

The default is gemini-flash-latest. Check that the name exists for your key, or set model.

Empty text with a small maxTokens

Thinking models can spend the output budget on thoughts. Raise maxTokens, or pick a model that is not a thinking model.

Rate limit exceeded

Wrap the action with withRetry from @agentiny/utils, or space calls out with a trigger delay.

License

MIT

See also