libs/langchain/langchain_classic/chat_models/base.py PYTHON 1,069 lines View on github.com → Search inside
1from __future__ import annotations23import warnings4from collections.abc import AsyncIterator, Callable, Iterator, Sequence5from importlib import util6from typing import Any, Literal, TypeAlias, cast, overload78from langchain_core._api import deprecated9from langchain_core.language_models import (10    BaseChatModel,11    LanguageModelInput,12    SimpleChatModel,13)14from langchain_core.language_models.chat_models import (15    agenerate_from_stream,16    generate_from_stream,17)18from langchain_core.messages import AIMessage, AnyMessage19from langchain_core.runnables import Runnable, RunnableConfig, ensure_config20from langchain_core.runnables.schema import StreamEvent21from langchain_core.tools import BaseTool22from langchain_core.tracers import RunLog, RunLogPatch23from pydantic import BaseModel24from typing_extensions import override2526__all__ = [27    # For backwards compatibility28    "BaseChatModel",29    "SimpleChatModel",30    "agenerate_from_stream",31    "generate_from_stream",32    "init_chat_model",33]343536@overload37def init_chat_model(38    model: str,39    *,40    model_provider: str | None = None,41    configurable_fields: None = None,42    config_prefix: str | None = None,43    **kwargs: Any,44) -> BaseChatModel: ...454647@overload48def init_chat_model(49    model: None = None,50    *,51    model_provider: str | None = None,52    configurable_fields: None = None,53    config_prefix: str | None = None,54    **kwargs: Any,55) -> _ConfigurableModel: ...565758@overload59def init_chat_model(60    model: str | None = None,61    *,62    model_provider: str | None = None,63    configurable_fields: Literal["any"] | list[str] | tuple[str, ...] = ...,64    config_prefix: str | None = None,65    **kwargs: Any,66) -> _ConfigurableModel: ...676869# FOR CONTRIBUTORS: If adding support for a new provider, please append the provider70# name to the supported list in the docstring below. Do *not* change the order of the71# existing providers.72@deprecated(73    since="1.0.5",74    removal="2.0.0",75    alternative="langchain.chat_models.init_chat_model",76    addendum=(77        "Maintained in `langchain`; `langchain-classic` retains this entry point "78        "for import-compatibility only."79    ),80)81def init_chat_model(82    model: str | None = None,83    *,84    model_provider: str | None = None,85    configurable_fields: Literal["any"] | list[str] | tuple[str, ...] | None = None,86    config_prefix: str | None = None,87    **kwargs: Any,88) -> BaseChatModel | _ConfigurableModel:89    """Initialize a chat model from any supported provider using a unified interface.9091    !!! warning "Use `langchain.chat_models.init_chat_model` instead"9293        This function lives in `langchain-classic` and is no longer actively94        maintained. New features and fixes land in the `langchain` package.9596        Update your imports:9798        ```python99        # Don't do this:100        from langchain.chat_models import init_chat_model101102        # Do this instead:103        from langchain.chat_models import init_chat_model104        ```105106    **Two main use cases:**107108    1. **Fixed model**  specify the model upfront and get a109        ready-to-use chat model.110    2. **Configurable model**  choose to specify parameters111        (including model name) at runtime via `config`. Makes it easy to112        switch between models/providers without changing your code113114    !!! note "Installation requirements"115116        Requires the integration package for the chosen model provider to117        be installed.118119        See the `model_provider` parameter below for specific package names120        (e.g., `pip install langchain-openai`).121122        Refer to the [provider integration's API reference](https://docs.langchain.com/oss/python/integrations/providers)123        for supported model parameters to use as `**kwargs`.124125    Args:126        model: Name of the model to use, with provider prefix  e.g.,127            `'openai:gpt-5.5'`.128129            A bare model name (e.g., `'claude-opus-4-7'`) is also accepted; we130            will attempt to infer the provider from the prefix using the mapping131            below. Inference is best-effort and not guaranteed, so prefer132            the prefixed form when possible.133134            Prefer pinned model IDs over moving aliases (e.g.,135            `'claude-haiku-4-5-20251001'` rather than `'claude-haiku-4-5'`)136            so behavior does not drift if the alias is repointed upstream.137138            Inferred providers by prefix (case-insensitive):139140            - `gpt-...` | `o1...` | `o3...`               -> `openai`141            - `claude...`                                 -> `anthropic`142            - `amazon....` | `anthropic....` | `meta....` -> `bedrock`143            - `gemini...`                                 -> `google_vertexai`144            - `command...`                                -> `cohere`145            - `accounts/fireworks...`                     -> `fireworks`146            - `mistral...` | `mixtral...`                 -> `mistralai`147            - `deepseek...`                               -> `deepseek`148            - `grok...`                                   -> `xai`149            - `sonar...`                                  -> `perplexity`150            - `solar...`                                  -> `upstage`151            - `chatgpt...` | `text-davinci...`            -> `openai` (legacy)152        model_provider: Provider of the model, passed separately instead of153            as a prefix on `model`.154155            Equivalent to the prefix form  e.g.,156            `model='claude-sonnet-4-5', model_provider='anthropic'` behaves157            the same as `model='anthropic:claude-sonnet-4-5'`.158159            Prefer the prefix form on `model` for most usage. Reach for this160            kwarg when:161162            - The provider is dynamic (read from config or an env var) and163                you'd otherwise concatenate strings.164            - You want `model` and `model_provider` to be independently165                swappable at runtime via `configurable_fields` (e.g., to route166                the same model name to a different host).167168            Supported values and the integration package each requires:169170            - `openai`                  -> [`langchain-openai`](https://docs.langchain.com/oss/python/integrations/providers/openai)171            - `anthropic`               -> [`langchain-anthropic`](https://docs.langchain.com/oss/python/integrations/providers/anthropic)172            - `azure_openai`            -> [`langchain-openai`](https://docs.langchain.com/oss/python/integrations/providers/openai)173            - `azure_ai`                -> [`langchain-azure-ai`](https://docs.langchain.com/oss/python/integrations/providers/microsoft)174            - `google_vertexai`         -> [`langchain-google-vertexai`](https://docs.langchain.com/oss/python/integrations/providers/google)175            - `google_genai`            -> [`langchain-google-genai`](https://docs.langchain.com/oss/python/integrations/providers/google)176            - `bedrock`                 -> [`langchain-aws`](https://docs.langchain.com/oss/python/integrations/providers/aws)177            - `bedrock_converse`        -> [`langchain-aws`](https://docs.langchain.com/oss/python/integrations/providers/aws)178            - `cohere`                  -> [`langchain-cohere`](https://docs.langchain.com/oss/python/integrations/providers/cohere)179            - `fireworks`               -> [`langchain-fireworks`](https://docs.langchain.com/oss/python/integrations/providers/fireworks)180            - `together`                -> [`langchain-together`](https://docs.langchain.com/oss/python/integrations/providers/together)181            - `mistralai`               -> [`langchain-mistralai`](https://docs.langchain.com/oss/python/integrations/providers/mistralai)182            - `huggingface`             -> [`langchain-huggingface`](https://docs.langchain.com/oss/python/integrations/providers/huggingface)183            - `groq`                    -> [`langchain-groq`](https://docs.langchain.com/oss/python/integrations/providers/groq)184            - `ollama`                  -> [`langchain-ollama`](https://docs.langchain.com/oss/python/integrations/providers/ollama)185            - `google_anthropic_vertex` -> [`langchain-google-vertexai`](https://docs.langchain.com/oss/python/integrations/providers/google)186            - `deepseek`                -> [`langchain-deepseek`](https://docs.langchain.com/oss/python/integrations/providers/deepseek)187            - `ibm`                     -> [`langchain-ibm`](https://docs.langchain.com/oss/python/integrations/providers/ibm)188            - `nvidia`                  -> [`langchain-nvidia-ai-endpoints`](https://docs.langchain.com/oss/python/integrations/providers/nvidia)189            - `xai`                     -> [`langchain-xai`](https://docs.langchain.com/oss/python/integrations/providers/xai)190            - `perplexity`              -> [`langchain-perplexity`](https://docs.langchain.com/oss/python/integrations/providers/perplexity)191            - `upstage`                 -> [`langchain-upstage`](https://docs.langchain.com/oss/python/integrations/providers/upstage)192        configurable_fields: Which model parameters are configurable at runtime:193194            - `None`: No configurable fields (i.e., a fixed model).195            - `'any'`: All fields are configurable. **See security note below.**196            - `list[str] | Tuple[str, ...]`: Specified fields are configurable.197198            Fields are assumed to have `config_prefix` stripped if a `config_prefix` is199            specified.200201            If `model` is specified, then defaults to `None`.202203            If `model` is not specified, then defaults to `("model", "model_provider")`.204205            !!! warning "Security note"206207                Setting `configurable_fields="any"` means fields like `api_key`,208                `base_url`, etc., can be altered at runtime, potentially redirecting209                model requests to a different service/user.210211                Make sure that if you're accepting untrusted configurations that you212                enumerate the `configurable_fields=(...)` explicitly.213214        config_prefix: Optional prefix for configuration keys.215216            Useful when you have multiple configurable models in the same application.217218            If `'config_prefix'` is a non-empty string then `model` will be configurable219            at runtime via the `config["configurable"]["{config_prefix}_{param}"]` keys.220            See examples below.221222            If `'config_prefix'` is an empty string then model will be configurable via223            `config["configurable"]["{param}"]`.224        **kwargs: Additional model-specific keyword args to pass to the underlying225            chat model's `__init__` method. Common parameters include:226227            - `temperature`: Model temperature for controlling randomness.228            - `max_tokens`: Maximum number of output tokens.229            - `timeout`: Maximum time (in seconds) to wait for a response.230            - `max_retries`: Maximum number of retry attempts for failed requests.231            - `base_url`: Custom API endpoint URL.232            - `rate_limiter`: A233                [`BaseRateLimiter`][langchain_core.rate_limiters.BaseRateLimiter]234                instance to control request rate.235236            Refer to the specific model provider's237            [integration reference](https://reference.langchain.com/python/integrations/)238            for all available parameters.239240    Returns:241        A [`BaseChatModel`][langchain_core.language_models.BaseChatModel] corresponding242            to the `model_name` and `model_provider` specified if configurability is243            inferred to be `False`.244            If configurable, a chat model emulator that initializes the245            underlying model at runtime once a config is passed in.246247    Raises:248        ValueError: If `model_provider` cannot be inferred or isn't supported.249        ImportError: If the model provider integration package is not installed.250251    ???+ example "Initialize a non-configurable model"252253        ```python254        # pip install langchain langchain-openai255256        from langchain.chat_models import init_chat_model257258        gpt_5 = init_chat_model("openai:gpt-5.5", temperature=0)259        gpt_5.invoke("what's your name")260        ```261262    ??? example "Partially configurable model with no default"263264        ```python265        # pip install langchain langchain-openai266267        from langchain.chat_models import init_chat_model268269        # (We don't need to specify configurable=True if a model isn't specified.)270        configurable_model = init_chat_model(temperature=0)271272        # Use GPT-5.5 to generate the response273        configurable_model.invoke(274            "what's your name",275            config={"configurable": {"model": "gpt-5.5"}},276        )277        ```278279    ??? example "Fully configurable model with a default"280281        ```python282        # pip install langchain langchain-openai langchain-anthropic283284        from langchain.chat_models import init_chat_model285286        configurable_model_with_default = init_chat_model(287            "openai:gpt-5.5",288            configurable_fields="any",  # This allows us to configure other params like temperature, max_tokens, etc at runtime.289            config_prefix="foo",290            temperature=0,291        )292293        configurable_model_with_default.invoke("what's your name")294        # GPT-5.5 response with temperature 0 (as set in default)295296        # Invoke overriding model and temperature at runtime via config.297        # Note the use of the "foo_" prefix on the config keys, which matches298        # the config_prefix we set when initializing the model.299        configurable_model_with_default.invoke(300            "what's your name",301            config={302                "configurable": {303                    "foo_model": "anthropic:claude-opus-4-7",304                    "foo_temperature": 0.6,305                }306            },307        )308        ```309310    ??? example "Bind tools to a configurable model"311312        You can call any chat model declarative methods on a configurable model313        in the same way that you would with a normal model:314315        ```python316        # pip install langchain langchain-openai langchain-anthropic317318        from langchain.chat_models import init_chat_model319        from pydantic import BaseModel, Field320321322        class GetWeather(BaseModel):323            '''Get the current weather in a given location'''324325            location: str = Field(326                ..., description="The city and state, e.g. San Francisco, CA"327            )328329330        class GetPopulation(BaseModel):331            '''Get the current population in a given location'''332333            location: str = Field(334                ..., description="The city and state, e.g. San Francisco, CA"335            )336337338        configurable_model = init_chat_model(339            "gpt-5.5", configurable_fields=("model", "model_provider"), temperature=0340        )341342        configurable_model_with_tools = configurable_model.bind_tools(343            [344                GetWeather,345                GetPopulation,346            ]347        )348        configurable_model_with_tools.invoke(349            "Which city is hotter today and which is bigger: LA or NY?"350        )351        # Use GPT-5.5352353        configurable_model_with_tools.invoke(354            "Which city is hotter today and which is bigger: LA or NY?",355            config={"configurable": {"model": "claude-opus-4-7"}},356        )357        # Use Opus 4.7358        ```359360    """  # noqa: E501361    if not model and not configurable_fields:362        configurable_fields = ("model", "model_provider")363    config_prefix = config_prefix or ""364    if config_prefix and not configurable_fields:365        warnings.warn(366            f"{config_prefix=} has been set but no fields are configurable. Set "367            f"`configurable_fields=(...)` to specify the model params that are "368            f"configurable.",369            stacklevel=2,370        )371372    if not configurable_fields:373        return _init_chat_model_helper(374            cast("str", model),375            model_provider=model_provider,376            **kwargs,377        )378    if model:379        kwargs["model"] = model380    if model_provider:381        kwargs["model_provider"] = model_provider382    return _ConfigurableModel(383        default_config=kwargs,384        config_prefix=config_prefix,385        configurable_fields=configurable_fields,386    )387388389def _init_chat_model_helper(390    model: str,391    *,392    model_provider: str | None = None,393    **kwargs: Any,394) -> BaseChatModel:395    model, model_provider = _parse_model(model, model_provider)396    if model_provider == "openai":397        _check_pkg("langchain_openai", "ChatOpenAI")398        from langchain_openai import ChatOpenAI399400        return ChatOpenAI(model=model, **kwargs)401    if model_provider == "anthropic":402        _check_pkg("langchain_anthropic", "ChatAnthropic")403        from langchain_anthropic import ChatAnthropic404405        return ChatAnthropic(model=model, **kwargs)  # type: ignore[call-arg,unused-ignore]406    if model_provider == "azure_openai":407        _check_pkg("langchain_openai", "AzureChatOpenAI")408        from langchain_openai import AzureChatOpenAI409410        return AzureChatOpenAI(model=model, **kwargs)411    if model_provider == "azure_ai":412        _check_pkg("langchain_azure_ai", "AzureAIOpenAIApiChatModel")413        from langchain_azure_ai.chat_models import AzureAIOpenAIApiChatModel414415        return AzureAIOpenAIApiChatModel(model=model, **kwargs)416    if model_provider == "cohere":417        _check_pkg("langchain_cohere", "ChatCohere")418        from langchain_cohere import ChatCohere419420        return ChatCohere(model=model, **kwargs)421    if model_provider == "google_vertexai":422        _check_pkg("langchain_google_vertexai", "ChatVertexAI")423        from langchain_google_vertexai import ChatVertexAI424425        return ChatVertexAI(model=model, **kwargs)426    if model_provider == "google_genai":427        _check_pkg("langchain_google_genai", "ChatGoogleGenerativeAI")428        from langchain_google_genai import ChatGoogleGenerativeAI429430        return ChatGoogleGenerativeAI(model=model, **kwargs)431    if model_provider == "fireworks":432        _check_pkg("langchain_fireworks", "ChatFireworks")433        from langchain_fireworks import ChatFireworks434435        return ChatFireworks(model=model, **kwargs)436    if model_provider == "ollama":437        try:438            _check_pkg("langchain_ollama", "ChatOllama")439            from langchain_ollama import ChatOllama440        except ImportError:441            # For backwards compatibility442            try:443                _check_pkg("langchain_community", "ChatOllama")444                from langchain_community.chat_models import ChatOllama445            except ImportError:446                # If both langchain-ollama and langchain-community aren't available,447                # raise an error related to langchain-ollama448                _check_pkg("langchain_ollama", "ChatOllama")449450        return ChatOllama(model=model, **kwargs)451    if model_provider == "together":452        _check_pkg("langchain_together", "ChatTogether")453        from langchain_together import ChatTogether454455        return ChatTogether(model=model, **kwargs)456    if model_provider == "mistralai":457        _check_pkg("langchain_mistralai", "ChatMistralAI")458        from langchain_mistralai import ChatMistralAI459460        return ChatMistralAI(model=model, **kwargs)  # type: ignore[call-arg,unused-ignore]461462    if model_provider == "huggingface":463        _check_pkg("langchain_huggingface", "ChatHuggingFace")464        from langchain_huggingface import ChatHuggingFace465466        return ChatHuggingFace.from_model_id(model_id=model, **kwargs)467468    if model_provider == "groq":469        _check_pkg("langchain_groq", "ChatGroq")470        from langchain_groq import ChatGroq471472        return ChatGroq(model=model, **kwargs)473    if model_provider == "bedrock":474        _check_pkg("langchain_aws", "ChatBedrock")475        from langchain_aws import ChatBedrock476477        # TODO: update to use model= once ChatBedrock supports478        return ChatBedrock(model_id=model, **kwargs)479    if model_provider == "bedrock_converse":480        _check_pkg("langchain_aws", "ChatBedrockConverse")481        from langchain_aws import ChatBedrockConverse482483        return ChatBedrockConverse(model=model, **kwargs)484    if model_provider == "google_anthropic_vertex":485        _check_pkg("langchain_google_vertexai", "ChatAnthropicVertex")486        from langchain_google_vertexai.model_garden import ChatAnthropicVertex487488        return ChatAnthropicVertex(model=model, **kwargs)489    if model_provider == "deepseek":490        _check_pkg("langchain_deepseek", "ChatDeepSeek", pkg_kebab="langchain-deepseek")491        from langchain_deepseek import ChatDeepSeek492493        return ChatDeepSeek(model=model, **kwargs)494    if model_provider == "nvidia":495        _check_pkg("langchain_nvidia_ai_endpoints", "ChatNVIDIA")496        from langchain_nvidia_ai_endpoints import ChatNVIDIA497498        return ChatNVIDIA(model=model, **kwargs)499    if model_provider == "ibm":500        _check_pkg("langchain_ibm", "ChatWatsonx")501        from langchain_ibm import ChatWatsonx502503        return ChatWatsonx(model_id=model, **kwargs)504    if model_provider == "xai":505        _check_pkg("langchain_xai", "ChatXAI")506        from langchain_xai import ChatXAI507508        return ChatXAI(model=model, **kwargs)509    if model_provider == "perplexity":510        _check_pkg("langchain_perplexity", "ChatPerplexity")511        from langchain_perplexity import ChatPerplexity512513        return ChatPerplexity(model=model, **kwargs)514    if model_provider == "upstage":515        _check_pkg("langchain_upstage", "ChatUpstage")516        from langchain_upstage import ChatUpstage517518        return ChatUpstage(model=model, **kwargs)519    supported = ", ".join(_SUPPORTED_PROVIDERS)520    msg = (521        f"Unsupported {model_provider=}.\n\nSupported model providers are: {supported}"522    )523    raise ValueError(msg)524525526_SUPPORTED_PROVIDERS = {527    "openai",528    "anthropic",529    "azure_openai",530    "azure_ai",531    "cohere",532    "google_vertexai",533    "google_genai",534    "fireworks",535    "ollama",536    "together",537    "mistralai",538    "huggingface",539    "groq",540    "bedrock",541    "bedrock_converse",542    "google_anthropic_vertex",543    "deepseek",544    "ibm",545    "xai",546    "perplexity",547    "upstage",548}549550551def _attempt_infer_model_provider(model_name: str) -> str | None:552    """Attempt to infer model provider from model name.553554    Args:555        model_name: The name of the model to infer provider for.556557    Returns:558        The inferred provider name, or `None` if no provider could be inferred.559    """560    model_lower = model_name.lower()561562    # OpenAI models (including newer models and aliases)563    if any(564        model_lower.startswith(pre)565        for pre in (566            "gpt-",567            "o1",568            "o3",569            "chatgpt",570            "text-davinci",571        )572    ):573        return "openai"574575    # Anthropic models576    if model_lower.startswith("claude"):577        return "anthropic"578579    # Cohere models580    if model_lower.startswith("command"):581        return "cohere"582583    # Fireworks models584    if model_name.startswith("accounts/fireworks"):585        return "fireworks"586587    # Google models588    if model_lower.startswith("gemini"):589        return "google_vertexai"590591    # AWS Bedrock models592    if model_name.startswith("amazon.") or model_lower.startswith(593        (594            "anthropic.",595            "meta.",596        )597    ):598        return "bedrock"599600    # Mistral models601    if model_lower.startswith(("mistral", "mixtral")):602        return "mistralai"603604    # DeepSeek models605    if model_lower.startswith("deepseek"):606        return "deepseek"607608    # xAI models609    if model_lower.startswith("grok"):610        return "xai"611612    # Perplexity models613    if model_lower.startswith("sonar"):614        return "perplexity"615616    # Upstage models617    if model_lower.startswith("solar"):618        return "upstage"619620    return None621622623def _parse_model(model: str, model_provider: str | None) -> tuple[str, str]:624    """Parse model name and provider, inferring provider if necessary."""625    if not model_provider and ":" in model:626        prefix, suffix = model.split(":", 1)627        if prefix in _SUPPORTED_PROVIDERS:628            model_provider = prefix629            model = suffix630        else:631            inferred = _attempt_infer_model_provider(prefix)632            if inferred:633                model_provider = inferred634                model = suffix635636    model_provider = model_provider or _attempt_infer_model_provider(model)637    if not model_provider:638        supported_list = ", ".join(sorted(_SUPPORTED_PROVIDERS))639        msg = (640            f"Unable to infer model provider for {model=}. "641            f"Please specify 'model_provider' directly.\n\n"642            f"Supported providers: {supported_list}\n\n"643            f"For help with specific providers, see: "644            f"https://docs.langchain.com/oss/python/integrations/providers"645        )646        raise ValueError(msg)647648    # Normalize provider name649    model_provider = model_provider.replace("-", "_").lower()650    return model, model_provider651652653def _check_pkg(pkg: str, class_name: str, *, pkg_kebab: str | None = None) -> None:654    if not util.find_spec(pkg):655        pkg_kebab = pkg_kebab if pkg_kebab is not None else pkg.replace("_", "-")656        msg = (657            f"Initializing {class_name} requires the {pkg_kebab} package. "658            f"Please install it with `pip install {pkg_kebab}`"659        )660        raise ImportError(msg)661662663_DECLARATIVE_METHODS = ("bind_tools", "with_structured_output")664665666class _ConfigurableModel(Runnable[LanguageModelInput, Any]):667    def __init__(668        self,669        *,670        default_config: dict | None = None,671        configurable_fields: Literal["any"] | list[str] | tuple[str, ...] = "any",672        config_prefix: str = "",673        queued_declarative_operations: Sequence[tuple[str, tuple, dict]] = (),674    ) -> None:675        self._default_config: dict = default_config or {}676        self._configurable_fields: Literal["any"] | list[str] = (677            configurable_fields678            if configurable_fields == "any"679            else list(configurable_fields)680        )681        self._config_prefix = (682            config_prefix + "_"683            if config_prefix and not config_prefix.endswith("_")684            else config_prefix685        )686        self._queued_declarative_operations: list[tuple[str, tuple, dict]] = list(687            queued_declarative_operations,688        )689690    def __getattr__(self, name: str) -> Any:691        if name in _DECLARATIVE_METHODS:692            # Declarative operations that cannot be applied until after an actual model693            # object is instantiated. So instead of returning the actual operation,694            # we record the operation and its arguments in a queue. This queue is695            # then applied in order whenever we actually instantiate the model (in696            # self._model()).697            def queue(*args: Any, **kwargs: Any) -> _ConfigurableModel:698                queued_declarative_operations = list(699                    self._queued_declarative_operations,700                )701                queued_declarative_operations.append((name, args, kwargs))702                return _ConfigurableModel(703                    default_config=dict(self._default_config),704                    configurable_fields=list(self._configurable_fields)705                    if isinstance(self._configurable_fields, list)706                    else self._configurable_fields,707                    config_prefix=self._config_prefix,708                    queued_declarative_operations=queued_declarative_operations,709                )710711            return queue712        if self._default_config and (model := self._model()) and hasattr(model, name):713            return getattr(model, name)714        msg = f"{name} is not a BaseChatModel attribute"715        if self._default_config:716            msg += " and is not implemented on the default model"717        msg += "."718        raise AttributeError(msg)719720    def _model(self, config: RunnableConfig | None = None) -> Runnable:721        params = {**self._default_config, **self._model_params(config)}722        model = _init_chat_model_helper(**params)723        for name, args, kwargs in self._queued_declarative_operations:724            model = getattr(model, name)(*args, **kwargs)725        return model726727    def _model_params(self, config: RunnableConfig | None) -> dict:728        config = ensure_config(config)729        model_params = {730            k.removeprefix(self._config_prefix): v731            for k, v in config.get("configurable", {}).items()732            if k.startswith(self._config_prefix)733        }734        if self._configurable_fields != "any":735            model_params = {736                k: v for k, v in model_params.items() if k in self._configurable_fields737            }738        return model_params739740    def with_config(741        self,742        config: RunnableConfig | None = None,743        **kwargs: Any,744    ) -> _ConfigurableModel:745        """Bind config to a `Runnable`, returning a new `Runnable`."""746        config = RunnableConfig(**(config or {}), **cast("RunnableConfig", kwargs))747        model_params = self._model_params(config)748        remaining_config = {k: v for k, v in config.items() if k != "configurable"}749        remaining_config["configurable"] = {750            k: v751            for k, v in config.get("configurable", {}).items()752            if k.removeprefix(self._config_prefix) not in model_params753        }754        queued_declarative_operations = list(self._queued_declarative_operations)755        if remaining_config:756            queued_declarative_operations.append(757                (758                    "with_config",759                    (),760                    {"config": remaining_config},761                ),762            )763        return _ConfigurableModel(764            default_config={**self._default_config, **model_params},765            configurable_fields=list(self._configurable_fields)766            if isinstance(self._configurable_fields, list)767            else self._configurable_fields,768            config_prefix=self._config_prefix,769            queued_declarative_operations=queued_declarative_operations,770        )771772    @property773    @override774    def InputType(self) -> TypeAlias:775        """Get the input type for this `Runnable`."""776        from langchain_core.prompt_values import (777            ChatPromptValueConcrete,778            StringPromptValue,779        )780781        # This is a version of LanguageModelInput which replaces the abstract782        # base class BaseMessage with a union of its subclasses, which makes783        # for a much better schema.784        return str | StringPromptValue | ChatPromptValueConcrete | list[AnyMessage]785786    @override787    def invoke(788        self,789        input: LanguageModelInput,790        config: RunnableConfig | None = None,791        **kwargs: Any,792    ) -> Any:793        return self._model(config).invoke(input, config=config, **kwargs)794795    @override796    async def ainvoke(797        self,798        input: LanguageModelInput,799        config: RunnableConfig | None = None,800        **kwargs: Any,801    ) -> Any:802        return await self._model(config).ainvoke(input, config=config, **kwargs)803804    @override805    def stream(806        self,807        input: LanguageModelInput,808        config: RunnableConfig | None = None,809        **kwargs: Any | None,810    ) -> Iterator[Any]:811        yield from self._model(config).stream(input, config=config, **kwargs)812813    @override814    async def astream(815        self,816        input: LanguageModelInput,817        config: RunnableConfig | None = None,818        **kwargs: Any | None,819    ) -> AsyncIterator[Any]:820        async for x in self._model(config).astream(input, config=config, **kwargs):821            yield x822823    def batch(824        self,825        inputs: list[LanguageModelInput],826        config: RunnableConfig | list[RunnableConfig] | None = None,827        *,828        return_exceptions: bool = False,829        **kwargs: Any | None,830    ) -> list[Any]:831        config = config or None832        # If <= 1 config use the underlying models batch implementation.833        if config is None or isinstance(config, dict) or len(config) <= 1:834            if isinstance(config, list):835                config = config[0]836            return self._model(config).batch(837                inputs,838                config=config,839                return_exceptions=return_exceptions,840                **kwargs,841            )842        # If multiple configs default to Runnable.batch which uses executor to invoke843        # in parallel.844        return super().batch(845            inputs,846            config=config,847            return_exceptions=return_exceptions,848            **kwargs,849        )850851    async def abatch(852        self,853        inputs: list[LanguageModelInput],854        config: RunnableConfig | list[RunnableConfig] | None = None,855        *,856        return_exceptions: bool = False,857        **kwargs: Any | None,858    ) -> list[Any]:859        config = config or None860        # If <= 1 config use the underlying models batch implementation.861        if config is None or isinstance(config, dict) or len(config) <= 1:862            if isinstance(config, list):863                config = config[0]864            return await self._model(config).abatch(865                inputs,866                config=config,867                return_exceptions=return_exceptions,868                **kwargs,869            )870        # If multiple configs default to Runnable.batch which uses executor to invoke871        # in parallel.872        return await super().abatch(873            inputs,874            config=config,875            return_exceptions=return_exceptions,876            **kwargs,877        )878879    def batch_as_completed(880        self,881        inputs: Sequence[LanguageModelInput],882        config: RunnableConfig | Sequence[RunnableConfig] | None = None,883        *,884        return_exceptions: bool = False,885        **kwargs: Any,886    ) -> Iterator[tuple[int, Any | Exception]]:887        config = config or None888        # If <= 1 config use the underlying models batch implementation.889        if config is None or isinstance(config, dict) or len(config) <= 1:890            if isinstance(config, list):891                config = config[0]892            yield from self._model(cast("RunnableConfig", config)).batch_as_completed(  # type: ignore[call-overload]893                inputs,894                config=config,895                return_exceptions=return_exceptions,896                **kwargs,897            )898        # If multiple configs default to Runnable.batch which uses executor to invoke899        # in parallel.900        else:901            yield from super().batch_as_completed(  # type: ignore[call-overload]902                inputs,903                config=config,904                return_exceptions=return_exceptions,905                **kwargs,906            )907908    async def abatch_as_completed(909        self,910        inputs: Sequence[LanguageModelInput],911        config: RunnableConfig | Sequence[RunnableConfig] | None = None,912        *,913        return_exceptions: bool = False,914        **kwargs: Any,915    ) -> AsyncIterator[tuple[int, Any]]:916        config = config or None917        # If <= 1 config use the underlying models batch implementation.918        if config is None or isinstance(config, dict) or len(config) <= 1:919            if isinstance(config, list):920                config = config[0]921            async for x in self._model(922                cast("RunnableConfig", config),923            ).abatch_as_completed(  # type: ignore[call-overload]924                inputs,925                config=config,926                return_exceptions=return_exceptions,927                **kwargs,928            ):929                yield x930        # If multiple configs default to Runnable.batch which uses executor to invoke931        # in parallel.932        else:933            async for x in super().abatch_as_completed(  # type: ignore[call-overload]934                inputs,935                config=config,936                return_exceptions=return_exceptions,937                **kwargs,938            ):939                yield x940941    @override942    def transform(943        self,944        input: Iterator[LanguageModelInput],945        config: RunnableConfig | None = None,946        **kwargs: Any | None,947    ) -> Iterator[Any]:948        yield from self._model(config).transform(input, config=config, **kwargs)949950    @override951    async def atransform(952        self,953        input: AsyncIterator[LanguageModelInput],954        config: RunnableConfig | None = None,955        **kwargs: Any | None,956    ) -> AsyncIterator[Any]:957        async for x in self._model(config).atransform(input, config=config, **kwargs):958            yield x959960    @overload961    def astream_log(962        self,963        input: Any,964        config: RunnableConfig | None = None,965        *,966        diff: Literal[True] = True,967        with_streamed_output_list: bool = True,968        include_names: Sequence[str] | None = None,969        include_types: Sequence[str] | None = None,970        include_tags: Sequence[str] | None = None,971        exclude_names: Sequence[str] | None = None,972        exclude_types: Sequence[str] | None = None,973        exclude_tags: Sequence[str] | None = None,974        **kwargs: Any,975    ) -> AsyncIterator[RunLogPatch]: ...976977    @overload978    def astream_log(979        self,980        input: Any,981        config: RunnableConfig | None = None,982        *,983        diff: Literal[False],984        with_streamed_output_list: bool = True,985        include_names: Sequence[str] | None = None,986        include_types: Sequence[str] | None = None,987        include_tags: Sequence[str] | None = None,988        exclude_names: Sequence[str] | None = None,989        exclude_types: Sequence[str] | None = None,990        exclude_tags: Sequence[str] | None = None,991        **kwargs: Any,992    ) -> AsyncIterator[RunLog]: ...993994    @override995    async def astream_log(996        self,997        input: Any,998        config: RunnableConfig | None = None,999        *,1000        diff: bool = True,1001        with_streamed_output_list: bool = True,1002        include_names: Sequence[str] | None = None,1003        include_types: Sequence[str] | None = None,1004        include_tags: Sequence[str] | None = None,1005        exclude_names: Sequence[str] | None = None,1006        exclude_types: Sequence[str] | None = None,1007        exclude_tags: Sequence[str] | None = None,1008        **kwargs: Any,1009    ) -> AsyncIterator[RunLogPatch] | AsyncIterator[RunLog]:1010        async for x in self._model(config).astream_log(  # type: ignore[call-overload, misc]1011            input,1012            config=config,1013            diff=diff,1014            with_streamed_output_list=with_streamed_output_list,1015            include_names=include_names,1016            include_types=include_types,1017            include_tags=include_tags,1018            exclude_tags=exclude_tags,1019            exclude_types=exclude_types,1020            exclude_names=exclude_names,1021            **kwargs,1022        ):1023            yield x10241025    @override1026    async def astream_events(  # type: ignore[override]1027        self,1028        input: Any,1029        config: RunnableConfig | None = None,1030        *,1031        version: Literal["v1", "v2"] = "v2",1032        include_names: Sequence[str] | None = None,1033        include_types: Sequence[str] | None = None,1034        include_tags: Sequence[str] | None = None,1035        exclude_names: Sequence[str] | None = None,1036        exclude_types: Sequence[str] | None = None,1037        exclude_tags: Sequence[str] | None = None,1038        **kwargs: Any,1039    ) -> AsyncIterator[StreamEvent]:1040        async for x in self._model(config).astream_events(1041            input,1042            config=config,1043            version=version,1044            include_names=include_names,1045            include_types=include_types,1046            include_tags=include_tags,1047            exclude_tags=exclude_tags,1048            exclude_types=exclude_types,1049            exclude_names=exclude_names,1050            **kwargs,1051        ):1052            yield x10531054    # Explicitly added to satisfy downstream linters.1055    def bind_tools(1056        self,1057        tools: Sequence[dict[str, Any] | type[BaseModel] | Callable | BaseTool],1058        **kwargs: Any,1059    ) -> Runnable[LanguageModelInput, AIMessage]:1060        return self.__getattr__("bind_tools")(tools, **kwargs)10611062    # Explicitly added to satisfy downstream linters.1063    def with_structured_output(1064        self,1065        schema: dict | type[BaseModel],1066        **kwargs: Any,1067    ) -> Runnable[LanguageModelInput, dict | BaseModel]:1068        return self.__getattr__("with_structured_output")(schema, **kwargs)

Code quality findings 36

Ensure functions have docstrings for documentation
missing-docstring
def init_chat_model(
Ensure functions have docstrings for documentation
missing-docstring
def init_chat_model(
Ensure functions have docstrings for documentation
missing-docstring
def init_chat_model(
Ensure functions have docstrings for documentation
missing-docstring
def init_chat_model(
Avoid unnecessary list conversions; use generators where possible
unnecessary-list
else list(configurable_fields)
Avoid unnecessary list conversions; use generators where possible
unnecessary-list
self._queued_declarative_operations: list[tuple[str, tuple, dict]] = list(
Ensure functions have docstrings for documentation
missing-docstring
def queue(*args: Any, **kwargs: Any) -> _ConfigurableModel:
Avoid unnecessary list conversions; use generators where possible
unnecessary-list
queued_declarative_operations = list(
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
if isinstance(self._configurable_fields, list)
Ensure functions have docstrings for documentation
missing-docstring
def with_config(
Avoid unnecessary list conversions; use generators where possible
unnecessary-list
queued_declarative_operations = list(self._queued_declarative_operations)
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
if isinstance(self._configurable_fields, list)
Ensure functions have docstrings for documentation
missing-docstring
def invoke(
Ensure functions have docstrings for documentation
missing-docstring
async def ainvoke(
Ensure functions have docstrings for documentation
missing-docstring
def stream(
Ensure functions have docstrings for documentation
missing-docstring
async def astream(
Ensure functions have docstrings for documentation
missing-docstring
def batch(
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
if config is None or isinstance(config, dict) or len(config) <= 1:
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
if isinstance(config, list):
Ensure functions have docstrings for documentation
missing-docstring
async def abatch(
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
if config is None or isinstance(config, dict) or len(config) <= 1:
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
if isinstance(config, list):
Ensure functions have docstrings for documentation
missing-docstring
def batch_as_completed(
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
if config is None or isinstance(config, dict) or len(config) <= 1:
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
if isinstance(config, list):
Ensure functions have docstrings for documentation
missing-docstring
async def abatch_as_completed(
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
if config is None or isinstance(config, dict) or len(config) <= 1:
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
if isinstance(config, list):
Ensure functions have docstrings for documentation
missing-docstring
def transform(
Ensure functions have docstrings for documentation
missing-docstring
async def atransform(
Ensure functions have docstrings for documentation
missing-docstring
def astream_log(
Ensure functions have docstrings for documentation
missing-docstring
def astream_log(
Ensure functions have docstrings for documentation
missing-docstring
async def astream_log(
Ensure functions have docstrings for documentation
missing-docstring
async def astream_events( # type: ignore[override]
Ensure functions have docstrings for documentation
missing-docstring
def bind_tools(
Ensure functions have docstrings for documentation
missing-docstring
def with_structured_output(

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.