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
360012be
authored
Jul 26, 2025
by
CaIon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: support claude convert to gemini
parent
2cb7b9ae
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
84 additions
and
37 deletions
+84
-37
model/ability.go
+1
-1
model/channel_cache.go
+1
-1
relay/channel/gemini/adaptor.go
+8
-4
relay/channel/gemini/relay-gemini.go
+49
-12
relay/channel/openai/helper.go
+2
-2
relay/channel/openai/relay-openai.go
+4
-17
relay/helper/common.go
+18
-0
types/error.go
+1
-0
No files found.
model/ability.go
View file @
360012be
...
...
@@ -136,7 +136,7 @@ func GetRandomSatisfiedChannel(group string, model string, retry int) (*Channel,
}
}
}
else
{
return
nil
,
errors
.
New
(
"channel not found"
)
return
nil
,
nil
}
err
=
DB
.
First
(
&
channel
,
"id = ?"
,
channel
.
Id
)
.
Error
return
&
channel
,
err
...
...
model/channel_cache.go
View file @
360012be
...
...
@@ -130,7 +130,7 @@ func getRandomSatisfiedChannel(group string, model string, retry int) (*Channel,
channels
:=
group2model2channels
[
group
][
model
]
if
len
(
channels
)
==
0
{
return
nil
,
errors
.
New
(
"channel not found"
)
return
nil
,
nil
}
if
len
(
channels
)
==
1
{
...
...
relay/channel/gemini/adaptor.go
View file @
360012be
...
...
@@ -9,6 +9,7 @@ import (
"one-api/common"
"one-api/dto"
"one-api/relay/channel"
"one-api/relay/channel/openai"
relaycommon
"one-api/relay/common"
"one-api/relay/constant"
"one-api/setting/model_setting"
...
...
@@ -21,10 +22,13 @@ import (
type
Adaptor
struct
{
}
func
(
a
*
Adaptor
)
ConvertClaudeRequest
(
*
gin
.
Context
,
*
relaycommon
.
RelayInfo
,
*
dto
.
ClaudeRequest
)
(
any
,
error
)
{
//TODO implement me
panic
(
"implement me"
)
return
nil
,
nil
func
(
a
*
Adaptor
)
ConvertClaudeRequest
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
req
*
dto
.
ClaudeRequest
)
(
any
,
error
)
{
adaptor
:=
openai
.
Adaptor
{}
oaiReq
,
err
:=
adaptor
.
ConvertClaudeRequest
(
c
,
info
,
req
)
if
err
!=
nil
{
return
nil
,
err
}
return
a
.
ConvertOpenAIRequest
(
c
,
info
,
oaiReq
.
(
*
dto
.
GeneralOpenAIRequest
))
}
func
(
a
*
Adaptor
)
ConvertAudioRequest
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
request
dto
.
AudioRequest
)
(
io
.
Reader
,
error
)
{
...
...
relay/channel/gemini/relay-gemini.go
View file @
360012be
...
...
@@ -9,6 +9,7 @@ import (
"one-api/common"
"one-api/constant"
"one-api/dto"
"one-api/relay/channel/openai"
relaycommon
"one-api/relay/common"
"one-api/relay/helper"
"one-api/service"
...
...
@@ -736,7 +737,7 @@ func streamResponseGeminiChat2OpenAI(geminiResponse *GeminiChatResponse) (*dto.C
choice
:=
dto
.
ChatCompletionsStreamResponseChoice
{
Index
:
int
(
candidate
.
Index
),
Delta
:
dto
.
ChatCompletionsStreamResponseChoiceDelta
{
Role
:
"assistant"
,
//
Role: "assistant",
},
}
var
texts
[]
string
...
...
@@ -798,6 +799,27 @@ func streamResponseGeminiChat2OpenAI(geminiResponse *GeminiChatResponse) (*dto.C
return
&
response
,
isStop
,
hasImage
}
func
handleStream
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
resp
*
dto
.
ChatCompletionsStreamResponse
)
error
{
streamData
,
err
:=
common
.
Marshal
(
resp
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to marshal stream response: %w"
,
err
)
}
err
=
openai
.
HandleStreamFormat
(
c
,
info
,
string
(
streamData
),
info
.
ChannelSetting
.
ForceFormat
,
info
.
ChannelSetting
.
ThinkingToContent
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to handle stream format: %w"
,
err
)
}
return
nil
}
func
handleFinalStream
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
resp
*
dto
.
ChatCompletionsStreamResponse
)
error
{
streamData
,
err
:=
common
.
Marshal
(
resp
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to marshal stream response: %w"
,
err
)
}
openai
.
HandleFinalResponse
(
c
,
info
,
string
(
streamData
),
resp
.
Id
,
resp
.
Created
,
resp
.
Model
,
resp
.
GetSystemFingerprint
(),
resp
.
Usage
,
info
.
ShouldIncludeUsage
)
return
nil
}
func
GeminiChatStreamHandler
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
resp
*
http
.
Response
)
(
*
dto
.
Usage
,
*
types
.
NewAPIError
)
{
// responseText := ""
id
:=
helper
.
GetResponseID
(
c
)
...
...
@@ -805,6 +827,8 @@ func GeminiChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *
var
usage
=
&
dto
.
Usage
{}
var
imageCount
int
respCount
:=
0
helper
.
StreamScannerHandler
(
c
,
resp
,
info
,
func
(
data
string
)
bool
{
var
geminiResponse
GeminiChatResponse
err
:=
common
.
UnmarshalJsonStr
(
data
,
&
geminiResponse
)
...
...
@@ -833,18 +857,31 @@ func GeminiChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *
}
}
}
err
=
helper
.
ObjectData
(
c
,
response
)
if
respCount
==
0
{
// send first response
err
=
handleStream
(
c
,
info
,
helper
.
GenerateStartEmptyResponse
(
id
,
createAt
,
info
.
UpstreamModelName
,
nil
))
if
err
!=
nil
{
common
.
LogError
(
c
,
err
.
Error
())
}
}
err
=
handleStream
(
c
,
info
,
response
)
if
err
!=
nil
{
common
.
LogError
(
c
,
err
.
Error
())
}
if
isStop
{
response
:=
helper
.
GenerateStopResponse
(
id
,
createAt
,
info
.
UpstreamModelName
,
constant
.
FinishReasonStop
)
helper
.
ObjectData
(
c
,
response
)
_
=
handleStream
(
c
,
info
,
helper
.
GenerateStopResponse
(
id
,
createAt
,
info
.
UpstreamModelName
,
constant
.
FinishReasonStop
))
}
respCount
++
return
true
})
var
response
*
dto
.
ChatCompletionsStreamResponse
if
respCount
==
0
{
// 空补全,报错不计费
// empty response, throw an error
return
nil
,
types
.
NewOpenAIError
(
errors
.
New
(
"no response received from Gemini API"
),
types
.
ErrorCodeEmptyResponse
,
http
.
StatusInternalServerError
)
}
if
imageCount
!=
0
{
if
usage
.
CompletionTokens
==
0
{
...
...
@@ -855,14 +892,14 @@ func GeminiChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *
usage
.
PromptTokensDetails
.
TextTokens
=
usage
.
PromptTokens
usage
.
CompletionTokens
=
usage
.
TotalTokens
-
usage
.
PromptTokens
if
info
.
ShouldIncludeUsage
{
response
=
helper
.
GenerateFinalUsageResponse
(
id
,
createAt
,
info
.
UpstreamModelName
,
*
usage
)
err
:=
helper
.
ObjectData
(
c
,
response
)
if
err
!=
nil
{
common
.
SysError
(
"send final response failed: "
+
err
.
Error
())
}
response
:=
helper
.
GenerateFinalUsageResponse
(
id
,
createAt
,
info
.
UpstreamModelName
,
*
usage
)
err
:=
handleFinalStream
(
c
,
info
,
response
)
if
err
!=
nil
{
common
.
SysError
(
"send final response failed: "
+
err
.
Error
())
}
helper
.
Done
(
c
)
//if info.RelayFormat == relaycommon.RelayFormatOpenAI {
// helper.Done(c)
//}
//resp.Body.Close()
return
usage
,
nil
}
...
...
relay/channel/openai/helper.go
View file @
360012be
...
...
@@ -14,7 +14,7 @@ import (
)
// 辅助函数
func
h
andleStreamFormat
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
data
string
,
forceFormat
bool
,
thinkToContent
bool
)
error
{
func
H
andleStreamFormat
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
data
string
,
forceFormat
bool
,
thinkToContent
bool
)
error
{
info
.
SendResponseCount
++
switch
info
.
RelayFormat
{
case
relaycommon
.
RelayFormatOpenAI
:
...
...
@@ -158,7 +158,7 @@ func handleLastResponse(lastStreamData string, responseId *string, createAt *int
return
nil
}
func
h
andleFinalResponse
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
lastStreamData
string
,
func
H
andleFinalResponse
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
lastStreamData
string
,
responseId
string
,
createAt
int64
,
model
string
,
systemFingerprint
string
,
usage
*
dto
.
Usage
,
containStreamUsage
bool
)
{
...
...
relay/channel/openai/relay-openai.go
View file @
360012be
...
...
@@ -123,24 +123,11 @@ func OaiStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Re
var
toolCount
int
var
usage
=
&
dto
.
Usage
{}
var
streamItems
[]
string
// store stream items
var
forceFormat
bool
var
thinkToContent
bool
if
info
.
ChannelSetting
.
ForceFormat
{
forceFormat
=
true
}
if
info
.
ChannelSetting
.
ThinkingToContent
{
thinkToContent
=
true
}
var
(
lastStreamData
string
)
var
lastStreamData
string
helper
.
StreamScannerHandler
(
c
,
resp
,
info
,
func
(
data
string
)
bool
{
if
lastStreamData
!=
""
{
err
:=
handleStreamFormat
(
c
,
info
,
lastStreamData
,
forceFormat
,
think
ToContent
)
err
:=
HandleStreamFormat
(
c
,
info
,
lastStreamData
,
info
.
ChannelSetting
.
ForceFormat
,
info
.
ChannelSetting
.
Thinking
ToContent
)
if
err
!=
nil
{
common
.
SysError
(
"error handling stream format: "
+
err
.
Error
())
}
...
...
@@ -161,7 +148,7 @@ func OaiStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Re
if
info
.
RelayFormat
==
relaycommon
.
RelayFormatOpenAI
{
if
shouldSendLastResp
{
_
=
sendStreamData
(
c
,
info
,
lastStreamData
,
forceFormat
,
think
ToContent
)
_
=
sendStreamData
(
c
,
info
,
lastStreamData
,
info
.
ChannelSetting
.
ForceFormat
,
info
.
ChannelSetting
.
Thinking
ToContent
)
}
}
...
...
@@ -180,7 +167,7 @@ func OaiStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Re
}
}
}
h
andleFinalResponse
(
c
,
info
,
lastStreamData
,
responseId
,
createAt
,
model
,
systemFingerprint
,
usage
,
containStreamUsage
)
H
andleFinalResponse
(
c
,
info
,
lastStreamData
,
responseId
,
createAt
,
model
,
systemFingerprint
,
usage
,
containStreamUsage
)
return
usage
,
nil
}
...
...
relay/helper/common.go
View file @
360012be
...
...
@@ -139,6 +139,24 @@ func GetLocalRealtimeID(c *gin.Context) string {
return
fmt
.
Sprintf
(
"evt_%s"
,
logID
)
}
func
GenerateStartEmptyResponse
(
id
string
,
createAt
int64
,
model
string
,
systemFingerprint
*
string
)
*
dto
.
ChatCompletionsStreamResponse
{
return
&
dto
.
ChatCompletionsStreamResponse
{
Id
:
id
,
Object
:
"chat.completion.chunk"
,
Created
:
createAt
,
Model
:
model
,
SystemFingerprint
:
systemFingerprint
,
Choices
:
[]
dto
.
ChatCompletionsStreamResponseChoice
{
{
Delta
:
dto
.
ChatCompletionsStreamResponseChoiceDelta
{
Role
:
"assistant"
,
Content
:
common
.
GetPointer
(
""
),
},
},
},
}
}
func
GenerateStopResponse
(
id
string
,
createAt
int64
,
model
string
,
finishReason
string
)
*
dto
.
ChatCompletionsStreamResponse
{
return
&
dto
.
ChatCompletionsStreamResponse
{
Id
:
id
,
...
...
types/error.go
View file @
360012be
...
...
@@ -63,6 +63,7 @@ const (
ErrorCodeBadResponseStatusCode
ErrorCode
=
"bad_response_status_code"
ErrorCodeBadResponse
ErrorCode
=
"bad_response"
ErrorCodeBadResponseBody
ErrorCode
=
"bad_response_body"
ErrorCodeEmptyResponse
ErrorCode
=
"empty_response"
// sql error
ErrorCodeQueryDataError
ErrorCode
=
"query_data_error"
...
...
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