Skip to content

Commit 272c31f

Browse files
authored
chore: make index optional (#99)
1 parent a20a176 commit 272c31f

File tree

5 files changed

+188
-189
lines changed

5 files changed

+188
-189
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@openrouter/ai-sdk-provider",
3-
"version": "1.0.0-beta.0",
3+
"version": "1.0.0-beta.1",
44
"license": "Apache-2.0",
55
"sideEffects": false,
66
"main": "./dist/index.js",

src/chat/index.ts

Lines changed: 8 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type {
1111
} from '@ai-sdk/provider';
1212
import type { ParseResult } from '@ai-sdk/provider-utils';
1313
import type { FinishReason } from 'ai';
14+
import type { z } from 'zod/v4';
1415
import type { ReasoningDetailUnion } from '@/src/schemas/reasoning-details';
1516
import type { OpenRouterUsageAccounting } from '@/src/types/index';
1617
import type {
@@ -27,18 +28,15 @@ import {
2728
isParsableJson,
2829
postJsonToApi,
2930
} from '@ai-sdk/provider-utils';
30-
import { z } from 'zod/v4';
31-
import {
32-
ReasoningDetailArraySchema,
33-
ReasoningDetailType,
34-
} from '@/src/schemas/reasoning-details';
35-
import {
36-
OpenRouterErrorResponseSchema,
37-
openrouterFailedResponseHandler,
38-
} from '../schemas/error-response';
31+
import { ReasoningDetailType } from '@/src/schemas/reasoning-details';
32+
import { openrouterFailedResponseHandler } from '../schemas/error-response';
3933
import { mapOpenRouterFinishReason } from '../utils/map-finish-reason';
4034
import { convertToOpenRouterChatMessages } from './convert-to-openrouter-chat-messages';
4135
import { getChatCompletionToolChoice } from './get-tool-choice';
36+
import {
37+
OpenRouterNonStreamChatCompletionResponseSchema,
38+
OpenRouterStreamChatCompletionChunkSchema,
39+
} from './schemas';
4240

4341
type OpenRouterChatConfig = {
4442
provider: string;
@@ -562,7 +560,7 @@ export class OpenRouterChatLanguageModel implements LanguageModelV2 {
562560

563561
if (delta.tool_calls != null) {
564562
for (const toolCallDelta of delta.tool_calls) {
565-
const index = toolCallDelta.index;
563+
const index = toolCallDelta.index ?? toolCalls.length - 1;
566564

567565
// Tool call start. OpenRouter returns all information except the arguments in the first chunk.
568566
if (toolCalls[index] == null) {
@@ -725,129 +723,3 @@ export class OpenRouterChatLanguageModel implements LanguageModelV2 {
725723
};
726724
}
727725
}
728-
729-
const OpenRouterChatCompletionBaseResponseSchema = z.object({
730-
id: z.string().optional(),
731-
model: z.string().optional(),
732-
usage: z
733-
.object({
734-
prompt_tokens: z.number(),
735-
prompt_tokens_details: z
736-
.object({
737-
cached_tokens: z.number(),
738-
})
739-
.nullish(),
740-
completion_tokens: z.number(),
741-
completion_tokens_details: z
742-
.object({
743-
reasoning_tokens: z.number(),
744-
})
745-
.nullish(),
746-
total_tokens: z.number(),
747-
cost: z.number().optional(),
748-
})
749-
.nullish(),
750-
});
751-
752-
// limited version of the schema, focussed on what is needed for the implementation
753-
// this approach limits breakages when the API changes and increases efficiency
754-
const OpenRouterNonStreamChatCompletionResponseSchema =
755-
OpenRouterChatCompletionBaseResponseSchema.extend({
756-
choices: z.array(
757-
z.object({
758-
message: z.object({
759-
role: z.literal('assistant'),
760-
content: z.string().nullable().optional(),
761-
reasoning: z.string().nullable().optional(),
762-
reasoning_details: ReasoningDetailArraySchema.nullish(),
763-
764-
tool_calls: z
765-
.array(
766-
z.object({
767-
id: z.string().optional().nullable(),
768-
type: z.literal('function'),
769-
function: z.object({
770-
name: z.string(),
771-
arguments: z.string(),
772-
}),
773-
}),
774-
)
775-
.optional(),
776-
}),
777-
index: z.number(),
778-
logprobs: z
779-
.object({
780-
content: z
781-
.array(
782-
z.object({
783-
token: z.string(),
784-
logprob: z.number(),
785-
top_logprobs: z.array(
786-
z.object({
787-
token: z.string(),
788-
logprob: z.number(),
789-
}),
790-
),
791-
}),
792-
)
793-
.nullable(),
794-
})
795-
.nullable()
796-
.optional(),
797-
finish_reason: z.string().optional().nullable(),
798-
}),
799-
),
800-
});
801-
802-
// limited version of the schema, focussed on what is needed for the implementation
803-
// this approach limits breakages when the API changes and increases efficiency
804-
const OpenRouterStreamChatCompletionChunkSchema = z.union([
805-
OpenRouterChatCompletionBaseResponseSchema.extend({
806-
choices: z.array(
807-
z.object({
808-
delta: z
809-
.object({
810-
role: z.enum(['assistant']).optional(),
811-
content: z.string().nullish(),
812-
reasoning: z.string().nullish().optional(),
813-
reasoning_details: ReasoningDetailArraySchema.nullish(),
814-
tool_calls: z
815-
.array(
816-
z.object({
817-
index: z.number(),
818-
id: z.string().nullish(),
819-
type: z.literal('function').optional(),
820-
function: z.object({
821-
name: z.string().nullish(),
822-
arguments: z.string().nullish(),
823-
}),
824-
}),
825-
)
826-
.nullish(),
827-
})
828-
.nullish(),
829-
logprobs: z
830-
.object({
831-
content: z
832-
.array(
833-
z.object({
834-
token: z.string(),
835-
logprob: z.number(),
836-
top_logprobs: z.array(
837-
z.object({
838-
token: z.string(),
839-
logprob: z.number(),
840-
}),
841-
),
842-
}),
843-
)
844-
.nullable(),
845-
})
846-
.nullish(),
847-
finish_reason: z.string().nullable().optional(),
848-
index: z.number(),
849-
}),
850-
),
851-
}),
852-
OpenRouterErrorResponseSchema,
853-
]);

src/chat/schemas.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { z } from 'zod/v4';
2+
import { OpenRouterErrorResponseSchema } from '../schemas/error-response';
3+
import { ReasoningDetailArraySchema } from '../schemas/reasoning-details';
4+
5+
const OpenRouterChatCompletionBaseResponseSchema = z.object({
6+
id: z.string().optional(),
7+
model: z.string().optional(),
8+
usage: z
9+
.object({
10+
prompt_tokens: z.number(),
11+
prompt_tokens_details: z
12+
.object({
13+
cached_tokens: z.number(),
14+
})
15+
.nullish(),
16+
completion_tokens: z.number(),
17+
completion_tokens_details: z
18+
.object({
19+
reasoning_tokens: z.number(),
20+
})
21+
.nullish(),
22+
total_tokens: z.number(),
23+
cost: z.number().optional(),
24+
})
25+
.nullish(),
26+
});
27+
// limited version of the schema, focussed on what is needed for the implementation
28+
// this approach limits breakages when the API changes and increases efficiency
29+
export const OpenRouterNonStreamChatCompletionResponseSchema =
30+
OpenRouterChatCompletionBaseResponseSchema.extend({
31+
choices: z.array(
32+
z.object({
33+
message: z.object({
34+
role: z.literal('assistant'),
35+
content: z.string().nullable().optional(),
36+
reasoning: z.string().nullable().optional(),
37+
reasoning_details: ReasoningDetailArraySchema.nullish(),
38+
39+
tool_calls: z
40+
.array(
41+
z.object({
42+
id: z.string().optional().nullable(),
43+
type: z.literal('function'),
44+
function: z.object({
45+
name: z.string(),
46+
arguments: z.string(),
47+
}),
48+
}),
49+
)
50+
.optional(),
51+
}),
52+
index: z.number().nullish(),
53+
logprobs: z
54+
.object({
55+
content: z
56+
.array(
57+
z.object({
58+
token: z.string(),
59+
logprob: z.number(),
60+
top_logprobs: z.array(
61+
z.object({
62+
token: z.string(),
63+
logprob: z.number(),
64+
}),
65+
),
66+
}),
67+
)
68+
.nullable(),
69+
})
70+
.nullable()
71+
.optional(),
72+
finish_reason: z.string().optional().nullable(),
73+
}),
74+
),
75+
});
76+
// limited version of the schema, focussed on what is needed for the implementation
77+
// this approach limits breakages when the API changes and increases efficiency
78+
export const OpenRouterStreamChatCompletionChunkSchema = z.union([
79+
OpenRouterChatCompletionBaseResponseSchema.extend({
80+
choices: z.array(
81+
z.object({
82+
delta: z
83+
.object({
84+
role: z.enum(['assistant']).optional(),
85+
content: z.string().nullish(),
86+
reasoning: z.string().nullish().optional(),
87+
reasoning_details: ReasoningDetailArraySchema.nullish(),
88+
tool_calls: z
89+
.array(
90+
z.object({
91+
index: z.number().nullish(),
92+
id: z.string().nullish(),
93+
type: z.literal('function').optional(),
94+
function: z.object({
95+
name: z.string().nullish(),
96+
arguments: z.string().nullish(),
97+
}),
98+
}),
99+
)
100+
.nullish(),
101+
})
102+
.nullish(),
103+
logprobs: z
104+
.object({
105+
content: z
106+
.array(
107+
z.object({
108+
token: z.string(),
109+
logprob: z.number(),
110+
top_logprobs: z.array(
111+
z.object({
112+
token: z.string(),
113+
logprob: z.number(),
114+
}),
115+
),
116+
}),
117+
)
118+
.nullable(),
119+
})
120+
.nullish(),
121+
finish_reason: z.string().nullable().optional(),
122+
index: z.number().nullish(),
123+
}),
124+
),
125+
}),
126+
OpenRouterErrorResponseSchema,
127+
]);

src/completion/index.ts

Lines changed: 3 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {
66
} from '@ai-sdk/provider';
77
import type { ParseResult } from '@ai-sdk/provider-utils';
88
import type { FinishReason } from 'ai';
9+
import type { z } from 'zod/v4';
910
import type { OpenRouterUsageAccounting } from '../types';
1011
import type {
1112
OpenRouterCompletionModelId,
@@ -20,60 +21,10 @@ import {
2021
generateId,
2122
postJsonToApi,
2223
} from '@ai-sdk/provider-utils';
23-
import { z } from 'zod/v4';
24-
import {
25-
OpenRouterErrorResponseSchema,
26-
openrouterFailedResponseHandler,
27-
} from '../schemas/error-response';
28-
import { ReasoningDetailArraySchema } from '../schemas/reasoning-details';
24+
import { openrouterFailedResponseHandler } from '../schemas/error-response';
2925
import { mapOpenRouterFinishReason } from '../utils/map-finish-reason';
3026
import { convertToOpenRouterCompletionPrompt } from './convert-to-openrouter-completion-prompt';
31-
32-
// limited version of the schema, focussed on what is needed for the implementation
33-
// this approach limits breakages when the API changes and increases efficiency
34-
const OpenRouterCompletionChunkSchema = z.union([
35-
z.object({
36-
id: z.string().optional(),
37-
model: z.string().optional(),
38-
choices: z.array(
39-
z.object({
40-
text: z.string(),
41-
reasoning: z.string().nullish().optional(),
42-
reasoning_details: ReasoningDetailArraySchema.nullish(),
43-
44-
finish_reason: z.string().nullish(),
45-
index: z.number(),
46-
logprobs: z
47-
.object({
48-
tokens: z.array(z.string()),
49-
token_logprobs: z.array(z.number()),
50-
top_logprobs: z.array(z.record(z.string(), z.number())).nullable(),
51-
})
52-
.nullable()
53-
.optional(),
54-
}),
55-
),
56-
usage: z
57-
.object({
58-
prompt_tokens: z.number(),
59-
prompt_tokens_details: z
60-
.object({
61-
cached_tokens: z.number(),
62-
})
63-
.nullish(),
64-
completion_tokens: z.number(),
65-
completion_tokens_details: z
66-
.object({
67-
reasoning_tokens: z.number(),
68-
})
69-
.nullish(),
70-
total_tokens: z.number(),
71-
cost: z.number().optional(),
72-
})
73-
.nullish(),
74-
}),
75-
OpenRouterErrorResponseSchema,
76-
]);
27+
import { OpenRouterCompletionChunkSchema } from './schemas';
7728

7829
type OpenRouterCompletionConfig = {
7930
provider: string;

0 commit comments

Comments
 (0)