Skip to content

embeddings: default check_embedding_ctx_length=False for OpenAI-compa… - #129

Open
kkampli-singlestore wants to merge 5 commits into
mainfrom
users/kaushik/embed-ctx-length-default
Open

embeddings: default check_embedding_ctx_length=False for OpenAI-compa…#129
kkampli-singlestore wants to merge 5 commits into
mainfrom
users/kaushik/embed-ctx-length-default

Conversation

@kkampli-singlestore

@kkampli-singlestore kkampli-singlestore commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Don’t use OpenAI’s tokenizer for Qwen — LangChain’s default (check_embedding_ctx_length=True) turns text into OpenAI token IDs. Qwen can’t read those, so embeddings are garbage. Send raw text instead (False) so the server tokenizes correctly.

Keep OpenAI/Azure on the default — Real OpenAI/Azure models still use True (tiktoken + LangChain’s built-in long-text handling). Only non-OpenAI/Nova models get the raw-text default.

Chunk long Qwen inputs ourselves — With raw text, LangChain won’t split long docs, so the server 400s or truncates. For those models, split into character chunks, embed each, then average into one vector so long text still works.

Test cases:
1.
Before:
label cosine_vs_anchor
anchor 1.0
unrelated2 0.3536
same_intent 0.3461
paraphrase 0.304
unrelated 0.2523

After
image

Chunking Long Text
Screenshot from 2026-07-22 21-56-26


Note

Medium Risk
Changes default embedding behavior and vector composition for non-Azure OpenAI-compatible models, which can alter embedding outputs and API call patterns for long documents; Azure and Amazon paths are explicitly preserved.

Overview
SingleStoreEmbeddingsFactory now picks the embeddings client by hosting platform instead of always returning stock OpenAIEmbeddings.

For Azure, behavior stays aligned with real OpenAI models: check_embedding_ctx_length=True and LangChain’s tiktoken-based client-side handling.

For other OpenAI-compatible hosts (e.g. Qwen on Nova), the factory defaults check_embedding_ctx_length=False so requests send raw text for server-side tokenization, and returns a new _ChunkedOpenAIEmbeddings wrapper. That class splits inputs longer than max_chunk_chars (default 24k), embeds each chunk via sync/async embed_documents, then length-weighted averages chunk vectors and L2-normalizes them back to one vector per input so long text avoids server reject/truncate limits.

The Bedrock/Amazon path is unchanged.

Reviewed by Cursor Bugbot for commit 6def72b. Bugbot is set up for automated code reviews on this repo. Configure here.

…tible endpoints

langchain OpenAIEmbeddings defaults to check_embedding_ctx_length=True, which
tokenizes client-side with tiktoken and sends OpenAI token IDs. Non-OpenAI models
behind an OpenAI-compatible endpoint (e.g. Qwen) can't interpret those IDs and
return nonsensical embeddings. Default the flag to False so raw text is sent and
the server tokenizes correctly. No-op for genuine OpenAI models; callers can opt
back in with check_embedding_ctx_length=True. Scoped to the OpenAI path, so the
Bedrock/Amazon branch is unaffected.

Co-authored-by: Cursor <cursoragent@cursor.com>
Only default check_embedding_ctx_length=False for self-hosted / non-OpenAI models
(e.g. Qwen on the 'Nova' platform). Genuine OpenAI/Azure models ('Azure'/'OpenAI')
keep langchain's default (True), preserving correct tiktoken tokenization and
client-side long-input chunking for them.

Co-authored-by: Cursor <cursoragent@cursor.com>

@mgiannakopoulos mgiannakopoulos left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🚢 Ship IT!

…models

- Azure (OpenAI) models: keep check_embedding_ctx_length=True so langchain uses
  tiktoken (correct for these models) with the passed model name, incl. its long-
  input chunking.
- Non-OpenAI models (e.g. Qwen on 'Nova'): send raw text (check_embedding_ctx_length
  =False) so the server's own tokenizer is used, and split long inputs into
  character-bounded chunks client-side, embedding each and length-weighted-averaging
  them into one vector per input -- irrespective of the flag -- so long texts never
  hit the server's hard context limit (which otherwise errors or silently truncates).

Output stays 1:1 with input rows; short inputs pass through unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
…False otherwise

Non-OpenAI models (e.g. Qwen on 'Nova') get wrong embeddings from langchain's default
tiktoken tokenization (it sends OpenAI token IDs). Send raw text for them so the server
tokenizes with the model's own tokenizer; keep tiktoken (True) for Azure/OpenAI models,
where it's correct. Long-input chunking deferred.

Co-authored-by: Cursor <cursoragent@cursor.com>
Re-add _ChunkedOpenAIEmbeddings: for non-Azure models (e.g. Qwen on 'Nova'), send raw
text (check_embedding_ctx_length=False) and split long inputs into character-bounded
chunks, embedding each and length-weighted-averaging into one vector per input, so long
texts never hit the server's context limit. Azure keeps tiktoken (True).

Co-authored-by: Cursor <cursoragent@cursor.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6def72b. Configure here.

n = max(1, self.max_chunk_chars)
if len(text) <= n:
return [text]
return [text[i:i + n] for i in range(0, len(text), n)]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Character chunks exceed token limit

Medium Severity

_ChunkedOpenAIEmbeddings caps chunks at max_chunk_chars (24000), but Nova-style servers enforce a token context window (often 8192). For scripts where characters map roughly one-to-one to tokens, a single chunk can still exceed that limit, so the server may reject the request or truncate input while the client treats the embedding as complete.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6def72b. Configure here.

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.

3 participants