[docs]classChatDeepSeek(BaseChatOpenAI):"""DeepSeek chat model integration to access models hosted in DeepSeek's API. Setup: Install ``langchain-deepseek`` and set environment variable ``DEEPSEEK_API_KEY``. .. code-block:: bash pip install -U langchain-deepseek export DEEPSEEK_API_KEY="your-api-key" Key init args — completion params: model: str Name of DeepSeek model to use, e.g. "deepseek-chat". temperature: float Sampling temperature. max_tokens: Optional[int] Max number of tokens to generate. Key init args — client params: timeout: Optional[float] Timeout for requests. max_retries: int Max number of retries. api_key: Optional[str] DeepSeek API key. If not passed in will be read from env var DEEPSEEK_API_KEY. See full list of supported init args and their descriptions in the params section. Instantiate: .. code-block:: python from langchain_deepseek import ChatDeepSeek llm = ChatDeepSeek( model="...", temperature=0, max_tokens=None, timeout=None, max_retries=2, # api_key="...", # other params... ) Invoke: .. code-block:: python messages = [ ("system", "You are a helpful translator. Translate the user sentence to French."), ("human", "I love programming."), ] llm.invoke(messages) Stream: .. code-block:: python for chunk in llm.stream(messages): print(chunk.text(), end="") .. code-block:: python stream = llm.stream(messages) full = next(stream) for chunk in stream: full += chunk full Async: .. code-block:: python await llm.ainvoke(messages) # stream: # async for chunk in (await llm.astream(messages)) # batch: # await llm.abatch([messages]) Tool calling: .. code-block:: python from pydantic import BaseModel, Field class GetWeather(BaseModel): '''Get the current weather in a given location''' location: str = Field(..., description="The city and state, e.g. San Francisco, CA") class GetPopulation(BaseModel): '''Get the current population in a given location''' location: str = Field(..., description="The city and state, e.g. San Francisco, CA") llm_with_tools = llm.bind_tools([GetWeather, GetPopulation]) ai_msg = llm_with_tools.invoke("Which city is hotter today and which is bigger: LA or NY?") ai_msg.tool_calls See ``ChatDeepSeek.bind_tools()`` method for more. Structured output: .. code-block:: python from typing import Optional from pydantic import BaseModel, Field class Joke(BaseModel): '''Joke to tell user.''' setup: str = Field(description="The setup of the joke") punchline: str = Field(description="The punchline to the joke") rating: Optional[int] = Field(description="How funny the joke is, from 1 to 10") structured_llm = llm.with_structured_output(Joke) structured_llm.invoke("Tell me a joke about cats") See ``ChatDeepSeek.with_structured_output()`` for more. Token usage: .. code-block:: python ai_msg = llm.invoke(messages) ai_msg.usage_metadata .. code-block:: python {'input_tokens': 28, 'output_tokens': 5, 'total_tokens': 33} Response metadata .. code-block:: python ai_msg = llm.invoke(messages) ai_msg.response_metadata """# noqa: E501model_name:str=Field(alias="model")"""The name of the model"""api_key:Optional[SecretStr]=Field(default_factory=secret_from_env("DEEPSEEK_API_KEY",default=None))"""DeepSeek API key"""api_base:str=Field(default_factory=from_env("DEEPSEEK_API_BASE",default=DEFAULT_API_BASE))"""DeepSeek API base URL"""model_config=ConfigDict(populate_by_name=True)@propertydef_llm_type(self)->str:"""Return type of chat model."""return"chat-deepseek"@propertydeflc_secrets(self)->Dict[str,str]:"""A map of constructor argument names to secret ids."""return{"api_key":"DEEPSEEK_API_KEY"}@model_validator(mode="after")defvalidate_environment(self)->Self:ifself.api_base==DEFAULT_API_BASEandnot(self.api_keyandself.api_key.get_secret_value()):raiseValueError("If using default api base, DEEPSEEK_API_KEY must be set.")client_params:dict={k:vfork,vin{"api_key":self.api_key.get_secret_value()ifself.api_keyelseNone,"base_url":self.api_base,"timeout":self.request_timeout,"max_retries":self.max_retries,"default_headers":self.default_headers,"default_query":self.default_query,}.items()ifvisnotNone}ifnot(self.clientorNone):sync_specific:dict={"http_client":self.http_client}self.root_client=openai.OpenAI(**client_params,**sync_specific)self.client=self.root_client.chat.completionsifnot(self.async_clientorNone):async_specific:dict={"http_client":self.http_async_client}self.root_async_client=openai.AsyncOpenAI(**client_params,**async_specific,)self.async_client=self.root_async_client.chat.completionsreturnselfdef_create_chat_result(self,response:Union[dict,openai.BaseModel],generation_info:Optional[Dict]=None,)->ChatResult:rtn=super()._create_chat_result(response,generation_info)ifnotisinstance(response,openai.BaseModel):returnrtnifhasattr(response.choices[0].message,"reasoning_content"):# type: ignorertn.generations[0].message.additional_kwargs["reasoning_content"]=(response.choices[0].message.reasoning_content# type: ignore)# Handle use via OpenRouterelifhasattr(response.choices[0].message,"model_extra"):# type: ignoremodel_extra=response.choices[0].message.model_extra# type: ignoreifisinstance(model_extra,dict)and(reasoning:=model_extra.get("reasoning")):rtn.generations[0].message.additional_kwargs["reasoning_content"]=(reasoning)returnrtndef_convert_chunk_to_generation_chunk(self,chunk:dict,default_chunk_class:Type,base_generation_info:Optional[Dict],)->Optional[ChatGenerationChunk]:generation_chunk=super()._convert_chunk_to_generation_chunk(chunk,default_chunk_class,base_generation_info,)if(choices:=chunk.get("choices"))andgeneration_chunk:top=choices[0]ifisinstance(generation_chunk.message,AIMessageChunk):ifreasoning_content:=top.get("delta",{}).get("reasoning_content"):generation_chunk.message.additional_kwargs["reasoning_content"]=(reasoning_content)# Handle use via OpenRouterelifreasoning:=top.get("delta",{}).get("reasoning"):generation_chunk.message.additional_kwargs["reasoning_content"]=(reasoning)returngeneration_chunkdef_stream(self,messages:List[BaseMessage],stop:Optional[List[str]]=None,run_manager:Optional[CallbackManagerForLLMRun]=None,**kwargs:Any,)->Iterator[ChatGenerationChunk]:try:yield fromsuper()._stream(messages,stop=stop,run_manager=run_manager,**kwargs)exceptJSONDecodeErrorase:raiseJSONDecodeError("DeepSeek API returned an invalid response. ""Please check the API status and try again.",e.doc,e.pos,)fromedef_generate(self,messages:List[BaseMessage],stop:Optional[List[str]]=None,run_manager:Optional[CallbackManagerForLLMRun]=None,**kwargs:Any,)->ChatResult:try:returnsuper()._generate(messages,stop=stop,run_manager=run_manager,**kwargs)exceptJSONDecodeErrorase:raiseJSONDecodeError("DeepSeek API returned an invalid response. ""Please check the API status and try again.",e.doc,e.pos,)frome
[docs]defwith_structured_output(self,schema:Optional[_DictOrPydanticClass]=None,*,method:Literal["function_calling","json_mode","json_schema"]="function_calling",include_raw:bool=False,strict:Optional[bool]=None,**kwargs:Any,)->Runnable[LanguageModelInput,_DictOrPydantic]:"""Model wrapper that returns outputs formatted to match the given schema. Args: schema: The output schema. Can be passed in as: - an OpenAI function/tool schema, - a JSON Schema, - a TypedDict class (support added in 0.1.20), - or a Pydantic class. If ``schema`` is a Pydantic class then the model output will be a Pydantic instance of that class, and the model-generated fields will be validated by the Pydantic class. Otherwise the model output will be a dict and will not be validated. See :meth:`langchain_core.utils.function_calling.convert_to_openai_tool` for more on how to properly specify types and descriptions of schema fields when specifying a Pydantic or TypedDict class. method: The method for steering model generation, one of: - "function_calling": Uses DeekSeek's `tool-calling features <https://api-docs.deepseek.com/guides/function_calling>`_. - "json_mode": Uses DeepSeek's `JSON mode feature <https://api-docs.deepseek.com/guides/json_mode>`_. .. versionchanged:: 0.1.3 Added support for ``"json_mode"``. include_raw: If False then only the parsed structured output is returned. If an error occurs during model output parsing it will be raised. If True then both the raw model response (a BaseMessage) and the parsed model response will be returned. If an error occurs during output parsing it will be caught and returned as well. The final output is always a dict with keys "raw", "parsed", and "parsing_error". kwargs: Additional keyword args aren't supported. Returns: A Runnable that takes same inputs as a :class:`langchain_core.language_models.chat.BaseChatModel`. | If ``include_raw`` is False and ``schema`` is a Pydantic class, Runnable outputs an instance of ``schema`` (i.e., a Pydantic object). Otherwise, if ``include_raw`` is False then Runnable outputs a dict. | If ``include_raw`` is True, then Runnable outputs a dict with keys: - "raw": BaseMessage - "parsed": None if there was a parsing error, otherwise the type depends on the ``schema`` as described above. - "parsing_error": Optional[BaseException] """# noqa: E501# Some applications require that incompatible parameters (e.g., unsupported# methods) be handled.ifmethod=="json_schema":method="function_calling"returnsuper().with_structured_output(schema,method=method,include_raw=include_raw,strict=strict,**kwargs)