Skip to content

Commit 33e5d01

Browse files
authored
feat(model-profiles): distribute data across packages (#34024)
1 parent ee3373a commit 33e5d01

File tree

74 files changed

+3277
-15713
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+3277
-15713
lines changed

libs/core/langchain_core/language_models/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@
5353
ParrotFakeChatModel,
5454
)
5555
from langchain_core.language_models.llms import LLM, BaseLLM
56+
from langchain_core.language_models.model_profile import (
57+
ModelProfile,
58+
ModelProfileRegistry,
59+
)
5660

5761
__all__ = (
5862
"LLM",
@@ -68,6 +72,8 @@
6872
"LanguageModelInput",
6973
"LanguageModelLike",
7074
"LanguageModelOutput",
75+
"ModelProfile",
76+
"ModelProfileRegistry",
7177
"ParrotFakeChatModel",
7278
"SimpleChatModel",
7379
"get_tokenizer",
@@ -90,6 +96,8 @@
9096
"GenericFakeChatModel": "fake_chat_models",
9197
"ParrotFakeChatModel": "fake_chat_models",
9298
"LLM": "llms",
99+
"ModelProfile": "model_profile",
100+
"ModelProfileRegistry": "model_profile",
93101
"BaseLLM": "llms",
94102
"is_openai_data_block": "_utils",
95103
}

libs/core/langchain_core/language_models/chat_models.py

Lines changed: 16 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
from pydantic import BaseModel, ConfigDict, Field
1616
from typing_extensions import override
1717

18-
from langchain_core._api.beta_decorator import beta
1918
from langchain_core.caches import BaseCache
2019
from langchain_core.callbacks import (
2120
AsyncCallbackManager,
@@ -34,6 +33,7 @@
3433
LangSmithParams,
3534
LanguageModelInput,
3635
)
36+
from langchain_core.language_models.model_profile import ModelProfile
3737
from langchain_core.load import dumpd, dumps
3838
from langchain_core.messages import (
3939
AIMessage,
@@ -76,8 +76,6 @@
7676
if TYPE_CHECKING:
7777
import uuid
7878

79-
from langchain_model_profiles import ModelProfile # type: ignore[import-untyped]
80-
8179
from langchain_core.output_parsers.base import OutputParserLike
8280
from langchain_core.runnables import Runnable, RunnableConfig
8381
from langchain_core.tools import BaseTool
@@ -339,6 +337,21 @@ class BaseChatModel(BaseLanguageModel[AIMessage], ABC):
339337
340338
"""
341339

340+
profile: ModelProfile | None = Field(default=None, exclude=True)
341+
"""Profile detailing model capabilities.
342+
343+
!!! warning "Beta feature"
344+
This is a beta feature. The format of model profiles is subject to change.
345+
346+
If not specified, automatically loaded from the provider package on initialization
347+
if data is available.
348+
349+
Example profile data includes context window sizes, supported modalities, or support
350+
for tool calling, structured output, and other features.
351+
352+
!!! version-added "Added in `langchain-core` 1.1"
353+
"""
354+
342355
model_config = ConfigDict(
343356
arbitrary_types_allowed=True,
344357
)
@@ -1688,40 +1701,6 @@ class AnswerWithJustification(BaseModel):
16881701
return RunnableMap(raw=llm) | parser_with_fallback
16891702
return llm | output_parser
16901703

1691-
@property
1692-
@beta()
1693-
def profile(self) -> ModelProfile:
1694-
"""Return profiling information for the model.
1695-
1696-
This property relies on the `langchain-model-profiles` package to retrieve chat
1697-
model capabilities, such as context window sizes and supported features.
1698-
1699-
Raises:
1700-
ImportError: If `langchain-model-profiles` is not installed.
1701-
1702-
Returns:
1703-
A `ModelProfile` object containing profiling information for the model.
1704-
"""
1705-
try:
1706-
from langchain_model_profiles import get_model_profile # noqa: PLC0415
1707-
except ImportError as err:
1708-
informative_error_message = (
1709-
"To access model profiling information, please install the "
1710-
"`langchain-model-profiles` package: "
1711-
"`pip install langchain-model-profiles`."
1712-
)
1713-
raise ImportError(informative_error_message) from err
1714-
1715-
provider_id = self._llm_type
1716-
model_name = (
1717-
# Model name is not standardized across integrations. New integrations
1718-
# should prefer `model`.
1719-
getattr(self, "model", None)
1720-
or getattr(self, "model_name", None)
1721-
or getattr(self, "model_id", "")
1722-
)
1723-
return get_model_profile(provider_id, model_name) or {}
1724-
17251704

17261705
class SimpleChatModel(BaseChatModel):
17271706
"""Simplified implementation for a chat model to inherit from.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"""Model profile types and utilities."""
2+
3+
from typing_extensions import TypedDict
4+
5+
6+
class ModelProfile(TypedDict, total=False):
7+
"""Model profile.
8+
9+
!!! warning "Beta feature"
10+
This is a beta feature. The format of model profiles is subject to change.
11+
12+
Provides information about chat model capabilities, such as context window sizes
13+
and supported features.
14+
"""
15+
16+
# --- Input constraints ---
17+
18+
max_input_tokens: int
19+
"""Maximum context window (tokens)"""
20+
21+
image_inputs: bool
22+
"""Whether image inputs are supported."""
23+
# TODO: add more detail about formats?
24+
25+
image_url_inputs: bool
26+
"""Whether [image URL inputs](https://docs.langchain.com/oss/python/langchain/models#multimodal)
27+
are supported."""
28+
29+
pdf_inputs: bool
30+
"""Whether [PDF inputs](https://docs.langchain.com/oss/python/langchain/models#multimodal)
31+
are supported."""
32+
# TODO: add more detail about formats? e.g. bytes or base64
33+
34+
audio_inputs: bool
35+
"""Whether [audio inputs](https://docs.langchain.com/oss/python/langchain/models#multimodal)
36+
are supported."""
37+
# TODO: add more detail about formats? e.g. bytes or base64
38+
39+
video_inputs: bool
40+
"""Whether [video inputs](https://docs.langchain.com/oss/python/langchain/models#multimodal)
41+
are supported."""
42+
# TODO: add more detail about formats? e.g. bytes or base64
43+
44+
image_tool_message: bool
45+
"""Whether images can be included in tool messages."""
46+
47+
pdf_tool_message: bool
48+
"""Whether PDFs can be included in tool messages."""
49+
50+
# --- Output constraints ---
51+
52+
max_output_tokens: int
53+
"""Maximum output tokens"""
54+
55+
reasoning_output: bool
56+
"""Whether the model supports [reasoning / chain-of-thought](https://docs.langchain.com/oss/python/langchain/models#reasoning)"""
57+
58+
image_outputs: bool
59+
"""Whether [image outputs](https://docs.langchain.com/oss/python/langchain/models#multimodal)
60+
are supported."""
61+
62+
audio_outputs: bool
63+
"""Whether [audio outputs](https://docs.langchain.com/oss/python/langchain/models#multimodal)
64+
are supported."""
65+
66+
video_outputs: bool
67+
"""Whether [video outputs](https://docs.langchain.com/oss/python/langchain/models#multimodal)
68+
are supported."""
69+
70+
# --- Tool calling ---
71+
tool_calling: bool
72+
"""Whether the model supports [tool calling](https://docs.langchain.com/oss/python/langchain/models#tool-calling)"""
73+
74+
tool_choice: bool
75+
"""Whether the model supports [tool choice](https://docs.langchain.com/oss/python/langchain/models#forcing-tool-calls)"""
76+
77+
# --- Structured output ---
78+
structured_output: bool
79+
"""Whether the model supports a native [structured output](https://docs.langchain.com/oss/python/langchain/models#structured-outputs)
80+
feature"""
81+
82+
83+
ModelProfileRegistry = dict[str, ModelProfile]
84+
"""Registry mapping model identifiers or names to their ModelProfile."""

libs/core/pyproject.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ typing = [
3636
"mypy>=1.18.1,<1.19.0",
3737
"types-pyyaml>=6.0.12.2,<7.0.0.0",
3838
"types-requests>=2.28.11.5,<3.0.0.0",
39-
"langchain-model-profiles",
4039
"langchain-text-splitters",
4140
]
4241
dev = [
@@ -58,15 +57,13 @@ test = [
5857
"blockbuster>=1.5.18,<1.6.0",
5958
"numpy>=1.26.4; python_version<'3.13'",
6059
"numpy>=2.1.0; python_version>='3.13'",
61-
"langchain-model-profiles",
6260
"langchain-tests",
6361
"pytest-benchmark",
6462
"pytest-codspeed",
6563
]
6664
test_integration = []
6765

6866
[tool.uv.sources]
69-
langchain-model-profiles = { path = "../model-profiles" }
7067
langchain-tests = { path = "../standard-tests" }
7168
langchain-text-splitters = { path = "../text-splitters" }
7269

libs/core/tests/unit_tests/language_models/chat_models/test_base.py

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,19 +1222,12 @@ def _llm_type(self) -> str:
12221222

12231223
def test_model_profiles() -> None:
12241224
model = GenericFakeChatModel(messages=iter([]))
1225-
profile = model.profile
1226-
assert profile == {}
1225+
assert model.profile is None
12271226

1228-
class MyModel(GenericFakeChatModel):
1229-
model: str = "gpt-5"
1230-
1231-
@property
1232-
def _llm_type(self) -> str:
1233-
return "openai-chat"
1234-
1235-
model = MyModel(messages=iter([]))
1236-
profile = model.profile
1237-
assert profile
1227+
model_with_profile = GenericFakeChatModel(
1228+
messages=iter([]), profile={"max_input_tokens": 100}
1229+
)
1230+
assert model_with_profile.profile == {"max_input_tokens": 100}
12381231

12391232

12401233
class MockResponse:

libs/core/tests/unit_tests/language_models/test_imports.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
"FakeStreamingListLLM",
1919
"FakeListLLM",
2020
"ParrotFakeChatModel",
21+
"ModelProfile",
22+
"ModelProfileRegistry",
2123
"is_openai_data_block",
2224
]
2325

libs/core/uv.lock

Lines changed: 0 additions & 44 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libs/langchain_v1/langchain/agents/factory.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
STRUCTURED_OUTPUT_ERROR_TEMPLATE = "Error: {error}\n Please fix your mistakes."
6565

6666
FALLBACK_MODELS_WITH_STRUCTURED_OUTPUT = [
67-
# if langchain-model-profiles is not installed, these models are assumed to support
67+
# if model profile data are not available, these models are assumed to support
6868
# structured output
6969
"grok",
7070
"gpt-5",
@@ -381,18 +381,15 @@ def _supports_provider_strategy(model: str | BaseChatModel, tools: list | None =
381381
or getattr(model, "model", None)
382382
or getattr(model, "model_id", "")
383383
)
384-
try:
385-
model_profile = model.profile
386-
except ImportError:
387-
pass
388-
else:
389-
if (
390-
model_profile.get("structured_output")
391-
# We make an exception for Gemini models, which currently do not support
392-
# simultaneous tool use with structured output
393-
and not (tools and isinstance(model_name, str) and "gemini" in model_name.lower())
394-
):
395-
return True
384+
model_profile = model.profile
385+
if (
386+
model_profile is not None
387+
and model_profile.get("structured_output")
388+
# We make an exception for Gemini models, which currently do not support
389+
# simultaneous tool use with structured output
390+
and not (tools and isinstance(model_name, str) and "gemini" in model_name.lower())
391+
):
392+
return True
396393

397394
return (
398395
any(part in model_name.lower() for part in FALLBACK_MODELS_WITH_STRUCTURED_OUTPUT)

libs/langchain_v1/langchain/agents/middleware/summarization.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,11 @@ def __init__(
159159
requires_profile = True
160160
if requires_profile and self._get_profile_limits() is None:
161161
msg = (
162-
"Model profile information is required to use fractional token limits. "
163-
'pip install "langchain[model-profiles]" or use absolute token counts '
164-
"instead."
162+
"Model profile information is required to use fractional token limits, "
163+
"and is unavailable for the specified model. Please use absolute token "
164+
"counts instead, or pass "
165+
'`\n\nChatModel(..., profile={"max_input_tokens": ...})`.\n\n'
166+
"with a desired integer value of the model's maximum input tokens."
165167
)
166168
raise ValueError(msg)
167169

@@ -308,7 +310,7 @@ def _get_profile_limits(self) -> int | None:
308310
"""Retrieve max input token limit from the model profile."""
309311
try:
310312
profile = self.model.profile
311-
except (AttributeError, ImportError):
313+
except AttributeError:
312314
return None
313315

314316
if not isinstance(profile, Mapping):

0 commit comments

Comments
 (0)