Some Responses API conversations can safely replay prior response item
IDs because the server stored those items. That assumption breaks when
`store=False`: prior `rs_*` reasoning items and `msg_*` assistant
message IDs are not available on the server for the next turn, so
replaying them can crash with `Item with id 'rs_...' not found` or
similar item lookup errors.
This updates the Responses API payload builder to treat `store=False` as
a stateless replay mode. The visible assistant text is still preserved
in history, but server-side response item IDs are not sent back unless
they are usable without server persistence.
In practical terms:
- Bare `rs_*` reasoning items are dropped for `store=False` because they
only reference server-side state that was not stored.
- Reasoning items with `encrypted_content` are preserved because OpenAI
uses them as the stateless/ZDR way to carry reasoning context forward.
- Prior assistant `msg_*` IDs are omitted for `store=False`; the
assistant message is replayed as ordinary assistant text instead of as a
reference to a stored server item.
Dropping `msg_*` IDs in this case should not remove useful user-visible
context: the text content remains in the request. It only removes an
item identity that the server cannot reliably resolve when
`store=False`. Persisted `store=True` Responses flows continue to replay
item IDs as before.
The regression test mirrors the minimal user story: make one
Responses/Codex call, reuse the returned `AIMessage` in a follow-up
request, and verify the next payload keeps the visible assistant message
and encrypted reasoning context while omitting unresolvable bare item
references.
When using `ProviderStrategy`, `create_agent` unnecessarily sets
`strict=True` on tools for all providers. This is only needed for OpenAI
/ chat completions. Here we unset `strict`. For OpenAI:
1. We set it in `BaseChatOpenAI.bind_tools` (as a convenience to users
calling `model.bind_tools` directly)
2. We (redundantly) special-case OpenAI in the `create_agent` factory
logic so that things will not break for users who upgrade `langchain`
but not `langchain-openai`.
Note: payloads for OpenAI are tested here and appear unchanged:
https://github.com/langchain-ai/langchain/blob/master/libs/langchain_v1/tests/unit_tests/agents/test_response_format_integration.py
Quick test:
```python
from langchain.agents import create_agent
from langchain.agents.structured_output import ProviderStrategy
from pydantic import BaseModel
class Weather(BaseModel):
temperature: float
condition: str
def weather_tool(location: str) -> str:
"""Get the weather at a location."""
return "Sunny and 75 degrees F."
for model in [
"anthropic:claude-sonnet-4-6",
"openai:gpt-5.4",
"google_genai:gemini-3.5-flash",
]:
agent = create_agent(
model=model,
tools=[weather_tool],
response_format=ProviderStrategy(Weather),
)
result = agent.invoke({
"messages": [{"role": "user", "content": "What's the weather in SF?"}]
})
print(result["structured_response"])
```
Responses API requests now strip the Chat Completions-only `stop`
parameter before sending the payload. This avoids request rejection
while preserving `stop` for the Chat Completions API path.
The Codex `_astream` path was reworked to build its auth headers from an
async-fetched token, but `_agenerate` was left on the old "prime the
cache, then read it back synchronously" approach. That sync read still
went through `_FileChatGPTOAuthTokenProvider.get_token`, which acquires
a thread lock and a cross-process file lock on every call — blocking the
event loop even when the token is already warm. Both async paths now
build headers the same way, so neither touches sync `get_token` on the
loop.
## Changes
- `_ChatOpenAICodex._agenerate` now fetches the token via `aget_token`,
builds the Codex headers off-loop, and hands them to
`_get_request_payload` through the private `_codex_headers` kwarg —
eliminating the synchronous token read (and its lock acquisition) that
previously ran on the event loop inside `super()._agenerate`.
- Replaced the duplicated `"_codex_headers"` string literal across
`_agenerate`, `_astream`, and `_get_request_payload` with a
`_CODEX_HEADERS_KWARG` module constant, documenting that the kwarg is
popped before the payload reaches the SDK.
- Documented the deliberate `is not None` check in
`_get_request_payload`: an explicitly-built empty header dict
(accountless token with `originator=None`) is honored as-is rather than
falling back to the blocking sync read.
Codex streaming now builds request headers from the async token path
instead of refreshing asynchronously and later reading the token
synchronously during payload construction. That keeps
`_ChatOpenAICodex._astream` off the sync token path while preserving the
`ChatGPT-Account-Id` and `originator` headers needed by Codex requests.
- Mark the Codex OAuth model/token helper classes private with leading
underscores
- Remove `_ChatOpenAICodex` from package-level public exports
- Keep a once-per-process runtime warning that use is
experimental/unofficial and must comply with applicable OpenAI account,
workspace, plan, terms, policies, rate limits, and safeguards
[Docs](https://github.com/langchain-ai/docs/pull/4115)
Adds a new `ChatOpenAICodex` chat model and a small `chatgpt_oauth`
module so users can authenticate with their ChatGPT subscription (OAuth
2.0 Authorization Code Flow with PKCE) and route Responses-API requests
to the ChatGPT Codex backend at `https://chatgpt.com/backend-api/codex`.
Login and token persistence live behind a refresh-aware
`ChatGPTOAuthTokenProvider` protocol so they stay decoupled from model
invocation. The existing API-key `ChatOpenAI` behavior is untouched. By
default the file-backed provider writes to
`~/.langchain/chatgpt-auth.json` to avoid stomping on Codex CLI / VS
Code sessions at `~/.codex/auth.json`. No new required dependencies are
introduced (uses stdlib + `httpx`).
```python
from langchain_openai import ChatOpenAICodex
from langchain_openai.chatgpt_oauth import login_chatgpt
login_chatgpt()
model = ChatOpenAICodex(model="gpt-5.5")
response = model.invoke("hello")
```
_Opened collaboratively by Mason Daugherty and open-swe._
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Mason Daugherty <61371264+mdrxy@users.noreply.github.com>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
Co-authored-by: Mason Daugherty <mason@langchain.dev>
Package-version trace metadata now uses the LangChain-owned
`metadata["lc_versions"]` convention instead of the user-owned
`metadata["versions"]` key. Metadata merging is narrowed so only
`lc_versions` accumulates nested package-version entries, while generic
nested metadata keeps normal last-writer-wins behavior.
## Changes
- Renamed `BaseLanguageModel._add_version()` trace metadata from
`versions` to `lc_versions`, including docstrings and the non-dict
replacement warning.
- Scoped `_merge_metadata_dicts()` nested-map accumulation to only
`lc_versions`; duplicate package entries remain last-writer-wins and
`lc_versions` mappings are copied defensively.
- Preserved user-owned `metadata["versions"]` semantics by keeping it
out of package-version tracking and generic nested metadata merging.
- Updated runnable snapshots and partner package metadata assertions
across Anthropic, DeepSeek, Fireworks, Groq, Hugging Face, MistralAI,
Ollama, OpenAI, OpenRouter, Perplexity, and xAI to expect `lc_versions`.
## Testing
- Added/adjusted core tests for `lc_versions` accumulation, duplicate
package overwrite behavior, non-dict `lc_versions` replacement,
defensive copying, and `metadata["versions"]` last-writer-wins behavior.
- Ran focused core and partner metadata tests plus Ruff checks for
changed areas.
Following on the heels of #35293
TODO:
- Packages outside of this repo (e.g. LiteLLM, Nvidia, Google, AWS)
---
## Summary
Surface partner package versions in `metadata.versions` on LangSmith
traces. Mirrors the JS SDK's `_addVersion()` pattern
([langchainjs#10106](https://github.com/langchain-ai/langchainjs/pull/10106)).
Each model constructor records its package version via `_add_version()`
on `BaseLanguageModel`. The version dict accumulates through the class
hierarchy — `langchain-core` is added in
`BaseLanguageModel.model_post_init`, `langchain-openai` in
`BaseChatOpenAI._set_openai_chat_version`, and each leaf partner in its
uniquely-named `model_validator`. Traces end up with:
```json
{
"metadata": {
"versions": {
"langchain-core": "1.4.5",
"langchain-openai": "1.3.0",
"langchain-xai": "1.2.2"
}
}
}
```
### Changes
- `BaseLanguageModel._add_version(pkg, version)` — appends to
`self.metadata["versions"]`; accepts any `Mapping` type; emits a warning
if a non-mapping value is found and replaced
- `BaseLanguageModel.model_post_init` — adds `langchain-core` version;
calls `super()` for MRO safety
- `_merge_metadata_dicts` — one-level-deep (non-recursive) merge for
nested dict metadata keys
- `CallbackManager.add_metadata` — uses `_merge_metadata_dicts` instead
of flat `dict.update()` so nested metadata dicts (like `versions`)
coexist rather than clobber
- `merge_configs` — uses `_merge_metadata_dicts` for config merging
**Partners:**
- Each now calls `self._add_version("langchain-<pkg>", __version__)`
### Design decisions
- **Constructor-based, not `_get_ls_params`-based** — versions flow
through `self.metadata` (local metadata on traces), not through
`LangSmithParams`. This matches JS and makes child-class version
inheritance automatic (no merge/clobber issues).
- **`versions` is local (non-inheritable) metadata** — `self.metadata`
is passed to `CallbackManager.configure` as `local_metadata`
(`add_metadata(..., inherit=False)`), so `versions` is attached **once
per chat-model run** and is **not** propagated to child runs or
duplicated onto every streaming chunk. This is intentionally the
opposite of the inheritable-per-chunk metadata that #36588 was reducing
for performance — `versions` does not regress that path.
- **`add_metadata` deep-merge is a correctness fix, not just for
versions** — previously `add_metadata`/`merge_configs` did a flat
top-level `dict.update`/spread, so any nested metadata dict baked into a
config (e.g. via `.with_config({"metadata": {...}})`) would be wholly
replaced when a caller also passed `metadata`. `_merge_metadata_dicts`
merges one level deep so user-provided `config.metadata.versions` and
model-set `versions` coexist instead of clobbering. The merge runs once
per `configure` (not per chunk), so it is off the streaming hot path.
- **One level deep only** — `_merge_metadata_dicts` is deliberately
*not* a recursive deep merge; values nested more than one level are
last-writer-wins. This covers the `versions` case without the
ambiguity/cost of arbitrary-depth merging.
- **Warn on non-dict `metadata["versions"]`** — if a user sets
`metadata={"versions": "some-string"}`, `_add_version` emits a warning
and replaces the value with the version dict rather than silently
discarding user data or crashing. This is a soft breaking change for
anyone who previously stored non-dict values at this key.
### Follow-ups (tracked separately, out of scope here)
- JS `mergeConfigs` still flat-spreads nested metadata, so
`metadata.versions` can still clobber on the JS side until an equivalent
deep-merge lands.
---
Made by [Open SWE](https://openswe.vercel.app)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
OpenAI Chat Completions streaming has a v1 normalization gap when tool
calls are streamed.
When users opt into `output_version="v1"`, `.content_blocks` is expected
to be the normalized cross-provider view of the message. For OpenAI Chat
Completions streams, though, chunks still carry raw string `content`
plus side-channel `tool_call_chunks` / `tool_calls`.
Practically, an OpenAI stream chunk can look like this internally:
```python
AIMessageChunk(
content="",
tool_call_chunks=[
{
"name": "get_weather",
"args": '{"location": "SF"}',
"id": "call_123",
"index": 0,
"type": "tool_call_chunk",
}
],
response_metadata={"model_provider": "openai", "output_version": "v1"},
)
```
That is not already-normalized v1 content like this:
```python
AIMessageChunk(
content=[
{
"type": "tool_call_chunk",
"name": "get_weather",
"args": '{"location": "SF"}',
"id": "call_123",
"index": 0,
}
],
)
```
Because `.content_blocks` currently short-circuits solely on
`output_version="v1"`, it can return the raw string/empty list directly
instead of running the OpenAI translator that incorporates
`tool_call_chunks` / `tool_calls` into normalized v1 blocks.
In practice, a streamed OpenAI tool call can be parsed successfully into
`tool_calls`, but still be missing from the final aggregated
`.content_blocks`. Downstream code that consumes the v1 block interface
then sees no `tool_call` block and must know to inspect OpenAI-specific
chunk fields instead.
User story:
> As a LangChain user streaming OpenAI Chat Completions with bound tools
and `output_version="v1"`, I need the final aggregated message's
`.content_blocks` to include normalized `tool_call` blocks, so that code
written against the v1 content-block interface handles streamed tool
calls consistently across providers.
Expected final aggregated view:
```python
message.content_blocks == [
{
"type": "tool_call",
"name": "get_weather",
"args": {"location": "SF"},
"id": "call_123",
}
]
```
Root causes:
1. The usage-only Chat Completions chunk uses `content=[]` in v1 mode
while normal streaming chunks use `content=""`, creating inconsistent
content types during chunk aggregation.
2. `AIMessage.content_blocks` and `AIMessageChunk.content_blocks` treat
any `output_version="v1"` message as already-normalized, even when
`content` is still raw string content from Chat Completions.
3. Content-bearing OpenAI stream chunks do not carry
`output_version="v1"`, so the final merged chunk may not reliably take
the v1 normalization path.
Changes:
- Keep usage-only Chat Completions chunks as `content=""` instead of
overriding to `[]`, so streaming chunks merge consistently.
- Propagate `output_version="v1"` to content-bearing chunks.
- Only short-circuit v1 `.content_blocks` when `content` is already a
list of blocks; otherwise fall through to the provider translator.
- Add regression tests covering string-content v1 fallback, usage-only
chunk content consistency, and streamed tool calls appearing as
normalized final v1 blocks.
Provider-native structured output fallback detection now uses bounded
model-name patterns instead of broad substring checks, reducing false
positives for unrelated model IDs. The model examples and test fixtures
across OpenAI/OpenRouter-facing code were refreshed around current
OpenAI model families while preserving shipped defaults.
## Changes
- Tightened `FALLBACK_MODELS_WITH_STRUCTURED_OUTPUT` from loose string
fragments to regex patterns, with `_supports_provider_strategy` matching
full model-name segments instead of arbitrary substrings.
- Expanded structured-output fallback coverage for newer OpenAI,
Anthropic, and xAI/Grok model families, including `gpt-5.x`, newer
Claude 4/5-style names, and `grok-build`.
- Reused `_attempt_infer_model_provider` in provider tool search routing
so `_provider_from_model_name` follows the same provider inference
behavior as `init_chat_model`.
- Suppressed irrelevant provider-inference deprecation warnings during
provider tool search registry lookup.
- Refreshed OpenAI, Azure OpenAI, OpenRouter, core metadata, and example
model references from older fixtures like `gpt-4`, `gpt-4o`, `o1`, and
`o4-mini` to current test/profile models such as `gpt-5.5`,
`gpt-5-nano`, and `gpt-4.1-mini`.
- Removed outdated OpenAI test assumptions around legacy `o1` behavior
and narrowed legacy structured-output checks to explicitly legacy model
names.
Same shape as the merged anthropic patch in #37064, ported to
`libs/partners/openai`.
`_SyncHttpxClientWrapper.__del__` / `_AsyncHttpxClientWrapper.__del__`
check `self.is_closed`, which reads `self._state`. When a wrapper is
created without `__init__` running to completion — `copy.deepcopy` via
`__new__` + `__setstate__`, or a constructor that raised partway through
— `_state` is missing and the finalizer prints
```
Exception ignored in: <function _SyncHttpxClientWrapper.__del__ at 0x...>
Traceback (most recent call last):
File ".../langchain_openai/chat_models/_client_utils.py", line 366, in __del__
if self.is_closed:
File ".../httpx/_client.py", line 228, in is_closed
return self._state == ClientState.CLOSED
AttributeError: '_SyncHttpxClientWrapper' object has no attribute '_state'
```
at GC time. Same noise pattern that #37064 fixed for the anthropic
partner.
Hoist the `is_closed` access inside the existing `try/except` so the
`AttributeError` is swallowed alongside the `close()` / `aclose()`
exceptions that block already handles.
Tests: two new unit tests build the wrappers via `__new__` (no
`__init__` → no `_state`) and call `__del__` directly, mirroring the
tests added in #37064.
Verified:
- `cd libs/partners/openai && make format` -> all checks passed
- `cd libs/partners/openai && make test
TEST_FILE=tests/unit_tests/chat_models/test_client_utils.py` -> 37
passed, 1 skipped (linux-only)
- `cd libs/partners/openai && make lint` -> all checks passed, mypy
clean
> [!IMPORTANT]
> **Behavior change on upgrade — minor bump (`1.1.16` → `1.2.0`).**
>
> Streaming calls now raise `StreamChunkTimeoutError` (a `TimeoutError`
subclass — existing `except TimeoutError:` / `except
asyncio.TimeoutError:` handlers catch it) after 120s of content silence
instead of hanging forever. Opt out with `stream_chunk_timeout=None` or
`LANGCHAIN_OPENAI_STREAM_CHUNK_TIMEOUT_S=0`.
>
> Kernel-level TCP keepalive / `TCP_USER_TIMEOUT` are applied via a
custom `httpx` transport. `httpx` disables its env-proxy auto-detection
(`HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY` / `NO_PROXY` and
macOS/Windows system proxy) whenever a transport is supplied, so to
avoid silently breaking enterprise proxy users, `ChatOpenAI` now detects
the "proxy-env-shadow" shape at construction and **skips the custom
transport entirely** when **all** of these hold:
>
> - `http_socket_options` left at default (`None`)
> - No `http_client` or `http_async_client` supplied
> - No `openai_proxy` supplied
> - A proxy env var / system proxy is visible to httpx
>
> On that shape the instance falls back to pre-PR behavior and env-proxy
auto-detection still applies. A one-time `INFO` records the bypass.
>
> Users who explicitly set `http_socket_options=[...]` alongside an env
proxy still get the shadowed behavior with a one-time `WARNING` log —
they opted in. Full opt-outs below.
---
Streaming chat completions can hang forever when the underlying TCP
connection silently dies mid-stream (idle NAT/LB timeouts, sandboxed
runtimes killing long-lived connections, peer gone without a FIN or
RST). httpx's read timeout doesn't help here because it's reset by any
bytes arriving on the socket, including OpenAI's SSE keepalive comments,
so a stream that's quiet on content but still producing keepalives looks
alive forever.
This PR adds two knobs to `ChatOpenAI`, both on by default with
opt-outs:
- `stream_chunk_timeout` (default 120s): wraps the async streaming
iterator in `asyncio.wait_for` per chunk. Measures the gap between
*parsed* SSE chunks, so keepalives don't reset it. Fires on genuine
content silence and raises `StreamChunkTimeoutError` — a `TimeoutError`
subclass carrying `timeout_s`, `model_name`, and `chunks_received` as
structured attributes (mirrored in the WARNING log's `extra=`) for
alerting without message-regex. Override with the kwarg or
`LANGCHAIN_OPENAI_STREAM_CHUNK_TIMEOUT_S`.
- `http_socket_options`: applies `SO_KEEPALIVE` + `TCP_KEEPIDLE` /
`TCP_KEEPINTVL` / `TCP_KEEPCNT` + `TCP_USER_TIMEOUT` on Linux (macOS
equivalents where available). On platforms missing some options, they're
dropped silently and the remaining set still does useful work.
Pool limits are set explicitly on the custom transport to mirror the
`openai` SDK — without that, passing `transport=` to `httpx.AsyncClient`
silently shrinks the connection pool.
## Behavior change
The default-shape proxy-env bypass (above) covers the common enterprise
case. Beyond that:
- Connections that would previously have hung forever will now error out
via `StreamChunkTimeoutError`.
- Users who explicitly opt into `http_socket_options` while also relying
on env proxies will see a one-time `WARNING` and lose env-proxy
auto-detection — the custom transport shadows it. This is the original
shipped behavior, retained for anyone who *wants* socket tuning on top
of an env-proxied setup.
Full opt-outs:
- `stream_chunk_timeout=None` or
`LANGCHAIN_OPENAI_STREAM_CHUNK_TIMEOUT_S=0`
- `http_socket_options=()` or `LANGCHAIN_OPENAI_TCP_KEEPALIVE=0`
- Supply your own `http_client` **and** `http_async_client`.
`http_socket_options` is applied per side: passing only one still leaves
the other side's default builder getting socket options. Supply both (or
combine with `http_socket_options=()`) to take full control.
Unparseable or negative values for the `LANGCHAIN_OPENAI_*` env vars
fall back to the default with a `WARNING` log rather than silently being
accepted, so a misconfigured environment still boots but the fallback is
discoverable.
---------
Co-authored-by: Mason Daugherty <github@mdrxy.com>
Co-authored-by: Mason Daugherty <mason@langchain.dev>
Python's `or` operator treats `0` as falsy, so
`token_usage.get("total_tokens") or fallback` silently replaces a
provider-reported `total_tokens=0` with the computed sum of input +
output tokens. Providers can legitimately report zero tokens (e.g.,
cached responses, empty completions).
The same pattern exists in the dual-key lookups for
`input_tokens`/`output_tokens` in Groq and OpenRouter. While current
APIs don't return both key formats simultaneously (making the `or`-chain
functionally correct today), the semantics are still wrong; `0` should
not fall through to a fallback.
## Changes
- Replace `x.get(key) or fallback` with explicit `is not None` checks in
`_create_usage_metadata` across `langchain-openai`, `langchain-groq`,
and `langchain-openrouter` for `input_tokens`, `output_tokens`, and
`total_tokens`
- Fix a concrete bug in the `total_tokens` path: a provider-reported `0`
was silently replaced by the computed sum
- Harden dual-key lookups in Groq and OpenRouter to correctly preserve
zero values from the preferred key, should both key formats ever coexist
- Update OpenAI's single-key extraction for consistency — the old `or 0`
pattern happened to produce correct results (`0 or 0 == 0`) but was
semantically wrong
Fixed a bug where GPT-5 temperature validation was case-sensitive,
causing issues when users
specified Azure deployment names or model names in uppercase (e.g.,
`"GPT-5-2025-01-01"`, `"GPT-5-NANO"`). The validation now correctly
handles model names regardless of case.
Changes made:
- Updated `validate_temperature()` method in `BaseChatOpenAI` to perform
case-insensitive
model name comparisons
- Updated `_get_encoding_model()` method to use case-insensitive checks
for tiktoken encoder
selection
- Added comprehensive unit tests to verify case-insensitive behavior
with various case
combinations
**Issue:** Fixes#34003
**Dependencies:** None
**Test Coverage:**
- All existing tests pass
- New test `test_gpt_5_temperature_case_insensitive` covers uppercase,
lowercase, and
mixed-case model names
- Tests verify both non-chat GPT-5 models (temperature removed) and chat
models (temperature
preserved)
- Lint and format checks pass (`make lint`, `make format`)
---------
Co-authored-by: Mason Daugherty <github@mdrxy.com>
Now returns (`_iter`, `tokens`, `indices`, token_counts`). The
`token_counts` are calculated directly during tokenization, which is
more accurate and efficient than splitting strings later.
## Description
Fixes#31227 - Resolves the issue where `OpenAIEmbeddings` exceeds
OpenAI's 300,000 token per request limit, causing 400 BadRequest errors.
## Problem
When embedding large document sets, LangChain would send batches
containing more than 300,000 tokens in a single API request, causing
this error:
```
openai.BadRequestError: Error code: 400 - {'error': {'message': 'Requested 673477 tokens, max 300000 tokens per request'}}
```
The issue occurred because:
- The code chunks texts by `embedding_ctx_length` (8191 tokens per
chunk)
- Then batches chunks by `chunk_size` (default 1000 chunks per request)
- **But didn't check**: Total tokens per batch against OpenAI's 300k
limit
- Result: `1000 chunks × 8191 tokens = 8,191,000 tokens` → Exceeds
limit!
## Solution
This PR implements dynamic batching that respects the 300k token limit:
1. **Added constant**: `MAX_TOKENS_PER_REQUEST = 300000`
2. **Track token counts**: Calculate actual tokens for each chunk
3. **Dynamic batching**: Instead of fixed `chunk_size` batches,
accumulate chunks until approaching the 300k limit
4. **Applied to both sync and async**: Fixed both
`_get_len_safe_embeddings` and `_aget_len_safe_embeddings`
## Changes
- Modified `langchain_openai/embeddings/base.py`:
- Added `MAX_TOKENS_PER_REQUEST` constant
- Replaced fixed-size batching with token-aware dynamic batching
- Applied to both sync (line ~478) and async (line ~527) methods
- Added test in `tests/unit_tests/embeddings/test_base.py`:
- `test_embeddings_respects_token_limit()` - Verifies large document
sets are properly batched
## Testing
All existing tests pass (280 passed, 4 xfailed, 1 xpassed).
New test verifies:
- Large document sets (500 texts × 1000 tokens = 500k tokens) are split
into multiple API calls
- Each API call respects the 300k token limit
## Usage
After this fix, users can embed large document sets without errors:
```python
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain_text_splitters import CharacterTextSplitter
# This will now work without exceeding token limits
embeddings = OpenAIEmbeddings()
documents = CharacterTextSplitter().split_documents(large_documents)
Chroma.from_documents(documents, embeddings)
```
Resolves#31227
---------
Co-authored-by: Kaparthy Reddy <kaparthyreddy@Kaparthys-MacBook-Air.local>
Co-authored-by: Chester Curme <chester.curme@gmail.com>
Co-authored-by: Mason Daugherty <mason@langchain.dev>
Co-authored-by: Mason Daugherty <github@mdrxy.com>