@tanstack/ai-gemini 0.10.3 → 0.10.5

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,14 @@ 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
+ parentRunId: options.parentRunId
124
+ };
126
125
  }
127
126
  if (chunk.candidates?.[0]?.content?.parts) {
128
127
  const parts = chunk.candidates[0].content.parts;
@@ -133,80 +132,80 @@ class GeminiTextAdapter extends BaseTextAdapter {
133
132
  hasEmittedStepStarted = true;
134
133
  stepId = generateId(this.name);
135
134
  reasoningMessageId = generateId(this.name);
136
- yield asChunk({
137
- type: "REASONING_START",
135
+ yield {
136
+ type: EventType.REASONING_START,
138
137
  messageId: reasoningMessageId,
139
138
  model,
140
- timestamp
141
- });
142
- yield asChunk({
143
- type: "REASONING_MESSAGE_START",
139
+ timestamp: Date.now()
140
+ };
141
+ yield {
142
+ type: EventType.REASONING_MESSAGE_START,
144
143
  messageId: reasoningMessageId,
145
144
  role: "reasoning",
146
145
  model,
147
- timestamp
148
- });
149
- yield asChunk({
150
- type: "STEP_STARTED",
146
+ timestamp: Date.now()
147
+ };
148
+ yield {
149
+ type: EventType.STEP_STARTED,
151
150
  stepName: stepId,
152
151
  stepId,
153
152
  model,
154
- timestamp,
153
+ timestamp: Date.now(),
155
154
  stepType: "thinking"
156
- });
155
+ };
157
156
  }
158
157
  accumulatedThinking += part.text;
159
- yield asChunk({
160
- type: "REASONING_MESSAGE_CONTENT",
158
+ yield {
159
+ type: EventType.REASONING_MESSAGE_CONTENT,
161
160
  messageId: reasoningMessageId,
162
161
  delta: part.text,
163
162
  model,
164
- timestamp
165
- });
166
- yield asChunk({
167
- type: "STEP_FINISHED",
163
+ timestamp: Date.now()
164
+ };
165
+ yield {
166
+ type: EventType.STEP_FINISHED,
168
167
  stepName: stepId || generateId(this.name),
169
168
  stepId: stepId || generateId(this.name),
170
169
  model,
171
- timestamp,
170
+ timestamp: Date.now(),
172
171
  delta: part.text,
173
172
  content: accumulatedThinking
174
- });
173
+ };
175
174
  } else if (part.text.trim()) {
176
175
  if (reasoningMessageId && !hasClosedReasoning) {
177
176
  hasClosedReasoning = true;
178
- yield asChunk({
179
- type: "REASONING_MESSAGE_END",
177
+ yield {
178
+ type: EventType.REASONING_MESSAGE_END,
180
179
  messageId: reasoningMessageId,
181
180
  model,
182
- timestamp
183
- });
184
- yield asChunk({
185
- type: "REASONING_END",
181
+ timestamp: Date.now()
182
+ };
183
+ yield {
184
+ type: EventType.REASONING_END,
186
185
  messageId: reasoningMessageId,
187
186
  model,
188
- timestamp
189
- });
187
+ timestamp: Date.now()
188
+ };
190
189
  }
191
190
  if (!hasEmittedTextMessageStart) {
192
191
  hasEmittedTextMessageStart = true;
193
- yield asChunk({
194
- type: "TEXT_MESSAGE_START",
192
+ yield {
193
+ type: EventType.TEXT_MESSAGE_START,
195
194
  messageId,
196
195
  model,
197
- timestamp,
196
+ timestamp: Date.now(),
198
197
  role: "assistant"
199
- });
198
+ };
200
199
  }
201
200
  accumulatedContent += part.text;
202
- yield asChunk({
203
- type: "TEXT_MESSAGE_CONTENT",
201
+ yield {
202
+ type: EventType.TEXT_MESSAGE_CONTENT,
204
203
  messageId,
205
204
  model,
206
- timestamp,
205
+ timestamp: Date.now(),
207
206
  delta: part.text,
208
207
  content: accumulatedContent
209
- });
208
+ };
210
209
  }
211
210
  }
212
211
  const functionCall = part.functionCall;
@@ -239,51 +238,51 @@ class GeminiTextAdapter extends BaseTextAdapter {
239
238
  }
240
239
  if (!toolCallData.started) {
241
240
  toolCallData.started = true;
242
- yield asChunk({
243
- type: "TOOL_CALL_START",
241
+ yield {
242
+ type: EventType.TOOL_CALL_START,
244
243
  toolCallId,
245
244
  toolCallName: toolCallData.name,
246
245
  toolName: toolCallData.name,
247
246
  model,
248
- timestamp,
247
+ timestamp: Date.now(),
249
248
  index: toolCallData.index,
250
249
  ...toolCallData.thoughtSignature && {
251
250
  metadata: {
252
251
  thoughtSignature: toolCallData.thoughtSignature
253
252
  }
254
253
  }
255
- });
254
+ };
256
255
  }
257
- yield asChunk({
258
- type: "TOOL_CALL_ARGS",
256
+ yield {
257
+ type: EventType.TOOL_CALL_ARGS,
259
258
  toolCallId,
260
259
  model,
261
- timestamp,
260
+ timestamp: Date.now(),
262
261
  delta: toolCallData.args,
263
262
  args: toolCallData.args
264
- });
263
+ };
265
264
  }
266
265
  }
267
266
  } else if (chunk.data && chunk.data.trim()) {
268
267
  if (!hasEmittedTextMessageStart) {
269
268
  hasEmittedTextMessageStart = true;
270
- yield asChunk({
271
- type: "TEXT_MESSAGE_START",
269
+ yield {
270
+ type: EventType.TEXT_MESSAGE_START,
272
271
  messageId,
273
272
  model,
274
- timestamp,
273
+ timestamp: Date.now(),
275
274
  role: "assistant"
276
- });
275
+ };
277
276
  }
278
277
  accumulatedContent += chunk.data;
279
- yield asChunk({
280
- type: "TEXT_MESSAGE_CONTENT",
278
+ yield {
279
+ type: EventType.TEXT_MESSAGE_CONTENT,
281
280
  messageId,
282
281
  model,
283
- timestamp,
282
+ timestamp: Date.now(),
284
283
  delta: chunk.data,
285
284
  content: accumulatedContent
286
- });
285
+ };
287
286
  }
288
287
  if (chunk.candidates?.[0]?.finishReason) {
289
288
  const finishReason = chunk.candidates[0].finishReason;
@@ -301,15 +300,15 @@ class GeminiTextAdapter extends BaseTextAdapter {
301
300
  index: nextToolIndex++,
302
301
  started: true
303
302
  });
304
- yield asChunk({
305
- type: "TOOL_CALL_START",
303
+ yield {
304
+ type: EventType.TOOL_CALL_START,
306
305
  toolCallId,
307
306
  toolCallName: functionCall.name || "",
308
307
  toolName: functionCall.name || "",
309
308
  model,
310
- timestamp,
309
+ timestamp: Date.now(),
311
310
  index: nextToolIndex - 1
312
- });
311
+ };
313
312
  let parsedInput = {};
314
313
  try {
315
314
  const parsed = typeof functionArgs === "string" ? JSON.parse(functionArgs) : functionArgs;
@@ -317,15 +316,15 @@ class GeminiTextAdapter extends BaseTextAdapter {
317
316
  } catch {
318
317
  parsedInput = {};
319
318
  }
320
- yield asChunk({
321
- type: "TOOL_CALL_END",
319
+ yield {
320
+ type: EventType.TOOL_CALL_END,
322
321
  toolCallId,
323
322
  toolCallName: functionCall.name || "",
324
323
  toolName: functionCall.name || "",
325
324
  model,
326
- timestamp,
325
+ timestamp: Date.now(),
327
326
  input: parsedInput
328
- });
327
+ };
329
328
  }
330
329
  }
331
330
  }
@@ -338,69 +337,69 @@ class GeminiTextAdapter extends BaseTextAdapter {
338
337
  } catch {
339
338
  parsedInput = {};
340
339
  }
341
- yield asChunk({
342
- type: "TOOL_CALL_END",
340
+ yield {
341
+ type: EventType.TOOL_CALL_END,
343
342
  toolCallId,
344
343
  toolCallName: toolCallData.name,
345
344
  toolName: toolCallData.name,
346
345
  model,
347
- timestamp,
346
+ timestamp: Date.now(),
348
347
  input: parsedInput
349
- });
348
+ };
350
349
  }
351
350
  if (toolCallMap.size > 0) {
352
351
  hasEmittedTextMessageStart = false;
353
352
  }
354
353
  if (finishReason === FinishReason.MAX_TOKENS) {
355
- yield asChunk({
356
- type: "RUN_ERROR",
354
+ yield {
355
+ type: EventType.RUN_ERROR,
357
356
  runId,
358
357
  model,
359
- timestamp,
358
+ timestamp: Date.now(),
360
359
  message: "The response was cut off because the maximum token limit was reached.",
361
360
  code: "max_tokens",
362
361
  error: {
363
362
  message: "The response was cut off because the maximum token limit was reached.",
364
363
  code: "max_tokens"
365
364
  }
366
- });
365
+ };
367
366
  }
368
367
  if (reasoningMessageId && !hasClosedReasoning) {
369
368
  hasClosedReasoning = true;
370
- yield asChunk({
371
- type: "REASONING_MESSAGE_END",
369
+ yield {
370
+ type: EventType.REASONING_MESSAGE_END,
372
371
  messageId: reasoningMessageId,
373
372
  model,
374
- timestamp
375
- });
376
- yield asChunk({
377
- type: "REASONING_END",
373
+ timestamp: Date.now()
374
+ };
375
+ yield {
376
+ type: EventType.REASONING_END,
378
377
  messageId: reasoningMessageId,
379
378
  model,
380
- timestamp
381
- });
379
+ timestamp: Date.now()
380
+ };
382
381
  }
383
382
  if (hasEmittedTextMessageStart) {
384
- yield asChunk({
385
- type: "TEXT_MESSAGE_END",
383
+ yield {
384
+ type: EventType.TEXT_MESSAGE_END,
386
385
  messageId,
387
386
  model,
388
- timestamp
389
- });
387
+ timestamp: Date.now()
388
+ };
390
389
  }
391
- yield asChunk({
392
- type: "RUN_FINISHED",
390
+ yield {
391
+ type: EventType.RUN_FINISHED,
393
392
  runId,
394
393
  threadId,
395
394
  model,
396
- timestamp,
395
+ timestamp: Date.now(),
397
396
  finishReason: toolCallMap.size > 0 ? "tool_calls" : "stop",
398
397
  usage: chunk.usageMetadata ? {
399
398
  promptTokens: chunk.usageMetadata.promptTokenCount ?? 0,
400
399
  completionTokens: chunk.usageMetadata.candidatesTokenCount ?? 0,
401
400
  totalTokens: chunk.usageMetadata.totalTokenCount ?? 0
402
401
  } : void 0
403
- });
402
+ };
404
403
  }
405
404
  }
406
405
  }