@openrouter/sdk 0.4.0 → 0.5.1

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.
@@ -1,3 +1,4 @@
1
+ import type { Hook } from "../hooks/types.js";
1
2
  import { HTTPClient } from "./http.js";
2
3
  import { Logger } from "./logger.js";
3
4
  import { RetryConfig } from "./retries.js";
@@ -40,13 +41,18 @@ export type SDKOptions = {
40
41
  retryConfig?: RetryConfig;
41
42
  timeoutMs?: number;
42
43
  debugLogger?: Logger;
44
+ /**
45
+ * Hooks for request/response lifecycle events.
46
+ * Can be a single hook object or an array of hooks.
47
+ */
48
+ hooks?: Hook | Hook[];
43
49
  };
44
50
  export declare function serverURLFromOptions(options: SDKOptions): URL | null;
45
51
  export declare const SDK_METADATA: {
46
52
  readonly language: "typescript";
47
53
  readonly openapiDocVersion: "1.0.0";
48
- readonly sdkVersion: "0.4.0";
54
+ readonly sdkVersion: "0.5.1";
49
55
  readonly genVersion: "2.788.4";
50
- readonly userAgent: "speakeasy-sdk/typescript 0.4.0 2.788.4 1.0.0 @openrouter/sdk";
56
+ readonly userAgent: "speakeasy-sdk/typescript 0.5.1 2.788.4 1.0.0 @openrouter/sdk";
51
57
  };
52
58
  //# sourceMappingURL=config.d.ts.map
package/esm/lib/config.js CHANGED
@@ -26,8 +26,8 @@ export function serverURLFromOptions(options) {
26
26
  export const SDK_METADATA = {
27
27
  language: "typescript",
28
28
  openapiDocVersion: "1.0.0",
29
- sdkVersion: "0.4.0",
29
+ sdkVersion: "0.5.1",
30
30
  genVersion: "2.788.4",
31
- userAgent: "speakeasy-sdk/typescript 0.4.0 2.788.4 1.0.0 @openrouter/sdk",
31
+ userAgent: "speakeasy-sdk/typescript 0.5.1 2.788.4 1.0.0 @openrouter/sdk",
32
32
  };
33
33
  //# sourceMappingURL=config.js.map
@@ -267,10 +267,11 @@ export declare class ModelResult<TTools extends readonly Tool[]> {
267
267
  *
268
268
  * Stream incremental message updates as content is added in responses format.
269
269
  * Each iteration yields an updated version of the message with new content.
270
- * Also yields OpenResponsesFunctionCallOutput after tool execution completes.
271
- * Returns ResponsesOutputMessage or OpenResponsesFunctionCallOutput compatible with OpenAI Responses API format.
270
+ * Also yields function_call items and OpenResponsesFunctionCallOutput after tool execution completes.
271
+ * Returns ResponsesOutputMessage, ResponsesOutputItemFunctionCall, or OpenResponsesFunctionCallOutput
272
+ * compatible with OpenAI Responses API format.
272
273
  */
273
- getNewMessagesStream(): AsyncIterableIterator<models.ResponsesOutputMessage | models.OpenResponsesFunctionCallOutput>;
274
+ getNewMessagesStream(): AsyncIterableIterator<models.ResponsesOutputMessage | models.OpenResponsesFunctionCallOutput | models.ResponsesOutputItemFunctionCall>;
274
275
  /**
275
276
  * Stream only reasoning deltas as they arrive.
276
277
  * This filters the full event stream to only yield reasoning content.
@@ -8,7 +8,7 @@ import { executeTool } from './tool-executor.js';
8
8
  import { executeNextTurnParamsFunctions, applyNextTurnParamsToRequest } from './next-turn-params.js';
9
9
  import { hasExecuteFunction } from './tool-types.js';
10
10
  import { isStopConditionMet, stepCountIs } from './stop-conditions.js';
11
- import { isOutputMessage, isFunctionCallOutputItem, isReasoningOutputItem, isWebSearchCallOutputItem, isFileSearchCallOutputItem, isImageGenerationCallOutputItem, hasTypeProperty, } from './stream-type-guards.js';
11
+ import { isOutputMessage, isFunctionCallItem, isReasoningOutputItem, isWebSearchCallOutputItem, isFileSearchCallOutputItem, isImageGenerationCallOutputItem, hasTypeProperty, } from './stream-type-guards.js';
12
12
  /**
13
13
  * Default maximum number of tool execution steps if no stopWhen is specified.
14
14
  * This prevents infinite loops in tool execution.
@@ -305,6 +305,32 @@ export class ModelResult {
305
305
  const tool = this.options.tools?.find((t) => t.function.name === toolCall.name);
306
306
  if (!tool || !hasExecuteFunction(tool))
307
307
  continue;
308
+ // Check if arguments failed to parse (remained as string instead of object)
309
+ // This happens when the model returns invalid JSON for tool call arguments
310
+ // We use 'unknown' cast because the type system doesn't know arguments can be a string
311
+ // when JSON parsing fails in stream-transformers.ts
312
+ const args = toolCall.arguments;
313
+ if (typeof args === 'string') {
314
+ const rawArgs = args;
315
+ const errorMessage = `Failed to parse tool call arguments for "${toolCall.name}": The model provided invalid JSON. ` +
316
+ `Raw arguments received: "${rawArgs}". ` +
317
+ `Please provide valid JSON arguments for this tool call.`;
318
+ // Emit error event if broadcaster exists
319
+ if (this.toolEventBroadcaster) {
320
+ this.toolEventBroadcaster.push({
321
+ type: 'tool_result',
322
+ toolCallId: toolCall.id,
323
+ result: { error: errorMessage },
324
+ });
325
+ }
326
+ toolResults.push({
327
+ type: 'function_call_output',
328
+ id: `output_${toolCall.id}`,
329
+ callId: toolCall.id,
330
+ output: JSON.stringify({ error: errorMessage }),
331
+ });
332
+ continue;
333
+ }
308
334
  // Track preliminary results for this specific tool call
309
335
  const preliminaryResultsForCall = [];
310
336
  // Create callback for real-time preliminary results
@@ -352,7 +378,13 @@ export class ModelResult {
352
378
  async resolveAsyncFunctionsForTurn(turnContext) {
353
379
  if (hasAsyncFunctions(this.options.request)) {
354
380
  const resolved = await resolveAsyncFunctions(this.options.request, turnContext);
355
- this.resolvedRequest = { ...resolved, stream: false };
381
+ // Preserve accumulated input from previous turns
382
+ const preservedInput = this.resolvedRequest?.input;
383
+ this.resolvedRequest = {
384
+ ...resolved,
385
+ stream: false,
386
+ ...(preservedInput !== undefined && { input: preservedInput }),
387
+ };
356
388
  }
357
389
  }
358
390
  /**
@@ -379,8 +411,15 @@ export class ModelResult {
379
411
  * @returns The new response from the API
380
412
  */
381
413
  async makeFollowupRequest(currentResponse, toolResults) {
382
- // Build new input with tool results
414
+ // Build new input preserving original conversation + tool results
415
+ const originalInput = this.resolvedRequest?.input;
416
+ const normalizedOriginalInput = Array.isArray(originalInput)
417
+ ? originalInput
418
+ : originalInput
419
+ ? [{ role: 'user', content: originalInput }]
420
+ : [];
383
421
  const newInput = [
422
+ ...normalizedOriginalInput,
384
423
  ...(Array.isArray(currentResponse.output)
385
424
  ? currentResponse.output
386
425
  : [currentResponse.output]),
@@ -389,9 +428,13 @@ export class ModelResult {
389
428
  if (!this.resolvedRequest) {
390
429
  throw new Error('Request not initialized');
391
430
  }
392
- const newRequest = {
431
+ // Update resolvedRequest.input with accumulated conversation for next turn
432
+ this.resolvedRequest = {
393
433
  ...this.resolvedRequest,
394
434
  input: newInput,
435
+ };
436
+ const newRequest = {
437
+ ...this.resolvedRequest,
395
438
  stream: false,
396
439
  };
397
440
  const newResult = await betaResponsesSend(this.options.client, newRequest, this.options.options);
@@ -890,8 +933,16 @@ export class ModelResult {
890
933
  yield* buildItemsStream(this.reusableStream);
891
934
  // Execute tools if needed
892
935
  await this.executeToolsIfNeeded();
893
- // Yield function call outputs for each executed tool
936
+ // Yield function calls and outputs for each tool round
894
937
  for (const round of this.allToolExecutionRounds) {
938
+ // Round 0's function_calls already yielded via buildItemsStream
939
+ if (round.round > 0) {
940
+ for (const item of round.response.output) {
941
+ if (isFunctionCallItem(item)) {
942
+ yield item;
943
+ }
944
+ }
945
+ }
895
946
  for (const toolResult of round.toolResults) {
896
947
  yield toolResult;
897
948
  }
@@ -900,7 +951,7 @@ export class ModelResult {
900
951
  if (this.finalResponse && this.allToolExecutionRounds.length > 0) {
901
952
  for (const item of this.finalResponse.output) {
902
953
  if (isOutputMessage(item) ||
903
- isFunctionCallOutputItem(item) ||
954
+ isFunctionCallItem(item) ||
904
955
  isReasoningOutputItem(item) ||
905
956
  isWebSearchCallOutputItem(item) ||
906
957
  isFileSearchCallOutputItem(item) ||
@@ -918,8 +969,9 @@ export class ModelResult {
918
969
  *
919
970
  * Stream incremental message updates as content is added in responses format.
920
971
  * Each iteration yields an updated version of the message with new content.
921
- * Also yields OpenResponsesFunctionCallOutput after tool execution completes.
922
- * Returns ResponsesOutputMessage or OpenResponsesFunctionCallOutput compatible with OpenAI Responses API format.
972
+ * Also yields function_call items and OpenResponsesFunctionCallOutput after tool execution completes.
973
+ * Returns ResponsesOutputMessage, ResponsesOutputItemFunctionCall, or OpenResponsesFunctionCallOutput
974
+ * compatible with OpenAI Responses API format.
923
975
  */
924
976
  getNewMessagesStream() {
925
977
  return async function* () {
@@ -931,8 +983,15 @@ export class ModelResult {
931
983
  yield* buildResponsesMessageStream(this.reusableStream);
932
984
  // Execute tools if needed
933
985
  await this.executeToolsIfNeeded();
934
- // Yield function call outputs for each executed tool
986
+ // Yield function calls and their outputs for each executed tool
935
987
  for (const round of this.allToolExecutionRounds) {
988
+ // First yield the function_call items from the response that triggered tool execution
989
+ for (const item of round.response.output) {
990
+ if (isFunctionCallItem(item)) {
991
+ yield item;
992
+ }
993
+ }
994
+ // Then yield the function_call_output results
936
995
  for (const toolResult of round.toolResults) {
937
996
  yield toolResult;
938
997
  }
package/esm/lib/sdks.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { SDKHooks } from "../hooks/hooks.js";
2
- import { HookContext } from "../hooks/types.js";
2
+ import type { HookContext } from "../hooks/types.js";
3
3
  import { ConnectionError, InvalidRequestError, RequestAbortedError, RequestTimeoutError, UnexpectedClientError } from "../models/errors/httpclienterrors.js";
4
4
  import { Result } from "../types/fp.js";
5
5
  import { SDKOptions } from "./config.js";
package/esm/lib/sdks.js CHANGED
@@ -13,7 +13,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
13
13
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
14
14
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
15
15
  };
16
- var _ClientSDK_httpClient, _ClientSDK_hooks, _ClientSDK_logger;
16
+ var _ClientSDK_instances, _ClientSDK_httpClient, _ClientSDK_hooks, _ClientSDK_logger, _ClientSDK_registerHook;
17
17
  import { SDKHooks } from "../hooks/hooks.js";
18
18
  import { ConnectionError, InvalidRequestError, RequestAbortedError, RequestTimeoutError, UnexpectedClientError, } from "../models/errors/httpclienterrors.js";
19
19
  import { ERR, OK } from "../types/fp.js";
@@ -33,18 +33,22 @@ const isBrowserLike = webWorkerLike
33
33
  || (typeof window === "object" && typeof window.document !== "undefined");
34
34
  export class ClientSDK {
35
35
  constructor(options = {}) {
36
+ _ClientSDK_instances.add(this);
36
37
  _ClientSDK_httpClient.set(this, void 0);
37
38
  _ClientSDK_hooks.set(this, void 0);
38
39
  _ClientSDK_logger.set(this, void 0);
39
- const opt = options;
40
- if (typeof opt === "object"
41
- && opt != null
42
- && "hooks" in opt
43
- && opt.hooks instanceof SDKHooks) {
44
- __classPrivateFieldSet(this, _ClientSDK_hooks, opt.hooks, "f");
40
+ // Reuse existing SDKHooks if passed (for sub-SDKs)
41
+ if (options.hooks instanceof SDKHooks) {
42
+ __classPrivateFieldSet(this, _ClientSDK_hooks, options.hooks, "f");
45
43
  }
46
44
  else {
47
45
  __classPrivateFieldSet(this, _ClientSDK_hooks, new SDKHooks(), "f");
46
+ if (options.hooks) {
47
+ const hooksArray = Array.isArray(options.hooks) ? options.hooks : [options.hooks];
48
+ for (const hook of hooksArray) {
49
+ __classPrivateFieldGet(this, _ClientSDK_instances, "m", _ClientSDK_registerHook).call(this, hook);
50
+ }
51
+ }
48
52
  }
49
53
  const defaultHttpClient = new HTTPClient();
50
54
  options.httpClient = options.httpClient || defaultHttpClient;
@@ -185,7 +189,23 @@ export class ClientSDK {
185
189
  });
186
190
  }
187
191
  }
188
- _ClientSDK_httpClient = new WeakMap(), _ClientSDK_hooks = new WeakMap(), _ClientSDK_logger = new WeakMap();
192
+ _ClientSDK_httpClient = new WeakMap(), _ClientSDK_hooks = new WeakMap(), _ClientSDK_logger = new WeakMap(), _ClientSDK_instances = new WeakSet(), _ClientSDK_registerHook = function _ClientSDK_registerHook(hook) {
193
+ if ("sdkInit" in hook) {
194
+ __classPrivateFieldGet(this, _ClientSDK_hooks, "f").registerSDKInitHook(hook);
195
+ }
196
+ if ("beforeCreateRequest" in hook) {
197
+ __classPrivateFieldGet(this, _ClientSDK_hooks, "f").registerBeforeCreateRequestHook(hook);
198
+ }
199
+ if ("beforeRequest" in hook) {
200
+ __classPrivateFieldGet(this, _ClientSDK_hooks, "f").registerBeforeRequestHook(hook);
201
+ }
202
+ if ("afterSuccess" in hook) {
203
+ __classPrivateFieldGet(this, _ClientSDK_hooks, "f").registerAfterSuccessHook(hook);
204
+ }
205
+ if ("afterError" in hook) {
206
+ __classPrivateFieldGet(this, _ClientSDK_hooks, "f").registerAfterErrorHook(hook);
207
+ }
208
+ };
189
209
  const jsonLikeContentTypeRE = /(application|text)\/.*?\+*json.*/;
190
210
  const jsonlLikeContentTypeRE = /(application|text)\/(.*?\+*\bjsonl\b.*|.*?\+*\bx-ndjson\b.*)/;
191
211
  async function logRequest(logger, req) {
@@ -1,4 +1,4 @@
1
- import { isOutputTextDeltaEvent, isReasoningDeltaEvent, isFunctionCallArgumentsDeltaEvent, isOutputItemAddedEvent, isOutputItemDoneEvent, isResponseCompletedEvent, isResponseFailedEvent, isResponseIncompleteEvent, isFunctionCallArgumentsDoneEvent, isOutputMessage, isFunctionCallOutputItem, isReasoningOutputItem, isWebSearchCallOutputItem, isFileSearchCallOutputItem, isImageGenerationCallOutputItem, isOutputTextPart, isRefusalPart, isFileCitationAnnotation, isURLCitationAnnotation, isFilePathAnnotation, } from './stream-type-guards.js';
1
+ import { isOutputTextDeltaEvent, isReasoningDeltaEvent, isFunctionCallArgumentsDeltaEvent, isOutputItemAddedEvent, isOutputItemDoneEvent, isResponseCompletedEvent, isResponseFailedEvent, isResponseIncompleteEvent, isFunctionCallArgumentsDoneEvent, isOutputMessage, isFunctionCallItem, isReasoningOutputItem, isWebSearchCallOutputItem, isFileSearchCallOutputItem, isImageGenerationCallOutputItem, isOutputTextPart, isRefusalPart, isFileCitationAnnotation, isURLCitationAnnotation, isFilePathAnnotation, } from './stream-type-guards.js';
2
2
  /**
3
3
  * Extract text deltas from responses stream events
4
4
  */
@@ -149,7 +149,7 @@ function handleOutputItemAdded(event, itemsInProgress) {
149
149
  content: [],
150
150
  };
151
151
  }
152
- if (isFunctionCallOutputItem(item)) {
152
+ if (isFunctionCallItem(item)) {
153
153
  // Use item.id if available (matches itemId in delta events), fall back to callId
154
154
  const itemKey = item.id ?? item.callId;
155
155
  itemsInProgress.set(itemKey, {
@@ -276,7 +276,7 @@ function handleOutputItemDone(event, itemsInProgress) {
276
276
  itemsInProgress.delete(item.id);
277
277
  return item;
278
278
  }
279
- if (isFunctionCallOutputItem(item)) {
279
+ if (isFunctionCallItem(item)) {
280
280
  // Use item.id if available (matches itemId in delta events), fall back to callId
281
281
  itemsInProgress.delete(item.id ?? item.callId);
282
282
  return item;
@@ -438,7 +438,7 @@ export function extractTextFromResponse(response) {
438
438
  export function extractToolCallsFromResponse(response) {
439
439
  const toolCalls = [];
440
440
  for (const item of response.output) {
441
- if (isFunctionCallOutputItem(item)) {
441
+ if (isFunctionCallItem(item)) {
442
442
  try {
443
443
  const parsedArguments = JSON.parse(item.arguments);
444
444
  toolCalls.push({
@@ -474,7 +474,7 @@ export async function* buildToolCallStream(stream) {
474
474
  }
475
475
  switch (event.type) {
476
476
  case 'response.output_item.added': {
477
- if (isOutputItemAddedEvent(event) && event.item && isFunctionCallOutputItem(event.item)) {
477
+ if (isOutputItemAddedEvent(event) && event.item && isFunctionCallItem(event.item)) {
478
478
  toolCallsInProgress.set(event.item.callId, {
479
479
  id: event.item.callId,
480
480
  name: event.item.name,
@@ -521,7 +521,7 @@ export async function* buildToolCallStream(stream) {
521
521
  break;
522
522
  }
523
523
  case 'response.output_item.done': {
524
- if (isOutputItemDoneEvent(event) && event.item && isFunctionCallOutputItem(event.item)) {
524
+ if (isOutputItemDoneEvent(event) && event.item && isFunctionCallItem(event.item)) {
525
525
  // Yield final tool call if we haven't already
526
526
  if (toolCallsInProgress.has(event.item.callId)) {
527
527
  try {
@@ -709,7 +709,7 @@ export function convertToClaudeMessage(response) {
709
709
  break;
710
710
  }
711
711
  case 'function_call': {
712
- if (isFunctionCallOutputItem(item)) {
712
+ if (isFunctionCallItem(item)) {
713
713
  let parsedInput;
714
714
  try {
715
715
  parsedInput = JSON.parse(item.arguments);
@@ -13,7 +13,7 @@ export declare function isResponseFailedEvent(event: models.OpenResponsesStreamE
13
13
  export declare function isResponseIncompleteEvent(event: models.OpenResponsesStreamEvent): event is models.OpenResponsesStreamEventResponseIncomplete;
14
14
  export declare function isFunctionCallArgumentsDoneEvent(event: models.OpenResponsesStreamEvent): event is models.OpenResponsesStreamEventResponseFunctionCallArgumentsDone;
15
15
  export declare function isOutputMessage(item: unknown): item is models.ResponsesOutputMessage;
16
- export declare function isFunctionCallOutputItem(item: unknown): item is models.ResponsesOutputItemFunctionCall;
16
+ export declare function isFunctionCallItem(item: unknown): item is models.ResponsesOutputItemFunctionCall;
17
17
  export declare function isReasoningOutputItem(item: unknown): item is models.ResponsesOutputItemReasoning;
18
18
  export declare function isWebSearchCallOutputItem(item: unknown): item is models.ResponsesWebSearchCallOutput;
19
19
  export declare function isFileSearchCallOutputItem(item: unknown): item is models.ResponsesOutputItemFileSearchCall;
@@ -37,7 +37,7 @@ export function isOutputMessage(item) {
37
37
  'type' in item &&
38
38
  item.type === 'message');
39
39
  }
40
- export function isFunctionCallOutputItem(item) {
40
+ export function isFunctionCallItem(item) {
41
41
  return (typeof item === 'object' &&
42
42
  item !== null &&
43
43
  'type' in item &&
@@ -158,43 +158,40 @@ export async function executeGeneratorTool(tool, toolCall, context, onPreliminar
158
158
  // Validate input - the schema validation ensures type safety at runtime
159
159
  // The inputSchema's inferred type matches the execute function's parameter type by construction
160
160
  const validatedInput = validateToolInput(tool.function.inputSchema, toolCall.arguments);
161
- // Stream preliminary results in realtime
162
- // Final result is identified by: matches outputSchema BUT NOT eventSchema
163
- // All other yields are preliminary results (validated against eventSchema)
164
- // If no explicit final result is found, the last emitted value is used as the final result
165
161
  const preliminaryResults = [];
166
162
  let finalResult = undefined;
167
163
  let hasFinalResult = false;
168
164
  let lastEmittedValue = undefined;
169
165
  let hasEmittedValue = false;
170
- for await (const event of tool.function.execute(validatedInput, context)) {
166
+ const iterator = tool.function.execute(validatedInput, context);
167
+ let iterResult = await iterator.next();
168
+ while (!iterResult.done) {
169
+ const event = iterResult.value;
171
170
  lastEmittedValue = event;
172
171
  hasEmittedValue = true;
173
- // Try to determine if this is the final result:
174
- // It must match outputSchema but NOT match eventSchema
175
172
  const matchesOutputSchema = tryValidate(tool.function.outputSchema, event);
176
173
  const matchesEventSchema = tryValidate(tool.function.eventSchema, event);
177
174
  if (matchesOutputSchema && !matchesEventSchema && !hasFinalResult) {
178
- // This is the final result - matches output but not event schema
179
175
  finalResult = validateToolOutput(tool.function.outputSchema, event);
180
176
  hasFinalResult = true;
181
177
  }
182
178
  else {
183
- // This is a preliminary result - validate against eventSchema and emit in realtime
184
179
  const validatedPreliminary = validateToolOutput(tool.function.eventSchema, event);
185
180
  preliminaryResults.push(validatedPreliminary);
186
181
  if (onPreliminaryResult) {
187
182
  onPreliminaryResult(toolCall.id, validatedPreliminary);
188
183
  }
189
184
  }
185
+ iterResult = await iterator.next();
190
186
  }
191
- // Generator must emit at least one value
192
- if (!hasEmittedValue) {
193
- throw new Error(`Generator tool "${toolCall.name}" completed without emitting any values`);
187
+ if (iterResult.value !== undefined) {
188
+ finalResult = validateToolOutput(tool.function.outputSchema, iterResult.value);
189
+ hasFinalResult = true;
194
190
  }
195
- // If no explicit final result was found (no yield matched outputSchema but not eventSchema),
196
- // use the last emitted value as the final result
197
191
  if (!hasFinalResult) {
192
+ if (!hasEmittedValue) {
193
+ throw new Error(`Generator tool "${toolCall.name}" completed without emitting any values or returning a result`);
194
+ }
198
195
  finalResult = validateToolOutput(tool.function.outputSchema, lastEmittedValue);
199
196
  }
200
197
  return {
@@ -1,5 +1,5 @@
1
1
  import { extractToolCallsFromResponse, responseHasToolCalls } from './stream-transformers.js';
2
- import { isFunctionCallOutputItem } from './stream-type-guards.js';
2
+ import { isFunctionCallItem } from './stream-type-guards.js';
3
3
  import { executeTool, findToolByName } from './tool-executor.js';
4
4
  import { hasExecuteFunction } from './tool-types.js';
5
5
  import { buildTurnContext } from './turn-context.js';
@@ -61,7 +61,7 @@ export async function executeToolLoop(sendRequest, initialInput, initialRequest,
61
61
  return null;
62
62
  }
63
63
  // Find the raw tool call from the response output
64
- const rawToolCall = currentResponse.output.find((item) => isFunctionCallOutputItem(item) && item.callId === toolCall.id);
64
+ const rawToolCall = currentResponse.output.find((item) => isFunctionCallItem(item) && item.callId === toolCall.id);
65
65
  if (!rawToolCall) {
66
66
  throw new Error(`Could not find raw tool call for ${toolCall.id}`);
67
67
  }
@@ -103,7 +103,7 @@ export interface ToolFunctionWithExecute<TInput extends $ZodObject<$ZodShape>, T
103
103
  export interface ToolFunctionWithGenerator<TInput extends $ZodObject<$ZodShape>, TEvent extends $ZodType = $ZodType<unknown>, TOutput extends $ZodType = $ZodType<unknown>> extends BaseToolFunction<TInput> {
104
104
  eventSchema: TEvent;
105
105
  outputSchema: TOutput;
106
- execute: (params: zodInfer<TInput>, context?: TurnContext) => AsyncGenerator<zodInfer<TEvent> | zodInfer<TOutput>>;
106
+ execute: (params: zodInfer<TInput>, context?: TurnContext) => AsyncGenerator<zodInfer<TEvent> | zodInfer<TOutput>, zodInfer<TOutput> | void>;
107
107
  }
108
108
  /**
109
109
  * Manual tool without execute function - requires manual handling by developer
package/jsr.json CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  {
4
4
  "name": "@openrouter/sdk",
5
- "version": "0.4.0",
5
+ "version": "0.5.1",
6
6
  "exports": {
7
7
  ".": "./src/index.ts",
8
8
  "./models/errors": "./src/models/errors/index.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openrouter/sdk",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "author": "OpenRouter",
5
5
  "description": "The OpenRouter TypeScript SDK is a type-safe toolkit for building AI applications with access to 300+ language models through a unified API.",
6
6
  "keywords": [
@@ -89,4 +89,4 @@
89
89
  "zod": "^3.25.0 || ^4.0.0"
90
90
  },
91
91
  "packageManager": "pnpm@10.22.0"
92
- }
92
+ }