@tanstack/ai-grok 0.6.7 → 0.6.8

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.
@@ -9,16 +9,28 @@ class GrokImageAdapter extends BaseImageAdapter {
9
9
  this.client = createGrokClient(config);
10
10
  }
11
11
  async generateImages(options) {
12
- const { model, prompt, numberOfImages, size } = options;
13
- validatePrompt({ prompt });
14
- validateImageSize(model, size);
15
- validateNumberOfImages(model, numberOfImages);
16
- const request = this.buildRequest(options);
17
- const response = await this.client.images.generate({
18
- ...request,
19
- stream: false
12
+ const { model, prompt, numberOfImages, size, logger } = options;
13
+ logger.request(`activity=generateImage provider=grok model=${this.model}`, {
14
+ provider: "grok",
15
+ model: this.model
20
16
  });
21
- return this.transformResponse(model, response);
17
+ try {
18
+ validatePrompt({ prompt, model });
19
+ validateImageSize(model, size);
20
+ validateNumberOfImages(model, numberOfImages);
21
+ const request = this.buildRequest(options);
22
+ const response = await this.client.images.generate({
23
+ ...request,
24
+ stream: false
25
+ });
26
+ return this.transformResponse(model, response);
27
+ } catch (error) {
28
+ logger.errors("grok.generateImage fatal", {
29
+ error,
30
+ source: "grok.generateImage"
31
+ });
32
+ throw error;
33
+ }
22
34
  }
23
35
  buildRequest(options) {
24
36
  const { model, prompt, numberOfImages, size, modelOptions } = options;
@@ -1 +1 @@
1
- {"version":3,"file":"image.js","sources":["../../../src/adapters/image.ts"],"sourcesContent":["import { BaseImageAdapter } from '@tanstack/ai/adapters'\nimport { createGrokClient, generateId, getGrokApiKeyFromEnv } from '../utils'\nimport {\n validateImageSize,\n validateNumberOfImages,\n validatePrompt,\n} from '../image/image-provider-options'\nimport type { GrokImageModel } from '../model-meta'\nimport type {\n GrokImageModelProviderOptionsByName,\n GrokImageModelSizeByName,\n GrokImageProviderOptions,\n} from '../image/image-provider-options'\nimport type {\n GeneratedImage,\n ImageGenerationOptions,\n ImageGenerationResult,\n} from '@tanstack/ai'\nimport type OpenAI_SDK from 'openai'\nimport type { GrokClientConfig } from '../utils'\n\n/**\n * Configuration for Grok image adapter\n */\nexport interface GrokImageConfig extends GrokClientConfig {}\n\n/**\n * Grok Image Generation Adapter\n *\n * Tree-shakeable adapter for Grok image generation functionality.\n * Supports grok-2-image-1212 model.\n *\n * Features:\n * - Model-specific type-safe provider options\n * - Size validation per model\n * - Number of images validation\n */\nexport class GrokImageAdapter<\n TModel extends GrokImageModel,\n> extends BaseImageAdapter<\n TModel,\n GrokImageProviderOptions,\n GrokImageModelProviderOptionsByName,\n GrokImageModelSizeByName\n> {\n readonly kind = 'image' as const\n readonly name = 'grok' as const\n\n private client: OpenAI_SDK\n\n constructor(config: GrokImageConfig, model: TModel) {\n super({}, model)\n this.client = createGrokClient(config)\n }\n\n async generateImages(\n options: ImageGenerationOptions<GrokImageProviderOptions>,\n ): Promise<ImageGenerationResult> {\n const { model, prompt, numberOfImages, size } = options\n\n // Validate inputs\n validatePrompt({ prompt, model })\n validateImageSize(model, size)\n validateNumberOfImages(model, numberOfImages)\n\n // Build request based on model type\n const request = this.buildRequest(options)\n\n const response = await this.client.images.generate({\n ...request,\n stream: false,\n })\n\n return this.transformResponse(model, response)\n }\n\n private buildRequest(\n options: ImageGenerationOptions<GrokImageProviderOptions>,\n ): OpenAI_SDK.Images.ImageGenerateParams {\n const { model, prompt, numberOfImages, size, modelOptions } = options\n\n return {\n model,\n prompt,\n n: numberOfImages ?? 1,\n size: size as OpenAI_SDK.Images.ImageGenerateParams['size'],\n ...modelOptions,\n }\n }\n\n private transformResponse(\n model: string,\n response: OpenAI_SDK.Images.ImagesResponse,\n ): ImageGenerationResult {\n const images: Array<GeneratedImage> = (response.data ?? []).map((item) => ({\n b64Json: item.b64_json,\n url: item.url,\n revisedPrompt: item.revised_prompt,\n }))\n\n return {\n id: generateId(this.name),\n model,\n images,\n usage: response.usage\n ? {\n inputTokens: response.usage.input_tokens,\n outputTokens: response.usage.output_tokens,\n totalTokens: response.usage.total_tokens,\n }\n : undefined,\n }\n }\n}\n\n/**\n * Creates a Grok image adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'grok-2-image-1212')\n * @param apiKey - Your xAI API key\n * @param config - Optional additional configuration\n * @returns Configured Grok image adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGrokImage('grok-2-image-1212', \"xai-...\");\n *\n * const result = await generateImage({\n * adapter,\n * prompt: 'A cute baby sea otter'\n * });\n * ```\n */\nexport function createGrokImage<TModel extends GrokImageModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GrokImageConfig, 'apiKey'>,\n): GrokImageAdapter<TModel> {\n return new GrokImageAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Grok image adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * Looks for `XAI_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'grok-2-image-1212')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Grok image adapter instance with resolved types\n * @throws Error if XAI_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses XAI_API_KEY from environment\n * const adapter = grokImage('grok-2-image-1212');\n *\n * const result = await generateImage({\n * adapter,\n * prompt: 'A beautiful sunset over mountains'\n * });\n * ```\n */\nexport function grokImage<TModel extends GrokImageModel>(\n model: TModel,\n config?: Omit<GrokImageConfig, 'apiKey'>,\n): GrokImageAdapter<TModel> {\n const apiKey = getGrokApiKeyFromEnv()\n return createGrokImage(model, apiKey, config)\n}\n"],"names":[],"mappings":";;;AAqCO,MAAM,yBAEH,iBAKR;AAAA,EAMA,YAAY,QAAyB,OAAe;AAClD,UAAM,CAAA,GAAI,KAAK;AANjB,SAAS,OAAO;AAChB,SAAS,OAAO;AAMd,SAAK,SAAS,iBAAiB,MAAM;AAAA,EACvC;AAAA,EAEA,MAAM,eACJ,SACgC;AAChC,UAAM,EAAE,OAAO,QAAQ,gBAAgB,SAAS;AAGhD,mBAAe,EAAE,OAAc,CAAC;AAChC,sBAAkB,OAAO,IAAI;AAC7B,2BAAuB,OAAO,cAAc;AAG5C,UAAM,UAAU,KAAK,aAAa,OAAO;AAEzC,UAAM,WAAW,MAAM,KAAK,OAAO,OAAO,SAAS;AAAA,MACjD,GAAG;AAAA,MACH,QAAQ;AAAA,IAAA,CACT;AAED,WAAO,KAAK,kBAAkB,OAAO,QAAQ;AAAA,EAC/C;AAAA,EAEQ,aACN,SACuC;AACvC,UAAM,EAAE,OAAO,QAAQ,gBAAgB,MAAM,iBAAiB;AAE9D,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,GAAG,kBAAkB;AAAA,MACrB;AAAA,MACA,GAAG;AAAA,IAAA;AAAA,EAEP;AAAA,EAEQ,kBACN,OACA,UACuB;AACvB,UAAM,UAAiC,SAAS,QAAQ,CAAA,GAAI,IAAI,CAAC,UAAU;AAAA,MACzE,SAAS,KAAK;AAAA,MACd,KAAK,KAAK;AAAA,MACV,eAAe,KAAK;AAAA,IAAA,EACpB;AAEF,WAAO;AAAA,MACL,IAAI,WAAW,KAAK,IAAI;AAAA,MACxB;AAAA,MACA;AAAA,MACA,OAAO,SAAS,QACZ;AAAA,QACE,aAAa,SAAS,MAAM;AAAA,QAC5B,cAAc,SAAS,MAAM;AAAA,QAC7B,aAAa,SAAS,MAAM;AAAA,MAAA,IAE9B;AAAA,IAAA;AAAA,EAER;AACF;AAqBO,SAAS,gBACd,OACA,QACA,QAC0B;AAC1B,SAAO,IAAI,iBAAiB,EAAE,QAAQ,GAAG,OAAA,GAAU,KAAK;AAC1D;AA0BO,SAAS,UACd,OACA,QAC0B;AAC1B,QAAM,SAAS,qBAAA;AACf,SAAO,gBAAgB,OAAO,QAAQ,MAAM;AAC9C;"}
1
+ {"version":3,"file":"image.js","sources":["../../../src/adapters/image.ts"],"sourcesContent":["import { BaseImageAdapter } from '@tanstack/ai/adapters'\nimport { createGrokClient, generateId, getGrokApiKeyFromEnv } from '../utils'\nimport {\n validateImageSize,\n validateNumberOfImages,\n validatePrompt,\n} from '../image/image-provider-options'\nimport type { GrokImageModel } from '../model-meta'\nimport type {\n GrokImageModelProviderOptionsByName,\n GrokImageModelSizeByName,\n GrokImageProviderOptions,\n} from '../image/image-provider-options'\nimport type {\n GeneratedImage,\n ImageGenerationOptions,\n ImageGenerationResult,\n} from '@tanstack/ai'\nimport type OpenAI_SDK from 'openai'\nimport type { GrokClientConfig } from '../utils'\n\n/**\n * Configuration for Grok image adapter\n */\nexport interface GrokImageConfig extends GrokClientConfig {}\n\n/**\n * Grok Image Generation Adapter\n *\n * Tree-shakeable adapter for Grok image generation functionality.\n * Supports grok-2-image-1212 model.\n *\n * Features:\n * - Model-specific type-safe provider options\n * - Size validation per model\n * - Number of images validation\n */\nexport class GrokImageAdapter<\n TModel extends GrokImageModel,\n> extends BaseImageAdapter<\n TModel,\n GrokImageProviderOptions,\n GrokImageModelProviderOptionsByName,\n GrokImageModelSizeByName\n> {\n readonly kind = 'image' as const\n readonly name = 'grok' as const\n\n private client: OpenAI_SDK\n\n constructor(config: GrokImageConfig, model: TModel) {\n super({}, model)\n this.client = createGrokClient(config)\n }\n\n async generateImages(\n options: ImageGenerationOptions<GrokImageProviderOptions>,\n ): Promise<ImageGenerationResult> {\n const { model, prompt, numberOfImages, size, logger } = options\n\n logger.request(`activity=generateImage provider=grok model=${this.model}`, {\n provider: 'grok',\n model: this.model,\n })\n\n try {\n // Validate inputs\n validatePrompt({ prompt, model })\n validateImageSize(model, size)\n validateNumberOfImages(model, numberOfImages)\n\n // Build request based on model type\n const request = this.buildRequest(options)\n\n const response = await this.client.images.generate({\n ...request,\n stream: false,\n })\n\n return this.transformResponse(model, response)\n } catch (error) {\n logger.errors('grok.generateImage fatal', {\n error,\n source: 'grok.generateImage',\n })\n throw error\n }\n }\n\n private buildRequest(\n options: ImageGenerationOptions<GrokImageProviderOptions>,\n ): OpenAI_SDK.Images.ImageGenerateParams {\n const { model, prompt, numberOfImages, size, modelOptions } = options\n\n return {\n model,\n prompt,\n n: numberOfImages ?? 1,\n size: size as OpenAI_SDK.Images.ImageGenerateParams['size'],\n ...modelOptions,\n }\n }\n\n private transformResponse(\n model: string,\n response: OpenAI_SDK.Images.ImagesResponse,\n ): ImageGenerationResult {\n const images: Array<GeneratedImage> = (response.data ?? []).map((item) => ({\n b64Json: item.b64_json,\n url: item.url,\n revisedPrompt: item.revised_prompt,\n }))\n\n return {\n id: generateId(this.name),\n model,\n images,\n usage: response.usage\n ? {\n inputTokens: response.usage.input_tokens,\n outputTokens: response.usage.output_tokens,\n totalTokens: response.usage.total_tokens,\n }\n : undefined,\n }\n }\n}\n\n/**\n * Creates a Grok image adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'grok-2-image-1212')\n * @param apiKey - Your xAI API key\n * @param config - Optional additional configuration\n * @returns Configured Grok image adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGrokImage('grok-2-image-1212', \"xai-...\");\n *\n * const result = await generateImage({\n * adapter,\n * prompt: 'A cute baby sea otter'\n * });\n * ```\n */\nexport function createGrokImage<TModel extends GrokImageModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GrokImageConfig, 'apiKey'>,\n): GrokImageAdapter<TModel> {\n return new GrokImageAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Grok image adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * Looks for `XAI_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'grok-2-image-1212')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Grok image adapter instance with resolved types\n * @throws Error if XAI_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses XAI_API_KEY from environment\n * const adapter = grokImage('grok-2-image-1212');\n *\n * const result = await generateImage({\n * adapter,\n * prompt: 'A beautiful sunset over mountains'\n * });\n * ```\n */\nexport function grokImage<TModel extends GrokImageModel>(\n model: TModel,\n config?: Omit<GrokImageConfig, 'apiKey'>,\n): GrokImageAdapter<TModel> {\n const apiKey = getGrokApiKeyFromEnv()\n return createGrokImage(model, apiKey, config)\n}\n"],"names":[],"mappings":";;;AAqCO,MAAM,yBAEH,iBAKR;AAAA,EAMA,YAAY,QAAyB,OAAe;AAClD,UAAM,CAAA,GAAI,KAAK;AANjB,SAAS,OAAO;AAChB,SAAS,OAAO;AAMd,SAAK,SAAS,iBAAiB,MAAM;AAAA,EACvC;AAAA,EAEA,MAAM,eACJ,SACgC;AAChC,UAAM,EAAE,OAAO,QAAQ,gBAAgB,MAAM,WAAW;AAExD,WAAO,QAAQ,8CAA8C,KAAK,KAAK,IAAI;AAAA,MACzE,UAAU;AAAA,MACV,OAAO,KAAK;AAAA,IAAA,CACb;AAED,QAAI;AAEF,qBAAe,EAAE,QAAQ,OAAO;AAChC,wBAAkB,OAAO,IAAI;AAC7B,6BAAuB,OAAO,cAAc;AAG5C,YAAM,UAAU,KAAK,aAAa,OAAO;AAEzC,YAAM,WAAW,MAAM,KAAK,OAAO,OAAO,SAAS;AAAA,QACjD,GAAG;AAAA,QACH,QAAQ;AAAA,MAAA,CACT;AAED,aAAO,KAAK,kBAAkB,OAAO,QAAQ;AAAA,IAC/C,SAAS,OAAO;AACd,aAAO,OAAO,4BAA4B;AAAA,QACxC;AAAA,QACA,QAAQ;AAAA,MAAA,CACT;AACD,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEQ,aACN,SACuC;AACvC,UAAM,EAAE,OAAO,QAAQ,gBAAgB,MAAM,iBAAiB;AAE9D,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,GAAG,kBAAkB;AAAA,MACrB;AAAA,MACA,GAAG;AAAA,IAAA;AAAA,EAEP;AAAA,EAEQ,kBACN,OACA,UACuB;AACvB,UAAM,UAAiC,SAAS,QAAQ,CAAA,GAAI,IAAI,CAAC,UAAU;AAAA,MACzE,SAAS,KAAK;AAAA,MACd,KAAK,KAAK;AAAA,MACV,eAAe,KAAK;AAAA,IAAA,EACpB;AAEF,WAAO;AAAA,MACL,IAAI,WAAW,KAAK,IAAI;AAAA,MACxB;AAAA,MACA;AAAA,MACA,OAAO,SAAS,QACZ;AAAA,QACE,aAAa,SAAS,MAAM;AAAA,QAC5B,cAAc,SAAS,MAAM;AAAA,QAC7B,aAAa,SAAS,MAAM;AAAA,MAAA,IAE9B;AAAA,IAAA;AAAA,EAER;AACF;AAqBO,SAAS,gBACd,OACA,QACA,QAC0B;AAC1B,SAAO,IAAI,iBAAiB,EAAE,QAAQ,GAAG,OAAA,GAAU,KAAK;AAC1D;AA0BO,SAAS,UACd,OACA,QAC0B;AAC1B,QAAM,SAAS,qBAAA;AACf,SAAO,gBAAgB,OAAO,QAAQ,MAAM;AAC9C;"}
@@ -9,43 +9,72 @@ class GrokSummarizeAdapter extends BaseSummarizeAdapter {
9
9
  this.textAdapter = new GrokTextAdapter(config, model);
10
10
  }
11
11
  async summarize(options) {
12
+ const { logger } = options;
12
13
  const systemPrompt = this.buildSummarizationPrompt(options);
14
+ logger.request(`activity=summarize provider=grok`, {
15
+ provider: "grok",
16
+ model: options.model
17
+ });
13
18
  let summary = "";
14
19
  const id = "";
15
20
  let model = options.model;
16
21
  let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
17
- for await (const chunk of this.textAdapter.chatStream({
18
- model: options.model,
19
- messages: [{ role: "user", content: options.text }],
20
- systemPrompts: [systemPrompt],
21
- maxTokens: options.maxLength,
22
- temperature: 0.3
23
- })) {
24
- if (chunk.type === "TEXT_MESSAGE_CONTENT") {
25
- if (chunk.content) {
26
- summary = chunk.content;
27
- } else {
28
- summary += chunk.delta;
22
+ try {
23
+ for await (const chunk of this.textAdapter.chatStream({
24
+ model: options.model,
25
+ messages: [{ role: "user", content: options.text }],
26
+ systemPrompts: [systemPrompt],
27
+ maxTokens: options.maxLength,
28
+ temperature: 0.3,
29
+ logger
30
+ })) {
31
+ if (chunk.type === "TEXT_MESSAGE_CONTENT") {
32
+ if (chunk.content) {
33
+ summary = chunk.content;
34
+ } else {
35
+ summary += chunk.delta;
36
+ }
37
+ model = chunk.model || model;
29
38
  }
30
- model = chunk.model || model;
31
- }
32
- if (chunk.type === "RUN_FINISHED") {
33
- if (chunk.usage) {
34
- usage = chunk.usage;
39
+ if (chunk.type === "RUN_FINISHED") {
40
+ if (chunk.usage) {
41
+ usage = chunk.usage;
42
+ }
35
43
  }
36
44
  }
45
+ } catch (error) {
46
+ logger.errors("grok.summarize fatal", {
47
+ error,
48
+ source: "grok.summarize"
49
+ });
50
+ throw error;
37
51
  }
38
52
  return { id, model, summary, usage };
39
53
  }
40
54
  async *summarizeStream(options) {
55
+ const { logger } = options;
41
56
  const systemPrompt = this.buildSummarizationPrompt(options);
42
- yield* this.textAdapter.chatStream({
57
+ logger.request(`activity=summarize provider=grok`, {
58
+ provider: "grok",
43
59
  model: options.model,
44
- messages: [{ role: "user", content: options.text }],
45
- systemPrompts: [systemPrompt],
46
- maxTokens: options.maxLength,
47
- temperature: 0.3
60
+ stream: true
48
61
  });
62
+ try {
63
+ yield* this.textAdapter.chatStream({
64
+ model: options.model,
65
+ messages: [{ role: "user", content: options.text }],
66
+ systemPrompts: [systemPrompt],
67
+ maxTokens: options.maxLength,
68
+ temperature: 0.3,
69
+ logger
70
+ });
71
+ } catch (error) {
72
+ logger.errors("grok.summarize fatal", {
73
+ error,
74
+ source: "grok.summarize"
75
+ });
76
+ throw error;
77
+ }
49
78
  }
50
79
  buildSummarizationPrompt(options) {
51
80
  let prompt = "You are a professional summarizer. ";
@@ -1 +1 @@
1
- {"version":3,"file":"summarize.js","sources":["../../../src/adapters/summarize.ts"],"sourcesContent":["import { BaseSummarizeAdapter } from '@tanstack/ai/adapters'\nimport { getGrokApiKeyFromEnv } from '../utils'\nimport { GrokTextAdapter } from './text'\nimport type { GROK_CHAT_MODELS } from '../model-meta'\nimport type {\n StreamChunk,\n SummarizationOptions,\n SummarizationResult,\n} from '@tanstack/ai'\nimport type { GrokClientConfig } from '../utils'\n\n/**\n * Configuration for Grok summarize adapter\n */\nexport interface GrokSummarizeConfig extends GrokClientConfig {}\n\n/**\n * Grok-specific provider options for summarization\n */\nexport interface GrokSummarizeProviderOptions {\n /** Temperature for response generation (0-2) */\n temperature?: number\n /** Maximum tokens in the response */\n maxTokens?: number\n}\n\n/** Model type for Grok summarization */\nexport type GrokSummarizeModel = (typeof GROK_CHAT_MODELS)[number]\n\n/**\n * Grok Summarize Adapter\n *\n * A thin wrapper around the text adapter that adds summarization-specific prompting.\n * Delegates all API calls to the GrokTextAdapter.\n */\nexport class GrokSummarizeAdapter<\n TModel extends GrokSummarizeModel,\n> extends BaseSummarizeAdapter<TModel, GrokSummarizeProviderOptions> {\n readonly kind = 'summarize' as const\n readonly name = 'grok' as const\n\n private textAdapter: GrokTextAdapter<TModel>\n\n constructor(config: GrokSummarizeConfig, model: TModel) {\n super({}, model)\n this.textAdapter = new GrokTextAdapter(config, model)\n }\n\n async summarize(options: SummarizationOptions): Promise<SummarizationResult> {\n const systemPrompt = this.buildSummarizationPrompt(options)\n\n // Use the text adapter's streaming and collect the result\n let summary = ''\n const id = ''\n let model = options.model\n let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 }\n\n for await (const chunk of this.textAdapter.chatStream({\n model: options.model,\n messages: [{ role: 'user', content: options.text }],\n systemPrompts: [systemPrompt],\n maxTokens: options.maxLength,\n temperature: 0.3,\n })) {\n // AG-UI TEXT_MESSAGE_CONTENT event\n if (chunk.type === 'TEXT_MESSAGE_CONTENT') {\n if (chunk.content) {\n summary = chunk.content\n } else {\n summary += chunk.delta\n }\n model = chunk.model || model\n }\n // AG-UI RUN_FINISHED event\n if (chunk.type === 'RUN_FINISHED') {\n if (chunk.usage) {\n usage = chunk.usage\n }\n }\n }\n\n return { id, model, summary, usage }\n }\n\n async *summarizeStream(\n options: SummarizationOptions,\n ): AsyncIterable<StreamChunk> {\n const systemPrompt = this.buildSummarizationPrompt(options)\n\n // Delegate directly to the text adapter's streaming\n yield* this.textAdapter.chatStream({\n model: options.model,\n messages: [{ role: 'user', content: options.text }],\n systemPrompts: [systemPrompt],\n maxTokens: options.maxLength,\n temperature: 0.3,\n })\n }\n\n private buildSummarizationPrompt(options: SummarizationOptions): string {\n let prompt = 'You are a professional summarizer. '\n\n switch (options.style) {\n case 'bullet-points':\n prompt += 'Provide a summary in bullet point format. '\n break\n case 'paragraph':\n prompt += 'Provide a summary in paragraph format. '\n break\n case 'concise':\n prompt += 'Provide a very concise summary in 1-2 sentences. '\n break\n default:\n prompt += 'Provide a clear and concise summary. '\n }\n\n if (options.focus && options.focus.length > 0) {\n prompt += `Focus on the following aspects: ${options.focus.join(', ')}. `\n }\n\n if (options.maxLength) {\n prompt += `Keep the summary under ${options.maxLength} tokens. `\n }\n\n return prompt\n }\n}\n\n/**\n * Creates a Grok summarize adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'grok-3', 'grok-4')\n * @param apiKey - Your xAI API key\n * @param config - Optional additional configuration\n * @returns Configured Grok summarize adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGrokSummarize('grok-3', \"xai-...\");\n * ```\n */\nexport function createGrokSummarize<TModel extends GrokSummarizeModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GrokSummarizeConfig, 'apiKey'>,\n): GrokSummarizeAdapter<TModel> {\n return new GrokSummarizeAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Grok summarize adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * Looks for `XAI_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'grok-3', 'grok-4')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Grok summarize adapter instance with resolved types\n * @throws Error if XAI_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses XAI_API_KEY from environment\n * const adapter = grokSummarize('grok-3');\n *\n * await summarize({\n * adapter,\n * text: \"Long article text...\"\n * });\n * ```\n */\nexport function grokSummarize<TModel extends GrokSummarizeModel>(\n model: TModel,\n config?: Omit<GrokSummarizeConfig, 'apiKey'>,\n): GrokSummarizeAdapter<TModel> {\n const apiKey = getGrokApiKeyFromEnv()\n return createGrokSummarize(model, apiKey, config)\n}\n"],"names":[],"mappings":";;;AAmCO,MAAM,6BAEH,qBAA2D;AAAA,EAMnE,YAAY,QAA6B,OAAe;AACtD,UAAM,CAAA,GAAI,KAAK;AANjB,SAAS,OAAO;AAChB,SAAS,OAAO;AAMd,SAAK,cAAc,IAAI,gBAAgB,QAAQ,KAAK;AAAA,EACtD;AAAA,EAEA,MAAM,UAAU,SAA6D;AAC3E,UAAM,eAAe,KAAK,yBAAyB,OAAO;AAG1D,QAAI,UAAU;AACd,UAAM,KAAK;AACX,QAAI,QAAQ,QAAQ;AACpB,QAAI,QAAQ,EAAE,cAAc,GAAG,kBAAkB,GAAG,aAAa,EAAA;AAEjE,qBAAiB,SAAS,KAAK,YAAY,WAAW;AAAA,MACpD,OAAO,QAAQ;AAAA,MACf,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,QAAQ,MAAM;AAAA,MAClD,eAAe,CAAC,YAAY;AAAA,MAC5B,WAAW,QAAQ;AAAA,MACnB,aAAa;AAAA,IAAA,CACd,GAAG;AAEF,UAAI,MAAM,SAAS,wBAAwB;AACzC,YAAI,MAAM,SAAS;AACjB,oBAAU,MAAM;AAAA,QAClB,OAAO;AACL,qBAAW,MAAM;AAAA,QACnB;AACA,gBAAQ,MAAM,SAAS;AAAA,MACzB;AAEA,UAAI,MAAM,SAAS,gBAAgB;AACjC,YAAI,MAAM,OAAO;AACf,kBAAQ,MAAM;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,IAAI,OAAO,SAAS,MAAA;AAAA,EAC/B;AAAA,EAEA,OAAO,gBACL,SAC4B;AAC5B,UAAM,eAAe,KAAK,yBAAyB,OAAO;AAG1D,WAAO,KAAK,YAAY,WAAW;AAAA,MACjC,OAAO,QAAQ;AAAA,MACf,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,QAAQ,MAAM;AAAA,MAClD,eAAe,CAAC,YAAY;AAAA,MAC5B,WAAW,QAAQ;AAAA,MACnB,aAAa;AAAA,IAAA,CACd;AAAA,EACH;AAAA,EAEQ,yBAAyB,SAAuC;AACtE,QAAI,SAAS;AAEb,YAAQ,QAAQ,OAAA;AAAA,MACd,KAAK;AACH,kBAAU;AACV;AAAA,MACF,KAAK;AACH,kBAAU;AACV;AAAA,MACF,KAAK;AACH,kBAAU;AACV;AAAA,MACF;AACE,kBAAU;AAAA,IAAA;AAGd,QAAI,QAAQ,SAAS,QAAQ,MAAM,SAAS,GAAG;AAC7C,gBAAU,mCAAmC,QAAQ,MAAM,KAAK,IAAI,CAAC;AAAA,IACvE;AAEA,QAAI,QAAQ,WAAW;AACrB,gBAAU,0BAA0B,QAAQ,SAAS;AAAA,IACvD;AAEA,WAAO;AAAA,EACT;AACF;AAgBO,SAAS,oBACd,OACA,QACA,QAC8B;AAC9B,SAAO,IAAI,qBAAqB,EAAE,QAAQ,GAAG,OAAA,GAAU,KAAK;AAC9D;AA0BO,SAAS,cACd,OACA,QAC8B;AAC9B,QAAM,SAAS,qBAAA;AACf,SAAO,oBAAoB,OAAO,QAAQ,MAAM;AAClD;"}
1
+ {"version":3,"file":"summarize.js","sources":["../../../src/adapters/summarize.ts"],"sourcesContent":["import { BaseSummarizeAdapter } from '@tanstack/ai/adapters'\nimport { getGrokApiKeyFromEnv } from '../utils'\nimport { GrokTextAdapter } from './text'\nimport type { GROK_CHAT_MODELS } from '../model-meta'\nimport type {\n StreamChunk,\n SummarizationOptions,\n SummarizationResult,\n} from '@tanstack/ai'\nimport type { GrokClientConfig } from '../utils'\n\n/**\n * Configuration for Grok summarize adapter\n */\nexport interface GrokSummarizeConfig extends GrokClientConfig {}\n\n/**\n * Grok-specific provider options for summarization\n */\nexport interface GrokSummarizeProviderOptions {\n /** Temperature for response generation (0-2) */\n temperature?: number\n /** Maximum tokens in the response */\n maxTokens?: number\n}\n\n/** Model type for Grok summarization */\nexport type GrokSummarizeModel = (typeof GROK_CHAT_MODELS)[number]\n\n/**\n * Grok Summarize Adapter\n *\n * A thin wrapper around the text adapter that adds summarization-specific prompting.\n * Delegates all API calls to the GrokTextAdapter.\n */\nexport class GrokSummarizeAdapter<\n TModel extends GrokSummarizeModel,\n> extends BaseSummarizeAdapter<TModel, GrokSummarizeProviderOptions> {\n readonly kind = 'summarize' as const\n readonly name = 'grok' as const\n\n private textAdapter: GrokTextAdapter<TModel>\n\n constructor(config: GrokSummarizeConfig, model: TModel) {\n super({}, model)\n this.textAdapter = new GrokTextAdapter(config, model)\n }\n\n async summarize(options: SummarizationOptions): Promise<SummarizationResult> {\n const { logger } = options\n const systemPrompt = this.buildSummarizationPrompt(options)\n\n logger.request(`activity=summarize provider=grok`, {\n provider: 'grok',\n model: options.model,\n })\n\n // Use the text adapter's streaming and collect the result\n let summary = ''\n const id = ''\n let model = options.model\n let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 }\n\n try {\n for await (const chunk of this.textAdapter.chatStream({\n model: options.model,\n messages: [{ role: 'user', content: options.text }],\n systemPrompts: [systemPrompt],\n maxTokens: options.maxLength,\n temperature: 0.3,\n logger,\n })) {\n // AG-UI TEXT_MESSAGE_CONTENT event\n if (chunk.type === 'TEXT_MESSAGE_CONTENT') {\n if (chunk.content) {\n summary = chunk.content\n } else {\n summary += chunk.delta\n }\n model = chunk.model || model\n }\n // AG-UI RUN_FINISHED event\n if (chunk.type === 'RUN_FINISHED') {\n if (chunk.usage) {\n usage = chunk.usage\n }\n }\n }\n } catch (error) {\n logger.errors('grok.summarize fatal', {\n error,\n source: 'grok.summarize',\n })\n throw error\n }\n\n return { id, model, summary, usage }\n }\n\n async *summarizeStream(\n options: SummarizationOptions,\n ): AsyncIterable<StreamChunk> {\n const { logger } = options\n const systemPrompt = this.buildSummarizationPrompt(options)\n\n logger.request(`activity=summarize provider=grok`, {\n provider: 'grok',\n model: options.model,\n stream: true,\n })\n\n try {\n // Delegate directly to the text adapter's streaming\n yield* this.textAdapter.chatStream({\n model: options.model,\n messages: [{ role: 'user', content: options.text }],\n systemPrompts: [systemPrompt],\n maxTokens: options.maxLength,\n temperature: 0.3,\n logger,\n })\n } catch (error) {\n logger.errors('grok.summarize fatal', {\n error,\n source: 'grok.summarize',\n })\n throw error\n }\n }\n\n private buildSummarizationPrompt(options: SummarizationOptions): string {\n let prompt = 'You are a professional summarizer. '\n\n switch (options.style) {\n case 'bullet-points':\n prompt += 'Provide a summary in bullet point format. '\n break\n case 'paragraph':\n prompt += 'Provide a summary in paragraph format. '\n break\n case 'concise':\n prompt += 'Provide a very concise summary in 1-2 sentences. '\n break\n default:\n prompt += 'Provide a clear and concise summary. '\n }\n\n if (options.focus && options.focus.length > 0) {\n prompt += `Focus on the following aspects: ${options.focus.join(', ')}. `\n }\n\n if (options.maxLength) {\n prompt += `Keep the summary under ${options.maxLength} tokens. `\n }\n\n return prompt\n }\n}\n\n/**\n * Creates a Grok summarize adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'grok-3', 'grok-4')\n * @param apiKey - Your xAI API key\n * @param config - Optional additional configuration\n * @returns Configured Grok summarize adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGrokSummarize('grok-3', \"xai-...\");\n * ```\n */\nexport function createGrokSummarize<TModel extends GrokSummarizeModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GrokSummarizeConfig, 'apiKey'>,\n): GrokSummarizeAdapter<TModel> {\n return new GrokSummarizeAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Grok summarize adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * Looks for `XAI_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'grok-3', 'grok-4')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Grok summarize adapter instance with resolved types\n * @throws Error if XAI_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses XAI_API_KEY from environment\n * const adapter = grokSummarize('grok-3');\n *\n * await summarize({\n * adapter,\n * text: \"Long article text...\"\n * });\n * ```\n */\nexport function grokSummarize<TModel extends GrokSummarizeModel>(\n model: TModel,\n config?: Omit<GrokSummarizeConfig, 'apiKey'>,\n): GrokSummarizeAdapter<TModel> {\n const apiKey = getGrokApiKeyFromEnv()\n return createGrokSummarize(model, apiKey, config)\n}\n"],"names":[],"mappings":"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"}
@@ -14,6 +14,7 @@ class GrokTextAdapter extends BaseTextAdapter {
14
14
  async *chatStream(options) {
15
15
  const requestParams = this.mapTextOptionsToGrok(options);
16
16
  const timestamp = Date.now();
17
+ const { logger } = options;
17
18
  const aguiState = {
18
19
  runId: options.runId ?? generateId(this.name),
19
20
  threadId: options.threadId ?? generateId(this.name),
@@ -22,11 +23,15 @@ class GrokTextAdapter extends BaseTextAdapter {
22
23
  hasEmittedRunStarted: false
23
24
  };
24
25
  try {
26
+ logger.request(
27
+ `activity=chat provider=grok model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,
28
+ { provider: "grok", model: this.model }
29
+ );
25
30
  const stream = await this.client.chat.completions.create({
26
31
  ...requestParams,
27
32
  stream: true
28
33
  });
29
- yield* this.processGrokStreamChunks(stream, options, aguiState);
34
+ yield* this.processGrokStreamChunks(stream, options, aguiState, logger);
30
35
  } catch (error) {
31
36
  const err = error;
32
37
  if (!aguiState.hasEmittedRunStarted) {
@@ -51,10 +56,10 @@ class GrokTextAdapter extends BaseTextAdapter {
51
56
  code: err.code
52
57
  }
53
58
  });
54
- console.error(">>> chatStream: Fatal error during response creation <<<");
55
- console.error(">>> Error message:", err.message);
56
- console.error(">>> Error stack:", err.stack);
57
- console.error(">>> Full error:", err);
59
+ logger.errors("grok.chatStream fatal", {
60
+ error,
61
+ source: "grok.chatStream"
62
+ });
58
63
  }
59
64
  }
60
65
  /**
@@ -72,11 +77,16 @@ class GrokTextAdapter extends BaseTextAdapter {
72
77
  async structuredOutput(options) {
73
78
  const { chatOptions, outputSchema } = options;
74
79
  const requestParams = this.mapTextOptionsToGrok(chatOptions);
80
+ const { logger } = chatOptions;
75
81
  const jsonSchema = makeGrokStructuredOutputCompatible(
76
82
  outputSchema,
77
83
  outputSchema.required || []
78
84
  );
79
85
  try {
86
+ logger.request(
87
+ `activity=chat provider=grok model=${this.model} messages=${chatOptions.messages.length} tools=${chatOptions.tools?.length ?? 0} stream=false`,
88
+ { provider: "grok", model: this.model }
89
+ );
80
90
  const response = await this.client.chat.completions.create({
81
91
  ...requestParams,
82
92
  stream: false,
@@ -104,19 +114,21 @@ class GrokTextAdapter extends BaseTextAdapter {
104
114
  rawText
105
115
  };
106
116
  } catch (error) {
107
- const err = error;
108
- console.error(">>> structuredOutput: Error during response creation <<<");
109
- console.error(">>> Error message:", err.message);
117
+ logger.errors("grok.structuredOutput fatal", {
118
+ error,
119
+ source: "grok.structuredOutput"
120
+ });
110
121
  throw error;
111
122
  }
112
123
  }
113
- async *processGrokStreamChunks(stream, options, aguiState) {
124
+ async *processGrokStreamChunks(stream, options, aguiState, logger) {
114
125
  let accumulatedContent = "";
115
126
  const timestamp = aguiState.timestamp;
116
127
  let hasEmittedTextMessageStart = false;
117
128
  const toolCallsInProgress = /* @__PURE__ */ new Map();
118
129
  try {
119
130
  for await (const chunk of stream) {
131
+ logger.provider(`provider=grok`, { chunk });
120
132
  const choice = chunk.choices[0];
121
133
  if (!choice) continue;
122
134
  if (!aguiState.hasEmittedRunStarted) {
@@ -243,7 +255,10 @@ class GrokTextAdapter extends BaseTextAdapter {
243
255
  }
244
256
  } catch (error) {
245
257
  const err = error;
246
- console.log("[Grok Adapter] Stream ended with error:", err.message);
258
+ logger.errors("grok stream ended with error", {
259
+ error,
260
+ source: "grok.processGrokStreamChunks"
261
+ });
247
262
  yield asChunk({
248
263
  type: "RUN_ERROR",
249
264
  runId: aguiState.runId,
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","sources":["../../../src/adapters/text.ts"],"sourcesContent":["import { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { validateTextProviderOptions } from '../text/text-provider-options'\nimport { convertToolsToProviderFormat } from '../tools'\nimport {\n createGrokClient,\n generateId,\n getGrokApiKeyFromEnv,\n makeGrokStructuredOutputCompatible,\n transformNullsToUndefined,\n} from '../utils'\nimport type {\n GROK_CHAT_MODELS,\n GrokChatModelToolCapabilitiesByName,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type OpenAI_SDK from 'openai'\nimport type {\n ContentPart,\n Modality,\n ModelMessage,\n StreamChunk,\n TextOptions,\n} from '@tanstack/ai'\nimport type {\n ExternalTextProviderOptions as GrokTextProviderOptions,\n InternalTextProviderOptions,\n} from '../text/text-provider-options'\nimport type {\n GrokImageMetadata,\n GrokMessageMetadataByModality,\n} from '../message-types'\nimport type { GrokClientConfig } from '../utils'\n\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof GrokChatModelToolCapabilitiesByName\n ? NonNullable<GrokChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n/** Cast an event object to StreamChunk. Adapters construct events with string\n * literal types which are structurally compatible with the EventType enum. */\nconst asChunk = (chunk: Record<string, unknown>) =>\n chunk as unknown as StreamChunk\n\n/**\n * Configuration for Grok text adapter\n */\nexport interface GrokTextConfig extends GrokClientConfig {}\n\n/**\n * Alias for TextProviderOptions for external use\n */\nexport type { ExternalTextProviderOptions as GrokTextProviderOptions } from '../text/text-provider-options'\n\n/**\n * Grok Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Grok chat/text completion functionality.\n * Uses OpenAI-compatible Chat Completions API (not Responses API).\n */\nexport class GrokTextAdapter<\n TModel extends (typeof GROK_CHAT_MODELS)[number],\n TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n GrokMessageMetadataByModality,\n TToolCapabilities\n> {\n readonly kind = 'text' as const\n readonly name = 'grok' as const\n\n private client: OpenAI_SDK\n\n constructor(config: GrokTextConfig, model: TModel) {\n super({}, model)\n this.client = createGrokClient(config)\n }\n\n async *chatStream(\n options: TextOptions<GrokTextProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const requestParams = this.mapTextOptionsToGrok(options)\n const timestamp = Date.now()\n\n // AG-UI lifecycle tracking (mutable state object for ESLint compatibility)\n const aguiState = {\n runId: options.runId ?? generateId(this.name),\n threadId: options.threadId ?? generateId(this.name),\n messageId: generateId(this.name),\n timestamp,\n hasEmittedRunStarted: false,\n }\n\n try {\n const stream = await this.client.chat.completions.create({\n ...requestParams,\n stream: true,\n })\n\n yield* this.processGrokStreamChunks(stream, options, aguiState)\n } catch (error: unknown) {\n const err = error as Error & { code?: string }\n\n // Emit RUN_STARTED if not yet emitted\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield asChunk({\n type: 'RUN_STARTED',\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: options.model,\n timestamp,\n })\n }\n\n // Emit AG-UI RUN_ERROR\n yield asChunk({\n type: 'RUN_ERROR',\n runId: aguiState.runId,\n model: options.model,\n timestamp,\n message: err.message || 'Unknown error',\n code: err.code,\n error: {\n message: err.message || 'Unknown error',\n code: err.code,\n },\n })\n\n console.error('>>> chatStream: Fatal error during response creation <<<')\n console.error('>>> Error message:', err.message)\n console.error('>>> Error stack:', err.stack)\n console.error('>>> Full error:', err)\n }\n }\n\n /**\n * Generate structured output using Grok's JSON Schema response format.\n * Uses stream: false to get the complete response in one call.\n *\n * Grok has strict requirements for structured output (via OpenAI-compatible API):\n * - All properties must be in the `required` array\n * - Optional fields should have null added to their type union\n * - additionalProperties must be false for all objects\n *\n * The outputSchema is already JSON Schema (converted in the ai layer).\n * We apply Grok-specific transformations for structured output compatibility.\n */\n async structuredOutput(\n options: StructuredOutputOptions<GrokTextProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n const requestParams = this.mapTextOptionsToGrok(chatOptions)\n\n // Apply Grok-specific transformations for structured output compatibility\n const jsonSchema = makeGrokStructuredOutputCompatible(\n outputSchema,\n outputSchema.required || [],\n )\n\n try {\n const response = await this.client.chat.completions.create({\n ...requestParams,\n stream: false,\n response_format: {\n type: 'json_schema',\n json_schema: {\n name: 'structured_output',\n schema: jsonSchema,\n strict: true,\n },\n },\n })\n\n // Extract text content from the response\n const rawText = response.choices[0]?.message.content || ''\n\n // Parse the JSON response\n let parsed: unknown\n try {\n parsed = JSON.parse(rawText)\n } catch {\n throw new Error(\n `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`,\n )\n }\n\n // Transform null values to undefined to match original Zod schema expectations\n // Grok returns null for optional fields we made nullable in the schema\n const transformed = transformNullsToUndefined(parsed)\n\n return {\n data: transformed,\n rawText,\n }\n } catch (error: unknown) {\n const err = error as Error\n console.error('>>> structuredOutput: Error during response creation <<<')\n console.error('>>> Error message:', err.message)\n throw error\n }\n }\n\n private async *processGrokStreamChunks(\n stream: AsyncIterable<OpenAI_SDK.Chat.Completions.ChatCompletionChunk>,\n options: TextOptions,\n aguiState: {\n runId: string\n threadId: string\n messageId: string\n timestamp: number\n hasEmittedRunStarted: boolean\n },\n ): AsyncIterable<StreamChunk> {\n let accumulatedContent = ''\n const timestamp = aguiState.timestamp\n let hasEmittedTextMessageStart = false\n\n // Track tool calls being streamed (arguments come in chunks)\n const toolCallsInProgress = new Map<\n number,\n {\n id: string\n name: string\n arguments: string\n started: boolean // Track if TOOL_CALL_START has been emitted\n }\n >()\n\n try {\n for await (const chunk of stream) {\n const choice = chunk.choices[0]\n\n if (!choice) continue\n\n // Emit RUN_STARTED on first chunk\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield asChunk({\n type: 'RUN_STARTED',\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: chunk.model || options.model,\n timestamp,\n })\n }\n\n const delta = choice.delta\n const deltaContent = delta.content\n const deltaToolCalls = delta.tool_calls\n\n // Handle content delta\n if (deltaContent) {\n // Emit TEXT_MESSAGE_START on first text content\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield asChunk({\n type: 'TEXT_MESSAGE_START',\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp,\n role: 'assistant',\n })\n }\n\n accumulatedContent += deltaContent\n\n // Emit AG-UI TEXT_MESSAGE_CONTENT\n yield asChunk({\n type: 'TEXT_MESSAGE_CONTENT',\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp,\n delta: deltaContent,\n content: accumulatedContent,\n })\n }\n\n // Handle tool calls - they come in as deltas\n if (deltaToolCalls) {\n for (const toolCallDelta of deltaToolCalls) {\n const index = toolCallDelta.index\n\n // Initialize or update the tool call in progress\n if (!toolCallsInProgress.has(index)) {\n toolCallsInProgress.set(index, {\n id: toolCallDelta.id || '',\n name: toolCallDelta.function?.name || '',\n arguments: '',\n started: false,\n })\n }\n\n const toolCall = toolCallsInProgress.get(index)!\n\n // Update with any new data from the delta\n if (toolCallDelta.id) {\n toolCall.id = toolCallDelta.id\n }\n if (toolCallDelta.function?.name) {\n toolCall.name = toolCallDelta.function.name\n }\n if (toolCallDelta.function?.arguments) {\n toolCall.arguments += toolCallDelta.function.arguments\n }\n\n // Emit TOOL_CALL_START when we have id and name\n if (toolCall.id && toolCall.name && !toolCall.started) {\n toolCall.started = true\n yield asChunk({\n type: 'TOOL_CALL_START',\n toolCallId: toolCall.id,\n toolCallName: toolCall.name,\n toolName: toolCall.name,\n model: chunk.model || options.model,\n timestamp,\n index,\n })\n }\n\n // Emit TOOL_CALL_ARGS for argument deltas\n if (toolCallDelta.function?.arguments && toolCall.started) {\n yield asChunk({\n type: 'TOOL_CALL_ARGS',\n toolCallId: toolCall.id,\n model: chunk.model || options.model,\n timestamp,\n delta: toolCallDelta.function.arguments,\n })\n }\n }\n }\n\n // Handle finish reason\n if (choice.finish_reason) {\n // Emit all completed tool calls\n if (\n choice.finish_reason === 'tool_calls' ||\n toolCallsInProgress.size > 0\n ) {\n for (const [, toolCall] of toolCallsInProgress) {\n // Parse arguments for TOOL_CALL_END\n let parsedInput: unknown = {}\n try {\n parsedInput = toolCall.arguments\n ? JSON.parse(toolCall.arguments)\n : {}\n } catch {\n parsedInput = {}\n }\n\n // Emit AG-UI TOOL_CALL_END\n yield asChunk({\n type: 'TOOL_CALL_END',\n toolCallId: toolCall.id,\n toolCallName: toolCall.name,\n toolName: toolCall.name,\n model: chunk.model || options.model,\n timestamp,\n input: parsedInput,\n })\n }\n }\n\n const computedFinishReason =\n choice.finish_reason === 'tool_calls' ||\n toolCallsInProgress.size > 0\n ? 'tool_calls'\n : 'stop'\n\n // Emit TEXT_MESSAGE_END if we had text content\n if (hasEmittedTextMessageStart) {\n yield asChunk({\n type: 'TEXT_MESSAGE_END',\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp,\n })\n }\n\n // Emit AG-UI RUN_FINISHED\n yield asChunk({\n type: 'RUN_FINISHED',\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: chunk.model || options.model,\n timestamp,\n usage: chunk.usage\n ? {\n promptTokens: chunk.usage.prompt_tokens || 0,\n completionTokens: chunk.usage.completion_tokens || 0,\n totalTokens: chunk.usage.total_tokens || 0,\n }\n : undefined,\n finishReason: computedFinishReason,\n })\n }\n }\n } catch (error: unknown) {\n const err = error as Error & { code?: string }\n console.log('[Grok Adapter] Stream ended with error:', err.message)\n\n // Emit AG-UI RUN_ERROR\n yield asChunk({\n type: 'RUN_ERROR',\n runId: aguiState.runId,\n model: options.model,\n timestamp,\n message: err.message || 'Unknown error occurred',\n code: err.code,\n error: {\n message: err.message || 'Unknown error occurred',\n code: err.code,\n },\n })\n }\n }\n\n /**\n * Maps common options to Grok-specific Chat Completions format\n */\n private mapTextOptionsToGrok(\n options: TextOptions,\n ): OpenAI_SDK.Chat.Completions.ChatCompletionCreateParamsStreaming {\n const modelOptions = options.modelOptions as\n | Omit<\n InternalTextProviderOptions,\n 'max_tokens' | 'tools' | 'temperature' | 'input' | 'top_p'\n >\n | undefined\n\n if (modelOptions) {\n validateTextProviderOptions({\n ...modelOptions,\n model: options.model,\n })\n }\n\n const tools = options.tools\n ? convertToolsToProviderFormat(options.tools)\n : undefined\n\n // Build messages array with system prompts\n const messages: Array<OpenAI_SDK.Chat.Completions.ChatCompletionMessageParam> =\n []\n\n // Add system prompts first\n if (options.systemPrompts && options.systemPrompts.length > 0) {\n messages.push({\n role: 'system',\n content: options.systemPrompts.join('\\n'),\n })\n }\n\n // Convert messages\n for (const message of options.messages) {\n messages.push(this.convertMessageToGrok(message))\n }\n\n return {\n model: options.model,\n messages,\n temperature: options.temperature,\n max_tokens: options.maxTokens,\n top_p: options.topP,\n tools: tools as Array<OpenAI_SDK.Chat.Completions.ChatCompletionTool>,\n stream: true,\n stream_options: { include_usage: true },\n }\n }\n\n private convertMessageToGrok(\n message: ModelMessage,\n ): OpenAI_SDK.Chat.Completions.ChatCompletionMessageParam {\n // Handle tool messages\n if (message.role === 'tool') {\n return {\n role: 'tool',\n tool_call_id: message.toolCallId || '',\n content:\n typeof message.content === 'string'\n ? message.content\n : JSON.stringify(message.content),\n }\n }\n\n // Handle assistant messages\n if (message.role === 'assistant') {\n const toolCalls = message.toolCalls?.map((tc) => ({\n id: tc.id,\n type: 'function' as const,\n function: {\n name: tc.function.name,\n arguments:\n typeof tc.function.arguments === 'string'\n ? tc.function.arguments\n : JSON.stringify(tc.function.arguments),\n },\n }))\n\n return {\n role: 'assistant',\n content: this.extractTextContent(message.content),\n ...(toolCalls && toolCalls.length > 0 ? { tool_calls: toolCalls } : {}),\n }\n }\n\n // Handle user messages - support multimodal content\n const contentParts = this.normalizeContent(message.content)\n\n // If only text, use simple string format\n if (contentParts.length === 1 && contentParts[0]?.type === 'text') {\n return {\n role: 'user',\n content: contentParts[0].content,\n }\n }\n\n // Otherwise, use array format for multimodal\n const parts: Array<OpenAI_SDK.Chat.Completions.ChatCompletionContentPart> =\n []\n for (const part of contentParts) {\n if (part.type === 'text') {\n parts.push({ type: 'text', text: part.content })\n } else if (part.type === 'image') {\n const imageMetadata = part.metadata as GrokImageMetadata | undefined\n // For base64 data, construct a data URI using the mimeType from source\n const imageValue = part.source.value\n const imageUrl =\n part.source.type === 'data' && !imageValue.startsWith('data:')\n ? `data:${part.source.mimeType};base64,${imageValue}`\n : imageValue\n parts.push({\n type: 'image_url',\n image_url: {\n url: imageUrl,\n detail: imageMetadata?.detail || 'auto',\n },\n })\n }\n }\n\n return {\n role: 'user',\n content: parts.length > 0 ? parts : '',\n }\n }\n\n /**\n * Normalizes message content to an array of ContentPart.\n * Handles backward compatibility with string content.\n */\n private normalizeContent(\n content: string | null | Array<ContentPart>,\n ): Array<ContentPart> {\n if (content === null) {\n return []\n }\n if (typeof content === 'string') {\n return [{ type: 'text', content: content }]\n }\n return content\n }\n\n /**\n * Extracts text content from a content value that may be string, null, or ContentPart array.\n */\n private extractTextContent(\n content: string | null | Array<ContentPart>,\n ): string {\n if (content === null) {\n return ''\n }\n if (typeof content === 'string') {\n return content\n }\n // It's an array of ContentPart\n return content\n .filter((p) => p.type === 'text')\n .map((p) => p.content)\n .join('')\n }\n}\n\n/**\n * Creates a Grok text adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'grok-3', 'grok-4')\n * @param apiKey - Your xAI API key\n * @param config - Optional additional configuration\n * @returns Configured Grok text adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGrokText('grok-3', \"xai-...\");\n * // adapter has type-safe providerOptions for grok-3\n * ```\n */\nexport function createGrokText<\n TModel extends (typeof GROK_CHAT_MODELS)[number],\n>(\n model: TModel,\n apiKey: string,\n config?: Omit<GrokTextConfig, 'apiKey'>,\n): GrokTextAdapter<TModel> {\n return new GrokTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Grok text adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * Looks for `XAI_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'grok-3', 'grok-4')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Grok text adapter instance with resolved types\n * @throws Error if XAI_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses XAI_API_KEY from environment\n * const adapter = grokText('grok-3');\n *\n * const stream = chat({\n * adapter,\n * messages: [{ role: \"user\", content: \"Hello!\" }]\n * });\n * ```\n */\nexport function grokText<TModel extends (typeof GROK_CHAT_MODELS)[number]>(\n model: TModel,\n config?: Omit<GrokTextConfig, 'apiKey'>,\n): GrokTextAdapter<TModel> {\n const apiKey = getGrokApiKeyFromEnv()\n return createGrokText(model, apiKey, config)\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"text.js","sources":["../../../src/adapters/text.ts"],"sourcesContent":["import { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { validateTextProviderOptions } from '../text/text-provider-options'\nimport { convertToolsToProviderFormat } from '../tools'\nimport {\n createGrokClient,\n generateId,\n getGrokApiKeyFromEnv,\n makeGrokStructuredOutputCompatible,\n transformNullsToUndefined,\n} from '../utils'\nimport type {\n GROK_CHAT_MODELS,\n GrokChatModelToolCapabilitiesByName,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type { InternalLogger } from '@tanstack/ai/adapter-internals'\nimport type OpenAI_SDK from 'openai'\nimport type {\n ContentPart,\n Modality,\n ModelMessage,\n StreamChunk,\n TextOptions,\n} from '@tanstack/ai'\nimport type {\n ExternalTextProviderOptions as GrokTextProviderOptions,\n InternalTextProviderOptions,\n} from '../text/text-provider-options'\nimport type {\n GrokImageMetadata,\n GrokMessageMetadataByModality,\n} from '../message-types'\nimport type { GrokClientConfig } from '../utils'\n\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof GrokChatModelToolCapabilitiesByName\n ? NonNullable<GrokChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n/** Cast an event object to StreamChunk. Adapters construct events with string\n * literal types which are structurally compatible with the EventType enum. */\nconst asChunk = (chunk: Record<string, unknown>) =>\n chunk as unknown as StreamChunk\n\n/**\n * Configuration for Grok text adapter\n */\nexport interface GrokTextConfig extends GrokClientConfig {}\n\n/**\n * Alias for TextProviderOptions for external use\n */\nexport type { ExternalTextProviderOptions as GrokTextProviderOptions } from '../text/text-provider-options'\n\n/**\n * Grok Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Grok chat/text completion functionality.\n * Uses OpenAI-compatible Chat Completions API (not Responses API).\n */\nexport class GrokTextAdapter<\n TModel extends (typeof GROK_CHAT_MODELS)[number],\n TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n GrokMessageMetadataByModality,\n TToolCapabilities\n> {\n readonly kind = 'text' as const\n readonly name = 'grok' as const\n\n private client: OpenAI_SDK\n\n constructor(config: GrokTextConfig, model: TModel) {\n super({}, model)\n this.client = createGrokClient(config)\n }\n\n async *chatStream(\n options: TextOptions<GrokTextProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const requestParams = this.mapTextOptionsToGrok(options)\n const timestamp = Date.now()\n const { logger } = options\n\n // AG-UI lifecycle tracking (mutable state object for ESLint compatibility)\n const aguiState = {\n runId: options.runId ?? generateId(this.name),\n threadId: options.threadId ?? generateId(this.name),\n messageId: generateId(this.name),\n timestamp,\n hasEmittedRunStarted: false,\n }\n\n try {\n logger.request(\n `activity=chat provider=grok model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,\n { provider: 'grok', model: this.model },\n )\n const stream = await this.client.chat.completions.create({\n ...requestParams,\n stream: true,\n })\n\n yield* this.processGrokStreamChunks(stream, options, aguiState, logger)\n } catch (error: unknown) {\n const err = error as Error & { code?: string }\n\n // Emit RUN_STARTED if not yet emitted\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield asChunk({\n type: 'RUN_STARTED',\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: options.model,\n timestamp,\n })\n }\n\n // Emit AG-UI RUN_ERROR\n yield asChunk({\n type: 'RUN_ERROR',\n runId: aguiState.runId,\n model: options.model,\n timestamp,\n message: err.message || 'Unknown error',\n code: err.code,\n error: {\n message: err.message || 'Unknown error',\n code: err.code,\n },\n })\n\n logger.errors('grok.chatStream fatal', {\n error,\n source: 'grok.chatStream',\n })\n }\n }\n\n /**\n * Generate structured output using Grok's JSON Schema response format.\n * Uses stream: false to get the complete response in one call.\n *\n * Grok has strict requirements for structured output (via OpenAI-compatible API):\n * - All properties must be in the `required` array\n * - Optional fields should have null added to their type union\n * - additionalProperties must be false for all objects\n *\n * The outputSchema is already JSON Schema (converted in the ai layer).\n * We apply Grok-specific transformations for structured output compatibility.\n */\n async structuredOutput(\n options: StructuredOutputOptions<GrokTextProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n const requestParams = this.mapTextOptionsToGrok(chatOptions)\n const { logger } = chatOptions\n\n // Apply Grok-specific transformations for structured output compatibility\n const jsonSchema = makeGrokStructuredOutputCompatible(\n outputSchema,\n outputSchema.required || [],\n )\n\n try {\n logger.request(\n `activity=chat provider=grok model=${this.model} messages=${chatOptions.messages.length} tools=${chatOptions.tools?.length ?? 0} stream=false`,\n { provider: 'grok', model: this.model },\n )\n const response = await this.client.chat.completions.create({\n ...requestParams,\n stream: false,\n response_format: {\n type: 'json_schema',\n json_schema: {\n name: 'structured_output',\n schema: jsonSchema,\n strict: true,\n },\n },\n })\n\n // Extract text content from the response\n const rawText = response.choices[0]?.message.content || ''\n\n // Parse the JSON response\n let parsed: unknown\n try {\n parsed = JSON.parse(rawText)\n } catch {\n throw new Error(\n `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`,\n )\n }\n\n // Transform null values to undefined to match original Zod schema expectations\n // Grok returns null for optional fields we made nullable in the schema\n const transformed = transformNullsToUndefined(parsed)\n\n return {\n data: transformed,\n rawText,\n }\n } catch (error: unknown) {\n logger.errors('grok.structuredOutput fatal', {\n error,\n source: 'grok.structuredOutput',\n })\n throw error\n }\n }\n\n private async *processGrokStreamChunks(\n stream: AsyncIterable<OpenAI_SDK.Chat.Completions.ChatCompletionChunk>,\n options: TextOptions,\n aguiState: {\n runId: string\n threadId: string\n messageId: string\n timestamp: number\n hasEmittedRunStarted: boolean\n },\n logger: InternalLogger,\n ): AsyncIterable<StreamChunk> {\n let accumulatedContent = ''\n const timestamp = aguiState.timestamp\n let hasEmittedTextMessageStart = false\n\n // Track tool calls being streamed (arguments come in chunks)\n const toolCallsInProgress = new Map<\n number,\n {\n id: string\n name: string\n arguments: string\n started: boolean // Track if TOOL_CALL_START has been emitted\n }\n >()\n\n try {\n for await (const chunk of stream) {\n logger.provider(`provider=grok`, { chunk })\n const choice = chunk.choices[0]\n\n if (!choice) continue\n\n // Emit RUN_STARTED on first chunk\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield asChunk({\n type: 'RUN_STARTED',\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: chunk.model || options.model,\n timestamp,\n })\n }\n\n const delta = choice.delta\n const deltaContent = delta.content\n const deltaToolCalls = delta.tool_calls\n\n // Handle content delta\n if (deltaContent) {\n // Emit TEXT_MESSAGE_START on first text content\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield asChunk({\n type: 'TEXT_MESSAGE_START',\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp,\n role: 'assistant',\n })\n }\n\n accumulatedContent += deltaContent\n\n // Emit AG-UI TEXT_MESSAGE_CONTENT\n yield asChunk({\n type: 'TEXT_MESSAGE_CONTENT',\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp,\n delta: deltaContent,\n content: accumulatedContent,\n })\n }\n\n // Handle tool calls - they come in as deltas\n if (deltaToolCalls) {\n for (const toolCallDelta of deltaToolCalls) {\n const index = toolCallDelta.index\n\n // Initialize or update the tool call in progress\n if (!toolCallsInProgress.has(index)) {\n toolCallsInProgress.set(index, {\n id: toolCallDelta.id || '',\n name: toolCallDelta.function?.name || '',\n arguments: '',\n started: false,\n })\n }\n\n const toolCall = toolCallsInProgress.get(index)!\n\n // Update with any new data from the delta\n if (toolCallDelta.id) {\n toolCall.id = toolCallDelta.id\n }\n if (toolCallDelta.function?.name) {\n toolCall.name = toolCallDelta.function.name\n }\n if (toolCallDelta.function?.arguments) {\n toolCall.arguments += toolCallDelta.function.arguments\n }\n\n // Emit TOOL_CALL_START when we have id and name\n if (toolCall.id && toolCall.name && !toolCall.started) {\n toolCall.started = true\n yield asChunk({\n type: 'TOOL_CALL_START',\n toolCallId: toolCall.id,\n toolCallName: toolCall.name,\n toolName: toolCall.name,\n model: chunk.model || options.model,\n timestamp,\n index,\n })\n }\n\n // Emit TOOL_CALL_ARGS for argument deltas\n if (toolCallDelta.function?.arguments && toolCall.started) {\n yield asChunk({\n type: 'TOOL_CALL_ARGS',\n toolCallId: toolCall.id,\n model: chunk.model || options.model,\n timestamp,\n delta: toolCallDelta.function.arguments,\n })\n }\n }\n }\n\n // Handle finish reason\n if (choice.finish_reason) {\n // Emit all completed tool calls\n if (\n choice.finish_reason === 'tool_calls' ||\n toolCallsInProgress.size > 0\n ) {\n for (const [, toolCall] of toolCallsInProgress) {\n // Parse arguments for TOOL_CALL_END\n let parsedInput: unknown = {}\n try {\n parsedInput = toolCall.arguments\n ? JSON.parse(toolCall.arguments)\n : {}\n } catch {\n parsedInput = {}\n }\n\n // Emit AG-UI TOOL_CALL_END\n yield asChunk({\n type: 'TOOL_CALL_END',\n toolCallId: toolCall.id,\n toolCallName: toolCall.name,\n toolName: toolCall.name,\n model: chunk.model || options.model,\n timestamp,\n input: parsedInput,\n })\n }\n }\n\n const computedFinishReason =\n choice.finish_reason === 'tool_calls' ||\n toolCallsInProgress.size > 0\n ? 'tool_calls'\n : 'stop'\n\n // Emit TEXT_MESSAGE_END if we had text content\n if (hasEmittedTextMessageStart) {\n yield asChunk({\n type: 'TEXT_MESSAGE_END',\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp,\n })\n }\n\n // Emit AG-UI RUN_FINISHED\n yield asChunk({\n type: 'RUN_FINISHED',\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: chunk.model || options.model,\n timestamp,\n usage: chunk.usage\n ? {\n promptTokens: chunk.usage.prompt_tokens || 0,\n completionTokens: chunk.usage.completion_tokens || 0,\n totalTokens: chunk.usage.total_tokens || 0,\n }\n : undefined,\n finishReason: computedFinishReason,\n })\n }\n }\n } catch (error: unknown) {\n const err = error as Error & { code?: string }\n logger.errors('grok stream ended with error', {\n error,\n source: 'grok.processGrokStreamChunks',\n })\n\n // Emit AG-UI RUN_ERROR\n yield asChunk({\n type: 'RUN_ERROR',\n runId: aguiState.runId,\n model: options.model,\n timestamp,\n message: err.message || 'Unknown error occurred',\n code: err.code,\n error: {\n message: err.message || 'Unknown error occurred',\n code: err.code,\n },\n })\n }\n }\n\n /**\n * Maps common options to Grok-specific Chat Completions format\n */\n private mapTextOptionsToGrok(\n options: TextOptions,\n ): OpenAI_SDK.Chat.Completions.ChatCompletionCreateParamsStreaming {\n const modelOptions = options.modelOptions as\n | Omit<\n InternalTextProviderOptions,\n 'max_tokens' | 'tools' | 'temperature' | 'input' | 'top_p'\n >\n | undefined\n\n if (modelOptions) {\n validateTextProviderOptions({\n ...modelOptions,\n model: options.model,\n })\n }\n\n const tools = options.tools\n ? convertToolsToProviderFormat(options.tools)\n : undefined\n\n // Build messages array with system prompts\n const messages: Array<OpenAI_SDK.Chat.Completions.ChatCompletionMessageParam> =\n []\n\n // Add system prompts first\n if (options.systemPrompts && options.systemPrompts.length > 0) {\n messages.push({\n role: 'system',\n content: options.systemPrompts.join('\\n'),\n })\n }\n\n // Convert messages\n for (const message of options.messages) {\n messages.push(this.convertMessageToGrok(message))\n }\n\n return {\n model: options.model,\n messages,\n temperature: options.temperature,\n max_tokens: options.maxTokens,\n top_p: options.topP,\n tools: tools as Array<OpenAI_SDK.Chat.Completions.ChatCompletionTool>,\n stream: true,\n stream_options: { include_usage: true },\n }\n }\n\n private convertMessageToGrok(\n message: ModelMessage,\n ): OpenAI_SDK.Chat.Completions.ChatCompletionMessageParam {\n // Handle tool messages\n if (message.role === 'tool') {\n return {\n role: 'tool',\n tool_call_id: message.toolCallId || '',\n content:\n typeof message.content === 'string'\n ? message.content\n : JSON.stringify(message.content),\n }\n }\n\n // Handle assistant messages\n if (message.role === 'assistant') {\n const toolCalls = message.toolCalls?.map((tc) => ({\n id: tc.id,\n type: 'function' as const,\n function: {\n name: tc.function.name,\n arguments:\n typeof tc.function.arguments === 'string'\n ? tc.function.arguments\n : JSON.stringify(tc.function.arguments),\n },\n }))\n\n return {\n role: 'assistant',\n content: this.extractTextContent(message.content),\n ...(toolCalls && toolCalls.length > 0 ? { tool_calls: toolCalls } : {}),\n }\n }\n\n // Handle user messages - support multimodal content\n const contentParts = this.normalizeContent(message.content)\n\n // If only text, use simple string format\n if (contentParts.length === 1 && contentParts[0]?.type === 'text') {\n return {\n role: 'user',\n content: contentParts[0].content,\n }\n }\n\n // Otherwise, use array format for multimodal\n const parts: Array<OpenAI_SDK.Chat.Completions.ChatCompletionContentPart> =\n []\n for (const part of contentParts) {\n if (part.type === 'text') {\n parts.push({ type: 'text', text: part.content })\n } else if (part.type === 'image') {\n const imageMetadata = part.metadata as GrokImageMetadata | undefined\n // For base64 data, construct a data URI using the mimeType from source\n const imageValue = part.source.value\n const imageUrl =\n part.source.type === 'data' && !imageValue.startsWith('data:')\n ? `data:${part.source.mimeType};base64,${imageValue}`\n : imageValue\n parts.push({\n type: 'image_url',\n image_url: {\n url: imageUrl,\n detail: imageMetadata?.detail || 'auto',\n },\n })\n }\n }\n\n return {\n role: 'user',\n content: parts.length > 0 ? parts : '',\n }\n }\n\n /**\n * Normalizes message content to an array of ContentPart.\n * Handles backward compatibility with string content.\n */\n private normalizeContent(\n content: string | null | Array<ContentPart>,\n ): Array<ContentPart> {\n if (content === null) {\n return []\n }\n if (typeof content === 'string') {\n return [{ type: 'text', content: content }]\n }\n return content\n }\n\n /**\n * Extracts text content from a content value that may be string, null, or ContentPart array.\n */\n private extractTextContent(\n content: string | null | Array<ContentPart>,\n ): string {\n if (content === null) {\n return ''\n }\n if (typeof content === 'string') {\n return content\n }\n // It's an array of ContentPart\n return content\n .filter((p) => p.type === 'text')\n .map((p) => p.content)\n .join('')\n }\n}\n\n/**\n * Creates a Grok text adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'grok-3', 'grok-4')\n * @param apiKey - Your xAI API key\n * @param config - Optional additional configuration\n * @returns Configured Grok text adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGrokText('grok-3', \"xai-...\");\n * // adapter has type-safe providerOptions for grok-3\n * ```\n */\nexport function createGrokText<\n TModel extends (typeof GROK_CHAT_MODELS)[number],\n>(\n model: TModel,\n apiKey: string,\n config?: Omit<GrokTextConfig, 'apiKey'>,\n): GrokTextAdapter<TModel> {\n return new GrokTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Grok text adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * Looks for `XAI_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'grok-3', 'grok-4')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Grok text adapter instance with resolved types\n * @throws Error if XAI_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses XAI_API_KEY from environment\n * const adapter = grokText('grok-3');\n *\n * const stream = chat({\n * adapter,\n * messages: [{ role: \"user\", content: \"Hello!\" }]\n * });\n * ```\n */\nexport function grokText<TModel extends (typeof GROK_CHAT_MODELS)[number]>(\n model: TModel,\n config?: Omit<GrokTextConfig, 'apiKey'>,\n): GrokTextAdapter<TModel> {\n const apiKey = getGrokApiKeyFromEnv()\n return createGrokText(model, apiKey, config)\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-grok",
3
- "version": "0.6.7",
3
+ "version": "0.6.8",
4
4
  "description": "Grok (xAI) adapter for TanStack AI",
5
5
  "author": "",
6
6
  "license": "MIT",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "peerDependencies": {
44
44
  "zod": "^4.0.0",
45
- "@tanstack/ai": "^0.12.0"
45
+ "@tanstack/ai": "^0.13.0"
46
46
  },
47
47
  "scripts": {
48
48
  "build": "vite build",
@@ -56,22 +56,35 @@ export class GrokImageAdapter<
56
56
  async generateImages(
57
57
  options: ImageGenerationOptions<GrokImageProviderOptions>,
58
58
  ): Promise<ImageGenerationResult> {
59
- const { model, prompt, numberOfImages, size } = options
59
+ const { model, prompt, numberOfImages, size, logger } = options
60
60
 
61
- // Validate inputs
62
- validatePrompt({ prompt, model })
63
- validateImageSize(model, size)
64
- validateNumberOfImages(model, numberOfImages)
61
+ logger.request(`activity=generateImage provider=grok model=${this.model}`, {
62
+ provider: 'grok',
63
+ model: this.model,
64
+ })
65
65
 
66
- // Build request based on model type
67
- const request = this.buildRequest(options)
66
+ try {
67
+ // Validate inputs
68
+ validatePrompt({ prompt, model })
69
+ validateImageSize(model, size)
70
+ validateNumberOfImages(model, numberOfImages)
68
71
 
69
- const response = await this.client.images.generate({
70
- ...request,
71
- stream: false,
72
- })
72
+ // Build request based on model type
73
+ const request = this.buildRequest(options)
73
74
 
74
- return this.transformResponse(model, response)
75
+ const response = await this.client.images.generate({
76
+ ...request,
77
+ stream: false,
78
+ })
79
+
80
+ return this.transformResponse(model, response)
81
+ } catch (error) {
82
+ logger.errors('grok.generateImage fatal', {
83
+ error,
84
+ source: 'grok.generateImage',
85
+ })
86
+ throw error
87
+ }
75
88
  }
76
89
 
77
90
  private buildRequest(
@@ -47,36 +47,51 @@ export class GrokSummarizeAdapter<
47
47
  }
48
48
 
49
49
  async summarize(options: SummarizationOptions): Promise<SummarizationResult> {
50
+ const { logger } = options
50
51
  const systemPrompt = this.buildSummarizationPrompt(options)
51
52
 
53
+ logger.request(`activity=summarize provider=grok`, {
54
+ provider: 'grok',
55
+ model: options.model,
56
+ })
57
+
52
58
  // Use the text adapter's streaming and collect the result
53
59
  let summary = ''
54
60
  const id = ''
55
61
  let model = options.model
56
62
  let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 }
57
63
 
58
- for await (const chunk of this.textAdapter.chatStream({
59
- model: options.model,
60
- messages: [{ role: 'user', content: options.text }],
61
- systemPrompts: [systemPrompt],
62
- maxTokens: options.maxLength,
63
- temperature: 0.3,
64
- })) {
65
- // AG-UI TEXT_MESSAGE_CONTENT event
66
- if (chunk.type === 'TEXT_MESSAGE_CONTENT') {
67
- if (chunk.content) {
68
- summary = chunk.content
69
- } else {
70
- summary += chunk.delta
64
+ try {
65
+ for await (const chunk of this.textAdapter.chatStream({
66
+ model: options.model,
67
+ messages: [{ role: 'user', content: options.text }],
68
+ systemPrompts: [systemPrompt],
69
+ maxTokens: options.maxLength,
70
+ temperature: 0.3,
71
+ logger,
72
+ })) {
73
+ // AG-UI TEXT_MESSAGE_CONTENT event
74
+ if (chunk.type === 'TEXT_MESSAGE_CONTENT') {
75
+ if (chunk.content) {
76
+ summary = chunk.content
77
+ } else {
78
+ summary += chunk.delta
79
+ }
80
+ model = chunk.model || model
71
81
  }
72
- model = chunk.model || model
73
- }
74
- // AG-UI RUN_FINISHED event
75
- if (chunk.type === 'RUN_FINISHED') {
76
- if (chunk.usage) {
77
- usage = chunk.usage
82
+ // AG-UI RUN_FINISHED event
83
+ if (chunk.type === 'RUN_FINISHED') {
84
+ if (chunk.usage) {
85
+ usage = chunk.usage
86
+ }
78
87
  }
79
88
  }
89
+ } catch (error) {
90
+ logger.errors('grok.summarize fatal', {
91
+ error,
92
+ source: 'grok.summarize',
93
+ })
94
+ throw error
80
95
  }
81
96
 
82
97
  return { id, model, summary, usage }
@@ -85,16 +100,32 @@ export class GrokSummarizeAdapter<
85
100
  async *summarizeStream(
86
101
  options: SummarizationOptions,
87
102
  ): AsyncIterable<StreamChunk> {
103
+ const { logger } = options
88
104
  const systemPrompt = this.buildSummarizationPrompt(options)
89
105
 
90
- // Delegate directly to the text adapter's streaming
91
- yield* this.textAdapter.chatStream({
106
+ logger.request(`activity=summarize provider=grok`, {
107
+ provider: 'grok',
92
108
  model: options.model,
93
- messages: [{ role: 'user', content: options.text }],
94
- systemPrompts: [systemPrompt],
95
- maxTokens: options.maxLength,
96
- temperature: 0.3,
109
+ stream: true,
97
110
  })
111
+
112
+ try {
113
+ // Delegate directly to the text adapter's streaming
114
+ yield* this.textAdapter.chatStream({
115
+ model: options.model,
116
+ messages: [{ role: 'user', content: options.text }],
117
+ systemPrompts: [systemPrompt],
118
+ maxTokens: options.maxLength,
119
+ temperature: 0.3,
120
+ logger,
121
+ })
122
+ } catch (error) {
123
+ logger.errors('grok.summarize fatal', {
124
+ error,
125
+ source: 'grok.summarize',
126
+ })
127
+ throw error
128
+ }
98
129
  }
99
130
 
100
131
  private buildSummarizationPrompt(options: SummarizationOptions): string {
@@ -18,6 +18,7 @@ import type {
18
18
  StructuredOutputOptions,
19
19
  StructuredOutputResult,
20
20
  } from '@tanstack/ai/adapters'
21
+ import type { InternalLogger } from '@tanstack/ai/adapter-internals'
21
22
  import type OpenAI_SDK from 'openai'
22
23
  import type {
23
24
  ContentPart,
@@ -91,6 +92,7 @@ export class GrokTextAdapter<
91
92
  ): AsyncIterable<StreamChunk> {
92
93
  const requestParams = this.mapTextOptionsToGrok(options)
93
94
  const timestamp = Date.now()
95
+ const { logger } = options
94
96
 
95
97
  // AG-UI lifecycle tracking (mutable state object for ESLint compatibility)
96
98
  const aguiState = {
@@ -102,12 +104,16 @@ export class GrokTextAdapter<
102
104
  }
103
105
 
104
106
  try {
107
+ logger.request(
108
+ `activity=chat provider=grok model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,
109
+ { provider: 'grok', model: this.model },
110
+ )
105
111
  const stream = await this.client.chat.completions.create({
106
112
  ...requestParams,
107
113
  stream: true,
108
114
  })
109
115
 
110
- yield* this.processGrokStreamChunks(stream, options, aguiState)
116
+ yield* this.processGrokStreamChunks(stream, options, aguiState, logger)
111
117
  } catch (error: unknown) {
112
118
  const err = error as Error & { code?: string }
113
119
 
@@ -137,10 +143,10 @@ export class GrokTextAdapter<
137
143
  },
138
144
  })
139
145
 
140
- console.error('>>> chatStream: Fatal error during response creation <<<')
141
- console.error('>>> Error message:', err.message)
142
- console.error('>>> Error stack:', err.stack)
143
- console.error('>>> Full error:', err)
146
+ logger.errors('grok.chatStream fatal', {
147
+ error,
148
+ source: 'grok.chatStream',
149
+ })
144
150
  }
145
151
  }
146
152
 
@@ -161,6 +167,7 @@ export class GrokTextAdapter<
161
167
  ): Promise<StructuredOutputResult<unknown>> {
162
168
  const { chatOptions, outputSchema } = options
163
169
  const requestParams = this.mapTextOptionsToGrok(chatOptions)
170
+ const { logger } = chatOptions
164
171
 
165
172
  // Apply Grok-specific transformations for structured output compatibility
166
173
  const jsonSchema = makeGrokStructuredOutputCompatible(
@@ -169,6 +176,10 @@ export class GrokTextAdapter<
169
176
  )
170
177
 
171
178
  try {
179
+ logger.request(
180
+ `activity=chat provider=grok model=${this.model} messages=${chatOptions.messages.length} tools=${chatOptions.tools?.length ?? 0} stream=false`,
181
+ { provider: 'grok', model: this.model },
182
+ )
172
183
  const response = await this.client.chat.completions.create({
173
184
  ...requestParams,
174
185
  stream: false,
@@ -204,9 +215,10 @@ export class GrokTextAdapter<
204
215
  rawText,
205
216
  }
206
217
  } catch (error: unknown) {
207
- const err = error as Error
208
- console.error('>>> structuredOutput: Error during response creation <<<')
209
- console.error('>>> Error message:', err.message)
218
+ logger.errors('grok.structuredOutput fatal', {
219
+ error,
220
+ source: 'grok.structuredOutput',
221
+ })
210
222
  throw error
211
223
  }
212
224
  }
@@ -221,6 +233,7 @@ export class GrokTextAdapter<
221
233
  timestamp: number
222
234
  hasEmittedRunStarted: boolean
223
235
  },
236
+ logger: InternalLogger,
224
237
  ): AsyncIterable<StreamChunk> {
225
238
  let accumulatedContent = ''
226
239
  const timestamp = aguiState.timestamp
@@ -239,6 +252,7 @@ export class GrokTextAdapter<
239
252
 
240
253
  try {
241
254
  for await (const chunk of stream) {
255
+ logger.provider(`provider=grok`, { chunk })
242
256
  const choice = chunk.choices[0]
243
257
 
244
258
  if (!choice) continue
@@ -408,7 +422,10 @@ export class GrokTextAdapter<
408
422
  }
409
423
  } catch (error: unknown) {
410
424
  const err = error as Error & { code?: string }
411
- console.log('[Grok Adapter] Stream ended with error:', err.message)
425
+ logger.errors('grok stream ended with error', {
426
+ error,
427
+ source: 'grok.processGrokStreamChunks',
428
+ })
412
429
 
413
430
  // Emit AG-UI RUN_ERROR
414
431
  yield asChunk({