@rouvanpm/rouva 0.2.11 → 0.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -190,9 +190,94 @@ const rouva = new Rouva({
190
190
  })
191
191
  ```
192
192
 
193
+ ## Agentic runs — `rouva.run()`
194
+
195
+ `rouva.run()` hands the entire agent loop to the Rouva gateway. You define your tools and handler URLs; Rouva calls the model, dispatches tool calls in parallel to your endpoints, and loops until the model stops or a limit is reached — no client-side loop, no streaming required.
196
+
197
+ ```typescript
198
+ const result = await rouva.run({
199
+ model: 'claude-haiku-4-5-20251001',
200
+ messages: [{ role: 'user', content: 'What is the weather in Paris and London?' }],
201
+ tools: [
202
+ {
203
+ name: 'get_weather',
204
+ description: 'Get current weather for a city',
205
+ input_schema: {
206
+ type: 'object',
207
+ properties: { city: { type: 'string' } },
208
+ required: ['city'],
209
+ },
210
+ },
211
+ ],
212
+ tool_handlers: {
213
+ get_weather: 'https://api.example.com/weather', // gateway POSTs tool input here
214
+ },
215
+ max_turns: 5,
216
+ session_budget_usd: 0.10, // optional hard cost cap
217
+ })
218
+
219
+ console.log(result.content) // final model response
220
+ console.log(result.turns) // number of turns taken
221
+ console.log(result.tool_calls_made) // total tool calls dispatched
222
+ console.log(result.session_id) // dashboard session ID
223
+ console.log(result.session_cost_usd) // total USD spent
224
+ ```
225
+
226
+ ### How it works
227
+
228
+ 1. The gateway sends your `messages` to the model (non-streaming)
229
+ 2. When the model returns tool calls, all handlers are dispatched **in parallel** via POST — the request body is the tool's JSON input, the response body is used as the tool result
230
+ 3. Results are appended and the loop continues
231
+ 4. The loop exits when `finish_reason` is `"stop"` / `"end_turn"`, `max_turns` is reached, or `session_budget_usd` is exceeded
232
+
233
+ Every turn is logged as a usage snapshot in the Rouva dashboard under the shared `session_id`, giving you a full session replay with per-turn cost, model used, tool calls, and quality score.
234
+
235
+ ### Handler URL requirements
236
+
237
+ - Must be a public HTTPS URL (private/local addresses are rejected)
238
+ - The gateway sends a `POST` with `Content-Type: application/json` and the tool's input object as the body
239
+ - The response body (text or JSON) is returned to the model as the tool result
240
+
241
+ ### Parameters
242
+
243
+ | Parameter | Type | Required | Description |
244
+ |---|---|---|---|
245
+ | `messages` | `ChatMessage[]` | ✓ | Conversation seed — usually a single user message |
246
+ | `tools` | `object[]` | ✓ | Tool definitions (OpenAI or Anthropic format) |
247
+ | `tool_handlers` | `Record<string, string>` | ✓ | Tool name → handler URL mapping |
248
+ | `model` | `RouvaModel` | recommended | Model to use — required for Anthropic tools |
249
+ | `provider` | `RouvaProvider` | | Force a specific provider |
250
+ | `max_turns` | `number` | | Max loop iterations (default: 10) |
251
+ | `max_tokens` | `number` | | Max tokens per turn (default: 4096) |
252
+ | `temperature` | `number` | | Sampling temperature 0–1 |
253
+ | `session_budget_usd` | `number` | | Hard USD cap across all turns |
254
+
255
+ ### Result shape
256
+
257
+ ```typescript
258
+ interface RunResult {
259
+ content: string | null // final model response text
260
+ turns: number // turns executed
261
+ tool_calls_made: number // total tool calls dispatched
262
+ session_id: string // groups all turns in the dashboard
263
+ finish_reason: string | null // "stop", "end_turn", "max_turns", "budget_exceeded", "length"
264
+ cost_usd: number // cost of the last turn
265
+ session_cost_usd: number // cumulative cost across all turns
266
+ }
267
+ ```
268
+
269
+ ### When to use `run()` vs `chat.completions.create()`
270
+
271
+ | | `rouva.run()` | `chat.completions.create()` |
272
+ |---|---|---|
273
+ | Tool loop | Gateway-managed | Client-managed |
274
+ | Session grouping | Automatic | Manual (`startSession`) |
275
+ | Streaming | No | Yes |
276
+ | Use when | Fully delegating an agentic task | Building your own loop or need streaming |
277
+
193
278
  ## Agent session tracking
194
279
 
195
- Group all turns of an agent run under a single session ID so the Rouva dashboard can show per-session cost, token usage, and quality.
280
+ Group all turns of an agent run under a single session ID so the Rouva dashboard can show per-session cost, token usage, and quality. Use this with `chat.completions.create()` when you manage the loop yourself.
196
281
 
197
282
  ```typescript
198
283
  const rouva = new Rouva({ apiKey: 'rva_...' })
package/dist/index.d.mts CHANGED
@@ -164,6 +164,80 @@ interface RouvaResponseMeta {
164
164
  /** Semantic cache status when exposed by the gateway */
165
165
  cache?: string;
166
166
  }
167
+ interface RunParams {
168
+ /**
169
+ * Conversation history to seed the agent loop with.
170
+ * Typically a single user message, but may include prior turns for
171
+ * multi-shot agents.
172
+ */
173
+ messages: ChatMessage[];
174
+ /**
175
+ * Tool definitions to expose to the model. Accepts both OpenAI-style
176
+ * `{ type: "function", function: { name, description, parameters } }` and
177
+ * Anthropic-style `{ name, description, input_schema }` objects.
178
+ * At least one tool is required.
179
+ */
180
+ tools: Array<Record<string, unknown>>;
181
+ /**
182
+ * Map of tool name → handler URL. When the model calls a tool, the gateway
183
+ * POSTs the tool input as JSON to the corresponding URL and feeds the
184
+ * response body back as the tool result. All handlers must be public HTTPS
185
+ * endpoints.
186
+ *
187
+ * @example
188
+ * { search: 'https://api.example.com/search', calculator: 'https://api.example.com/calc' }
189
+ */
190
+ tool_handlers: Record<string, string>;
191
+ /**
192
+ * Target model. Must be specified for tool calls — the gateway never
193
+ * re-routes tool requests across providers since tool schemas are
194
+ * provider-specific.
195
+ */
196
+ model?: RouvaModel;
197
+ /**
198
+ * Force a specific provider when paired with `model`.
199
+ */
200
+ provider?: RouvaProvider;
201
+ /**
202
+ * Maximum number of agentic turns before the loop exits.
203
+ * Defaults to 10. Each turn = one model call (plus parallel tool dispatch).
204
+ */
205
+ max_turns?: number;
206
+ /**
207
+ * Maximum tokens the model may generate per turn.
208
+ * Defaults to 4096.
209
+ */
210
+ max_tokens?: number;
211
+ /**
212
+ * Sampling temperature 0–1.
213
+ */
214
+ temperature?: number;
215
+ /**
216
+ * Hard cap on total session spend in USD. The loop exits cleanly when the
217
+ * cumulative cost of all turns reaches this threshold.
218
+ */
219
+ session_budget_usd?: number;
220
+ }
221
+ interface RunResult {
222
+ /** Final model response text after all tool loops complete. */
223
+ content: string | null;
224
+ /** Number of agentic turns executed. */
225
+ turns: number;
226
+ /** Total number of tool calls dispatched across all turns. */
227
+ tool_calls_made: number;
228
+ /** Session ID — all turns are grouped under this ID in the Rouva dashboard. */
229
+ session_id: string;
230
+ /**
231
+ * Why the loop exited: `"stop"` (model finished), `"max_turns"` (turn limit
232
+ * reached), `"budget_exceeded"` (session_budget_usd hit), or a provider
233
+ * finish reason such as `"end_turn"` or `"length"`.
234
+ */
235
+ finish_reason: string | null;
236
+ /** USD cost of the final turn. */
237
+ cost_usd: number;
238
+ /** Cumulative USD cost of all turns in the session. */
239
+ session_cost_usd: number;
240
+ }
167
241
 
168
242
  declare class Rouva {
169
243
  private apiKey;
@@ -189,7 +263,29 @@ declare class Rouva {
189
263
  endSession(): void;
190
264
  /** The active session ID, or undefined if no session is in progress. */
191
265
  get sessionId(): string | undefined;
266
+ /**
267
+ * Run a server-side agentic loop. The gateway calls the model, dispatches
268
+ * tool calls in parallel to your handler URLs, and loops until the model
269
+ * stops or limits are reached — no streaming, no client-side loop required.
270
+ *
271
+ * Every turn is logged as a usage snapshot grouped by `session_id` in the
272
+ * Rouva dashboard, giving you a full session replay with per-turn cost,
273
+ * model, and tool call details.
274
+ *
275
+ * @example
276
+ * const result = await rouva.run({
277
+ * model: 'claude-haiku-4-5-20251001',
278
+ * messages: [{ role: 'user', content: 'What is the weather in Paris?' }],
279
+ * tools: [{ name: 'get_weather', description: '...', input_schema: { ... } }],
280
+ * tool_handlers: { get_weather: 'https://api.example.com/weather' },
281
+ * max_turns: 5,
282
+ * })
283
+ * console.log(result.content) // final model response
284
+ * console.log(result.turns) // how many turns it took
285
+ * console.log(result.session_id) // dashboard session ID
286
+ */
287
+ run(params: RunParams): Promise<RunResult>;
192
288
  private _createChatCompletion;
193
289
  }
194
290
 
195
- export { type AssistantToolCallMessage, type ChatCompletion, type ChatCompletionChoice, type ChatCompletionParams, type ChatCompletionUsage, type ChatMessage, type ContentBlockMessage, type FunctionToolCall, type Message, type ResponseMessage, Rouva, type RouvaOptions, type RouvaProvider, type RouvaResponseMeta, type SystemTextBlock, type ToolResultMessage };
291
+ export { type AssistantToolCallMessage, type ChatCompletion, type ChatCompletionChoice, type ChatCompletionParams, type ChatCompletionUsage, type ChatMessage, type ContentBlockMessage, type FunctionToolCall, type Message, type ResponseMessage, Rouva, type RouvaOptions, type RouvaProvider, type RouvaResponseMeta, type RunParams, type RunResult, type SystemTextBlock, type ToolResultMessage };
package/dist/index.d.ts CHANGED
@@ -164,6 +164,80 @@ interface RouvaResponseMeta {
164
164
  /** Semantic cache status when exposed by the gateway */
165
165
  cache?: string;
166
166
  }
167
+ interface RunParams {
168
+ /**
169
+ * Conversation history to seed the agent loop with.
170
+ * Typically a single user message, but may include prior turns for
171
+ * multi-shot agents.
172
+ */
173
+ messages: ChatMessage[];
174
+ /**
175
+ * Tool definitions to expose to the model. Accepts both OpenAI-style
176
+ * `{ type: "function", function: { name, description, parameters } }` and
177
+ * Anthropic-style `{ name, description, input_schema }` objects.
178
+ * At least one tool is required.
179
+ */
180
+ tools: Array<Record<string, unknown>>;
181
+ /**
182
+ * Map of tool name → handler URL. When the model calls a tool, the gateway
183
+ * POSTs the tool input as JSON to the corresponding URL and feeds the
184
+ * response body back as the tool result. All handlers must be public HTTPS
185
+ * endpoints.
186
+ *
187
+ * @example
188
+ * { search: 'https://api.example.com/search', calculator: 'https://api.example.com/calc' }
189
+ */
190
+ tool_handlers: Record<string, string>;
191
+ /**
192
+ * Target model. Must be specified for tool calls — the gateway never
193
+ * re-routes tool requests across providers since tool schemas are
194
+ * provider-specific.
195
+ */
196
+ model?: RouvaModel;
197
+ /**
198
+ * Force a specific provider when paired with `model`.
199
+ */
200
+ provider?: RouvaProvider;
201
+ /**
202
+ * Maximum number of agentic turns before the loop exits.
203
+ * Defaults to 10. Each turn = one model call (plus parallel tool dispatch).
204
+ */
205
+ max_turns?: number;
206
+ /**
207
+ * Maximum tokens the model may generate per turn.
208
+ * Defaults to 4096.
209
+ */
210
+ max_tokens?: number;
211
+ /**
212
+ * Sampling temperature 0–1.
213
+ */
214
+ temperature?: number;
215
+ /**
216
+ * Hard cap on total session spend in USD. The loop exits cleanly when the
217
+ * cumulative cost of all turns reaches this threshold.
218
+ */
219
+ session_budget_usd?: number;
220
+ }
221
+ interface RunResult {
222
+ /** Final model response text after all tool loops complete. */
223
+ content: string | null;
224
+ /** Number of agentic turns executed. */
225
+ turns: number;
226
+ /** Total number of tool calls dispatched across all turns. */
227
+ tool_calls_made: number;
228
+ /** Session ID — all turns are grouped under this ID in the Rouva dashboard. */
229
+ session_id: string;
230
+ /**
231
+ * Why the loop exited: `"stop"` (model finished), `"max_turns"` (turn limit
232
+ * reached), `"budget_exceeded"` (session_budget_usd hit), or a provider
233
+ * finish reason such as `"end_turn"` or `"length"`.
234
+ */
235
+ finish_reason: string | null;
236
+ /** USD cost of the final turn. */
237
+ cost_usd: number;
238
+ /** Cumulative USD cost of all turns in the session. */
239
+ session_cost_usd: number;
240
+ }
167
241
 
168
242
  declare class Rouva {
169
243
  private apiKey;
@@ -189,7 +263,29 @@ declare class Rouva {
189
263
  endSession(): void;
190
264
  /** The active session ID, or undefined if no session is in progress. */
191
265
  get sessionId(): string | undefined;
266
+ /**
267
+ * Run a server-side agentic loop. The gateway calls the model, dispatches
268
+ * tool calls in parallel to your handler URLs, and loops until the model
269
+ * stops or limits are reached — no streaming, no client-side loop required.
270
+ *
271
+ * Every turn is logged as a usage snapshot grouped by `session_id` in the
272
+ * Rouva dashboard, giving you a full session replay with per-turn cost,
273
+ * model, and tool call details.
274
+ *
275
+ * @example
276
+ * const result = await rouva.run({
277
+ * model: 'claude-haiku-4-5-20251001',
278
+ * messages: [{ role: 'user', content: 'What is the weather in Paris?' }],
279
+ * tools: [{ name: 'get_weather', description: '...', input_schema: { ... } }],
280
+ * tool_handlers: { get_weather: 'https://api.example.com/weather' },
281
+ * max_turns: 5,
282
+ * })
283
+ * console.log(result.content) // final model response
284
+ * console.log(result.turns) // how many turns it took
285
+ * console.log(result.session_id) // dashboard session ID
286
+ */
287
+ run(params: RunParams): Promise<RunResult>;
192
288
  private _createChatCompletion;
193
289
  }
194
290
 
195
- export { type AssistantToolCallMessage, type ChatCompletion, type ChatCompletionChoice, type ChatCompletionParams, type ChatCompletionUsage, type ChatMessage, type ContentBlockMessage, type FunctionToolCall, type Message, type ResponseMessage, Rouva, type RouvaOptions, type RouvaProvider, type RouvaResponseMeta, type SystemTextBlock, type ToolResultMessage };
291
+ export { type AssistantToolCallMessage, type ChatCompletion, type ChatCompletionChoice, type ChatCompletionParams, type ChatCompletionUsage, type ChatMessage, type ContentBlockMessage, type FunctionToolCall, type Message, type ResponseMessage, Rouva, type RouvaOptions, type RouvaProvider, type RouvaResponseMeta, type RunParams, type RunResult, type SystemTextBlock, type ToolResultMessage };
package/dist/index.js CHANGED
@@ -404,6 +404,43 @@ var Rouva = class {
404
404
  get sessionId() {
405
405
  return this._sessionId;
406
406
  }
407
+ /**
408
+ * Run a server-side agentic loop. The gateway calls the model, dispatches
409
+ * tool calls in parallel to your handler URLs, and loops until the model
410
+ * stops or limits are reached — no streaming, no client-side loop required.
411
+ *
412
+ * Every turn is logged as a usage snapshot grouped by `session_id` in the
413
+ * Rouva dashboard, giving you a full session replay with per-turn cost,
414
+ * model, and tool call details.
415
+ *
416
+ * @example
417
+ * const result = await rouva.run({
418
+ * model: 'claude-haiku-4-5-20251001',
419
+ * messages: [{ role: 'user', content: 'What is the weather in Paris?' }],
420
+ * tools: [{ name: 'get_weather', description: '...', input_schema: { ... } }],
421
+ * tool_handlers: { get_weather: 'https://api.example.com/weather' },
422
+ * max_turns: 5,
423
+ * })
424
+ * console.log(result.content) // final model response
425
+ * console.log(result.turns) // how many turns it took
426
+ * console.log(result.session_id) // dashboard session ID
427
+ */
428
+ async run(params) {
429
+ const url = `${this.baseURL}/api/gateway/run`;
430
+ const res = await fetch(url, {
431
+ method: "POST",
432
+ headers: {
433
+ "Content-Type": "application/json",
434
+ "Authorization": `Bearer ${this.apiKey}`
435
+ },
436
+ body: JSON.stringify(params)
437
+ });
438
+ if (!res.ok) {
439
+ const body = await res.text().catch(() => res.statusText);
440
+ throw new Error(`[Rouva] Run error ${res.status}: ${body}`);
441
+ }
442
+ return res.json();
443
+ }
407
444
  async _createChatCompletion(params) {
408
445
  const url = `${this.baseURL}/api/gateway/messages`;
409
446
  const { stream: _stream, ...body } = params;
package/dist/index.mjs CHANGED
@@ -378,6 +378,43 @@ var Rouva = class {
378
378
  get sessionId() {
379
379
  return this._sessionId;
380
380
  }
381
+ /**
382
+ * Run a server-side agentic loop. The gateway calls the model, dispatches
383
+ * tool calls in parallel to your handler URLs, and loops until the model
384
+ * stops or limits are reached — no streaming, no client-side loop required.
385
+ *
386
+ * Every turn is logged as a usage snapshot grouped by `session_id` in the
387
+ * Rouva dashboard, giving you a full session replay with per-turn cost,
388
+ * model, and tool call details.
389
+ *
390
+ * @example
391
+ * const result = await rouva.run({
392
+ * model: 'claude-haiku-4-5-20251001',
393
+ * messages: [{ role: 'user', content: 'What is the weather in Paris?' }],
394
+ * tools: [{ name: 'get_weather', description: '...', input_schema: { ... } }],
395
+ * tool_handlers: { get_weather: 'https://api.example.com/weather' },
396
+ * max_turns: 5,
397
+ * })
398
+ * console.log(result.content) // final model response
399
+ * console.log(result.turns) // how many turns it took
400
+ * console.log(result.session_id) // dashboard session ID
401
+ */
402
+ async run(params) {
403
+ const url = `${this.baseURL}/api/gateway/run`;
404
+ const res = await fetch(url, {
405
+ method: "POST",
406
+ headers: {
407
+ "Content-Type": "application/json",
408
+ "Authorization": `Bearer ${this.apiKey}`
409
+ },
410
+ body: JSON.stringify(params)
411
+ });
412
+ if (!res.ok) {
413
+ const body = await res.text().catch(() => res.statusText);
414
+ throw new Error(`[Rouva] Run error ${res.status}: ${body}`);
415
+ }
416
+ return res.json();
417
+ }
381
418
  async _createChatCompletion(params) {
382
419
  const url = `${this.baseURL}/api/gateway/messages`;
383
420
  const { stream: _stream, ...body } = params;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rouvanpm/rouva",
3
- "version": "0.2.11",
3
+ "version": "0.2.12",
4
4
  "description": "Official Node.js SDK for Rouva — managed AI gateway with intelligent routing and spend tracking",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",