From 75708351bbe428a09730a13172ba29ea1a1b8169 Mon Sep 17 00:00:00 2001 From: Star <> Date: Thu, 31 Oct 2024 15:25:04 +0800 Subject: [PATCH] new version for apigo.cc/ai --- ai_test.go | 20 +++++++ ai_test.js | 6 ++ chat.go | 164 ++++++++++++++++++++++------------------------------ config.go | 69 ++++++---------------- default.yml | 36 ++++++++++++ gc.go | 114 ++++++++++++++++++------------------ go.mod | 18 +++++- 7 files changed, 222 insertions(+), 205 deletions(-) create mode 100644 ai_test.go create mode 100644 ai_test.js create mode 100644 default.yml diff --git a/ai_test.go b/ai_test.go new file mode 100644 index 0000000..3e34684 --- /dev/null +++ b/ai_test.go @@ -0,0 +1,20 @@ +package zhipu_test + +import ( + "testing" + + _ "apigo.cc/ai" + _ "apigo.cc/ai/zhipu" + "apigo.cc/gojs" + _ "apigo.cc/gojs/console" + "github.com/ssgo/u" +) + +func TestAI(t *testing.T) { + gojs.ExportForDev() + r, err := gojs.RunFile("ai_test.js") + if err != nil { + t.Fatal(err.Error()) + } + println(u.Cyan(u.JsonP(r))) +} diff --git a/ai_test.js b/ai_test.js new file mode 100644 index 0000000..7d26652 --- /dev/null +++ b/ai_test.js @@ -0,0 +1,6 @@ +import co from 'apigo.cc/gojs/console' +import ai from 'apigo.cc/ai' + +return ai.zhipu.fastAsk('用一句话介绍一下你的主人', co.info, { + systemPrompt: '你的主人叫张三,是个程序员' +}) diff --git a/chat.go b/chat.go index e82ca7b..617a33b 100644 --- a/chat.go +++ b/chat.go @@ -1,123 +1,106 @@ package zhipu import ( - "apigo.cc/ai/llm/llm" "bytes" "context" "encoding/binary" - "fmt" - "github.com/ssgo/u" - "github.com/yankeguo/zhipu" "strings" "time" + + "apigo.cc/ai" + "github.com/ssgo/u" + "github.com/yankeguo/zhipu" ) -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 getClient(aiConf *ai.AIConfig) (client *zhipu.Client, err error) { + opt := []zhipu.ClientOption{zhipu.WithAPIKey(aiConf.ApiKey)} + if aiConf.Endpoint != "" { + opt = append(opt, zhipu.WithBaseURL(aiConf.Endpoint)) + } + return zhipu.NewClient(opt...) } -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 (lm *LLM) BatterAsk(messages []llm.ChatMessage, callback func(answer string)) (string, llm.Usage, error) { - return lm.Ask(messages, llm.ChatConfig{ - Model: ModelGLM4Plus, - }, callback) -} - -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 (lm *LLM) MultiAsk(messages []llm.ChatMessage, callback func(answer string)) (string, llm.Usage, error) { - return lm.Ask(messages, llm.ChatConfig{ - Model: ModelGLM4VPlus, - }, callback) -} - -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 (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{llm.ToolCodeInterpreter: nil}, - }, callback) -} - -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{llm.ToolWebSearch: nil}, - }, callback) -} - -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)) +func Chat(aiConf *ai.AIConfig, messages []ai.ChatMessage, callback func(string), conf ai.ChatConfig) (ai.ChatResult, error) { + c, err := getClient(aiConf) if err != nil { - return "", llm.Usage{}, err + return ai.ChatResult{}, err } - cc := c.ChatCompletion(config.GetModel()) + cc := c.ChatCompletion(conf.Model) + if conf.SystemPrompt != "" { + cc.AddMessage(zhipu.ChatCompletionMessage{ + Role: zhipu.RoleSystem, + Content: conf.SystemPrompt, + }) + } for _, msg := range messages { var contents []zhipu.ChatCompletionMultiContent if msg.Contents != nil { contents = make([]zhipu.ChatCompletionMultiContent, len(msg.Contents)) for j, inPart := range msg.Contents { part := zhipu.ChatCompletionMultiContent{} - part.Type = NameMap[inPart.Type] + part.Type = inPart.Type switch inPart.Type { - case llm.TypeText: + case ai.TypeText: part.Text = inPart.Content - case llm.TypeImage: + case ai.TypeImage: part.ImageURL = &zhipu.URLItem{URL: inPart.Content} - //case llm.TypeVideo: + //case ai.TypeVideo: // part.VideoURL = &zhipu.URLItem{URL: inPart.Content} } contents[j] = part } } - if len(contents) == 1 && contents[0].Type == llm.TypeText { + if len(contents) == 1 && contents[0].Type == ai.TypeText { cc.AddMessage(zhipu.ChatCompletionMessage{ - Role: NameMap[msg.Role], + Role: msg.Role, Content: contents[0].Text, }) } else { cc.AddMessage(zhipu.ChatCompletionMultiMessage{ - Role: NameMap[msg.Role], + Role: msg.Role, Content: contents, }) } } - for name := range config.GetTools() { + for name, toolConf := range conf.Tools { switch name { - case llm.ToolCodeInterpreter: - cc.AddTool(zhipu.ChatCompletionToolCodeInterpreter{}) - case llm.ToolWebSearch: - cc.AddTool(zhipu.ChatCompletionToolWebBrowser{}) + case ai.ToolFunction: + conf := zhipu.ChatCompletionToolFunction{} + u.Convert(toolConf, &conf) + cc.AddTool(conf) + case ai.ToolCodeInterpreter: + conf := zhipu.ChatCompletionToolCodeInterpreter{} + u.Convert(toolConf, &conf) + cc.AddTool(conf) + case ai.ToolWebSearch: + conf := zhipu.ChatCompletionToolWebSearch{} + u.Convert(toolConf, &conf) + cc.AddTool(conf) + case ai.ToolWebBrowser: + conf := zhipu.ChatCompletionToolWebBrowser{} + u.Convert(toolConf, &conf) + cc.AddTool(conf) + case ai.ToolDrawingTool: + conf := zhipu.ChatCompletionToolDrawingTool{} + u.Convert(toolConf, &conf) + cc.AddTool(conf) + case ai.ToolRetrieval: + conf := zhipu.ChatCompletionToolRetrieval{} + u.Convert(toolConf, &conf) + cc.AddTool(conf) } } - if config.GetMaxTokens() != 0 { - cc.SetMaxTokens(config.GetMaxTokens()) + if conf.MaxTokens != 0 { + cc.SetMaxTokens(conf.MaxTokens) } - if config.GetTemperature() != 0 { - cc.SetTemperature(config.GetTemperature()) + if conf.Temperature != 0 { + cc.SetTemperature(conf.Temperature) } - if config.GetTopP() != 0 { - cc.SetTopP(config.GetTopP()) + if conf.TopP != 0 { + cc.SetTopP(conf.TopP) } if callback != nil { cc.SetStreamHandler(func(r2 zhipu.ChatCompletionResponse) error { @@ -131,11 +114,6 @@ func (lm *LLM) Ask(messages []llm.ChatMessage, config llm.ChatConfig, callback f }) } - 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 @@ -145,32 +123,25 @@ func (lm *LLM) Ask(messages []llm.ChatMessage, config llm.ChatConfig, callback f results = append(results, ch.Message.Content) } } - return strings.Join(results, ""), llm.Usage{ + return ai.ChatResult{ + Result: strings.Join(results, ""), AskTokens: r.Usage.PromptTokens, AnswerTokens: r.Usage.CompletionTokens, TotalTokens: r.Usage.TotalTokens, UsedTime: t2, }, nil } else { - return "", llm.Usage{}, err + return ai.ChatResult{}, 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)) +func Embedding(aiConf *ai.AIConfig, text string, embeddingConf ai.EmbeddingConfig) (ai.EmbeddingResult, error) { + c, err := getClient(aiConf) if err != nil { - return nil, llm.Usage{}, err + return ai.EmbeddingResult{}, err } - cc := c.Embedding(model) + cc := c.Embedding(embeddingConf.Model) cc.SetInput(text) t1 := time.Now().UnixMilli() if r, err := cc.Do(context.Background()); err == nil { @@ -183,13 +154,14 @@ func (lm *LLM) Embedding(text, model string) ([]byte, llm.Usage, error) { } } } - return buf.Bytes(), llm.Usage{ + return ai.EmbeddingResult{ + Result: buf.Bytes(), AskTokens: r.Usage.PromptTokens, AnswerTokens: r.Usage.CompletionTokens, TotalTokens: r.Usage.TotalTokens, UsedTime: t2, }, nil } else { - return nil, llm.Usage{}, err + return ai.EmbeddingResult{}, err } } diff --git a/config.go b/config.go index 9dfcbdd..61bac47 100644 --- a/config.go +++ b/config.go @@ -1,60 +1,27 @@ package zhipu import ( - "apigo.cc/ai/llm/llm" - "github.com/yankeguo/zhipu" + _ "embed" + + "apigo.cc/ai" + "github.com/ssgo/u" ) -type LLM struct { - config llm.Config -} - -var NameMap = map[string]string{ - 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 ( - ModelGLM4Plus = "GLM-4-Plus" - ModelGLM40520 = "GLM-4-0520" - ModelGLM4Long = "GLM-4-Long" - ModelGLM4AirX = "GLM-4-AirX" - ModelGLM4Air = "GLM-4-Air" - ModelGLM4Flash = "GLM-4-Flash" - ModelGLM4AllTools = "GLM-4-AllTools" - ModelGLM4 = "GLM-4" - ModelGLM4VPlus = "GLM-4V-Plus" - ModelGLM4V = "GLM-4V" - ModelCogVideoX = "CogVideoX" - ModelCogView3Plus = "CogView-3-Plus" - ModelCogView3 = "CogView-3" - ModelEmbedding3 = "Embedding-3" - ModelEmbedding2 = "Embedding-2" - ModelCharGLM3 = "CharGLM-3" - ModelEmohaa = "Emohaa" - ModelCodeGeeX4 = "CodeGeeX-4" -) - -func (lm *LLM) Support() llm.Support { - return llm.Support{ - Ask: true, - AskWithImage: true, - AskWithVideo: false, - AskWithCodeInterpreter: true, - AskWithWebSearch: true, - MakeImage: true, - MakeVideo: true, - Models: []string{ModelGLM4Plus, ModelGLM40520, ModelGLM4Long, ModelGLM4AirX, ModelGLM4Air, ModelGLM4Flash, ModelGLM4AllTools, ModelGLM4, ModelGLM4VPlus, ModelGLM4V, ModelCogVideoX, ModelCogView3Plus, ModelCogView3, ModelEmbedding3, ModelEmbedding2, ModelCharGLM3, ModelEmohaa, ModelCodeGeeX4}, - } -} +//go:embed default.yml +var defaultYml string func init() { - llm.Register("zhipu", func(config llm.Config) llm.LLM { - return &LLM{config: config} + defaultConf := ai.AILoadConfig{} + u.Convert(u.UnYamlMap(defaultYml), &defaultConf) + ai.Register("zhipu", &ai.Agent{ + ChatConfigs: defaultConf.Chat, + EmbeddingConfigs: defaultConf.Embedding, + ImageConfigs: defaultConf.Image, + VideoConfigs: defaultConf.Video, + Chat: Chat, + Embedding: Embedding, + MakeImage: MakeImage, + MakeVideo: MakeVideo, + GetVideoResult: GetVideoResult, }) } diff --git a/default.yml b/default.yml new file mode 100644 index 0000000..b7ac4d8 --- /dev/null +++ b/default.yml @@ -0,0 +1,36 @@ +chat: + fastAsk: + model: GLM-4-Flash + longAsk: + model: GLM-4-Long + plusAsk: + model: GLM-4-Plus + plusAskV: + model: GLM-4V-Plus + bestAsk: + model: GLM-4-0520 + bestAskV: + model: GLM-4V + codeInterpreter: + model: GLM-4-AllTools + tools: + codeInterpreter: + webSearch: + model: GLM-4-AllTools + tools: + webSearch: + codeGeex: + model: CodeGeeX-4 + emohaa: + model: Emohaa +embedding: + embedding: + model: Embedding-3 +image: + makeImage: + model: CogView-3-Plus + width: 1024 + height: 1024 +video: + makeVideo: + model: CogVideoX diff --git a/gc.go b/gc.go index 88947ed..8cdbb94 100644 --- a/gc.go +++ b/gc.go @@ -5,28 +5,17 @@ import ( "errors" "time" - "apigo.cc/ai/llm/llm" + "apigo.cc/ai" "github.com/yankeguo/zhipu" ) -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)) +func MakeImage(aiConf *ai.AIConfig, conf ai.ImageConfig) (ai.ImageResult, error) { + c, err := getClient(aiConf) if err != nil { - return nil, llm.Usage{}, err + return ai.ImageResult{}, err } - config.SetDefault(&lm.config.GCConfig) - cc := c.ImageGeneration(config.GetModel()).SetPrompt(prompt) + cc := c.ImageGeneration(conf.Model).SetPrompt(conf.SystemPrompt + conf.Prompt) //cc.SetSize(config.GetSize()) t1 := time.Now().UnixMilli() @@ -36,60 +25,75 @@ func (lm *LLM) MakeImage(prompt string, config llm.GCConfig) ([]string, llm.Usag for _, item := range r.Data { results = append(results, item.URL) } - return results, llm.Usage{ + if len(results) == 0 { + results = append(results, "") + } + return ai.ImageResult{ + Results: results, UsedTime: t2, }, nil } else { - return nil, llm.Usage{}, err + return ai.ImageResult{}, 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)) +func MakeVideo(aiConf *ai.AIConfig, conf ai.VideoConfig) (string, error) { + c, err := getClient(aiConf) if err != nil { - return nil, nil, llm.Usage{}, err + return "", err } - config.SetDefault(&lm.config.GCConfig) - cc := c.VideoGeneration(config.GetModel()).SetPrompt(prompt) - cc.SetImageURL(config.GetRef()) + cc := c.VideoGeneration(conf.Model).SetPrompt(conf.SystemPrompt + conf.Prompt) + if len(conf.Ref) > 0 { + cc.SetImageURL(conf.Ref[0]) + } - 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 + return resp.ID, nil + } else { + return "", err + } +} + +func GetVideoResult(aiConf *ai.AIConfig, taskId string, waitSeconds int) (ai.VideoResult, error) { + c, err := getClient(aiConf) + if err != nil { + return ai.VideoResult{}, err + } + + var r zhipu.AsyncResultResponse + for i := 0; i < waitSeconds; i += 3 { + r, err = c.AsyncResult(taskId).Do(context.Background()) + if err != nil { + return ai.VideoResult{}, err + } + if r.TaskStatus == zhipu.VideoGenerationTaskStatusSuccess { + results := make([]string, 0) + previews := make([]string, 0) + for _, item := range r.VideoResult { + results = append(results, item.URL) + previews = append(previews, item.CoverImageURL) } - 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 len(results) == 0 { + results = append(results, "") + previews = append(previews, "") } - if r.TaskStatus == zhipu.VideoGenerationTaskStatusFail { - return nil, nil, llm.Usage{}, errors.New("fail on task " + resp.ID) + return ai.VideoResult{ + Results: results, + Previews: previews, + UsedTime: 0, + }, nil + } + if r.TaskStatus == zhipu.VideoGenerationTaskStatusFail { + return ai.VideoResult{}, errors.New("fail on video task " + taskId) + } else if r.TaskStatus == zhipu.VideoGenerationTaskStatusProcessing { + if waitSeconds == 0 { + return ai.VideoResult{IsProcessing: true}, nil } time.Sleep(3 * time.Second) + } else { + return ai.VideoResult{}, errors.New("unknow status " + r.TaskStatus + " on video task " + taskId) } - return nil, nil, llm.Usage{}, errors.New("timeout on task " + resp.ID) - } else { - return nil, nil, llm.Usage{}, err } + return ai.VideoResult{IsProcessing: true}, errors.New("timeout on video task " + taskId) } diff --git a/go.mod b/go.mod index d480a5c..cdfc97d 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,28 @@ module apigo.cc/ai/zhipu -go 1.22 +go 1.18 require ( - apigo.cc/ai/llm v0.0.4 + apigo.cc/ai v0.0.1 + apigo.cc/gojs v0.0.4 + apigo.cc/gojs/console v0.0.1 github.com/ssgo/u v1.7.9 github.com/yankeguo/zhipu v0.1.2 ) require ( - github.com/go-resty/resty/v2 v2.14.0 // indirect + github.com/dlclark/regexp2 v1.11.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-resty/resty/v2 v2.15.3 // indirect + github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect + github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect + github.com/ssgo/config v1.7.8 // indirect + github.com/ssgo/log v1.7.7 // indirect + github.com/ssgo/standard v1.7.7 // indirect + github.com/ssgo/tool v0.4.27 // indirect golang.org/x/net v0.30.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect )