mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-09-18 01:26:37 +08:00
feat: add support for extended reasoning in Claude 3.7 model
This commit is contained in:
parent
95527d76ef
commit
3a8924d7af
@ -36,8 +36,8 @@ func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Request, meta *me
|
|||||||
|
|
||||||
// https://x.com/alexalbert__/status/1812921642143900036
|
// https://x.com/alexalbert__/status/1812921642143900036
|
||||||
// claude-3-5-sonnet can support 8k context
|
// claude-3-5-sonnet can support 8k context
|
||||||
if strings.HasPrefix(meta.ActualModelName, "claude-3-5-sonnet") {
|
if strings.HasPrefix(meta.ActualModelName, "claude-3-7-sonnet") {
|
||||||
req.Header.Set("anthropic-beta", "max-tokens-3-5-sonnet-2024-07-15")
|
req.Header.Set("anthropic-beta", "output-128k-2025-02-19")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -4,11 +4,12 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/songquanpeng/one-api/common/render"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/songquanpeng/one-api/common/render"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/helper"
|
"github.com/songquanpeng/one-api/common/helper"
|
||||||
@ -61,6 +62,7 @@ func ConvertRequest(textRequest model.GeneralOpenAIRequest) *Request {
|
|||||||
TopK: textRequest.TopK,
|
TopK: textRequest.TopK,
|
||||||
Stream: textRequest.Stream,
|
Stream: textRequest.Stream,
|
||||||
Tools: claudeTools,
|
Tools: claudeTools,
|
||||||
|
Thinking: textRequest.Thinking,
|
||||||
}
|
}
|
||||||
if len(claudeTools) > 0 {
|
if len(claudeTools) > 0 {
|
||||||
claudeToolChoice := struct {
|
claudeToolChoice := struct {
|
||||||
@ -149,6 +151,7 @@ func ConvertRequest(textRequest model.GeneralOpenAIRequest) *Request {
|
|||||||
func StreamResponseClaude2OpenAI(claudeResponse *StreamResponse) (*openai.ChatCompletionsStreamResponse, *Response) {
|
func StreamResponseClaude2OpenAI(claudeResponse *StreamResponse) (*openai.ChatCompletionsStreamResponse, *Response) {
|
||||||
var response *Response
|
var response *Response
|
||||||
var responseText string
|
var responseText string
|
||||||
|
var reasoningText string
|
||||||
var stopReason string
|
var stopReason string
|
||||||
tools := make([]model.Tool, 0)
|
tools := make([]model.Tool, 0)
|
||||||
|
|
||||||
@ -158,6 +161,10 @@ func StreamResponseClaude2OpenAI(claudeResponse *StreamResponse) (*openai.ChatCo
|
|||||||
case "content_block_start":
|
case "content_block_start":
|
||||||
if claudeResponse.ContentBlock != nil {
|
if claudeResponse.ContentBlock != nil {
|
||||||
responseText = claudeResponse.ContentBlock.Text
|
responseText = claudeResponse.ContentBlock.Text
|
||||||
|
if claudeResponse.ContentBlock.Thinking != nil {
|
||||||
|
reasoningText = *claudeResponse.ContentBlock.Thinking
|
||||||
|
}
|
||||||
|
|
||||||
if claudeResponse.ContentBlock.Type == "tool_use" {
|
if claudeResponse.ContentBlock.Type == "tool_use" {
|
||||||
tools = append(tools, model.Tool{
|
tools = append(tools, model.Tool{
|
||||||
Id: claudeResponse.ContentBlock.Id,
|
Id: claudeResponse.ContentBlock.Id,
|
||||||
@ -172,6 +179,10 @@ func StreamResponseClaude2OpenAI(claudeResponse *StreamResponse) (*openai.ChatCo
|
|||||||
case "content_block_delta":
|
case "content_block_delta":
|
||||||
if claudeResponse.Delta != nil {
|
if claudeResponse.Delta != nil {
|
||||||
responseText = claudeResponse.Delta.Text
|
responseText = claudeResponse.Delta.Text
|
||||||
|
if claudeResponse.Delta.Thinking != nil {
|
||||||
|
reasoningText = *claudeResponse.Delta.Thinking
|
||||||
|
}
|
||||||
|
|
||||||
if claudeResponse.Delta.Type == "input_json_delta" {
|
if claudeResponse.Delta.Type == "input_json_delta" {
|
||||||
tools = append(tools, model.Tool{
|
tools = append(tools, model.Tool{
|
||||||
Function: model.Function{
|
Function: model.Function{
|
||||||
@ -189,9 +200,20 @@ func StreamResponseClaude2OpenAI(claudeResponse *StreamResponse) (*openai.ChatCo
|
|||||||
if claudeResponse.Delta != nil && claudeResponse.Delta.StopReason != nil {
|
if claudeResponse.Delta != nil && claudeResponse.Delta.StopReason != nil {
|
||||||
stopReason = *claudeResponse.Delta.StopReason
|
stopReason = *claudeResponse.Delta.StopReason
|
||||||
}
|
}
|
||||||
|
case "thinking_delta":
|
||||||
|
if claudeResponse.Delta != nil && claudeResponse.Delta.Thinking != nil {
|
||||||
|
reasoningText = *claudeResponse.Delta.Thinking
|
||||||
}
|
}
|
||||||
|
case "ping",
|
||||||
|
"message_stop",
|
||||||
|
"content_block_stop":
|
||||||
|
default:
|
||||||
|
logger.SysErrorf("unknown stream response type %q", claudeResponse.Type)
|
||||||
|
}
|
||||||
|
|
||||||
var choice openai.ChatCompletionsStreamResponseChoice
|
var choice openai.ChatCompletionsStreamResponseChoice
|
||||||
choice.Delta.Content = responseText
|
choice.Delta.Content = responseText
|
||||||
|
choice.Delta.Reasoning = &reasoningText
|
||||||
if len(tools) > 0 {
|
if len(tools) > 0 {
|
||||||
choice.Delta.Content = nil // compatible with other OpenAI derivative applications, like LobeOpenAICompatibleFactory ...
|
choice.Delta.Content = nil // compatible with other OpenAI derivative applications, like LobeOpenAICompatibleFactory ...
|
||||||
choice.Delta.ToolCalls = tools
|
choice.Delta.ToolCalls = tools
|
||||||
@ -209,11 +231,15 @@ func StreamResponseClaude2OpenAI(claudeResponse *StreamResponse) (*openai.ChatCo
|
|||||||
|
|
||||||
func ResponseClaude2OpenAI(claudeResponse *Response) *openai.TextResponse {
|
func ResponseClaude2OpenAI(claudeResponse *Response) *openai.TextResponse {
|
||||||
var responseText string
|
var responseText string
|
||||||
if len(claudeResponse.Content) > 0 {
|
var reasoningText string
|
||||||
responseText = claudeResponse.Content[0].Text
|
|
||||||
}
|
|
||||||
tools := make([]model.Tool, 0)
|
tools := make([]model.Tool, 0)
|
||||||
for _, v := range claudeResponse.Content {
|
for _, v := range claudeResponse.Content {
|
||||||
|
reasoningText += v.Text
|
||||||
|
if v.Thinking != nil {
|
||||||
|
reasoningText += *v.Thinking
|
||||||
|
}
|
||||||
|
|
||||||
if v.Type == "tool_use" {
|
if v.Type == "tool_use" {
|
||||||
args, _ := json.Marshal(v.Input)
|
args, _ := json.Marshal(v.Input)
|
||||||
tools = append(tools, model.Tool{
|
tools = append(tools, model.Tool{
|
||||||
@ -231,6 +257,7 @@ func ResponseClaude2OpenAI(claudeResponse *Response) *openai.TextResponse {
|
|||||||
Message: model.Message{
|
Message: model.Message{
|
||||||
Role: "assistant",
|
Role: "assistant",
|
||||||
Content: responseText,
|
Content: responseText,
|
||||||
|
Reasoning: &reasoningText,
|
||||||
Name: nil,
|
Name: nil,
|
||||||
ToolCalls: tools,
|
ToolCalls: tools,
|
||||||
},
|
},
|
||||||
@ -277,6 +304,8 @@ func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusC
|
|||||||
data = strings.TrimPrefix(data, "data:")
|
data = strings.TrimPrefix(data, "data:")
|
||||||
data = strings.TrimSpace(data)
|
data = strings.TrimSpace(data)
|
||||||
|
|
||||||
|
logger.Debugf(c.Request.Context(), "stream <- %q\n", data)
|
||||||
|
|
||||||
var claudeResponse StreamResponse
|
var claudeResponse StreamResponse
|
||||||
err := json.Unmarshal([]byte(data), &claudeResponse)
|
err := json.Unmarshal([]byte(data), &claudeResponse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -344,6 +373,7 @@ func Handler(c *gin.Context, resp *http.Response, promptTokens int, modelName st
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var claudeResponse Response
|
var claudeResponse Response
|
||||||
err = json.Unmarshal(responseBody, &claudeResponse)
|
err = json.Unmarshal(responseBody, &claudeResponse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package anthropic
|
package anthropic
|
||||||
|
|
||||||
|
import "github.com/songquanpeng/one-api/relay/model"
|
||||||
|
|
||||||
// https://docs.anthropic.com/claude/reference/messages_post
|
// https://docs.anthropic.com/claude/reference/messages_post
|
||||||
|
|
||||||
type Metadata struct {
|
type Metadata struct {
|
||||||
@ -22,6 +24,9 @@ type Content struct {
|
|||||||
Input any `json:"input,omitempty"`
|
Input any `json:"input,omitempty"`
|
||||||
Content string `json:"content,omitempty"`
|
Content string `json:"content,omitempty"`
|
||||||
ToolUseId string `json:"tool_use_id,omitempty"`
|
ToolUseId string `json:"tool_use_id,omitempty"`
|
||||||
|
// https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking#implementing-extended-thinking
|
||||||
|
Thinking *string `json:"thinking,omitempty"`
|
||||||
|
Signature *string `json:"signature,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
@ -54,6 +59,7 @@ type Request struct {
|
|||||||
Tools []Tool `json:"tools,omitempty"`
|
Tools []Tool `json:"tools,omitempty"`
|
||||||
ToolChoice any `json:"tool_choice,omitempty"`
|
ToolChoice any `json:"tool_choice,omitempty"`
|
||||||
//Metadata `json:"metadata,omitempty"`
|
//Metadata `json:"metadata,omitempty"`
|
||||||
|
Thinking *model.Thinking `json:"thinking,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Usage struct {
|
type Usage struct {
|
||||||
@ -84,6 +90,8 @@ type Delta struct {
|
|||||||
PartialJson string `json:"partial_json,omitempty"`
|
PartialJson string `json:"partial_json,omitempty"`
|
||||||
StopReason *string `json:"stop_reason"`
|
StopReason *string `json:"stop_reason"`
|
||||||
StopSequence *string `json:"stop_sequence"`
|
StopSequence *string `json:"stop_sequence"`
|
||||||
|
Thinking *string `json:"thinking,omitempty"`
|
||||||
|
Signature *string `json:"signature,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type StreamResponse struct {
|
type StreamResponse struct {
|
||||||
|
@ -73,6 +73,16 @@ type GeneralOpenAIRequest struct {
|
|||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
Provider *openrouter.RequestProvider `json:"provider,omitempty"`
|
Provider *openrouter.RequestProvider `json:"provider,omitempty"`
|
||||||
IncludeReasoning *bool `json:"include_reasoning,omitempty"`
|
IncludeReasoning *bool `json:"include_reasoning,omitempty"`
|
||||||
|
// -------------------------------------
|
||||||
|
// Anthropic
|
||||||
|
// -------------------------------------
|
||||||
|
Thinking *Thinking `json:"thinking,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking#implementing-extended-thinking
|
||||||
|
type Thinking struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
BudgetTokens int `json:"budget_tokens" binding:"omitempty,min=1024"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r GeneralOpenAIRequest) ParseInput() []string {
|
func (r GeneralOpenAIRequest) ParseInput() []string {
|
||||||
|
Loading…
Reference in New Issue
Block a user