Skip to content

Commit 8b07a88

Browse files
committedFeb 9, 2025
✨ feat(大模型): 支持fuction calling
1 parent 704c5ec commit 8b07a88

File tree

3 files changed

+209
-1
lines changed

3 files changed

+209
-1
lines changed
 

‎chat.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ type realChatStreamResponse struct {
5555
Content string `json:"content"`
5656
} `json:"delta"`
5757
Logprobs interface{} `json:"logprobs"`
58-
FinishReason interface{} `json:"finish_reason"`
58+
FinishReason string `json:"finish_reason"`
5959
} `json:"choices"`
6060
}
6161

‎chattool.go

+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
package openai
2+
3+
import (
4+
"bufio"
5+
"encoding/json"
6+
)
7+
8+
type ChatToolRequest struct {
9+
Messages []ToolMessage `json:"messages"`
10+
Model string `json:"model"`
11+
FrequencyPenalty int `json:"frequency_penalty"`
12+
MaxTokens int `json:"max_tokens"`
13+
PresencePenalty int `json:"presence_penalty"`
14+
ResponseFormat struct {
15+
Type string `json:"type"`
16+
} `json:"response_format"`
17+
Stop []string `json:"stop"`
18+
Stream bool `json:"stream"`
19+
Temperature float32 `json:"temperature"`
20+
TopP int `json:"top_p"`
21+
Tools []ChatToolFunction `json:"tools"`
22+
}
23+
24+
type ChatToolFunction struct {
25+
Type string `json:"type"`
26+
Function ChatToolFuctionDetail `json:"function"`
27+
}
28+
29+
type ChatToolFuctionDetail struct {
30+
Name string `json:"name"`
31+
Description string `json:"description"`
32+
Parameters ChatToolParameters `json:"parameters"`
33+
}
34+
35+
type ChatToolParameters struct {
36+
Type string `json:"type"`
37+
Properties map[string]ChatToolFuctionPropertie `json:"properties"`
38+
Required []string `json:"required"`
39+
}
40+
41+
type ChatToolFuctionPropertie struct {
42+
Type string `json:"type"`
43+
Description string `json:"description"`
44+
}
45+
46+
type realChatToolStreamMessage struct {
47+
ID string `json:"id"`
48+
Object string `json:"object"`
49+
Created int `json:"created"`
50+
Model string `json:"model"`
51+
Choices []struct {
52+
Index int `json:"index"`
53+
Message struct {
54+
Role string `json:"role"`
55+
Content string `json:"content"`
56+
ToolCalls []realToolCalls `json:"tool_calls"`
57+
} `json:"message"`
58+
Logprobs interface{} `json:"logprobs"`
59+
FinishReason string `json:"finish_reason"`
60+
} `json:"choices"`
61+
Usage struct {
62+
PromptTokens int `json:"prompt_tokens"`
63+
CompletionTokens int `json:"completion_tokens"`
64+
TotalTokens int `json:"total_tokens"`
65+
PromptTokensDetails struct {
66+
CachedTokens int `json:"cached_tokens"`
67+
} `json:"prompt_tokens_details"`
68+
PromptCacheHitTokens int `json:"prompt_cache_hit_tokens"`
69+
PromptCacheMissTokens int `json:"prompt_cache_miss_tokens"`
70+
} `json:"usage"`
71+
SystemFingerprint string `json:"system_fingerprint"`
72+
}
73+
74+
type ToolMessage struct {
75+
Role string `json:"role"`
76+
Content string `json:"content"`
77+
ToolCalls []realToolCalls `json:"tool_calls"`
78+
ToolCallID string `json:"tool_call_id"`
79+
}
80+
81+
type realToolCalls struct {
82+
Index int `json:"index"`
83+
ID string `json:"id"`
84+
Type string `json:"type"`
85+
Function struct {
86+
Name string `json:"name"`
87+
Arguments string `json:"arguments"`
88+
} `json:"function"`
89+
}
90+
91+
func checkChatToolRequest(cr *ChatToolRequest) {
92+
if cr.MaxTokens == 0 {
93+
cr.MaxTokens = 4096
94+
}
95+
if cr.ResponseFormat.Type == "" {
96+
cr.ResponseFormat.Type = "text"
97+
}
98+
if cr.Temperature == 0 {
99+
cr.Temperature = 0.3
100+
}
101+
if cr.TopP == 0 {
102+
cr.TopP = 1
103+
}
104+
}
105+
106+
func (client *Client) ChatWithTools(model string, messages []ToolMessage, toolInfo []ChatToolFunction, functionMap map[string](func(map[string]interface{}) string), during func(string)) error {
107+
reqBody := ChatToolRequest{}
108+
reqBody.Tools = toolInfo
109+
reqBody.Messages = messages
110+
reqBody.Model = model
111+
checkChatToolRequest(&reqBody)
112+
body, e := json.Marshal(reqBody)
113+
if e != nil {
114+
return e
115+
}
116+
reqClient := client.newStreamClient()
117+
reqClient.SetBody(body)
118+
reqClient.SetDoNotParseResponse(true)
119+
httpres, e := reqClient.Post(client.Config.BaseUrl + "/chat/completions")
120+
if e != nil {
121+
return e
122+
}
123+
defer httpres.RawBody().Close()
124+
scanner := bufio.NewScanner(httpres.RawBody())
125+
for scanner.Scan() {
126+
_res := scanner.Text()
127+
if _res == "" {
128+
continue
129+
}
130+
if _res == "data: [DONE]" {
131+
break
132+
}
133+
var _Tooljson realChatToolStreamMessage
134+
e := json.Unmarshal([]byte(_res), &_Tooljson)
135+
if e == nil {
136+
if _Tooljson.Choices[0].FinishReason == "tool_calls" {
137+
messages = append(messages, ToolMessage{
138+
Role: _Tooljson.Choices[0].Message.Role,
139+
Content: _Tooljson.Choices[0].Message.Content,
140+
ToolCalls: _Tooljson.Choices[0].Message.ToolCalls,
141+
})
142+
for _, tool_item := range _Tooljson.Choices[0].Message.ToolCalls {
143+
if tool_item.Type == "function" {
144+
var arguments_data map[string]interface{}
145+
e := json.Unmarshal([]byte(tool_item.Function.Arguments), &arguments_data)
146+
if e != nil {
147+
return e
148+
}
149+
toolRes := functionMap[tool_item.Function.Name](arguments_data)
150+
messages = append(messages, ToolMessage{
151+
Role: "tool",
152+
ToolCallID: tool_item.ID,
153+
Content: toolRes,
154+
})
155+
}
156+
}
157+
return client.ChatWithTools(model, messages, toolInfo, functionMap, during)
158+
}
159+
}
160+
var _json realChatToolStreamMessage
161+
e = json.Unmarshal([]byte(_res), &_json)
162+
if e != nil {
163+
return e
164+
}
165+
during(_json.Choices[0].Message.Content)
166+
}
167+
return nil
168+
}

‎chattool_test.go

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package openai
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"testing"
7+
)
8+
9+
func TestChatToolStream(t *testing.T) {
10+
client := NewClient(&ClientConfig{
11+
BaseUrl: "https://api.siliconflow.cn/v1",
12+
ApiKey: os.Getenv("APIKEY"),
13+
})
14+
client.ChatWithTools("deepseek-ai/DeepSeek-V3", []ToolMessage{
15+
{Role: "system", Content: "你是智能机器人,可以辅助用户操作,如果调用的函数出现了错误,直接把错误信息原样返回"},
16+
{Role: "user", Content: "帮我删除test.txt"},
17+
}, []ChatToolFunction{
18+
{Type: "function", Function: ChatToolFuctionDetail{
19+
Name: "deleteFile",
20+
Description: "给入文件名,可以删除对应的文件,返回删除的结果",
21+
Parameters: ChatToolParameters{
22+
Type: "object",
23+
Properties: map[string]ChatToolFuctionPropertie{
24+
"file": {
25+
Type: "string",
26+
Description: "文件名",
27+
},
28+
},
29+
Required: []string{"location"},
30+
},
31+
}},
32+
}, map[string]func(map[string]interface{}) string{
33+
"deleteFile": func(i map[string]interface{}) string {
34+
fmt.Println("假装真的删除了文件" + i["file"].(string))
35+
return "因为服务器正在被dinglz拿来玩原神,所以无法删除"
36+
},
37+
}, func(s string) {
38+
fmt.Println("回复:" + s)
39+
})
40+
}

0 commit comments

Comments
 (0)
Please sign in to comment.