models_io (legacy alias) — Re-export façade for prompts/llms/chat_models/parsers
Re-export façade for prompts/llms/chat_models/parsers
Module langchain_classic/models_io/ — Re-export façade for prompts/llms/chat_models/parsers. Part of the Models I/O group.
🎯 Why This Module Exists
This module is documented from the models_io_deep.md Phase 1B research artifact. See sections below for design decisions, algorithms, error philosophy, performance, and evolution clues.
Façade Backwards-compat
Architecture
flowchart LR
subgraph Core["langchain_classic/models_io/"]
BASE["Base abstraction"]
IMPLS["Concrete implementations"]
BASE --> IMPLS
end
subgraph Up["Upstream callers"]
U1["chains/"]
U2["agents/"]
end
subgraph Down["Downstream"]
D1["langchain_core"]
D2["External providers"]
end
Up --> Core
Core --> Down
Data Flow
sequenceDiagram participant Caller as Caller participant Models_I as models_io (legacy alias) participant Core as langchain_core participant Ext as External Caller->>Models_I: invoke(input) Note over Models_I: Validate & route Models_I->>Core: delegate primitive Core->>Ext: provider call Ext-->>Core: response Core-->>Models_I: typed output Models_I-->>Caller: result
Existence rationale
These four modules are the provider façade between user code and the actual LLM/embedding service. After the package split, the real implementations migrated to langchain_community.{llms,chat_models,embeddings} and provider packages (langchain_openai, langchain_anthropic, …). What remains in langchain_classic is:
- A **lazy
__getattr__trampoline** that forwardslangchain_classic.llms.OpenAItolangchain_community.llms.OpenAI. - A deprecation warning emitted on first access.
- A handful of classic, non-trivial wrappers that pre-date the split:
CacheBackedEmbeddings,init_chat_model(provider-detection),prompts/example_selector/re-exports.
The point of this design is migration: legacy code keeps working (with warnings) while users are nudged toward the canonical import path.
Design decisions
| Decision | Choice | Alternative | Rationale |
|---|---|---|---|
Lazy __getattr__ per module | First-access import + warn | Eager imports | Avoids loading 70+ provider deps on import langchain |
| Deprecation messages with replacement path | "...moved to langchain_openai" | Generic warning | Actionable for the user |
CacheBackedEmbeddings lives in langchain_classic | Unique algorithm | Move to community | It's provider-agnostic and used by every retriever pipeline; staying in classic gives it a stable home |
prompts/ re-exports langchain_core.prompts.* | Pure shim | Implement here | Templates are core abstractions; classic should not duplicate |
init_chat_model provider routing | String like "openai:gpt-4o-mini" parsed | One factory per provider | Lets configuration files specify model without import-time provider choice |
Algorithm deep-dive — CacheBackedEmbeddings
Problem. Embedding the same documents twice (during a re-index, during dev iteration, during testing) wastes API calls. Cache the result.
Trace. embed_documents(texts):
keys = [hash_func(t) for t in texts](default SHA256).cached = byte_store.mget(keys)— bytes-typed values for hits, None for misses.- Decode hits via
decoderto get back float vectors. missing_idx = [i for i, c in enumerate(cached) if c is None].new_vectors = underlying_embedder.embed_documents([texts[i] for i in missing_idx]).byte_store.mset([(keys[i], encoder(new_vectors[j])) for j, i in enumerate(missing_idx)]).- Splice cached + new in original order; return.
embed_query does not cache by default — queries are usually unique strings (typos, paraphrases) so cache hit rate is low and the cost of cache pollution outweighs the win.
**Why a ByteStore rather than a typed Mapping[str, list[float]].** Lets the same cache back arbitrary content (embeddings now, raw bytes later) and slots into any of LocalFileStore, RedisStore, InMemoryByteStore. The encoder/decoder are pluggable.
Error philosophy
Deprecated trampolines emit a warning (suppressed in interactive REPLs to avoid spam) and proceed. Real errors come from the underlying community/provider package and propagate unchanged.
Cross-module dependencies
langchain_classic.{llms, chat_models, embeddings} ──`__getattr__`──► langchain_community.{llms, chat_models, embeddings}
│
▼
provider packages (httpx, openai, …)
langchain_classic.prompts ──re-exports──► langchain_core.prompts (PromptTemplate, ChatPromptTemplate, MessagesPlaceholder, FewShotPromptTemplate, …)
CacheBackedEmbeddings ──► storage.ByteStore ──► Local / In-memory / Redis backends
Evolution clues
chat_models.base.init_chat_modelis a relative newcomer — string-based provider routing reflects the "config as code" pattern.- The
embeddings/cache.py(CacheBackedEmbeddings) migrated tolangchain_classicfromlangchain_communitybecause it depends only on core + storage and is widely reused. - A few internal helper symbols in
prompts/example_selector/still live here (LLM-driven semantic similarity selectors) rather than in core, because they carry an LLM dependency.
Step-by-Step Walkthrough
Follow a typical operation through the models_io/ module:
-
Caller invokes the public surface — usually
.invoke(input)on a Runnable, or a legacy__call__(inputs_dict)on aChainsubclass. - Pydantic validation — every entry point is a BaseModel; constructor args, input dicts, and tool args_schema are validated before any expensive work runs. Fail-fast on type errors.
-
CallbackManager.configure — a fresh manager is built per call, fans
on_*_startevents to all subscribers (LangSmith tracer, stdout handler, custom logging), and stamps a UUID4run_idfor tree reconstruction. - models_io (legacy alias)-specific logic — the module's core method runs: prompt rendering, retrieval, tool execution, memory load, document split, etc. See the deep-dive sections above.
- Provider call — typically a single HTTP request to OpenAI/Anthropic/etc., or a local SDK call. Retries are handled by tenacity with exponential backoff and a configurable max-attempts knob.
-
Streaming fan-out — if the provider streams tokens, each chunk fires
on_llm_new_tokenthrough the callback manager so downstream observers (UI, log, tracer) see partial output. -
OutputParser pass — if a parser is attached, the raw provider output is parsed into a structured Python object;
OutputParserExceptionmay trigger a retry chain (RetryWithErrorOutputParser). -
Memory write-back — for chains/agents that have a
BaseMemoryattached,save_context(inputs, outputs)persists the turn before returning to the caller. -
on_*_end fan-out — the callback manager closes the run span; tracers serialise the
Runtree and ship it to LangSmith asynchronously. - Result returned — caller receives a typed object (AIMessage, dict, BaseModel, list[Document], ...) matching the module's declared output schema.
What Could Go Wrong
| Failure Mode | Trigger | Detection | Recovery |
|---|---|---|---|
| Provider 5xx / rate-limit | OpenAI/Anthropic/etc. transient error | HTTP status from SDK | tenacity retries with exponential backoff up to max_retries; then surface to caller |
| Output parser failure | LLM returned malformed JSON / wrong schema |
OutputParserException in parser |
RetryWithErrorOutputParser re-prompts with the parser error included; or surface |
| Pydantic validation | Caller passes wrong-shaped dict |
ValidationError at __init__ / invoke
|
Surface immediately — no fallback is correct here |
| Callback handler exception | User-supplied tracer raises | Try/except around each fan-out | Logged and swallowed by default; raise_error=True to opt in to propagation |
| Provider auth missing |
OPENAI_API_KEY unset |
SDK AuthenticationError
|
Surface with a helpful message; no retry (would just fail again) |
Performance Profile
Hot Paths
- Provider HTTP round-trip dominates wall-clock time (100ms–30s depending on model and prompt length).
-
Pydantic validation is repeated on every
invoke; cached schema parsing keeps it under 1ms for typical inputs. - Callback fan-out is synchronous by default — a slow handler will slow every call. Async tracers (LangSmith) push work to a background thread to avoid blocking.
Resource Usage
-
Memory: token-history buffers (in
memory/) and document caches (in retrieval pipelines) are the largest steady-state allocations. - I/O: bounded by provider rate limits; vector store I/O for retrieval-flavored modules.
-
Concurrency: async support via
a*methods; agents and parallel chains useasyncio.gatherfor concurrent tool calls.
Directory Map
Source tree for models_io (legacy alias)
models_io/ (source listing unavailable)