@hebo-ai/gateway 0.5.2 → 0.6.0-rc0

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.
Files changed (180) hide show
  1. package/README.md +32 -1
  2. package/package.json +17 -12
  3. package/src/endpoints/chat-completions/converters.test.ts +85 -1
  4. package/src/endpoints/chat-completions/converters.ts +139 -18
  5. package/src/endpoints/chat-completions/handler.test.ts +2 -0
  6. package/src/endpoints/chat-completions/index.ts +1 -0
  7. package/src/endpoints/chat-completions/otel.ts +1 -0
  8. package/src/endpoints/chat-completions/schema.ts +38 -4
  9. package/src/endpoints/embeddings/index.ts +1 -0
  10. package/src/lifecycle.ts +2 -2
  11. package/src/models/anthropic/middleware.test.ts +45 -1
  12. package/src/models/anthropic/middleware.ts +21 -1
  13. package/src/models/google/middleware.test.ts +30 -1
  14. package/src/models/google/middleware.ts +20 -1
  15. package/src/models/openai/middleware.test.ts +32 -1
  16. package/src/models/openai/middleware.ts +25 -1
  17. package/src/providers/bedrock/middleware.test.ts +121 -1
  18. package/src/providers/bedrock/middleware.ts +61 -1
  19. package/src/telemetry/fetch.ts +31 -4
  20. package/src/telemetry/index.ts +1 -0
  21. package/dist/config.d.ts +0 -2
  22. package/dist/config.js +0 -81
  23. package/dist/endpoints/chat-completions/converters.d.ts +0 -43
  24. package/dist/endpoints/chat-completions/converters.js +0 -551
  25. package/dist/endpoints/chat-completions/handler.d.ts +0 -2
  26. package/dist/endpoints/chat-completions/handler.js +0 -145
  27. package/dist/endpoints/chat-completions/index.d.ts +0 -3
  28. package/dist/endpoints/chat-completions/index.js +0 -3
  29. package/dist/endpoints/chat-completions/otel.d.ts +0 -6
  30. package/dist/endpoints/chat-completions/otel.js +0 -134
  31. package/dist/endpoints/chat-completions/schema.d.ts +0 -946
  32. package/dist/endpoints/chat-completions/schema.js +0 -257
  33. package/dist/endpoints/embeddings/converters.d.ts +0 -10
  34. package/dist/endpoints/embeddings/converters.js +0 -31
  35. package/dist/endpoints/embeddings/handler.d.ts +0 -2
  36. package/dist/endpoints/embeddings/handler.js +0 -101
  37. package/dist/endpoints/embeddings/index.d.ts +0 -3
  38. package/dist/endpoints/embeddings/index.js +0 -3
  39. package/dist/endpoints/embeddings/otel.d.ts +0 -6
  40. package/dist/endpoints/embeddings/otel.js +0 -35
  41. package/dist/endpoints/embeddings/schema.d.ts +0 -38
  42. package/dist/endpoints/embeddings/schema.js +0 -26
  43. package/dist/endpoints/models/converters.d.ts +0 -6
  44. package/dist/endpoints/models/converters.js +0 -42
  45. package/dist/endpoints/models/handler.d.ts +0 -2
  46. package/dist/endpoints/models/handler.js +0 -29
  47. package/dist/endpoints/models/index.d.ts +0 -3
  48. package/dist/endpoints/models/index.js +0 -3
  49. package/dist/endpoints/models/schema.d.ts +0 -42
  50. package/dist/endpoints/models/schema.js +0 -31
  51. package/dist/errors/ai-sdk.d.ts +0 -2
  52. package/dist/errors/ai-sdk.js +0 -52
  53. package/dist/errors/gateway.d.ts +0 -5
  54. package/dist/errors/gateway.js +0 -13
  55. package/dist/errors/openai.d.ts +0 -20
  56. package/dist/errors/openai.js +0 -40
  57. package/dist/errors/utils.d.ts +0 -22
  58. package/dist/errors/utils.js +0 -44
  59. package/dist/gateway.d.ts +0 -9
  60. package/dist/gateway.js +0 -34
  61. package/dist/index.d.ts +0 -14
  62. package/dist/index.js +0 -13
  63. package/dist/lifecycle.d.ts +0 -2
  64. package/dist/lifecycle.js +0 -94
  65. package/dist/logger/default.d.ts +0 -4
  66. package/dist/logger/default.js +0 -81
  67. package/dist/logger/index.d.ts +0 -14
  68. package/dist/logger/index.js +0 -25
  69. package/dist/middleware/common.d.ts +0 -12
  70. package/dist/middleware/common.js +0 -145
  71. package/dist/middleware/matcher.d.ts +0 -27
  72. package/dist/middleware/matcher.js +0 -112
  73. package/dist/middleware/utils.d.ts +0 -2
  74. package/dist/middleware/utils.js +0 -27
  75. package/dist/models/amazon/index.d.ts +0 -2
  76. package/dist/models/amazon/index.js +0 -2
  77. package/dist/models/amazon/middleware.d.ts +0 -3
  78. package/dist/models/amazon/middleware.js +0 -65
  79. package/dist/models/amazon/presets.d.ts +0 -2390
  80. package/dist/models/amazon/presets.js +0 -80
  81. package/dist/models/anthropic/index.d.ts +0 -2
  82. package/dist/models/anthropic/index.js +0 -2
  83. package/dist/models/anthropic/middleware.d.ts +0 -4
  84. package/dist/models/anthropic/middleware.js +0 -111
  85. package/dist/models/anthropic/presets.d.ts +0 -4802
  86. package/dist/models/anthropic/presets.js +0 -135
  87. package/dist/models/catalog.d.ts +0 -4
  88. package/dist/models/catalog.js +0 -4
  89. package/dist/models/cohere/index.d.ts +0 -2
  90. package/dist/models/cohere/index.js +0 -2
  91. package/dist/models/cohere/middleware.d.ts +0 -3
  92. package/dist/models/cohere/middleware.js +0 -60
  93. package/dist/models/cohere/presets.d.ts +0 -2918
  94. package/dist/models/cohere/presets.js +0 -134
  95. package/dist/models/google/index.d.ts +0 -2
  96. package/dist/models/google/index.js +0 -2
  97. package/dist/models/google/middleware.d.ts +0 -7
  98. package/dist/models/google/middleware.js +0 -103
  99. package/dist/models/google/presets.d.ts +0 -2553
  100. package/dist/models/google/presets.js +0 -83
  101. package/dist/models/meta/index.d.ts +0 -1
  102. package/dist/models/meta/index.js +0 -1
  103. package/dist/models/meta/presets.d.ts +0 -3254
  104. package/dist/models/meta/presets.js +0 -95
  105. package/dist/models/openai/index.d.ts +0 -2
  106. package/dist/models/openai/index.js +0 -2
  107. package/dist/models/openai/middleware.d.ts +0 -3
  108. package/dist/models/openai/middleware.js +0 -62
  109. package/dist/models/openai/presets.d.ts +0 -6634
  110. package/dist/models/openai/presets.js +0 -213
  111. package/dist/models/types.d.ts +0 -20
  112. package/dist/models/types.js +0 -84
  113. package/dist/models/voyage/index.d.ts +0 -2
  114. package/dist/models/voyage/index.js +0 -2
  115. package/dist/models/voyage/middleware.d.ts +0 -2
  116. package/dist/models/voyage/middleware.js +0 -18
  117. package/dist/models/voyage/presets.d.ts +0 -3471
  118. package/dist/models/voyage/presets.js +0 -85
  119. package/dist/providers/anthropic/canonical.d.ts +0 -3
  120. package/dist/providers/anthropic/canonical.js +0 -9
  121. package/dist/providers/anthropic/index.d.ts +0 -1
  122. package/dist/providers/anthropic/index.js +0 -1
  123. package/dist/providers/bedrock/canonical.d.ts +0 -17
  124. package/dist/providers/bedrock/canonical.js +0 -61
  125. package/dist/providers/bedrock/index.d.ts +0 -2
  126. package/dist/providers/bedrock/index.js +0 -2
  127. package/dist/providers/bedrock/middleware.d.ts +0 -3
  128. package/dist/providers/bedrock/middleware.js +0 -55
  129. package/dist/providers/cohere/canonical.d.ts +0 -3
  130. package/dist/providers/cohere/canonical.js +0 -17
  131. package/dist/providers/cohere/index.d.ts +0 -1
  132. package/dist/providers/cohere/index.js +0 -1
  133. package/dist/providers/groq/canonical.d.ts +0 -3
  134. package/dist/providers/groq/canonical.js +0 -12
  135. package/dist/providers/groq/index.d.ts +0 -1
  136. package/dist/providers/groq/index.js +0 -1
  137. package/dist/providers/openai/canonical.d.ts +0 -3
  138. package/dist/providers/openai/canonical.js +0 -8
  139. package/dist/providers/openai/index.d.ts +0 -1
  140. package/dist/providers/openai/index.js +0 -1
  141. package/dist/providers/registry.d.ts +0 -24
  142. package/dist/providers/registry.js +0 -100
  143. package/dist/providers/types.d.ts +0 -7
  144. package/dist/providers/types.js +0 -11
  145. package/dist/providers/vertex/canonical.d.ts +0 -3
  146. package/dist/providers/vertex/canonical.js +0 -8
  147. package/dist/providers/vertex/index.d.ts +0 -1
  148. package/dist/providers/vertex/index.js +0 -1
  149. package/dist/providers/voyage/canonical.d.ts +0 -3
  150. package/dist/providers/voyage/canonical.js +0 -7
  151. package/dist/providers/voyage/index.d.ts +0 -1
  152. package/dist/providers/voyage/index.js +0 -1
  153. package/dist/telemetry/ai-sdk.d.ts +0 -2
  154. package/dist/telemetry/ai-sdk.js +0 -31
  155. package/dist/telemetry/baggage.d.ts +0 -1
  156. package/dist/telemetry/baggage.js +0 -24
  157. package/dist/telemetry/fetch.d.ts +0 -2
  158. package/dist/telemetry/fetch.js +0 -24
  159. package/dist/telemetry/gen-ai.d.ts +0 -5
  160. package/dist/telemetry/gen-ai.js +0 -60
  161. package/dist/telemetry/http.d.ts +0 -3
  162. package/dist/telemetry/http.js +0 -54
  163. package/dist/telemetry/memory.d.ts +0 -2
  164. package/dist/telemetry/memory.js +0 -27
  165. package/dist/telemetry/span.d.ts +0 -13
  166. package/dist/telemetry/span.js +0 -60
  167. package/dist/telemetry/stream.d.ts +0 -3
  168. package/dist/telemetry/stream.js +0 -51
  169. package/dist/types.d.ts +0 -176
  170. package/dist/types.js +0 -1
  171. package/dist/utils/env.d.ts +0 -2
  172. package/dist/utils/env.js +0 -5
  173. package/dist/utils/headers.d.ts +0 -4
  174. package/dist/utils/headers.js +0 -22
  175. package/dist/utils/preset.d.ts +0 -9
  176. package/dist/utils/preset.js +0 -41
  177. package/dist/utils/request.d.ts +0 -2
  178. package/dist/utils/request.js +0 -14
  179. package/dist/utils/response.d.ts +0 -3
  180. package/dist/utils/response.js +0 -68
@@ -1,145 +0,0 @@
1
- import { generateText, Output, streamText, wrapLanguageModel, } from "ai";
2
- import * as z from "zod/mini";
3
- import { GatewayError } from "../../errors/gateway";
4
- import { winterCgHandler } from "../../lifecycle";
5
- import { logger } from "../../logger";
6
- import { modelMiddlewareMatcher } from "../../middleware/matcher";
7
- import { resolveProvider } from "../../providers/registry";
8
- import { recordRequestDuration, recordTimePerOutputToken, recordTokenUsage, } from "../../telemetry/gen-ai";
9
- import { addSpanEvent, setSpanAttributes } from "../../telemetry/span";
10
- import { prepareForwardHeaders } from "../../utils/request";
11
- import { convertToTextCallOptions, toChatCompletions, toChatCompletionsStream } from "./converters";
12
- import { getChatGeneralAttributes, getChatRequestAttributes, getChatResponseAttributes, } from "./otel";
13
- import { ChatCompletionsBodySchema } from "./schema";
14
- export const chatCompletions = (config) => {
15
- const hooks = config.hooks;
16
- const handler = async (ctx) => {
17
- const start = performance.now();
18
- ctx.operation = "chat";
19
- addSpanEvent("hebo.handler.started");
20
- // Guard: enforce HTTP method early.
21
- if (!ctx.request || ctx.request.method !== "POST") {
22
- throw new GatewayError("Method Not Allowed", 405);
23
- }
24
- // Parse + validate input.
25
- try {
26
- ctx.body = await ctx.request.json();
27
- }
28
- catch {
29
- throw new GatewayError("Invalid JSON", 400);
30
- }
31
- addSpanEvent("hebo.request.deserialized");
32
- const parsed = ChatCompletionsBodySchema.safeParse(ctx.body);
33
- if (!parsed.success) {
34
- // FUTURE: consider adding body shape to metadata
35
- throw new GatewayError(z.prettifyError(parsed.error), 400, undefined, parsed.error);
36
- }
37
- ctx.body = parsed.data;
38
- addSpanEvent("hebo.request.parsed");
39
- if (hooks?.before) {
40
- ctx.body = (await hooks.before(ctx)) ?? ctx.body;
41
- addSpanEvent("hebo.hooks.before.completed");
42
- }
43
- // Resolve model + provider (hooks may override defaults).
44
- let inputs, stream;
45
- ({ model: ctx.modelId, stream, ...inputs } = ctx.body);
46
- ctx.resolvedModelId =
47
- (await hooks?.resolveModelId?.(ctx)) ?? ctx.modelId;
48
- logger.debug(`[chat] resolved ${ctx.modelId} to ${ctx.resolvedModelId}`);
49
- addSpanEvent("hebo.model.resolved");
50
- const override = await hooks?.resolveProvider?.(ctx);
51
- ctx.provider =
52
- override ??
53
- resolveProvider({
54
- providers: ctx.providers,
55
- models: ctx.models,
56
- modelId: ctx.resolvedModelId,
57
- operation: ctx.operation,
58
- });
59
- const languageModel = ctx.provider.languageModel(ctx.resolvedModelId);
60
- ctx.resolvedProviderId = languageModel.provider;
61
- logger.debug(`[chat] using ${languageModel.provider} for ${ctx.resolvedModelId}`);
62
- addSpanEvent("hebo.provider.resolved");
63
- const genAiSignalLevel = config.telemetry?.signals?.gen_ai;
64
- const genAiGeneralAttrs = getChatGeneralAttributes(ctx, genAiSignalLevel);
65
- setSpanAttributes(genAiGeneralAttrs);
66
- // Convert inputs to AI SDK call options.
67
- const textOptions = convertToTextCallOptions(inputs);
68
- logger.trace({
69
- requestId: ctx.requestId,
70
- options: textOptions,
71
- }, "[chat] AI SDK options");
72
- addSpanEvent("hebo.options.prepared");
73
- setSpanAttributes(getChatRequestAttributes(inputs, genAiSignalLevel));
74
- // Build middleware chain (model -> forward params -> provider).
75
- const languageModelWithMiddleware = wrapLanguageModel({
76
- model: languageModel,
77
- middleware: modelMiddlewareMatcher.for(ctx.resolvedModelId, languageModel.provider),
78
- });
79
- // Execute request (streaming vs. non-streaming).
80
- if (stream) {
81
- addSpanEvent("hebo.ai-sdk.started");
82
- const result = streamText({
83
- model: languageModelWithMiddleware,
84
- headers: prepareForwardHeaders(ctx.request),
85
- abortSignal: ctx.request.signal,
86
- timeout: {
87
- totalMs: 5 * 60 * 1000,
88
- },
89
- onAbort: () => {
90
- throw new DOMException("The operation was aborted.", "AbortError");
91
- },
92
- onError: () => { },
93
- onFinish: (res) => {
94
- addSpanEvent("hebo.ai-sdk.completed");
95
- const streamResult = toChatCompletions(res, ctx.resolvedModelId);
96
- addSpanEvent("hebo.result.transformed");
97
- const genAiResponseAttrs = getChatResponseAttributes(streamResult, genAiSignalLevel);
98
- setSpanAttributes(genAiResponseAttrs);
99
- recordTokenUsage(genAiResponseAttrs, genAiGeneralAttrs, genAiSignalLevel);
100
- recordTimePerOutputToken(start, genAiResponseAttrs, genAiGeneralAttrs, genAiSignalLevel);
101
- recordRequestDuration(start, genAiGeneralAttrs, genAiSignalLevel);
102
- },
103
- experimental_include: {
104
- requestBody: false,
105
- },
106
- includeRawChunks: false,
107
- ...textOptions,
108
- });
109
- ctx.result = toChatCompletionsStream(result, ctx.resolvedModelId);
110
- if (hooks?.after) {
111
- ctx.result = (await hooks.after(ctx)) ?? ctx.result;
112
- addSpanEvent("hebo.hooks.after.completed");
113
- }
114
- return ctx.result;
115
- }
116
- addSpanEvent("hebo.ai-sdk.started");
117
- const result = await generateText({
118
- model: languageModelWithMiddleware,
119
- headers: prepareForwardHeaders(ctx.request),
120
- abortSignal: ctx.request.signal,
121
- timeout: 5 * 60 * 1000,
122
- experimental_include: {
123
- requestBody: false,
124
- responseBody: false,
125
- },
126
- ...textOptions,
127
- });
128
- logger.trace({ requestId: ctx.requestId, result }, "[chat] AI SDK result");
129
- addSpanEvent("hebo.ai-sdk.completed");
130
- // Transform result.
131
- ctx.result = toChatCompletions(result, ctx.resolvedModelId);
132
- addSpanEvent("hebo.result.transformed");
133
- const genAiResponseAttrs = getChatResponseAttributes(ctx.result, genAiSignalLevel);
134
- setSpanAttributes(genAiResponseAttrs);
135
- recordTokenUsage(genAiResponseAttrs, genAiGeneralAttrs, genAiSignalLevel);
136
- if (hooks?.after) {
137
- ctx.result = (await hooks.after(ctx)) ?? ctx.result;
138
- addSpanEvent("hebo.hooks.after.completed");
139
- }
140
- recordTimePerOutputToken(start, genAiResponseAttrs, genAiGeneralAttrs, genAiSignalLevel);
141
- recordRequestDuration(start, genAiGeneralAttrs, genAiSignalLevel);
142
- return ctx.result;
143
- };
144
- return { handler: winterCgHandler(handler, config) };
145
- };
@@ -1,3 +0,0 @@
1
- export * from "./converters";
2
- export * from "./handler";
3
- export * from "./schema";
@@ -1,3 +0,0 @@
1
- export * from "./converters";
2
- export * from "./handler";
3
- export * from "./schema";
@@ -1,6 +0,0 @@
1
- import type { Attributes } from "@opentelemetry/api";
2
- import type { ChatCompletions, ChatCompletionsBody } from "./schema";
3
- import { type GatewayContext, type TelemetrySignalLevel } from "../../types";
4
- export declare const getChatGeneralAttributes: (ctx: GatewayContext, signalLevel?: TelemetrySignalLevel) => Attributes;
5
- export declare const getChatRequestAttributes: (inputs: ChatCompletionsBody, signalLevel?: TelemetrySignalLevel) => Attributes;
6
- export declare const getChatResponseAttributes: (completions: ChatCompletions, signalLevel?: TelemetrySignalLevel) => Attributes;
@@ -1,134 +0,0 @@
1
- import {} from "../../types";
2
- const toTextPart = (content) => ({ type: "text", content });
3
- const toMessageParts = (message) => {
4
- if (message.role === "assistant") {
5
- const parts = [];
6
- if (typeof message.content === "string")
7
- parts.push(toTextPart(message.content));
8
- if (Array.isArray(message.tool_calls)) {
9
- for (const call of message.tool_calls) {
10
- parts.push({
11
- type: "tool_call",
12
- id: call.id,
13
- name: call.function.name,
14
- arguments: call.function.arguments,
15
- });
16
- }
17
- }
18
- return parts;
19
- }
20
- if (message.role === "tool") {
21
- return [{ type: "tool_call_response", id: message.tool_call_id, content: message.content }];
22
- }
23
- if (message.role === "user") {
24
- const parts = [];
25
- if (typeof message.content === "string")
26
- parts.push(toTextPart(message.content));
27
- if (Array.isArray(message.content)) {
28
- for (const part of message.content) {
29
- if (part.type === "text") {
30
- parts.push(toTextPart(part.text));
31
- }
32
- else if (part.type === "image_url") {
33
- parts.push({ type: "image", content: part.image_url.url });
34
- }
35
- else if (part.type === "input_audio") {
36
- parts.push({
37
- type: "audio",
38
- content: "[REDACTED_BINARY_DATA]",
39
- format: part.input_audio.format,
40
- });
41
- }
42
- else {
43
- parts.push({
44
- type: "file",
45
- // FUTURE: optionally expose safe metadata without raw binary payloads.
46
- content: part.file.filename ?? "[REDACTED_BINARY_DATA]",
47
- media_type: part.file.media_type,
48
- });
49
- }
50
- }
51
- }
52
- return parts;
53
- }
54
- // FUTURE: remove once Langfuse supports gen_ai.system_instructions
55
- if (message.role === "system") {
56
- return [toTextPart(message.content)];
57
- }
58
- return [];
59
- };
60
- export const getChatGeneralAttributes = (ctx, signalLevel) => {
61
- if (!signalLevel || signalLevel === "off")
62
- return {};
63
- const requestModel = typeof ctx.body?.model === "string" ? ctx.body.model : ctx.modelId;
64
- return {
65
- "gen_ai.operation.name": ctx.operation,
66
- "gen_ai.request.model": requestModel,
67
- "gen_ai.response.model": ctx.resolvedModelId,
68
- "gen_ai.provider.name": ctx.resolvedProviderId,
69
- };
70
- };
71
- export const getChatRequestAttributes = (inputs, signalLevel) => {
72
- if (!signalLevel || signalLevel === "off")
73
- return {};
74
- const attrs = {};
75
- if (inputs.seed !== undefined) {
76
- Object.assign(attrs, { "gen_ai.request.seed": inputs.seed });
77
- }
78
- if (signalLevel !== "required") {
79
- Object.assign(attrs, {
80
- "gen_ai.request.stream": inputs.stream,
81
- "gen_ai.request.frequency_penalty": inputs.frequency_penalty,
82
- "gen_ai.request.max_tokens": inputs.max_completion_tokens,
83
- "gen_ai.request.presence_penalty": inputs.presence_penalty,
84
- "gen_ai.request.stop_sequences": inputs.stop
85
- ? Array.isArray(inputs.stop)
86
- ? inputs.stop
87
- : [inputs.stop]
88
- : undefined,
89
- "gen_ai.request.temperature": inputs.temperature,
90
- "gen_ai.request.top_p": inputs.top_p,
91
- });
92
- }
93
- if (signalLevel === "full") {
94
- Object.assign(attrs, {
95
- // FUTURE: move system instructions from messages to here
96
- // blocker: https://github.com/langfuse/langfuse/issues/11607
97
- // "gen_ai.system_instructions": inputs.messages
98
- // .filter((m) => m.role === "system")
99
- // .map((m) => JSON.stringify(toTextPart(m.content))),
100
- "gen_ai.input.messages": inputs.messages
101
- //.filter((m) => m.role !== "system")
102
- .map((m) => JSON.stringify({ role: m.role, parts: toMessageParts(m) })),
103
- "gen_ai.tool.definitions": JSON.stringify(inputs.tools),
104
- });
105
- }
106
- return attrs;
107
- };
108
- export const getChatResponseAttributes = (completions, signalLevel) => {
109
- if (!signalLevel || signalLevel === "off")
110
- return {};
111
- const attrs = {
112
- "gen_ai.response.id": completions.id,
113
- };
114
- if (signalLevel !== "required") {
115
- Object.assign(attrs, {
116
- "gen_ai.response.finish_reasons": completions.choices?.map((c) => c.finish_reason),
117
- "gen_ai.usage.total_tokens": completions.usage?.total_tokens,
118
- "gen_ai.usage.input_tokens": completions.usage?.prompt_tokens,
119
- "gen_ai.usage.cached_tokens": completions.usage?.prompt_tokens_details?.cached_tokens,
120
- "gen_ai.usage.output_tokens": completions.usage?.completion_tokens,
121
- "gen_ai.usage.reasoning_tokens": completions.usage?.completion_tokens_details?.reasoning_tokens,
122
- });
123
- }
124
- if (signalLevel === "full") {
125
- Object.assign(attrs, {
126
- "gen_ai.output.messages": completions.choices?.map((c) => JSON.stringify({
127
- role: c.message.role,
128
- parts: toMessageParts(c.message),
129
- finish_reason: c.finish_reason,
130
- })),
131
- });
132
- }
133
- return attrs;
134
- };