diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..09e93e0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.* +!.gitignore +go.sum +env.yml +node_modules +package.json diff --git a/aigc.go b/aigc.go deleted file mode 100644 index f9540db..0000000 --- a/aigc.go +++ /dev/null @@ -1,83 +0,0 @@ -package zhipu - -import ( - "apigo.cc/ai/zhipu/zhipu" - "context" - "errors" - "time" -) - -func (ag *Agent) FastMakeImage(prompt, size, refImage string) ([]string, error) { - return ag.MakeImage(ModelCogView3Plus, prompt, size, refImage) -} - -func (ag *Agent) BestMakeImage(prompt, size, refImage string) ([]string, error) { - return ag.MakeImage(ModelCogView3, prompt, size, refImage) -} - -func (ag *Agent) MakeImage(model, prompt, size, refImage string) ([]string, error) { - c, err := zhipu.NewClient(zhipu.WithAPIKey(ag.config.ApiKey), zhipu.WithBaseURL(ag.config.Endpoint)) - if err != nil { - return nil, err - } - - cc := c.ImageGeneration(model).SetPrompt(prompt) - if size != "" { - cc.SetSize(size) - } - - if r, err := cc.Do(context.Background()); err == nil { - results := make([]string, 0) - for _, item := range r.Data { - results = append(results, item.URL) - } - return results, nil - } else { - return nil, err - } -} - -func (ag *Agent) FastMakeVideo(prompt, size, refImage string) ([]string, []string, error) { - return ag.MakeVideo(ModelCogVideoX, prompt, size, refImage) -} - -func (ag *Agent) BestMakeVideo(prompt, size, refImage string) ([]string, []string, error) { - return ag.MakeVideo(ModelCogVideoX, prompt, size, refImage) -} - -func (ag *Agent) MakeVideo(model, prompt, size, refImage string) ([]string, []string, error) { - c, err := zhipu.NewClient(zhipu.WithAPIKey(ag.config.ApiKey), zhipu.WithBaseURL(ag.config.Endpoint)) - if err != nil { - return nil, nil, err - } - - cc := c.VideoGeneration(model).SetPrompt(prompt) - if refImage != "" { - cc.SetImageURL(refImage) - } - - if resp, err := cc.Do(context.Background()); err == nil { - for i := 0; i < 1200; i++ { - r, err := c.AsyncResult(resp.ID).Do(context.Background()) - if err != nil { - return nil, nil, err - } - if r.TaskStatus == zhipu.VideoGenerationTaskStatusSuccess { - covers := make([]string, 0) - results := make([]string, 0) - for _, item := range r.VideoResult { - results = append(results, item.URL) - covers = append(covers, item.CoverImageURL) - } - return results, covers, nil - } - if r.TaskStatus == zhipu.VideoGenerationTaskStatusFail { - return nil, nil, errors.New("fail on task " + resp.ID) - } - time.Sleep(3 * time.Second) - } - return nil, nil, errors.New("timeout on task " + resp.ID) - } else { - return nil, nil, err - } -} diff --git a/chat.go b/chat.go index 2151f07..e82ca7b 100644 --- a/chat.go +++ b/chat.go @@ -1,70 +1,72 @@ package zhipu import ( - "apigo.cc/ai/agent" - "apigo.cc/ai/zhipu/zhipu" + "apigo.cc/ai/llm/llm" + "bytes" "context" + "encoding/binary" + "fmt" + "github.com/ssgo/u" + "github.com/yankeguo/zhipu" "strings" + "time" ) -func (ag *Agent) FastAsk(messages []agent.ChatMessage, callback func(answer string)) (string, agent.TokenUsage, error) { - return ag.Ask(messages, &agent.ChatModelConfig{ +func (lm *LLM) FastAsk(messages []llm.ChatMessage, callback func(answer string)) (string, llm.Usage, error) { + return lm.Ask(messages, llm.ChatConfig{ Model: ModelGLM4Flash, }, callback) } -func (ag *Agent) LongAsk(messages []agent.ChatMessage, callback func(answer string)) (string, agent.TokenUsage, error) { - return ag.Ask(messages, &agent.ChatModelConfig{ +func (lm *LLM) LongAsk(messages []llm.ChatMessage, callback func(answer string)) (string, llm.Usage, error) { + return lm.Ask(messages, llm.ChatConfig{ Model: ModelGLM4Long, }, callback) } -func (ag *Agent) BatterAsk(messages []agent.ChatMessage, callback func(answer string)) (string, agent.TokenUsage, error) { - return ag.Ask(messages, &agent.ChatModelConfig{ +func (lm *LLM) BatterAsk(messages []llm.ChatMessage, callback func(answer string)) (string, llm.Usage, error) { + return lm.Ask(messages, llm.ChatConfig{ Model: ModelGLM4Plus, }, callback) } -func (ag *Agent) BestAsk(messages []agent.ChatMessage, callback func(answer string)) (string, agent.TokenUsage, error) { - return ag.Ask(messages, &agent.ChatModelConfig{ +func (lm *LLM) BestAsk(messages []llm.ChatMessage, callback func(answer string)) (string, llm.Usage, error) { + return lm.Ask(messages, llm.ChatConfig{ Model: ModelGLM40520, }, callback) } -func (ag *Agent) MultiAsk(messages []agent.ChatMessage, callback func(answer string)) (string, agent.TokenUsage, error) { - return ag.Ask(messages, &agent.ChatModelConfig{ +func (lm *LLM) MultiAsk(messages []llm.ChatMessage, callback func(answer string)) (string, llm.Usage, error) { + return lm.Ask(messages, llm.ChatConfig{ Model: ModelGLM4VPlus, }, callback) } -func (ag *Agent) BestMultiAsk(messages []agent.ChatMessage, callback func(answer string)) (string, agent.TokenUsage, error) { - return ag.Ask(messages, &agent.ChatModelConfig{ +func (lm *LLM) BestMultiAsk(messages []llm.ChatMessage, callback func(answer string)) (string, llm.Usage, error) { + return lm.Ask(messages, llm.ChatConfig{ Model: ModelGLM4V, }, callback) } -func (ag *Agent) CodeInterpreterAsk(messages []agent.ChatMessage, callback func(answer string)) (string, agent.TokenUsage, error) { - return ag.Ask(messages, &agent.ChatModelConfig{ +func (lm *LLM) CodeInterpreterAsk(messages []llm.ChatMessage, callback func(answer string)) (string, llm.Usage, error) { + return lm.Ask(messages, llm.ChatConfig{ Model: ModelGLM4AllTools, - Tools: map[string]any{agent.ToolCodeInterpreter: nil}, + Tools: map[string]any{llm.ToolCodeInterpreter: nil}, }, callback) } -func (ag *Agent) WebSearchAsk(messages []agent.ChatMessage, callback func(answer string)) (string, agent.TokenUsage, error) { - return ag.Ask(messages, &agent.ChatModelConfig{ +func (lm *LLM) WebSearchAsk(messages []llm.ChatMessage, callback func(answer string)) (string, llm.Usage, error) { + return lm.Ask(messages, llm.ChatConfig{ Model: ModelGLM4AllTools, - Tools: map[string]any{agent.ToolWebSearch: nil}, + Tools: map[string]any{llm.ToolWebSearch: nil}, }, callback) } -func (ag *Agent) Ask(messages []agent.ChatMessage, config *agent.ChatModelConfig, callback func(answer string)) (string, agent.TokenUsage, error) { - if config == nil { - config = &agent.ChatModelConfig{} - } - config.SetDefault(&ag.config.DefaultChatModelConfig) - c, err := zhipu.NewClient(zhipu.WithAPIKey(ag.config.ApiKey), zhipu.WithBaseURL(ag.config.Endpoint)) +func (lm *LLM) Ask(messages []llm.ChatMessage, config llm.ChatConfig, callback func(answer string)) (string, llm.Usage, error) { + config.SetDefault(&lm.config.ChatConfig) + c, err := zhipu.NewClient(zhipu.WithAPIKey(lm.config.ApiKey), zhipu.WithBaseURL(lm.config.Endpoint)) if err != nil { - return "", agent.TokenUsage{}, err + return "", llm.Usage{}, err } cc := c.ChatCompletion(config.GetModel()) @@ -76,27 +78,34 @@ func (ag *Agent) Ask(messages []agent.ChatMessage, config *agent.ChatModelConfig part := zhipu.ChatCompletionMultiContent{} part.Type = NameMap[inPart.Type] switch inPart.Type { - case agent.TypeText: + case llm.TypeText: part.Text = inPart.Content - case agent.TypeImage: + case llm.TypeImage: part.ImageURL = &zhipu.URLItem{URL: inPart.Content} - case agent.TypeVideo: - part.VideoURL = &zhipu.URLItem{URL: inPart.Content} + //case llm.TypeVideo: + // part.VideoURL = &zhipu.URLItem{URL: inPart.Content} } contents[j] = part } } - cc.AddMessage(zhipu.ChatCompletionMultiMessage{ - Role: NameMap[msg.Role], - Content: contents, - }) + if len(contents) == 1 && contents[0].Type == llm.TypeText { + cc.AddMessage(zhipu.ChatCompletionMessage{ + Role: NameMap[msg.Role], + Content: contents[0].Text, + }) + } else { + cc.AddMessage(zhipu.ChatCompletionMultiMessage{ + Role: NameMap[msg.Role], + Content: contents, + }) + } } for name := range config.GetTools() { switch name { - case agent.ToolCodeInterpreter: + case llm.ToolCodeInterpreter: cc.AddTool(zhipu.ChatCompletionToolCodeInterpreter{}) - case agent.ToolWebSearch: + case llm.ToolWebSearch: cc.AddTool(zhipu.ChatCompletionToolWebBrowser{}) } } @@ -122,19 +131,65 @@ func (ag *Agent) Ask(messages []agent.ChatMessage, config *agent.ChatModelConfig }) } + if lm.config.Debug { + fmt.Println(cc.BatchMethod(), cc.BatchURL()) + fmt.Println(u.JsonP(cc.BatchBody())) + } + + t1 := time.Now().UnixMilli() if r, err := cc.Do(context.Background()); err == nil { + t2 := time.Now().UnixMilli() - t1 results := make([]string, 0) if r.Choices != nil { for _, ch := range r.Choices { results = append(results, ch.Message.Content) } } - return strings.Join(results, ""), agent.TokenUsage{ + return strings.Join(results, ""), llm.Usage{ AskTokens: r.Usage.PromptTokens, AnswerTokens: r.Usage.CompletionTokens, TotalTokens: r.Usage.TotalTokens, + UsedTime: t2, }, nil } else { - return "", agent.TokenUsage{}, err + return "", llm.Usage{}, err + } +} + +func (lm *LLM) FastEmbedding(text string) ([]byte, llm.Usage, error) { + return lm.Embedding(text, ModelEmbedding3) +} + +func (lm *LLM) BestEmbedding(text string) ([]byte, llm.Usage, error) { + return lm.Embedding(text, ModelEmbedding3) +} + +func (lm *LLM) Embedding(text, model string) ([]byte, llm.Usage, error) { + c, err := zhipu.NewClient(zhipu.WithAPIKey(lm.config.ApiKey), zhipu.WithBaseURL(lm.config.Endpoint)) + if err != nil { + return nil, llm.Usage{}, err + } + + cc := c.Embedding(model) + cc.SetInput(text) + t1 := time.Now().UnixMilli() + if r, err := cc.Do(context.Background()); err == nil { + t2 := time.Now().UnixMilli() - t1 + buf := new(bytes.Buffer) + if r.Data != nil { + for _, ch := range r.Data { + for _, v := range ch.Embedding { + _ = binary.Write(buf, binary.LittleEndian, float32(v)) + } + } + } + return buf.Bytes(), llm.Usage{ + AskTokens: r.Usage.PromptTokens, + AnswerTokens: r.Usage.CompletionTokens, + TotalTokens: r.Usage.TotalTokens, + UsedTime: t2, + }, nil + } else { + return nil, llm.Usage{}, err } } diff --git a/config.go b/config.go index a8e57b0..8f30389 100644 --- a/config.go +++ b/config.go @@ -1,22 +1,22 @@ package zhipu import ( - "apigo.cc/ai/agent" - "apigo.cc/ai/zhipu/zhipu" + "apigo.cc/ai/llm/llm" + "github.com/yankeguo/zhipu" ) -type Agent struct { - config agent.APIConfig +type LLM struct { + config llm.Config } var NameMap = map[string]string{ - agent.TypeText: zhipu.MultiContentTypeText, - agent.TypeImage: zhipu.MultiContentTypeImageURL, - agent.TypeVideo: zhipu.MultiContentTypeVideoURL, - agent.RoleSystem: zhipu.RoleSystem, - agent.RoleUser: zhipu.RoleUser, - agent.RoleAssistant: zhipu.RoleAssistant, - agent.RoleTool: zhipu.RoleTool, + llm.TypeText: zhipu.MultiContentTypeText, + llm.TypeImage: zhipu.MultiContentTypeImageURL, + //llm.TypeVideo: zhipu.MultiContentTypeVideoURL, + llm.RoleSystem: zhipu.RoleSystem, + llm.RoleUser: zhipu.RoleUser, + llm.RoleAssistant: zhipu.RoleAssistant, + llm.RoleTool: zhipu.RoleTool, } const ( @@ -40,8 +40,8 @@ const ( ModelCodeGeeX4 = "CodeGeeX-4" ) -func (ag *Agent) Support() agent.Support { - return agent.Support{ +func (lm *LLM) Support() llm.Support { + return llm.Support{ Ask: true, AskWithImage: true, AskWithVideo: true, @@ -54,7 +54,7 @@ func (ag *Agent) Support() agent.Support { } func init() { - agent.RegisterAgentMaker("zhipu", func(config agent.APIConfig) agent.Agent { - return &Agent{config: config} + llm.Register("zhipu", func(config llm.Config) llm.LLM { + return &LLM{config: config} }) } diff --git a/gc.go b/gc.go new file mode 100644 index 0000000..8a3b920 --- /dev/null +++ b/gc.go @@ -0,0 +1,94 @@ +package zhipu + +import ( + "apigo.cc/ai/llm/llm" + "context" + "errors" + "github.com/yankeguo/zhipu" + "time" +) + +func (lm *LLM) FastMakeImage(prompt string, config llm.GCConfig) ([]string, llm.Usage, error) { + config.Model = ModelCogView3Plus + return lm.MakeImage(prompt, config) +} + +func (lm *LLM) BestMakeImage(prompt string, config llm.GCConfig) ([]string, llm.Usage, error) { + config.Model = ModelCogView3 + return lm.MakeImage(prompt, config) +} + +func (lm *LLM) MakeImage(prompt string, config llm.GCConfig) ([]string, llm.Usage, error) { + c, err := zhipu.NewClient(zhipu.WithAPIKey(lm.config.ApiKey), zhipu.WithBaseURL(lm.config.Endpoint)) + if err != nil { + return nil, llm.Usage{}, err + } + + config.SetDefault(&lm.config.GCConfig) + cc := c.ImageGeneration(config.Model).SetPrompt(prompt) + //cc.SetSize(config.GetSize()) + + t1 := time.Now().UnixMilli() + if r, err := cc.Do(context.Background()); err == nil { + t2 := time.Now().UnixMilli() - t1 + results := make([]string, 0) + for _, item := range r.Data { + results = append(results, item.URL) + } + return results, llm.Usage{ + UsedTime: t2, + }, nil + } else { + return nil, llm.Usage{}, err + } +} + +func (lm *LLM) FastMakeVideo(prompt string, config llm.GCConfig) ([]string, []string, llm.Usage, error) { + config.Model = ModelCogVideoX + return lm.MakeVideo(prompt, config) +} + +func (lm *LLM) BestMakeVideo(prompt string, config llm.GCConfig) ([]string, []string, llm.Usage, error) { + config.Model = ModelCogVideoX + return lm.MakeVideo(prompt, config) +} + +func (lm *LLM) MakeVideo(prompt string, config llm.GCConfig) ([]string, []string, llm.Usage, error) { + c, err := zhipu.NewClient(zhipu.WithAPIKey(lm.config.ApiKey), zhipu.WithBaseURL(lm.config.Endpoint)) + if err != nil { + return nil, nil, llm.Usage{}, err + } + + config.SetDefault(&lm.config.GCConfig) + cc := c.VideoGeneration(config.Model).SetPrompt(prompt) + cc.SetImageURL(config.GetRef()) + + t1 := time.Now().UnixMilli() + if resp, err := cc.Do(context.Background()); err == nil { + t2 := time.Now().UnixMilli() - t1 + for i := 0; i < 1200; i++ { + r, err := c.AsyncResult(resp.ID).Do(context.Background()) + if err != nil { + return nil, nil, llm.Usage{}, err + } + if r.TaskStatus == zhipu.VideoGenerationTaskStatusSuccess { + covers := make([]string, 0) + results := make([]string, 0) + for _, item := range r.VideoResult { + results = append(results, item.URL) + covers = append(covers, item.CoverImageURL) + } + return results, covers, llm.Usage{ + UsedTime: t2, + }, nil + } + if r.TaskStatus == zhipu.VideoGenerationTaskStatusFail { + return nil, nil, llm.Usage{}, errors.New("fail on task " + resp.ID) + } + time.Sleep(3 * time.Second) + } + return nil, nil, llm.Usage{}, errors.New("timeout on task " + resp.ID) + } else { + return nil, nil, llm.Usage{}, err + } +} diff --git a/go.mod b/go.mod index fd5f3e2..d480a5c 100644 --- a/go.mod +++ b/go.mod @@ -3,15 +3,14 @@ module apigo.cc/ai/zhipu go 1.22 require ( - apigo.cc/ai/agent v0.0.1 - github.com/go-resty/resty/v2 v2.14.0 - github.com/golang-jwt/jwt/v5 v5.2.1 - github.com/stretchr/testify v1.9.0 + apigo.cc/ai/llm v0.0.4 + github.com/ssgo/u v1.7.9 + github.com/yankeguo/zhipu v0.1.2 ) require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/net v0.29.0 // indirect + github.com/go-resty/resty/v2 v2.14.0 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect + golang.org/x/net v0.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/zhipu/CHANGELOG.md b/zhipu/CHANGELOG.md deleted file mode 100644 index 8e1033f..0000000 --- a/zhipu/CHANGELOG.md +++ /dev/null @@ -1,108 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines. - -- - - -## v0.1.2 - 2024-08-15 -#### Bug Fixes -- add FinishReasonStopSequence - (01b4201) - GUO YANKE -#### Documentation -- update README.md [skip ci] - (e48a88b) - GUO YANKE -#### Features -- add videos/generations - (7261999) - GUO YANKE -#### Miscellaneous Chores -- relaxing go version to 1.18 - (6acc17c) - GUO YANKE - -- - - - -## v0.1.1 - 2024-07-17 -#### Documentation -- update README.md [skip ci] - (695432a) - GUO YANKE -#### Features -- add support for GLM-4-AllTools - (9627a36) - GUO YANKE - -- - - - -## v0.1.0 - 2024-06-28 -#### Bug Fixes -- rename client function for batch list - (40ac05f) - GUO YANKE -#### Documentation -- update README.md [skip ci] - (6ce5754) - GUO YANKE -#### Features -- add knowledge capacity service - (4ce62b3) - GUO YANKE -#### Refactoring -- update batch service - (b92d438) - GUO YANKE -- update chat completion service - (19dd77f) - GUO YANKE -- update embedding service - (c1bbc2d) - GUO YANKE -- update file services - (7ef4d87) - GUO YANKE -- update fine tune services, using APIError - (15aed88) - GUO YANKE -- update fine tune services - (664523b) - GUO YANKE -- update image generation service - (a18e028) - GUO YANKE -- update knowledge services - (c7bfb73) - GUO YANKE - -- - - - -## v0.0.6 - 2024-06-28 -#### Features -- add batch support for result reader - (c062095) - GUO YANKE -- add fine tune services - (f172f51) - GUO YANKE -- add knowledge service - (09792b5) - GUO YANKE - -- - - - -## v0.0.5 - 2024-06-28 -#### Bug Fixes -- api error parsing - (60a17f4) - GUO YANKE -#### Features -- add batch service - (389aec3) - GUO YANKE -- add batch support for chat completions, image generations and embeddings - (c017ffd) - GUO YANKE -- add file edit/get/delete service - (8a4d309) - GUO YANKE -- add file create serivce - (6d2140b) - GUO YANKE - -- - - - -## v0.0.4 - 2024-06-26 -#### Bug Fixes -- remove Client.R(), hide resty for future removal - (dc2a4ca) - GUO YANKE -#### Features -- add meta support for charglm - (fdd20e7) - GUO YANKE -- add client option to custom http client - (c62d6a9) - GUO YANKE - -- - - - -## v0.0.3 - 2024-06-26 -#### Features -- add image generation service - (9f3f54f) - GUO YANKE -- add support for vision models - (2dcd82a) - GUO YANKE -- add embedding service - (f57806a) - GUO YANKE - -- - - - -## v0.0.2 - 2024-06-26 -#### Bug Fixes -- **(deps)** update golang-jwt/jwt to v5 - (2f76a57) - GUO YANKE -#### Features -- add constants for roles - (3d08a72) - GUO YANKE - -- - - - -## v0.0.1 - 2024-06-26 -#### Bug Fixes -- add json tag "omitempty" to various types - (bf81097) - GUO YANKE -#### Continuous Integration -- add github action workflows for testing - (5a64987) - GUO YANKE -#### Documentation -- update README.md [skip ci] - (d504f57) - GUO YANKE -#### Features -- add chat completion in stream mode - (130fe1d) - GUO YANKE -- add chat completion in non-stream mode - (2326e37) - GUO YANKE -- support debug option while creating client - (0f104d8) - GUO YANKE -- add APIError and APIErrorResponse - (1886d85) - GUO YANKE -- add client struct - (710d8e8) - GUO YANKE -#### Refactoring -- change signature of Client#createJWT since there is no reason to fail - (f0d7887) - GUO YANKE -#### Tests -- add client_test.go - (a3fc217) - GUO YANKE - -- - - - -Changelog generated by [cocogitto](https://github.com/cocogitto/cocogitto). \ No newline at end of file diff --git a/zhipu/LICENSE b/zhipu/LICENSE deleted file mode 100644 index 67dc60b..0000000 --- a/zhipu/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 Yanke G. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/zhipu/README.md b/zhipu/README.md deleted file mode 100644 index 555e351..0000000 --- a/zhipu/README.md +++ /dev/null @@ -1,280 +0,0 @@ -# zhipu - -[![Go Reference](https://pkg.go.dev/badge/github.com/yankeguo/zhipu.svg)](https://pkg.go.dev/github.com/yankeguo/zhipu) -[![go](https://github.com/yankeguo/zhipu/actions/workflows/go.yml/badge.svg)](https://github.com/yankeguo/zhipu/actions/workflows/go.yml) - -[中文文档](README.zh.md) - -A 3rd-Party Golang Client Library for Zhipu AI Platform - -## Usage - -### Install the package - -```bash -go get -u github.com/yankeguo/zhipu -``` - -### Create a client - -```go -// this will use environment variables ZHIPUAI_API_KEY -client, err := zhipu.NewClient() -// or you can specify the API key -client, err = zhipu.NewClient(zhipu.WithAPIKey("your api key")) -``` - -### Use the client - -**ChatCompletion** - -```go -service := client.ChatCompletion("glm-4-flash"). - AddMessage(zhipu.ChatCompletionMessage{ - Role: "user", - Content: "你好", - }) - -res, err := service.Do(context.Background()) - -if err != nil { - zhipu.GetAPIErrorCode(err) // get the API error code -} else { - println(res.Choices[0].Message.Content) -} -``` - -**ChatCompletion (Stream)** - -```go -service := client.ChatCompletion("glm-4-flash"). - AddMessage(zhipu.ChatCompletionMessage{ - Role: "user", - Content: "你好", - }).SetStreamHandler(func(chunk zhipu.ChatCompletionResponse) error { - println(chunk.Choices[0].Delta.Content) - return nil - }) - -res, err := service.Do(context.Background()) - -if err != nil { - zhipu.GetAPIErrorCode(err) // get the API error code -} else { - // this package will combine the stream chunks and build a final result mimicking the non-streaming API - println(res.Choices[0].Message.Content) -} -``` - -**ChatCompletion (Stream with GLM-4-AllTools)** - -```go -// CodeInterpreter -s := client.ChatCompletion("GLM-4-AllTools") -s.AddMessage(zhipu.ChatCompletionMultiMessage{ - Role: "user", - Content: []zhipu.ChatCompletionMultiContent{ - { - Type: "text", - Text: "计算[5,10,20,700,99,310,978,100]的平均值和方差。", - }, - }, -}) -s.AddTool(zhipu.ChatCompletionToolCodeInterpreter{ - Sandbox: zhipu.Ptr(CodeInterpreterSandboxAuto), -}) -s.SetStreamHandler(func(chunk zhipu.ChatCompletionResponse) error { - for _, c := range chunk.Choices { - for _, tc := range c.Delta.ToolCalls { - if tc.Type == ToolTypeCodeInterpreter && tc.CodeInterpreter != nil { - if tc.CodeInterpreter.Input != "" { - // DO SOMETHING - } - if len(tc.CodeInterpreter.Outputs) > 0 { - // DO SOMETHING - } - } - } - } - return nil -}) - -// WebBrowser -// CAUTION: NOT 'WebSearch' -s := client.ChatCompletion("GLM-4-AllTools") -s.AddMessage(zhipu.ChatCompletionMultiMessage{ - Role: "user", - Content: []zhipu.ChatCompletionMultiContent{ - { - Type: "text", - Text: "搜索下本周深圳天气如何", - }, - }, -}) -s.AddTool(zhipu.ChatCompletionToolWebBrowser{}) -s.SetStreamHandler(func(chunk zhipu.ChatCompletionResponse) error { - for _, c := range chunk.Choices { - for _, tc := range c.Delta.ToolCalls { - if tc.Type == ToolTypeWebBrowser && tc.WebBrowser != nil { - if tc.WebBrowser.Input != "" { - // DO SOMETHING - } - if len(tc.WebBrowser.Outputs) > 0 { - // DO SOMETHING - } - } - } - } - return nil -}) -s.Do(context.Background()) - -// DrawingTool -s := client.ChatCompletion("GLM-4-AllTools") -s.AddMessage(zhipu.ChatCompletionMultiMessage{ - Role: "user", - Content: []zhipu.ChatCompletionMultiContent{ - { - Type: "text", - Text: "画一个正弦函数图像", - }, - }, -}) -s.AddTool(zhipu.ChatCompletionToolDrawingTool{}) -s.SetStreamHandler(func(chunk zhipu.ChatCompletionResponse) error { - for _, c := range chunk.Choices { - for _, tc := range c.Delta.ToolCalls { - if tc.Type == ToolTypeDrawingTool && tc.DrawingTool != nil { - if tc.DrawingTool.Input != "" { - // DO SOMETHING - } - if len(tc.DrawingTool.Outputs) > 0 { - // DO SOMETHING - } - } - } - } - return nil -}) -s.Do(context.Background()) -``` - -**Embedding** - -```go -service := client.Embedding("embedding-v2").SetInput("你好呀") -service.Do(context.Background()) -``` - -**Image Generation** - -```go -service := client.ImageGeneration("cogview-3").SetPrompt("一只可爱的小猫咪") -service.Do(context.Background()) -``` - -**Video Generation** - -```go -service := client.VideoGeneration("cogvideox").SetPrompt("一只可爱的小猫咪") -resp, err := service.Do(context.Background()) - -for { - result, err := client.AsyncResult(resp.ID).Do(context.Background()) - - if result.TaskStatus == zhipu.VideoGenerationTaskStatusSuccess { - _ = result.VideoResult[0].URL - _ = result.VideoResult[0].CoverImageURL - break - } - - if result.TaskStatus != zhipu.VideoGenerationTaskStatusProcessing { - break - } - - time.Sleep(5 * time.Second) -} -``` - -**Upload File (Retrieval)** - -```go -service := client.FileCreate(zhipu.FilePurposeRetrieval) -service.SetLocalFile(filepath.Join("testdata", "test-file.txt")) -service.SetKnowledgeID("your-knowledge-id") - -service.Do(context.Background()) -``` - -**Upload File (Fine-Tune)** - -```go -service := client.FileCreate(zhipu.FilePurposeFineTune) -service.SetLocalFile(filepath.Join("testdata", "test-file.jsonl")) -service.Do(context.Background()) -``` - -**Batch Create** - -```go -service := client.BatchCreate(). - SetInputFileID("fileid"). - SetCompletionWindow(zhipu.BatchCompletionWindow24h). - SetEndpoint(BatchEndpointV4ChatCompletions) -service.Do(context.Background()) -``` - -**Knowledge Base** - -```go -client.KnowledgeCreate("") -client.KnowledgeEdit("") -``` - -**Fine Tune** - -```go -client.FineTuneCreate("") -``` - -### Batch Support - -**Batch File Writer** - -```go -f, err := os.OpenFile("batch.jsonl", os.O_CREATE|os.O_WRONLY, 0644) - -bw := zhipu.NewBatchFileWriter(f) - -bw.Add("action_1", client.ChatCompletion("glm-4-flash"). - AddMessage(zhipu.ChatCompletionMessage{ - Role: "user", - Content: "你好", - })) -bw.Add("action_2", client.Embedding("embedding-v2").SetInput("你好呀")) -bw.Add("action_3", client.ImageGeneration("cogview-3").SetPrompt("一只可爱的小猫咪")) -``` - -**Batch Result Reader** - -```go -br := zhipu.NewBatchResultReader[zhipu.ChatCompletionResponse](r) - -for { - var res zhipu.BatchResult[zhipu.ChatCompletionResponse] - err := br.Read(&res) - if err != nil { - break - } -} -``` - -## Donation - -Executing unit tests will actually call the ChatGLM API and consume my quota. Please donate and thank you for your support! - - - -## Credits - -GUO YANKE, MIT License diff --git a/zhipu/README.zh.md b/zhipu/README.zh.md deleted file mode 100644 index 2563f05..0000000 --- a/zhipu/README.zh.md +++ /dev/null @@ -1,278 +0,0 @@ -# zhipu - -[![Go Reference](https://pkg.go.dev/badge/github.com/yankeguo/zhipu.svg)](https://pkg.go.dev/github.com/yankeguo/zhipu) -[![go](https://github.com/yankeguo/zhipu/actions/workflows/go.yml/badge.svg)](https://github.com/yankeguo/zhipu/actions/workflows/go.yml) - -Zhipu AI 平台第三方 Golang 客户端库 - -## 用法 - -### 安装库 - -```bash -go get -u github.com/yankeguo/zhipu -``` - -### 创建客户端 - -```go -// 默认使用环境变量 ZHIPUAI_API_KEY -client, err := zhipu.NewClient() -// 或者手动指定密钥 -client, err = zhipu.NewClient(zhipu.WithAPIKey("your api key")) -``` - -### 使用客户端 - -**ChatCompletion(大语言模型)** - -```go -service := client.ChatCompletion("glm-4-flash"). - AddMessage(zhipu.ChatCompletionMessage{ - Role: "user", - Content: "你好", - }) - -res, err := service.Do(context.Background()) - -if err != nil { - zhipu.GetAPIErrorCode(err) // get the API error code -} else { - println(res.Choices[0].Message.Content) -} -``` - -**ChatCompletion(流式调用大语言模型)** - -```go -service := client.ChatCompletion("glm-4-flash"). - AddMessage(zhipu.ChatCompletionMessage{ - Role: "user", - Content: "你好", - }).SetStreamHandler(func(chunk zhipu.ChatCompletionResponse) error { - println(chunk.Choices[0].Delta.Content) - return nil - }) - -res, err := service.Do(context.Background()) - -if err != nil { - zhipu.GetAPIErrorCode(err) // get the API error code -} else { - // this package will combine the stream chunks and build a final result mimicking the non-streaming API - println(res.Choices[0].Message.Content) -} -``` - -**ChatCompletion(流式调用大语言工具模型GLM-4-AllTools)** - -```go -// CodeInterpreter -s := client.ChatCompletion("GLM-4-AllTools") -s.AddMessage(zhipu.ChatCompletionMultiMessage{ - Role: "user", - Content: []zhipu.ChatCompletionMultiContent{ - { - Type: "text", - Text: "计算[5,10,20,700,99,310,978,100]的平均值和方差。", - }, - }, -}) -s.AddTool(zhipu.ChatCompletionToolCodeInterpreter{ - Sandbox: zhipu.Ptr(CodeInterpreterSandboxAuto), -}) -s.SetStreamHandler(func(chunk zhipu.ChatCompletionResponse) error { - for _, c := range chunk.Choices { - for _, tc := range c.Delta.ToolCalls { - if tc.Type == ToolTypeCodeInterpreter && tc.CodeInterpreter != nil { - if tc.CodeInterpreter.Input != "" { - // DO SOMETHING - } - if len(tc.CodeInterpreter.Outputs) > 0 { - // DO SOMETHING - } - } - } - } - return nil -}) - -// WebBrowser -// CAUTION: NOT 'WebSearch' -s := client.ChatCompletion("GLM-4-AllTools") -s.AddMessage(zhipu.ChatCompletionMultiMessage{ - Role: "user", - Content: []zhipu.ChatCompletionMultiContent{ - { - Type: "text", - Text: "搜索下本周深圳天气如何", - }, - }, -}) -s.AddTool(zhipu.ChatCompletionToolWebBrowser{}) -s.SetStreamHandler(func(chunk zhipu.ChatCompletionResponse) error { - for _, c := range chunk.Choices { - for _, tc := range c.Delta.ToolCalls { - if tc.Type == ToolTypeWebBrowser && tc.WebBrowser != nil { - if tc.WebBrowser.Input != "" { - // DO SOMETHING - } - if len(tc.WebBrowser.Outputs) > 0 { - // DO SOMETHING - } - } - } - } - return nil -}) -s.Do(context.Background()) - -// DrawingTool -s := client.ChatCompletion("GLM-4-AllTools") -s.AddMessage(zhipu.ChatCompletionMultiMessage{ - Role: "user", - Content: []zhipu.ChatCompletionMultiContent{ - { - Type: "text", - Text: "画一个正弦函数图像", - }, - }, -}) -s.AddTool(zhipu.ChatCompletionToolDrawingTool{}) -s.SetStreamHandler(func(chunk zhipu.ChatCompletionResponse) error { - for _, c := range chunk.Choices { - for _, tc := range c.Delta.ToolCalls { - if tc.Type == ToolTypeDrawingTool && tc.DrawingTool != nil { - if tc.DrawingTool.Input != "" { - // DO SOMETHING - } - if len(tc.DrawingTool.Outputs) > 0 { - // DO SOMETHING - } - } - } - } - return nil -}) -s.Do(context.Background()) -``` - -**Embedding** - -```go -service := client.Embedding("embedding-v2").SetInput("你好呀") -service.Do(context.Background()) -``` - -**ImageGeneration(图像生成)** - -```go -service := client.ImageGeneration("cogview-3").SetPrompt("一只可爱的小猫咪") -service.Do(context.Background()) -``` - -**VideoGeneration(视频生成)** - -```go -service := client.VideoGeneration("cogvideox").SetPrompt("一只可爱的小猫咪") -resp, err := service.Do(context.Background()) - -for { - result, err := client.AsyncResult(resp.ID).Do(context.Background()) - - if result.TaskStatus == zhipu.VideoGenerationTaskStatusSuccess { - _ = result.VideoResult[0].URL - _ = result.VideoResult[0].CoverImageURL - break - } - - if result.TaskStatus != zhipu.VideoGenerationTaskStatusProcessing { - break - } - - time.Sleep(5 * time.Second) -} -``` - -**UploadFile(上传文件用于取回)** - -```go -service := client.FileCreate(zhipu.FilePurposeRetrieval) -service.SetLocalFile(filepath.Join("testdata", "test-file.txt")) -service.SetKnowledgeID("your-knowledge-id") - -service.Do(context.Background()) -``` - -**UploadFile(上传文件用于微调)** - -```go -service := client.FileCreate(zhipu.FilePurposeFineTune) -service.SetLocalFile(filepath.Join("testdata", "test-file.jsonl")) -service.Do(context.Background()) -``` - -**BatchCreate(创建批量任务)** - -```go -service := client.BatchCreate(). - SetInputFileID("fileid"). - SetCompletionWindow(zhipu.BatchCompletionWindow24h). - SetEndpoint(BatchEndpointV4ChatCompletions) -service.Do(context.Background()) -``` - -**KnowledgeBase(知识库)** - -```go -client.KnowledgeCreate("") -client.KnowledgeEdit("") -``` - -**FineTune(微调)** - -```go -client.FineTuneCreate("") -``` - -### 批量任务辅助工具 - -**批量任务文件创建** - -```go -f, err := os.OpenFile("batch.jsonl", os.O_CREATE|os.O_WRONLY, 0644) - -bw := zhipu.NewBatchFileWriter(f) - -bw.Add("action_1", client.ChatCompletion("glm-4-flash"). - AddMessage(zhipu.ChatCompletionMessage{ - Role: "user", - Content: "你好", - })) -bw.Add("action_2", client.Embedding("embedding-v2").SetInput("你好呀")) -bw.Add("action_3", client.ImageGeneration("cogview-3").SetPrompt("一只可爱的小猫咪")) -``` - -**批量任务结果解析** - -```go -br := zhipu.NewBatchResultReader[zhipu.ChatCompletionResponse](r) - -for { - var res zhipu.BatchResult[zhipu.ChatCompletionResponse] - err := br.Read(&res) - if err != nil { - break - } -} -``` - -## 赞助 - -执行单元测试会真实调用GLM接口,消耗我充值的额度,开发不易,请微信扫码捐赠,感谢您的支持! - - - -## 许可证 - -GUO YANKE, MIT License diff --git a/zhipu/async_result.go b/zhipu/async_result.go deleted file mode 100644 index 5e51db1..0000000 --- a/zhipu/async_result.go +++ /dev/null @@ -1,63 +0,0 @@ -package zhipu - -import ( - "context" - - "github.com/go-resty/resty/v2" -) - -// AsyncResultService creates a new async result get service -type AsyncResultService struct { - client *Client - - id string -} - -// AsyncResultVideo is the video result of the AsyncResultService -type AsyncResultVideo struct { - URL string `json:"url"` - CoverImageURL string `json:"cover_image_url"` -} - -// AsyncResultResponse is the response of the AsyncResultService -type AsyncResultResponse struct { - Model string `json:"model"` - TaskStatus string `json:"task_status"` - RequestID string `json:"request_id"` - ID string `json:"id"` - VideoResult []AsyncResultVideo `json:"video_result"` -} - -// NewAsyncResultService creates a new async result get service -func NewAsyncResultService(client *Client) *AsyncResultService { - return &AsyncResultService{ - client: client, - } -} - -// SetID sets the id parameter -func (s *AsyncResultService) SetID(id string) *AsyncResultService { - s.id = id - return s -} - -func (s *AsyncResultService) Do(ctx context.Context) (res AsyncResultResponse, err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - - if resp, err = s.client.request(ctx). - SetResult(&res). - SetError(&apiError). - Get("async-result/" + s.id); err != nil { - return - } - - if resp.IsError() { - err = apiError - return - } - - return -} diff --git a/zhipu/async_result_test.go b/zhipu/async_result_test.go deleted file mode 100644 index f4a78b5..0000000 --- a/zhipu/async_result_test.go +++ /dev/null @@ -1 +0,0 @@ -package zhipu diff --git a/zhipu/batch.go b/zhipu/batch.go deleted file mode 100644 index d661f56..0000000 --- a/zhipu/batch.go +++ /dev/null @@ -1,258 +0,0 @@ -package zhipu - -import ( - "context" - "encoding/json" - "strconv" - - "github.com/go-resty/resty/v2" -) - -const ( - BatchEndpointV4ChatCompletions = "/v4/chat/completions" - BatchEndpointV4ImagesGenerations = "/v4/images/generations" - BatchEndpointV4Embeddings = "/v4/embeddings" - BatchEndpointV4VideosGenerations = "/v4/videos/generations" - - BatchCompletionWindow24h = "24h" -) - -// BatchRequestCounts represents the counts of the batch requests. -type BatchRequestCounts struct { - Total int64 `json:"total"` - Completed int64 `json:"completed"` - Failed int64 `json:"failed"` -} - -// BatchItem represents a batch item. -type BatchItem struct { - ID string `json:"id"` - Object any `json:"object"` - Endpoint string `json:"endpoint"` - InputFileID string `json:"input_file_id"` - CompletionWindow string `json:"completion_window"` - Status string `json:"status"` - OutputFileID string `json:"output_file_id"` - ErrorFileID string `json:"error_file_id"` - CreatedAt int64 `json:"created_at"` - InProgressAt int64 `json:"in_progress_at"` - ExpiresAt int64 `json:"expires_at"` - FinalizingAt int64 `json:"finalizing_at"` - CompletedAt int64 `json:"completed_at"` - FailedAt int64 `json:"failed_at"` - ExpiredAt int64 `json:"expired_at"` - CancellingAt int64 `json:"cancelling_at"` - CancelledAt int64 `json:"cancelled_at"` - RequestCounts BatchRequestCounts `json:"request_counts"` - Metadata json.RawMessage `json:"metadata"` -} - -// BatchCreateService is a service to create a batch. -type BatchCreateService struct { - client *Client - - inputFileID string - endpoint string - completionWindow string - metadata any -} - -// NewBatchCreateService creates a new BatchCreateService. -func NewBatchCreateService(client *Client) *BatchCreateService { - return &BatchCreateService{client: client} -} - -// SetInputFileID sets the input file id for the batch. -func (s *BatchCreateService) SetInputFileID(inputFileID string) *BatchCreateService { - s.inputFileID = inputFileID - return s -} - -// SetEndpoint sets the endpoint for the batch. -func (s *BatchCreateService) SetEndpoint(endpoint string) *BatchCreateService { - s.endpoint = endpoint - return s -} - -// SetCompletionWindow sets the completion window for the batch. -func (s *BatchCreateService) SetCompletionWindow(window string) *BatchCreateService { - s.completionWindow = window - return s -} - -// SetMetadata sets the metadata for the batch. -func (s *BatchCreateService) SetMetadata(metadata any) *BatchCreateService { - s.metadata = metadata - return s -} - -// Do executes the batch create service. -func (s *BatchCreateService) Do(ctx context.Context) (res BatchItem, err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - - if resp, err = s.client.request(ctx). - SetBody(M{ - "input_file_id": s.inputFileID, - "endpoint": s.endpoint, - "completion_window": s.completionWindow, - "metadata": s.metadata, - }). - SetResult(&res). - SetError(&apiError). - Post("batches"); err != nil { - return - } - - if resp.IsError() { - err = apiError - } - - return -} - -// BatchGetService is a service to get a batch. -type BatchGetService struct { - client *Client - batchID string -} - -// BatchGetResponse represents the response of the batch get service. -type BatchGetResponse = BatchItem - -// NewBatchGetService creates a new BatchGetService. -func NewBatchGetService(client *Client) *BatchGetService { - return &BatchGetService{client: client} -} - -// SetBatchID sets the batch id for the batch get service. -func (s *BatchGetService) SetBatchID(batchID string) *BatchGetService { - s.batchID = batchID - return s -} - -// Do executes the batch get service. -func (s *BatchGetService) Do(ctx context.Context) (res BatchGetResponse, err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - - if resp, err = s.client.request(ctx). - SetPathParam("batch_id", s.batchID). - SetResult(&res). - SetError(&apiError). - Get("batches/{batch_id}"); err != nil { - return - } - - if resp.IsError() { - err = apiError - } - - return -} - -// BatchCancelService is a service to cancel a batch. -type BatchCancelService struct { - client *Client - batchID string -} - -// NewBatchCancelService creates a new BatchCancelService. -func NewBatchCancelService(client *Client) *BatchCancelService { - return &BatchCancelService{client: client} -} - -// SetBatchID sets the batch id for the batch cancel service. -func (s *BatchCancelService) SetBatchID(batchID string) *BatchCancelService { - s.batchID = batchID - return s -} - -// Do executes the batch cancel service. -func (s *BatchCancelService) Do(ctx context.Context) (err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - - if resp, err = s.client.request(ctx). - SetPathParam("batch_id", s.batchID). - SetBody(M{}). - SetError(&apiError). - Post("batches/{batch_id}/cancel"); err != nil { - return - } - - if resp.IsError() { - err = apiError - } - - return -} - -// BatchListService is a service to list batches. -type BatchListService struct { - client *Client - - after *string - limit *int -} - -// BatchListResponse represents the response of the batch list service. -type BatchListResponse struct { - Object string `json:"object"` - Data []BatchItem `json:"data"` - FirstID string `json:"first_id"` - LastID string `json:"last_id"` - HasMore bool `json:"has_more"` -} - -// NewBatchListService creates a new BatchListService. -func NewBatchListService(client *Client) *BatchListService { - return &BatchListService{client: client} -} - -// SetAfter sets the after cursor for the batch list service. -func (s *BatchListService) SetAfter(after string) *BatchListService { - s.after = &after - return s -} - -// SetLimit sets the limit for the batch list service. -func (s *BatchListService) SetLimit(limit int) *BatchListService { - s.limit = &limit - return s -} - -// Do executes the batch list service. -func (s *BatchListService) Do(ctx context.Context) (res BatchListResponse, err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - - req := s.client.request(ctx) - if s.after != nil { - req.SetQueryParam("after", *s.after) - } - if s.limit != nil { - req.SetQueryParam("limit", strconv.Itoa(*s.limit)) - } - - if resp, err = req. - SetResult(&res). - SetError(&apiError). - Get("batches"); err != nil { - return - } - - if resp.IsError() { - err = apiError - } - - return -} diff --git a/zhipu/batch_support.go b/zhipu/batch_support.go deleted file mode 100644 index 9427ef4..0000000 --- a/zhipu/batch_support.go +++ /dev/null @@ -1,63 +0,0 @@ -package zhipu - -import ( - "encoding/json" - "io" -) - -// BatchSupport is the interface for services with batch support. -type BatchSupport interface { - BatchMethod() string - BatchURL() string - BatchBody() any -} - -// BatchFileWriter is a writer for batch files. -type BatchFileWriter struct { - w io.Writer - je *json.Encoder -} - -// NewBatchFileWriter creates a new BatchFileWriter. -func NewBatchFileWriter(w io.Writer) *BatchFileWriter { - return &BatchFileWriter{w: w, je: json.NewEncoder(w)} -} - -// Write writes a batch file. -func (b *BatchFileWriter) Write(customID string, s BatchSupport) error { - return b.je.Encode(M{ - "custom_id": customID, - "method": s.BatchMethod(), - "url": s.BatchURL(), - "body": s.BatchBody(), - }) -} - -// BatchResultResponse is the response of a batch result. -type BatchResultResponse[T any] struct { - StatusCode int `json:"status_code"` - Body T `json:"body"` -} - -// BatchResult is the result of a batch. -type BatchResult[T any] struct { - ID string `json:"id"` - CustomID string `json:"custom_id"` - Response BatchResultResponse[T] `json:"response"` -} - -// BatchResultReader reads batch results. -type BatchResultReader[T any] struct { - r io.Reader - jd *json.Decoder -} - -// NewBatchResultReader creates a new BatchResultReader. -func NewBatchResultReader[T any](r io.Reader) *BatchResultReader[T] { - return &BatchResultReader[T]{r: r, jd: json.NewDecoder(r)} -} - -// Read reads a batch result. -func (r *BatchResultReader[T]) Read(out *BatchResult[T]) error { - return r.jd.Decode(out) -} diff --git a/zhipu/batch_support_test.go b/zhipu/batch_support_test.go deleted file mode 100644 index 59a19c6..0000000 --- a/zhipu/batch_support_test.go +++ /dev/null @@ -1,73 +0,0 @@ -package zhipu - -import ( - "bytes" - "io" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestBatchFileWriter(t *testing.T) { - client, err := NewClient() - require.NoError(t, err) - - buf := &bytes.Buffer{} - - w := NewBatchFileWriter(buf) - err = w.Write("batch-1", client.ChatCompletion("a").AddMessage(ChatCompletionMessage{ - Role: "user", Content: "hello", - })) - require.NoError(t, err) - err = w.Write("batch-2", client.Embedding("c").SetInput("whoa")) - require.NoError(t, err) - err = w.Write("batch-3", client.ImageGeneration("d").SetPrompt("whoa")) - require.NoError(t, err) - - require.Equal(t, `{"body":{"messages":[{"role":"user","content":"hello"}],"model":"a"},"custom_id":"batch-1","method":"POST","url":"/v4/chat/completions"} -{"body":{"input":"whoa","model":"c"},"custom_id":"batch-2","method":"POST","url":"/v4/embeddings"} -{"body":{"model":"d","prompt":"whoa"},"custom_id":"batch-3","method":"POST","url":"/v4/images/generations"} -`, buf.String()) -} - -func TestBatchResultReader(t *testing.T) { - result := ` - {"response":{"status_code":200,"body":{"created":1715959701,"usage":{"completion_tokens":26,"prompt_tokens":89,"total_tokens":115},"model":"glm-4","id":"8668357533850320547","choices":[{"finish_reason":"stop","index":0,"message":{"role":"assistant","content":"'''json\n{\n \"分类标签\": \"负面\",\n \"特定问题标注\": \"订单处理慢\"\n}\n'''"}}],"request_id":"615-request-1"}},"custom_id":"request-1","id":"batch_1791490810192076800"} -{"response":{"status_code":200,"body":{"created":1715959701,"usage":{"completion_tokens":22,"prompt_tokens":94,"total_tokens":116},"model":"glm-4","id":"8668368425887509080","choices":[{"finish_reason":"stop","index":0,"message":{"role":"assistant","content":"'''json\n{\n \"分类标签\": \"负面\",\n \"特定问题标注\": \"产品缺陷\"\n}\n'''"}}],"request_id":"616-request-2"}},"custom_id":"request-2","id":"batch_1791490810192076800"} -{"response":{"status_code":200,"body":{"created":1715959701,"usage":{"completion_tokens":25,"prompt_tokens":86,"total_tokens":111},"model":"glm-4","id":"8668355815863214980","choices":[{"finish_reason":"stop","index":0,"message":{"role":"assistant","content":"'''json\n{\n \"分类标签\": \"正面\",\n \"特定问题标注\": \"性价比\"\n}\n'''"}}],"request_id":"617-request-3"}},"custom_id":"request-3","id":"batch_1791490810192076800"} -{"response":{"status_code":200,"body":{"created":1715959701,"usage":{"completion_tokens":28,"prompt_tokens":89,"total_tokens":117},"model":"glm-4","id":"8668355815863214981","choices":[{"finish_reason":"stop","index":0,"message":{"role":"assistant","content":"'''json\n{\n \"分类标签\": \"负面\",\n \"特定问题标注\": \"说明文档不清晰\"\n}\n'''"}}],"request_id":"618-request-4"}},"custom_id":"request-4","id":"batch_1791490810192076800"} - -{"response":{"status_code":200,"body":{"created":1715959701,"usage":{"completion_tokens":26,"prompt_tokens":88,"total_tokens":114},"model":"glm-4","id":"8668357533850320546","choices":[{"finish_reason":"stop","index":0,"message":{"role":"assistant","content":"'''json\n{\n \"分类标签\": \"中性\",\n \"特定问题标注\": \"价格问题\"\n}\n'''"}}],"request_id":"619-request-5"}},"custom_id":"request-5","id":"batch_1791490810192076800"} - -{"response":{"status_code":200,"body":{"created":1715959701,"usage":{"completion_tokens":26,"prompt_tokens":90,"total_tokens":116},"model":"glm-4","id":"8668356159460662846","choices":[{"finish_reason":"stop","index":0,"message":{"role":"assistant","content":"'''json\n{\n \"分类标签\": \"负面\",\n \"特定问题标注\": \"配送延迟\"\n}\n'''"}}],"request_id":"620-request-6"}},"custom_id":"request-6","id":"batch_1791490810192076800"} - - -{"response":{"status_code":200,"body":{"created":1715959701,"usage":{"completion_tokens":27,"prompt_tokens":88,"total_tokens":115},"model":"glm-4","id":"8668357671289274638","choices":[{"finish_reason":"stop","index":0,"message":{"role":"assistant","content":"'''json\n{\n \"分类标签\": \"负面\",\n \"特定问题标注\": \"产品描述不符\"\n}\n'''"}}],"request_id":"621-request-7"}},"custom_id":"request-7","id":"batch_1791490810192076800"} -{"response":{"status_code":200,"body":{"created":1715959702,"usage":{"completion_tokens":26,"prompt_tokens":87,"total_tokens":113},"model":"glm-4","id":"8668355644064514872","choices":[{"finish_reason":"stop","index":0,"message":{"role":"assistant","content":"'''json\n{\n \"分类标签\": \"正面\",\n \"特定问题标注\": \"客服态度\"\n}\n'''"}}],"request_id":"622-request-8"}},"custom_id":"request-8","id":"batch_1791490810192076800"} - {"response":{"status_code":200,"body":{"created":1715959701,"usage":{"completion_tokens":29,"prompt_tokens":90,"total_tokens":119},"model":"glm-4","id":"8668357671289274639","choices":[{"finish_reason":"stop","index":0,"message":{"role":"assistant","content":"'''json\n{\n \"分类标签\": \"负面\",\n \"特定问题标注\": \"包装问题, 产品损坏\"\n}\n'''"}}],"request_id":"623-request-9"}},"custom_id":"request-9","id":"batch_1791490810192076800"} -{"response":{"status_code":200,"body":{"created":1715959701,"usage":{"completion_tokens":27,"prompt_tokens":87,"total_tokens":114},"model":"glm-4","id":"8668355644064514871","choices":[{"finish_reason":"stop","index":0,"message":{"role":"assistant","content":"'''json\n{\n \"分类标签\": \"正面\",\n \"特定问题标注\": \"产品描述不符\"\n}\n'''"}}],"request_id":"624-request-10"}},"custom_id":"request-10","id":"batch_1791490810192076800"} -` - - brr := NewBatchResultReader[ChatCompletionResponse](bytes.NewReader([]byte(result))) - - var count int - - for { - var res BatchResult[ChatCompletionResponse] - - err := brr.Read(&res) - - if err != nil { - if err == io.EOF { - err = nil - } - require.Equal(t, 10, count) - require.NoError(t, err) - break - } - - require.Equal(t, 200, res.Response.StatusCode) - - count++ - } -} diff --git a/zhipu/batch_test.go b/zhipu/batch_test.go deleted file mode 100644 index 8ce5aa5..0000000 --- a/zhipu/batch_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package zhipu - -import ( - "bytes" - "context" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestBatchServiceAll(t *testing.T) { - client, err := NewClient() - require.NoError(t, err) - - buf := &bytes.Buffer{} - - bfw := NewBatchFileWriter(buf) - err = bfw.Write("batch_1", client.ChatCompletion("glm-4-flash").AddMessage(ChatCompletionMessage{ - Role: RoleUser, Content: "你好呀", - })) - require.NoError(t, err) - err = bfw.Write("batch_2", client.ChatCompletion("glm-4-flash").AddMessage(ChatCompletionMessage{ - Role: RoleUser, Content: "你叫什么名字", - })) - require.NoError(t, err) - - res, err := client.FileCreate(FilePurposeBatch).SetFile(bytes.NewReader(buf.Bytes()), "batch.jsonl").Do(context.Background()) - require.NoError(t, err) - - fileID := res.FileCreateFineTuneResponse.ID - require.NotEmpty(t, fileID) - - res1, err := client.BatchCreate(). - SetInputFileID(fileID). - SetCompletionWindow(BatchCompletionWindow24h). - SetEndpoint(BatchEndpointV4ChatCompletions).Do(context.Background()) - require.NoError(t, err) - require.NotEmpty(t, res1.ID) - - res2, err := client.BatchGet(res1.ID).Do(context.Background()) - require.NoError(t, err) - require.Equal(t, res2.ID, res1.ID) - - res3, err := client.BatchList().Do(context.Background()) - require.NoError(t, err) - require.NotEmpty(t, res3.Data) - - err = client.BatchCancel(res1.ID).Do(context.Background()) - require.NoError(t, err) -} - -func TestBatchListService(t *testing.T) { - client, err := NewClient() - require.NoError(t, err) - - res, err := client.BatchList().Do(context.Background()) - require.NoError(t, err) - t.Log(res) -} diff --git a/zhipu/chat_completion.go b/zhipu/chat_completion.go deleted file mode 100644 index b00db85..0000000 --- a/zhipu/chat_completion.go +++ /dev/null @@ -1,577 +0,0 @@ -package zhipu - -import ( - "bufio" - "bytes" - "context" - "encoding/json" - "errors" - "io" - - "github.com/go-resty/resty/v2" -) - -const ( - RoleSystem = "system" - RoleUser = "user" - RoleAssistant = "assistant" - RoleTool = "tool" - - ToolChoiceAuto = "auto" - - FinishReasonStop = "stop" - FinishReasonStopSequence = "stop_sequence" - FinishReasonToolCalls = "tool_calls" - FinishReasonLength = "length" - FinishReasonSensitive = "sensitive" - FinishReasonNetworkError = "network_error" - - ToolTypeFunction = "function" - ToolTypeWebSearch = "web_search" - ToolTypeRetrieval = "retrieval" - - MultiContentTypeText = "text" - MultiContentTypeImageURL = "image_url" - MultiContentTypeVideoURL = "video_url" - - // New in GLM-4-AllTools - ToolTypeCodeInterpreter = "code_interpreter" - ToolTypeDrawingTool = "drawing_tool" - ToolTypeWebBrowser = "web_browser" - - CodeInterpreterSandboxNone = "none" - CodeInterpreterSandboxAuto = "auto" - - ChatCompletionStatusFailed = "failed" - ChatCompletionStatusCompleted = "completed" - ChatCompletionStatusRequiresAction = "requires_action" -) - -// ChatCompletionTool is the interface for chat completion tool -type ChatCompletionTool interface { - isChatCompletionTool() -} - -// ChatCompletionToolFunction is the function for chat completion -type ChatCompletionToolFunction struct { - Name string `json:"name"` - Description string `json:"description"` - Parameters any `json:"parameters"` -} - -func (ChatCompletionToolFunction) isChatCompletionTool() {} - -// ChatCompletionToolRetrieval is the retrieval for chat completion -type ChatCompletionToolRetrieval struct { - KnowledgeID string `json:"knowledge_id"` - PromptTemplate string `json:"prompt_template,omitempty"` -} - -func (ChatCompletionToolRetrieval) isChatCompletionTool() {} - -// ChatCompletionToolWebSearch is the web search for chat completion -type ChatCompletionToolWebSearch struct { - Enable *bool `json:"enable,omitempty"` - SearchQuery string `json:"search_query,omitempty"` - SearchResult bool `json:"search_result,omitempty"` -} - -func (ChatCompletionToolWebSearch) isChatCompletionTool() {} - -// ChatCompletionToolCodeInterpreter is the code interpreter for chat completion -// only in GLM-4-AllTools -type ChatCompletionToolCodeInterpreter struct { - Sandbox *string `json:"sandbox,omitempty"` -} - -func (ChatCompletionToolCodeInterpreter) isChatCompletionTool() {} - -// ChatCompletionToolDrawingTool is the drawing tool for chat completion -// only in GLM-4-AllTools -type ChatCompletionToolDrawingTool struct { - // no fields -} - -func (ChatCompletionToolDrawingTool) isChatCompletionTool() {} - -// ChatCompletionToolWebBrowser is the web browser for chat completion -type ChatCompletionToolWebBrowser struct { - // no fields -} - -func (ChatCompletionToolWebBrowser) isChatCompletionTool() {} - -// ChatCompletionUsage is the usage for chat completion -type ChatCompletionUsage struct { - PromptTokens int64 `json:"prompt_tokens"` - CompletionTokens int64 `json:"completion_tokens"` - TotalTokens int64 `json:"total_tokens"` -} - -// ChatCompletionWebSearch is the web search result for chat completion -type ChatCompletionWebSearch struct { - Icon string `json:"icon"` - Title string `json:"title"` - Link string `json:"link"` - Media string `json:"media"` - Content string `json:"content"` -} - -// ChatCompletionToolCallFunction is the function for chat completion tool call -type ChatCompletionToolCallFunction struct { - Name string `json:"name"` - Arguments json.RawMessage `json:"arguments"` -} - -// ChatCompletionToolCallCodeInterpreterOutput is the output for chat completion tool call code interpreter -type ChatCompletionToolCallCodeInterpreterOutput struct { - Type string `json:"type"` - Logs string `json:"logs"` - File string `json:"file"` -} - -// ChatCompletionToolCallCodeInterpreter is the code interpreter for chat completion tool call -type ChatCompletionToolCallCodeInterpreter struct { - Input string `json:"input"` - Outputs []ChatCompletionToolCallCodeInterpreterOutput `json:"outputs"` -} - -// ChatCompletionToolCallDrawingToolOutput is the output for chat completion tool call drawing tool -type ChatCompletionToolCallDrawingToolOutput struct { - Image string `json:"image"` -} - -// ChatCompletionToolCallDrawingTool is the drawing tool for chat completion tool call -type ChatCompletionToolCallDrawingTool struct { - Input string `json:"input"` - Outputs []ChatCompletionToolCallDrawingToolOutput `json:"outputs"` -} - -// ChatCompletionToolCallWebBrowserOutput is the output for chat completion tool call web browser -type ChatCompletionToolCallWebBrowserOutput struct { - Title string `json:"title"` - Link string `json:"link"` - Content string `json:"content"` -} - -// ChatCompletionToolCallWebBrowser is the web browser for chat completion tool call -type ChatCompletionToolCallWebBrowser struct { - Input string `json:"input"` - Outputs []ChatCompletionToolCallWebBrowserOutput `json:"outputs"` -} - -// ChatCompletionToolCall is the tool call for chat completion -type ChatCompletionToolCall struct { - ID string `json:"id"` - Type string `json:"type"` - Function *ChatCompletionToolCallFunction `json:"function,omitempty"` - CodeInterpreter *ChatCompletionToolCallCodeInterpreter `json:"code_interpreter,omitempty"` - DrawingTool *ChatCompletionToolCallDrawingTool `json:"drawing_tool,omitempty"` - WebBrowser *ChatCompletionToolCallWebBrowser `json:"web_browser,omitempty"` -} - -type ChatCompletionMessageType interface { - isChatCompletionMessageType() -} - -// ChatCompletionMessage is the message for chat completion -type ChatCompletionMessage struct { - Role string `json:"role"` - Content string `json:"content,omitempty"` - ToolCalls []ChatCompletionToolCall `json:"tool_calls,omitempty"` - ToolCallID string `json:"tool_call_id,omitempty"` -} - -func (ChatCompletionMessage) isChatCompletionMessageType() {} - -type ChatCompletionMultiContent struct { - Type string `json:"type"` - Text string `json:"text"` - ImageURL *URLItem `json:"image_url,omitempty"` - VideoURL *URLItem `json:"video_url,omitempty"` -} - -// ChatCompletionMultiMessage is the multi message for chat completion -type ChatCompletionMultiMessage struct { - Role string `json:"role"` - Content []ChatCompletionMultiContent `json:"content"` -} - -func (ChatCompletionMultiMessage) isChatCompletionMessageType() {} - -// ChatCompletionMeta is the meta for chat completion -type ChatCompletionMeta struct { - UserInfo string `json:"user_info"` - BotInfo string `json:"bot_info"` - UserName string `json:"user_name"` - BotName string `json:"bot_name"` -} - -// ChatCompletionChoice is the choice for chat completion -type ChatCompletionChoice struct { - Index int `json:"index"` - FinishReason string `json:"finish_reason"` - Delta ChatCompletionMessage `json:"delta"` // stream mode - Message ChatCompletionMessage `json:"message"` // non-stream mode -} - -// ChatCompletionResponse is the response for chat completion -type ChatCompletionResponse struct { - ID string `json:"id"` - Created int64 `json:"created"` - Model string `json:"model"` - Choices []ChatCompletionChoice `json:"choices"` - Usage ChatCompletionUsage `json:"usage"` - WebSearch []ChatCompletionWebSearch `json:"web_search"` - // Status is the status of the chat completion, only in GLM-4-AllTools - Status string `json:"status"` -} - -// ChatCompletionStreamHandler is the handler for chat completion stream -type ChatCompletionStreamHandler func(chunk ChatCompletionResponse) error - -var ( - chatCompletionStreamPrefix = []byte("data:") - chatCompletionStreamDone = []byte("[DONE]") -) - -// chatCompletionReduceResponse reduce the chunk to the response -func chatCompletionReduceResponse(out *ChatCompletionResponse, chunk ChatCompletionResponse) { - if len(out.Choices) == 0 { - out.Choices = append(out.Choices, ChatCompletionChoice{}) - } - - // basic - out.ID = chunk.ID - out.Created = chunk.Created - out.Model = chunk.Model - - // choices - if len(chunk.Choices) != 0 { - oc := &out.Choices[0] - cc := chunk.Choices[0] - - oc.Index = cc.Index - if cc.Delta.Role != "" { - oc.Message.Role = cc.Delta.Role - } - oc.Message.Content += cc.Delta.Content - oc.Message.ToolCalls = append(oc.Message.ToolCalls, cc.Delta.ToolCalls...) - if cc.FinishReason != "" { - oc.FinishReason = cc.FinishReason - } - } - - // usage - if chunk.Usage.CompletionTokens != 0 { - out.Usage.CompletionTokens = chunk.Usage.CompletionTokens - } - if chunk.Usage.PromptTokens != 0 { - out.Usage.PromptTokens = chunk.Usage.PromptTokens - } - if chunk.Usage.TotalTokens != 0 { - out.Usage.TotalTokens = chunk.Usage.TotalTokens - } - - // web search - out.WebSearch = append(out.WebSearch, chunk.WebSearch...) -} - -// chatCompletionDecodeStream decode the sse stream of chat completion -func chatCompletionDecodeStream(r io.Reader, fn func(chunk ChatCompletionResponse) error) (err error) { - br := bufio.NewReader(r) - - for { - var line []byte - - if line, err = br.ReadBytes('\n'); err != nil { - if errors.Is(err, io.EOF) { - err = nil - } - break - } - - line = bytes.TrimSpace(line) - - if len(line) == 0 { - continue - } - - if !bytes.HasPrefix(line, chatCompletionStreamPrefix) { - continue - } - - data := bytes.TrimSpace(line[len(chatCompletionStreamPrefix):]) - - if bytes.Equal(data, chatCompletionStreamDone) { - break - } - - if len(data) == 0 { - continue - } - - var chunk ChatCompletionResponse - if err = json.Unmarshal(data, &chunk); err != nil { - return - } - if err = fn(chunk); err != nil { - return - } - } - - return -} - -// ChatCompletionStreamService is the service for chat completion stream -type ChatCompletionService struct { - client *Client - - model string - requestID *string - doSample *bool - temperature *float64 - topP *float64 - maxTokens *int - stop []string - toolChoice *string - userID *string - meta *ChatCompletionMeta - - messages []any - tools []any - - streamHandler ChatCompletionStreamHandler -} - -var ( - _ BatchSupport = &ChatCompletionService{} -) - -// NewChatCompletionService creates a new ChatCompletionService. -func NewChatCompletionService(client *Client) *ChatCompletionService { - return &ChatCompletionService{ - client: client, - } -} - -func (s *ChatCompletionService) BatchMethod() string { - return "POST" -} - -func (s *ChatCompletionService) BatchURL() string { - return BatchEndpointV4ChatCompletions -} - -func (s *ChatCompletionService) BatchBody() any { - return s.buildBody() -} - -// SetModel set the model of the chat completion -func (s *ChatCompletionService) SetModel(model string) *ChatCompletionService { - s.model = model - return s -} - -// SetMeta set the meta of the chat completion, optional -func (s *ChatCompletionService) SetMeta(meta ChatCompletionMeta) *ChatCompletionService { - s.meta = &meta - return s -} - -// SetRequestID set the request id of the chat completion, optional -func (s *ChatCompletionService) SetRequestID(requestID string) *ChatCompletionService { - s.requestID = &requestID - return s -} - -// SetTemperature set the temperature of the chat completion, optional -func (s *ChatCompletionService) SetDoSample(doSample bool) *ChatCompletionService { - s.doSample = &doSample - return s -} - -// SetTemperature set the temperature of the chat completion, optional -func (s *ChatCompletionService) SetTemperature(temperature float64) *ChatCompletionService { - s.temperature = &temperature - return s -} - -// SetTopP set the top p of the chat completion, optional -func (s *ChatCompletionService) SetTopP(topP float64) *ChatCompletionService { - s.topP = &topP - return s -} - -// SetMaxTokens set the max tokens of the chat completion, optional -func (s *ChatCompletionService) SetMaxTokens(maxTokens int) *ChatCompletionService { - s.maxTokens = &maxTokens - return s -} - -// SetStop set the stop of the chat completion, optional -func (s *ChatCompletionService) SetStop(stop ...string) *ChatCompletionService { - s.stop = stop - return s -} - -// SetToolChoice set the tool choice of the chat completion, optional -func (s *ChatCompletionService) SetToolChoice(toolChoice string) *ChatCompletionService { - s.toolChoice = &toolChoice - return s -} - -// SetUserID set the user id of the chat completion, optional -func (s *ChatCompletionService) SetUserID(userID string) *ChatCompletionService { - s.userID = &userID - return s -} - -// SetStreamHandler set the stream handler of the chat completion, optional -// this will enable the stream mode -func (s *ChatCompletionService) SetStreamHandler(handler ChatCompletionStreamHandler) *ChatCompletionService { - s.streamHandler = handler - return s -} - -// AddMessage add the message to the chat completion -func (s *ChatCompletionService) AddMessage(messages ...ChatCompletionMessageType) *ChatCompletionService { - for _, message := range messages { - s.messages = append(s.messages, message) - } - return s -} - -// AddFunction add the function to the chat completion -func (s *ChatCompletionService) AddTool(tools ...ChatCompletionTool) *ChatCompletionService { - for _, tool := range tools { - switch tool := tool.(type) { - case ChatCompletionToolFunction: - s.tools = append(s.tools, map[string]any{ - "type": ToolTypeFunction, - ToolTypeFunction: tool, - }) - case ChatCompletionToolRetrieval: - s.tools = append(s.tools, map[string]any{ - "type": ToolTypeRetrieval, - ToolTypeRetrieval: tool, - }) - case ChatCompletionToolWebSearch: - s.tools = append(s.tools, map[string]any{ - "type": ToolTypeWebSearch, - ToolTypeWebSearch: tool, - }) - case ChatCompletionToolCodeInterpreter: - s.tools = append(s.tools, map[string]any{ - "type": ToolTypeCodeInterpreter, - ToolTypeCodeInterpreter: tool, - }) - case ChatCompletionToolDrawingTool: - s.tools = append(s.tools, map[string]any{ - "type": ToolTypeDrawingTool, - ToolTypeDrawingTool: tool, - }) - case ChatCompletionToolWebBrowser: - s.tools = append(s.tools, map[string]any{ - "type": ToolTypeWebBrowser, - ToolTypeWebBrowser: tool, - }) - } - } - return s -} - -func (s *ChatCompletionService) buildBody() M { - body := map[string]any{ - "model": s.model, - "messages": s.messages, - } - if s.requestID != nil { - body["request_id"] = *s.requestID - } - if s.doSample != nil { - body["do_sample"] = *s.doSample - } - if s.temperature != nil { - body["temperature"] = *s.temperature - } - if s.topP != nil { - body["top_p"] = *s.topP - } - if s.maxTokens != nil { - body["max_tokens"] = *s.maxTokens - } - if len(s.stop) != 0 { - body["stop"] = s.stop - } - if len(s.tools) != 0 { - body["tools"] = s.tools - } - if s.toolChoice != nil { - body["tool_choice"] = *s.toolChoice - } - if s.userID != nil { - body["user_id"] = *s.userID - } - if s.meta != nil { - body["meta"] = s.meta - } - return body -} - -// Do send the request of the chat completion and return the response -func (s *ChatCompletionService) Do(ctx context.Context) (res ChatCompletionResponse, err error) { - body := s.buildBody() - - streamHandler := s.streamHandler - - if streamHandler == nil { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - //fmt.Println(u.BMagenta(u.JsonP(body)), 111) - if resp, err = s.client.request(ctx).SetBody(body).SetResult(&res).SetError(&apiError).Post("chat/completions"); err != nil { - //fmt.Println(u.BRed(err.Error()), 2221) - return - } - if resp.IsError() { - err = apiError - //fmt.Println(u.BRed(err.Error()), 2222) - return - } - //fmt.Println(u.BGreen(u.JsonP(resp.Result())), resp.Status(), resp.Status(), 333) - return - } - - // stream mode - - body["stream"] = true - - var resp *resty.Response - - if resp, err = s.client.request(ctx).SetBody(body).SetDoNotParseResponse(true).Post("chat/completions"); err != nil { - return - } - defer resp.RawBody().Close() - - if resp.IsError() { - err = errors.New(resp.Status()) - return - } - - var choice ChatCompletionChoice - - if err = chatCompletionDecodeStream(resp.RawBody(), func(chunk ChatCompletionResponse) error { - // reduce the chunk to the response - chatCompletionReduceResponse(&res, chunk) - // invoke the stream handler - return streamHandler(chunk) - }); err != nil { - return - } - - res.Choices = append(res.Choices, choice) - - return -} diff --git a/zhipu/chat_completion_test.go b/zhipu/chat_completion_test.go deleted file mode 100644 index 8839850..0000000 --- a/zhipu/chat_completion_test.go +++ /dev/null @@ -1,251 +0,0 @@ -package zhipu - -import ( - "context" - "encoding/json" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestChatCompletionService(t *testing.T) { - client, err := NewClient() - require.NoError(t, err) - - s := client.ChatCompletion("glm-4-flash") - s.AddMessage(ChatCompletionMessage{ - Role: RoleUser, - Content: "你好呀", - }) - res, err := s.Do(context.Background()) - require.NoError(t, err) - require.NotEmpty(t, res.Choices) - choice := res.Choices[0] - require.Equal(t, FinishReasonStop, choice.FinishReason) - require.NotEmpty(t, choice.Message.Content) -} - -func TestChatCompletionServiceCharGLM(t *testing.T) { - client, err := NewClient() - require.NoError(t, err) - - s := client.ChatCompletion("charglm-3") - s.SetMeta( - ChatCompletionMeta{ - UserName: "啵酱", - UserInfo: "啵酱是小少爷", - BotName: "塞巴斯酱", - BotInfo: "塞巴斯酱是一个冷酷的恶魔管家", - }, - ).AddMessage(ChatCompletionMessage{ - Role: RoleUser, - Content: "早上好", - }) - res, err := s.Do(context.Background()) - require.NoError(t, err) - require.NotEmpty(t, res.Choices) - choice := res.Choices[0] - require.Contains(t, []string{FinishReasonStop, FinishReasonStopSequence}, choice.FinishReason) - require.NotEmpty(t, choice.Message.Content) -} - -func TestChatCompletionServiceAllToolsCodeInterpreter(t *testing.T) { - client, err := NewClient() - require.NoError(t, err) - - s := client.ChatCompletion("GLM-4-AllTools") - s.AddMessage(ChatCompletionMultiMessage{ - Role: "user", - Content: []ChatCompletionMultiContent{ - { - Type: "text", - Text: "计算[5,10,20,700,99,310,978,100]的平均值和方差。", - }, - }, - }) - s.AddTool(ChatCompletionToolCodeInterpreter{ - Sandbox: Ptr(CodeInterpreterSandboxAuto), - }) - - foundInterpreterInput := false - foundInterpreterOutput := false - - s.SetStreamHandler(func(chunk ChatCompletionResponse) error { - for _, c := range chunk.Choices { - for _, tc := range c.Delta.ToolCalls { - if tc.Type == ToolTypeCodeInterpreter && tc.CodeInterpreter != nil { - if tc.CodeInterpreter.Input != "" { - foundInterpreterInput = true - } - if len(tc.CodeInterpreter.Outputs) > 0 { - foundInterpreterOutput = true - } - } - } - } - buf, _ := json.MarshalIndent(chunk, "", " ") - t.Log(string(buf)) - return nil - }) - - res, err := s.Do(context.Background()) - require.True(t, foundInterpreterInput) - require.True(t, foundInterpreterOutput) - require.NotNil(t, res) - require.NoError(t, err) -} - -func TestChatCompletionServiceAllToolsDrawingTool(t *testing.T) { - client, err := NewClient() - require.NoError(t, err) - - s := client.ChatCompletion("GLM-4-AllTools") - s.AddMessage(ChatCompletionMultiMessage{ - Role: "user", - Content: []ChatCompletionMultiContent{ - { - Type: "text", - Text: "画一个正弦函数图像", - }, - }, - }) - s.AddTool(ChatCompletionToolDrawingTool{}) - - foundInput := false - foundOutput := false - outputImage := "" - - s.SetStreamHandler(func(chunk ChatCompletionResponse) error { - for _, c := range chunk.Choices { - for _, tc := range c.Delta.ToolCalls { - if tc.Type == ToolTypeDrawingTool && tc.DrawingTool != nil { - if tc.DrawingTool.Input != "" { - foundInput = true - } - if len(tc.DrawingTool.Outputs) > 0 { - foundOutput = true - } - for _, output := range tc.DrawingTool.Outputs { - if output.Image != "" { - outputImage = output.Image - } - } - } - } - } - buf, _ := json.MarshalIndent(chunk, "", " ") - t.Log(string(buf)) - return nil - }) - - res, err := s.Do(context.Background()) - require.True(t, foundInput) - require.True(t, foundOutput) - require.NotEmpty(t, outputImage) - t.Log(outputImage) - require.NotNil(t, res) - require.NoError(t, err) -} - -func TestChatCompletionServiceAllToolsWebBrowser(t *testing.T) { - client, err := NewClient() - require.NoError(t, err) - - s := client.ChatCompletion("GLM-4-AllTools") - s.AddMessage(ChatCompletionMultiMessage{ - Role: "user", - Content: []ChatCompletionMultiContent{ - { - Type: "text", - Text: "搜索下本周深圳天气如何", - }, - }, - }) - s.AddTool(ChatCompletionToolWebBrowser{}) - - foundInput := false - foundOutput := false - outputContent := "" - - s.SetStreamHandler(func(chunk ChatCompletionResponse) error { - for _, c := range chunk.Choices { - for _, tc := range c.Delta.ToolCalls { - if tc.Type == ToolTypeWebBrowser && tc.WebBrowser != nil { - if tc.WebBrowser.Input != "" { - foundInput = true - } - if len(tc.WebBrowser.Outputs) > 0 { - foundOutput = true - } - for _, output := range tc.WebBrowser.Outputs { - if output.Content != "" { - outputContent = output.Content - } - } - } - } - } - buf, _ := json.MarshalIndent(chunk, "", " ") - t.Log(string(buf)) - return nil - }) - - res, err := s.Do(context.Background()) - require.True(t, foundInput) - require.True(t, foundOutput) - require.NotEmpty(t, outputContent) - t.Log(outputContent) - require.NotNil(t, res) - require.NoError(t, err) -} - -func TestChatCompletionServiceStream(t *testing.T) { - client, err := NewClient() - require.NoError(t, err) - - var content string - - s := client.ChatCompletion("glm-4-flash").AddMessage(ChatCompletionMessage{ - Role: RoleUser, - Content: "你好呀", - }).SetStreamHandler(func(chunk ChatCompletionResponse) error { - content += chunk.Choices[0].Delta.Content - return nil - }) - res, err := s.Do(context.Background()) - require.NoError(t, err) - require.NotEmpty(t, res.Choices) - choice := res.Choices[0] - require.Equal(t, FinishReasonStop, choice.FinishReason) - require.NotEmpty(t, choice.Message.Content) - require.Equal(t, content, choice.Message.Content) -} - -func TestChatCompletionServiceVision(t *testing.T) { - client, err := NewClient() - require.NoError(t, err) - - s := client.ChatCompletion("glm-4v") - s.AddMessage(ChatCompletionMultiMessage{ - Role: RoleUser, - Content: []ChatCompletionMultiContent{ - { - Type: MultiContentTypeText, - Text: "图里有什么", - }, - { - Type: MultiContentTypeImageURL, - ImageURL: &URLItem{ - URL: "https://img1.baidu.com/it/u=1369931113,3388870256&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1703696400&t=f3028c7a1dca43a080aeb8239f09cc2f", - }, - }, - }, - }) - res, err := s.Do(context.Background()) - require.NoError(t, err) - require.NotEmpty(t, res.Choices) - require.NotZero(t, res.Usage.CompletionTokens) - choice := res.Choices[0] - require.Equal(t, FinishReasonStop, choice.FinishReason) - require.NotEmpty(t, choice.Message.Content) -} diff --git a/zhipu/client.go b/zhipu/client.go deleted file mode 100644 index 5f2aa9b..0000000 --- a/zhipu/client.go +++ /dev/null @@ -1,291 +0,0 @@ -package zhipu - -import ( - "context" - "errors" - "net/http" - "os" - "strconv" - "strings" - "time" - - "github.com/go-resty/resty/v2" - "github.com/golang-jwt/jwt/v5" -) - -const ( - envAPIKey = "ZHIPUAI_API_KEY" - envBaseURL = "ZHIPUAI_BASE_URL" - envDebug = "ZHIPUAI_DEBUG" - - defaultBaseURL = "https://open.bigmodel.cn/api/paas/v4" -) - -var ( - // ErrAPIKeyMissing is the error when the api key is missing - ErrAPIKeyMissing = errors.New("zhipu: api key is missing") - // ErrAPIKeyMalformed is the error when the api key is malformed - ErrAPIKeyMalformed = errors.New("zhipu: api key is malformed") -) - -type clientOptions struct { - baseURL string - apiKey string - client *http.Client - debug *bool -} - -// ClientOption is a function that configures the client -type ClientOption func(opts *clientOptions) - -// WithAPIKey set the api key of the client -func WithAPIKey(apiKey string) ClientOption { - return func(opts *clientOptions) { - opts.apiKey = apiKey - } -} - -// WithBaseURL set the base url of the client -func WithBaseURL(baseURL string) ClientOption { - return func(opts *clientOptions) { - opts.baseURL = baseURL - } -} - -// WithHTTPClient set the http client of the client -func WithHTTPClient(client *http.Client) ClientOption { - return func(opts *clientOptions) { - opts.client = client - } -} - -// WithDebug set the debug mode of the client -func WithDebug(debug bool) ClientOption { - return func(opts *clientOptions) { - opts.debug = new(bool) - *opts.debug = debug - } -} - -// Client is the client for zhipu ai platform -type Client struct { - client *resty.Client - debug bool - keyID string - keySecret []byte -} - -func (c *Client) createJWT() string { - timestamp := time.Now().UnixMilli() - exp := timestamp + time.Hour.Milliseconds()*24*7 - - t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ - "api_key": c.keyID, - "timestamp": timestamp, - "exp": exp, - }) - t.Header = map[string]interface{}{ - "alg": "HS256", - "sign_type": "SIGN", - } - - token, err := t.SignedString(c.keySecret) - if err != nil { - panic(err) - } - return token -} - -// request creates a new resty request with the jwt token and context -func (c *Client) request(ctx context.Context) *resty.Request { - return c.client.R().SetContext(ctx).SetHeader("Authorization", c.createJWT()) -} - -// NewClient creates a new client -// It will read the api key from the environment variable ZHIPUAI_API_KEY -// It will read the base url from the environment variable ZHIPUAI_BASE_URL -func NewClient(optFns ...ClientOption) (client *Client, err error) { - var opts clientOptions - for _, optFn := range optFns { - optFn(&opts) - } - // base url - if opts.baseURL == "" { - opts.baseURL = strings.TrimSpace(os.Getenv(envBaseURL)) - } - if opts.baseURL == "" { - opts.baseURL = defaultBaseURL - } - // api key - if opts.apiKey == "" { - opts.apiKey = strings.TrimSpace(os.Getenv(envAPIKey)) - } - if opts.apiKey == "" { - err = ErrAPIKeyMissing - return - } - // debug - if opts.debug == nil { - if debugStr := strings.TrimSpace(os.Getenv(envDebug)); debugStr != "" { - if debug, err1 := strconv.ParseBool(debugStr); err1 == nil { - opts.debug = &debug - } - } - } - - keyComponents := strings.SplitN(opts.apiKey, ".", 2) - - if len(keyComponents) != 2 { - err = ErrAPIKeyMalformed - return - } - - client = &Client{ - keyID: keyComponents[0], - keySecret: []byte(keyComponents[1]), - } - - if opts.client == nil { - client.client = resty.New() - } else { - client.client = resty.NewWithClient(opts.client) - } - - client.client = client.client.SetBaseURL(opts.baseURL) - - if opts.debug != nil { - client.client.SetDebug(*opts.debug) - client.debug = *opts.debug - } - return -} - -// BatchCreate creates a new BatchCreateService. -func (c *Client) BatchCreate() *BatchCreateService { - return NewBatchCreateService(c) -} - -// BatchGet creates a new BatchGetService. -func (c *Client) BatchGet(batchID string) *BatchGetService { - return NewBatchGetService(c).SetBatchID(batchID) -} - -// BatchCancel creates a new BatchCancelService. -func (c *Client) BatchCancel(batchID string) *BatchCancelService { - return NewBatchCancelService(c).SetBatchID(batchID) -} - -// BatchList creates a new BatchListService. -func (c *Client) BatchList() *BatchListService { - return NewBatchListService(c) -} - -// ChatCompletion creates a new ChatCompletionService. -func (c *Client) ChatCompletion(model string) *ChatCompletionService { - return NewChatCompletionService(c).SetModel(model) -} - -// Embedding embeds a list of text into a vector space. -func (c *Client) Embedding(model string) *EmbeddingService { - return NewEmbeddingService(c).SetModel(model) -} - -// FileCreate creates a new FileCreateService. -func (c *Client) FileCreate(purpose string) *FileCreateService { - return NewFileCreateService(c).SetPurpose(purpose) -} - -// FileEditService creates a new FileEditService. -func (c *Client) FileEdit(documentID string) *FileEditService { - return NewFileEditService(c).SetDocumentID(documentID) -} - -// FileList creates a new FileListService. -func (c *Client) FileList(purpose string) *FileListService { - return NewFileListService(c).SetPurpose(purpose) -} - -// FileDeleteService creates a new FileDeleteService. -func (c *Client) FileDelete(documentID string) *FileDeleteService { - return NewFileDeleteService(c).SetDocumentID(documentID) -} - -// FileGetService creates a new FileGetService. -func (c *Client) FileGet(documentID string) *FileGetService { - return NewFileGetService(c).SetDocumentID(documentID) -} - -// FileDownload creates a new FileDownloadService. -func (c *Client) FileDownload(fileID string) *FileDownloadService { - return NewFileDownloadService(c).SetFileID(fileID) -} - -// FineTuneCreate creates a new fine tune create service -func (c *Client) FineTuneCreate(model string) *FineTuneCreateService { - return NewFineTuneCreateService(c).SetModel(model) -} - -// FineTuneEventList creates a new fine tune event list service -func (c *Client) FineTuneEventList(jobID string) *FineTuneEventListService { - return NewFineTuneEventListService(c).SetJobID(jobID) -} - -// FineTuneGet creates a new fine tune get service -func (c *Client) FineTuneGet(jobID string) *FineTuneGetService { - return NewFineTuneGetService(c).SetJobID(jobID) -} - -// FineTuneList creates a new fine tune list service -func (c *Client) FineTuneList() *FineTuneListService { - return NewFineTuneListService(c) -} - -// FineTuneDelete creates a new fine tune delete service -func (c *Client) FineTuneDelete(jobID string) *FineTuneDeleteService { - return NewFineTuneDeleteService(c).SetJobID(jobID) -} - -// FineTuneCancel creates a new fine tune cancel service -func (c *Client) FineTuneCancel(jobID string) *FineTuneCancelService { - return NewFineTuneCancelService(c).SetJobID(jobID) -} - -// ImageGeneration creates a new image generation service -func (c *Client) ImageGeneration(model string) *ImageGenerationService { - return NewImageGenerationService(c).SetModel(model) -} - -// KnowledgeCreate creates a new knowledge create service -func (c *Client) KnowledgeCreate() *KnowledgeCreateService { - return NewKnowledgeCreateService(c) -} - -// KnowledgeEdit creates a new knowledge edit service -func (c *Client) KnowledgeEdit(knowledgeID string) *KnowledgeEditService { - return NewKnowledgeEditService(c).SetKnowledgeID(knowledgeID) -} - -// KnowledgeList list all the knowledge -func (c *Client) KnowledgeList() *KnowledgeListService { - return NewKnowledgeListService(c) -} - -// KnowledgeDelete creates a new knowledge delete service -func (c *Client) KnowledgeDelete(knowledgeID string) *KnowledgeDeleteService { - return NewKnowledgeDeleteService(c).SetKnowledgeID(knowledgeID) -} - -// KnowledgeGet creates a new knowledge get service -func (c *Client) KnowledgeCapacity() *KnowledgeCapacityService { - return NewKnowledgeCapacityService(c) -} - -// VideoGeneration creates a new video generation service -func (c *Client) VideoGeneration(model string) *VideoGenerationService { - return NewVideoGenerationService(c).SetModel(model) -} - -// AsyncResult creates a new async result get service -func (c *Client) AsyncResult(id string) *AsyncResultService { - return NewAsyncResultService(c).SetID(id) -} diff --git a/zhipu/client_test.go b/zhipu/client_test.go deleted file mode 100644 index dd4800d..0000000 --- a/zhipu/client_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package zhipu - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestClientR(t *testing.T) { - c, err := NewClient() - require.NoError(t, err) - // the only free api is to list fine-tuning jobs - res, err := c.request(context.Background()).Get("fine_tuning/jobs") - require.NoError(t, err) - require.True(t, res.IsSuccess()) -} diff --git a/zhipu/cog.toml b/zhipu/cog.toml deleted file mode 100644 index ee92ed8..0000000 --- a/zhipu/cog.toml +++ /dev/null @@ -1,25 +0,0 @@ -from_latest_tag = false -ignore_merge_commits = false -disable_changelog = false -disable_bump_commit = false -generate_mono_repository_global_tag = true -branch_whitelist = [] -skip_ci = "[skip ci]" -skip_untracked = false -pre_bump_hooks = [] -post_bump_hooks = [] -pre_package_bump_hooks = [] -post_package_bump_hooks = [] -tag_prefix = "v" - -[git_hooks] - -[commit_types] - -[changelog] -path = "CHANGELOG.md" -authors = [] - -[bump_profiles] - -[packages] diff --git a/zhipu/embedding.go b/zhipu/embedding.go deleted file mode 100644 index 45c672b..0000000 --- a/zhipu/embedding.go +++ /dev/null @@ -1,87 +0,0 @@ -package zhipu - -import ( - "context" - - "github.com/go-resty/resty/v2" -) - -// EmbeddingData is the data for each embedding. -type EmbeddingData struct { - Embedding []float64 `json:"embedding"` - Index int `json:"index"` - Object string `json:"object"` -} - -// EmbeddingResponse is the response from the embedding service. -type EmbeddingResponse struct { - Model string `json:"model"` - Data []EmbeddingData `json:"data"` - Object string `json:"object"` - Usage ChatCompletionUsage `json:"usage"` -} - -// EmbeddingService embeds a list of text into a vector space. -type EmbeddingService struct { - client *Client - - model string - input string -} - -var ( - _ BatchSupport = &EmbeddingService{} -) - -// NewEmbeddingService creates a new EmbeddingService. -func NewEmbeddingService(client *Client) *EmbeddingService { - return &EmbeddingService{client: client} -} - -func (s *EmbeddingService) BatchMethod() string { - return "POST" -} - -func (s *EmbeddingService) BatchURL() string { - return BatchEndpointV4Embeddings -} - -func (s *EmbeddingService) BatchBody() any { - return s.buildBody() -} - -// SetModel sets the model to use for the embedding. -func (s *EmbeddingService) SetModel(model string) *EmbeddingService { - s.model = model - return s -} - -// SetInput sets the input text to embed. -func (s *EmbeddingService) SetInput(input string) *EmbeddingService { - s.input = input - return s -} - -func (s *EmbeddingService) buildBody() M { - return M{"model": s.model, "input": s.input} -} - -func (s *EmbeddingService) Do(ctx context.Context) (res EmbeddingResponse, err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - - if resp, err = s.client.request(ctx). - SetBody(s.buildBody()). - SetResult(&res). - SetError(&apiError). - Post("embeddings"); err != nil { - return - } - if resp.IsError() { - err = apiError - return - } - return -} diff --git a/zhipu/embedding_test.go b/zhipu/embedding_test.go deleted file mode 100644 index 46f4aeb..0000000 --- a/zhipu/embedding_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package zhipu - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestEmbeddingService(t *testing.T) { - client, err := NewClient() - require.NoError(t, err) - - service := client.Embedding("embedding-2") - - resp, err := service.SetInput("你好").Do(context.Background()) - require.NoError(t, err) - require.NotZero(t, resp.Usage.TotalTokens) - require.NotEmpty(t, resp.Data) - require.NotEmpty(t, resp.Data[0].Embedding) -} diff --git a/zhipu/error.go b/zhipu/error.go deleted file mode 100644 index ea07ad7..0000000 --- a/zhipu/error.go +++ /dev/null @@ -1,58 +0,0 @@ -package zhipu - -type APIError struct { - Code string `json:"code"` - Message string `json:"message"` -} - -func (e APIError) Error() string { - return e.Message -} - -type APIErrorResponse struct { - APIError `json:"error"` -} - -func (e APIErrorResponse) Error() string { - return e.APIError.Error() -} - -// GetAPIErrorCode returns the error code of an API error. -func GetAPIErrorCode(err error) string { - if err == nil { - return "" - } - if e, ok := err.(APIError); ok { - return e.Code - } - if e, ok := err.(APIErrorResponse); ok { - return e.Code - } - if e, ok := err.(*APIError); ok && e != nil { - return e.Code - } - if e, ok := err.(*APIErrorResponse); ok && e != nil { - return e.Code - } - return "" -} - -// GetAPIErrorMessage returns the error message of an API error. -func GetAPIErrorMessage(err error) string { - if err == nil { - return "" - } - if e, ok := err.(APIError); ok { - return e.Message - } - if e, ok := err.(APIErrorResponse); ok { - return e.Message - } - if e, ok := err.(*APIError); ok && e != nil { - return e.Message - } - if e, ok := err.(*APIErrorResponse); ok && e != nil { - return e.Message - } - return err.Error() -} diff --git a/zhipu/error_test.go b/zhipu/error_test.go deleted file mode 100644 index 3f2fa08..0000000 --- a/zhipu/error_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package zhipu - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestAPIError(t *testing.T) { - err := APIError{ - Code: "code", - Message: "message", - } - require.Equal(t, "message", err.Error()) - require.Equal(t, "code", GetAPIErrorCode(err)) - require.Equal(t, "message", GetAPIErrorMessage(err)) -} - -func TestAPIErrorResponse(t *testing.T) { - err := APIErrorResponse{ - APIError: APIError{ - Code: "code", - Message: "message", - }, - } - require.Equal(t, "message", err.Error()) - require.Equal(t, "code", GetAPIErrorCode(err)) - require.Equal(t, "message", GetAPIErrorMessage(err)) -} - -func TestAPIErrorResponseFromDoc(t *testing.T) { - var res APIErrorResponse - err := json.Unmarshal([]byte(`{"error":{"code":"1002","message":"Authorization Token非法,请确认Authorization Token正确传递。"}}`), &res) - require.NoError(t, err) - require.Equal(t, "1002", res.Code) - require.Equal(t, "1002", GetAPIErrorCode(res)) -} diff --git a/zhipu/file.go b/zhipu/file.go deleted file mode 100644 index e8b66af..0000000 --- a/zhipu/file.go +++ /dev/null @@ -1,541 +0,0 @@ -package zhipu - -import ( - "context" - "errors" - "io" - "os" - "path/filepath" - "strconv" - - "github.com/go-resty/resty/v2" -) - -const ( - FilePurposeFineTune = "fine-tune" - FilePurposeRetrieval = "retrieval" - FilePurposeBatch = "batch" - - KnowledgeTypeArticle = 1 - KnowledgeTypeQADocument = 2 - KnowledgeTypeQASpreadsheet = 3 - KnowledgeTypeProductDatabaseSpreadsheet = 4 - KnowledgeTypeCustom = 5 -) - -// FileCreateService is a service to create a file. -type FileCreateService struct { - client *Client - - purpose string - - localFile string - file io.Reader - filename string - - customSeparator *string - sentenceSize *int - knowledgeID *string -} - -// FileCreateKnowledgeSuccessInfo is the success info of the FileCreateKnowledgeResponse. -type FileCreateKnowledgeSuccessInfo struct { - Filename string `json:"fileName"` - DocumentID string `json:"documentId"` -} - -// FileCreateKnowledgeFailedInfo is the failed info of the FileCreateKnowledgeResponse. -type FileCreateKnowledgeFailedInfo struct { - Filename string `json:"fileName"` - FailReason string `json:"failReason"` -} - -// FileCreateKnowledgeResponse is the response of the FileCreateService. -type FileCreateKnowledgeResponse struct { - SuccessInfos []FileCreateKnowledgeSuccessInfo `json:"successInfos"` - FailedInfos []FileCreateKnowledgeFailedInfo `json:"failedInfos"` -} - -// FileCreateFineTuneResponse is the response of the FileCreateService. -type FileCreateFineTuneResponse struct { - Bytes int64 `json:"bytes"` - CreatedAt int64 `json:"created_at"` - Filename string `json:"filename"` - Object string `json:"object"` - Purpose string `json:"purpose"` - ID string `json:"id"` -} - -// FileCreateResponse is the response of the FileCreateService. -type FileCreateResponse struct { - FileCreateFineTuneResponse - FileCreateKnowledgeResponse -} - -// NewFileCreateService creates a new FileCreateService. -func NewFileCreateService(client *Client) *FileCreateService { - return &FileCreateService{client: client} -} - -// SetLocalFile sets the local_file parameter of the FileCreateService. -func (s *FileCreateService) SetLocalFile(localFile string) *FileCreateService { - s.localFile = localFile - return s -} - -// SetFile sets the file parameter of the FileCreateService. -func (s *FileCreateService) SetFile(file io.Reader, filename string) *FileCreateService { - s.file = file - s.filename = filename - return s -} - -// SetPurpose sets the purpose parameter of the FileCreateService. -func (s *FileCreateService) SetPurpose(purpose string) *FileCreateService { - s.purpose = purpose - return s -} - -// SetCustomSeparator sets the custom_separator parameter of the FileCreateService. -func (s *FileCreateService) SetCustomSeparator(customSeparator string) *FileCreateService { - s.customSeparator = &customSeparator - return s -} - -// SetSentenceSize sets the sentence_size parameter of the FileCreateService. -func (s *FileCreateService) SetSentenceSize(sentenceSize int) *FileCreateService { - s.sentenceSize = &sentenceSize - return s -} - -// SetKnowledgeID sets the knowledge_id parameter of the FileCreateService. -func (s *FileCreateService) SetKnowledgeID(knowledgeID string) *FileCreateService { - s.knowledgeID = &knowledgeID - return s -} - -// Do makes the request. -func (s *FileCreateService) Do(ctx context.Context) (res FileCreateResponse, err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - - body := map[string]string{"purpose": s.purpose} - - if s.customSeparator != nil { - body["custom_separator"] = *s.customSeparator - } - if s.sentenceSize != nil { - body["sentence_size"] = strconv.Itoa(*s.sentenceSize) - } - if s.knowledgeID != nil { - body["knowledge_id"] = *s.knowledgeID - } - - file, filename := s.file, s.filename - - if file == nil && s.localFile != "" { - var f *os.File - if f, err = os.Open(s.localFile); err != nil { - return - } - defer f.Close() - - file = f - filename = filepath.Base(s.localFile) - } - - if file == nil { - err = errors.New("no file specified") - return - } - - if resp, err = s.client.request(ctx). - SetFileReader("file", filename, file). - SetMultipartFormData(body). - SetResult(&res). - SetError(&apiError). - Post("files"); err != nil { - return - } - - if resp.IsError() { - err = apiError - return - } - - return -} - -// FileEditService is a service to edit a file. -type FileEditService struct { - client *Client - - documentID string - - knowledgeType *int - customSeparator []string - sentenceSize *int -} - -// NewFileEditService creates a new FileEditService. -func NewFileEditService(client *Client) *FileEditService { - return &FileEditService{client: client} -} - -// SetDocumentID sets the document_id parameter of the FileEditService. -func (s *FileEditService) SetDocumentID(documentID string) *FileEditService { - s.documentID = documentID - return s -} - -// SetKnowledgeType sets the knowledge_type parameter of the FileEditService. -func (s *FileEditService) SetKnowledgeType(knowledgeType int) *FileEditService { - s.knowledgeType = &knowledgeType - return s -} - -// SetSentenceSize sets the sentence_size parameter of the FileEditService. -func (s *FileEditService) SetCustomSeparator(customSeparator ...string) *FileEditService { - s.customSeparator = customSeparator - return s -} - -// SetSentenceSize sets the sentence_size parameter of the FileEditService. -func (s *FileEditService) SetSentenceSize(sentenceSize int) *FileEditService { - s.sentenceSize = &sentenceSize - return s -} - -// Do makes the request. -func (s *FileEditService) Do(ctx context.Context) (err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - - body := M{} - - if s.knowledgeType != nil { - body["knowledge_type"] = strconv.Itoa(*s.knowledgeType) - } - if len(s.customSeparator) > 0 { - body["custom_separator"] = s.customSeparator - } - if s.sentenceSize != nil { - body["sentence_size"] = strconv.Itoa(*s.sentenceSize) - } - - if resp, err = s.client.request(ctx). - SetPathParam("document_id", s.documentID). - SetBody(body). - SetError(&apiError). - Put("document/{document_id}"); err != nil { - return - } - - if resp.IsError() { - err = apiError - return - } - - return -} - -// FileListService is a service to list files. -type FileListService struct { - client *Client - - purpose string - - knowledgeID *string - page *int - limit *int - after *string - orderAsc *bool -} - -// FileFailInfo is the failed info of the FileListKnowledgeItem. -type FileFailInfo struct { - EmbeddingCode int `json:"embedding_code"` - EmbeddingMsg string `json:"embedding_msg"` -} - -// FileListKnowledgeItem is the item of the FileListKnowledgeResponse. -type FileListKnowledgeItem struct { - ID string `json:"id"` - Name string `json:"name"` - URL string `json:"url"` - Length int64 `json:"length"` - SentenceSize int64 `json:"sentence_size"` - CustomSeparator []string `json:"custom_separator"` - EmbeddingStat int `json:"embedding_stat"` - FailInfo *FileFailInfo `json:"failInfo"` - WordNum int64 `json:"word_num"` - ParseImage int `json:"parse_image"` -} - -// FileListKnowledgeResponse is the response of the FileListService. -type FileListKnowledgeResponse struct { - Total int `json:"total"` - List []FileListKnowledgeItem `json:"list"` -} - -// FileListFineTuneItem is the item of the FileListFineTuneResponse. -type FileListFineTuneItem struct { - Bytes int64 `json:"bytes"` - CreatedAt int64 `json:"created_at"` - Filename string `json:"filename"` - ID string `json:"id"` - Object string `json:"object"` - Purpose string `json:"purpose"` -} - -// FileListFineTuneResponse is the response of the FileListService. -type FileListFineTuneResponse struct { - Object string `json:"object"` - Data []FileListFineTuneItem `json:"data"` -} - -// FileListResponse is the response of the FileListService. -type FileListResponse struct { - FileListKnowledgeResponse - FileListFineTuneResponse -} - -// NewFileListService creates a new FileListService. -func NewFileListService(client *Client) *FileListService { - return &FileListService{client: client} -} - -// SetPurpose sets the purpose parameter of the FileListService. -func (s *FileListService) SetPurpose(purpose string) *FileListService { - s.purpose = purpose - return s -} - -// SetKnowledgeID sets the knowledge_id parameter of the FileListService. -func (s *FileListService) SetKnowledgeID(knowledgeID string) *FileListService { - s.knowledgeID = &knowledgeID - return s -} - -// SetPage sets the page parameter of the FileListService. -func (s *FileListService) SetPage(page int) *FileListService { - s.page = &page - return s -} - -// SetLimit sets the limit parameter of the FileListService. -func (s *FileListService) SetLimit(limit int) *FileListService { - s.limit = &limit - return s -} - -// SetAfter sets the after parameter of the FileListService. -func (s *FileListService) SetAfter(after string) *FileListService { - s.after = &after - return s -} - -// SetOrder sets the order parameter of the FileListService. -func (s *FileListService) SetOrder(asc bool) *FileListService { - s.orderAsc = &asc - return s -} - -// Do makes the request. -func (s *FileListService) Do(ctx context.Context) (res FileListResponse, err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - - m := map[string]string{ - "purpose": s.purpose, - } - - if s.knowledgeID != nil { - m["knowledge_id"] = *s.knowledgeID - } - if s.page != nil { - m["page"] = strconv.Itoa(*s.page) - } - if s.limit != nil { - m["limit"] = strconv.Itoa(*s.limit) - } - if s.after != nil { - m["after"] = *s.after - } - if s.orderAsc != nil { - if *s.orderAsc { - m["order"] = "asc" - } else { - m["order"] = "desc" - } - } - - if resp, err = s.client.request(ctx). - SetQueryParams(m). - SetResult(&res). - SetError(&apiError). - Get("files"); err != nil { - return - } - - if resp.IsError() { - err = apiError - return - } - - return -} - -// FileDeleteService is a service to delete a file. -type FileDeleteService struct { - client *Client - documentID string -} - -// NewFileDeleteService creates a new FileDeleteService. -func NewFileDeleteService(client *Client) *FileDeleteService { - return &FileDeleteService{client: client} -} - -// SetDocumentID sets the document_id parameter of the FileDeleteService. -func (s *FileDeleteService) SetDocumentID(documentID string) *FileDeleteService { - s.documentID = documentID - return s -} - -// Do makes the request. -func (s *FileDeleteService) Do(ctx context.Context) (err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - - if resp, err = s.client.request(ctx). - SetPathParam("document_id", s.documentID). - SetError(&apiError). - Delete("document/{document_id}"); err != nil { - return - } - - if resp.IsError() { - err = apiError - return - } - - return -} - -// FileGetService is a service to get a file. -type FileGetService struct { - client *Client - documentID string -} - -// FileGetResponse is the response of the FileGetService. -type FileGetResponse = FileListKnowledgeItem - -// NewFileGetService creates a new FileGetService. -func NewFileGetService(client *Client) *FileGetService { - return &FileGetService{client: client} -} - -// SetDocumentID sets the document_id parameter of the FileGetService. -func (s *FileGetService) SetDocumentID(documentID string) *FileGetService { - s.documentID = documentID - return s -} - -// Do makes the request. -func (s *FileGetService) Do(ctx context.Context) (res FileGetResponse, err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - - if resp, err = s.client.request(ctx). - SetPathParam("document_id", s.documentID). - SetResult(&res). - SetError(&apiError). - Get("document/{document_id}"); err != nil { - return - } - - if resp.IsError() { - err = apiError - return - } - - return -} - -// FileDownloadService is a service to download a file. -type FileDownloadService struct { - client *Client - - fileID string - - writer io.Writer - filename string -} - -// NewFileDownloadService creates a new FileDownloadService. -func NewFileDownloadService(client *Client) *FileDownloadService { - return &FileDownloadService{client: client} -} - -// SetFileID sets the file_id parameter of the FileDownloadService. -func (s *FileDownloadService) SetFileID(fileID string) *FileDownloadService { - s.fileID = fileID - return s -} - -// SetOutput sets the output parameter of the FileDownloadService. -func (s *FileDownloadService) SetOutput(w io.Writer) *FileDownloadService { - s.writer = w - return s -} - -// SetOutputFile sets the output_file parameter of the FileDownloadService. -func (s *FileDownloadService) SetOutputFile(filename string) *FileDownloadService { - s.filename = filename - return s -} - -// Do makes the request. -func (s *FileDownloadService) Do(ctx context.Context) (err error) { - var resp *resty.Response - - writer := s.writer - - if writer == nil && s.filename != "" { - var f *os.File - if f, err = os.Create(s.filename); err != nil { - return - } - defer f.Close() - - writer = f - } - - if writer == nil { - return errors.New("no output specified") - } - - if resp, err = s.client.request(ctx). - SetDoNotParseResponse(true). - SetPathParam("file_id", s.fileID). - Get("files/{file_id}/content"); err != nil { - return - } - defer resp.RawBody().Close() - - _, err = io.Copy(writer, resp.RawBody()) - - return -} diff --git a/zhipu/file_test.go b/zhipu/file_test.go deleted file mode 100644 index 3d035ae..0000000 --- a/zhipu/file_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package zhipu - -import ( - "context" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestFileServiceFineTune(t *testing.T) { - client, err := NewClient() - require.NoError(t, err) - - s := client.FileCreate(FilePurposeFineTune) - s.SetLocalFile(filepath.Join("testdata", "test-file.jsonl")) - - res, err := s.Do(context.Background()) - require.NoError(t, err) - require.NotZero(t, res.Bytes) - require.NotZero(t, res.CreatedAt) - require.NotEmpty(t, res.ID) -} - -func TestFileServiceKnowledge(t *testing.T) { - client, err := NewClient() - require.NoError(t, err) - - s := client.FileCreate(FilePurposeRetrieval) - s.SetKnowledgeID(os.Getenv("TEST_KNOWLEDGE_ID")) - s.SetLocalFile(filepath.Join("testdata", "test-file.txt")) - - res, err := s.Do(context.Background()) - require.NoError(t, err) - require.NotEmpty(t, res.SuccessInfos) - require.NotEmpty(t, res.SuccessInfos[0].DocumentID) - require.NotEmpty(t, res.SuccessInfos[0].Filename) - - documentID := res.SuccessInfos[0].DocumentID - - res2, err := client.FileGet(documentID).Do(context.Background()) - require.NoError(t, err) - require.NotEmpty(t, res2.ID) - - err = client.FileEdit(documentID).SetKnowledgeType(KnowledgeTypeCustom).Do(context.Background()) - require.True(t, err == nil || GetAPIErrorCode(err) == "10019") - - err = client.FileDelete(res.SuccessInfos[0].DocumentID).Do(context.Background()) - require.True(t, err == nil || GetAPIErrorCode(err) == "10019") -} - -func TestFileListServiceKnowledge(t *testing.T) { - client, err := NewClient() - require.NoError(t, err) - - s := client.FileList(FilePurposeRetrieval).SetKnowledgeID(os.Getenv("TEST_KNOWLEDGE_ID")) - res, err := s.Do(context.Background()) - require.NoError(t, err) - require.NotEmpty(t, res.List) -} - -func TestFileListServiceFineTune(t *testing.T) { - client, err := NewClient() - require.NoError(t, err) - - s := client.FileList(FilePurposeFineTune) - res, err := s.Do(context.Background()) - require.NoError(t, err) - require.NotEmpty(t, res.Data) -} diff --git a/zhipu/fine_tune.go b/zhipu/fine_tune.go deleted file mode 100644 index f2121ea..0000000 --- a/zhipu/fine_tune.go +++ /dev/null @@ -1,456 +0,0 @@ -package zhipu - -import ( - "context" - "strconv" - - "github.com/go-resty/resty/v2" -) - -const ( - HyperParameterAuto = "auto" - - FineTuneStatusCreate = "create" - FineTuneStatusValidatingFiles = "validating_files" - FineTuneStatusQueued = "queued" - FineTuneStatusRunning = "running" - FineTuneStatusSucceeded = "succeeded" - FineTuneStatusFailed = "failed" - FineTuneStatusCancelled = "cancelled" -) - -// FineTuneItem is the item of the FineTune -type FineTuneItem struct { - ID string `json:"id"` - RequestID string `json:"request_id"` - FineTunedModel string `json:"fine_tuned_model"` - Status string `json:"status"` - Object string `json:"object"` - TrainingFile string `json:"training_file"` - ValidationFile string `json:"validation_file"` - Error APIError `json:"error"` -} - -// FineTuneCreateService creates a new fine tune -type FineTuneCreateService struct { - client *Client - - model string - trainingFile string - validationFile *string - - learningRateMultiplier *StringOr[float64] - batchSize *StringOr[int] - nEpochs *StringOr[int] - - suffix *string - requestID *string -} - -// FineTuneCreateResponse is the response of the FineTuneCreateService -type FineTuneCreateResponse = FineTuneItem - -// NewFineTuneCreateService creates a new FineTuneCreateService -func NewFineTuneCreateService(client *Client) *FineTuneCreateService { - return &FineTuneCreateService{ - client: client, - } -} - -// SetModel sets the model parameter -func (s *FineTuneCreateService) SetModel(model string) *FineTuneCreateService { - s.model = model - return s -} - -// SetTrainingFile sets the trainingFile parameter -func (s *FineTuneCreateService) SetTrainingFile(trainingFile string) *FineTuneCreateService { - s.trainingFile = trainingFile - return s -} - -// SetValidationFile sets the validationFile parameter -func (s *FineTuneCreateService) SetValidationFile(validationFile string) *FineTuneCreateService { - s.validationFile = &validationFile - return s -} - -// SetLearningRateMultiplier sets the learningRateMultiplier parameter -func (s *FineTuneCreateService) SetLearningRateMultiplier(learningRateMultiplier float64) *FineTuneCreateService { - s.learningRateMultiplier = &StringOr[float64]{} - s.learningRateMultiplier.SetValue(learningRateMultiplier) - return s -} - -// SetLearningRateMultiplierAuto sets the learningRateMultiplier parameter to auto -func (s *FineTuneCreateService) SetLearningRateMultiplierAuto() *FineTuneCreateService { - s.learningRateMultiplier = &StringOr[float64]{} - s.learningRateMultiplier.SetString(HyperParameterAuto) - return s -} - -// SetBatchSize sets the batchSize parameter -func (s *FineTuneCreateService) SetBatchSize(batchSize int) *FineTuneCreateService { - s.batchSize = &StringOr[int]{} - s.batchSize.SetValue(batchSize) - return s -} - -// SetBatchSizeAuto sets the batchSize parameter to auto -func (s *FineTuneCreateService) SetBatchSizeAuto() *FineTuneCreateService { - s.batchSize = &StringOr[int]{} - s.batchSize.SetString(HyperParameterAuto) - return s -} - -// SetNEpochs sets the nEpochs parameter -func (s *FineTuneCreateService) SetNEpochs(nEpochs int) *FineTuneCreateService { - s.nEpochs = &StringOr[int]{} - s.nEpochs.SetValue(nEpochs) - return s -} - -// SetNEpochsAuto sets the nEpochs parameter to auto -func (s *FineTuneCreateService) SetNEpochsAuto() *FineTuneCreateService { - s.nEpochs = &StringOr[int]{} - s.nEpochs.SetString(HyperParameterAuto) - return s -} - -// SetSuffix sets the suffix parameter -func (s *FineTuneCreateService) SetSuffix(suffix string) *FineTuneCreateService { - s.suffix = &suffix - return s -} - -// SetRequestID sets the requestID parameter -func (s *FineTuneCreateService) SetRequestID(requestID string) *FineTuneCreateService { - s.requestID = &requestID - return s -} - -// Do makes the request -func (s *FineTuneCreateService) Do(ctx context.Context) (res FineTuneCreateResponse, err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - - body := M{ - "model": s.model, - "training_file": s.trainingFile, - } - - if s.validationFile != nil { - body["validation_file"] = *s.validationFile - } - if s.suffix != nil { - body["suffix"] = *s.suffix - } - if s.requestID != nil { - body["request_id"] = *s.requestID - } - if s.learningRateMultiplier != nil || s.batchSize != nil || s.nEpochs != nil { - hp := M{} - if s.learningRateMultiplier != nil { - hp["learning_rate_multiplier"] = s.learningRateMultiplier - } - if s.batchSize != nil { - hp["batch_size"] = s.batchSize - } - if s.nEpochs != nil { - hp["n_epochs"] = s.nEpochs - } - body["hyperparameters"] = hp - } - - if resp, err = s.client.request(ctx). - SetBody(body). - SetResult(&res). - SetError(&apiError). - Post("fine_tuning/jobs"); err != nil { - return - } - if resp.IsError() { - err = apiError - return - } - return -} - -// FineTuneEventListService creates a new fine tune event list -type FineTuneEventListService struct { - client *Client - - jobID string - - limit *int - after *string -} - -// FineTuneEventData is the data of the FineTuneEventItem -type FineTuneEventData struct { - Acc float64 `json:"acc"` - Loss float64 `json:"loss"` - CurrentSteps int64 `json:"current_steps"` - RemainingTime string `json:"remaining_time"` - ElapsedTime string `json:"elapsed_time"` - TotalSteps int64 `json:"total_steps"` - Epoch int64 `json:"epoch"` - TrainedTokens int64 `json:"trained_tokens"` - LearningRate float64 `json:"learning_rate"` -} - -// FineTuneEventItem is the item of the FineTuneEventListResponse -type FineTuneEventItem struct { - ID string `json:"id"` - Type string `json:"type"` - Level string `json:"level"` - Message string `json:"message"` - Object string `json:"object"` - CreatedAt int64 `json:"created_at"` - Data FineTuneEventData `json:"data"` -} - -// FineTuneEventListResponse is the response of the FineTuneEventListService -type FineTuneEventListResponse struct { - Data []FineTuneEventItem `json:"data"` - HasMore bool `json:"has_more"` - Object string `json:"object"` -} - -// NewFineTuneEventListService creates a new FineTuneEventListService -func NewFineTuneEventListService(client *Client) *FineTuneEventListService { - return &FineTuneEventListService{ - client: client, - } -} - -// SetJobID sets the jobID parameter -func (s *FineTuneEventListService) SetJobID(jobID string) *FineTuneEventListService { - s.jobID = jobID - return s -} - -// SetLimit sets the limit parameter -func (s *FineTuneEventListService) SetLimit(limit int) *FineTuneEventListService { - s.limit = &limit - return s -} - -// SetAfter sets the after parameter -func (s *FineTuneEventListService) SetAfter(after string) *FineTuneEventListService { - s.after = &after - return s -} - -// Do makes the request -func (s *FineTuneEventListService) Do(ctx context.Context) (res FineTuneEventListResponse, err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - - req := s.client.request(ctx) - - if s.limit != nil { - req.SetQueryParam("limit", strconv.Itoa(*s.limit)) - } - if s.after != nil { - req.SetQueryParam("after", *s.after) - } - - if resp, err = req. - SetPathParam("job_id", s.jobID). - SetResult(&res). - SetError(&apiError). - Get("fine_tuning/jobs/{job_id}/events"); err != nil { - return - } - if resp.IsError() { - err = apiError - return - } - return -} - -// FineTuneGetService creates a new fine tune get -type FineTuneGetService struct { - client *Client - jobID string -} - -// NewFineTuneGetService creates a new FineTuneGetService -func NewFineTuneGetService(client *Client) *FineTuneGetService { - return &FineTuneGetService{ - client: client, - } -} - -// SetJobID sets the jobID parameter -func (s *FineTuneGetService) SetJobID(jobID string) *FineTuneGetService { - s.jobID = jobID - return s -} - -// Do makes the request -func (s *FineTuneGetService) Do(ctx context.Context) (res FineTuneItem, err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - - if resp, err = s.client.request(ctx). - SetPathParam("job_id", s.jobID). - SetResult(&res). - SetError(&apiError). - Get("fine_tuning/jobs/{job_id}"); err != nil { - return - } - if resp.IsError() { - err = apiError - return - } - return -} - -// FineTuneListService creates a new fine tune list -type FineTuneListService struct { - client *Client - - limit *int - after *string -} - -// FineTuneListResponse is the response of the FineTuneListService -type FineTuneListResponse struct { - Data []FineTuneItem `json:"data"` - Object string `json:"object"` -} - -// NewFineTuneListService creates a new FineTuneListService -func NewFineTuneListService(client *Client) *FineTuneListService { - return &FineTuneListService{ - client: client, - } -} - -// SetLimit sets the limit parameter -func (s *FineTuneListService) SetLimit(limit int) *FineTuneListService { - s.limit = &limit - return s -} - -// SetAfter sets the after parameter -func (s *FineTuneListService) SetAfter(after string) *FineTuneListService { - s.after = &after - return s -} - -// Do makes the request -func (s *FineTuneListService) Do(ctx context.Context) (res FineTuneListResponse, err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - - req := s.client.request(ctx) - if s.limit != nil { - req.SetQueryParam("limit", strconv.Itoa(*s.limit)) - } - if s.after != nil { - req.SetQueryParam("after", *s.after) - } - - if resp, err = req. - SetResult(&res). - SetError(&apiError). - Get("fine_tuning/jobs"); err != nil { - return - } - if resp.IsError() { - err = apiError - return - } - return -} - -// FineTuneDeleteService creates a new fine tune delete -type FineTuneDeleteService struct { - client *Client - jobID string -} - -// NewFineTuneDeleteService creates a new FineTuneDeleteService -func NewFineTuneDeleteService(client *Client) *FineTuneDeleteService { - return &FineTuneDeleteService{ - client: client, - } -} - -// SetJobID sets the jobID parameter -func (s *FineTuneDeleteService) SetJobID(jobID string) *FineTuneDeleteService { - s.jobID = jobID - return s -} - -// Do makes the request -func (s *FineTuneDeleteService) Do(ctx context.Context) (res FineTuneItem, err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - - if resp, err = s.client.request(ctx). - SetPathParam("job_id", s.jobID). - SetResult(&res). - SetError(&apiError). - Delete("fine_tuning/jobs/{job_id}"); err != nil { - return - } - if resp.IsError() { - err = apiError - return - } - return -} - -// FineTuneCancelService creates a new fine tune cancel -type FineTuneCancelService struct { - client *Client - jobID string -} - -// NewFineTuneCancelService creates a new FineTuneCancelService -func NewFineTuneCancelService(client *Client) *FineTuneCancelService { - return &FineTuneCancelService{ - client: client, - } -} - -// SetJobID sets the jobID parameter -func (s *FineTuneCancelService) SetJobID(jobID string) *FineTuneCancelService { - s.jobID = jobID - return s -} - -// Do makes the request -func (s *FineTuneCancelService) Do(ctx context.Context) (res FineTuneItem, err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - - if resp, err = s.client.request(ctx). - SetPathParam("job_id", s.jobID). - SetResult(&res). - SetError(&apiError). - Post("fine_tuning/jobs/{job_id}/cancel"); err != nil { - return - } - if resp.IsError() { - err = apiError - return - } - return -} diff --git a/zhipu/fine_tune_test.go b/zhipu/fine_tune_test.go deleted file mode 100644 index 2adb1fa..0000000 --- a/zhipu/fine_tune_test.go +++ /dev/null @@ -1,3 +0,0 @@ -package zhipu - -// tests not available since lack of budget to test it diff --git a/zhipu/image_generation.go b/zhipu/image_generation.go deleted file mode 100644 index 32486a9..0000000 --- a/zhipu/image_generation.go +++ /dev/null @@ -1,110 +0,0 @@ -package zhipu - -import ( - "context" - - "github.com/go-resty/resty/v2" -) - -// ImageGenerationService creates a new image generation -type ImageGenerationService struct { - client *Client - - model string - prompt string - size string - userID string -} - -var ( - _ BatchSupport = &ImageGenerationService{} -) - -// ImageGenerationResponse is the response of the ImageGenerationService -type ImageGenerationResponse struct { - Created int64 `json:"created"` - Data []URLItem `json:"data"` -} - -// NewImageGenerationService creates a new ImageGenerationService -func NewImageGenerationService(client *Client) *ImageGenerationService { - return &ImageGenerationService{ - client: client, - } -} - -func (s *ImageGenerationService) BatchMethod() string { - return "POST" -} - -func (s *ImageGenerationService) BatchURL() string { - return BatchEndpointV4ImagesGenerations -} - -func (s *ImageGenerationService) BatchBody() any { - return s.buildBody() -} - -// SetModel sets the model parameter -func (s *ImageGenerationService) SetModel(model string) *ImageGenerationService { - s.model = model - return s -} - -// SetPrompt sets the prompt parameter -func (s *ImageGenerationService) SetPrompt(prompt string) *ImageGenerationService { - s.prompt = prompt - return s -} - -func (s *ImageGenerationService) SetSize(size string) *ImageGenerationService { - s.size = size - return s -} - -// SetUserID sets the userID parameter -func (s *ImageGenerationService) SetUserID(userID string) *ImageGenerationService { - s.userID = userID - return s -} - -func (s *ImageGenerationService) buildBody() M { - body := M{ - "model": s.model, - "prompt": s.prompt, - } - - if s.userID != "" { - body["user_id"] = s.userID - } - - if s.size != "" { - body["size"] = s.size - } - - return body -} - -func (s *ImageGenerationService) Do(ctx context.Context) (res ImageGenerationResponse, err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - - body := s.buildBody() - - if resp, err = s.client.request(ctx). - SetBody(body). - SetResult(&res). - SetError(&apiError). - Post("images/generations"); err != nil { - return - } - - if resp.IsError() { - err = apiError - return - } - - return -} diff --git a/zhipu/image_generation_test.go b/zhipu/image_generation_test.go deleted file mode 100644 index 7ab7807..0000000 --- a/zhipu/image_generation_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package zhipu - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestImageGenerationService(t *testing.T) { - client, err := NewClient() - require.NoError(t, err) - - s := client.ImageGeneration("cogview-3") - s.SetPrompt("一只可爱的小猫") - - res, err := s.Do(context.Background()) - require.NoError(t, err) - require.NotEmpty(t, res.Data) - t.Log(res.Data[0].URL) -} diff --git a/zhipu/knowledge.go b/zhipu/knowledge.go deleted file mode 100644 index 7f43808..0000000 --- a/zhipu/knowledge.go +++ /dev/null @@ -1,299 +0,0 @@ -package zhipu - -import ( - "context" - "strconv" - - "github.com/go-resty/resty/v2" -) - -const ( - KnowledgeEmbeddingIDEmbedding2 = 3 -) - -// KnowledgeCreateService creates a new knowledge -type KnowledgeCreateService struct { - client *Client - - embeddingID int - name string - description *string -} - -// KnowledgeCreateResponse is the response of the KnowledgeCreateService -type KnowledgeCreateResponse = IDItem - -// NewKnowledgeCreateService creates a new KnowledgeCreateService -func NewKnowledgeCreateService(client *Client) *KnowledgeCreateService { - return &KnowledgeCreateService{ - client: client, - } -} - -// SetEmbeddingID sets the embedding id of the knowledge -func (s *KnowledgeCreateService) SetEmbeddingID(embeddingID int) *KnowledgeCreateService { - s.embeddingID = embeddingID - return s -} - -// SetName sets the name of the knowledge -func (s *KnowledgeCreateService) SetName(name string) *KnowledgeCreateService { - s.name = name - return s -} - -// SetDescription sets the description of the knowledge -func (s *KnowledgeCreateService) SetDescription(description string) *KnowledgeCreateService { - s.description = &description - return s -} - -// Do creates the knowledge -func (s *KnowledgeCreateService) Do(ctx context.Context) (res KnowledgeCreateResponse, err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - body := M{ - "name": s.name, - "embedding_id": s.embeddingID, - } - if s.description != nil { - body["description"] = *s.description - } - if resp, err = s.client.request(ctx). - SetBody(body). - SetResult(&res). - SetError(&apiError). - Post("knowledge"); err != nil { - return - } - if resp.IsError() { - err = apiError - return - } - return -} - -// KnowledgeEditService edits a knowledge -type KnowledgeEditService struct { - client *Client - - knowledgeID string - - embeddingID *int - name *string - description *string -} - -// NewKnowledgeEditService creates a new KnowledgeEditService -func NewKnowledgeEditService(client *Client) *KnowledgeEditService { - return &KnowledgeEditService{ - client: client, - } -} - -// SetKnowledgeID sets the knowledge id -func (s *KnowledgeEditService) SetKnowledgeID(knowledgeID string) *KnowledgeEditService { - s.knowledgeID = knowledgeID - return s -} - -// SetName sets the name of the knowledge -func (s *KnowledgeEditService) SetName(name string) *KnowledgeEditService { - s.name = &name - return s -} - -// SetEmbeddingID sets the embedding id of the knowledge -func (s *KnowledgeEditService) SetEmbeddingID(embeddingID int) *KnowledgeEditService { - s.embeddingID = &embeddingID - return s -} - -// SetDescription sets the description of the knowledge -func (s *KnowledgeEditService) SetDescription(description string) *KnowledgeEditService { - s.description = &description - return s -} - -// Do edits the knowledge -func (s *KnowledgeEditService) Do(ctx context.Context) (err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - body := M{} - if s.name != nil { - body["name"] = *s.name - } - if s.description != nil { - body["description"] = *s.description - } - if s.embeddingID != nil { - body["embedding_id"] = *s.embeddingID - } - if resp, err = s.client.request(ctx). - SetPathParam("knowledge_id", s.knowledgeID). - SetBody(body). - SetError(&apiError). - Put("knowledge/{knowledge_id}"); err != nil { - return - } - if resp.IsError() { - err = apiError - return - } - return -} - -// KnowledgeListService lists the knowledge -type KnowledgeListService struct { - client *Client - - page *int - size *int -} - -// KnowledgeItem is an item in the knowledge list -type KnowledgeItem struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Icon string `json:"icon"` - Background string `json:"background"` - EmbeddingID int `json:"embedding_id"` - CustomIdentifier string `json:"custom_identifier"` - WordNum int64 `json:"word_num"` - Length int64 `json:"length"` - DocumentSize int64 `json:"document_size"` -} - -// KnowledgeListResponse is the response of the KnowledgeListService -type KnowledgeListResponse struct { - List []KnowledgeItem `json:"list"` - Total int `json:"total"` -} - -// NewKnowledgeListService creates a new KnowledgeListService -func NewKnowledgeListService(client *Client) *KnowledgeListService { - return &KnowledgeListService{client: client} -} - -// SetPage sets the page of the knowledge list -func (s *KnowledgeListService) SetPage(page int) *KnowledgeListService { - s.page = &page - return s -} - -// SetSize sets the size of the knowledge list -func (s *KnowledgeListService) SetSize(size int) *KnowledgeListService { - s.size = &size - return s -} - -// Do lists the knowledge -func (s *KnowledgeListService) Do(ctx context.Context) (res KnowledgeListResponse, err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - req := s.client.request(ctx) - if s.page != nil { - req.SetQueryParam("page", strconv.Itoa(*s.page)) - } - if s.size != nil { - req.SetQueryParam("size", strconv.Itoa(*s.size)) - } - if resp, err = req. - SetResult(&res). - SetError(&apiError). - Get("knowledge"); err != nil { - return - } - if resp.IsError() { - err = apiError - return - } - return -} - -// KnowledgeDeleteService deletes a knowledge -type KnowledgeDeleteService struct { - client *Client - - knowledgeID string -} - -// NewKnowledgeDeleteService creates a new KnowledgeDeleteService -func NewKnowledgeDeleteService(client *Client) *KnowledgeDeleteService { - return &KnowledgeDeleteService{ - client: client, - } -} - -// SetKnowledgeID sets the knowledge id -func (s *KnowledgeDeleteService) SetKnowledgeID(knowledgeID string) *KnowledgeDeleteService { - s.knowledgeID = knowledgeID - return s -} - -// Do deletes the knowledge -func (s *KnowledgeDeleteService) Do(ctx context.Context) (err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - if resp, err = s.client.request(ctx). - SetPathParam("knowledge_id", s.knowledgeID). - SetError(&apiError). - Delete("knowledge/{knowledge_id}"); err != nil { - return - } - if resp.IsError() { - err = apiError - return - } - return -} - -// KnowledgeCapacityService query the capacity of the knowledge -type KnowledgeCapacityService struct { - client *Client -} - -// KnowledgeCapacityItem is an item in the knowledge capacity -type KnowledgeCapacityItem struct { - WordNum int64 `json:"word_num"` - Length int64 `json:"length"` -} - -// KnowledgeCapacityResponse is the response of the KnowledgeCapacityService -type KnowledgeCapacityResponse struct { - Used KnowledgeCapacityItem `json:"used"` - Total KnowledgeCapacityItem `json:"total"` -} - -// SetKnowledgeID sets the knowledge id -func NewKnowledgeCapacityService(client *Client) *KnowledgeCapacityService { - return &KnowledgeCapacityService{client: client} -} - -// Do query the capacity of the knowledge -func (s *KnowledgeCapacityService) Do(ctx context.Context) (res KnowledgeCapacityResponse, err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - if resp, err = s.client.request(ctx). - SetResult(&res). - SetError(&apiError). - Get("knowledge/capacity"); err != nil { - return - } - if resp.IsError() { - err = apiError - return - } - return -} diff --git a/zhipu/knowledge_test.go b/zhipu/knowledge_test.go deleted file mode 100644 index a330e74..0000000 --- a/zhipu/knowledge_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package zhipu - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestKnowledgeCapacity(t *testing.T) { - client, err := NewClient() - require.NoError(t, err) - - s := client.KnowledgeCapacity() - res, err := s.Do(context.Background()) - require.NoError(t, err) - require.NotEmpty(t, res.Total.Length) - require.NotEmpty(t, res.Total.WordNum) -} - -func TestKnowledgeServiceAll(t *testing.T) { - client, err := NewClient() - require.NoError(t, err) - - s := client.KnowledgeCreate() - s.SetName("test") - s.SetDescription("test description") - s.SetEmbeddingID(KnowledgeEmbeddingIDEmbedding2) - - res, err := s.Do(context.Background()) - require.NoError(t, err) - require.NotEmpty(t, res.ID) - - s2 := client.KnowledgeList() - res2, err := s2.Do(context.Background()) - require.NoError(t, err) - require.NotEmpty(t, res2.List) - require.Equal(t, res.ID, res2.List[0].ID) - - s3 := client.KnowledgeEdit(res.ID) - s3.SetDescription("test description 2") - s3.SetName("test 2") - s3.SetEmbeddingID(KnowledgeEmbeddingIDEmbedding2) - err = s3.Do(context.Background()) - require.NoError(t, err) - - s4 := client.KnowledgeDelete(res.ID) - err = s4.Do(context.Background()) - require.NoError(t, err) -} diff --git a/zhipu/string_or.go b/zhipu/string_or.go deleted file mode 100644 index f0a72e6..0000000 --- a/zhipu/string_or.go +++ /dev/null @@ -1,54 +0,0 @@ -package zhipu - -import ( - "bytes" - "encoding/json" -) - -// StringOr is a struct that can be either a string or a value of type T. -type StringOr[T any] struct { - String *string - Value *T -} - -var ( - _ json.Marshaler = StringOr[float64]{} - _ json.Unmarshaler = &StringOr[float64]{} -) - -// SetString sets the string value of the struct. -func (f *StringOr[T]) SetString(v string) { - f.String = &v - f.Value = nil -} - -// SetValue sets the value of the struct. -func (f *StringOr[T]) SetValue(v T) { - f.String = nil - f.Value = &v -} - -func (f StringOr[T]) MarshalJSON() ([]byte, error) { - if f.Value != nil { - return json.Marshal(f.Value) - } - return json.Marshal(f.String) -} - -func (f *StringOr[T]) UnmarshalJSON(data []byte) error { - if len(data) == 0 { - return nil - } - if bytes.Equal(data, []byte("null")) { - return nil - } - if data[0] == '"' { - f.String = new(string) - f.Value = nil - return json.Unmarshal(data, f.String) - } else { - f.Value = new(T) - f.String = nil - return json.Unmarshal(data, f.Value) - } -} diff --git a/zhipu/string_or_test.go b/zhipu/string_or_test.go deleted file mode 100644 index cdc27da..0000000 --- a/zhipu/string_or_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package zhipu - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestStringOr(t *testing.T) { - data := struct { - Item *StringOr[int] `json:"item,omitempty"` - }{} - data.Item = &StringOr[int]{} - data.Item.SetString("test") - - b, err := json.Marshal(data) - require.NoError(t, err) - require.Equal(t, `{"item":"test"}`, string(b)) - - data.Item.SetValue(1) - b, err = json.Marshal(data) - require.NoError(t, err) - require.Equal(t, `{"item":1}`, string(b)) - - err = json.Unmarshal([]byte(`{"item":"test2"}`), &data) - require.NoError(t, err) - require.NotNil(t, data.Item.String) - require.Nil(t, data.Item.Value) - require.Equal(t, "test2", *data.Item.String) - - err = json.Unmarshal([]byte(`{"item":2}`), &data) - require.NoError(t, err) - require.Nil(t, data.Item.String) - require.NotNil(t, data.Item.Value) - require.Equal(t, 2, *data.Item.Value) -} diff --git a/zhipu/testdata/test-file.jsonl b/zhipu/testdata/test-file.jsonl deleted file mode 100644 index 4737cfa..0000000 --- a/zhipu/testdata/test-file.jsonl +++ /dev/null @@ -1,3 +0,0 @@ -{"messages": [{"role": "system", "content": "你是一位乐于助人,知识渊博的全能AI助手。"},{"role": "user", "content": "请介绍一下AlphaGo的基本原理。"}, {"role": "assistant", "content": "AlphaGo利用深度神经网络与树搜索算法相结合,通过对弈数据进行训练,逐步提升游戏水平,能够对围棋局面进行判断并选择最佳落子位置。"}]} -{"messages": [{"role": "system", "content": "你是一位乐于助人,知识渊博的全能AI助手。"},{"role": "user", "content": "请列举一些健康饮食的重要原则。"}, {"role": "assistant", "content": "健康饮食的重要原则包括:膳食应营养均衡,合理摄入各种营养素;多吃新鲜蔬果,适量饮水;少吃糖、油、盐,忌吃过量快餐;养成规律生活作息习惯,适量运动。"}]} -{"messages": [{"role": "system", "content": "你是一位乐于助人,知识渊博的全能AI助手。"},{"role": "user", "content": "请给出一些改善睡眠质量的建议。"}, {"role": "assistant", "content": "改善睡眠质量的建议包括:保证睡眠时间,避免睡眠不足;睡前放松身心,远离刺激;养成规律作息时间,不要频繁熬夜;适量运动,但不要运动过度;睡前可以喝一杯热牛奶等温和饮料。"}]} \ No newline at end of file diff --git a/zhipu/testdata/test-file.txt b/zhipu/testdata/test-file.txt deleted file mode 100644 index ff3bb63..0000000 --- a/zhipu/testdata/test-file.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown fox jumps over the lazy dog \ No newline at end of file diff --git a/zhipu/util.go b/zhipu/util.go deleted file mode 100644 index 4912b68..0000000 --- a/zhipu/util.go +++ /dev/null @@ -1,22 +0,0 @@ -package zhipu - -// URLItem is a struct that contains a URL. -type URLItem struct { - URL string `json:"url,omitempty"` -} - -// IDItem is a struct that contains an ID. -type IDItem struct { - ID string `json:"id,omitempty"` -} - -// Ptr returns a pointer to the value passed in. -// Example: -// -// web_search_enable = zhipu.Ptr(false) -func Ptr[T any](v T) *T { - return &v -} - -// M is a shorthand for map[string]any. -type M = map[string]any diff --git a/zhipu/util_test.go b/zhipu/util_test.go deleted file mode 100644 index 52b5052..0000000 --- a/zhipu/util_test.go +++ /dev/null @@ -1,3 +0,0 @@ -package zhipu - -// nothing to test diff --git a/zhipu/video_generation.go b/zhipu/video_generation.go deleted file mode 100644 index 3ae3279..0000000 --- a/zhipu/video_generation.go +++ /dev/null @@ -1,125 +0,0 @@ -package zhipu - -import ( - "context" - - "github.com/go-resty/resty/v2" -) - -const ( - VideoGenerationTaskStatusProcessing = "PROCESSING" - VideoGenerationTaskStatusSuccess = "SUCCESS" - VideoGenerationTaskStatusFail = "FAIL" -) - -// VideoGenerationService creates a new video generation -type VideoGenerationService struct { - client *Client - - model string - prompt string - userID string - imageURL string - requestID string -} - -var ( - _ BatchSupport = &VideoGenerationService{} -) - -// VideoGenerationResponse is the response of the VideoGenerationService -type VideoGenerationResponse struct { - RequestID string `json:"request_id"` - ID string `json:"id"` - Model string `json:"model"` - TaskStatus string `json:"task_status"` -} - -func NewVideoGenerationService(client *Client) *VideoGenerationService { - return &VideoGenerationService{ - client: client, - } -} - -func (s *VideoGenerationService) BatchMethod() string { - return "POST" -} - -func (s *VideoGenerationService) BatchURL() string { - return BatchEndpointV4VideosGenerations -} - -func (s *VideoGenerationService) BatchBody() any { - return s.buildBody() -} - -// SetModel sets the model parameter -func (s *VideoGenerationService) SetModel(model string) *VideoGenerationService { - s.model = model - return s -} - -// SetPrompt sets the prompt parameter -func (s *VideoGenerationService) SetPrompt(prompt string) *VideoGenerationService { - s.prompt = prompt - return s -} - -// SetUserID sets the userID parameter -func (s *VideoGenerationService) SetUserID(userID string) *VideoGenerationService { - s.userID = userID - return s -} - -// SetImageURL sets the imageURL parameter -func (s *VideoGenerationService) SetImageURL(imageURL string) *VideoGenerationService { - s.imageURL = imageURL - return s -} - -// SetRequestID sets the requestID parameter -func (s *VideoGenerationService) SetRequestID(requestID string) *VideoGenerationService { - s.requestID = requestID - return s -} - -func (s *VideoGenerationService) buildBody() M { - body := M{ - "model": s.model, - "prompt": s.prompt, - } - if s.userID != "" { - body["user_id"] = s.userID - } - if s.imageURL != "" { - body["image_url"] = s.imageURL - } - if s.requestID != "" { - body["request_id"] = s.requestID - } - return body -} - -func (s *VideoGenerationService) Do(ctx context.Context) (res VideoGenerationResponse, err error) { - var ( - resp *resty.Response - apiError APIErrorResponse - ) - - body := s.buildBody() - - if resp, err = s.client.request(ctx). - SetBody(body). - SetResult(&res). - SetError(&apiError). - Post("videos/generations"); err != nil { - return - } - - if resp.IsError() { - err = apiError - return - } - - return -} diff --git a/zhipu/video_generation_test.go b/zhipu/video_generation_test.go deleted file mode 100644 index 8dc5ab4..0000000 --- a/zhipu/video_generation_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package zhipu - -import ( - "context" - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -func TestVideoGeneration(t *testing.T) { - client, err := NewClient() - require.NoError(t, err) - - s := client.VideoGeneration("cogvideox") - s.SetPrompt("一只可爱的小猫") - - res, err := s.Do(context.Background()) - require.NoError(t, err) - require.NotEmpty(t, res.TaskStatus) - require.NotEmpty(t, res.ID) - t.Log(res.ID) - - for { - res, err := client.AsyncResult(res.ID).Do(context.Background()) - require.NoError(t, err) - require.NotEmpty(t, res.TaskStatus) - if res.TaskStatus == VideoGenerationTaskStatusSuccess { - require.NotEmpty(t, res.VideoResult) - t.Log(res.VideoResult[0].URL) - t.Log(res.VideoResult[0].CoverImageURL) - } - if res.TaskStatus != VideoGenerationTaskStatusProcessing { - break - } - time.Sleep(time.Second * 5) - } -} diff --git a/zhipu/wechat-donation.png b/zhipu/wechat-donation.png deleted file mode 100644 index f6519f6..0000000 Binary files a/zhipu/wechat-donation.png and /dev/null differ