Skip to content

Commit aa2cb31

Browse files
authored
fix tool-input-delta being sent before tool-input-start (#118)
* fix tool-input-delta being sent before tool-input-start * update assertion * format error-response.test.ts
1 parent 8aac1cf commit aa2cb31

File tree

3 files changed

+39
-24
lines changed

3 files changed

+39
-24
lines changed

src/chat/index.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,11 @@ describe('doStream', () => {
600600
type: 'response-metadata',
601601
modelId: 'gpt-3.5-turbo-0125',
602602
},
603+
{
604+
id: 'call_O17Uplv4lJvD6DVdIvFFeRMw',
605+
toolName: 'test-tool',
606+
type: 'tool-input-start',
607+
},
603608
{
604609
type: 'tool-input-delta',
605610
id: 'call_O17Uplv4lJvD6DVdIvFFeRMw',

src/chat/index.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { ReasoningDetailUnion } from '@/src/schemas/reasoning-details';
2+
import type { OpenRouterUsageAccounting } from '@/src/types/index';
13
import type {
24
LanguageModelV2,
35
LanguageModelV2CallOptions,
@@ -12,13 +14,12 @@ import type {
1214
import type { ParseResult } from '@ai-sdk/provider-utils';
1315
import type { FinishReason } from 'ai';
1416
import type { z } from 'zod/v4';
15-
import type { ReasoningDetailUnion } from '@/src/schemas/reasoning-details';
16-
import type { OpenRouterUsageAccounting } from '@/src/types/index';
1717
import type {
1818
OpenRouterChatModelId,
1919
OpenRouterChatSettings,
2020
} from '../types/openrouter-chat-settings';
2121

22+
import { ReasoningDetailType } from '@/src/schemas/reasoning-details';
2223
import { InvalidResponseDataError } from '@ai-sdk/provider';
2324
import {
2425
combineHeaders,
@@ -28,7 +29,6 @@ import {
2829
isParsableJson,
2930
postJsonToApi,
3031
} from '@ai-sdk/provider-utils';
31-
import { ReasoningDetailType } from '@/src/schemas/reasoning-details';
3232
import { openrouterFailedResponseHandler } from '../schemas/error-response';
3333
import { mapOpenRouterFinishReason } from '../utils/map-finish-reason';
3434
import { convertToOpenRouterChatMessages } from './convert-to-openrouter-chat-messages';
@@ -398,7 +398,7 @@ export class OpenRouterChatLanguageModel implements LanguageModelV2 {
398398
name: string;
399399
arguments: string;
400400
};
401-
401+
inputStarted: boolean;
402402
sent: boolean;
403403
}> = [];
404404

@@ -419,7 +419,7 @@ export class OpenRouterChatLanguageModel implements LanguageModelV2 {
419419
let textId: string | undefined;
420420
let reasoningId: string | undefined;
421421
let openrouterResponseId: string | undefined;
422-
422+
423423
return {
424424
stream: response.pipeThrough(
425425
new TransformStream<
@@ -604,6 +604,7 @@ export class OpenRouterChatLanguageModel implements LanguageModelV2 {
604604
name: toolCallDelta.function.name,
605605
arguments: toolCallDelta.function.arguments ?? '',
606606
},
607+
inputStarted: false,
607608
sent: false,
608609
};
609610

@@ -619,6 +620,8 @@ export class OpenRouterChatLanguageModel implements LanguageModelV2 {
619620
toolCall.function?.arguments != null &&
620621
isParsableJson(toolCall.function.arguments)
621622
) {
623+
toolCall.inputStarted = true;
624+
622625
controller.enqueue({
623626
type: 'tool-input-start',
624627
id: toolCall.id,
@@ -658,7 +661,8 @@ export class OpenRouterChatLanguageModel implements LanguageModelV2 {
658661
throw new Error('Tool call is missing');
659662
}
660663

661-
if (toolCallDelta.function?.name != null) {
664+
if (!toolCall.inputStarted) {
665+
toolCall.inputStarted = true;
662666
controller.enqueue({
663667
type: 'tool-input-start',
664668
id: toolCall.id,
@@ -717,10 +721,16 @@ export class OpenRouterChatLanguageModel implements LanguageModelV2 {
717721
}
718722

719723
if (textStarted) {
720-
controller.enqueue({ type: 'text-end', id: textId || generateId() });
724+
controller.enqueue({
725+
type: 'text-end',
726+
id: textId || generateId(),
727+
});
721728
}
722729
if (reasoningStarted) {
723-
controller.enqueue({ type: 'reasoning-end', id: reasoningId || generateId() });
730+
controller.enqueue({
731+
type: 'reasoning-end',
732+
id: reasoningId || generateId(),
733+
});
724734
}
725735

726736
controller.enqueue({

src/schemas/error-response.test.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,37 @@
1-
import { OpenRouterErrorResponseSchema } from "./error-response";
1+
import { OpenRouterErrorResponseSchema } from './error-response';
22

3-
describe("OpenRouterErrorResponseSchema", () => {
4-
it("should be valid without a type, code, and param", () => {
3+
describe('OpenRouterErrorResponseSchema', () => {
4+
it('should be valid without a type, code, and param', () => {
55
const errorWithoutTypeCodeAndParam = {
66
error: {
7-
message: "Example error message",
8-
metadata: { provider_name: "Morph" },
7+
message: 'Example error message',
8+
metadata: { provider_name: 'Morph' },
99
},
10-
user_id: "example_1",
10+
user_id: 'example_1',
1111
};
1212

1313
const result = OpenRouterErrorResponseSchema.parse(
14-
errorWithoutTypeCodeAndParam
14+
errorWithoutTypeCodeAndParam,
1515
);
1616

1717
expect(result).toEqual({
1818
error: {
19-
message: "Example error message",
19+
message: 'Example error message',
2020
code: null,
2121
type: null,
2222
param: null,
2323
},
2424
});
2525
});
2626

27-
it("should be invalid with a type", () => {
27+
it('should be invalid with a type', () => {
2828
const errorWithType = {
2929
error: {
30-
message: "Example error message with type",
31-
type: "invalid_request_error",
30+
message: 'Example error message with type',
31+
type: 'invalid_request_error',
3232
code: 400,
33-
param: "canBeAnything",
34-
metadata: { provider_name: "Morph" },
33+
param: 'canBeAnything',
34+
metadata: { provider_name: 'Morph' },
3535
},
3636
};
3737

@@ -40,9 +40,9 @@ describe("OpenRouterErrorResponseSchema", () => {
4040
expect(result).toEqual({
4141
error: {
4242
code: 400,
43-
message: "Example error message with type",
44-
type: "invalid_request_error",
45-
param: "canBeAnything",
43+
message: 'Example error message with type',
44+
type: 'invalid_request_error',
45+
param: 'canBeAnything',
4646
},
4747
});
4848
});

0 commit comments

Comments
 (0)