@tanstack/ai-groq 0.6.6 → 0.7.0

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.
@@ -37,13 +37,16 @@ export declare class GroqTextAdapter<TModel extends (typeof GROQ_CHAT_MODELS)[nu
37
37
  input?: unknown;
38
38
  error: string;
39
39
  } | undefined;
40
- protected makeStructuredOutputCompatible(schema: Record<string, any>, originalRequired?: Array<string>): Record<string, any>;
40
+ protected makeStructuredOutputCompatibleWithMap(schema: Record<string, any>, originalRequired?: Array<string>): {
41
+ schema: Record<string, any>;
42
+ nullWideningMap: import('@tanstack/ai-utils').NullWideningMap | undefined;
43
+ };
41
44
  protected processStreamChunks(stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>, options: TextOptions, aguiState: {
42
45
  runId: string;
43
46
  threadId: string;
44
47
  messageId: string;
45
48
  hasEmittedRunStarted: boolean;
46
- }): AsyncGenerator<import('@tanstack/ai').AGUIEvent, void, any>;
49
+ }): AsyncGenerator<import('@tanstack/ai').AdapterYieldChunk, void, any>;
47
50
  /**
48
51
  * Surfaces Groq's reasoning deltas during streaming structured output.
49
52
  * Groq emits `delta.reasoning` (or legacy `delta.reasoning_content`) on
@@ -1,5 +1,5 @@
1
1
  import { getGroqApiKeyFromEnv, withGroqDefaults } from "../utils/client.js";
2
- import { makeGroqStructuredOutputCompatible } from "../utils/schema-converter.js";
2
+ import { makeGroqStructuredOutputCompatibleWithMap } from "../utils/schema-converter.js";
3
3
  import OpenAI from "openai";
4
4
  import { OpenAIBaseChatCompletionsTextAdapter } from "@tanstack/openai-base";
5
5
  //#region src/adapters/text.ts
@@ -51,8 +51,8 @@ var GroqTextAdapter = class extends OpenAIBaseChatCompletionsTextAdapter {
51
51
  error: typeof rawEvent.message === "string" && rawEvent.message.length > 0 ? rawEvent.message : fallbackMessage
52
52
  };
53
53
  }
54
- makeStructuredOutputCompatible(schema, originalRequired) {
55
- return makeGroqStructuredOutputCompatible(schema, originalRequired);
54
+ makeStructuredOutputCompatibleWithMap(schema, originalRequired) {
55
+ return makeGroqStructuredOutputCompatibleWithMap(schema, originalRequired);
56
56
  }
57
57
  async *processStreamChunks(stream, options, aguiState) {
58
58
  yield* super.processStreamChunks(promoteGroqUsage(stream), options, aguiState);
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","names":[],"sources":["../../../src/adapters/text.ts"],"sourcesContent":["import OpenAI from 'openai'\nimport { OpenAIBaseChatCompletionsTextAdapter } from '@tanstack/openai-base'\nimport { getGroqApiKeyFromEnv, withGroqDefaults } from '../utils/client'\nimport { makeGroqStructuredOutputCompatible } from '../utils/schema-converter'\nimport type { Modality, TextOptions } from '@tanstack/ai'\nimport type {\n GROQ_CHAT_MODELS,\n GroqChatModelToolCapabilitiesByName,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\nimport type { GroqMessageMetadataByModality } from '../message-types'\nimport type { GroqClientConfig } from '../utils/client'\n\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof GroqChatModelToolCapabilitiesByName\n ? NonNullable<GroqChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n/**\n * Configuration for Groq text adapter\n */\nexport interface GroqTextConfig extends GroqClientConfig {}\n\n/**\n * Re-export of the public provider options type\n */\nexport type { ExternalTextProviderOptions as GroqTextProviderOptions } from '../text/text-provider-options'\n\n/**\n * Groq Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Groq chat/text completion. Groq exposes an\n * OpenAI-compatible Chat Completions endpoint at `/openai/v1`, so we drive\n * it with the OpenAI SDK via a `baseURL` override (the same pattern as\n * `ai-grok`).\n *\n * Quirk: when usage is present on a stream, Groq historically delivered it\n * under `chunk.x_groq.usage` rather than `chunk.usage`. The override below\n * promotes it to the standard location so the base's RUN_FINISHED usage\n * accounting works unchanged.\n */\nexport class GroqTextAdapter<\n TModel extends (typeof GROQ_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 OpenAIBaseChatCompletionsTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n GroqMessageMetadataByModality,\n TToolCapabilities\n> {\n override readonly kind = 'text' as const\n override readonly name = 'groq' as const\n\n constructor(config: GroqTextConfig, model: TModel) {\n super(model, 'groq', new OpenAI(withGroqDefaults(config)))\n }\n\n protected override extractRejectedToolCall(\n rawEvent: unknown,\n fallbackMessage: string,\n ):\n | {\n toolName: string\n arguments: string\n input?: unknown\n error: string\n }\n | undefined {\n if (\n !isRecord(rawEvent) ||\n rawEvent.code !== 'tool_use_failed' ||\n typeof rawEvent.failed_generation !== 'string'\n ) {\n return undefined\n }\n\n let failedGeneration: unknown\n try {\n failedGeneration = JSON.parse(rawEvent.failed_generation)\n } catch {\n return undefined\n }\n if (\n !isRecord(failedGeneration) ||\n typeof failedGeneration.name !== 'string' ||\n failedGeneration.name.trim().length === 0\n ) {\n return undefined\n }\n\n const rawArguments = failedGeneration.arguments\n let argumentsJson: string\n let input: unknown\n if (typeof rawArguments === 'string') {\n argumentsJson = rawArguments\n try {\n const parsed: unknown = JSON.parse(rawArguments)\n if (isRecord(parsed)) input = parsed\n } catch {\n // The provider-rejected call remains non-executable with its raw input.\n }\n } else if (isRecord(rawArguments)) {\n argumentsJson = JSON.stringify(rawArguments)\n input = rawArguments\n } else {\n return undefined\n }\n\n return {\n toolName: failedGeneration.name,\n arguments: argumentsJson,\n ...(input !== undefined && { input }),\n error:\n typeof rawEvent.message === 'string' && rawEvent.message.length > 0\n ? rawEvent.message\n : fallbackMessage,\n }\n }\n\n protected override makeStructuredOutputCompatible(\n schema: Record<string, any>,\n originalRequired?: Array<string>,\n ): Record<string, any> {\n return makeGroqStructuredOutputCompatible(schema, originalRequired)\n }\n\n protected override async *processStreamChunks(\n stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>,\n options: TextOptions,\n aguiState: {\n runId: string\n threadId: string\n messageId: string\n hasEmittedRunStarted: boolean\n },\n ) {\n yield* super.processStreamChunks(\n promoteGroqUsage(stream),\n options,\n aguiState,\n )\n }\n\n /**\n * Surfaces Groq's reasoning deltas during streaming structured output.\n * Groq emits `delta.reasoning` (or legacy `delta.reasoning_content`) on\n * reasoning models when the caller sets `reasoning_format: 'parsed'` in\n * modelOptions. The base's chatStream and structuredOutputStream both\n * route reasoning through this hook.\n */\n protected override extractReasoning(\n chunk: OpenAI.Chat.Completions.ChatCompletionChunk,\n ): { text: string } | undefined {\n const delta = chunk.choices[0]?.delta as\n | { reasoning?: unknown; reasoning_content?: unknown }\n | undefined\n const raw = delta?.reasoning ?? delta?.reasoning_content\n if (typeof raw === 'string' && raw.length > 0) {\n return { text: raw }\n }\n return undefined\n }\n\n /**\n * Groq's API rejects `response_format: json_schema` together with `tools`\n * + `stream` (returns 400 — see Groq Structured Outputs docs:\n * \"Streaming and tool use are not currently supported with Structured\n * Outputs.\"). Force the engine onto the legacy finalization path even\n * though the OpenAI Chat Completions base would otherwise opt in.\n */\n override supportsCombinedToolsAndSchema(): boolean {\n return false\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * Promotes Groq's non-standard `x_groq.usage` to the standard `chunk.usage`\n * slot the base reads. Pass-through for chunks that already carry usage at\n * the documented location.\n */\nasync function* promoteGroqUsage(\n stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>,\n): AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk> {\n for await (const chunk of stream) {\n const groqChunk = chunk as typeof chunk & {\n x_groq?: { usage?: OpenAI.Chat.Completions.ChatCompletionChunk['usage'] }\n }\n if (!chunk.usage && groqChunk.x_groq?.usage) {\n yield { ...chunk, usage: groqChunk.x_groq.usage }\n } else {\n yield chunk\n }\n }\n}\n\n/**\n * Creates a Groq text adapter with explicit API key.\n *\n * @example\n * ```typescript\n * const adapter = createGroqText('llama-3.3-70b-versatile', \"gsk_...\");\n * ```\n */\nexport function createGroqText<\n TModel extends (typeof GROQ_CHAT_MODELS)[number],\n>(\n model: TModel,\n apiKey: string,\n config?: Omit<GroqTextConfig, 'apiKey'>,\n): GroqTextAdapter<TModel> {\n return new GroqTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Groq text adapter with API key from `GROQ_API_KEY`.\n *\n * @example\n * ```typescript\n * const adapter = groqText('llama-3.3-70b-versatile');\n * ```\n */\nexport function groqText<TModel extends (typeof GROQ_CHAT_MODELS)[number]>(\n model: TModel,\n config?: Omit<GroqTextConfig, 'apiKey'>,\n): GroqTextAdapter<TModel> {\n const apiKey = getGroqApiKeyFromEnv()\n return createGroqText(model, apiKey, config)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA0CA,IAAa,kBAAb,cAOU,qCAMR;CACA,OAAyB;CACzB,OAAyB;CAEzB,YAAY,QAAwB,OAAe;EACjD,MAAM,OAAO,QAAQ,IAAI,OAAO,iBAAiB,MAAM,CAAC,CAAC;CAC3D;CAEA,wBACE,UACA,iBAQY;EACZ,IACE,CAAC,SAAS,QAAQ,KAClB,SAAS,SAAS,qBAClB,OAAO,SAAS,sBAAsB,UAEtC;EAGF,IAAI;EACJ,IAAI;GACF,mBAAmB,KAAK,MAAM,SAAS,iBAAiB;EAC1D,QAAQ;GACN;EACF;EACA,IACE,CAAC,SAAS,gBAAgB,KAC1B,OAAO,iBAAiB,SAAS,YACjC,iBAAiB,KAAK,KAAK,CAAC,CAAC,WAAW,GAExC;EAGF,MAAM,eAAe,iBAAiB;EACtC,IAAI;EACJ,IAAI;EACJ,IAAI,OAAO,iBAAiB,UAAU;GACpC,gBAAgB;GAChB,IAAI;IACF,MAAM,SAAkB,KAAK,MAAM,YAAY;IAC/C,IAAI,SAAS,MAAM,GAAG,QAAQ;GAChC,QAAQ,CAER;EACF,OAAO,IAAI,SAAS,YAAY,GAAG;GACjC,gBAAgB,KAAK,UAAU,YAAY;GAC3C,QAAQ;EACV,OACE;EAGF,OAAO;GACL,UAAU,iBAAiB;GAC3B,WAAW;GACX,GAAI,UAAU,KAAA,KAAa,EAAE,MAAM;GACnC,OACE,OAAO,SAAS,YAAY,YAAY,SAAS,QAAQ,SAAS,IAC9D,SAAS,UACT;EACR;CACF;CAEA,+BACE,QACA,kBACqB;EACrB,OAAO,mCAAmC,QAAQ,gBAAgB;CACpE;CAEA,OAA0B,oBACxB,QACA,SACA,WAMA;EACA,OAAO,MAAM,oBACX,iBAAiB,MAAM,GACvB,SACA,SACF;CACF;;;;;;;;CASA,iBACE,OAC8B;EAC9B,MAAM,QAAQ,MAAM,QAAQ,EAAE,EAAE;EAGhC,MAAM,MAAM,OAAO,aAAa,OAAO;EACvC,IAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,GAC1C,OAAO,EAAE,MAAM,IAAI;CAGvB;;;;;;;;CASA,iCAAmD;EACjD,OAAO;CACT;AACF;AAEA,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;;;;;;AAOA,gBAAgB,iBACd,QAC4D;CAC5D,WAAW,MAAM,SAAS,QAAQ;EAChC,MAAM,YAAY;EAGlB,IAAI,CAAC,MAAM,SAAS,UAAU,QAAQ,OACpC,MAAM;GAAE,GAAG;GAAO,OAAO,UAAU,OAAO;EAAM;OAEhD,MAAM;CAEV;AACF;;;;;;;;;AAUA,SAAgB,eAGd,OACA,QACA,QACyB;CACzB,OAAO,IAAI,gBAAgB;EAAE;EAAQ,GAAG;CAAO,GAAG,KAAK;AACzD;;;;;;;;;AAUA,SAAgB,SACd,OACA,QACyB;CAEzB,OAAO,eAAe,OADP,qBACc,GAAQ,MAAM;AAC7C"}
1
+ {"version":3,"file":"text.js","names":[],"sources":["../../../src/adapters/text.ts"],"sourcesContent":["import OpenAI from 'openai'\nimport { OpenAIBaseChatCompletionsTextAdapter } from '@tanstack/openai-base'\nimport { getGroqApiKeyFromEnv, withGroqDefaults } from '../utils/client'\nimport { makeGroqStructuredOutputCompatibleWithMap } from '../utils/schema-converter'\nimport type { Modality, TextOptions } from '@tanstack/ai'\nimport type {\n GROQ_CHAT_MODELS,\n GroqChatModelToolCapabilitiesByName,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\nimport type { GroqMessageMetadataByModality } from '../message-types'\nimport type { GroqClientConfig } from '../utils/client'\n\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof GroqChatModelToolCapabilitiesByName\n ? NonNullable<GroqChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n/**\n * Configuration for Groq text adapter\n */\nexport interface GroqTextConfig extends GroqClientConfig {}\n\n/**\n * Re-export of the public provider options type\n */\nexport type { ExternalTextProviderOptions as GroqTextProviderOptions } from '../text/text-provider-options'\n\n/**\n * Groq Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Groq chat/text completion. Groq exposes an\n * OpenAI-compatible Chat Completions endpoint at `/openai/v1`, so we drive\n * it with the OpenAI SDK via a `baseURL` override (the same pattern as\n * `ai-grok`).\n *\n * Quirk: when usage is present on a stream, Groq historically delivered it\n * under `chunk.x_groq.usage` rather than `chunk.usage`. The override below\n * promotes it to the standard location so the base's RUN_FINISHED usage\n * accounting works unchanged.\n */\nexport class GroqTextAdapter<\n TModel extends (typeof GROQ_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 OpenAIBaseChatCompletionsTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n GroqMessageMetadataByModality,\n TToolCapabilities\n> {\n override readonly kind = 'text' as const\n override readonly name = 'groq' as const\n\n constructor(config: GroqTextConfig, model: TModel) {\n super(model, 'groq', new OpenAI(withGroqDefaults(config)))\n }\n\n protected override extractRejectedToolCall(\n rawEvent: unknown,\n fallbackMessage: string,\n ):\n | {\n toolName: string\n arguments: string\n input?: unknown\n error: string\n }\n | undefined {\n if (\n !isRecord(rawEvent) ||\n rawEvent.code !== 'tool_use_failed' ||\n typeof rawEvent.failed_generation !== 'string'\n ) {\n return undefined\n }\n\n let failedGeneration: unknown\n try {\n failedGeneration = JSON.parse(rawEvent.failed_generation)\n } catch {\n return undefined\n }\n if (\n !isRecord(failedGeneration) ||\n typeof failedGeneration.name !== 'string' ||\n failedGeneration.name.trim().length === 0\n ) {\n return undefined\n }\n\n const rawArguments = failedGeneration.arguments\n let argumentsJson: string\n let input: unknown\n if (typeof rawArguments === 'string') {\n argumentsJson = rawArguments\n try {\n const parsed: unknown = JSON.parse(rawArguments)\n if (isRecord(parsed)) input = parsed\n } catch {\n // The provider-rejected call remains non-executable with its raw input.\n }\n } else if (isRecord(rawArguments)) {\n argumentsJson = JSON.stringify(rawArguments)\n input = rawArguments\n } else {\n return undefined\n }\n\n return {\n toolName: failedGeneration.name,\n arguments: argumentsJson,\n ...(input !== undefined && { input }),\n error:\n typeof rawEvent.message === 'string' && rawEvent.message.length > 0\n ? rawEvent.message\n : fallbackMessage,\n }\n }\n\n protected override makeStructuredOutputCompatibleWithMap(\n schema: Record<string, any>,\n originalRequired?: Array<string>,\n ) {\n return makeGroqStructuredOutputCompatibleWithMap(schema, originalRequired)\n }\n\n protected override async *processStreamChunks(\n stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>,\n options: TextOptions,\n aguiState: {\n runId: string\n threadId: string\n messageId: string\n hasEmittedRunStarted: boolean\n },\n ) {\n yield* super.processStreamChunks(\n promoteGroqUsage(stream),\n options,\n aguiState,\n )\n }\n\n /**\n * Surfaces Groq's reasoning deltas during streaming structured output.\n * Groq emits `delta.reasoning` (or legacy `delta.reasoning_content`) on\n * reasoning models when the caller sets `reasoning_format: 'parsed'` in\n * modelOptions. The base's chatStream and structuredOutputStream both\n * route reasoning through this hook.\n */\n protected override extractReasoning(\n chunk: OpenAI.Chat.Completions.ChatCompletionChunk,\n ): { text: string } | undefined {\n const delta = chunk.choices[0]?.delta as\n | { reasoning?: unknown; reasoning_content?: unknown }\n | undefined\n const raw = delta?.reasoning ?? delta?.reasoning_content\n if (typeof raw === 'string' && raw.length > 0) {\n return { text: raw }\n }\n return undefined\n }\n\n /**\n * Groq's API rejects `response_format: json_schema` together with `tools`\n * + `stream` (returns 400 — see Groq Structured Outputs docs:\n * \"Streaming and tool use are not currently supported with Structured\n * Outputs.\"). Force the engine onto the legacy finalization path even\n * though the OpenAI Chat Completions base would otherwise opt in.\n */\n override supportsCombinedToolsAndSchema(): boolean {\n return false\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * Promotes Groq's non-standard `x_groq.usage` to the standard `chunk.usage`\n * slot the base reads. Pass-through for chunks that already carry usage at\n * the documented location.\n */\nasync function* promoteGroqUsage(\n stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>,\n): AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk> {\n for await (const chunk of stream) {\n const groqChunk = chunk as typeof chunk & {\n x_groq?: { usage?: OpenAI.Chat.Completions.ChatCompletionChunk['usage'] }\n }\n if (!chunk.usage && groqChunk.x_groq?.usage) {\n yield { ...chunk, usage: groqChunk.x_groq.usage }\n } else {\n yield chunk\n }\n }\n}\n\n/**\n * Creates a Groq text adapter with explicit API key.\n *\n * @example\n * ```typescript\n * const adapter = createGroqText('llama-3.3-70b-versatile', \"gsk_...\");\n * ```\n */\nexport function createGroqText<\n TModel extends (typeof GROQ_CHAT_MODELS)[number],\n>(\n model: TModel,\n apiKey: string,\n config?: Omit<GroqTextConfig, 'apiKey'>,\n): GroqTextAdapter<TModel> {\n return new GroqTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Groq text adapter with API key from `GROQ_API_KEY`.\n *\n * @example\n * ```typescript\n * const adapter = groqText('llama-3.3-70b-versatile');\n * ```\n */\nexport function groqText<TModel extends (typeof GROQ_CHAT_MODELS)[number]>(\n model: TModel,\n config?: Omit<GroqTextConfig, 'apiKey'>,\n): GroqTextAdapter<TModel> {\n const apiKey = getGroqApiKeyFromEnv()\n return createGroqText(model, apiKey, config)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA0CA,IAAa,kBAAb,cAOU,qCAMR;CACA,OAAyB;CACzB,OAAyB;CAEzB,YAAY,QAAwB,OAAe;EACjD,MAAM,OAAO,QAAQ,IAAI,OAAO,iBAAiB,MAAM,CAAC,CAAC;CAC3D;CAEA,wBACE,UACA,iBAQY;EACZ,IACE,CAAC,SAAS,QAAQ,KAClB,SAAS,SAAS,qBAClB,OAAO,SAAS,sBAAsB,UAEtC;EAGF,IAAI;EACJ,IAAI;GACF,mBAAmB,KAAK,MAAM,SAAS,iBAAiB;EAC1D,QAAQ;GACN;EACF;EACA,IACE,CAAC,SAAS,gBAAgB,KAC1B,OAAO,iBAAiB,SAAS,YACjC,iBAAiB,KAAK,KAAK,CAAC,CAAC,WAAW,GAExC;EAGF,MAAM,eAAe,iBAAiB;EACtC,IAAI;EACJ,IAAI;EACJ,IAAI,OAAO,iBAAiB,UAAU;GACpC,gBAAgB;GAChB,IAAI;IACF,MAAM,SAAkB,KAAK,MAAM,YAAY;IAC/C,IAAI,SAAS,MAAM,GAAG,QAAQ;GAChC,QAAQ,CAER;EACF,OAAO,IAAI,SAAS,YAAY,GAAG;GACjC,gBAAgB,KAAK,UAAU,YAAY;GAC3C,QAAQ;EACV,OACE;EAGF,OAAO;GACL,UAAU,iBAAiB;GAC3B,WAAW;GACX,GAAI,UAAU,KAAA,KAAa,EAAE,MAAM;GACnC,OACE,OAAO,SAAS,YAAY,YAAY,SAAS,QAAQ,SAAS,IAC9D,SAAS,UACT;EACR;CACF;CAEA,sCACE,QACA,kBACA;EACA,OAAO,0CAA0C,QAAQ,gBAAgB;CAC3E;CAEA,OAA0B,oBACxB,QACA,SACA,WAMA;EACA,OAAO,MAAM,oBACX,iBAAiB,MAAM,GACvB,SACA,SACF;CACF;;;;;;;;CASA,iBACE,OAC8B;EAC9B,MAAM,QAAQ,MAAM,QAAQ,EAAE,EAAE;EAGhC,MAAM,MAAM,OAAO,aAAa,OAAO;EACvC,IAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,GAC1C,OAAO,EAAE,MAAM,IAAI;CAGvB;;;;;;;;CASA,iCAAmD;EACjD,OAAO;CACT;AACF;AAEA,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;;;;;;AAOA,gBAAgB,iBACd,QAC4D;CAC5D,WAAW,MAAM,SAAS,QAAQ;EAChC,MAAM,YAAY;EAGlB,IAAI,CAAC,MAAM,SAAS,UAAU,QAAQ,OACpC,MAAM;GAAE,GAAG;GAAO,OAAO,UAAU,OAAO;EAAM;OAEhD,MAAM;CAEV;AACF;;;;;;;;;AAUA,SAAgB,eAGd,OACA,QACA,QACyB;CACzB,OAAO,IAAI,gBAAgB;EAAE;EAAQ,GAAG;CAAO,GAAG,KAAK;AACzD;;;;;;;;;AAUA,SAAgB,SACd,OACA,QACyB;CAEzB,OAAO,eAAe,OADP,qBACc,GAAQ,MAAM;AAC7C"}
@@ -0,0 +1 @@
1
+ export declare const groqByok: import('@tanstack/ai/byok').ByokProvider<"groq">;
@@ -0,0 +1,11 @@
1
+ import { defineByokProvider } from "@tanstack/ai/byok";
2
+ //#region src/byok.ts
3
+ var groqByok = defineByokProvider({
4
+ id: "groq",
5
+ label: "Groq",
6
+ env: "GROQ_API_KEY"
7
+ });
8
+ //#endregion
9
+ export { groqByok };
10
+
11
+ //# sourceMappingURL=byok.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"byok.js","names":[],"sources":["../../src/byok.ts"],"sourcesContent":["import { defineByokProvider } from '@tanstack/ai/byok'\n\nexport const groqByok = defineByokProvider({\n id: 'groq',\n label: 'Groq',\n env: 'GROQ_API_KEY',\n})\n"],"mappings":";;AAEA,IAAa,WAAW,mBAAmB;CACzC,IAAI;CACJ,OAAO;CACP,KAAK;AACP,CAAC"}
@@ -14,4 +14,8 @@
14
14
  * @param originalRequired - Original required array (to know which fields were optional)
15
15
  * @returns Transformed schema compatible with Groq structured output
16
16
  */
17
+ export declare function makeGroqStructuredOutputCompatibleWithMap(schema: Record<string, any>, originalRequired?: Array<string>): {
18
+ schema: Record<string, any>;
19
+ nullWideningMap: import('@tanstack/ai-utils').NullWideningMap | undefined;
20
+ };
17
21
  export declare function makeGroqStructuredOutputCompatible(schema: Record<string, any>, originalRequired?: Array<string>): Record<string, any>;
@@ -1,4 +1,4 @@
1
- import { makeStructuredOutputCompatible } from "@tanstack/openai-base";
1
+ import { makeStructuredOutputCompatibleWithMap } from "@tanstack/openai-base";
2
2
  //#region src/utils/schema-converter.ts
3
3
  /**
4
4
  * Recursively removes `required: []` from a schema object.
@@ -64,11 +64,18 @@ function normalizeObjectSchemas(schema) {
64
64
  * @param originalRequired - Original required array (to know which fields were optional)
65
65
  * @returns Transformed schema compatible with Groq structured output
66
66
  */
67
- function makeGroqStructuredOutputCompatible(schema, originalRequired = []) {
67
+ function makeGroqStructuredOutputCompatibleWithMap(schema, originalRequired = []) {
68
68
  const normalised = normalizeObjectSchemas(schema);
69
- return removeEmptyRequired(makeStructuredOutputCompatible(normalised, originalRequired));
69
+ const { schema: converted, nullWideningMap } = makeStructuredOutputCompatibleWithMap(normalised, originalRequired);
70
+ return {
71
+ schema: removeEmptyRequired(converted),
72
+ nullWideningMap
73
+ };
74
+ }
75
+ function makeGroqStructuredOutputCompatible(schema, originalRequired = []) {
76
+ return makeGroqStructuredOutputCompatibleWithMap(schema, originalRequired).schema;
70
77
  }
71
78
  //#endregion
72
- export { makeGroqStructuredOutputCompatible };
79
+ export { makeGroqStructuredOutputCompatible, makeGroqStructuredOutputCompatibleWithMap };
73
80
 
74
81
  //# sourceMappingURL=schema-converter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema-converter.js","names":[],"sources":["../../../src/utils/schema-converter.ts"],"sourcesContent":["import { makeStructuredOutputCompatible } from '@tanstack/openai-base'\n\n/**\n * Recursively removes `required: []` from a schema object.\n * Groq rejects `required` when it is an empty array, even though\n * OpenAI-compatible schemas allow it.\n */\nfunction removeEmptyRequired(schema: Record<string, any>): Record<string, any> {\n const result = { ...schema }\n\n if (Array.isArray(result.required) && result.required.length === 0) {\n delete result.required\n }\n\n if (result.properties && typeof result.properties === 'object') {\n const properties: Record<string, any> = {}\n for (const [key, value] of Object.entries(\n result.properties as Record<string, any>,\n )) {\n properties[key] =\n typeof value === 'object' && value !== null && !Array.isArray(value)\n ? removeEmptyRequired(value)\n : value\n }\n result.properties = properties\n }\n\n if (\n result.items &&\n typeof result.items === 'object' &&\n !Array.isArray(result.items)\n ) {\n result.items = removeEmptyRequired(result.items)\n }\n\n // Recurse into combinator arrays (anyOf, oneOf, allOf)\n for (const keyword of ['anyOf', 'oneOf', 'allOf'] as const) {\n if (Array.isArray(result[keyword])) {\n result[keyword] = result[keyword].map((entry: Record<string, any>) =>\n removeEmptyRequired(entry),\n )\n }\n }\n\n // Recurse into additionalProperties if it's a schema object\n if (\n result.additionalProperties &&\n typeof result.additionalProperties === 'object' &&\n !Array.isArray(result.additionalProperties)\n ) {\n result.additionalProperties = removeEmptyRequired(\n result.additionalProperties,\n )\n }\n\n return result\n}\n\n/**\n * Recursively normalise object schemas so any `{ type: 'object' }` node\n * without `properties` gets an empty `properties: {}` object. The\n * ai-openai-base transformer only descends into objects that already have\n * `properties` set, so a Zod `z.object({})` nested inside `properties`,\n * `items`, `additionalProperties`, or a combinator branch would otherwise\n * skip the strict-mode rewrite and fail Groq validation.\n */\nfunction normalizeObjectSchemas(\n schema: Record<string, any>,\n): Record<string, any> {\n const result: Record<string, any> =\n schema.type === 'object' && !schema.properties\n ? { ...schema, properties: {} }\n : { ...schema }\n\n if (result.properties && typeof result.properties === 'object') {\n result.properties = Object.fromEntries(\n Object.entries(result.properties as Record<string, any>).map(\n ([key, value]) => [\n key,\n typeof value === 'object' && value !== null && !Array.isArray(value)\n ? normalizeObjectSchemas(value)\n : value,\n ],\n ),\n )\n }\n\n if (\n result.items &&\n typeof result.items === 'object' &&\n !Array.isArray(result.items)\n ) {\n result.items = normalizeObjectSchemas(result.items)\n }\n\n for (const keyword of ['anyOf', 'oneOf', 'allOf'] as const) {\n const branch = result[keyword]\n if (Array.isArray(branch)) {\n result[keyword] = branch.map((entry) =>\n typeof entry === 'object' && entry !== null\n ? normalizeObjectSchemas(entry as Record<string, any>)\n : entry,\n )\n }\n }\n\n if (\n result.additionalProperties &&\n typeof result.additionalProperties === 'object' &&\n !Array.isArray(result.additionalProperties)\n ) {\n result.additionalProperties = normalizeObjectSchemas(\n result.additionalProperties as Record<string, any>,\n )\n }\n\n return result\n}\n\n/**\n * Transform a JSON schema to be compatible with Groq's structured output requirements.\n *\n * Groq requires:\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 objects\n * - `required` must be omitted (not empty array) when there are no properties\n *\n * Delegates to the shared OpenAI-compatible transformer and applies the\n * Groq-specific quirk of removing empty `required` arrays.\n *\n * @param schema - JSON schema to transform\n * @param originalRequired - Original required array (to know which fields were optional)\n * @returns Transformed schema compatible with Groq structured output\n */\nexport function makeGroqStructuredOutputCompatible(\n schema: Record<string, any>,\n originalRequired: Array<string> = [],\n): Record<string, any> {\n // Recursively patch every `{ type: 'object' }` node so the ai-openai-base\n // transformer descends into nested empty objects too.\n const normalised = normalizeObjectSchemas(schema)\n\n const result = makeStructuredOutputCompatible(normalised, originalRequired)\n\n // Groq rejects `required` when it is an empty array\n return removeEmptyRequired(result)\n}\n"],"mappings":";;;;;;;AAOA,SAAS,oBAAoB,QAAkD;CAC7E,MAAM,SAAS,EAAE,GAAG,OAAO;CAE3B,IAAI,MAAM,QAAQ,OAAO,QAAQ,KAAK,OAAO,SAAS,WAAW,GAC/D,OAAO,OAAO;CAGhB,IAAI,OAAO,cAAc,OAAO,OAAO,eAAe,UAAU;EAC9D,MAAM,aAAkC,CAAC;EACzC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAChC,OAAO,UACT,GACE,WAAW,OACT,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,IAC/D,oBAAoB,KAAK,IACzB;EAER,OAAO,aAAa;CACtB;CAEA,IACE,OAAO,SACP,OAAO,OAAO,UAAU,YACxB,CAAC,MAAM,QAAQ,OAAO,KAAK,GAE3B,OAAO,QAAQ,oBAAoB,OAAO,KAAK;CAIjD,KAAK,MAAM,WAAW;EAAC;EAAS;EAAS;CAAO,GAC9C,IAAI,MAAM,QAAQ,OAAO,QAAQ,GAC/B,OAAO,WAAW,OAAO,QAAQ,CAAC,KAAK,UACrC,oBAAoB,KAAK,CAC3B;CAKJ,IACE,OAAO,wBACP,OAAO,OAAO,yBAAyB,YACvC,CAAC,MAAM,QAAQ,OAAO,oBAAoB,GAE1C,OAAO,uBAAuB,oBAC5B,OAAO,oBACT;CAGF,OAAO;AACT;;;;;;;;;AAUA,SAAS,uBACP,QACqB;CACrB,MAAM,SACJ,OAAO,SAAS,YAAY,CAAC,OAAO,aAChC;EAAE,GAAG;EAAQ,YAAY,CAAC;CAAE,IAC5B,EAAE,GAAG,OAAO;CAElB,IAAI,OAAO,cAAc,OAAO,OAAO,eAAe,UACpD,OAAO,aAAa,OAAO,YACzB,OAAO,QAAQ,OAAO,UAAiC,CAAC,CAAC,KACtD,CAAC,KAAK,WAAW,CAChB,KACA,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,IAC/D,uBAAuB,KAAK,IAC5B,KACN,CACF,CACF;CAGF,IACE,OAAO,SACP,OAAO,OAAO,UAAU,YACxB,CAAC,MAAM,QAAQ,OAAO,KAAK,GAE3B,OAAO,QAAQ,uBAAuB,OAAO,KAAK;CAGpD,KAAK,MAAM,WAAW;EAAC;EAAS;EAAS;CAAO,GAAY;EAC1D,MAAM,SAAS,OAAO;EACtB,IAAI,MAAM,QAAQ,MAAM,GACtB,OAAO,WAAW,OAAO,KAAK,UAC5B,OAAO,UAAU,YAAY,UAAU,OACnC,uBAAuB,KAA4B,IACnD,KACN;CAEJ;CAEA,IACE,OAAO,wBACP,OAAO,OAAO,yBAAyB,YACvC,CAAC,MAAM,QAAQ,OAAO,oBAAoB,GAE1C,OAAO,uBAAuB,uBAC5B,OAAO,oBACT;CAGF,OAAO;AACT;;;;;;;;;;;;;;;;;AAkBA,SAAgB,mCACd,QACA,mBAAkC,CAAC,GACd;CAGrB,MAAM,aAAa,uBAAuB,MAAM;CAKhD,OAAO,oBAHQ,+BAA+B,YAAY,gBAG/B,CAAM;AACnC"}
1
+ {"version":3,"file":"schema-converter.js","names":[],"sources":["../../../src/utils/schema-converter.ts"],"sourcesContent":["import { makeStructuredOutputCompatibleWithMap } from '@tanstack/openai-base'\n\n/**\n * Recursively removes `required: []` from a schema object.\n * Groq rejects `required` when it is an empty array, even though\n * OpenAI-compatible schemas allow it.\n */\nfunction removeEmptyRequired(schema: Record<string, any>): Record<string, any> {\n const result = { ...schema }\n\n if (Array.isArray(result.required) && result.required.length === 0) {\n delete result.required\n }\n\n if (result.properties && typeof result.properties === 'object') {\n const properties: Record<string, any> = {}\n for (const [key, value] of Object.entries(\n result.properties as Record<string, any>,\n )) {\n properties[key] =\n typeof value === 'object' && value !== null && !Array.isArray(value)\n ? removeEmptyRequired(value)\n : value\n }\n result.properties = properties\n }\n\n if (\n result.items &&\n typeof result.items === 'object' &&\n !Array.isArray(result.items)\n ) {\n result.items = removeEmptyRequired(result.items)\n }\n\n // Recurse into combinator arrays (anyOf, oneOf, allOf)\n for (const keyword of ['anyOf', 'oneOf', 'allOf'] as const) {\n if (Array.isArray(result[keyword])) {\n result[keyword] = result[keyword].map((entry: Record<string, any>) =>\n removeEmptyRequired(entry),\n )\n }\n }\n\n // Recurse into additionalProperties if it's a schema object\n if (\n result.additionalProperties &&\n typeof result.additionalProperties === 'object' &&\n !Array.isArray(result.additionalProperties)\n ) {\n result.additionalProperties = removeEmptyRequired(\n result.additionalProperties,\n )\n }\n\n return result\n}\n\n/**\n * Recursively normalise object schemas so any `{ type: 'object' }` node\n * without `properties` gets an empty `properties: {}` object. The\n * ai-openai-base transformer only descends into objects that already have\n * `properties` set, so a Zod `z.object({})` nested inside `properties`,\n * `items`, `additionalProperties`, or a combinator branch would otherwise\n * skip the strict-mode rewrite and fail Groq validation.\n */\nfunction normalizeObjectSchemas(\n schema: Record<string, any>,\n): Record<string, any> {\n const result: Record<string, any> =\n schema.type === 'object' && !schema.properties\n ? { ...schema, properties: {} }\n : { ...schema }\n\n if (result.properties && typeof result.properties === 'object') {\n result.properties = Object.fromEntries(\n Object.entries(result.properties as Record<string, any>).map(\n ([key, value]) => [\n key,\n typeof value === 'object' && value !== null && !Array.isArray(value)\n ? normalizeObjectSchemas(value)\n : value,\n ],\n ),\n )\n }\n\n if (\n result.items &&\n typeof result.items === 'object' &&\n !Array.isArray(result.items)\n ) {\n result.items = normalizeObjectSchemas(result.items)\n }\n\n for (const keyword of ['anyOf', 'oneOf', 'allOf'] as const) {\n const branch = result[keyword]\n if (Array.isArray(branch)) {\n result[keyword] = branch.map((entry) =>\n typeof entry === 'object' && entry !== null\n ? normalizeObjectSchemas(entry as Record<string, any>)\n : entry,\n )\n }\n }\n\n if (\n result.additionalProperties &&\n typeof result.additionalProperties === 'object' &&\n !Array.isArray(result.additionalProperties)\n ) {\n result.additionalProperties = normalizeObjectSchemas(\n result.additionalProperties as Record<string, any>,\n )\n }\n\n return result\n}\n\n/**\n * Transform a JSON schema to be compatible with Groq's structured output requirements.\n *\n * Groq requires:\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 objects\n * - `required` must be omitted (not empty array) when there are no properties\n *\n * Delegates to the shared OpenAI-compatible transformer and applies the\n * Groq-specific quirk of removing empty `required` arrays.\n *\n * @param schema - JSON schema to transform\n * @param originalRequired - Original required array (to know which fields were optional)\n * @returns Transformed schema compatible with Groq structured output\n */\nexport function makeGroqStructuredOutputCompatibleWithMap(\n schema: Record<string, any>,\n originalRequired: Array<string> = [],\n) {\n // Recursively patch every `{ type: 'object' }` node so the ai-openai-base\n // transformer descends into nested empty objects too.\n const normalised = normalizeObjectSchemas(schema)\n const { schema: converted, nullWideningMap } =\n makeStructuredOutputCompatibleWithMap(normalised, originalRequired)\n\n // Groq rejects `required` when it is an empty array\n return {\n schema: removeEmptyRequired(converted),\n nullWideningMap,\n }\n}\n\nexport function makeGroqStructuredOutputCompatible(\n schema: Record<string, any>,\n originalRequired: Array<string> = [],\n): Record<string, any> {\n return makeGroqStructuredOutputCompatibleWithMap(schema, originalRequired)\n .schema\n}\n"],"mappings":";;;;;;;AAOA,SAAS,oBAAoB,QAAkD;CAC7E,MAAM,SAAS,EAAE,GAAG,OAAO;CAE3B,IAAI,MAAM,QAAQ,OAAO,QAAQ,KAAK,OAAO,SAAS,WAAW,GAC/D,OAAO,OAAO;CAGhB,IAAI,OAAO,cAAc,OAAO,OAAO,eAAe,UAAU;EAC9D,MAAM,aAAkC,CAAC;EACzC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAChC,OAAO,UACT,GACE,WAAW,OACT,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,IAC/D,oBAAoB,KAAK,IACzB;EAER,OAAO,aAAa;CACtB;CAEA,IACE,OAAO,SACP,OAAO,OAAO,UAAU,YACxB,CAAC,MAAM,QAAQ,OAAO,KAAK,GAE3B,OAAO,QAAQ,oBAAoB,OAAO,KAAK;CAIjD,KAAK,MAAM,WAAW;EAAC;EAAS;EAAS;CAAO,GAC9C,IAAI,MAAM,QAAQ,OAAO,QAAQ,GAC/B,OAAO,WAAW,OAAO,QAAQ,CAAC,KAAK,UACrC,oBAAoB,KAAK,CAC3B;CAKJ,IACE,OAAO,wBACP,OAAO,OAAO,yBAAyB,YACvC,CAAC,MAAM,QAAQ,OAAO,oBAAoB,GAE1C,OAAO,uBAAuB,oBAC5B,OAAO,oBACT;CAGF,OAAO;AACT;;;;;;;;;AAUA,SAAS,uBACP,QACqB;CACrB,MAAM,SACJ,OAAO,SAAS,YAAY,CAAC,OAAO,aAChC;EAAE,GAAG;EAAQ,YAAY,CAAC;CAAE,IAC5B,EAAE,GAAG,OAAO;CAElB,IAAI,OAAO,cAAc,OAAO,OAAO,eAAe,UACpD,OAAO,aAAa,OAAO,YACzB,OAAO,QAAQ,OAAO,UAAiC,CAAC,CAAC,KACtD,CAAC,KAAK,WAAW,CAChB,KACA,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,IAC/D,uBAAuB,KAAK,IAC5B,KACN,CACF,CACF;CAGF,IACE,OAAO,SACP,OAAO,OAAO,UAAU,YACxB,CAAC,MAAM,QAAQ,OAAO,KAAK,GAE3B,OAAO,QAAQ,uBAAuB,OAAO,KAAK;CAGpD,KAAK,MAAM,WAAW;EAAC;EAAS;EAAS;CAAO,GAAY;EAC1D,MAAM,SAAS,OAAO;EACtB,IAAI,MAAM,QAAQ,MAAM,GACtB,OAAO,WAAW,OAAO,KAAK,UAC5B,OAAO,UAAU,YAAY,UAAU,OACnC,uBAAuB,KAA4B,IACnD,KACN;CAEJ;CAEA,IACE,OAAO,wBACP,OAAO,OAAO,yBAAyB,YACvC,CAAC,MAAM,QAAQ,OAAO,oBAAoB,GAE1C,OAAO,uBAAuB,uBAC5B,OAAO,oBACT;CAGF,OAAO;AACT;;;;;;;;;;;;;;;;;AAkBA,SAAgB,0CACd,QACA,mBAAkC,CAAC,GACnC;CAGA,MAAM,aAAa,uBAAuB,MAAM;CAChD,MAAM,EAAE,QAAQ,WAAW,oBACzB,sCAAsC,YAAY,gBAAgB;CAGpE,OAAO;EACL,QAAQ,oBAAoB,SAAS;EACrC;CACF;AACF;AAEA,SAAgB,mCACd,QACA,mBAAkC,CAAC,GACd;CACrB,OAAO,0CAA0C,QAAQ,gBAAgB,CAAC,CACvE;AACL"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-groq",
3
- "version": "0.6.6",
3
+ "version": "0.7.0",
4
4
  "description": "Groq adapter for TanStack AI low-latency chat, tool calling, and structured outputs.",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -25,6 +25,10 @@
25
25
  "types": "./dist/esm/index.d.ts",
26
26
  "import": "./dist/esm/index.js"
27
27
  },
28
+ "./byok": {
29
+ "types": "./dist/esm/byok.d.ts",
30
+ "import": "./dist/esm/byok.js"
31
+ },
28
32
  "./tools": {
29
33
  "types": "./dist/esm/tools/index.d.ts",
30
34
  "import": "./dist/esm/tools/index.js"
@@ -53,12 +57,12 @@
53
57
  },
54
58
  "peerDependencies": {
55
59
  "zod": "^4.0.0",
56
- "@tanstack/ai": "^0.47.2"
60
+ "@tanstack/ai": "^0.49.1"
57
61
  },
58
62
  "dependencies": {
59
63
  "openai": "^6.41.0",
60
64
  "@tanstack/ai-utils": "^0.4.0",
61
- "@tanstack/openai-base": "^0.10.1"
65
+ "@tanstack/openai-base": "^0.10.5"
62
66
  },
63
67
  "scripts": {
64
68
  "build": "vite build",
@@ -1,7 +1,7 @@
1
1
  import OpenAI from 'openai'
2
2
  import { OpenAIBaseChatCompletionsTextAdapter } from '@tanstack/openai-base'
3
3
  import { getGroqApiKeyFromEnv, withGroqDefaults } from '../utils/client'
4
- import { makeGroqStructuredOutputCompatible } from '../utils/schema-converter'
4
+ import { makeGroqStructuredOutputCompatibleWithMap } from '../utils/schema-converter'
5
5
  import type { Modality, TextOptions } from '@tanstack/ai'
6
6
  import type {
7
7
  GROQ_CHAT_MODELS,
@@ -123,11 +123,11 @@ export class GroqTextAdapter<
123
123
  }
124
124
  }
125
125
 
126
- protected override makeStructuredOutputCompatible(
126
+ protected override makeStructuredOutputCompatibleWithMap(
127
127
  schema: Record<string, any>,
128
128
  originalRequired?: Array<string>,
129
- ): Record<string, any> {
130
- return makeGroqStructuredOutputCompatible(schema, originalRequired)
129
+ ) {
130
+ return makeGroqStructuredOutputCompatibleWithMap(schema, originalRequired)
131
131
  }
132
132
 
133
133
  protected override async *processStreamChunks(
package/src/byok.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { defineByokProvider } from '@tanstack/ai/byok'
2
+
3
+ export const groqByok = defineByokProvider({
4
+ id: 'groq',
5
+ label: 'Groq',
6
+ env: 'GROQ_API_KEY',
7
+ })
@@ -1,4 +1,4 @@
1
- import { makeStructuredOutputCompatible } from '@tanstack/openai-base'
1
+ import { makeStructuredOutputCompatibleWithMap } from '@tanstack/openai-base'
2
2
 
3
3
  /**
4
4
  * Recursively removes `required: []` from a schema object.
@@ -133,16 +133,27 @@ function normalizeObjectSchemas(
133
133
  * @param originalRequired - Original required array (to know which fields were optional)
134
134
  * @returns Transformed schema compatible with Groq structured output
135
135
  */
136
- export function makeGroqStructuredOutputCompatible(
136
+ export function makeGroqStructuredOutputCompatibleWithMap(
137
137
  schema: Record<string, any>,
138
138
  originalRequired: Array<string> = [],
139
- ): Record<string, any> {
139
+ ) {
140
140
  // Recursively patch every `{ type: 'object' }` node so the ai-openai-base
141
141
  // transformer descends into nested empty objects too.
142
142
  const normalised = normalizeObjectSchemas(schema)
143
-
144
- const result = makeStructuredOutputCompatible(normalised, originalRequired)
143
+ const { schema: converted, nullWideningMap } =
144
+ makeStructuredOutputCompatibleWithMap(normalised, originalRequired)
145
145
 
146
146
  // Groq rejects `required` when it is an empty array
147
- return removeEmptyRequired(result)
147
+ return {
148
+ schema: removeEmptyRequired(converted),
149
+ nullWideningMap,
150
+ }
151
+ }
152
+
153
+ export function makeGroqStructuredOutputCompatible(
154
+ schema: Record<string, any>,
155
+ originalRequired: Array<string> = [],
156
+ ): Record<string, any> {
157
+ return makeGroqStructuredOutputCompatibleWithMap(schema, originalRequired)
158
+ .schema
148
159
  }