Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
4597816a
authored
Feb 23, 2025
by
1808837298@qq.com
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: Add thinking-to-content conversion for stream responses
parent
991b6f8b
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
86 additions
and
15 deletions
+86
-15
constant/channel_setting.go
+1
-0
docs/channel/other_setting.md
+5
-0
dto/openai_response.go
+18
-0
relay/channel/openai/relay-openai.go
+57
-12
relay/common/relay_info.go
+5
-3
No files found.
constant/channel_setting.go
View file @
4597816a
...
@@ -3,4 +3,5 @@ package constant
...
@@ -3,4 +3,5 @@ package constant
var
(
var
(
ForceFormat
=
"force_format"
// ForceFormat 强制格式化为OpenAI格式
ForceFormat
=
"force_format"
// ForceFormat 强制格式化为OpenAI格式
ChanelSettingProxy
=
"proxy"
// Proxy 代理
ChanelSettingProxy
=
"proxy"
// Proxy 代理
ChannelSettingThinkingToContent
=
"thinking_to_content"
// ThinkingToContent
)
)
docs/channel/other_setting.md
View file @
4597816a
...
@@ -10,6 +10,10 @@
...
@@ -10,6 +10,10 @@
-
用于配置网络代理
-
用于配置网络代理
-
类型为字符串,填写代理地址(例如 socks5 协议的代理地址)
-
类型为字符串,填写代理地址(例如 socks5 协议的代理地址)
3.
thinking_to_content
-
用于标识是否将思考内容
`reasoning_conetnt`
转换为
`<think>`
标签拼接到内容中返回
-
类型为布尔值,设置为 true 时启用思考内容转换
--------------------------------------------------------------
--------------------------------------------------------------
## JSON 格式示例
## JSON 格式示例
...
@@ -19,6 +23,7 @@
...
@@ -19,6 +23,7 @@
```
json
```
json
{
{
"force_format"
:
true
,
"force_format"
:
true
,
"thinking_to_content"
:
true
,
"proxy"
:
"socks5://xxxxxxx"
"proxy"
:
"socks5://xxxxxxx"
}
}
```
```
...
...
dto/openai_response.go
View file @
4597816a
...
@@ -86,6 +86,10 @@ func (c *ChatCompletionsStreamResponseChoiceDelta) GetReasoningContent() string
...
@@ -86,6 +86,10 @@ func (c *ChatCompletionsStreamResponseChoiceDelta) GetReasoningContent() string
return
*
c
.
ReasoningContent
return
*
c
.
ReasoningContent
}
}
func
(
c
*
ChatCompletionsStreamResponseChoiceDelta
)
SetReasoningContent
(
s
string
)
{
c
.
ReasoningContent
=
&
s
}
type
ToolCall
struct
{
type
ToolCall
struct
{
// Index is not nil only in chat completion chunk object
// Index is not nil only in chat completion chunk object
Index
*
int
`json:"index,omitempty"`
Index
*
int
`json:"index,omitempty"`
...
@@ -116,6 +120,20 @@ type ChatCompletionsStreamResponse struct {
...
@@ -116,6 +120,20 @@ type ChatCompletionsStreamResponse struct {
Usage
*
Usage
`json:"usage"`
Usage
*
Usage
`json:"usage"`
}
}
func
(
c
*
ChatCompletionsStreamResponse
)
Copy
()
*
ChatCompletionsStreamResponse
{
choices
:=
make
([]
ChatCompletionsStreamResponseChoice
,
len
(
c
.
Choices
))
copy
(
choices
,
c
.
Choices
)
return
&
ChatCompletionsStreamResponse
{
Id
:
c
.
Id
,
Object
:
c
.
Object
,
Created
:
c
.
Created
,
Model
:
c
.
Model
,
SystemFingerprint
:
c
.
SystemFingerprint
,
Choices
:
choices
,
Usage
:
c
.
Usage
,
}
}
func
(
c
*
ChatCompletionsStreamResponse
)
GetSystemFingerprint
()
string
{
func
(
c
*
ChatCompletionsStreamResponse
)
GetSystemFingerprint
()
string
{
if
c
.
SystemFingerprint
==
nil
{
if
c
.
SystemFingerprint
==
nil
{
return
""
return
""
...
...
relay/channel/openai/relay-openai.go
View file @
4597816a
...
@@ -5,10 +5,6 @@ import (
...
@@ -5,10 +5,6 @@ import (
"bytes"
"bytes"
"encoding/json"
"encoding/json"
"fmt"
"fmt"
"github.com/bytedance/gopkg/util/gopool"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"github.com/pkg/errors"
"io"
"io"
"math"
"math"
"mime/multipart"
"mime/multipart"
...
@@ -23,21 +19,66 @@ import (
...
@@ -23,21 +19,66 @@ import (
"strings"
"strings"
"sync"
"sync"
"time"
"time"
"github.com/bytedance/gopkg/util/gopool"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"github.com/pkg/errors"
)
)
func
sendStreamData
(
c
*
gin
.
Context
,
data
string
,
forceForma
t
bool
)
error
{
func
sendStreamData
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
data
string
,
forceFormat
bool
,
thinkToConten
t
bool
)
error
{
if
data
==
""
{
if
data
==
""
{
return
nil
return
nil
}
}
if
forceFormat
{
if
!
forceFormat
&&
!
thinkToContent
{
return
service
.
StringData
(
c
,
data
)
}
var
lastStreamResponse
dto
.
ChatCompletionsStreamResponse
var
lastStreamResponse
dto
.
ChatCompletionsStreamResponse
if
err
:=
json
.
Unmarshal
(
common
.
StringToByteSlice
(
data
),
&
lastStreamResponse
);
err
!=
nil
{
if
err
:=
json
.
Unmarshal
(
common
.
StringToByteSlice
(
data
),
&
lastStreamResponse
);
err
!=
nil
{
return
err
return
err
}
}
if
!
thinkToContent
{
return
service
.
ObjectData
(
c
,
lastStreamResponse
)
return
service
.
ObjectData
(
c
,
lastStreamResponse
)
}
}
return
service
.
StringData
(
c
,
data
)
// Handle think to content conversion
if
info
.
IsFirstResponse
{
response
:=
lastStreamResponse
.
Copy
()
for
i
:=
range
response
.
Choices
{
response
.
Choices
[
i
]
.
Delta
.
SetContentString
(
"<think>
\n
"
)
response
.
Choices
[
i
]
.
Delta
.
SetReasoningContent
(
""
)
}
service
.
ObjectData
(
c
,
response
)
}
if
lastStreamResponse
.
Choices
==
nil
||
len
(
lastStreamResponse
.
Choices
)
==
0
{
return
service
.
ObjectData
(
c
,
lastStreamResponse
)
}
// Process each choice
for
i
,
choice
:=
range
lastStreamResponse
.
Choices
{
// Handle transition from thinking to content
if
len
(
choice
.
Delta
.
GetContentString
())
>
0
&&
!
info
.
SendLastReasoningResponse
{
response
:=
lastStreamResponse
.
Copy
()
for
j
:=
range
response
.
Choices
{
response
.
Choices
[
j
]
.
Delta
.
SetContentString
(
"
\n
</think>"
)
response
.
Choices
[
j
]
.
Delta
.
SetReasoningContent
(
""
)
}
info
.
SendLastReasoningResponse
=
true
service
.
ObjectData
(
c
,
response
)
}
// Convert reasoning content to regular content
if
len
(
choice
.
Delta
.
GetReasoningContent
())
>
0
{
lastStreamResponse
.
Choices
[
i
]
.
Delta
.
SetContentString
(
choice
.
Delta
.
GetReasoningContent
())
lastStreamResponse
.
Choices
[
i
]
.
Delta
.
SetReasoningContent
(
""
)
}
}
return
service
.
ObjectData
(
c
,
lastStreamResponse
)
}
}
func
OaiStreamHandler
(
c
*
gin
.
Context
,
resp
*
http
.
Response
,
info
*
relaycommon
.
RelayInfo
)
(
*
dto
.
OpenAIErrorWithStatusCode
,
*
dto
.
Usage
)
{
func
OaiStreamHandler
(
c
*
gin
.
Context
,
resp
*
http
.
Response
,
info
*
relaycommon
.
RelayInfo
)
(
*
dto
.
OpenAIErrorWithStatusCode
,
*
dto
.
Usage
)
{
...
@@ -56,11 +97,14 @@ func OaiStreamHandler(c *gin.Context, resp *http.Response, info *relaycommon.Rel
...
@@ -56,11 +97,14 @@ func OaiStreamHandler(c *gin.Context, resp *http.Response, info *relaycommon.Rel
var
usage
=
&
dto
.
Usage
{}
var
usage
=
&
dto
.
Usage
{}
var
streamItems
[]
string
// store stream items
var
streamItems
[]
string
// store stream items
var
forceFormat
bool
var
forceFormat
bool
var
thinkToContent
bool
if
info
.
ChannelType
==
common
.
ChannelTypeCustom
{
if
forceFmt
,
ok
:=
info
.
ChannelSetting
[
constant
.
ForceFormat
]
.
(
bool
);
ok
{
if
forceFmt
,
ok
:=
info
.
ChannelSetting
[
"force_format"
]
.
(
bool
);
ok
{
forceFormat
=
forceFmt
forceFormat
=
forceFmt
}
}
if
think2Content
,
ok
:=
info
.
ChannelSetting
[
constant
.
ChannelSettingThinkingToContent
]
.
(
bool
);
ok
{
thinkToContent
=
think2Content
}
}
toolCount
:=
0
toolCount
:=
0
...
@@ -84,7 +128,7 @@ func OaiStreamHandler(c *gin.Context, resp *http.Response, info *relaycommon.Rel
...
@@ -84,7 +128,7 @@ func OaiStreamHandler(c *gin.Context, resp *http.Response, info *relaycommon.Rel
)
)
gopool
.
Go
(
func
()
{
gopool
.
Go
(
func
()
{
for
scanner
.
Scan
()
{
for
scanner
.
Scan
()
{
info
.
SetFirstResponseTime
()
//
info.SetFirstResponseTime()
ticker
.
Reset
(
time
.
Duration
(
constant
.
StreamingTimeout
)
*
time
.
Second
)
ticker
.
Reset
(
time
.
Duration
(
constant
.
StreamingTimeout
)
*
time
.
Second
)
data
:=
scanner
.
Text
()
data
:=
scanner
.
Text
()
if
common
.
DebugEnabled
{
if
common
.
DebugEnabled
{
...
@@ -101,10 +145,11 @@ func OaiStreamHandler(c *gin.Context, resp *http.Response, info *relaycommon.Rel
...
@@ -101,10 +145,11 @@ func OaiStreamHandler(c *gin.Context, resp *http.Response, info *relaycommon.Rel
data
=
strings
.
TrimSpace
(
data
)
data
=
strings
.
TrimSpace
(
data
)
if
!
strings
.
HasPrefix
(
data
,
"[DONE]"
)
{
if
!
strings
.
HasPrefix
(
data
,
"[DONE]"
)
{
if
lastStreamData
!=
""
{
if
lastStreamData
!=
""
{
err
:=
sendStreamData
(
c
,
lastStreamData
,
forceForma
t
)
err
:=
sendStreamData
(
c
,
info
,
lastStreamData
,
forceFormat
,
thinkToConten
t
)
if
err
!=
nil
{
if
err
!=
nil
{
common
.
LogError
(
c
,
"streaming error: "
+
err
.
Error
())
common
.
LogError
(
c
,
"streaming error: "
+
err
.
Error
())
}
}
info
.
SetFirstResponseTime
()
}
}
lastStreamData
=
data
lastStreamData
=
data
streamItems
=
append
(
streamItems
,
data
)
streamItems
=
append
(
streamItems
,
data
)
...
@@ -144,7 +189,7 @@ func OaiStreamHandler(c *gin.Context, resp *http.Response, info *relaycommon.Rel
...
@@ -144,7 +189,7 @@ func OaiStreamHandler(c *gin.Context, resp *http.Response, info *relaycommon.Rel
}
}
}
}
if
shouldSendLastResp
{
if
shouldSendLastResp
{
sendStreamData
(
c
,
lastStreamData
,
forceForma
t
)
sendStreamData
(
c
,
info
,
lastStreamData
,
forceFormat
,
thinkToConten
t
)
}
}
// 计算token
// 计算token
...
...
relay/common/relay_info.go
View file @
4597816a
...
@@ -22,7 +22,8 @@ type RelayInfo struct {
...
@@ -22,7 +22,8 @@ type RelayInfo struct {
TokenUnlimited
bool
TokenUnlimited
bool
StartTime
time
.
Time
StartTime
time
.
Time
FirstResponseTime
time
.
Time
FirstResponseTime
time
.
Time
setFirstResponse
bool
IsFirstResponse
bool
SendLastReasoningResponse
bool
ApiType
int
ApiType
int
IsStream
bool
IsStream
bool
IsPlayground
bool
IsPlayground
bool
...
@@ -88,6 +89,7 @@ func GenRelayInfo(c *gin.Context) *RelayInfo {
...
@@ -88,6 +89,7 @@ func GenRelayInfo(c *gin.Context) *RelayInfo {
apiType
,
_
:=
relayconstant
.
ChannelType2APIType
(
channelType
)
apiType
,
_
:=
relayconstant
.
ChannelType2APIType
(
channelType
)
info
:=
&
RelayInfo
{
info
:=
&
RelayInfo
{
IsFirstResponse
:
true
,
RelayMode
:
relayconstant
.
Path2RelayMode
(
c
.
Request
.
URL
.
Path
),
RelayMode
:
relayconstant
.
Path2RelayMode
(
c
.
Request
.
URL
.
Path
),
BaseUrl
:
c
.
GetString
(
"base_url"
),
BaseUrl
:
c
.
GetString
(
"base_url"
),
RequestURLPath
:
c
.
Request
.
URL
.
String
(),
RequestURLPath
:
c
.
Request
.
URL
.
String
(),
...
@@ -139,9 +141,9 @@ func (info *RelayInfo) SetIsStream(isStream bool) {
...
@@ -139,9 +141,9 @@ func (info *RelayInfo) SetIsStream(isStream bool) {
}
}
func
(
info
*
RelayInfo
)
SetFirstResponseTime
()
{
func
(
info
*
RelayInfo
)
SetFirstResponseTime
()
{
if
!
info
.
set
FirstResponse
{
if
info
.
Is
FirstResponse
{
info
.
FirstResponseTime
=
time
.
Now
()
info
.
FirstResponseTime
=
time
.
Now
()
info
.
setFirstResponse
=
tru
e
info
.
IsFirstResponse
=
fals
e
}
}
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment