@raindrop-ai/openrouter-agent
v0.1.0
Published
Raindrop integration for the OpenRouter Agent SDK
Keywords
Readme
@raindrop-ai/openrouter-agent
Raindrop integration for the OpenRouter Agent SDK (@openrouter/agent).
Installation
pnpm add @raindrop-ai/openrouter-agent @openrouter/agentUsage
import { OpenRouter, tool } from "@openrouter/agent";
import { z } from "zod";
import { createRaindropOpenRouterAgent } from "@raindrop-ai/openrouter-agent";
const raindrop = createRaindropOpenRouterAgent({
writeKey: process.env.RAINDROP_WRITE_KEY,
userId: "user-123",
convoId: "convo-456",
});
const openrouter = raindrop.wrap(
new OpenRouter({ apiKey: process.env.OPENROUTER_API_KEY }),
);
const getWeather = tool({
name: "get_weather",
description: "Get the weather for a city.",
inputSchema: z.object({ city: z.string() }),
outputSchema: z.object({ forecast: z.string() }),
execute: async ({ city }) => ({ forecast: `Sunny in ${city}` }),
});
const result = openrouter.callModel({
model: "openai/gpt-4o-mini",
input: "Use get_weather for San Francisco, then answer briefly.",
tools: [getWeather] as const,
});
console.log(await result.getText());
if (raindrop.lastEventId) {
await raindrop.signals.track({
eventId: raindrop.lastEventId,
name: "thumbs_up",
type: "feedback",
sentiment: "POSITIVE",
});
}
await raindrop.shutdown();Standalone callModel
If you import the standalone callModel helper, wrap the function once and use
the wrapped function in place of the original:
import { OpenRouter, callModel } from "@openrouter/agent";
import { createRaindropOpenRouterAgent } from "@raindrop-ai/openrouter-agent";
const raindrop = createRaindropOpenRouterAgent({
writeKey: process.env.RAINDROP_WRITE_KEY,
userId: "user-123",
});
const trackedCallModel = raindrop.wrapCallModel(callModel);
const openrouter = new OpenRouter({ apiKey: process.env.OPENROUTER_API_KEY });
const result = trackedCallModel(openrouter, {
model: "openai/gpt-4o-mini",
input: "Hello from OpenRouter",
});
await result.getText();
await raindrop.shutdown();What Gets Captured
- Input text from the last user message, without role prefixes
- Output text from
getText(),getResponse(), and stream consumers - Model name
- Prompt, completion, cached, and total token usage when OpenRouter returns it
- OpenRouter response status and response ID
- Client tool calls as
ai.toolCalltrace spans linked to the event - Conversation grouping via
convoId - Manual
events,users.identify,signals.track, andtracesAPIs
Options
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| writeKey | string | undefined | Optional Raindrop write key. If omitted, telemetry is disabled gracefully. |
| endpoint | string | https://api.raindrop.ai/v1/ | Override the Raindrop ingest endpoint. |
| debug | boolean | false | Enable debug logging for event and trace shippers. |
| userId | string | undefined | Default user ID attached to captured events. |
| convoId | string | undefined | Default conversation ID attached to captured events. |
| localWorkshopUrl | string \| false \| null | auto | Force or disable local Workshop mirroring. |
| events.enabled | boolean | true | Enable or disable event shipping. |
| events.partialFlushMs | number | 1000 | Partial event flush interval. |
| traces.enabled | boolean | true | Enable or disable trace shipping. |
| traces.flushIntervalMs | number | 1000 | Trace batch flush interval. |
| traces.maxBatchSize | number | 50 | Trace batch size. |
| traces.maxQueueSize | number | 5000 | Maximum queued spans. |
| traces.debugSpans | boolean | false | Log span parent relationships. |
Known Limitations
- Telemetry starts when a
ModelResultis consumed. Creating a result without callinggetText(),getResponse(), or a stream method does not ship an event. - Client tool spans wrap the tool's
execute()/onToolCalled()function, so their timing reflects the local tool execution window. - Server-executed OpenRouter tools are represented from response items when available after the provider response completes. Client function tools have the richest live trace data.
- Stream consumers that stop early still finalize a best-effort event with the text observed so far.
Testing
pnpm testReal e2e tests require OpenRouter and Raindrop credentials:
RAINDROP_DASHBOARD_TOKEN=eyJ... \
RAINDROP_WRITE_KEY=... \
OPENROUTER_API_KEY=... \
pnpm test -- tests/e2e.test.ts