branch: elpa/gptel commit a6ee2e07b5f23ea3211141fad7cccb5ccf3fbb57 Author: Karthik Chikmagalur <karthikchikmaga...@gmail.com> Commit: Karthik Chikmagalur <karthikchikmaga...@gmail.com>
gptel-openai: Handle reasoning_content in gptel-openai (#1028) Look for "reasoning_content" fields in the OpenAI-API response parsers. Previously we only looked for "reasoning" fields, delegating "reasoning_content" handling to the separate gptel-deepseek backend. It turns out that OpenAI-compatible APIs are now split on this: - "reasoning": Used by Openrouter and some others - "reasoning_content": Used by vLLM, Llama.cpp, sglang, Deepseek and others A separate Deepseek backend is still required as it has extra requirements for the messages array format, see the implementation of `gptel--parse-buffer' for Deepseek. * gptel-openai.el (gptel-curl--parse-stream, gptel--parse-response): Handle "reasoning_content" field. * gptel-openai-extras.el (gptel-curl--parse-stream, gptel--parse-response): Remove methods specialized to Deepseek for handling "reasoning_content", as the underlying OpenAI implementation will handle this now. --- gptel-openai-extras.el | 31 ------------------------------- gptel-openai.el | 6 ++++-- 2 files changed, 4 insertions(+), 33 deletions(-) diff --git a/gptel-openai-extras.el b/gptel-openai-extras.el index 22064e02578..c45b4a4ab9c 100644 --- a/gptel-openai-extras.el +++ b/gptel-openai-extras.el @@ -278,37 +278,6 @@ parameters." (:copier nil) (:constructor gptel--make-deepseek))) -(cl-defmethod gptel-curl--parse-stream :before ((_backend gptel-deepseek) info) - "Capture reasoning block stream into INFO." - (unless (eq (plist-get info :reasoning-block) 'done) - (save-excursion - (ignore-errors - (catch 'done - (while (re-search-forward "^data:" nil t) - (unless (looking-at-p " *\\[DONE\\]") - (when-let* ((response (gptel--json-read)) - (delta (map-nested-elt response '(:choices 0 :delta)))) - (if-let* ((reasoning (plist-get delta :reasoning_content)) - ((not (eq reasoning :null)))) - ;; :reasoning will be consumed by the gptel-request callback - ;; and reset by the stream filter. - (plist-put info :reasoning - (concat (plist-get info :reasoning) reasoning)) - ;; Done with reasoning if we get non-empty content - (when-let* (((plist-member info :reasoning)) ;Is this a reasoning model? - (content (plist-get delta :content)) ;Started receiving text content? - ((not (or (eq content :null) (string-empty-p content))))) - (plist-put info :reasoning-block t) ;Signal end of reasoning block - (throw 'done t))))))))))) - -(cl-defmethod gptel--parse-response :before ((_backend gptel-deepseek) response info) - "Capture reasoning block in RESPONSE into INFO." - (let* ((choice0 (map-nested-elt response '(:choices 0))) - (message (plist-get choice0 :message)) - (reasoning (plist-get message :reasoning_content))) - (when (and (stringp reasoning) (length> reasoning 0)) - (plist-put info :reasoning reasoning)))) - (cl-defmethod gptel--parse-buffer :around ((_backend gptel-deepseek) _max-entries) "Merge successive prompts in the prompts list that have the same role. diff --git a/gptel-openai.el b/gptel-openai.el index f87642cff36..79bed587dd5 100644 --- a/gptel-openai.el +++ b/gptel-openai.el @@ -240,7 +240,8 @@ information if the stream contains it." (push (plist-get func :arguments) (plist-get info :partial_json))))) ;; Check for reasoning blocks, currently only used by Openrouter (unless (eq (plist-get info :reasoning-block) 'done) - (if-let* ((reasoning-chunk (plist-get delta :reasoning)) ;for Openrouter and co + (if-let* ((reasoning-chunk (or (plist-get delta :reasoning) ;for Openrouter and co + (plist-get delta :reasoning_content))) ;for Deepseek, Llama.cpp ((not (or (eq reasoning-chunk :null) (string-empty-p reasoning-chunk))))) (plist-put info :reasoning (concat (plist-get info :reasoning) reasoning-chunk)) @@ -281,7 +282,8 @@ Mutate state INFO with response metadata." collect call-spec into tool-use finally (plist-put info :tool-use tool-use))) (when (and content (not (or (eq content :null) (string-empty-p content)))) - (when-let* ((reasoning (plist-get message :reasoning)) ;look for reasoning blocks + (when-let* ((reasoning (or (plist-get message :reasoning) ;for Openrouter and co + (plist-get message :reasoning_content))) ;for Deepseek, Llama.cpp ((and (stringp reasoning) (not (string-empty-p reasoning))))) (plist-put info :reasoning reasoning)) content)))