@tanstack/ai-cohere 0.4.6 → 0.4.9

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.
@@ -2,7 +2,7 @@ import { getCohereApiKeyFromEnv, resolveCohereTransport } from "../utils/client.
2
2
  import { BaseEmbeddingAdapter } from "@tanstack/ai/adapters";
3
3
  import { toRunErrorPayload } from "@tanstack/ai/adapter-internals";
4
4
  import { arrayBufferToBase64, generateId } from "@tanstack/ai-utils";
5
- import { resolveEmbeddingInput } from "@tanstack/ai";
5
+ import { isFileSource, resolveEmbeddingInput, unsupportedFileSourceError } from "@tanstack/ai";
6
6
  //#region src/adapters/embedding.ts
7
7
  var DEFAULT_TIMEOUT_MS = 3e4;
8
8
  /**
@@ -138,6 +138,7 @@ var CohereEmbeddingAdapter = class extends BaseEmbeddingAdapter {
138
138
  */
139
139
  async resolveImageUrl(image) {
140
140
  const source = image.source;
141
+ if (isFileSource(source)) throw unsupportedFileSourceError(this.name);
141
142
  if (source.type === "data") return `data:${source.mimeType};base64,${source.value}`;
142
143
  if (source.value.startsWith("data:")) return source.value;
143
144
  if (!this.clientConfig.allowUrlFetch) throw new Error("Cohere does not fetch remote image URLs; pass base64 data or a data: URI (or enable config.allowUrlFetch to have the adapter download it)");
@@ -1 +1 @@
1
- {"version":3,"file":"embedding.js","names":[],"sources":["../../../src/adapters/embedding.ts"],"sourcesContent":["import { BaseEmbeddingAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport { arrayBufferToBase64, generateId } from '@tanstack/ai-utils'\nimport { resolveEmbeddingInput } from '@tanstack/ai'\nimport { getCohereApiKeyFromEnv, resolveCohereTransport } from '../utils/client'\nimport type {\n EmbeddingOptions,\n EmbeddingResult,\n ImagePart,\n TokenUsage,\n} from '@tanstack/ai'\nimport type {\n CohereEmbeddingModel,\n CohereEmbeddingModelInputModalitiesByName,\n CohereEmbeddingModelProviderOptionsByName,\n} from '../model-meta'\nimport type { CohereEmbeddingProviderOptions } from '../embedding/embedding-provider-options'\nimport type { CohereClientConfig } from '../utils/client'\n\n/**\n * Configuration for Cohere embedding adapter.\n */\nexport interface CohereEmbeddingConfig extends CohereClientConfig {}\n\nconst DEFAULT_TIMEOUT_MS = 30_000\n\n/**\n * Returns true when `url` is malformed, non-http(s), or targets a private /\n * loopback / link-local host. Used to block SSRF via `allowUrlFetch`.\n */\nfunction isPrivateOrInternalUrl(url: string): boolean {\n let parsed: URL\n try {\n parsed = new URL(url)\n } catch {\n return true\n }\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return true\n }\n const host = parsed.hostname.toLowerCase()\n if (\n host === 'localhost' ||\n host.endsWith('.localhost') ||\n host === '::1' ||\n host === '[::1]' ||\n host.startsWith('127.') ||\n host.startsWith('10.') ||\n host.startsWith('192.168.') ||\n host.startsWith('169.254.') ||\n /^172\\.(1[6-9]|2\\d|3[01])\\./.test(host)\n ) {\n return true\n }\n return false\n}\n\nasync function fetchWithTimeout(\n url: string,\n init: RequestInit | undefined,\n timeoutMs: number,\n): Promise<Response> {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs)\n try {\n return await fetch(url, { ...init, signal: controller.signal })\n } finally {\n clearTimeout(timeoutId)\n }\n}\n\n/** One content part of a Cohere v2/embed fused input. */\ntype CohereEmbedContentPart =\n | { type: 'text'; text: string }\n | { type: 'image_url'; image_url: { url: string } }\n\n/** Wire shape of the Cohere v2/embed request body. */\ninterface CohereEmbedRequestBody {\n model: string\n inputs: Array<{ content: Array<CohereEmbedContentPart> }>\n input_type: CohereEmbeddingProviderOptions['inputType']\n embedding_types: ['float']\n truncate?: 'NONE' | 'START' | 'END'\n output_dimension?: number\n}\n\n/** Wire shape of the Cohere v2/embed response (fields the adapter reads). */\ninterface CohereEmbedResponse {\n id?: string\n embeddings?: {\n float?: Array<Array<number>>\n }\n meta?: {\n billed_units?: {\n input_tokens?: number\n images?: number\n }\n }\n}\n\n/**\n * Cohere Embedding Adapter\n *\n * Tree-shakeable adapter for Cohere multimodal embeddings (embed-v4.0),\n * implemented with plain `fetch` against the v2/embed endpoint — no Cohere\n * SDK dependency.\n *\n * Features:\n * - Batch embedding (one request for the whole input array)\n * - Multimodal inputs: text, images, and fused text+image items (one vector\n * per input item)\n * - Matryoshka dimension reduction via the top-level `dimensions` option\n * (mapped to Cohere's `output_dimension`)\n */\nexport class CohereEmbeddingAdapter<\n TModel extends CohereEmbeddingModel,\n> extends BaseEmbeddingAdapter<\n TModel,\n CohereEmbeddingProviderOptions,\n CohereEmbeddingModelProviderOptionsByName,\n CohereEmbeddingModelInputModalitiesByName\n> {\n readonly name = 'cohere' as const\n\n protected clientConfig: CohereEmbeddingConfig\n\n constructor(config: CohereEmbeddingConfig, model: TModel) {\n super(model, {})\n this.clientConfig = config\n }\n\n async createEmbeddings(\n options: EmbeddingOptions<CohereEmbeddingProviderOptions>,\n ): Promise<EmbeddingResult> {\n const { model, logger, modelOptions } = options\n\n try {\n // The provider options type makes `modelOptions` required at the\n // embed() call site; this guard covers untyped/dynamic callers.\n const inputType: CohereEmbeddingProviderOptions['inputType'] | undefined =\n modelOptions?.inputType\n if (!inputType) {\n throw new Error(\n `Cohere embeddings require modelOptions.inputType ('search_document' | 'search_query' | 'classification' | 'clustering').`,\n )\n }\n\n const resolved = resolveEmbeddingInput(options.input)\n const inputs = await Promise.all(\n resolved.map(async (item) => {\n const content: Array<CohereEmbedContentPart> = item.texts.map(\n (text) => ({ type: 'text', text }),\n )\n for (const image of item.images) {\n content.push({\n type: 'image_url',\n image_url: { url: await this.resolveImageUrl(image) },\n })\n }\n return { content }\n }),\n )\n\n // embedding_types is pinned to ['float'] (overriding any disagreeing\n // modelOptions.embeddingTypes) so vectors are always number[].\n const body: CohereEmbedRequestBody = {\n model,\n inputs,\n input_type: inputType,\n embedding_types: ['float'],\n }\n const truncate = modelOptions?.truncate\n if (truncate !== undefined) {\n body.truncate = truncate\n }\n if (options.dimensions !== undefined) {\n body.output_dimension = options.dimensions\n }\n\n logger.request(\n `activity=embed provider=${this.name} model=${model} inputs=${inputs.length}`,\n { provider: this.name, model },\n )\n\n const timeoutMs = this.clientConfig.timeout ?? DEFAULT_TIMEOUT_MS\n const transport = resolveCohereTransport(this.clientConfig)\n const response = await fetchWithTimeout(\n `${transport.baseUrl}/v2/embed`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.clientConfig.apiKey}`,\n 'Content-Type': 'application/json',\n ...transport.headers,\n },\n body: JSON.stringify(body),\n },\n timeoutMs,\n )\n\n if (!response.ok) {\n const bodyText = await response.text()\n let message = bodyText\n try {\n const parsed: unknown = JSON.parse(bodyText)\n if (\n typeof parsed === 'object' &&\n parsed !== null &&\n 'message' in parsed &&\n typeof parsed.message === 'string'\n ) {\n message = parsed.message\n }\n } catch {\n // Not JSON — fall back to the raw body text.\n }\n throw new Error(`Cohere embed failed (${response.status}): ${message}`)\n }\n\n const data = (await response.json()) as CohereEmbedResponse\n\n const vectors = data.embeddings?.float\n if (!vectors) {\n throw new Error(\n 'Cohere embed response did not include float embeddings',\n )\n }\n if (vectors.length !== inputs.length) {\n throw new Error(\n `Cohere embed returned ${vectors.length} embeddings for ${inputs.length} inputs`,\n )\n }\n\n const result: EmbeddingResult = {\n id: generateId(this.name),\n model,\n embeddings: vectors.map((vector, index) => ({ vector, index })),\n }\n\n const inputTokens = data.meta?.billed_units?.input_tokens\n if (inputTokens !== undefined) {\n const usage: TokenUsage = {\n promptTokens: inputTokens,\n completionTokens: 0,\n totalTokens: inputTokens,\n }\n result.usage = usage\n }\n\n return result\n } catch (error: unknown) {\n logger.errors(`${this.name}.createEmbeddings fatal`, {\n error: toRunErrorPayload(error, `${this.name}.createEmbeddings failed`),\n source: `${this.name}.createEmbeddings`,\n })\n throw error\n }\n }\n\n /**\n * Resolves an image part to a URL Cohere accepts. Cohere does not fetch\n * remote image URLs, so everything is normalized to a `data:` URI unless\n * the caller already provided one.\n */\n protected async resolveImageUrl(image: ImagePart): Promise<string> {\n const source = image.source\n\n if (source.type === 'data') {\n return `data:${source.mimeType};base64,${source.value}`\n }\n\n if (source.value.startsWith('data:')) {\n return source.value\n }\n\n if (!this.clientConfig.allowUrlFetch) {\n throw new Error(\n 'Cohere does not fetch remote image URLs; pass base64 data or a data: URI (or enable config.allowUrlFetch to have the adapter download it)',\n )\n }\n\n if (isPrivateOrInternalUrl(source.value)) {\n throw new Error(\n `Refusing to fetch internal or private URL for Cohere embedding: ${source.value}`,\n )\n }\n\n const response = await fetchWithTimeout(\n source.value,\n undefined,\n this.clientConfig.timeout ?? DEFAULT_TIMEOUT_MS,\n )\n if (!response.ok) {\n throw new Error(\n `Failed to fetch image URL for Cohere embedding (${response.status}): ${source.value}`,\n )\n }\n const mimeType =\n response.headers.get('content-type') ??\n source.mimeType ??\n 'application/octet-stream'\n const base64 = arrayBufferToBase64(await response.arrayBuffer())\n return `data:${mimeType};base64,${base64}`\n }\n}\n\n/**\n * Creates a Cohere embedding adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'embed-v4.0')\n * @param apiKey - Your Cohere API key\n * @param config - Optional additional configuration\n * @returns Configured Cohere embedding adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createCohereEmbedding('embed-v4.0', 'api_key');\n *\n * const result = await embed({\n * adapter,\n * input: 'a red guitar',\n * modelOptions: { inputType: 'search_document' }\n * });\n * ```\n */\nexport function createCohereEmbedding<TModel extends CohereEmbeddingModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<CohereEmbeddingConfig, 'apiKey'>,\n): CohereEmbeddingAdapter<TModel> {\n return new CohereEmbeddingAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Cohere embedding adapter using the `COHERE_API_KEY` environment variable.\n * Type resolution happens here at the call site.\n *\n * Looks for `COHERE_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'embed-v4.0')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Cohere embedding adapter instance with resolved types\n * @throws Error if COHERE_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses COHERE_API_KEY from environment\n * const adapter = cohereEmbedding('embed-v4.0');\n *\n * const result = await embed({\n * adapter,\n * input: ['a red guitar', 'a blue drum kit'],\n * modelOptions: { inputType: 'search_query' },\n * dimensions: 1024\n * });\n *\n * console.log(result.embeddings[0].vector)\n * ```\n */\nexport function cohereEmbedding<TModel extends CohereEmbeddingModel>(\n model: TModel,\n config?: Omit<CohereEmbeddingConfig, 'apiKey'>,\n): CohereEmbeddingAdapter<TModel> {\n const apiKey = getCohereApiKeyFromEnv()\n return createCohereEmbedding(model, apiKey, config)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"embedding.js","names":[],"sources":["../../../src/adapters/embedding.ts"],"sourcesContent":["import { BaseEmbeddingAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport { arrayBufferToBase64, generateId } from '@tanstack/ai-utils'\nimport {\n isFileSource,\n resolveEmbeddingInput,\n unsupportedFileSourceError,\n} from '@tanstack/ai'\nimport { getCohereApiKeyFromEnv, resolveCohereTransport } from '../utils/client'\nimport type {\n EmbeddingOptions,\n EmbeddingResult,\n ImagePart,\n TokenUsage,\n} from '@tanstack/ai'\nimport type {\n CohereEmbeddingModel,\n CohereEmbeddingModelInputModalitiesByName,\n CohereEmbeddingModelProviderOptionsByName,\n} from '../model-meta'\nimport type { CohereEmbeddingProviderOptions } from '../embedding/embedding-provider-options'\nimport type { CohereClientConfig } from '../utils/client'\n\n/**\n * Configuration for Cohere embedding adapter.\n */\nexport interface CohereEmbeddingConfig extends CohereClientConfig {}\n\nconst DEFAULT_TIMEOUT_MS = 30_000\n\n/**\n * Returns true when `url` is malformed, non-http(s), or targets a private /\n * loopback / link-local host. Used to block SSRF via `allowUrlFetch`.\n */\nfunction isPrivateOrInternalUrl(url: string): boolean {\n let parsed: URL\n try {\n parsed = new URL(url)\n } catch {\n return true\n }\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return true\n }\n const host = parsed.hostname.toLowerCase()\n if (\n host === 'localhost' ||\n host.endsWith('.localhost') ||\n host === '::1' ||\n host === '[::1]' ||\n host.startsWith('127.') ||\n host.startsWith('10.') ||\n host.startsWith('192.168.') ||\n host.startsWith('169.254.') ||\n /^172\\.(1[6-9]|2\\d|3[01])\\./.test(host)\n ) {\n return true\n }\n return false\n}\n\nasync function fetchWithTimeout(\n url: string,\n init: RequestInit | undefined,\n timeoutMs: number,\n): Promise<Response> {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs)\n try {\n return await fetch(url, { ...init, signal: controller.signal })\n } finally {\n clearTimeout(timeoutId)\n }\n}\n\n/** One content part of a Cohere v2/embed fused input. */\ntype CohereEmbedContentPart =\n | { type: 'text'; text: string }\n | { type: 'image_url'; image_url: { url: string } }\n\n/** Wire shape of the Cohere v2/embed request body. */\ninterface CohereEmbedRequestBody {\n model: string\n inputs: Array<{ content: Array<CohereEmbedContentPart> }>\n input_type: CohereEmbeddingProviderOptions['inputType']\n embedding_types: ['float']\n truncate?: 'NONE' | 'START' | 'END'\n output_dimension?: number\n}\n\n/** Wire shape of the Cohere v2/embed response (fields the adapter reads). */\ninterface CohereEmbedResponse {\n id?: string\n embeddings?: {\n float?: Array<Array<number>>\n }\n meta?: {\n billed_units?: {\n input_tokens?: number\n images?: number\n }\n }\n}\n\n/**\n * Cohere Embedding Adapter\n *\n * Tree-shakeable adapter for Cohere multimodal embeddings (embed-v4.0),\n * implemented with plain `fetch` against the v2/embed endpoint — no Cohere\n * SDK dependency.\n *\n * Features:\n * - Batch embedding (one request for the whole input array)\n * - Multimodal inputs: text, images, and fused text+image items (one vector\n * per input item)\n * - Matryoshka dimension reduction via the top-level `dimensions` option\n * (mapped to Cohere's `output_dimension`)\n */\nexport class CohereEmbeddingAdapter<\n TModel extends CohereEmbeddingModel,\n> extends BaseEmbeddingAdapter<\n TModel,\n CohereEmbeddingProviderOptions,\n CohereEmbeddingModelProviderOptionsByName,\n CohereEmbeddingModelInputModalitiesByName\n> {\n readonly name = 'cohere' as const\n\n protected clientConfig: CohereEmbeddingConfig\n\n constructor(config: CohereEmbeddingConfig, model: TModel) {\n super(model, {})\n this.clientConfig = config\n }\n\n async createEmbeddings(\n options: EmbeddingOptions<CohereEmbeddingProviderOptions>,\n ): Promise<EmbeddingResult> {\n const { model, logger, modelOptions } = options\n\n try {\n // The provider options type makes `modelOptions` required at the\n // embed() call site; this guard covers untyped/dynamic callers.\n const inputType: CohereEmbeddingProviderOptions['inputType'] | undefined =\n modelOptions?.inputType\n if (!inputType) {\n throw new Error(\n `Cohere embeddings require modelOptions.inputType ('search_document' | 'search_query' | 'classification' | 'clustering').`,\n )\n }\n\n const resolved = resolveEmbeddingInput(options.input)\n const inputs = await Promise.all(\n resolved.map(async (item) => {\n const content: Array<CohereEmbedContentPart> = item.texts.map(\n (text) => ({ type: 'text', text }),\n )\n for (const image of item.images) {\n content.push({\n type: 'image_url',\n image_url: { url: await this.resolveImageUrl(image) },\n })\n }\n return { content }\n }),\n )\n\n // embedding_types is pinned to ['float'] (overriding any disagreeing\n // modelOptions.embeddingTypes) so vectors are always number[].\n const body: CohereEmbedRequestBody = {\n model,\n inputs,\n input_type: inputType,\n embedding_types: ['float'],\n }\n const truncate = modelOptions?.truncate\n if (truncate !== undefined) {\n body.truncate = truncate\n }\n if (options.dimensions !== undefined) {\n body.output_dimension = options.dimensions\n }\n\n logger.request(\n `activity=embed provider=${this.name} model=${model} inputs=${inputs.length}`,\n { provider: this.name, model },\n )\n\n const timeoutMs = this.clientConfig.timeout ?? DEFAULT_TIMEOUT_MS\n const transport = resolveCohereTransport(this.clientConfig)\n const response = await fetchWithTimeout(\n `${transport.baseUrl}/v2/embed`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.clientConfig.apiKey}`,\n 'Content-Type': 'application/json',\n ...transport.headers,\n },\n body: JSON.stringify(body),\n },\n timeoutMs,\n )\n\n if (!response.ok) {\n const bodyText = await response.text()\n let message = bodyText\n try {\n const parsed: unknown = JSON.parse(bodyText)\n if (\n typeof parsed === 'object' &&\n parsed !== null &&\n 'message' in parsed &&\n typeof parsed.message === 'string'\n ) {\n message = parsed.message\n }\n } catch {\n // Not JSON — fall back to the raw body text.\n }\n throw new Error(`Cohere embed failed (${response.status}): ${message}`)\n }\n\n const data = (await response.json()) as CohereEmbedResponse\n\n const vectors = data.embeddings?.float\n if (!vectors) {\n throw new Error(\n 'Cohere embed response did not include float embeddings',\n )\n }\n if (vectors.length !== inputs.length) {\n throw new Error(\n `Cohere embed returned ${vectors.length} embeddings for ${inputs.length} inputs`,\n )\n }\n\n const result: EmbeddingResult = {\n id: generateId(this.name),\n model,\n embeddings: vectors.map((vector, index) => ({ vector, index })),\n }\n\n const inputTokens = data.meta?.billed_units?.input_tokens\n if (inputTokens !== undefined) {\n const usage: TokenUsage = {\n promptTokens: inputTokens,\n completionTokens: 0,\n totalTokens: inputTokens,\n }\n result.usage = usage\n }\n\n return result\n } catch (error: unknown) {\n logger.errors(`${this.name}.createEmbeddings fatal`, {\n error: toRunErrorPayload(error, `${this.name}.createEmbeddings failed`),\n source: `${this.name}.createEmbeddings`,\n })\n throw error\n }\n }\n\n /**\n * Resolves an image part to a URL Cohere accepts. Cohere does not fetch\n * remote image URLs, so everything is normalized to a `data:` URI unless\n * the caller already provided one.\n */\n protected async resolveImageUrl(image: ImagePart): Promise<string> {\n const source = image.source\n if (isFileSource(source)) throw unsupportedFileSourceError(this.name)\n\n if (source.type === 'data') {\n return `data:${source.mimeType};base64,${source.value}`\n }\n\n if (source.value.startsWith('data:')) {\n return source.value\n }\n\n if (!this.clientConfig.allowUrlFetch) {\n throw new Error(\n 'Cohere does not fetch remote image URLs; pass base64 data or a data: URI (or enable config.allowUrlFetch to have the adapter download it)',\n )\n }\n\n if (isPrivateOrInternalUrl(source.value)) {\n throw new Error(\n `Refusing to fetch internal or private URL for Cohere embedding: ${source.value}`,\n )\n }\n\n const response = await fetchWithTimeout(\n source.value,\n undefined,\n this.clientConfig.timeout ?? DEFAULT_TIMEOUT_MS,\n )\n if (!response.ok) {\n throw new Error(\n `Failed to fetch image URL for Cohere embedding (${response.status}): ${source.value}`,\n )\n }\n const mimeType =\n response.headers.get('content-type') ??\n source.mimeType ??\n 'application/octet-stream'\n const base64 = arrayBufferToBase64(await response.arrayBuffer())\n return `data:${mimeType};base64,${base64}`\n }\n}\n\n/**\n * Creates a Cohere embedding adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'embed-v4.0')\n * @param apiKey - Your Cohere API key\n * @param config - Optional additional configuration\n * @returns Configured Cohere embedding adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createCohereEmbedding('embed-v4.0', 'api_key');\n *\n * const result = await embed({\n * adapter,\n * input: 'a red guitar',\n * modelOptions: { inputType: 'search_document' }\n * });\n * ```\n */\nexport function createCohereEmbedding<TModel extends CohereEmbeddingModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<CohereEmbeddingConfig, 'apiKey'>,\n): CohereEmbeddingAdapter<TModel> {\n return new CohereEmbeddingAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Cohere embedding adapter using the `COHERE_API_KEY` environment variable.\n * Type resolution happens here at the call site.\n *\n * Looks for `COHERE_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'embed-v4.0')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Cohere embedding adapter instance with resolved types\n * @throws Error if COHERE_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses COHERE_API_KEY from environment\n * const adapter = cohereEmbedding('embed-v4.0');\n *\n * const result = await embed({\n * adapter,\n * input: ['a red guitar', 'a blue drum kit'],\n * modelOptions: { inputType: 'search_query' },\n * dimensions: 1024\n * });\n *\n * console.log(result.embeddings[0].vector)\n * ```\n */\nexport function cohereEmbedding<TModel extends CohereEmbeddingModel>(\n model: TModel,\n config?: Omit<CohereEmbeddingConfig, 'apiKey'>,\n): CohereEmbeddingAdapter<TModel> {\n const apiKey = getCohereApiKeyFromEnv()\n return createCohereEmbedding(model, apiKey, config)\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-cohere",
3
- "version": "0.4.6",
3
+ "version": "0.4.9",
4
4
  "description": "Cohere adapter for TanStack AI — multimodal embeddings and document reranking.",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -53,12 +53,12 @@
53
53
  "retrieval"
54
54
  ],
55
55
  "peerDependencies": {
56
- "@tanstack/ai": "^0.58.0"
56
+ "@tanstack/ai": "^0.61.0"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@vitest/coverage-v8": "4.1.10",
60
60
  "vite": "^8.2.1",
61
- "@tanstack/ai": "0.58.0"
61
+ "@tanstack/ai": "0.61.0"
62
62
  },
63
63
  "dependencies": {
64
64
  "@tanstack/ai-utils": "^0.4.1"
@@ -1,7 +1,11 @@
1
1
  import { BaseEmbeddingAdapter } from '@tanstack/ai/adapters'
2
2
  import { toRunErrorPayload } from '@tanstack/ai/adapter-internals'
3
3
  import { arrayBufferToBase64, generateId } from '@tanstack/ai-utils'
4
- import { resolveEmbeddingInput } from '@tanstack/ai'
4
+ import {
5
+ isFileSource,
6
+ resolveEmbeddingInput,
7
+ unsupportedFileSourceError,
8
+ } from '@tanstack/ai'
5
9
  import { getCohereApiKeyFromEnv, resolveCohereTransport } from '../utils/client'
6
10
  import type {
7
11
  EmbeddingOptions,
@@ -264,6 +268,7 @@ export class CohereEmbeddingAdapter<
264
268
  */
265
269
  protected async resolveImageUrl(image: ImagePart): Promise<string> {
266
270
  const source = image.source
271
+ if (isFileSource(source)) throw unsupportedFileSourceError(this.name)
267
272
 
268
273
  if (source.type === 'data') {
269
274
  return `data:${source.mimeType};base64,${source.value}`