-
Notifications
You must be signed in to change notification settings - Fork 41
Expand file tree
/
Copy pathllm-deepseek.el
More file actions
105 lines (91 loc) · 4.77 KB
/
llm-deepseek.el
File metadata and controls
105 lines (91 loc) · 4.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
;;; llm-deepseek.el --- llm module for integrating with DeepSeek's service -*- lexical-binding: t; package-lint-main-file: "llm.el"; byte-compile-docstring-max-column: 200-*-
;; Copyright (c) 2025 Free Software Foundation, Inc.
;; Author: Andrew Hyatt <ahyatt@gmail.com>
;; Homepage: https://github.com/ahyatt/llm
;; SPDX-License-Identifier: GPL-3.0-or-later
;;
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 3 of the
;; License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This module provides integration with DeepSeek's service.
;;; Code:
(require 'llm)
(require 'llm-openai)
(require 'llm-models)
(require 'cl-lib)
(cl-defstruct (llm-deepseek
(:include llm-openai-compatible
(url "https://api.deepseek.com")
(chat-model "deepseek-chat"))
(:constructor make-llm-deepseek
(&key default-chat-temperature
default-chat-max-tokens
default-chat-non-standard-params
((:key raw-key))
(chat-model "deepseek-chat")
(embedding-model "unset")
(url "https://api.deepseek.com")
&aux
(key (llm-provider-utils--wrap-key raw-key))))))
(cl-defmethod llm-nonfree-message-info ((_ llm-deepseek))
"Location for the terms of service and privacy policy."
"https://cdn.deepseek.com/policies/en-US/deepseek-terms-of-use.html")
(cl-defmethod llm-provider-extract-reasoning ((_ llm-deepseek) response)
(when-let* ((choices (assoc-default 'choices response))
(message (when (> (length choices) 0)
(assoc-default 'message (aref choices 0)))))
(assoc-default 'reasoning_content message)))
(defun llm-deepseek--get-partial-chat-response (response)
"Return the text and reasoning in RESPONSE.
RESPONSE can be nil if the response is complete."
(when response
(let* ((choices (assoc-default 'choices response))
(usage (assoc-default 'usage response))
(delta (when (> (length choices) 0)
(assoc-default 'delta (aref choices 0))))
(content (llm-provider-utils-json-val
(assoc-default 'content delta)))
(reasoning (llm-provider-utils-json-val
(assoc-default 'reasoning_content delta))))
(append (when content (list :text content))
(when reasoning (list :reasoning reasoning))
(when (and usage (not (eq usage :null)))
(list :input-tokens (assoc-default 'prompt_tokens usage)
:output-tokens (assoc-default 'completion_tokens usage)))))))
(cl-defmethod llm-provider-streaming-media-handler ((_ llm-deepseek) receiver _)
(cons 'text/event-stream
(plz-event-source:text/event-stream
:events `((message
.
,(lambda (event)
(let ((data (plz-event-source-event-data event)))
(unless (equal data "[DONE]")
(when-let* ((response (llm-deepseek--get-partial-chat-response
(json-parse-string data :object-type 'alist))))
(funcall receiver response))))))))))
(cl-defmethod llm-capabilities ((provider llm-deepseek))
(append '(streaming model-list)
(when-let* ((model (llm-models-match (llm-deepseek-chat-model provider))))
(llm-model-capabilities model))))
(cl-defmethod llm-openai--build-reasoning ((_ llm-deepseek) prompt)
(when (llm-chat-prompt-reasoning prompt)
(pcase (llm-chat-prompt-reasoning prompt)
('none '(:thinking (:type "disabled")))
('light '(:thinking (:type "enabled") :reasoning_effort "low"))
('medium '(:thinking (:type "enabled") :reasoning_effort "medium"))
('maximum '(:thinking (:type "enabled") :reasoning_effort "high"))
(_ (signal 'llm-not-supported
(list (format "Unknown reasoning effort option: %s"
(llm-chat-prompt-reasoning prompt))))))))
(provide 'llm-deepseek)
;;; llm-deepseek.el ends here