Files
life-echo/api/app/adapters/llm/deepseek.py

96 lines
3.0 KiB
Python
Raw Normal View History

"""DeepSeek / OpenAI-compatible LLM adapter — implements LLMProvider port."""
from collections.abc import AsyncIterator
from langchain_openai import ChatOpenAI
class DeepSeekLLMProvider:
2026-03-20 15:15:35 +08:00
"""LangChain-based LLM adapter for DeepSeek and OpenAI-compatible APIs.
`langchain_llm` Agent / 任务同步调用若需 JSON object 模式请用
`app.core.langchain_llm.bind_json_object_mode` `invoke_json_object` /
`ainvoke_json_object` `api/docs/llm-json-mode.md`勿使用已废弃的
2026-03-20 15:15:35 +08:00
`bind(model_kwargs={"response_format": ...})`
"""
def __init__(
self,
api_key: str,
base_url: str = "https://api.deepseek.com",
model: str = "deepseek-chat",
temperature: float = 0.7,
):
self._default_model = model
self._default_temperature = temperature
kwargs: dict = {
"temperature": temperature,
"model": model,
"api_key": api_key,
}
if base_url:
cleaned = base_url.rstrip("/")
for suffix in ("/v1/chat/completions", "/v1"):
if cleaned.endswith(suffix):
cleaned = cleaned[: -len(suffix)]
kwargs["base_url"] = cleaned
self._llm = ChatOpenAI(**kwargs)
@property
def langchain_llm(self) -> ChatOpenAI:
"""Expose underlying ChatOpenAI for LangChain agent interop (Phase 2 will remove)."""
return self._llm
async def complete(
self,
messages: list[dict],
*,
temperature: float | None = None,
model: str | None = None,
) -> str:
llm = self._get_llm(temperature, model)
lc_messages = _to_langchain_messages(messages)
result = await llm.ainvoke(lc_messages)
return str(result.content)
async def stream(
self,
messages: list[dict],
*,
temperature: float | None = None,
model: str | None = None,
) -> AsyncIterator[str]:
llm = self._get_llm(temperature, model)
lc_messages = _to_langchain_messages(messages)
async for chunk in llm.astream(lc_messages):
if chunk.content:
yield str(chunk.content)
def _get_llm(self, temperature: float | None, model: str | None):
if temperature is None and model is None:
return self._llm
kwargs: dict = {}
if temperature is not None:
kwargs["temperature"] = temperature
if model is not None:
kwargs["model"] = model
return self._llm.bind(**kwargs) if kwargs else self._llm
def _to_langchain_messages(messages: list[dict]) -> list:
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
2026-03-19 14:36:14 +08:00
mapping = {
"system": SystemMessage,
"human": HumanMessage,
"user": HumanMessage,
"ai": AIMessage,
"assistant": AIMessage,
}
result = []
for msg in messages:
cls = mapping.get(msg.get("role", ""), HumanMessage)
result.append(cls(content=msg.get("content", "")))
return result