@tanstack/ai-gemini 0.10.3 → 0.10.4

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,58 +1,33 @@
1
+ import { ChatStreamSummarizeAdapter, InferTextProviderOptions } from '@tanstack/ai/adapters';
2
+ import { GeminiTextAdapter } from './text.js';
3
+ import { GEMINI_MODELS } from '../model-meta.js';
1
4
  import { GeminiClientConfig } from '../utils.js';
2
- import { SummarizeAdapter } from '@tanstack/ai/adapters';
3
- import { StreamChunk, SummarizationOptions, SummarizationResult } from '@tanstack/ai';
4
5
  /**
5
6
  * Configuration for Gemini summarize adapter
6
7
  */
7
8
  export interface GeminiSummarizeConfig extends GeminiClientConfig {
8
9
  }
10
+ export type GeminiSummarizeModel = (typeof GEMINI_MODELS)[number];
9
11
  /**
10
- * Available Gemini models for summarization
12
+ * Creates a Gemini summarize adapter with explicit API key and model.
13
+ *
14
+ * Note: keeps the historical (apiKey, model, config) argument order to
15
+ * avoid breaking existing callers.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * const adapter = createGeminiSummarize('AIza...', 'gemini-2.0-flash');
20
+ * ```
11
21
  */
12
- export declare const GeminiSummarizeModels: readonly ["gemini-3.1-flash-lite-preview", "gemini-2.0-flash", "gemini-1.5-flash", "gemini-1.5-pro", "gemini-2.0-flash-lite"];
13
- export type GeminiSummarizeModel = (typeof GeminiSummarizeModels)[number];
22
+ export declare function createGeminiSummarize<TModel extends GeminiSummarizeModel>(apiKey: string, model: TModel, config?: Omit<GeminiSummarizeConfig, 'apiKey'>): ChatStreamSummarizeAdapter<TModel, InferTextProviderOptions<GeminiTextAdapter<TModel>>>;
14
23
  /**
15
- * Provider-specific options for Gemini summarization
24
+ * Creates a Gemini summarize adapter with API key from `GOOGLE_API_KEY` /
25
+ * `GEMINI_API_KEY` environment variables.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const adapter = geminiSummarize('gemini-2.0-flash');
30
+ * await summarize({ adapter, text: 'Long article text...' });
31
+ * ```
16
32
  */
17
- export interface GeminiSummarizeProviderOptions {
18
- /** Generation configuration */
19
- generationConfig?: {
20
- temperature?: number;
21
- topP?: number;
22
- topK?: number;
23
- maxOutputTokens?: number;
24
- stopSequences?: Array<string>;
25
- };
26
- /** Safety settings */
27
- safetySettings?: Array<{
28
- category: string;
29
- threshold: string;
30
- }>;
31
- }
32
- export interface GeminiSummarizeAdapterOptions {
33
- }
34
- /**
35
- * Gemini Summarize Adapter
36
- * A tree-shakeable summarization adapter for Google Gemini
37
- */
38
- export declare class GeminiSummarizeAdapter<TModel extends GeminiSummarizeModel> implements SummarizeAdapter<TModel, GeminiSummarizeProviderOptions> {
39
- readonly kind: "summarize";
40
- readonly name: "gemini";
41
- readonly model: TModel;
42
- '~types': {
43
- providerOptions: GeminiSummarizeProviderOptions;
44
- };
45
- private client;
46
- constructor(config: GeminiSummarizeConfig, model: TModel);
47
- summarize(options: SummarizationOptions): Promise<SummarizationResult>;
48
- summarizeStream(options: SummarizationOptions): AsyncIterable<StreamChunk>;
49
- private getFormatInstructions;
50
- }
51
- /**
52
- * Creates a Gemini summarize adapter with explicit API key and model
53
- */
54
- export declare function createGeminiSummarize<TModel extends GeminiSummarizeModel>(apiKey: string, model: TModel, config?: Omit<GeminiSummarizeConfig, 'apiKey'>): GeminiSummarizeAdapter<TModel>;
55
- /**
56
- * Creates a Gemini summarize adapter with API key from environment and required model
57
- */
58
- export declare function geminiSummarize<TModel extends GeminiSummarizeModel>(model: TModel, config?: Omit<GeminiSummarizeConfig, 'apiKey'>): GeminiSummarizeAdapter<TModel>;
33
+ export declare function geminiSummarize<TModel extends GeminiSummarizeModel>(model: TModel, config?: Omit<GeminiSummarizeConfig, 'apiKey'>): ChatStreamSummarizeAdapter<TModel, InferTextProviderOptions<GeminiTextAdapter<TModel>>>;
@@ -1,167 +1,17 @@
1
- import { FinishReason } from "@google/genai";
2
- import { createGeminiClient, generateId, getGeminiApiKeyFromEnv } from "../utils/client.js";
3
- const asChunk = (chunk) => chunk;
4
- const GeminiSummarizeModels = [
5
- "gemini-3.1-flash-lite-preview",
6
- "gemini-2.0-flash",
7
- "gemini-1.5-flash",
8
- "gemini-1.5-pro",
9
- "gemini-2.0-flash-lite"
10
- ];
11
- class GeminiSummarizeAdapter {
12
- constructor(config, model) {
13
- this.kind = "summarize";
14
- this.name = "gemini";
15
- this.client = createGeminiClient(config);
16
- this.model = model;
17
- }
18
- async summarize(options) {
19
- const { logger } = options;
20
- const model = options.model;
21
- logger.request(`activity=summarize provider=gemini`, {
22
- provider: "gemini",
23
- model
24
- });
25
- const formatInstructions = this.getFormatInstructions(options.style);
26
- const lengthInstructions = options.maxLength ? ` Keep the summary under ${options.maxLength} tokens.` : "";
27
- const systemPrompt = `You are a helpful assistant that summarizes text. ${formatInstructions}${lengthInstructions}`;
28
- try {
29
- const response = await this.client.models.generateContent({
30
- model,
31
- contents: [
32
- {
33
- role: "user",
34
- parts: [
35
- { text: `Please summarize the following:
36
-
37
- ${options.text}` }
38
- ]
39
- }
40
- ],
41
- config: {
42
- systemInstruction: systemPrompt
43
- }
44
- });
45
- const summary = response.text ?? "";
46
- const inputTokens = response.usageMetadata?.promptTokenCount ?? 0;
47
- const outputTokens = response.usageMetadata?.candidatesTokenCount ?? 0;
48
- return {
49
- id: generateId("sum"),
50
- model,
51
- summary,
52
- usage: {
53
- promptTokens: inputTokens,
54
- completionTokens: outputTokens,
55
- totalTokens: inputTokens + outputTokens
56
- }
57
- };
58
- } catch (error) {
59
- logger.errors("gemini.summarize fatal", {
60
- error,
61
- source: "gemini.summarize"
62
- });
63
- throw error;
64
- }
65
- }
66
- async *summarizeStream(options) {
67
- const { logger } = options;
68
- const model = options.model;
69
- const id = generateId("sum");
70
- let accumulatedContent = "";
71
- let inputTokens = 0;
72
- let outputTokens = 0;
73
- const formatInstructions = this.getFormatInstructions(options.style);
74
- const lengthInstructions = options.maxLength ? ` Keep the summary under ${options.maxLength} words.` : "";
75
- const systemPrompt = `You are a helpful assistant that summarizes text. ${formatInstructions}${lengthInstructions}`;
76
- logger.request(`activity=summarize provider=gemini`, {
77
- provider: "gemini",
78
- model,
79
- stream: true
80
- });
81
- try {
82
- const result = await this.client.models.generateContentStream({
83
- model,
84
- contents: [
85
- {
86
- role: "user",
87
- parts: [
88
- { text: `Please summarize the following:
89
-
90
- ${options.text}` }
91
- ]
92
- }
93
- ],
94
- config: {
95
- systemInstruction: systemPrompt
96
- }
97
- });
98
- for await (const chunk of result) {
99
- logger.provider(`provider=gemini`, { chunk });
100
- if (chunk.usageMetadata) {
101
- inputTokens = chunk.usageMetadata.promptTokenCount ?? inputTokens;
102
- outputTokens = chunk.usageMetadata.candidatesTokenCount ?? outputTokens;
103
- }
104
- if (chunk.candidates?.[0]?.content?.parts) {
105
- for (const part of chunk.candidates[0].content.parts) {
106
- if (part.text) {
107
- accumulatedContent += part.text;
108
- yield asChunk({
109
- type: "TEXT_MESSAGE_CONTENT",
110
- messageId: id,
111
- model,
112
- timestamp: Date.now(),
113
- delta: part.text,
114
- content: accumulatedContent
115
- });
116
- }
117
- }
118
- }
119
- const finishReason = chunk.candidates?.[0]?.finishReason;
120
- if (finishReason === FinishReason.STOP || finishReason === FinishReason.MAX_TOKENS || finishReason === FinishReason.SAFETY) {
121
- yield asChunk({
122
- type: "RUN_FINISHED",
123
- runId: id,
124
- model,
125
- timestamp: Date.now(),
126
- finishReason: finishReason === FinishReason.STOP ? "stop" : finishReason === FinishReason.MAX_TOKENS ? "length" : "content_filter",
127
- usage: {
128
- promptTokens: inputTokens,
129
- completionTokens: outputTokens,
130
- totalTokens: inputTokens + outputTokens
131
- }
132
- });
133
- }
134
- }
135
- } catch (error) {
136
- logger.errors("gemini.summarize fatal", {
137
- error,
138
- source: "gemini.summarize"
139
- });
140
- throw error;
141
- }
142
- }
143
- getFormatInstructions(style) {
144
- switch (style) {
145
- case "bullet-points":
146
- return "Provide the summary as bullet points.";
147
- case "concise":
148
- return "Provide a very brief one or two sentence summary.";
149
- case "paragraph":
150
- default:
151
- return "Provide the summary in paragraph form.";
152
- }
153
- }
154
- }
1
+ import { ChatStreamSummarizeAdapter } from "@tanstack/ai/adapters";
2
+ import { getGeminiApiKeyFromEnv } from "../utils/client.js";
3
+ import { GeminiTextAdapter } from "./text.js";
155
4
  function createGeminiSummarize(apiKey, model, config) {
156
- return new GeminiSummarizeAdapter({ ...config, apiKey }, model);
5
+ return new ChatStreamSummarizeAdapter(
6
+ new GeminiTextAdapter({ ...config, apiKey }, model),
7
+ model,
8
+ "gemini"
9
+ );
157
10
  }
158
11
  function geminiSummarize(model, config) {
159
- const apiKey = getGeminiApiKeyFromEnv();
160
- return new GeminiSummarizeAdapter({ ...config, apiKey }, model);
12
+ return createGeminiSummarize(getGeminiApiKeyFromEnv(), model, config);
161
13
  }
162
14
  export {
163
- GeminiSummarizeAdapter,
164
- GeminiSummarizeModels,
165
15
  createGeminiSummarize,
166
16
  geminiSummarize
167
17
  };
@@ -1 +1 @@
1
- {"version":3,"file":"summarize.js","sources":["../../../src/adapters/summarize.ts"],"sourcesContent":["import { FinishReason } from '@google/genai'\nimport {\n createGeminiClient,\n generateId,\n getGeminiApiKeyFromEnv,\n} from '../utils'\nimport type { GoogleGenAI } from '@google/genai'\nimport type { GeminiClientConfig } from '../utils'\nimport type { SummarizeAdapter } from '@tanstack/ai/adapters'\nimport type {\n StreamChunk,\n SummarizationOptions,\n SummarizationResult,\n} from '@tanstack/ai'\n\n/** Cast an event object to StreamChunk. */\nconst asChunk = (chunk: Record<string, unknown>) =>\n chunk as unknown as StreamChunk\n\n/**\n * Configuration for Gemini summarize adapter\n */\nexport interface GeminiSummarizeConfig extends GeminiClientConfig {}\n/**\n * Available Gemini models for summarization\n */\nexport const GeminiSummarizeModels = [\n 'gemini-3.1-flash-lite-preview',\n 'gemini-2.0-flash',\n 'gemini-1.5-flash',\n 'gemini-1.5-pro',\n 'gemini-2.0-flash-lite',\n] as const\n\nexport type GeminiSummarizeModel = (typeof GeminiSummarizeModels)[number]\n\n/**\n * Provider-specific options for Gemini summarization\n */\nexport interface GeminiSummarizeProviderOptions {\n /** Generation configuration */\n generationConfig?: {\n temperature?: number\n topP?: number\n topK?: number\n maxOutputTokens?: number\n stopSequences?: Array<string>\n }\n /** Safety settings */\n safetySettings?: Array<{\n category: string\n threshold: string\n }>\n}\n\nexport interface GeminiSummarizeAdapterOptions {\n // Additional adapter options can be added here\n}\n\n/**\n * Gemini Summarize Adapter\n * A tree-shakeable summarization adapter for Google Gemini\n */\nexport class GeminiSummarizeAdapter<\n TModel extends GeminiSummarizeModel,\n> implements SummarizeAdapter<TModel, GeminiSummarizeProviderOptions> {\n readonly kind = 'summarize' as const\n readonly name = 'gemini' as const\n readonly model: TModel\n\n // Type-only property - never assigned at runtime\n declare '~types': {\n providerOptions: GeminiSummarizeProviderOptions\n }\n\n private client: GoogleGenAI\n\n constructor(config: GeminiSummarizeConfig, model: TModel) {\n this.client = createGeminiClient(config)\n this.model = model\n }\n\n async summarize(options: SummarizationOptions): Promise<SummarizationResult> {\n const { logger } = options\n const model = options.model\n\n logger.request(`activity=summarize provider=gemini`, {\n provider: 'gemini',\n model,\n })\n\n // Build the system prompt based on format\n const formatInstructions = this.getFormatInstructions(options.style)\n const lengthInstructions = options.maxLength\n ? ` Keep the summary under ${options.maxLength} tokens.`\n : ''\n\n const systemPrompt = `You are a helpful assistant that summarizes text. ${formatInstructions}${lengthInstructions}`\n\n try {\n const response = await this.client.models.generateContent({\n model,\n contents: [\n {\n role: 'user',\n parts: [\n { text: `Please summarize the following:\\n\\n${options.text}` },\n ],\n },\n ],\n config: {\n systemInstruction: systemPrompt,\n },\n })\n\n const summary = response.text ?? ''\n const inputTokens = response.usageMetadata?.promptTokenCount ?? 0\n const outputTokens = response.usageMetadata?.candidatesTokenCount ?? 0\n\n return {\n id: generateId('sum'),\n model,\n summary,\n usage: {\n promptTokens: inputTokens,\n completionTokens: outputTokens,\n totalTokens: inputTokens + outputTokens,\n },\n }\n } catch (error) {\n logger.errors('gemini.summarize fatal', {\n error,\n source: 'gemini.summarize',\n })\n throw error\n }\n }\n\n async *summarizeStream(\n options: SummarizationOptions,\n ): AsyncIterable<StreamChunk> {\n const { logger } = options\n const model = options.model\n const id = generateId('sum')\n let accumulatedContent = ''\n let inputTokens = 0\n let outputTokens = 0\n\n // Build the system prompt based on format\n const formatInstructions = this.getFormatInstructions(options.style)\n const lengthInstructions = options.maxLength\n ? ` Keep the summary under ${options.maxLength} words.`\n : ''\n\n const systemPrompt = `You are a helpful assistant that summarizes text. ${formatInstructions}${lengthInstructions}`\n\n logger.request(`activity=summarize provider=gemini`, {\n provider: 'gemini',\n model,\n stream: true,\n })\n\n try {\n const result = await this.client.models.generateContentStream({\n model,\n contents: [\n {\n role: 'user',\n parts: [\n { text: `Please summarize the following:\\n\\n${options.text}` },\n ],\n },\n ],\n config: {\n systemInstruction: systemPrompt,\n },\n })\n\n for await (const chunk of result) {\n logger.provider(`provider=gemini`, { chunk })\n // Track usage metadata\n if (chunk.usageMetadata) {\n inputTokens = chunk.usageMetadata.promptTokenCount ?? inputTokens\n outputTokens =\n chunk.usageMetadata.candidatesTokenCount ?? outputTokens\n }\n\n if (chunk.candidates?.[0]?.content?.parts) {\n for (const part of chunk.candidates[0].content.parts) {\n if (part.text) {\n accumulatedContent += part.text\n yield asChunk({\n type: 'TEXT_MESSAGE_CONTENT',\n messageId: id,\n model,\n timestamp: Date.now(),\n delta: part.text,\n content: accumulatedContent,\n })\n }\n }\n }\n\n // Check for finish reason\n const finishReason = chunk.candidates?.[0]?.finishReason\n if (\n finishReason === FinishReason.STOP ||\n finishReason === FinishReason.MAX_TOKENS ||\n finishReason === FinishReason.SAFETY\n ) {\n yield asChunk({\n type: 'RUN_FINISHED',\n runId: id,\n model,\n timestamp: Date.now(),\n finishReason:\n finishReason === FinishReason.STOP\n ? 'stop'\n : finishReason === FinishReason.MAX_TOKENS\n ? 'length'\n : 'content_filter',\n usage: {\n promptTokens: inputTokens,\n completionTokens: outputTokens,\n totalTokens: inputTokens + outputTokens,\n },\n })\n }\n }\n } catch (error) {\n logger.errors('gemini.summarize fatal', {\n error,\n source: 'gemini.summarize',\n })\n throw error\n }\n }\n\n private getFormatInstructions(\n style?: 'paragraph' | 'bullet-points' | 'concise',\n ): string {\n switch (style) {\n case 'bullet-points':\n return 'Provide the summary as bullet points.'\n case 'concise':\n return 'Provide a very brief one or two sentence summary.'\n case 'paragraph':\n default:\n return 'Provide the summary in paragraph form.'\n }\n }\n}\n\n/**\n * Creates a Gemini summarize adapter with explicit API key and model\n */\nexport function createGeminiSummarize<TModel extends GeminiSummarizeModel>(\n apiKey: string,\n model: TModel,\n config?: Omit<GeminiSummarizeConfig, 'apiKey'>,\n): GeminiSummarizeAdapter<TModel> {\n return new GeminiSummarizeAdapter({ ...config, apiKey }, model)\n}\n\n/**\n * Creates a Gemini summarize adapter with API key from environment and required model\n */\nexport function geminiSummarize<TModel extends GeminiSummarizeModel>(\n model: TModel,\n config?: Omit<GeminiSummarizeConfig, 'apiKey'>,\n): GeminiSummarizeAdapter<TModel> {\n const apiKey = getGeminiApiKeyFromEnv()\n return new GeminiSummarizeAdapter({ ...config, apiKey }, model)\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"summarize.js","sources":["../../../src/adapters/summarize.ts"],"sourcesContent":["import { ChatStreamSummarizeAdapter } from '@tanstack/ai/adapters'\nimport { getGeminiApiKeyFromEnv } from '../utils'\nimport { GeminiTextAdapter } from './text'\nimport type { InferTextProviderOptions } from '@tanstack/ai/adapters'\nimport type { GEMINI_MODELS } from '../model-meta'\nimport type { GeminiClientConfig } from '../utils'\n\n/**\n * Configuration for Gemini summarize adapter\n */\nexport interface GeminiSummarizeConfig extends GeminiClientConfig {}\n\nexport type GeminiSummarizeModel = (typeof GEMINI_MODELS)[number]\n\n/**\n * Creates a Gemini summarize adapter with explicit API key and model.\n *\n * Note: keeps the historical (apiKey, model, config) argument order to\n * avoid breaking existing callers.\n *\n * @example\n * ```typescript\n * const adapter = createGeminiSummarize('AIza...', 'gemini-2.0-flash');\n * ```\n */\nexport function createGeminiSummarize<TModel extends GeminiSummarizeModel>(\n apiKey: string,\n model: TModel,\n config?: Omit<GeminiSummarizeConfig, 'apiKey'>,\n): ChatStreamSummarizeAdapter<\n TModel,\n InferTextProviderOptions<GeminiTextAdapter<TModel>>\n> {\n return new ChatStreamSummarizeAdapter(\n new GeminiTextAdapter({ ...config, apiKey }, model),\n model,\n 'gemini',\n )\n}\n\n/**\n * Creates a Gemini summarize adapter with API key from `GOOGLE_API_KEY` /\n * `GEMINI_API_KEY` environment variables.\n *\n * @example\n * ```typescript\n * const adapter = geminiSummarize('gemini-2.0-flash');\n * await summarize({ adapter, text: 'Long article text...' });\n * ```\n */\nexport function geminiSummarize<TModel extends GeminiSummarizeModel>(\n model: TModel,\n config?: Omit<GeminiSummarizeConfig, 'apiKey'>,\n): ChatStreamSummarizeAdapter<\n TModel,\n InferTextProviderOptions<GeminiTextAdapter<TModel>>\n> {\n return createGeminiSummarize(getGeminiApiKeyFromEnv(), model, config)\n}\n"],"names":[],"mappings":";;;AAyBO,SAAS,sBACd,QACA,OACA,QAIA;AACA,SAAO,IAAI;AAAA,IACT,IAAI,kBAAkB,EAAE,GAAG,QAAQ,OAAA,GAAU,KAAK;AAAA,IAClD;AAAA,IACA;AAAA,EAAA;AAEJ;AAYO,SAAS,gBACd,OACA,QAIA;AACA,SAAO,sBAAsB,0BAA0B,OAAO,MAAM;AACtE;"}
@@ -1,8 +1,8 @@
1
1
  import { FinishReason } from "@google/genai";
2
+ import { EventType } from "@tanstack/ai";
2
3
  import { BaseTextAdapter } from "@tanstack/ai/adapters";
3
4
  import { convertToolsToProviderFormat } from "../tools/tool-converter.js";
4
5
  import { createGeminiClient, generateId, getGeminiApiKeyFromEnv } from "../utils/client.js";
5
- const asChunk = (chunk) => chunk;
6
6
  class GeminiTextAdapter extends BaseTextAdapter {
7
7
  constructor(config, model) {
8
8
  super({}, model);
@@ -21,20 +21,19 @@ class GeminiTextAdapter extends BaseTextAdapter {
21
21
  const result = await this.client.models.generateContentStream(mappedOptions);
22
22
  yield* this.processStreamChunks(result, options, logger);
23
23
  } catch (error) {
24
- const timestamp = Date.now();
25
24
  logger.errors("gemini.chatStream fatal", {
26
25
  error,
27
26
  source: "gemini.chatStream"
28
27
  });
29
- yield asChunk({
30
- type: "RUN_ERROR",
28
+ yield {
29
+ type: EventType.RUN_ERROR,
31
30
  model: options.model,
32
- timestamp,
31
+ timestamp: Date.now(),
33
32
  message: error instanceof Error ? error.message : "An unknown error occurred during the chat stream.",
34
33
  error: {
35
34
  message: error instanceof Error ? error.message : "An unknown error occurred during the chat stream."
36
35
  }
37
- });
36
+ };
38
37
  }
39
38
  }
40
39
  /**
@@ -98,7 +97,6 @@ class GeminiTextAdapter extends BaseTextAdapter {
98
97
  }
99
98
  async *processStreamChunks(result, options, logger) {
100
99
  const model = options.model;
101
- const timestamp = Date.now();
102
100
  let accumulatedContent = "";
103
101
  let accumulatedThinking = "";
104
102
  const toolCallMap = /* @__PURE__ */ new Map();
@@ -116,13 +114,13 @@ class GeminiTextAdapter extends BaseTextAdapter {
116
114
  logger.provider(`provider=gemini`, { chunk });
117
115
  if (!hasEmittedRunStarted) {
118
116
  hasEmittedRunStarted = true;
119
- yield asChunk({
120
- type: "RUN_STARTED",
117
+ yield {
118
+ type: EventType.RUN_STARTED,
121
119
  runId,
122
120
  threadId,
123
121
  model,
124
- timestamp
125
- });
122
+ timestamp: Date.now()
123
+ };
126
124
  }
127
125
  if (chunk.candidates?.[0]?.content?.parts) {
128
126
  const parts = chunk.candidates[0].content.parts;
@@ -133,80 +131,80 @@ class GeminiTextAdapter extends BaseTextAdapter {
133
131
  hasEmittedStepStarted = true;
134
132
  stepId = generateId(this.name);
135
133
  reasoningMessageId = generateId(this.name);
136
- yield asChunk({
137
- type: "REASONING_START",
134
+ yield {
135
+ type: EventType.REASONING_START,
138
136
  messageId: reasoningMessageId,
139
137
  model,
140
- timestamp
141
- });
142
- yield asChunk({
143
- type: "REASONING_MESSAGE_START",
138
+ timestamp: Date.now()
139
+ };
140
+ yield {
141
+ type: EventType.REASONING_MESSAGE_START,
144
142
  messageId: reasoningMessageId,
145
143
  role: "reasoning",
146
144
  model,
147
- timestamp
148
- });
149
- yield asChunk({
150
- type: "STEP_STARTED",
145
+ timestamp: Date.now()
146
+ };
147
+ yield {
148
+ type: EventType.STEP_STARTED,
151
149
  stepName: stepId,
152
150
  stepId,
153
151
  model,
154
- timestamp,
152
+ timestamp: Date.now(),
155
153
  stepType: "thinking"
156
- });
154
+ };
157
155
  }
158
156
  accumulatedThinking += part.text;
159
- yield asChunk({
160
- type: "REASONING_MESSAGE_CONTENT",
157
+ yield {
158
+ type: EventType.REASONING_MESSAGE_CONTENT,
161
159
  messageId: reasoningMessageId,
162
160
  delta: part.text,
163
161
  model,
164
- timestamp
165
- });
166
- yield asChunk({
167
- type: "STEP_FINISHED",
162
+ timestamp: Date.now()
163
+ };
164
+ yield {
165
+ type: EventType.STEP_FINISHED,
168
166
  stepName: stepId || generateId(this.name),
169
167
  stepId: stepId || generateId(this.name),
170
168
  model,
171
- timestamp,
169
+ timestamp: Date.now(),
172
170
  delta: part.text,
173
171
  content: accumulatedThinking
174
- });
172
+ };
175
173
  } else if (part.text.trim()) {
176
174
  if (reasoningMessageId && !hasClosedReasoning) {
177
175
  hasClosedReasoning = true;
178
- yield asChunk({
179
- type: "REASONING_MESSAGE_END",
176
+ yield {
177
+ type: EventType.REASONING_MESSAGE_END,
180
178
  messageId: reasoningMessageId,
181
179
  model,
182
- timestamp
183
- });
184
- yield asChunk({
185
- type: "REASONING_END",
180
+ timestamp: Date.now()
181
+ };
182
+ yield {
183
+ type: EventType.REASONING_END,
186
184
  messageId: reasoningMessageId,
187
185
  model,
188
- timestamp
189
- });
186
+ timestamp: Date.now()
187
+ };
190
188
  }
191
189
  if (!hasEmittedTextMessageStart) {
192
190
  hasEmittedTextMessageStart = true;
193
- yield asChunk({
194
- type: "TEXT_MESSAGE_START",
191
+ yield {
192
+ type: EventType.TEXT_MESSAGE_START,
195
193
  messageId,
196
194
  model,
197
- timestamp,
195
+ timestamp: Date.now(),
198
196
  role: "assistant"
199
- });
197
+ };
200
198
  }
201
199
  accumulatedContent += part.text;
202
- yield asChunk({
203
- type: "TEXT_MESSAGE_CONTENT",
200
+ yield {
201
+ type: EventType.TEXT_MESSAGE_CONTENT,
204
202
  messageId,
205
203
  model,
206
- timestamp,
204
+ timestamp: Date.now(),
207
205
  delta: part.text,
208
206
  content: accumulatedContent
209
- });
207
+ };
210
208
  }
211
209
  }
212
210
  const functionCall = part.functionCall;
@@ -239,51 +237,51 @@ class GeminiTextAdapter extends BaseTextAdapter {
239
237
  }
240
238
  if (!toolCallData.started) {
241
239
  toolCallData.started = true;
242
- yield asChunk({
243
- type: "TOOL_CALL_START",
240
+ yield {
241
+ type: EventType.TOOL_CALL_START,
244
242
  toolCallId,
245
243
  toolCallName: toolCallData.name,
246
244
  toolName: toolCallData.name,
247
245
  model,
248
- timestamp,
246
+ timestamp: Date.now(),
249
247
  index: toolCallData.index,
250
248
  ...toolCallData.thoughtSignature && {
251
249
  metadata: {
252
250
  thoughtSignature: toolCallData.thoughtSignature
253
251
  }
254
252
  }
255
- });
253
+ };
256
254
  }
257
- yield asChunk({
258
- type: "TOOL_CALL_ARGS",
255
+ yield {
256
+ type: EventType.TOOL_CALL_ARGS,
259
257
  toolCallId,
260
258
  model,
261
- timestamp,
259
+ timestamp: Date.now(),
262
260
  delta: toolCallData.args,
263
261
  args: toolCallData.args
264
- });
262
+ };
265
263
  }
266
264
  }
267
265
  } else if (chunk.data && chunk.data.trim()) {
268
266
  if (!hasEmittedTextMessageStart) {
269
267
  hasEmittedTextMessageStart = true;
270
- yield asChunk({
271
- type: "TEXT_MESSAGE_START",
268
+ yield {
269
+ type: EventType.TEXT_MESSAGE_START,
272
270
  messageId,
273
271
  model,
274
- timestamp,
272
+ timestamp: Date.now(),
275
273
  role: "assistant"
276
- });
274
+ };
277
275
  }
278
276
  accumulatedContent += chunk.data;
279
- yield asChunk({
280
- type: "TEXT_MESSAGE_CONTENT",
277
+ yield {
278
+ type: EventType.TEXT_MESSAGE_CONTENT,
281
279
  messageId,
282
280
  model,
283
- timestamp,
281
+ timestamp: Date.now(),
284
282
  delta: chunk.data,
285
283
  content: accumulatedContent
286
- });
284
+ };
287
285
  }
288
286
  if (chunk.candidates?.[0]?.finishReason) {
289
287
  const finishReason = chunk.candidates[0].finishReason;
@@ -301,15 +299,15 @@ class GeminiTextAdapter extends BaseTextAdapter {
301
299
  index: nextToolIndex++,
302
300
  started: true
303
301
  });
304
- yield asChunk({
305
- type: "TOOL_CALL_START",
302
+ yield {
303
+ type: EventType.TOOL_CALL_START,
306
304
  toolCallId,
307
305
  toolCallName: functionCall.name || "",
308
306
  toolName: functionCall.name || "",
309
307
  model,
310
- timestamp,
308
+ timestamp: Date.now(),
311
309
  index: nextToolIndex - 1
312
- });
310
+ };
313
311
  let parsedInput = {};
314
312
  try {
315
313
  const parsed = typeof functionArgs === "string" ? JSON.parse(functionArgs) : functionArgs;
@@ -317,15 +315,15 @@ class GeminiTextAdapter extends BaseTextAdapter {
317
315
  } catch {
318
316
  parsedInput = {};
319
317
  }
320
- yield asChunk({
321
- type: "TOOL_CALL_END",
318
+ yield {
319
+ type: EventType.TOOL_CALL_END,
322
320
  toolCallId,
323
321
  toolCallName: functionCall.name || "",
324
322
  toolName: functionCall.name || "",
325
323
  model,
326
- timestamp,
324
+ timestamp: Date.now(),
327
325
  input: parsedInput
328
- });
326
+ };
329
327
  }
330
328
  }
331
329
  }
@@ -338,69 +336,69 @@ class GeminiTextAdapter extends BaseTextAdapter {
338
336
  } catch {
339
337
  parsedInput = {};
340
338
  }
341
- yield asChunk({
342
- type: "TOOL_CALL_END",
339
+ yield {
340
+ type: EventType.TOOL_CALL_END,
343
341
  toolCallId,
344
342
  toolCallName: toolCallData.name,
345
343
  toolName: toolCallData.name,
346
344
  model,
347
- timestamp,
345
+ timestamp: Date.now(),
348
346
  input: parsedInput
349
- });
347
+ };
350
348
  }
351
349
  if (toolCallMap.size > 0) {
352
350
  hasEmittedTextMessageStart = false;
353
351
  }
354
352
  if (finishReason === FinishReason.MAX_TOKENS) {
355
- yield asChunk({
356
- type: "RUN_ERROR",
353
+ yield {
354
+ type: EventType.RUN_ERROR,
357
355
  runId,
358
356
  model,
359
- timestamp,
357
+ timestamp: Date.now(),
360
358
  message: "The response was cut off because the maximum token limit was reached.",
361
359
  code: "max_tokens",
362
360
  error: {
363
361
  message: "The response was cut off because the maximum token limit was reached.",
364
362
  code: "max_tokens"
365
363
  }
366
- });
364
+ };
367
365
  }
368
366
  if (reasoningMessageId && !hasClosedReasoning) {
369
367
  hasClosedReasoning = true;
370
- yield asChunk({
371
- type: "REASONING_MESSAGE_END",
368
+ yield {
369
+ type: EventType.REASONING_MESSAGE_END,
372
370
  messageId: reasoningMessageId,
373
371
  model,
374
- timestamp
375
- });
376
- yield asChunk({
377
- type: "REASONING_END",
372
+ timestamp: Date.now()
373
+ };
374
+ yield {
375
+ type: EventType.REASONING_END,
378
376
  messageId: reasoningMessageId,
379
377
  model,
380
- timestamp
381
- });
378
+ timestamp: Date.now()
379
+ };
382
380
  }
383
381
  if (hasEmittedTextMessageStart) {
384
- yield asChunk({
385
- type: "TEXT_MESSAGE_END",
382
+ yield {
383
+ type: EventType.TEXT_MESSAGE_END,
386
384
  messageId,
387
385
  model,
388
- timestamp
389
- });
386
+ timestamp: Date.now()
387
+ };
390
388
  }
391
- yield asChunk({
392
- type: "RUN_FINISHED",
389
+ yield {
390
+ type: EventType.RUN_FINISHED,
393
391
  runId,
394
392
  threadId,
395
393
  model,
396
- timestamp,
394
+ timestamp: Date.now(),
397
395
  finishReason: toolCallMap.size > 0 ? "tool_calls" : "stop",
398
396
  usage: chunk.usageMetadata ? {
399
397
  promptTokens: chunk.usageMetadata.promptTokenCount ?? 0,
400
398
  completionTokens: chunk.usageMetadata.candidatesTokenCount ?? 0,
401
399
  totalTokens: chunk.usageMetadata.totalTokenCount ?? 0
402
400
  } : void 0
403
- });
401
+ };
404
402
  }
405
403
  }
406
404
  }