Skip to content

Commit 6e6ad50

Browse files
authored
Chore lint (#91)
* refactor(test): 移除未使用的日志函数并清理导入 ci: 添加golangci配置以启用静态代码检查 style(usecase): 重命名MultiContent为UserInputMultiContent并更新结构体字段 * refactor: 重构模型列表和聊天模型创建逻辑 将模型列表过滤逻辑拆分为独立函数,优化代码结构 提取聊天模型创建逻辑到单独方法,减少重复代码 重构请求处理逻辑,分离URL构建和请求体构建
1 parent f027332 commit 6e6ad50

File tree

8 files changed

+536
-470
lines changed

8 files changed

+536
-470
lines changed

.golangci.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
version: "2"
2+
3+
linters:
4+
default: none
5+
enable:
6+
# - dupl # 重复代码
7+
# - errcheck # 检查是否存在未处理的错误
8+
# - govet
9+
# - ineffassign # 检测到对现有变量的赋值未被使用
10+
# - staticcheck # 检查 Go 代码中是否存在静态错误
11+
# - unused # 检查 Go 代码中未使用的常量、变量、函数和类型
12+
# - exhaustive # 检查枚举 switch 语句的详尽性
13+
# - exhaustruct # 检查结构体字段是否全部初始化
14+
# - goconst # 寻找可使用常量的地方
15+
# - gocritic # 检查 Go 代码中的错误
16+
# - gosec # 检查源代码中的安全问题
17+
# - intrange # 查找 for 循环可以使用的整数范围的位置
18+
# - misspell # 检查拼写错误
19+
# - nestif # 检查嵌套的 if 语句 复杂度
20+
# - nilnesserr # 报告检查 err != nil,但返回不同 nil 值错误的构造
21+
# - unconvert
22+
23+
# - funcorder # 没用
24+
# - nlreturn # return前要有空行 没用
25+
- gocognit # 检查函数 Cognitive Complexity 复杂度
26+
# - cyclop # 圈复杂度 没用
27+
# - err113 # 没用
28+
29+
settings:
30+
errcheck:
31+
exclude-functions:
32+
- (io.Closer).Close
33+
run:
34+
tests: false
35+
skip-dirs:
36+
- '(^|/)test($|/)'

AGENT.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1. 代码写完执行make lint, 必须通过

pkg/request/request.go

Lines changed: 70 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -53,90 +53,43 @@ func (c *Client) SetTransport(tr *http.Transport) {
5353
}
5454

5555
func sendRequest[T any](c *Client, method, path string, opts ...Opt) (*T, error) {
56-
u := url.URL{
57-
Scheme: c.scheme,
58-
Host: c.host,
59-
Path: path,
60-
}
6156
ctx := &Ctx{}
6257
rid := uuid.NewString()
6358

6459
for _, opt := range opts {
6560
opt(ctx)
6661
}
6762

68-
if len(ctx.query) > 0 {
69-
values := u.Query()
70-
for k, v := range ctx.query {
71-
values.Add(k, v)
72-
}
73-
u.RawQuery = values.Encode()
74-
}
75-
63+
urlStr := buildURL(c, path, ctx.query)
7664
if c.debug {
77-
log.Printf("[REQ:%s] url: %s", rid, u.String())
65+
log.Printf("[REQ:%s] url: %s", rid, urlStr)
7866
}
7967

80-
var body io.Reader
81-
var writer *multipart.Writer
82-
if ctx.body != nil {
83-
bs, err := json.Marshal(ctx.body)
84-
if err != nil {
85-
return nil, err
86-
}
87-
switch ctx.contentType {
88-
case "multipart/form-data":
89-
m := make(map[string]string)
90-
if err := json.Unmarshal(bs, &m); err != nil {
91-
return nil, err
92-
}
93-
buf := &bytes.Buffer{}
94-
writer = multipart.NewWriter(buf)
95-
for k, v := range m {
96-
if err := writer.WriteField(k, v); err != nil {
97-
return nil, err
98-
}
99-
}
100-
if err := writer.Close(); err != nil {
101-
return nil, err
102-
}
103-
body = buf
104-
case "application/x-www-form-urlencoded":
105-
m := make(map[string]string)
106-
if err := json.Unmarshal(bs, &m); err != nil {
107-
return nil, err
108-
}
109-
data := url.Values{}
110-
for k, v := range m {
111-
data.Add(k, v)
112-
}
113-
body = strings.NewReader(data.Encode())
114-
default:
115-
body = bytes.NewBuffer(bs)
116-
}
68+
body, contentType, err := buildBodyAndType(ctx)
69+
if err != nil {
70+
return nil, err
71+
}
11772

118-
if c.debug {
119-
buf := &bytes.Buffer{}
120-
if err := json.Indent(buf, bs, "", " "); err != nil {
121-
log.Printf("[REQ:%s] body: %s", rid, string(bs))
122-
} else {
123-
log.Printf("[REQ:%s] body: %s", rid, buf.String())
124-
}
73+
if c.debug && ctx.body != nil {
74+
bs, _ := json.Marshal(ctx.body)
75+
buf := &bytes.Buffer{}
76+
if err := json.Indent(buf, bs, "", " "); err != nil {
77+
log.Printf("[REQ:%s] body: %s", rid, string(bs))
78+
} else {
79+
log.Printf("[REQ:%s] body: %s", rid, buf.String())
12580
}
12681
}
127-
req, err := http.NewRequest(method, u.String(), body)
82+
83+
req, err := http.NewRequest(method, urlStr, body)
12884
if err != nil {
12985
return nil, err
13086
}
13187
for k, v := range ctx.header {
13288
req.Header.Add(k, v)
13389
}
134-
switch ctx.contentType {
135-
case "multipart/form-data":
136-
req.Header.Set("Content-Type", writer.FormDataContentType())
137-
case "application/x-www-form-urlencoded":
138-
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
139-
default:
90+
if contentType != "" {
91+
req.Header.Set("Content-Type", contentType)
92+
} else {
14093
req.Header.Set("Content-Type", "application/json")
14194
}
14295
if c.debug {
@@ -177,6 +130,58 @@ func sendRequest[T any](c *Client, method, path string, opts ...Opt) (*T, error)
177130
return &rr, nil
178131
}
179132

133+
func buildURL(c *Client, path string, q Query) string {
134+
u := url.URL{Scheme: c.scheme, Host: c.host, Path: path}
135+
if len(q) > 0 {
136+
values := u.Query()
137+
for k, v := range q {
138+
values.Add(k, v)
139+
}
140+
u.RawQuery = values.Encode()
141+
}
142+
return u.String()
143+
}
144+
145+
func buildBodyAndType(ctx *Ctx) (io.Reader, string, error) {
146+
if ctx.body == nil {
147+
return nil, "", nil
148+
}
149+
bs, err := json.Marshal(ctx.body)
150+
if err != nil {
151+
return nil, "", err
152+
}
153+
switch ctx.contentType {
154+
case "multipart/form-data":
155+
m := make(map[string]string)
156+
if err := json.Unmarshal(bs, &m); err != nil {
157+
return nil, "", err
158+
}
159+
buf := &bytes.Buffer{}
160+
w := multipart.NewWriter(buf)
161+
for k, v := range m {
162+
if err := w.WriteField(k, v); err != nil {
163+
return nil, "", err
164+
}
165+
}
166+
if err := w.Close(); err != nil {
167+
return nil, "", err
168+
}
169+
return buf, w.FormDataContentType(), nil
170+
case "application/x-www-form-urlencoded":
171+
m := make(map[string]string)
172+
if err := json.Unmarshal(bs, &m); err != nil {
173+
return nil, "", err
174+
}
175+
data := url.Values{}
176+
for k, v := range m {
177+
data.Add(k, v)
178+
}
179+
return strings.NewReader(data.Encode()), "application/x-www-form-urlencoded", nil
180+
default:
181+
return bytes.NewBuffer(bs), "application/json", nil
182+
}
183+
}
184+
180185
func Get[T any](c *Client, path string, opts ...Opt) (*T, error) {
181186
return sendRequest[T](c, http.MethodGet, path, opts...)
182187
}

test/embedding_test.go

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -252,30 +252,6 @@ func TestEmbedderCombinations(t *testing.T) {
252252
})
253253
}
254254

255-
func logHeadFloat(t *testing.T, v []float64) {
256-
n := 8
257-
if len(v) < n {
258-
n = len(v)
259-
}
260-
s := make([]string, 0, n)
261-
for i := 0; i < n; i++ {
262-
s = append(s, fmt.Sprintf("%.4f", v[i]))
263-
}
264-
t.Logf("head: %s", strings.Join(s, " "))
265-
}
266-
267-
func logSparseEntriesHead(t *testing.T, se []domain.SparseEmbedding) {
268-
n := 8
269-
if len(se) < n {
270-
n = len(se)
271-
}
272-
parts := make([]string, 0, n)
273-
for i := 0; i < n; i++ {
274-
parts = append(parts, fmt.Sprintf("%d:%.4f:%s", se[i].Index, se[i].Value, se[i].Token))
275-
}
276-
t.Logf("sparse_head: %s", strings.Join(parts, " "))
277-
}
278-
279255
func logFullFloat(t *testing.T, v []float64) {
280256
s := make([]string, 0, len(v))
281257
for i := 0; i < len(v); i++ {

test/modellist_test.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"os"
8+
"os/exec"
9+
"strings"
10+
"testing"
11+
12+
"github.com/chaitin/ModelKit/v2/domain"
13+
"github.com/chaitin/ModelKit/v2/usecase"
14+
)
15+
16+
func TestModelList_BaiZhiCloud_ByType(t *testing.T) {
17+
ctx := context.Background()
18+
mk := usecase.NewModelKit(nil)
19+
20+
apiKey := strings.TrimSpace(os.Getenv("baizhiapikey"))
21+
if apiKey == "" {
22+
t.Fatalf("missing baizhiapikey")
23+
}
24+
25+
baseURL := "https://model-square.app.baizhi.cloud/v1"
26+
provider := "baizhi-cloud"
27+
28+
types := []string{"chat", "embedding", "rerank", "code", "analysis", "analysis-vl"}
29+
30+
for _, tp := range types {
31+
tpLocal := tp
32+
name := fmt.Sprintf("provider=%s type=%s base_url=%s apiKey=present", provider, tpLocal, baseURL)
33+
t.Run(name, func(t *testing.T) {
34+
ml, err := mk.ModelList(ctx, &domain.ModelListReq{
35+
Provider: provider,
36+
BaseURL: baseURL,
37+
APIKey: apiKey,
38+
Type: tpLocal,
39+
})
40+
if err != nil {
41+
mlJSON, _ := json.Marshal(ml)
42+
t.Logf("test case: %s", name)
43+
t.Logf("modellist_resp=%s", string(mlJSON))
44+
t.Fatalf("ModelList error: %v", err)
45+
}
46+
47+
curlModels, curlErr := curlAllModels(ctx, baseURL, apiKey)
48+
if curlErr != nil {
49+
mlJSON, _ := json.Marshal(ml)
50+
t.Logf("test case: %s", name)
51+
t.Logf("modellist_resp=%s", string(mlJSON))
52+
t.Fatalf("curl models error: %v", curlErr)
53+
}
54+
55+
filtered := usecase.FilterModelsByType(curlModels, &domain.ModelListReq{Provider: provider, Type: tpLocal})
56+
57+
mlJSON, _ := json.Marshal(ml)
58+
filteredJSON, _ := json.Marshal(filtered)
59+
t.Logf("test case: %s", name)
60+
t.Logf("modellist_resp=%s", string(mlJSON))
61+
t.Logf("curl_filtered=%s", string(filteredJSON))
62+
63+
if !sameModelSets(ml.Models, filtered) {
64+
t.Fatalf("result mismatch: modellist_count=%d curl_filtered_count=%d", len(ml.Models), len(filtered))
65+
}
66+
})
67+
}
68+
}
69+
70+
func curlAllModels(ctx context.Context, baseURL, apiKey string) ([]domain.ModelListItem, error) {
71+
url := strings.TrimSuffix(baseURL, "/") + "/models"
72+
cmd := exec.CommandContext(ctx, "curl", "-s", "-H", "Authorization: Bearer "+apiKey, url)
73+
out, err := cmd.CombinedOutput()
74+
if err != nil {
75+
return nil, fmt.Errorf("curl failed: %v, output=%s", err, string(out))
76+
}
77+
var resp struct {
78+
Object string `json:"object"`
79+
Data []struct {
80+
ID string `json:"id"`
81+
} `json:"data"`
82+
}
83+
if uerr := json.Unmarshal(out, &resp); uerr != nil {
84+
return nil, fmt.Errorf("unmarshal failed: %v, body=%s", uerr, string(out))
85+
}
86+
items := make([]domain.ModelListItem, 0, len(resp.Data))
87+
for _, d := range resp.Data {
88+
items = append(items, domain.ModelListItem{Model: d.ID})
89+
}
90+
return items, nil
91+
}
92+
93+
func sameModelSets(a, b []domain.ModelListItem) bool {
94+
if len(a) != len(b) {
95+
return false
96+
}
97+
m := make(map[string]int, len(a))
98+
for _, it := range a {
99+
m[it.Model]++
100+
}
101+
for _, it := range b {
102+
if m[it.Model] == 0 {
103+
return false
104+
}
105+
m[it.Model]--
106+
}
107+
for _, v := range m {
108+
if v != 0 {
109+
return false
110+
}
111+
}
112+
return true
113+
}

test/rerank_test.go

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"fmt"
66
"os"
7-
"strings"
87
"testing"
98

109
"github.com/chaitin/ModelKit/v2/consts"
@@ -92,15 +91,3 @@ func TestRerankerCombinations(t *testing.T) {
9291
}
9392
})
9493
}
95-
96-
func logResultsHead(t *testing.T, v []domain.Result) {
97-
n := 5
98-
if len(v) < n {
99-
n = len(v)
100-
}
101-
s := make([]string, 0, n)
102-
for i := 0; i < n; i++ {
103-
s = append(s, fmt.Sprintf("%d:%.4f", v[i].Index, v[i].RelevanceScore))
104-
}
105-
t.Logf("head: %s", strings.Join(s, " "))
106-
}

0 commit comments

Comments
 (0)