deepseek_client.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. """
  2. DeepSeek API helpers (OpenAI-compatible SDK).
  3. """
  4. from __future__ import annotations
  5. import os
  6. from typing import Any
  7. from flask import current_app, has_app_context
  8. from openai import OpenAI
  9. DEEPSEEK_DEFAULT_BASE_URL = "https://api.deepseek.com"
  10. def _clean_secret(value: str | None) -> str:
  11. if not value:
  12. return ""
  13. return str(value).strip().strip("\r\n\t")
  14. def get_llm_api_key() -> str:
  15. """Resolve API key: DEEPSEEK_API_KEY first, then LLM_API_KEY / app config."""
  16. candidates = []
  17. if has_app_context():
  18. candidates.extend(
  19. [
  20. current_app.config.get("DEEPSEEK_API_KEY"),
  21. current_app.config.get("LLM_API_KEY"),
  22. ]
  23. )
  24. candidates.extend([os.environ.get("DEEPSEEK_API_KEY"), os.environ.get("LLM_API_KEY")])
  25. for candidate in candidates:
  26. cleaned = _clean_secret(candidate)
  27. if cleaned and cleaned not in {
  28. "replace-with-your-deepseek-api-key",
  29. "your-api-key",
  30. }:
  31. return cleaned
  32. return ""
  33. def normalize_llm_base_url(raw: str | None = None) -> str:
  34. """Normalize DeepSeek OpenAI-compatible base URL for SDK usage."""
  35. if raw is None:
  36. if has_app_context():
  37. raw = str(current_app.config.get("LLM_BASE_URL") or "")
  38. if not raw:
  39. raw = os.environ.get("LLM_BASE_URL", DEEPSEEK_DEFAULT_BASE_URL)
  40. url = _clean_secret(raw) or DEEPSEEK_DEFAULT_BASE_URL
  41. url = url.rstrip("/")
  42. # OpenAI SDK 会自动追加 /v1;若 env 已带 /v1,去掉以避免重复
  43. if url.endswith("/v1"):
  44. url = url[:-3]
  45. return url or DEEPSEEK_DEFAULT_BASE_URL
  46. def get_llm_base_url() -> str:
  47. return normalize_llm_base_url()
  48. def get_llm_chat_completions_url() -> str:
  49. """Return the HTTP endpoint for raw requests.post() callers."""
  50. return f"{get_llm_base_url()}/v1/chat/completions"
  51. def get_llm_model() -> str:
  52. raw = ""
  53. if has_app_context():
  54. raw = str(current_app.config.get("LLM_MODEL_NAME") or "")
  55. if not raw:
  56. raw = os.environ.get("LLM_MODEL_NAME", "deepseek-chat")
  57. return _clean_secret(raw) or "deepseek-chat"
  58. def create_llm_client() -> OpenAI:
  59. api_key = get_llm_api_key()
  60. if not api_key:
  61. raise ValueError(
  62. "DeepSeek API Key 未配置,请在 /etc/dataops-platform/dataops.env 中设置 DEEPSEEK_API_KEY"
  63. )
  64. return OpenAI(api_key=api_key, base_url=get_llm_base_url())
  65. def chat_completions_create(
  66. client: OpenAI,
  67. *,
  68. messages: list[dict[str, str]],
  69. temperature: float = 0.7,
  70. max_tokens: int = 1024,
  71. use_thinking: bool = False,
  72. **kwargs: Any,
  73. ):
  74. """Create a chat completion; optional DeepSeek thinking mode for complex tasks."""
  75. create_kwargs: dict[str, Any] = {
  76. "model": get_llm_model(),
  77. "messages": messages,
  78. "stream": False,
  79. "temperature": temperature,
  80. "max_tokens": max_tokens,
  81. **kwargs,
  82. }
  83. if use_thinking:
  84. create_kwargs["reasoning_effort"] = current_app.config.get(
  85. "LLM_REASONING_EFFORT", "high"
  86. )
  87. create_kwargs["extra_body"] = {"thinking": {"type": "enabled"}}
  88. return client.chat.completions.create(**create_kwargs)