Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
minimax.py799 linesDownload Raw Back to chat_models
1"""Wrapper around Minimax chat models."""2 3import json4import logging5from contextlib import asynccontextmanager, contextmanager6from operator import itemgetter7from typing import (8    Any,9    AsyncIterator,10    Callable,11    Dict,12    Iterator,13    List,14    Optional,15    Sequence,16    Type,17    Union,18)19 20from langchain_core.callbacks import (21    AsyncCallbackManagerForLLMRun,22    CallbackManagerForLLMRun,23)24from langchain_core.language_models import LanguageModelInput25from langchain_core.language_models.chat_models import (26    BaseChatModel,27    agenerate_from_stream,28    generate_from_stream,29)30from langchain_core.messages import (31    AIMessage,32    AIMessageChunk,33    BaseMessage,34    BaseMessageChunk,35    ChatMessage,36    ChatMessageChunk,37    HumanMessage,38    SystemMessage,39    ToolMessage,40)41from langchain_core.output_parsers.base import OutputParserLike42from langchain_core.output_parsers.openai_tools import (43    JsonOutputKeyToolsParser,44    PydanticToolsParser,45)46from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult47from langchain_core.runnables import Runnable, RunnableMap, RunnablePassthrough48from langchain_core.tools import BaseTool49from langchain_core.utils import convert_to_secret_str, get_from_dict_or_env50from langchain_core.utils.function_calling import convert_to_openai_tool51from langchain_core.utils.pydantic import get_fields52from pydantic import (53    BaseModel,54    ConfigDict,55    Field,56    SecretStr,57    model_validator,58)59 60logger = logging.getLogger(__name__)61 62 63@contextmanager64def connect_httpx_sse(client: Any, method: str, url: str, **kwargs: Any) -> Iterator:65    """Context manager for connecting to an SSE stream.66 67    Args:68        client: The httpx client.69        method: The HTTP method.70        url: The URL to connect to.71        kwargs: Additional keyword arguments to pass to the client.72 73    Yields:74        An EventSource object.75    """76    from httpx_sse import EventSource77 78    with client.stream(method, url, **kwargs) as response:79        yield EventSource(response)80 81 82@asynccontextmanager83async def aconnect_httpx_sse(84    client: Any, method: str, url: str, **kwargs: Any85) -> AsyncIterator:86    """Async context manager for connecting to an SSE stream.87 88    Args:89        client: The httpx client.90        method: The HTTP method.91        url: The URL to connect to.92        kwargs: Additional keyword arguments to pass to the client.93 94    Yields:95        An EventSource object.96    """97    from httpx_sse import EventSource98 99    async with client.stream(method, url, **kwargs) as response:100        yield EventSource(response)101 102 103def _convert_message_to_dict(message: BaseMessage) -> Dict[str, Any]:104    """Convert a LangChain messages to Dict."""105    message_dict: Dict[str, Any]106    if isinstance(message, HumanMessage):107        message_dict = {"role": "user", "content": message.content}108    elif isinstance(message, AIMessage):109        message_dict = {110            "role": "assistant",111            "content": message.content,112            "tool_calls": message.additional_kwargs.get("tool_calls"),113        }114    elif isinstance(message, SystemMessage):115        message_dict = {"role": "system", "content": message.content}116    elif isinstance(message, ToolMessage):117        message_dict = {118            "role": "tool",119            "content": message.content,120            "tool_call_id": message.tool_call_id,121            "name": message.name or message.additional_kwargs.get("name"),122        }123    else:124        raise TypeError(f"Got unknown type '{message.__class__.__name__}'.")125    return message_dict126 127 128def _convert_dict_to_message(dct: Dict[str, Any]) -> BaseMessage:129    """Convert a dict to LangChain message."""130    role = dct.get("role")131    content = dct.get("content", "")132    if role == "assistant":133        additional_kwargs = {}134        tool_calls = dct.get("tool_calls", None)135        if tool_calls is not None:136            additional_kwargs["tool_calls"] = tool_calls137        return AIMessage(content=content, additional_kwargs=additional_kwargs)138    return ChatMessage(role=role, content=content)  # type: ignore[arg-type]139 140 141def _convert_delta_to_message_chunk(142    dct: Dict[str, Any], default_class: Type[BaseMessageChunk]143) -> BaseMessageChunk:144    role = dct.get("role")145    content = dct.get("content", "")146    additional_kwargs = {}147    tool_calls = dct.get("tool_call", None)148    if tool_calls is not None:149        additional_kwargs["tool_calls"] = tool_calls150 151    if role == "assistant" or default_class == AIMessageChunk:152        return AIMessageChunk(content=content, additional_kwargs=additional_kwargs)153    if role or default_class == ChatMessageChunk:154        return ChatMessageChunk(content=content, role=role)  # type: ignore[arg-type]155    return default_class(content=content)  # type: ignore[call-arg]156 157 158class MiniMaxChat(BaseChatModel):159    """MiniMax chat model integration.160 161    Setup:162        To use, you should have the environment variable``MINIMAX_API_KEY`` set with163    your API KEY.164 165        .. code-block:: bash166 167            export MINIMAX_API_KEY="your-api-key"168 169    Key init args — completion params:170        model: Optional[str]171            Name of MiniMax model to use.172        max_tokens: Optional[int]173            Max number of tokens to generate.174        temperature: Optional[float]175            Sampling temperature.176        top_p: Optional[float]177            Total probability mass of tokens to consider at each step.178        streaming: Optional[bool]179             Whether to stream the results or not.180 181    Key init args — client params:182        api_key: Optional[str]183            MiniMax API key. If not passed in will be read from env var MINIMAX_API_KEY.184        base_url: Optional[str]185            Base URL for API requests.186 187    See full list of supported init args and their descriptions in the params section.188 189    Instantiate:190        .. code-block:: python191 192            from langchain_community.chat_models import MiniMaxChat193 194            chat = MiniMaxChat(195                api_key=api_key,196                model='abab6.5-chat',197                # temperature=...,198                # other params...199            )200 201    Invoke:202        .. code-block:: python203 204            messages = [205                ("system", "你是一名专业的翻译家,可以将用户的中文翻译为英文。"),206                ("human", "我喜欢编程。"),207            ]208            chat.invoke(messages)209 210        .. code-block:: python211 212            AIMessage(213                content='I enjoy programming.',214                response_metadata={215                    'token_usage': {'total_tokens': 48},216                    'model_name': 'abab6.5-chat',217                    'finish_reason': 'stop'218                },219                id='run-42d62ba6-5dc1-4e16-98dc-f72708a4162d-0'220            )221 222    Stream:223        .. code-block:: python224 225            for chunk in chat.stream(messages):226                print(chunk)227 228        .. code-block:: python229 230            content='I' id='run-a5837c45-4aaa-4f64-9ab4-2679bbd55522'231            content=' enjoy programming.' response_metadata={'finish_reason': 'stop'} id='run-a5837c45-4aaa-4f64-9ab4-2679bbd55522'232 233        .. code-block:: python234 235            stream = chat.stream(messages)236            full = next(stream)237            for chunk in stream:238                full += chunk239            full240 241        .. code-block:: python242 243            AIMessageChunk(244                content='I enjoy programming.',245                response_metadata={'finish_reason': 'stop'},246                id='run-01aed0a0-61c4-4709-be22-c6d8b17155d6'247            )248 249    Async:250        .. code-block:: python251 252            await chat.ainvoke(messages)253 254            # stream255            # async for chunk in chat.astream(messages):256            #     print(chunk)257 258            # batch259            # await chat.abatch([messages])260 261        .. code-block:: python262 263            AIMessage(264                content='I enjoy programming.',265                response_metadata={266                    'token_usage': {'total_tokens': 48},267                    'model_name': 'abab6.5-chat',268                    'finish_reason': 'stop'269                },270                id='run-c263b6f1-1736-4ece-a895-055c26b3436f-0'271            )272 273    Tool calling:274        .. code-block:: python275 276            from pydantic import BaseModel, Field277 278 279            class GetWeather(BaseModel):280                '''Get the current weather in a given location'''281 282                location: str = Field(283                    ..., description="The city and state, e.g. San Francisco, CA"284                )285 286 287            class GetPopulation(BaseModel):288                '''Get the current population in a given location'''289 290                location: str = Field(291                    ..., description="The city and state, e.g. San Francisco, CA"292                )293 294            chat_with_tools = chat.bind_tools([GetWeather, GetPopulation])295            ai_msg = chat_with_tools.invoke(296                "Which city is hotter today and which is bigger: LA or NY?"297            )298            ai_msg.tool_calls299 300        .. code-block:: python301 302            [303                {304                    'name': 'GetWeather',305                    'args': {'location': 'LA'},306                    'id': 'call_function_2140449382',307                    'type': 'tool_call'308                }309            ]310 311    Structured output:312        .. code-block:: python313 314            from typing import Optional315 316            from pydantic import BaseModel, Field317 318 319            class Joke(BaseModel):320                '''Joke to tell user.'''321                setup: str = Field(description="The setup of the joke")322                punchline: str = Field(description="The punchline to the joke")323                rating: Optional[int] = Field(description="How funny the joke is, from 1 to 10")324 325 326            structured_chat = chat.with_structured_output(Joke)327            structured_chat.invoke("Tell me a joke about cats")328 329        .. code-block:: python330 331            Joke(332                setup='Why do cats have nine lives?',333                punchline='Because they are so cute and cuddly!',334                rating=None335            )336 337    Response metadata338        .. code-block:: python339 340            ai_msg = chat.invoke(messages)341            ai_msg.response_metadata342 343        .. code-block:: python344 345            {'token_usage': {'total_tokens': 48},346             'model_name': 'abab6.5-chat',347             'finish_reason': 'stop'}348 349    """  # noqa: E501350 351    @property352    def _identifying_params(self) -> Dict[str, Any]:353        """Get the identifying parameters."""354        return {**{"model": self.model}, **self._default_params}355 356    @property357    def _llm_type(self) -> str:358        """Return type of llm."""359        return "minimax"360 361    @property362    def _default_params(self) -> Dict[str, Any]:363        """Get the default parameters for calling OpenAI API."""364        return {365            "model": self.model,366            "max_tokens": self.max_tokens,367            "temperature": self.temperature,368            "top_p": self.top_p,369            **self.model_kwargs,370        }371 372    _client: Any = None373    model: str = "abab6.5s-chat"374    """Model name to use."""375    max_tokens: int = 256376    """Denotes the number of tokens to predict per generation."""377    temperature: float = 0.7378    """A non-negative float that tunes the degree of randomness in generation."""379    top_p: float = 0.95380    """Total probability mass of tokens to consider at each step."""381    model_kwargs: Dict[str, Any] = Field(default_factory=dict)382    """Holds any model parameters valid for `create` call not explicitly specified."""383    minimax_api_host: str = Field(384        default="https://api.minimaxi.chat/v1/text/chatcompletion_v2", alias="base_url"385    )386    minimax_group_id: Optional[str] = Field(default=None, alias="group_id")387    """[DEPRECATED, keeping it for for backward compatibility] Group Id"""388    minimax_api_key: SecretStr = Field(alias="api_key")389    """Minimax API Key"""390    streaming: bool = False391    """Whether to stream the results or not."""392 393    model_config = ConfigDict(394        populate_by_name=True,395    )396 397    @model_validator(mode="before")398    @classmethod399    def validate_environment(cls, values: Dict) -> Any:400        """Validate that api key and python package exists in environment."""401        values["minimax_api_key"] = convert_to_secret_str(402            get_from_dict_or_env(403                values,404                ["minimax_api_key", "api_key"],405                "MINIMAX_API_KEY",406            )407        )408 409        default_values = {410            name: field.default411            for name, field in get_fields(cls).items()412            if field.default is not None413        }414        default_values.update(values)415 416        # Get custom api url from environment.417        values["minimax_api_host"] = get_from_dict_or_env(418            values,419            ["minimax_api_host", "base_url"],420            "MINIMAX_API_HOST",421            default_values["minimax_api_host"],422        )423        return values424 425    def _create_chat_result(self, response: Union[dict, BaseModel]) -> ChatResult:426        generations = []427        if not isinstance(response, dict):428            response = response.dict()429        for res in response["choices"]:430            message = _convert_dict_to_message(res["message"])431            generation_info = dict(finish_reason=res.get("finish_reason"))432            generations.append(433                ChatGeneration(message=message, generation_info=generation_info)434            )435        token_usage = response.get("usage", {})436        llm_output = {437            "token_usage": token_usage,438            "model_name": self.model,439        }440        return ChatResult(generations=generations, llm_output=llm_output)441 442    def _create_payload_parameters(443        self, messages: List[BaseMessage], is_stream: bool = False, **kwargs: Any444    ) -> Dict[str, Any]:445        """Create API request body parameters."""446        message_dicts = [_convert_message_to_dict(m) for m in messages]447        payload = self._default_params448        payload["messages"] = message_dicts449 450        self._reformat_function_parameters(kwargs.get("tools", {}))451        payload.update(**kwargs)452 453        if is_stream:454            payload["stream"] = True455 456        return payload457 458    @staticmethod459    def _reformat_function_parameters(tools_arg: Dict[Any, Any]) -> None:460        """Reformat the function parameters to strings."""461        for tool_arg in tools_arg:462            if tool_arg["type"] == "function" and not isinstance(463                tool_arg["function"]["parameters"], str464            ):465                tool_arg["function"]["parameters"] = json.dumps(466                    tool_arg["function"]["parameters"]467                )468 469    def _generate(470        self,471        messages: List[BaseMessage],472        stop: Optional[List[str]] = None,473        run_manager: Optional[CallbackManagerForLLMRun] = None,474        stream: Optional[bool] = None,475        **kwargs: Any,476    ) -> ChatResult:477        """Generate next turn in the conversation.478        Args:479            messages: The history of the conversation as a list of messages. Code chat480                does not support context.481            stop: The list of stop words (optional).482            run_manager: The CallbackManager for LLM run, it's not used at the moment.483            stream: Whether to stream the results or not.484 485        Returns:486            The ChatResult that contains outputs generated by the model.487 488        Raises:489            ValueError: if the last message in the list is not from human.490        """491        if not messages:492            raise ValueError(493                "You should provide at least one message to start the chat!"494            )495        is_stream = stream if stream is not None else self.streaming496        if is_stream:497            stream_iter = self._stream(498                messages, stop=stop, run_manager=run_manager, **kwargs499            )500            return generate_from_stream(stream_iter)501        payload = self._create_payload_parameters(messages, **kwargs)502        api_key = ""503        if self.minimax_api_key is not None:504            api_key = self.minimax_api_key.get_secret_value()505        headers = {506            "Authorization": f"Bearer {api_key}",507            "Content-Type": "application/json",508        }509        import httpx510 511        with httpx.Client(headers=headers, timeout=60) as client:512            response = client.post(self.minimax_api_host, json=payload)513            response.raise_for_status()514        final_response = response.json()515        if (516            "base_resp" in final_response517            and "status_msg" in final_response["base_resp"]518            and final_response["base_resp"]["status_msg"] == "invalid api key"519        ):520            raise Exception("Invalid API Key Provided")521        return self._create_chat_result(response.json())522 523    def _stream(524        self,525        messages: List[BaseMessage],526        stop: Optional[List[str]] = None,527        run_manager: Optional[CallbackManagerForLLMRun] = None,528        **kwargs: Any,529    ) -> Iterator[ChatGenerationChunk]:530        """Stream the chat response in chunks."""531        payload = self._create_payload_parameters(messages, is_stream=True, **kwargs)532        api_key = ""533        if self.minimax_api_key is not None:534            api_key = self.minimax_api_key.get_secret_value()535        headers = {536            "Authorization": f"Bearer {api_key}",537            "Content-Type": "application/json",538        }539        import httpx540 541        with httpx.Client(headers=headers, timeout=60) as client:542            with connect_httpx_sse(543                client, "POST", self.minimax_api_host, json=payload544            ) as event_source:545                for sse in event_source.iter_sse():546                    chunk = json.loads(sse.data)547                    if len(chunk["choices"]) == 0:548                        continue549                    choice = chunk["choices"][0]550                    chunk = _convert_delta_to_message_chunk(551                        choice["delta"], AIMessageChunk552                    )553                    finish_reason = choice.get("finish_reason", None)554 555                    generation_info = (556                        {"finish_reason": finish_reason}557                        if finish_reason is not None558                        else None559                    )560                    chunk = ChatGenerationChunk(561                        message=chunk, generation_info=generation_info562                    )563                    if run_manager:564                        run_manager.on_llm_new_token(chunk.text, chunk=chunk)565                    yield chunk566 567                    if finish_reason is not None:568                        break569 570    async def _agenerate(571        self,572        messages: List[BaseMessage],573        stop: Optional[List[str]] = None,574        run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,575        stream: Optional[bool] = None,576        **kwargs: Any,577    ) -> ChatResult:578        if not messages:579            raise ValueError(580                "You should provide at least one message to start the chat!"581            )582        is_stream = stream if stream is not None else self.streaming583        if is_stream:584            stream_iter = self._astream(585                messages, stop=stop, run_manager=run_manager, **kwargs586            )587            return await agenerate_from_stream(stream_iter)588        payload = self._create_payload_parameters(messages, **kwargs)589        api_key = ""590        if self.minimax_api_key is not None:591            api_key = self.minimax_api_key.get_secret_value()592        headers = {593            "Authorization": f"Bearer {api_key}",594            "Content-Type": "application/json",595        }596        import httpx597 598        async with httpx.AsyncClient(headers=headers, timeout=60) as client:599            response = await client.post(self.minimax_api_host, json=payload)600            response.raise_for_status()601        return self._create_chat_result(response.json())602 603    async def _astream(604        self,605        messages: List[BaseMessage],606        stop: Optional[List[str]] = None,607        run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,608        **kwargs: Any,609    ) -> AsyncIterator[ChatGenerationChunk]:610        payload = self._create_payload_parameters(messages, is_stream=True, **kwargs)611        api_key = ""612        if self.minimax_api_key is not None:613            api_key = self.minimax_api_key.get_secret_value()614        headers = {615            "Authorization": f"Bearer {api_key}",616            "Content-Type": "application/json",617        }618        import httpx619 620        async with httpx.AsyncClient(headers=headers, timeout=60) as client:621            async with aconnect_httpx_sse(622                client, "POST", self.minimax_api_host, json=payload623            ) as event_source:624                async for sse in event_source.aiter_sse():625                    chunk = json.loads(sse.data)626                    if len(chunk["choices"]) == 0:627                        continue628                    choice = chunk["choices"][0]629                    chunk = _convert_delta_to_message_chunk(630                        choice["delta"], AIMessageChunk631                    )632                    finish_reason = choice.get("finish_reason", None)633 634                    generation_info = (635                        {"finish_reason": finish_reason}636                        if finish_reason is not None637                        else None638                    )639                    chunk = ChatGenerationChunk(640                        message=chunk, generation_info=generation_info641                    )642                    if run_manager:643                        await run_manager.on_llm_new_token(chunk.text, chunk=chunk)644                    yield chunk645 646                    if finish_reason is not None:647                        break648 649    def bind_tools(650        self,651        tools: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]],652        **kwargs: Any,653    ) -> Runnable[LanguageModelInput, AIMessage]:654        """Bind tool-like objects to this chat model.655 656        Args:657            tools: A list of tool definitions to bind to this chat model.658                Can be a dictionary, pydantic model, callable, or BaseTool. Pydantic659                models, callables, and BaseTools will be automatically converted to660                their schema dictionary representation.661            **kwargs: Any additional parameters to pass to the662                :class: `~langchain.runnable.Runnable` constructor.663        """664 665        formatted_tools = [convert_to_openai_tool(tool) for tool in tools]666        return super().bind(tools=formatted_tools, **kwargs)667 668    def with_structured_output(669        self,670        schema: Union[Dict, Type[BaseModel]],671        *,672        include_raw: bool = False,673        **kwargs: Any,674    ) -> Runnable[LanguageModelInput, Union[Dict, BaseModel]]:675        """Model wrapper that returns outputs formatted to match the given schema.676 677        Args:678            schema: The output schema as a dict or a Pydantic class. If a Pydantic class679                then the model output will be an object of that class. If a dict then680                the model output will be a dict. With a Pydantic class the returned681                attributes will be validated, whereas with a dict they will not be. If682                `method` is "function_calling" and `schema` is a dict, then the dict683                must match the OpenAI function-calling spec.684            include_raw: If False then only the parsed structured output is returned. If685                an error occurs during model output parsing it will be raised. If True686                then both the raw model response (a BaseMessage) and the parsed model687                response will be returned. If an error occurs during output parsing it688                will be caught and returned as well. The final output is always a dict689                with keys "raw", "parsed", and "parsing_error".690 691        Returns:692            A Runnable that takes any ChatModel input and returns as output:693 694                If include_raw is True then a dict with keys:695                    raw: BaseMessage696                    parsed: Optional[_DictOrPydantic]697                    parsing_error: Optional[BaseException]698 699                If include_raw is False then just _DictOrPydantic is returned,700                where _DictOrPydantic depends on the schema:701 702                If schema is a Pydantic class then _DictOrPydantic is the Pydantic703                    class.704 705                If schema is a dict then _DictOrPydantic is a dict.706 707        Example: Function-calling, Pydantic schema (method="function_calling", include_raw=False):708            .. code-block:: python709 710                from langchain_community.chat_models import MiniMaxChat711                from pydantic import BaseModel712 713                class AnswerWithJustification(BaseModel):714                    '''An answer to the user question along with justification for the answer.'''715                    answer: str716                    justification: str717 718                llm = MiniMaxChat()719                structured_llm = llm.with_structured_output(AnswerWithJustification)720 721                structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers")722 723                # -> AnswerWithJustification(724                #     answer='A pound of bricks and a pound of feathers weigh the same.',725                #     justification='The weight of the feathers is much less dense than the weight of the bricks, but since both weigh one pound, they weigh the same.'726                # )727 728        Example: Function-calling, Pydantic schema (method="function_calling", include_raw=True):729            .. code-block:: python730 731                from langchain_community.chat_models import MiniMaxChat732                from pydantic import BaseModel733 734                class AnswerWithJustification(BaseModel):735                    '''An answer to the user question along with justification for the answer.'''736                    answer: str737                    justification: str738 739                llm = MiniMaxChat()740                structured_llm = llm.with_structured_output(AnswerWithJustification, include_raw=True)741 742                structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers")743 744                # -> {745                #     'raw': AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_function_8953642285', 'type': 'function', 'function': {'name': 'AnswerWithJustification', 'arguments': '{"answer": "A pound of bricks and a pound of feathers weigh the same.", "justification": "The weight of the feathers is much less dense than the weight of the bricks, but since both weigh one pound, they weigh the same."}'}}]}, response_metadata={'token_usage': {'total_tokens': 257}, 'model_name': 'abab6.5-chat', 'finish_reason': 'tool_calls'}, id='run-d897e037-2796-49f5-847e-f9f69dd390db-0', tool_calls=[{'name': 'AnswerWithJustification', 'args': {'answer': 'A pound of bricks and a pound of feathers weigh the same.', 'justification': 'The weight of the feathers is much less dense than the weight of the bricks, but since both weigh one pound, they weigh the same.'}, 'id': 'call_function_8953642285', 'type': 'tool_call'}]),746                #     'parsed': AnswerWithJustification(answer='A pound of bricks and a pound of feathers weigh the same.', justification='The weight of the feathers is much less dense than the weight of the bricks, but since both weigh one pound, they weigh the same.'),747                #     'parsing_error': None748                # }749 750        Example: Function-calling, dict schema (method="function_calling", include_raw=False):751            .. code-block:: python752 753                from langchain_community.chat_models import MiniMaxChat754                from pydantic import BaseModel755                from langchain_core.utils.function_calling import convert_to_openai_tool756 757                class AnswerWithJustification(BaseModel):758                    '''An answer to the user question along with justification for the answer.'''759                    answer: str760                    justification: str761 762                dict_schema = convert_to_openai_tool(AnswerWithJustification)763                llm = MiniMaxChat()764                structured_llm = llm.with_structured_output(dict_schema)765 766                structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers")767 768                # -> {769                #     'answer': 'A pound of bricks and a pound of feathers both weigh the same, which is a pound.',770                #     'justification': 'The difference is that bricks are much denser than feathers, so a pound of bricks will take up much less space than a pound of feathers.'771                # }772        """  # noqa: E501773        if kwargs:774            raise ValueError(f"Received unsupported arguments {kwargs}")775        is_pydantic_schema = isinstance(schema, type) and issubclass(schema, BaseModel)776        llm = self.bind_tools([schema])777        if is_pydantic_schema:778            output_parser: OutputParserLike = PydanticToolsParser(779                tools=[schema],  # type: ignore[list-item]780                first_tool_only=True,781            )782        else:783            key_name = convert_to_openai_tool(schema)["function"]["name"]784            output_parser = JsonOutputKeyToolsParser(785                key_name=key_name, first_tool_only=True786            )787 788        if include_raw:789            parser_assign = RunnablePassthrough.assign(790                parsed=itemgetter("raw") | output_parser, parsing_error=lambda _: None791            )792            parser_none = RunnablePassthrough.assign(parsed=lambda _: None)793            parser_with_fallback = parser_assign.with_fallbacks(794                [parser_none], exception_key="parsing_error"795            )796            return RunnableMap(raw=llm) | parser_with_fallback797        else:798            return llm | output_parser799 
codekingpro/portable-devtools · Team Ai