Skip to content

feat: add multimodal embed() activity and provider adapters#926

Open
jherr wants to merge 9 commits into
mainfrom
feat/embed
Open

feat: add multimodal embed() activity and provider adapters#926
jherr wants to merge 9 commits into
mainfrom
feat/embed

Conversation

@jherr

@jherr jherr commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds a typed multimodal embed() activity with per-model input modalities, Matryoshka dimensions, middleware/OTel/devtools integration, and embeddings: [{ vector, index }] results
  • Ships embedding adapters for OpenAI, Gemini, Mistral, Ollama, and Bedrock (including Titan multimodal), plus a new @tanstack/ai-cohere package for embed-v4.0
  • Includes docs, migration notes, unit tests, and aimock-backed E2E coverage for openai/gemini/ollama/mistral

Test plan

  • pnpm test:pr (sherif, knip, docs, eslint, lib, types, build)
  • Embedding unit tests across @tanstack/ai and provider packages
  • pnpm --filter @tanstack/ai-e2e test:e2e -- tests/embedding.spec.ts (4/4 passed)
  • Spot-check docs pages: Embeddings, Cohere, Bedrock multimodal examples

Made with Cursor

Summary by CodeRabbit

  • New Features

    • Added the embed() activity for single or batch text and image inputs, including fused multimodal content.
    • Added embedding support for OpenAI, Gemini, Mistral, Ollama, Bedrock, and Cohere.
    • Added configurable vector dimensions where supported, usage reporting, middleware integration, and embedding activity events.
    • Added a new Cohere embedding package and Bedrock embedding capabilities.
  • Documentation

    • Added a comprehensive embeddings guide, provider examples, support details, and migration guidance.
  • Tests

    • Added unit, type-safety, and end-to-end coverage for embedding workflows.

jherr and others added 7 commits June 17, 2026 08:10
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 23d692da-0d11-4da7-b246-95d13e7b7f56

📥 Commits

Reviewing files that changed from the base of the PR and between af708d4 and 0246c4a.

📒 Files selected for processing (6)
  • packages/ai-cohere/src/adapters/embedding.ts
  • packages/ai-cohere/src/utils/client.ts
  • packages/ai-cohere/tests/embedding-adapter.test.ts
  • packages/ai-event-client/src/index.ts
  • packages/ai/src/activities/embed/adapter.ts
  • testing/e2e/src/routes/api.embedding.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/ai-cohere/src/utils/client.ts
  • packages/ai/src/activities/embed/adapter.ts
  • packages/ai-event-client/src/index.ts
  • packages/ai-cohere/src/adapters/embedding.ts
  • packages/ai-cohere/tests/embedding-adapter.test.ts
  • testing/e2e/src/routes/api.embedding.ts

📝 Walkthrough

Walkthrough

Adds the shared embed() activity with typed text and multimodal inputs, provider adapters for OpenAI, Gemini, Mistral, Ollama, Bedrock, and Cohere, embedding observability events, documentation, and end-to-end tests.

Changes

Embedding platform

Layer / File(s) Summary
Core embed activity and contracts
packages/ai/src/activities/embed/*, packages/ai/src/types.ts, packages/ai/src/utilities/embedding-input.ts, packages/ai/tests/*
Adds typed embedding inputs/results, adapter abstractions, modality normalization, middleware lifecycle handling, and tests.
Provider adapters
packages/ai-openai/*, packages/ai-gemini/*, packages/ai-mistral/*, packages/ai-ollama/*
Adds text embedding adapters, provider options, model metadata, factories, exports, and adapter tests.
Bedrock and Cohere adapters
packages/ai-bedrock/*, packages/ai-cohere/*
Adds batched and multimodal adapters, dimension and provider-option mapping, image handling, factories, packaging, and tests.
Events and E2E flow
packages/ai-event-client/*, testing/e2e/*
Adds embedding devtools events, mock provider endpoints, feature routing, UI rendering, fixtures, and Playwright coverage.
Documentation and release metadata
.changeset/*, docs/*
Documents the API, provider support, migration guidance, navigation, and package release metadata.

Estimated code review effort: 5 (Critical) | ~90 minutes

Possibly related PRs

  • TanStack/ai#242: Updates shared token-usage event structures used by embedding results and middleware.

Suggested reviewers: tombeckenham, AlemTuzlak

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is missing the required Changes, Checklist, and Release Impact sections from the template. Rewrite it to match the template, add a Changes section, complete the checklist, and state the release impact.
Docstring Coverage ⚠️ Warning Docstring coverage is 61.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding a multimodal embed() activity and provider adapters.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/embed
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feat/embed

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.agentsroom/agents.json:
- Around line 7-9: Remove the tracked generated files under .agentsroom,
including agents.json and prompts.json, from version control while leaving the
existing ignore rule intact so future state files remain untracked.

In `@docs/config.json`:
- Around line 309-313: Add an updatedAt field with value "2026-07-10" to both
new documentation entries identified by the "Embeddings" and "Cohere" labels in
docs/config.json, alongside their existing addedAt fields.

In `@docs/embeddings.md`:
- Around line 113-116: Add the missing trailing comma after the text object in
the multimodal example’s content array, before the following image object, so
the array is valid TypeScript.

In `@packages/ai-cohere/src/adapters/embedding.ts`:
- Around line 140-151: Add configurable AbortController-based timeouts with a
sensible default to both the Cohere API fetch in the embedding request method
and the image URL fetch. Create and wire an abort signal for each request,
ensure the timer is cleared after completion, and preserve any existing
caller-provided cancellation behavior if supported by the adapter configuration.
- Around line 228-245: Validate image URLs before fetching when allowUrlFetch is
enabled: parse the value in the URL-fetch branch and reject loopback, private,
link-local, and other non-public IP addresses (including equivalent IPv6 ranges)
before calling fetch in the image URL handling logic. Preserve the existing
error behavior for disallowed fetching and fetch failures, and ensure malformed
or unsupported URLs are rejected clearly.

In `@packages/ai-gemini/src/adapters/embedding.ts`:
- Around line 48-51: Update the Gemini embedding adapter constructor to call
super(model, {}) instead of passing config, so BaseEmbeddingAdapter does not
retain the Gemini apiKey in this.config while createGeminiClient(config) still
receives the credentials.

In `@packages/ai/src/activities/embed/adapter.ts`:
- Around line 108-110: Resolve the generateId signature mismatch by either
updating generateId to accept and use a prefix parameter consistent with
Cohere’s generateId(this.name) call and embed/index.ts createId(prefix), or
changing the downstream call to match the no-argument API. Also replace
substring(7) with the consistent slice(2, 9) random suffix logic.

In `@packages/ai/src/activities/embed/index.ts`:
- Around line 216-226: Add the optional modelOptions field to the event payload
interfaces in the ai-event-client index, specifically
EmbeddingRequestStartedEvent, EmbeddingRequestCompletedEvent, and
EmbeddingRequestErrorEvent, using the appropriate existing model-options type.
Keep the modelOptions fields in all corresponding emissions in the embedding
activity aligned with these interface definitions.

In `@testing/e2e/src/routes/api.embedding.ts`:
- Around line 47-68: Change the factories declaration to a type that allows
missing provider keys, such as an object index signature returning (() => any) |
undefined, while retaining the optional call on factories[provider] in the
embedding factory lookup. This preserves runtime safety for unsupported
providers and resolves the unnecessary optional-chain lint error.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e692e98a-9142-4e54-b846-abe1a11d5aa4

📥 Commits

Reviewing files that changed from the base of the PR and between 5fcaf90 and af708d4.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (71)
  • .agentsroom/.gitignore
  • .agentsroom/agents.json
  • .agentsroom/prompts.json
  • .changeset/embed-activity-multimodal.md
  • docs/adapters/bedrock.md
  • docs/adapters/cohere.md
  • docs/adapters/gemini.md
  • docs/adapters/mistral.md
  • docs/adapters/ollama.md
  • docs/adapters/openai.md
  • docs/config.json
  • docs/embeddings.md
  • docs/migration/migration-from-vercel-ai.md
  • docs/migration/migration.md
  • packages/ai-bedrock/src/adapters/embedding.ts
  • packages/ai-bedrock/src/embedding/embedding-provider-options.ts
  • packages/ai-bedrock/src/index.ts
  • packages/ai-bedrock/src/model-meta.ts
  • packages/ai-bedrock/tests/embedding-adapter.test.ts
  • packages/ai-cohere/README.md
  • packages/ai-cohere/package.json
  • packages/ai-cohere/src/adapters/embedding.ts
  • packages/ai-cohere/src/embedding/embedding-provider-options.ts
  • packages/ai-cohere/src/index.ts
  • packages/ai-cohere/src/model-meta.ts
  • packages/ai-cohere/src/utils/client.ts
  • packages/ai-cohere/tests/embedding-adapter.test.ts
  • packages/ai-cohere/tsconfig.json
  • packages/ai-cohere/vite.config.ts
  • packages/ai-event-client/src/index.ts
  • packages/ai-gemini/src/adapters/embedding.ts
  • packages/ai-gemini/src/embedding/embedding-provider-options.ts
  • packages/ai-gemini/src/index.ts
  • packages/ai-gemini/src/model-meta.ts
  • packages/ai-gemini/tests/embedding-adapter.test.ts
  • packages/ai-mistral/package.json
  • packages/ai-mistral/src/adapters/embedding.ts
  • packages/ai-mistral/src/embedding/embedding-provider-options.ts
  • packages/ai-mistral/src/index.ts
  • packages/ai-mistral/src/model-meta.ts
  • packages/ai-mistral/tests/embedding-adapter.test.ts
  • packages/ai-ollama/src/adapters/embedding.ts
  • packages/ai-ollama/src/embedding/embedding-provider-options.ts
  • packages/ai-ollama/src/index.ts
  • packages/ai-ollama/src/model-meta.ts
  • packages/ai-ollama/tests/embedding-adapter.test.ts
  • packages/ai-openai/src/adapters/embedding.ts
  • packages/ai-openai/src/embedding/embedding-provider-options.ts
  • packages/ai-openai/src/index.ts
  • packages/ai-openai/src/model-meta.ts
  • packages/ai-openai/tests/embedding-adapter.test.ts
  • packages/ai/src/activities/embed/adapter.ts
  • packages/ai/src/activities/embed/index.ts
  • packages/ai/src/activities/index.ts
  • packages/ai/src/activities/middleware/types.ts
  • packages/ai/src/index.ts
  • packages/ai/src/middlewares/otel.ts
  • packages/ai/src/types.ts
  • packages/ai/src/utilities/embedding-input.ts
  • packages/ai/tests/embed-per-model-type-safety.test.ts
  • packages/ai/tests/embed.test.ts
  • testing/e2e/fixtures/embedding/basic.json
  • testing/e2e/global-setup.ts
  • testing/e2e/src/components/EmbeddingUI.tsx
  • testing/e2e/src/lib/feature-support.ts
  • testing/e2e/src/lib/features.ts
  • testing/e2e/src/lib/types.ts
  • testing/e2e/src/routeTree.gen.ts
  • testing/e2e/src/routes/$provider/$feature.tsx
  • testing/e2e/src/routes/api.embedding.ts
  • testing/e2e/tests/embedding.spec.ts

Comment thread .agentsroom/agents.json Outdated
Comment on lines +7 to +9
"id": "agent-1776361243376-3sekdc",
"claudeSessionId": "96773a93-be2a-45a9-a732-ceb224d3d0e5"
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether .agentsroom files are tracked despite the root .gitignore rule
git ls-files .agentsroom/

Repository: TanStack/ai

Length of output: 221


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== root .gitignore ==\n'
if [ -f .gitignore ]; then
  cat -n .gitignore
else
  echo 'No root .gitignore'
fi

printf '\n== .agentsroom/.gitignore ==\n'
if [ -f .agentsroom/.gitignore ]; then
  cat -n .agentsroom/.gitignore
else
  echo 'No .agentsroom/.gitignore'
fi

printf '\n== .agentsroom/agents.json ==\n'
cat -n .agentsroom/agents.json

Repository: TanStack/ai

Length of output: 2498


Remove the tracked .agentsroom state.agentsroom/agents.json:7-9 is still committed even though .agentsroom/ is ignored, and it includes a claudeSessionId. Untrack the generated .agentsroom files (including agents.json and prompts.json) so personal/session data stays out of git.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.agentsroom/agents.json around lines 7 - 9, Remove the tracked generated
files under .agentsroom, including agents.json and prompts.json, from version
control while leaving the existing ignore rule intact so future state files
remain untracked.

Comment thread docs/config.json
Comment on lines +309 to +313
{
"label": "Embeddings",
"to": "embeddings",
"addedAt": "2026-07-10"
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add updatedAt to the new documentation entries.

The new Embeddings and Cohere pages have addedAt but no updatedAt, although their content is introduced in this change. Per the docs/config.json guideline, add updatedAt: "2026-07-10" to both entries.

Also applies to: 570-572

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/config.json` around lines 309 - 313, Add an updatedAt field with value
"2026-07-10" to both new documentation entries identified by the "Embeddings"
and "Cohere" labels in docs/config.json, alongside their existing addedAt
fields.

Source: Coding guidelines

Comment thread docs/embeddings.md
Comment on lines +113 to +116
type: "content",
content: [
{ type: "text", content: "Fender Stratocaster, sunburst finish" },
{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Add the missing comma in the multimodal example.

The text object at Line [115] is immediately followed by the image object, making the content array invalid TypeScript.

Proposed fix
-        { type: "text", content: "Fender Stratocaster, sunburst finish" }
+        { type: "text", content: "Fender Stratocaster, sunburst finish" },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
type: "content",
content: [
{ type: "text", content: "Fender Stratocaster, sunburst finish" },
{
type: "content",
content: [
{ type: "text", content: "Fender Stratocaster, sunburst finish" },
{
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/embeddings.md` around lines 113 - 116, Add the missing trailing comma
after the text object in the multimodal example’s content array, before the
following image object, so the array is valid TypeScript.

Source: Coding guidelines

Comment thread packages/ai-cohere/src/adapters/embedding.ts Outdated
Comment thread packages/ai-cohere/src/adapters/embedding.ts
Comment on lines +48 to +51
constructor(config: GeminiEmbeddingConfig, model: TModel) {
super(model, config)
this.client = createGeminiClient(config)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check BaseEmbeddingAdapter class definition and constructor
rg -n 'class BaseEmbeddingAdapter' --type=ts -A 20

# Check if other Gemini adapters pass config to super (package convention check)
rg -n 'super\(' packages/ai-gemini/src/adapters/ --type=ts -A 1

# Check if the base class stores or exposes the second constructor argument
rg -n 'constructor' packages/ai/src/activities/embed/adapter.ts --type=ts -A 15

Repository: TanStack/ai

Length of output: 149


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Files under packages/ai-gemini/src/adapters ==\n'
git ls-files 'packages/ai-gemini/src/adapters/**/*.ts' || true

printf '\n== Locate BaseEmbeddingAdapter ==\n'
rg -n 'class BaseEmbeddingAdapter|BaseEmbeddingAdapter' packages -g '*.ts' -A 20 -B 5 || true

printf '\n== Read Gemini embedding adapter ==\n'
sed -n '1,220p' packages/ai-gemini/src/adapters/embedding.ts || true

printf '\n== Read likely base embedding adapter file(s) ==\n'
rg -n 'constructor\(' packages/ai -g '*adapter*.ts' -A 20 -B 5 || true

Repository: TanStack/ai

Length of output: 50369


Pass {} to super() here
BaseEmbeddingAdapter stores the second constructor argument on this.config, so passing config keeps the Gemini apiKey on the adapter instance. Use super(model, {}) here to avoid retaining the secret, matching the other embedding adapters.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai-gemini/src/adapters/embedding.ts` around lines 48 - 51, Update
the Gemini embedding adapter constructor to call super(model, {}) instead of
passing config, so BaseEmbeddingAdapter does not retain the Gemini apiKey in
this.config while createGeminiClient(config) still receives the credentials.

Comment thread packages/ai/src/activities/embed/adapter.ts Outdated
Comment thread packages/ai/src/activities/embed/index.ts
Comment thread testing/e2e/src/routes/api.embedding.ts Outdated
jherr and others added 2 commits July 10, 2026 19:43
These personal agent config files should not ship in the embed PR.

Co-authored-by: Cursor <cursoragent@cursor.com>
Align embedding event types with emitted modelOptions, harden generateId,
fix e2e factory typing, and add Cohere fetch timeouts plus SSRF guards.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant