Skip to content

feat(providers): add Cloudflare Workers AI as an LLM and embedding provider#1111

Open
noises1990 wants to merge 2 commits into
rohitg00:mainfrom
noises1990:feat/cloudflare-provider
Open

feat(providers): add Cloudflare Workers AI as an LLM and embedding provider#1111
noises1990 wants to merge 2 commits into
rohitg00:mainfrom
noises1990:feat/cloudflare-provider

Conversation

@noises1990

@noises1990 noises1990 commented Jul 25, 2026

Copy link
Copy Markdown

What

Adds Cloudflare Workers AI as a first-class LLM and embedding provider.

CLOUDFLARE_API_TOKEN alone enables both roles. Workers AI exposes OpenAI-compatible /ai/v1/chat/completions and /ai/v1/embeddings endpoints, so both providers reuse the existing fetchWithTimeout transport and the same wire shapes as the OpenAI providers — no new dependencies.

Why

Workers AI is a cheap, no-egress option for the background compress/summarize workload, and it is the only major provider that covers both chat and embeddings from a single token. For anyone already on Cloudflare it removes a second vendor from the setup.

Concretely, on the compress-a-short-observation workload this PR's default model costs 0.8 neurons per call — roughly two orders of magnitude below a reasoning-class model (measured, table below).

Detection order

Cloudflare slots in so it never displaces a working setup:

  • LLM: OPENAI_API_KEYMINIMAX_API_KEYCLOUDFLARE_API_TOKENANTHROPIC_API_KEYGEMINI_API_KEYOPENROUTER_API_KEY → noop
  • Embedding: EMBEDDING_PROVIDERGEMINI_API_KEYOPENAI_API_KEYCLOUDFLARE_API_TOKENVOYAGE_API_KEYCOHERE_API_KEYOPENROUTER_API_KEY → local

EMBEDDING_PROVIDER=cloudflare still works as an explicit override, and FALLBACK_PROVIDERS=cloudflare is accepted.

Environment variables

Var Purpose
CLOUDFLARE_API_TOKEN Auth for both LLM and embeddings
CLOUDFLARE_ACCOUNT_ID Builds the endpoint URL; not needed if a *_BASE_URL is set
CLOUDFLARE_MODEL Chat model (default @cf/meta/llama-3.1-8b-instruct-fp8)
CLOUDFLARE_AI_BASE_URL Full chat endpoint override
CLOUDFLARE_AI_GATEWAY_ID Pin a named AI Gateway
CLOUDFLARE_TIMEOUT_MS Per-request timeout; falls back to AGENTMEMORY_LLM_TIMEOUT_MS
CLOUDFLARE_EMBEDDING_MODEL Embedding model (default @cf/baai/bge-base-en-v1.5)
CLOUDFLARE_EMBEDDING_DIMENSIONS Required for models outside the known-models table
CLOUDFLARE_EMBEDDING_BASE_URL Full embedding endpoint override

Three decisions worth reviewing

1. The default chat model is the -fp8 variant. The obvious pick, @cf/meta/llama-3.1-8b-instruct, was deprecated on 2026-05-30 and now returns HTTP 410 — it is gone from the model catalogue entirely. @cf/meta/llama-3.1-8b-instruct-fp8 is the live successor and carries no deprecation flag.

2. There is deliberately no reasoning fallback when extracting content. Reasoning models (@cf/zai-org/glm-*, qwq, deepseek-r1) return content: null alongside a populated reasoning field once the token budget is spent thinking. Treating that as the answer writes model scratchpad into memory and feeds it to the XML parsers in summarize.ts. Truncation now raises an error naming MAX_TOKENS instead. Two tests cover this.

3. Embedding dimensions come from a MODEL_DIMENSIONS table, mirroring embedding/openai.ts. withDimensionGuard throws on every embed when the reported size is wrong, so guessing one size for all models turns a config mistake into a total outage. CLOUDFLARE_EMBEDDING_DIMENSIONS covers anything not in the table.

AI Gateway

The default endpoint already routes through the account's default gateway, so logging, caching, rate limiting and guardrails apply with no configuration. CLOUDFLARE_AI_GATEWAY_ID pins a named gateway — Cloudflare selects it via the cf-aig-gateway-id header, not a different base URL. Applies to both chat and embeddings.

Structure

src/providers/_cloudflare-shared.ts holds endpoint construction, auth headers and gateway selection so the chat and embedding providers don't mirror them — the same split as the existing _openai-shared.ts.

One knowing exception: detectProvider() in config.ts repeats the default model as a literal rather than importing the constant, because providers/ imports config.ts and importing back would cycle. Every other provider default in that function makes the same trade-off; there's a comment saying so.

Testing

test/cloudflare-provider.test.ts — 20 tests covering endpoint resolution, the missing-account-id error, timeout precedence, detectProvider selection, FALLBACK_PROVIDERS acceptance, every dimension path, response parsing, and gateway header behaviour. Plus a detection case in test/embedding-provider.test.ts.

Tests blank competing env keys to "" rather than delete-ing them: getMergedEnv() layers process.env over the developer's real ~/.agentmemory/.env, so deleting a key lets that file's value through and the suite fails on any machine that has an OPENAI_API_KEY configured.

Also verified against live Workers AI (not just mocked): compress, summarize, the full loadConfig()createProvider()ResilientProvider path, 384/768/1024-dim embeddings through withDimensionGuard, gateway routing (unpinned / named / bad-id → 400), and the bad-token error path.

Measured cost, identical prompt, neurons as reported by the API:

model neurons completion tokens
@cf/meta/llama-3.1-8b-instruct-fp8 (default) 0.8 13
@cf/meta/llama-3.2-3b-instruct 0.6 12
@cf/zai-org/glm-5.2 9.3 14
@cf/zai-org/glm-4.7-flash 15.2 415

How to verify

export CLOUDFLARE_API_TOKEN=... CLOUDFLARE_ACCOUNT_ID=...
npx @agentmemory/agentmemory doctor          # LLM + embedding provider both green
npx vitest run test/cloudflare-provider.test.ts test/embedding-provider.test.ts

Checks

  • npm run build — clean
  • npm test — 1431 passing. The 5 failures in test/hook-project.test.ts are pre-existing and reproduce on an unmodified main; they assert basename(cwd) === "agentmemory" and fail whenever the clone directory has another name.
  • npx tsc --noEmit — 25 errors, all pre-existing on main, none in the files this PR touches.
  • No CHANGELOG change, per CONTRIBUTING.

Not in scope

  • Per-task model selection (AGENTMEMORY_SUMMARIZE_MODEL and friends). That needs task identity at the MemoryProvider boundary and overlaps feat(providers): route graph extraction via AGENTMEMORY_GRAPH_MODEL #954 — worth its own issue, and provider-agnostic rather than Cloudflare-specific.
  • Deduplicating resolveDimensions across embedding/openai.ts and embedding/cloudflare.ts. Real duplication, but collapsing it means editing the OpenAI provider, which doesn't belong in a PR that adds a provider. Happy to follow up.

Summary by CodeRabbit

  • New Features
    • Added Cloudflare Workers AI as a supported provider for chat, summarization, and embeddings.
    • Included Cloudflare Workers AI in the interactive first-run provider selection.
    • Added configuration support for custom models, endpoints, request timeouts, AI Gateway routing, and embedding model/dimensions.
  • Documentation
    • Updated environment variable setup and troubleshooting guidance for Cloudflare Workers AI (including embeddings endpoint overrides).
  • Bug Fixes
    • Improved diagnostics for missing provider credentials (including clearer Cloudflare token/account/base-URL guidance).
    • Enhanced error messaging for token-limit truncation scenarios.

…ovider

Workers AI exposes OpenAI-compatible /ai/v1/chat/completions and
/ai/v1/embeddings endpoints, so both providers reuse the existing
fetchWithTimeout transport and the same request/response shapes as the
OpenAI providers. CLOUDFLARE_AI_BASE_URL / CLOUDFLARE_EMBEDDING_BASE_URL
also accept an AI Gateway URL for logging, caching and rate limiting.

CLOUDFLARE_API_TOKEN alone enables Cloudflare for both roles. It sits
after MINIMAX and before ANTHROPIC in the LLM detection order, and after
OPENAI in the embedding order, so it never displaces an already-working
setup. EMBEDDING_PROVIDER still overrides if you want to mix providers.

Requests already flow through the account's default AI Gateway, so
logging, caching, rate limiting and guardrails apply with no config.
CLOUDFLARE_AI_GATEWAY_ID pins a named gateway instead — Cloudflare
selects it via the cf-aig-gateway-id header, not a different base URL.

Default chat model is @cf/meta/llama-3.1-8b-instruct-fp8: the non-fp8
@cf/meta/llama-3.1-8b-instruct was deprecated on 2026-05-30 and now
returns HTTP 410, so it cannot be the default.

Content extraction deliberately has no `reasoning` fallback. Reasoning
models (@cf/zai-org/glm-*, qwq, deepseek-r1) return content:null plus a
populated `reasoning` field once the token budget is spent thinking;
treating that scratchpad as the answer would write chain-of-thought into
memory and feed it to the XML parsers in summarize.ts. Truncation now
raises an error naming MAX_TOKENS instead.

Endpoint construction, auth headers and gateway selection live in
_cloudflare-shared.ts so the chat and embedding providers do not mirror
them, matching the existing _openai-shared.ts split.

Embedding dimensions come from a known-models table (bge-small 384,
bge-base 768, bge-large / bge-m3 / qwen3-embedding 1024,
embeddinggemma-300m 768) because withDimensionGuard throws on every
embed when the reported size is wrong. CLOUDFLARE_EMBEDDING_DIMENSIONS
covers anything not in the table.

Verified against live Workers AI: compress, summarize, the full
loadConfig -> createProvider path, 384/768/1024-dim embeddings through
withDimensionGuard, and gateway routing (unpinned, named, and a bad
gateway id surfacing a 400).

Verify:
  CLOUDFLARE_API_TOKEN=... CLOUDFLARE_ACCOUNT_ID=... npx @agentmemory/agentmemory doctor
  npx vitest run test/cloudflare-provider.test.ts test/embedding-provider.test.ts
Signed-off-by: Adrian Coroi <coroi_adrian@outlook.com>
@vercel

vercel Bot commented Jul 25, 2026

Copy link
Copy Markdown

@noises1990 is attempting to deploy a commit to the rohitg00's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 25, 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 Plus

Run ID: f07057c6-3b2b-41a2-8a14-e6fa47ea7839

📥 Commits

Reviewing files that changed from the base of the PR and between 0e1228e and 8aaaf87.

📒 Files selected for processing (8)
  • README.md
  • src/cli.ts
  • src/cli/doctor-diagnostics.ts
  • src/providers/_cloudflare-shared.ts
  • src/providers/cloudflare.ts
  • src/providers/embedding/cloudflare.ts
  • src/viewer/index.html
  • test/cloudflare-provider.test.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • README.md
  • src/providers/_cloudflare-shared.ts
  • src/cli/doctor-diagnostics.ts
  • src/providers/embedding/cloudflare.ts
  • src/providers/cloudflare.ts
  • test/cloudflare-provider.test.ts
  • src/viewer/index.html
  • src/cli.ts

📝 Walkthrough

Walkthrough

Cloudflare Workers AI is added as an LLM and embedding provider. The change adds shared endpoint and gateway handling, provider detection and construction, embedding dimension validation, onboarding and diagnostics updates, documentation, and automated coverage.

Changes

Cloudflare provider integration

Layer / File(s) Summary
Chat transport and provider
src/providers/_cloudflare-shared.ts, src/providers/cloudflare.ts, src/types.ts, test/cloudflare-provider.test.ts
Adds OpenAI-compatible Cloudflare chat requests, gateway headers, timeout handling, response parsing, truncation errors, and tests.
Embeddings provider and factory
src/providers/embedding/*, test/cloudflare-provider.test.ts, test/embedding-provider.test.ts
Adds Cloudflare embeddings with model-dimension resolution, validation, batch requests, and factory support.
Configuration and provider wiring
src/config.ts, src/providers/index.ts, src/types.ts, test/cloudflare-provider.test.ts
Detects Cloudflare credentials, supports fallback configuration, selects default models, and constructs Cloudflare providers.
Onboarding, diagnostics, and documentation
.env.example, README.md, src/cli.ts, src/cli/doctor-diagnostics.ts, src/cli/onboarding.ts, src/functions/*, src/viewer/index.html
Documents Cloudflare variables and updates setup, diagnostics, runtime messages, and viewer guidance.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant Config
  participant CloudflareProvider
  participant WorkersAI
  CLI->>Config: Load provider configuration
  Config->>CloudflareProvider: Construct cloudflare provider
  CloudflareProvider->>WorkersAI: Send chat or embedding request
  WorkersAI-->>CloudflareProvider: Return completion or embedding data
  CloudflareProvider-->>CLI: Return provider result
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: adding Cloudflare Workers AI as both an LLM and embedding provider.
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 unit tests (beta)
  • Create PR with unit tests

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: 1

🧹 Nitpick comments (2)
README.md (1)

1379-1380: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document Cloudflare base URL overrides here.

The README lists token, account, model, and dimensions settings but omits CLOUDFLARE_AI_BASE_URL and CLOUDFLARE_EMBEDDING_BASE_URL, despite both being supported configuration paths. Add those variables so users following the README can configure custom endpoints without relying on .env.example.

Also applies to: 1416-1419

🤖 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 `@README.md` around lines 1379 - 1380, Update the Cloudflare configuration
examples in the README to include CLOUDFLARE_AI_BASE_URL and
CLOUDFLARE_EMBEDDING_BASE_URL alongside the existing token, account, model, and
dimensions settings, documenting that they override the default endpoints.
src/providers/embedding/cloudflare.ts (1)

28-39: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Dimensions override parsing is lenient, and the doc comment overstates enforcement.

parseInt(override, 10) accepts trailing garbage ("1024abc"1024, "10.5"10) silently, unlike the sibling parsePositiveInt in cloudflare.ts which strictly validates the whole string with /^\d+$/. Separately, the doc comment says CLOUDFLARE_EMBEDDING_DIMENSIONS is "required for models not in the MODEL_DIMENSIONS table," but the code never throws for an unknown model without an override — it silently uses DEFAULT_DIMENSIONS.

♻️ Align validation strictness with parsePositiveInt
-  if (override !== undefined && override.trim().length > 0) {
-    const parsed = parseInt(override, 10);
-    if (!Number.isFinite(parsed) || parsed <= 0) {
+  if (override !== undefined && override.trim().length > 0) {
+    const trimmed = override.trim();
+    const parsed = /^\d+$/.test(trimmed) ? Number(trimmed) : NaN;
+    if (!Number.isFinite(parsed) || parsed <= 0) {
       throw new Error(
         `CLOUDFLARE_EMBEDDING_DIMENSIONS must be a positive integer, got: ${override}`,
       );
     }
     return parsed;
   }

Also applies to: 56-57

🤖 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 `@src/providers/embedding/cloudflare.ts` around lines 28 - 39, Update
resolveDimensions to validate the entire override string with the same strict
positive-integer rules as the sibling parsePositiveInt, rejecting decimal
values, trailing characters, and non-positive values before returning the parsed
dimension. Correct the related documentation to state the actual fallback
behavior for models absent from MODEL_DIMENSIONS, since unknown models without
an override use DEFAULT_DIMENSIONS rather than throwing.
🤖 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 `@src/cli.ts`:
- Around line 1733-1740: Update the setup hints to describe complete Cloudflare
configuration: in src/cli.ts lines 1733-1740, mention CLOUDFLARE_ACCOUNT_ID or
the relevant chat and embedding base-URL overrides in both Cloudflare hints; in
src/viewer/index.html line 3752, broaden the LLM banner beyond the token alone;
at line 3765 include CLOUDFLARE_ACCOUNT_ID or CLOUDFLARE_AI_BASE_URL in the LLM
snippet; and at line 3777 include CLOUDFLARE_ACCOUNT_ID or
CLOUDFLARE_EMBEDDING_BASE_URL in the embedding snippet.

---

Nitpick comments:
In `@README.md`:
- Around line 1379-1380: Update the Cloudflare configuration examples in the
README to include CLOUDFLARE_AI_BASE_URL and CLOUDFLARE_EMBEDDING_BASE_URL
alongside the existing token, account, model, and dimensions settings,
documenting that they override the default endpoints.

In `@src/providers/embedding/cloudflare.ts`:
- Around line 28-39: Update resolveDimensions to validate the entire override
string with the same strict positive-integer rules as the sibling
parsePositiveInt, rejecting decimal values, trailing characters, and
non-positive values before returning the parsed dimension. Correct the related
documentation to state the actual fallback behavior for models absent from
MODEL_DIMENSIONS, since unknown models without an override use
DEFAULT_DIMENSIONS rather than throwing.
🪄 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 Plus

Run ID: 1f01f4b4-9dd5-446a-b72d-6cdcb2a27a78

📥 Commits

Reviewing files that changed from the base of the PR and between a8e7d19 and 0e1228e.

📒 Files selected for processing (17)
  • .env.example
  • README.md
  • src/cli.ts
  • src/cli/doctor-diagnostics.ts
  • src/cli/onboarding.ts
  • src/config.ts
  • src/functions/consolidation-pipeline.ts
  • src/functions/summarize.ts
  • src/providers/_cloudflare-shared.ts
  • src/providers/cloudflare.ts
  • src/providers/embedding/cloudflare.ts
  • src/providers/embedding/index.ts
  • src/providers/index.ts
  • src/types.ts
  • src/viewer/index.html
  • test/cloudflare-provider.test.ts
  • test/embedding-provider.test.ts

Comment thread src/cli.ts Outdated
- Setup hints advertised CLOUDFLARE_API_TOKEN alone. Cloudflare is the only
  provider needing a second variable, so doctor, init, the diagnostics
  moreInfo and the three viewer banners now name CLOUDFLARE_ACCOUNT_ID
  (or the base-URL override) alongside the token.

- CLOUDFLARE_EMBEDDING_DIMENSIONS used parseInt, which took "1024abc" as
  1024 and "10.5" as 10 — silently producing vectors withDimensionGuard
  rejects on every embed. It now shares the strict parsePositiveInt used
  for CLOUDFLARE_TIMEOUT_MS, moved into _cloudflare-shared.ts so the two
  providers stop carrying separate copies. Covered by a table test.

- MODEL_DIMENSIONS doc claimed the override was "required" for unknown
  models; the code falls back to the default size instead. Reworded to
  describe what actually happens.

- README omitted CLOUDFLARE_AI_BASE_URL, CLOUDFLARE_EMBEDDING_BASE_URL and
  CLOUDFLARE_AI_GATEWAY_ID from the env blocks.

Re-verified against live Workers AI: chat and 768-dim embeddings still
resolve through the default path.

Signed-off-by: Adrian Coroi <coroi_adrian@outlook.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