What does langchain-model-profiles 0.0.6 actually do?
langchain-model-profiles is a CLI plus data pipeline that generates model-capability metadata for LangChain integration packages. It is not a request router, load balancer, or fallback orchestrator. Its PyPI summary describes it as a "CLI tool for updating model profile data in LangChain integration packages" . The "routing" association is a common misread: the package only produces the structured data that downstream code uses to decide what a model can do.
Version 0.0.6 was released on June 11, 2026, and is the current latest release . The distribution is a 7.0 kB py3-none-any wheel plus a 149.9 kB source distribution, uploaded via PyPI Trusted Publishing . It is marked Beta, MIT licensed, maintained by langchain, and requires Python >=3.10,<4.0 . The README warns the package is in development and its API is subject to change .
The primary consumers are LangChain partner and integration-package maintainers who need refreshed capability data without hand-editing generated modules . They run the CLI; application and agent builders feel the result indirectly.
Where builders actually touch this data is the .profile attribute on LangChain chat models, introduced as a beta feature in langchain>=1.1, which exposes a model's supported features and capabilities through a dictionary . langchain-model-profiles is the maintenance-side tooling that generates the data behind that attribute. So the mental model is: this package writes the metadata; your code reads model.profile to query it. Public sources do not enumerate a dedicated 0.0.6 changelog, so it is best treated as an incremental maintenance release on top of 0.0.5 .
The upstream dependency: models.dev

Every profile that langchain-model-profiles writes traces back to one source: models.dev, an open-source database of AI model specifications, pricing, and feature flags . The data is stored as TOML files organized by provider and canonical model name, then published through three JSON endpoints: /api.json, /models.json, and /catalog.json . The package consumes exactly one of them: its CLI calls httpx.get("https://models.dev/api.json", timeout=30), expects a top-level provider dictionary, and reads all_data[provider]['models'] from the response .
That single dependency is also the system's ceiling. The accuracy of every downstream LangChain .profile is bounded by two things: how fresh models.dev is, and what each integration package's profile_augmentations.toml patches on top . If models.dev has not yet recorded a model's context window or tool-calling support, the generated profile inherits that gap until someone updates the upstream record or overrides it locally.
The augmentation layer exists precisely for that lag. models.dev is community-maintained and can trail a provider's release by days, or miss provider-specific nuances that do not fit its schema. Rather than block on an upstream pull request, integration maintainers edit a local profile_augmentations.toml, parsed from an [overrides] table where dictionary values are treated as per-model overrides, non-dictionary values as provider-wide defaults, and None values are skipped entirely . The CLI also includes augmentation-only models (entries that exist solely in the local TOML and have no models.dev counterpart), so a brand-new model can ship a usable profile before the upstream database catches up .
In practice this gives maintainers two correction paths, as LangChain's own docs frame it:
"Builders can override profile data locally (pass a custom profile at init or use model_copy() for a quick fix) or contribute an upstream fix via the CLI and augmentation files," per the LangChain models documentation.In short, treat models.dev as the default and profile_augmentations.toml as the escape hatch. When a profile looks wrong, check whether the upstream record is stale before assuming the package is broken; the fix usually belongs in the augmentation file, and the CLI is what stitches the two layers into the generated module the rest of LangChain reads .
The refresh lifecycle
The entire package exposes a single console script: langchain-profiles refresh --provider <provider> --data-dir <data_dir>. The 0.0.6 pyproject.toml registers exactly one entry point, langchain-profiles = langchain_model_profiles.cli:main , so there is no daemon, no server, and no second command to learn. You run refresh against one provider at a time and point it at the integration package's data directory, for example ./langchain_anthropic/, and the CLI does the rest.
Under that one command, the CLI walks five sequential steps :
- Validate the data directory. The target path is checked before anything is fetched or written, so a bad
--data-dirfails fast rather than mid-write. - Fetch models.dev. The CLI calls
httpx.get("https://models.dev/api.json", timeout=30), expects a top-level provider dictionary, and extractsall_data[provider]['models']. - Load
profile_augmentations.toml. The local augmentation file is read so its overrides can be layered onto the upstream records. - Convert each record to a LangChain profile dict. Every models.dev model is mapped into the profile shape, provider-level and model-level overrides are applied, augmentation-only models are included, and, when
langchain-coreis importable, keys are checked againstlangchain_core.language_models.model_profile.ModelProfile. - Write the generated module. The assembled dictionary is serialized to a Python file on disk for the rest of LangChain to import .
The write step is deliberately defensive. Output first lands in a temporary file and is then moved into place with Path.replace(), giving an atomic swap so a crashed run never leaves a half-written module on disk . The writer also refuses to operate through symlinked data directories or symlinked output files, and creates the target directory with mode 0755 . For a code-generation tool that maintainers commit straight into integration packages, those guardrails matter: they keep a generated artifact from being silently redirected or corrupted.
What stands out about the runtime footprint is how little it pulls in. The 0.0.6 distribution declares only three dependencies: httpx>=0.23,<1, typing-extensions>=4.7,<5, and tomli>=2,<3, with the last installed only on Python earlier than 3.11 . Notably, LangChain itself is not required to generate profiles; the optional langchain-core key check is a validation nicety, not a hard requirement. That keeps the tool installable in a minimal maintainer environment and explains why it ships as a 7.0 kB wheel . Taken together: one command, five predictable steps, an atomic write, and almost no surface area to go wrong.
What the generated dictionary exposes

The generated profile is a flat dictionary of capability fields, and it falls into three families: token limits, modality booleans, and capability flags. Each LangChain chat model surfaces this dictionary through model.profile, so reading it tells you a model's context window, what input and output types it accepts, and which features (tool calling, structured output, reasoning) it reports as supported . The data is produced by mapping each models.dev record into these keys during a refresh .
Token limits come straight from the source record: models.dev limit.context becomes max_input_tokens and limit.output becomes max_output_tokens . Modality arrays are flattened into booleans: the modalities.input and modalities.output lists are expanded into discrete flags so a single membership test answers "does this model accept PDFs?" without parsing an array.
| Profile group | Generated keys | Source in models.dev |
|---|---|---|
| Token limits | max_input_tokens, max_output_tokens | limit.context, limit.output |
| Input modalities | text_inputs, image_inputs, audio_inputs, pdf_inputs, video_inputs | modalities.input array |
| Output modalities | text_outputs, image_outputs, audio_outputs, video_outputs | modalities.output array |
| Capability flags | reasoning, tool_calling, tool_choice, structured_output, attachment, temperature, image_url_inputs, image_tool_message, pdf_tool_message | capability fields (e.g. tool_call → tool_calling) |
The capability flags are where the naming is worth watching. Most map cleanly, but a few are renamed in transit, notably the source field tool_call which is written as tool_calling in the profile, alongside tool_choice, structured_output, attachment, temperature, image_url_inputs, image_tool_message, and pdf_tool_message . The reasoning flag is reported here, while the consumer-facing docs also describe a related reasoning_output field on the profile .
The structural detail that matters most for anyone reading these dictionaries: ModelProfile is a total=False TypedDict. That means every field is optional and any key may simply be absent for a given model, so callers are expected to use .get() rather than direct indexing . The type also tolerates extra keys it does not declare, and helper logic emits a warning on unknown profile keys rather than raising . In practice, treat the profile as a best-effort capability hint, not a guaranteed schema:
profile = model.profile
ctx = profile.get("max_input_tokens") # may be None
can_tools = profile.get("tool_calling", False) # default if absent
accepts_pdf = profile.get("pdf_inputs", False)This optionality is deliberate. Because the format is still beta and the data is only as complete as models.dev plus each package's augmentations, defaulting on missing keys keeps consuming code stable when a model reports a partial profile. The flat boolean design also makes feature gating cheap: a single dictionary lookup decides whether to attempt structured output or reject an oversized prompt before a request leaves the process.
The _profiles.py naming discrepancy
The single most likely thing to trip up a maintainer running langchain-profiles refresh is the generated filename. The README and prose docs say the command writes profiles.py, but the 0.0.6 source code actually writes _profiles.py, an underscore-prefixed private module . This is a documentation/code mismatch, not a runtime bug: the refresh succeeds and produces a valid module, just under a name the docs don't mention.
As of the June 11, 2026 release of 0.0.6, no fix shipped for this inconsistency . The package itself is candid about its instability; its README warns that the project is in development and the API is subject to change . A naming drift between prose and code is exactly the kind of rough edge that survives in a beta utility where no dedicated 0.0.6 changelog documents what moved between releases.
The practical consequence is narrow but real. An integration maintainer who follows the README literally will look for profiles.py in their data directory after running refresh, find nothing under that name, and may assume the command failed. The generated _profiles.py is sitting right beside their search . The fix is to check for the underscore-prefixed file.
There is good evidence the code is the correct half of the mismatch and the prose is wrong. The langchain-anthropic integration imports the generated module's contents as _PROFILES, casts it to a ModelProfileRegistry, and returns copies from _get_default_model_profile; the underscore convention is deliberately applied in at least one shipping downstream integration . A generated, machine-managed module is precisely the thing Python convention marks private with a leading underscore, so the _profiles.py output is consistent with how the broader codebase treats it. Until the docs are corrected, treat the README's profiles.py as the stale reference and the underscore-prefixed file as ground truth.
Consuming .profile in application logic

Consuming .profile means reading a model's reported capabilities at runtime and branching on them, instead of baking per-model assumptions into your application. Every LangChain chat model exposes model.profile as a dictionary, and because the underlying ModelProfile type is a total=False TypedDict, callers are expected to use .get() so a missing field returns None rather than raising . That single defensive convention is what makes the four patterns below safe to ship.
Context summarization. LangChain's summarization middleware reads profile.get('max_input_tokens') to decide when a conversation is approaching the context window and summarization should fire . The win is that no per-model token constant lives in your application code. Swap Claude for a model with a larger or smaller window and the trigger point moves automatically, because the threshold comes from the profile rather than a hardcoded literal.
Structured-output inference. Rather than forcing JSON mode on every model, application code can check profile.get('structured_output') first and pick a strategy accordingly . Models that report native structured-output support get the native path; models that do not fall back to prompt-based extraction. This avoids sending a JSON-mode request to a backend that silently ignores it or errors, and it keeps the decision data-driven instead of maintained in a hand-edited allow-list.
Input gating. The profile's modality booleans (image_inputs, audio_inputs, pdf_inputs, and the rest) plus max_input_tokens let you validate a request at the application boundary before any network call . Rejecting an unsupported image attachment or an over-length prompt locally is cheaper and clearer than catching a provider 400 downstream, and it gives you a precise error message tied to the capability that was missing.
Dynamic switching. The most routing-adjacent use combines profiles with agent middleware. LangChain's @wrap_model_call hook can replace the model at runtime based on state or context, and that selection logic can read profile data to choose between candidates . The concrete shipping example is the Deep Agents model switcher, which filters its interactive picker to models reporting tool_calling=True with text input and output, selecting candidates based on capability rather than a static menu .
Worth keeping straight: langchain-model-profiles only generates the capability metadata. The routing itself lives in your middleware. When a profile value is wrong or missing for your case, you can override it locally (pass a custom profile at initialization or use model_copy() for a quick fix) or contribute the correction upstream through the CLI and augmentation files . Either way, the application reads one dictionary and the per-model knowledge stays out of your business logic.
How freshness and augmentation interact
Freshness in langchain-model-profiles comes from two layers that move on different clocks: the upstream models.dev data the CLI fetches, and the local profile_augmentations.toml a maintainer keeps in each integration package. Version 0.0.6 itself was an incremental maintenance bump. It shipped on June 11, 2026, the first release in roughly seven months after 0.0.5 landed on November 21, 2025 . No dedicated 0.0.6 changelog exists beyond that version-and-date metadata, so treat it as a routine update to the tooling rather than a behavioral change .
The augmentation layer is where maintainers correct or extend what models.dev reports. Overrides live in an [overrides] table inside profile_augmentations.toml, and the parsing rules are specific: dictionary values are applied as per-model overrides, non-dictionary values are applied provider-wide, and any override whose value is None is skipped entirely . The CLI also includes augmentation-only models that have no models.dev record, so the local file can both patch and add.
If you are not a package maintainer, you do not need the CLI to fix a profile. You can pass a custom profile dictionary at model initialization, or call model_copy() for a one-off patch in application code, a quick local correction that never touches the generated module . The CLI-and-augmentation path is for pushing the same fix upstream so every consumer benefits.
One caution shapes all of this: both the package and the ModelProfile format are explicitly beta, with an API the maintainers say is subject to change . For integration packages that ship generated profile data, pin to a specific version rather than tracking the latest, and re-run langchain-profiles refresh deliberately when you want fresh models.dev data. Capability data is generated, not authoritative. Keep your override file under version control, pin the tool, and remember the output module is _profiles.py, not profiles.py, when you go looking for what changed.
Frequently asked questions
What does langchain-model-profiles 0.0.6 actually do?
It generates the capability data behind LangChain's .profile attribute. The CLI fetches AI model specifications from models.dev, converts each record into a LangChain profile dictionary, applies local augmentations, and writes a generated Python module. It is not a runtime request router, load balancer, or fallback orchestrator. Despite the "routing" framing, version 0.0.6 (released June 11, 2026 ) is a maintenance CLI run by integration-package maintainers to keep capability metadata current without hand-editing generated files. See PyPI for the release metadata.
What is the _profiles.py vs profiles.py discrepancy?
The README and docs say the refresh command writes profiles.py, but the 0.0.6 source actually writes _profiles.py (underscore-prefixed). This is an unresolved documentation/code mismatch as of the June 11, 2026 release . When you import or look for the generated module, check for the underscore form. The langchain-anthropic integration, for example, imports a generated _PROFILES registry from this mechanism (source).
Which LangChain version is required to use .profile?
You need langchain>=1.1, the release where chat models first exposed supported features through the .profile attribute as a beta feature . Note that langchain-model-profiles itself does not depend on LangChain at generation time; it only requires Python >=3.10,<4.0 and depends on httpx, typing-extensions, and tomli . It checks against langchain-core only when that package is importable.
How do I override profile data for a specific model?
There are two paths. For a quick, local fix, pass a custom profile dictionary at model initialization or use model_copy() to produce a one-off override (LangChain models docs). For a durable, upstream fix, add entries to the package's profile_augmentations.toml and re-run langchain-profiles refresh to regenerate the module. Augmentations use an [overrides] table where dictionary values are per-model overrides and non-dictionary values apply provider-wide .
What happens if models.dev data is stale?
Profile data inherits the staleness directly. Because models.dev is the upstream source, incorrect or outdated context-window sizes, modality flags, and capability fields flow downstream into the generated module . The profile_augmentations.toml override mechanism exists partly for this reason: maintainers can patch incorrect or missing values without waiting for a models.dev update, then regenerate. Correctness is only ever as current as models.dev plus each package's augmentation file, so keep that file under version control.