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
dc58ff04
authored
Jul 18, 2024
by
CalciumIon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: support claude tool calling
parent
363ae951
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
102 additions
and
13 deletions
+102
-13
dto/text_request.go
+6
-1
dto/text_response.go
+2
-0
relay/channel/claude/dto.go
+22
-5
relay/channel/claude/relay-claude.go
+72
-7
No files found.
dto/text_request.go
View file @
dc58ff04
...
...
@@ -26,7 +26,7 @@ type GeneralOpenAIRequest struct {
PresencePenalty
float64
`json:"presence_penalty,omitempty"`
ResponseFormat
*
ResponseFormat
`json:"response_format,omitempty"`
Seed
float64
`json:"seed,omitempty"`
Tools
any
`json:"tools,omitempty"`
Tools
[]
ToolCall
`json:"tools,omitempty"`
ToolChoice
any
`json:"tool_choice,omitempty"`
User
string
`json:"user,omitempty"`
LogProbs
bool
`json:"logprobs,omitempty"`
...
...
@@ -104,6 +104,11 @@ func (m Message) StringContent() string {
return
string
(
m
.
Content
)
}
func
(
m
*
Message
)
SetStringContent
(
content
string
)
{
jsonContent
,
_
:=
json
.
Marshal
(
content
)
m
.
Content
=
jsonContent
}
func
(
m
Message
)
IsStringContent
()
bool
{
var
stringContent
string
if
err
:=
json
.
Unmarshal
(
m
.
Content
,
&
stringContent
);
err
==
nil
{
...
...
dto/text_response.go
View file @
dc58ff04
...
...
@@ -86,8 +86,10 @@ type ToolCall struct {
}
type
FunctionCall
struct
{
Description
string
`json:"description,omitempty"`
Name
string
`json:"name,omitempty"`
// call function with arguments in JSON format
Parameters
any
`json:"parameters,omitempty"`
// request
Arguments
string
`json:"arguments,omitempty"`
}
...
...
relay/channel/claude/dto.go
View file @
dc58ff04
...
...
@@ -10,6 +10,13 @@ type ClaudeMediaMessage struct {
Source
*
ClaudeMessageSource
`json:"source,omitempty"`
Usage
*
ClaudeUsage
`json:"usage,omitempty"`
StopReason
*
string
`json:"stop_reason,omitempty"`
PartialJson
string
`json:"partial_json,omitempty"`
// tool_calls
Id
string
`json:"id,omitempty"`
Name
string
`json:"name,omitempty"`
Input
any
`json:"input,omitempty"`
Content
string
`json:"content,omitempty"`
ToolUseId
string
`json:"tool_use_id,omitempty"`
}
type
ClaudeMessageSource
struct
{
...
...
@@ -23,6 +30,18 @@ type ClaudeMessage struct {
Content
any
`json:"content"`
}
type
Tool
struct
{
Name
string
`json:"name"`
Description
string
`json:"description,omitempty"`
InputSchema
InputSchema
`json:"input_schema"`
}
type
InputSchema
struct
{
Type
string
`json:"type"`
Properties
any
`json:"properties,omitempty"`
Required
any
`json:"required,omitempty"`
}
type
ClaudeRequest
struct
{
Model
string
`json:"model"`
Prompt
string
`json:"prompt,omitempty"`
...
...
@@ -36,6 +55,8 @@ type ClaudeRequest struct {
TopK
int
`json:"top_k,omitempty"`
//ClaudeMetadata `json:"metadata,omitempty"`
Stream
bool
`json:"stream,omitempty"`
Tools
[]
Tool
`json:"tools,omitempty"`
ToolChoice
any
`json:"tool_choice,omitempty"`
}
type
ClaudeError
struct
{
...
...
@@ -53,15 +74,11 @@ type ClaudeResponse struct {
Error
ClaudeError
`json:"error"`
Usage
ClaudeUsage
`json:"usage"`
Index
int
`json:"index"`
// stream only
ContentBlock
*
ClaudeMediaMessage
`json:"content_block"`
Delta
*
ClaudeMediaMessage
`json:"delta"`
// stream only
Message
*
ClaudeResponse
`json:"message"`
// stream only: message_start
}
//type ClaudeResponseChoice struct {
// Index int `json:"index"`
// Type string `json:"type"`
//}
type
ClaudeUsage
struct
{
InputTokens
int
`json:"input_tokens"`
OutputTokens
int
`json:"output_tokens"`
...
...
relay/channel/claude/relay-claude.go
View file @
dc58ff04
...
...
@@ -30,6 +30,7 @@ func stopReasonClaude2OpenAI(reason string) string {
}
func
RequestOpenAI2ClaudeComplete
(
textRequest
dto
.
GeneralOpenAIRequest
)
*
ClaudeRequest
{
claudeRequest
:=
ClaudeRequest
{
Model
:
textRequest
.
Model
,
Prompt
:
""
,
...
...
@@ -60,6 +61,22 @@ func RequestOpenAI2ClaudeComplete(textRequest dto.GeneralOpenAIRequest) *ClaudeR
}
func
RequestOpenAI2ClaudeMessage
(
textRequest
dto
.
GeneralOpenAIRequest
)
(
*
ClaudeRequest
,
error
)
{
claudeTools
:=
make
([]
Tool
,
0
,
len
(
textRequest
.
Tools
))
for
_
,
tool
:=
range
textRequest
.
Tools
{
if
params
,
ok
:=
tool
.
Function
.
Parameters
.
(
map
[
string
]
any
);
ok
{
claudeTools
=
append
(
claudeTools
,
Tool
{
Name
:
tool
.
Function
.
Name
,
Description
:
tool
.
Function
.
Description
,
InputSchema
:
InputSchema
{
Type
:
params
[
"type"
]
.
(
string
),
Properties
:
params
[
"properties"
],
Required
:
params
[
"required"
],
},
})
}
}
claudeRequest
:=
ClaudeRequest
{
Model
:
textRequest
.
Model
,
MaxTokens
:
textRequest
.
MaxTokens
,
...
...
@@ -68,6 +85,7 @@ func RequestOpenAI2ClaudeMessage(textRequest dto.GeneralOpenAIRequest) (*ClaudeR
TopP
:
textRequest
.
TopP
,
TopK
:
textRequest
.
TopK
,
Stream
:
textRequest
.
Stream
,
Tools
:
claudeTools
,
}
if
claudeRequest
.
MaxTokens
==
0
{
claudeRequest
.
MaxTokens
=
4096
...
...
@@ -184,6 +202,7 @@ func StreamResponseClaude2OpenAI(reqMode int, claudeResponse *ClaudeResponse) (*
response
.
Object
=
"chat.completion.chunk"
response
.
Model
=
claudeResponse
.
Model
response
.
Choices
=
make
([]
dto
.
ChatCompletionsStreamResponseChoice
,
0
)
tools
:=
make
([]
dto
.
ToolCall
,
0
)
var
choice
dto
.
ChatCompletionsStreamResponseChoice
if
reqMode
==
RequestModeCompletion
{
choice
.
Delta
.
SetContentString
(
claudeResponse
.
Completion
)
...
...
@@ -199,10 +218,33 @@ func StreamResponseClaude2OpenAI(reqMode int, claudeResponse *ClaudeResponse) (*
choice
.
Delta
.
SetContentString
(
""
)
choice
.
Delta
.
Role
=
"assistant"
}
else
if
claudeResponse
.
Type
==
"content_block_start"
{
if
claudeResponse
.
ContentBlock
!=
nil
{
//choice.Delta.SetContentString(claudeResponse.ContentBlock.Text)
if
claudeResponse
.
ContentBlock
.
Type
==
"tool_use"
{
tools
=
append
(
tools
,
dto
.
ToolCall
{
ID
:
claudeResponse
.
ContentBlock
.
Id
,
Type
:
"function"
,
Function
:
dto
.
FunctionCall
{
Name
:
claudeResponse
.
ContentBlock
.
Name
,
Arguments
:
""
,
},
})
}
}
else
{
return
nil
,
nil
}
}
else
if
claudeResponse
.
Type
==
"content_block_delta"
{
if
claudeResponse
.
Delta
!=
nil
{
choice
.
Index
=
claudeResponse
.
Index
choice
.
Delta
.
SetContentString
(
claudeResponse
.
Delta
.
Text
)
if
claudeResponse
.
Delta
.
Type
==
"input_json_delta"
{
tools
=
append
(
tools
,
dto
.
ToolCall
{
Function
:
dto
.
FunctionCall
{
Arguments
:
claudeResponse
.
Delta
.
PartialJson
,
},
})
}
}
}
else
if
claudeResponse
.
Type
==
"message_delta"
{
finishReason
:=
stopReasonClaude2OpenAI
(
*
claudeResponse
.
Delta
.
StopReason
)
if
finishReason
!=
"null"
{
...
...
@@ -218,6 +260,10 @@ func StreamResponseClaude2OpenAI(reqMode int, claudeResponse *ClaudeResponse) (*
if
claudeUsage
==
nil
{
claudeUsage
=
&
ClaudeUsage
{}
}
if
len
(
tools
)
>
0
{
choice
.
Delta
.
Content
=
nil
// compatible with other OpenAI derivative applications, like LobeOpenAICompatibleFactory ...
choice
.
Delta
.
ToolCalls
=
tools
}
response
.
Choices
=
append
(
response
.
Choices
,
choice
)
return
&
response
,
claudeUsage
...
...
@@ -230,6 +276,11 @@ func ResponseClaude2OpenAI(reqMode int, claudeResponse *ClaudeResponse) *dto.Ope
Object
:
"chat.completion"
,
Created
:
common
.
GetTimestamp
(),
}
var
responseText
string
if
len
(
claudeResponse
.
Content
)
>
0
{
responseText
=
claudeResponse
.
Content
[
0
]
.
Text
}
tools
:=
make
([]
dto
.
ToolCall
,
0
)
if
reqMode
==
RequestModeCompletion
{
content
,
_
:=
json
.
Marshal
(
strings
.
TrimPrefix
(
claudeResponse
.
Completion
,
" "
))
choice
:=
dto
.
OpenAITextResponseChoice
{
...
...
@@ -244,20 +295,32 @@ func ResponseClaude2OpenAI(reqMode int, claudeResponse *ClaudeResponse) *dto.Ope
choices
=
append
(
choices
,
choice
)
}
else
{
fullTextResponse
.
Id
=
claudeResponse
.
Id
for
i
,
message
:=
range
claudeResponse
.
Content
{
content
,
_
:=
json
.
Marshal
(
message
.
Text
)
for
_
,
message
:=
range
claudeResponse
.
Content
{
if
message
.
Type
==
"tool_use"
{
args
,
_
:=
json
.
Marshal
(
message
.
Input
)
tools
=
append
(
tools
,
dto
.
ToolCall
{
ID
:
message
.
Id
,
Type
:
"function"
,
// compatible with other OpenAI derivative applications
Function
:
dto
.
FunctionCall
{
Name
:
message
.
Name
,
Arguments
:
string
(
args
),
},
})
}
}
}
choice
:=
dto
.
OpenAITextResponseChoice
{
Index
:
i
,
Index
:
0
,
Message
:
dto
.
Message
{
Role
:
"assistant"
,
Content
:
content
,
},
FinishReason
:
stopReasonClaude2OpenAI
(
claudeResponse
.
StopReason
),
}
choices
=
append
(
choices
,
choice
)
}
choice
.
SetStringContent
(
responseText
)
if
len
(
tools
)
>
0
{
choice
.
Message
.
ToolCalls
=
tools
}
choices
=
append
(
choices
,
choice
)
fullTextResponse
.
Choices
=
choices
return
&
fullTextResponse
}
...
...
@@ -334,6 +397,8 @@ func claudeStreamHandler(c *gin.Context, resp *http.Response, info *relaycommon.
}
else
if
claudeResponse
.
Type
==
"message_delta"
{
usage
.
CompletionTokens
=
claudeUsage
.
OutputTokens
usage
.
TotalTokens
=
claudeUsage
.
InputTokens
+
claudeUsage
.
OutputTokens
}
else
if
claudeResponse
.
Type
==
"content_block_start"
{
}
else
{
return
true
}
...
...
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