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
31d5e39d
authored
Jul 26, 2025
by
Calcium-Ion
Committed by
GitHub
Jul 26, 2025
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1443 from QuantumNous/claude_to_gemini
feat: Claude to gemini (适配claude格式调用gemini渠道模型)
parents
4ce58dbf
360012be
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 @
31d5e39d
...
@@ -136,7 +136,7 @@ func GetRandomSatisfiedChannel(group string, model string, retry int) (*Channel,
...
@@ -136,7 +136,7 @@ func GetRandomSatisfiedChannel(group string, model string, retry int) (*Channel,
}
}
}
}
}
else
{
}
else
{
return
nil
,
errors
.
New
(
"channel not found"
)
return
nil
,
nil
}
}
err
=
DB
.
First
(
&
channel
,
"id = ?"
,
channel
.
Id
)
.
Error
err
=
DB
.
First
(
&
channel
,
"id = ?"
,
channel
.
Id
)
.
Error
return
&
channel
,
err
return
&
channel
,
err
...
...
model/channel_cache.go
View file @
31d5e39d
...
@@ -130,7 +130,7 @@ func getRandomSatisfiedChannel(group string, model string, retry int) (*Channel,
...
@@ -130,7 +130,7 @@ func getRandomSatisfiedChannel(group string, model string, retry int) (*Channel,
channels
:=
group2model2channels
[
group
][
model
]
channels
:=
group2model2channels
[
group
][
model
]
if
len
(
channels
)
==
0
{
if
len
(
channels
)
==
0
{
return
nil
,
errors
.
New
(
"channel not found"
)
return
nil
,
nil
}
}
if
len
(
channels
)
==
1
{
if
len
(
channels
)
==
1
{
...
...
relay/channel/gemini/adaptor.go
View file @
31d5e39d
...
@@ -9,6 +9,7 @@ import (
...
@@ -9,6 +9,7 @@ import (
"one-api/common"
"one-api/common"
"one-api/dto"
"one-api/dto"
"one-api/relay/channel"
"one-api/relay/channel"
"one-api/relay/channel/openai"
relaycommon
"one-api/relay/common"
relaycommon
"one-api/relay/common"
"one-api/relay/constant"
"one-api/relay/constant"
"one-api/setting/model_setting"
"one-api/setting/model_setting"
...
@@ -21,10 +22,13 @@ import (
...
@@ -21,10 +22,13 @@ import (
type
Adaptor
struct
{
type
Adaptor
struct
{
}
}
func
(
a
*
Adaptor
)
ConvertClaudeRequest
(
*
gin
.
Context
,
*
relaycommon
.
RelayInfo
,
*
dto
.
ClaudeRequest
)
(
any
,
error
)
{
func
(
a
*
Adaptor
)
ConvertClaudeRequest
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
req
*
dto
.
ClaudeRequest
)
(
any
,
error
)
{
//TODO implement me
adaptor
:=
openai
.
Adaptor
{}
panic
(
"implement me"
)
oaiReq
,
err
:=
adaptor
.
ConvertClaudeRequest
(
c
,
info
,
req
)
return
nil
,
nil
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
)
{
func
(
a
*
Adaptor
)
ConvertAudioRequest
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
request
dto
.
AudioRequest
)
(
io
.
Reader
,
error
)
{
...
...
relay/channel/gemini/relay-gemini.go
View file @
31d5e39d
...
@@ -9,6 +9,7 @@ import (
...
@@ -9,6 +9,7 @@ import (
"one-api/common"
"one-api/common"
"one-api/constant"
"one-api/constant"
"one-api/dto"
"one-api/dto"
"one-api/relay/channel/openai"
relaycommon
"one-api/relay/common"
relaycommon
"one-api/relay/common"
"one-api/relay/helper"
"one-api/relay/helper"
"one-api/service"
"one-api/service"
...
@@ -736,7 +737,7 @@ func streamResponseGeminiChat2OpenAI(geminiResponse *GeminiChatResponse) (*dto.C
...
@@ -736,7 +737,7 @@ func streamResponseGeminiChat2OpenAI(geminiResponse *GeminiChatResponse) (*dto.C
choice
:=
dto
.
ChatCompletionsStreamResponseChoice
{
choice
:=
dto
.
ChatCompletionsStreamResponseChoice
{
Index
:
int
(
candidate
.
Index
),
Index
:
int
(
candidate
.
Index
),
Delta
:
dto
.
ChatCompletionsStreamResponseChoiceDelta
{
Delta
:
dto
.
ChatCompletionsStreamResponseChoiceDelta
{
Role
:
"assistant"
,
//
Role: "assistant",
},
},
}
}
var
texts
[]
string
var
texts
[]
string
...
@@ -798,6 +799,27 @@ func streamResponseGeminiChat2OpenAI(geminiResponse *GeminiChatResponse) (*dto.C
...
@@ -798,6 +799,27 @@ func streamResponseGeminiChat2OpenAI(geminiResponse *GeminiChatResponse) (*dto.C
return
&
response
,
isStop
,
hasImage
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
)
{
func
GeminiChatStreamHandler
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
resp
*
http
.
Response
)
(
*
dto
.
Usage
,
*
types
.
NewAPIError
)
{
// responseText := ""
// responseText := ""
id
:=
helper
.
GetResponseID
(
c
)
id
:=
helper
.
GetResponseID
(
c
)
...
@@ -805,6 +827,8 @@ func GeminiChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *
...
@@ -805,6 +827,8 @@ func GeminiChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *
var
usage
=
&
dto
.
Usage
{}
var
usage
=
&
dto
.
Usage
{}
var
imageCount
int
var
imageCount
int
respCount
:=
0
helper
.
StreamScannerHandler
(
c
,
resp
,
info
,
func
(
data
string
)
bool
{
helper
.
StreamScannerHandler
(
c
,
resp
,
info
,
func
(
data
string
)
bool
{
var
geminiResponse
GeminiChatResponse
var
geminiResponse
GeminiChatResponse
err
:=
common
.
UnmarshalJsonStr
(
data
,
&
geminiResponse
)
err
:=
common
.
UnmarshalJsonStr
(
data
,
&
geminiResponse
)
...
@@ -833,18 +857,31 @@ func GeminiChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *
...
@@ -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
{
if
err
!=
nil
{
common
.
LogError
(
c
,
err
.
Error
())
common
.
LogError
(
c
,
err
.
Error
())
}
}
if
isStop
{
if
isStop
{
response
:=
helper
.
GenerateStopResponse
(
id
,
createAt
,
info
.
UpstreamModelName
,
constant
.
FinishReasonStop
)
_
=
handleStream
(
c
,
info
,
helper
.
GenerateStopResponse
(
id
,
createAt
,
info
.
UpstreamModelName
,
constant
.
FinishReasonStop
))
helper
.
ObjectData
(
c
,
response
)
}
}
respCount
++
return
true
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
imageCount
!=
0
{
if
usage
.
CompletionTokens
==
0
{
if
usage
.
CompletionTokens
==
0
{
...
@@ -855,14 +892,14 @@ func GeminiChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *
...
@@ -855,14 +892,14 @@ func GeminiChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *
usage
.
PromptTokensDetails
.
TextTokens
=
usage
.
PromptTokens
usage
.
PromptTokensDetails
.
TextTokens
=
usage
.
PromptTokens
usage
.
CompletionTokens
=
usage
.
TotalTokens
-
usage
.
PromptTokens
usage
.
CompletionTokens
=
usage
.
TotalTokens
-
usage
.
PromptTokens
if
info
.
ShouldIncludeUsage
{
response
:=
helper
.
GenerateFinalUsageResponse
(
id
,
createAt
,
info
.
UpstreamModelName
,
*
usage
)
response
=
helper
.
GenerateFinalUsageResponse
(
id
,
createAt
,
info
.
UpstreamModelName
,
*
usage
)
err
:=
handleFinalStream
(
c
,
info
,
response
)
err
:=
helper
.
ObjectData
(
c
,
response
)
if
err
!=
nil
{
if
err
!=
nil
{
common
.
SysError
(
"send final response failed: "
+
err
.
Error
())
common
.
SysError
(
"send final response failed: "
+
err
.
Error
())
}
}
}
helper
.
Done
(
c
)
//if info.RelayFormat == relaycommon.RelayFormatOpenAI {
// helper.Done(c)
//}
//resp.Body.Close()
//resp.Body.Close()
return
usage
,
nil
return
usage
,
nil
}
}
...
...
relay/channel/openai/helper.go
View file @
31d5e39d
...
@@ -14,7 +14,7 @@ import (
...
@@ -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
++
info
.
SendResponseCount
++
switch
info
.
RelayFormat
{
switch
info
.
RelayFormat
{
case
relaycommon
.
RelayFormatOpenAI
:
case
relaycommon
.
RelayFormatOpenAI
:
...
@@ -158,7 +158,7 @@ func handleLastResponse(lastStreamData string, responseId *string, createAt *int
...
@@ -158,7 +158,7 @@ func handleLastResponse(lastStreamData string, responseId *string, createAt *int
return
nil
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
,
responseId
string
,
createAt
int64
,
model
string
,
systemFingerprint
string
,
usage
*
dto
.
Usage
,
containStreamUsage
bool
)
{
usage
*
dto
.
Usage
,
containStreamUsage
bool
)
{
...
...
relay/channel/openai/relay-openai.go
View file @
31d5e39d
...
@@ -123,24 +123,11 @@ func OaiStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Re
...
@@ -123,24 +123,11 @@ func OaiStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Re
var
toolCount
int
var
toolCount
int
var
usage
=
&
dto
.
Usage
{}
var
usage
=
&
dto
.
Usage
{}
var
streamItems
[]
string
// store stream items
var
streamItems
[]
string
// store stream items
var
forceFormat
bool
var
lastStreamData
string
var
thinkToContent
bool
if
info
.
ChannelSetting
.
ForceFormat
{
forceFormat
=
true
}
if
info
.
ChannelSetting
.
ThinkingToContent
{
thinkToContent
=
true
}
var
(
lastStreamData
string
)
helper
.
StreamScannerHandler
(
c
,
resp
,
info
,
func
(
data
string
)
bool
{
helper
.
StreamScannerHandler
(
c
,
resp
,
info
,
func
(
data
string
)
bool
{
if
lastStreamData
!=
""
{
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
{
if
err
!=
nil
{
common
.
SysError
(
"error handling stream format: "
+
err
.
Error
())
common
.
SysError
(
"error handling stream format: "
+
err
.
Error
())
}
}
...
@@ -161,7 +148,7 @@ func OaiStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Re
...
@@ -161,7 +148,7 @@ func OaiStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Re
if
info
.
RelayFormat
==
relaycommon
.
RelayFormatOpenAI
{
if
info
.
RelayFormat
==
relaycommon
.
RelayFormatOpenAI
{
if
shouldSendLastResp
{
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
...
@@ -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
return
usage
,
nil
}
}
...
...
relay/helper/common.go
View file @
31d5e39d
...
@@ -139,6 +139,24 @@ func GetLocalRealtimeID(c *gin.Context) string {
...
@@ -139,6 +139,24 @@ func GetLocalRealtimeID(c *gin.Context) string {
return
fmt
.
Sprintf
(
"evt_%s"
,
logID
)
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
{
func
GenerateStopResponse
(
id
string
,
createAt
int64
,
model
string
,
finishReason
string
)
*
dto
.
ChatCompletionsStreamResponse
{
return
&
dto
.
ChatCompletionsStreamResponse
{
return
&
dto
.
ChatCompletionsStreamResponse
{
Id
:
id
,
Id
:
id
,
...
...
types/error.go
View file @
31d5e39d
...
@@ -63,6 +63,7 @@ const (
...
@@ -63,6 +63,7 @@ const (
ErrorCodeBadResponseStatusCode
ErrorCode
=
"bad_response_status_code"
ErrorCodeBadResponseStatusCode
ErrorCode
=
"bad_response_status_code"
ErrorCodeBadResponse
ErrorCode
=
"bad_response"
ErrorCodeBadResponse
ErrorCode
=
"bad_response"
ErrorCodeBadResponseBody
ErrorCode
=
"bad_response_body"
ErrorCodeBadResponseBody
ErrorCode
=
"bad_response_body"
ErrorCodeEmptyResponse
ErrorCode
=
"empty_response"
// sql error
// sql error
ErrorCodeQueryDataError
ErrorCode
=
"query_data_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