first
This commit is contained in:
commit
70ded2fb95
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
.*
|
||||
!.gitignore
|
||||
go.sum
|
||||
env.yml
|
||||
node_modules
|
||||
package.json
|
9
LICENSE
Normal file
9
LICENSE
Normal file
@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 apigo
|
||||
|
||||
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.
|
136
README.md
Normal file
136
README.md
Normal file
@ -0,0 +1,136 @@
|
||||
# AI大模型低代码工具
|
||||
|
||||
## 命令行工具
|
||||
|
||||
### Install
|
||||
|
||||
```shell
|
||||
go install apigo.cc/ai/llm/llm-cli@latest
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```shell
|
||||
llm-cli -h | help show usage
|
||||
llm-cli -e | export export ai.ts file for develop
|
||||
llm-cli test.js run test.js, if not specified, run ai.js
|
||||
llm-cli -w | watch test.js run test.js, if .js files changed will be reloaded
|
||||
```
|
||||
|
||||
### Sample
|
||||
|
||||
#### test.js
|
||||
|
||||
```javascript
|
||||
import {zhipu} from 'llm'
|
||||
import console from 'console'
|
||||
|
||||
function main(...args) {
|
||||
let r = zhipu.fastAsk((args.length>0?args[0]:'你好', r => {
|
||||
console.print(r)
|
||||
})
|
||||
console.println()
|
||||
return r
|
||||
}
|
||||
```
|
||||
|
||||
#### run sample
|
||||
|
||||
```shell
|
||||
llm-cli test.js "你好"
|
||||
```
|
||||
|
||||
### Configure
|
||||
|
||||
#### env.yml
|
||||
|
||||
```yaml
|
||||
llm:
|
||||
openai:
|
||||
default:
|
||||
apiKey: ...
|
||||
aurze:
|
||||
apiKey: ...
|
||||
endpoint: ...
|
||||
zhipu:
|
||||
default:
|
||||
apiKey: ...
|
||||
```
|
||||
|
||||
#### encrypt apiKey
|
||||
|
||||
install sskey
|
||||
|
||||
```shell
|
||||
go install github.com/ssgo/tool/sskey@latest
|
||||
sskey -e 'your apiKey'
|
||||
```
|
||||
|
||||
copy url base64 format encrypted apiKey into llm.yml or env.yml
|
||||
|
||||
|
||||
## 将 [llm](https://apigo.cc/ai/llm) 和 [低代码](https://apigo.cc/apigo/gojs) 集成到应用
|
||||
|
||||
### Install
|
||||
|
||||
```shell
|
||||
go get -u apigo.cc/apigo/gojs
|
||||
go get -u apigo.cc/ai/llm
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "apigo.cc/ai/llm"
|
||||
"apigo.cc/apigo/gojs"
|
||||
_ "apigo.cc/apigo/gojs/modules"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result, err := gojs.Run(`return llm.zhipu.fastAsk('你好', console.print)`, "")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
} else if result != nil {
|
||||
fmt.Println(result)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 调用 Go API
|
||||
|
||||
### Install
|
||||
|
||||
```shell
|
||||
go get -u apigo.cc/ai/llm
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"apigo.cc/ai/llm/llm"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
zhipu := llm.Get("zhipu")
|
||||
|
||||
r, usage, err := zhipu.FastAsk(llm.Messages().User().Text("你是什么模型").Make(), func(text string) {
|
||||
fmt.Print(text)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Println()
|
||||
fmt.Println("result:", r)
|
||||
fmt.Println("usage:", usage)
|
||||
}
|
||||
}
|
||||
```
|
80
config.go
Normal file
80
config.go
Normal file
@ -0,0 +1,80 @@
|
||||
package llm
|
||||
|
||||
import (
|
||||
"apigo.cc/ai/llm/llm"
|
||||
"apigo.cc/apigo/gojs"
|
||||
"apigo.cc/apigo/gojs/dop251/goja"
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"github.com/ssgo/config"
|
||||
"github.com/ssgo/u"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
//go:embed llm.ts
|
||||
var llmTS string
|
||||
|
||||
//go:embed llm.md
|
||||
var llmMD string
|
||||
|
||||
var confAes = u.NewAes([]byte("?GQ$0K0GgLdO=f+~L68PLm$uhKr4'=tV"), []byte("VFs7@sK61cj^f?HZ"))
|
||||
var keysIsSet = false
|
||||
|
||||
func SetSSKey(key, iv []byte) {
|
||||
if !keysIsSet {
|
||||
confAes = u.NewAes(key, iv)
|
||||
keysIsSet = true
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
list := map[string]*map[string]*struct {
|
||||
Endpoint string
|
||||
ApiKey string
|
||||
ChatConfig llm.ChatConfig
|
||||
GCConfig llm.GCConfig
|
||||
Debug bool
|
||||
}{}
|
||||
|
||||
jsObj := gojs.Map{}
|
||||
llmList := make([]string, 0)
|
||||
_ = config.LoadConfig("llm", &list)
|
||||
for llmName, llmConfigs := range list {
|
||||
for confName, llmConf := range *llmConfigs {
|
||||
llmConf.ApiKey = confAes.DecryptUrlBase64ToString(llmConf.ApiKey)
|
||||
if confName == "default" {
|
||||
confName = llmName
|
||||
}
|
||||
llmList = append(llmList, confName)
|
||||
llmObj := llm.Create(confName, llmName, llm.Config{
|
||||
Endpoint: llmConf.Endpoint,
|
||||
ApiKey: llmConf.ApiKey,
|
||||
ChatConfig: llmConf.ChatConfig,
|
||||
GCConfig: llmConf.GCConfig,
|
||||
Debug: llmConf.Debug,
|
||||
})
|
||||
jsObj[confName] = MakeLLM(llmObj)
|
||||
}
|
||||
}
|
||||
jsObj["similarity"] = func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(2)
|
||||
return vm.ToValue(llm.Similarity(args.Bytes(0), args.Bytes(1)))
|
||||
}
|
||||
|
||||
var tpl *template.Template
|
||||
var err error
|
||||
llmTSCode := ""
|
||||
if tpl, err = template.New("").Parse(llmTS); err == nil {
|
||||
buf := bytes.NewBuffer(make([]byte, 0))
|
||||
if err = tpl.Execute(buf, llmList); err == nil {
|
||||
llmTSCode = string(buf.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
gojs.Register("llm", gojs.Module{
|
||||
Object: jsObj,
|
||||
TsCode: llmTSCode,
|
||||
Desc: "llm plugin for gojs(http://apigo.cc/apigo/gojs)",
|
||||
Example: llmMD,
|
||||
})
|
||||
}
|
33
go.mod
Normal file
33
go.mod
Normal file
@ -0,0 +1,33 @@
|
||||
module apigo.cc/ai/llm
|
||||
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
apigo.cc/apigo/gojs v0.1.1
|
||||
github.com/go-resty/resty/v2 v2.15.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/sashabaranov/go-openai v1.30.3
|
||||
github.com/ssgo/config v1.7.7
|
||||
github.com/ssgo/log v1.7.7
|
||||
github.com/ssgo/u v1.7.7
|
||||
github.com/yankeguo/zhipu v0.1.2
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
|
||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/ssgo/dao v0.1.5 // indirect
|
||||
github.com/ssgo/db v1.7.9 // indirect
|
||||
github.com/ssgo/httpclient 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.29.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/text v0.18.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
71
llm-cli/main.go
Normal file
71
llm-cli/main.go
Normal file
@ -0,0 +1,71 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "apigo.cc/ai/llm"
|
||||
"apigo.cc/apigo/gojs"
|
||||
_ "apigo.cc/apigo/gojs/modules"
|
||||
"fmt"
|
||||
"github.com/ssgo/u"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
args := os.Args[1:]
|
||||
|
||||
if len(args) > 0 && (args[0] == "-e" || args[0] == "export") {
|
||||
imports := gojs.ExportForDev()
|
||||
fmt.Println(`exported to node_modules
|
||||
|
||||
test.js:
|
||||
`)
|
||||
fmt.Println(u.Cyan(imports + `
|
||||
|
||||
function main(...args) {
|
||||
let r = llm.zhipu.fastAsk(args.length>0?args[0]:'你好', console.print)
|
||||
console.println()
|
||||
return r
|
||||
}
|
||||
|
||||
run:
|
||||
|
||||
llm-cli test.js 你是谁
|
||||
|
||||
`))
|
||||
return
|
||||
}
|
||||
|
||||
isWatch := false
|
||||
if len(args) > 0 && args[0] == "-w" {
|
||||
isWatch = true
|
||||
args = args[1:]
|
||||
}
|
||||
|
||||
jsFile := ""
|
||||
if len(args) > 0 {
|
||||
jsFile = args[0]
|
||||
args = args[1:]
|
||||
}
|
||||
|
||||
if jsFile != "" && u.FileExists(jsFile) {
|
||||
if isWatch {
|
||||
if w, err := gojs.WatchRun(jsFile, nil, nil, u.ToInterfaceArray(args)...); err == nil {
|
||||
w.WaitForKill()
|
||||
} else {
|
||||
fmt.Println(u.BRed(err.Error()))
|
||||
}
|
||||
} else {
|
||||
if _, err := gojs.RunFile(jsFile, u.ToInterfaceArray(args)...); err != nil {
|
||||
fmt.Println(u.BRed(err.Error()))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(`Usage:
|
||||
llm-cli -h | help show usage
|
||||
llm-cli -e | export export ai.ts file for develop
|
||||
llm-cli test.js run test.js, if not specified, run ai.js
|
||||
llm-cli -w | watch test.js run test.js, if .js files changed will be reloaded
|
||||
`)
|
||||
return
|
||||
}
|
344
llm.go
Normal file
344
llm.go
Normal file
@ -0,0 +1,344 @@
|
||||
package llm
|
||||
|
||||
import (
|
||||
"apigo.cc/ai/llm/llm"
|
||||
_ "apigo.cc/ai/llm/openai"
|
||||
_ "apigo.cc/ai/llm/zhipu"
|
||||
"apigo.cc/apigo/gojs"
|
||||
"apigo.cc/apigo/gojs/dop251/goja"
|
||||
"github.com/ssgo/u"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func MakeLLM(lm llm.LLM) map[string]any {
|
||||
return map[string]any{
|
||||
"ask": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
conf, cb := getAskArgs(args.This, vm, args.Arguments)
|
||||
result, usage, err := lm.Ask(makeChatMessages(args.Arguments), conf, cb)
|
||||
return makeChatResult(vm, result, &usage, err)
|
||||
},
|
||||
"fastAsk": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
_, cb := getAskArgs(args.This, vm, args.Arguments)
|
||||
result, usage, err := lm.FastAsk(makeChatMessages(args.Arguments), cb)
|
||||
return makeChatResult(vm, result, &usage, err)
|
||||
},
|
||||
"longAsk": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
_, cb := getAskArgs(args.This, vm, args.Arguments)
|
||||
result, usage, err := lm.LongAsk(makeChatMessages(args.Arguments), cb)
|
||||
return makeChatResult(vm, result, &usage, err)
|
||||
},
|
||||
"batterAsk": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
_, cb := getAskArgs(args.This, vm, args.Arguments)
|
||||
result, usage, err := lm.BatterAsk(makeChatMessages(args.Arguments), cb)
|
||||
return makeChatResult(vm, result, &usage, err)
|
||||
},
|
||||
"bestAsk": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
_, cb := getAskArgs(args.This, vm, args.Arguments)
|
||||
result, usage, err := lm.BestAsk(makeChatMessages(args.Arguments), cb)
|
||||
return makeChatResult(vm, result, &usage, err)
|
||||
},
|
||||
|
||||
"multiAsk": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
_, cb := getAskArgs(args.This, vm, args.Arguments)
|
||||
result, usage, err := lm.MultiAsk(makeChatMessages(args.Arguments), cb)
|
||||
return makeChatResult(vm, result, &usage, err)
|
||||
},
|
||||
"bestMultiAsk": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
_, cb := getAskArgs(args.This, vm, args.Arguments)
|
||||
result, usage, err := lm.BestMultiAsk(makeChatMessages(args.Arguments), cb)
|
||||
return makeChatResult(vm, result, &usage, err)
|
||||
},
|
||||
|
||||
"codeInterpreterAsk": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
_, cb := getAskArgs(args.This, vm, args.Arguments)
|
||||
result, usage, err := lm.CodeInterpreterAsk(makeChatMessages(args.Arguments), cb)
|
||||
return makeChatResult(vm, result, &usage, err)
|
||||
},
|
||||
"webSearchAsk": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
_, cb := getAskArgs(args.This, vm, args.Arguments)
|
||||
result, usage, err := lm.WebSearchAsk(makeChatMessages(args.Arguments), cb)
|
||||
return makeChatResult(vm, result, &usage, err)
|
||||
},
|
||||
|
||||
"makeImage": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
prompt, conf := getGCArgs(args.Arguments)
|
||||
results, usage, err := lm.MakeImage(prompt, conf)
|
||||
return makeGCResult(vm, results, nil, &usage, err)
|
||||
},
|
||||
"fastMakeImage": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
prompt, conf := getGCArgs(args.Arguments)
|
||||
results, usage, err := lm.FastMakeImage(prompt, conf)
|
||||
return makeGCResult(vm, results, nil, &usage, err)
|
||||
},
|
||||
"bestMakeImage": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
prompt, conf := getGCArgs(args.Arguments)
|
||||
results, usage, err := lm.BestMakeImage(prompt, conf)
|
||||
return makeGCResult(vm, results, nil, &usage, err)
|
||||
},
|
||||
|
||||
"makeVideo": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
prompt, conf := getGCArgs(args.Arguments)
|
||||
results, previews, usage, err := lm.MakeVideo(prompt, conf)
|
||||
return makeGCResult(vm, results, previews, &usage, err)
|
||||
},
|
||||
"fastMakeVideo": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
prompt, conf := getGCArgs(args.Arguments)
|
||||
results, previews, usage, err := lm.FastMakeVideo(prompt, conf)
|
||||
return makeGCResult(vm, results, previews, &usage, err)
|
||||
},
|
||||
"bestMakeVideo": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
prompt, conf := getGCArgs(args.Arguments)
|
||||
results, previews, usage, err := lm.BestMakeVideo(prompt, conf)
|
||||
return makeGCResult(vm, results, previews, &usage, err)
|
||||
},
|
||||
|
||||
"embedding": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(2)
|
||||
results, usage, err := lm.Embedding(args.Str(0), args.Str(1))
|
||||
return makeEmbeddingResult(vm, results, &usage, err)
|
||||
},
|
||||
"fastEmbedding": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
results, usage, err := lm.FastEmbedding(args.Str(0))
|
||||
return makeEmbeddingResult(vm, results, &usage, err)
|
||||
},
|
||||
"bestEmbedding": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
results, usage, err := lm.BestEmbedding(args.Str(0))
|
||||
return makeEmbeddingResult(vm, results, &usage, err)
|
||||
},
|
||||
|
||||
"support": lm.Support(),
|
||||
}
|
||||
}
|
||||
|
||||
func getErrorStr(err error) string {
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func makeChatResult(vm *goja.Runtime, result string, usage *llm.Usage, err error) goja.Value {
|
||||
if err != nil {
|
||||
panic(vm.NewGoError(err))
|
||||
}
|
||||
return vm.ToValue(map[string]any{
|
||||
"result": result,
|
||||
"askTokens": usage.AskTokens,
|
||||
"answerTokens": usage.AnswerTokens,
|
||||
"totalTokens": usage.TotalTokens,
|
||||
"usedTime": usage.UsedTime,
|
||||
})
|
||||
}
|
||||
|
||||
func makeEmbeddingResult(vm *goja.Runtime, result []byte, usage *llm.Usage, err error) goja.Value {
|
||||
if err != nil {
|
||||
panic(vm.NewGoError(err))
|
||||
}
|
||||
return vm.ToValue(map[string]any{
|
||||
"result": result,
|
||||
"askTokens": usage.AskTokens,
|
||||
"answerTokens": usage.AnswerTokens,
|
||||
"totalTokens": usage.TotalTokens,
|
||||
"usedTime": usage.UsedTime,
|
||||
})
|
||||
}
|
||||
|
||||
func makeGCResult(vm *goja.Runtime, results []string, previews []string, usage *llm.Usage, err error) goja.Value {
|
||||
if err != nil {
|
||||
panic(vm.NewGoError(err))
|
||||
}
|
||||
result := ""
|
||||
preview := ""
|
||||
if len(results) > 0 {
|
||||
result = results[0]
|
||||
} else {
|
||||
results = make([]string, 0)
|
||||
}
|
||||
if len(previews) > 0 {
|
||||
preview = previews[0]
|
||||
} else {
|
||||
previews = make([]string, 0)
|
||||
}
|
||||
return vm.ToValue(map[string]any{
|
||||
"result": result,
|
||||
"preview": preview,
|
||||
"results": results,
|
||||
"previews": previews,
|
||||
"usedTime": usage.UsedTime,
|
||||
})
|
||||
}
|
||||
|
||||
func getGCArgs(args []goja.Value) (string, llm.GCConfig) {
|
||||
prompt := ""
|
||||
var config llm.GCConfig
|
||||
if len(args) > 0 {
|
||||
prompt = u.String(args[0].Export())
|
||||
if len(args) > 1 {
|
||||
u.Convert(args[1].Export(), &config)
|
||||
}
|
||||
}
|
||||
return prompt, config
|
||||
}
|
||||
|
||||
func getAskArgs(thisArg goja.Value, vm *goja.Runtime, args []goja.Value) (llm.ChatConfig, func(string)) {
|
||||
var chatConfig llm.ChatConfig
|
||||
var callback func(answer string)
|
||||
if len(args) > 0 {
|
||||
for i := 1; i < len(args); i++ {
|
||||
if cb, ok := goja.AssertFunction(args[i]); ok {
|
||||
callback = func(answer string) {
|
||||
_, _ = cb(thisArg, vm.ToValue(answer))
|
||||
}
|
||||
} else if args[i].ExportType() != nil {
|
||||
switch args[i].ExportType().Kind() {
|
||||
case reflect.Map, reflect.Struct:
|
||||
u.Convert(args[i].Export(), &chatConfig)
|
||||
default:
|
||||
chatConfig.Model = u.String(args[i].Export())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return chatConfig, callback
|
||||
}
|
||||
|
||||
func makeChatMessages(args []goja.Value) []llm.ChatMessage {
|
||||
out := make([]llm.ChatMessage, 0)
|
||||
if len(args) > 0 {
|
||||
v := args[0].Export()
|
||||
vv := reflect.ValueOf(v)
|
||||
t := args[0].ExportType()
|
||||
if t != nil {
|
||||
lastRoleIsUser := false
|
||||
switch t.Kind() {
|
||||
// 数组,根据成员类型处理
|
||||
// 字符串:
|
||||
// 含有媒体:单条多模态消息
|
||||
// 无媒体:多条文本消息
|
||||
// 数组:多条消息(第一个成员不是 role 则自动生成)
|
||||
// 对象:多条消息(无 role 则自动生成)(支持 content 或 contents)
|
||||
// 结构:转换为 llm.ChatMessage
|
||||
// 对象:单条消息(支持 content 或 contents)
|
||||
// 结构:转换为 llm.ChatMessage
|
||||
// 字符串:单条文本消息
|
||||
case reflect.Slice:
|
||||
hasSub := false
|
||||
hasMulti := false
|
||||
for i := 0; i < vv.Len(); i++ {
|
||||
vv2 := u.FinalValue(vv.Index(i))
|
||||
if vv2.Kind() == reflect.Slice || vv2.Kind() == reflect.Map || vv2.Kind() == reflect.Struct {
|
||||
hasSub = true
|
||||
break
|
||||
}
|
||||
if vv2.Kind() == reflect.String {
|
||||
str := vv2.String()
|
||||
if strings.HasPrefix(str, "data:") || strings.HasPrefix(str, "https://") || strings.HasPrefix(str, "http://") {
|
||||
hasMulti = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if hasSub || !hasMulti {
|
||||
// 有子对象或纯文本数组
|
||||
var defaultRole string
|
||||
for i := 0; i < vv.Len(); i++ {
|
||||
lastRoleIsUser = !lastRoleIsUser
|
||||
if lastRoleIsUser {
|
||||
defaultRole = llm.RoleUser
|
||||
} else {
|
||||
defaultRole = llm.RoleAssistant
|
||||
}
|
||||
vv2 := u.FinalValue(vv.Index(i))
|
||||
switch vv2.Kind() {
|
||||
case reflect.Slice:
|
||||
out = append(out, makeChatMessageFromSlice(vv2, defaultRole))
|
||||
case reflect.Map:
|
||||
out = append(out, makeChatMessageFromSlice(vv2, defaultRole))
|
||||
case reflect.Struct:
|
||||
item := llm.ChatMessage{}
|
||||
u.Convert(vv2.Interface(), &item)
|
||||
out = append(out, item)
|
||||
default:
|
||||
out = append(out, llm.ChatMessage{Role: llm.RoleUser, Contents: []llm.ChatMessageContent{makeChatMessageContent(u.String(vv2.Interface()))}})
|
||||
}
|
||||
lastRoleIsUser = out[len(out)-1].Role != llm.RoleUser
|
||||
}
|
||||
} else {
|
||||
// 单条多模态消息
|
||||
out = append(out, makeChatMessageFromSlice(vv, llm.RoleUser))
|
||||
}
|
||||
case reflect.Map:
|
||||
out = append(out, makeChatMessageFromMap(vv, llm.RoleUser))
|
||||
case reflect.Struct:
|
||||
item := llm.ChatMessage{}
|
||||
u.Convert(v, &item)
|
||||
out = append(out, item)
|
||||
default:
|
||||
out = append(out, llm.ChatMessage{Role: llm.RoleUser, Contents: []llm.ChatMessageContent{makeChatMessageContent(u.String(v))}})
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func makeChatMessageFromSlice(vv reflect.Value, defaultRole string) llm.ChatMessage {
|
||||
role := u.String(vv.Index(0).Interface())
|
||||
j := 0
|
||||
if role == llm.RoleUser || role == llm.RoleAssistant || role == llm.RoleSystem || role == llm.RoleTool {
|
||||
j = 1
|
||||
} else {
|
||||
role = defaultRole
|
||||
}
|
||||
contents := make([]llm.ChatMessageContent, 0)
|
||||
for ; j < vv.Len(); j++ {
|
||||
contents = append(contents, makeChatMessageContent(u.String(vv.Index(j).Interface())))
|
||||
}
|
||||
return llm.ChatMessage{Role: role, Contents: contents}
|
||||
}
|
||||
|
||||
func makeChatMessageFromMap(vv reflect.Value, defaultRole string) llm.ChatMessage {
|
||||
role := u.String(vv.MapIndex(reflect.ValueOf("role")).Interface())
|
||||
if role == "" {
|
||||
role = defaultRole
|
||||
}
|
||||
contents := make([]llm.ChatMessageContent, 0)
|
||||
content := u.String(vv.MapIndex(reflect.ValueOf("content")).Interface())
|
||||
if content != "" {
|
||||
contents = append(contents, makeChatMessageContent(content))
|
||||
} else {
|
||||
contentsV := vv.MapIndex(reflect.ValueOf("contents"))
|
||||
if contentsV.IsValid() && contentsV.Kind() == reflect.Slice {
|
||||
for i := 0; i < contentsV.Len(); i++ {
|
||||
contents = append(contents, makeChatMessageContent(u.String(contentsV.Index(i).Interface())))
|
||||
}
|
||||
}
|
||||
}
|
||||
return llm.ChatMessage{Role: role, Contents: contents}
|
||||
}
|
||||
|
||||
func makeChatMessageContent(contnet string) llm.ChatMessageContent {
|
||||
if strings.HasPrefix(contnet, "data:image/") || ((strings.HasPrefix(contnet, "https://") || strings.HasPrefix(contnet, "http://")) && (strings.HasSuffix(contnet, ".png") || strings.HasSuffix(contnet, ".jpg") || strings.HasSuffix(contnet, ".jpeg") || strings.HasSuffix(contnet, ".gif") || strings.HasSuffix(contnet, ".svg"))) {
|
||||
return llm.ChatMessageContent{Type: llm.TypeImage, Content: contnet}
|
||||
} else if strings.HasPrefix(contnet, "data:video/") || ((strings.HasPrefix(contnet, "https://") || strings.HasPrefix(contnet, "http://")) && (strings.HasSuffix(contnet, ".mp4") || strings.HasSuffix(contnet, ".mov") || strings.HasSuffix(contnet, ".m4v") || strings.HasSuffix(contnet, ".avi") || strings.HasSuffix(contnet, ".wmv"))) {
|
||||
return llm.ChatMessageContent{Type: llm.TypeVideo, Content: contnet}
|
||||
}
|
||||
return llm.ChatMessageContent{Type: llm.TypeText, Content: contnet}
|
||||
}
|
39
llm.md
Normal file
39
llm.md
Normal file
@ -0,0 +1,39 @@
|
||||
|
||||
### Configure
|
||||
|
||||
#### env.yml
|
||||
|
||||
```yaml
|
||||
llm:
|
||||
openai:
|
||||
default:
|
||||
apiKey: ...
|
||||
aurze:
|
||||
apiKey: ...
|
||||
endpoint: ...
|
||||
zhipu:
|
||||
default:
|
||||
apiKey: ...
|
||||
```
|
||||
|
||||
#### encrypt apiKey
|
||||
|
||||
```shell
|
||||
go install github.com/ssgo/tool/sskey@latest
|
||||
sskey -e 'your apiKey' | grep 'url base64'
|
||||
```
|
||||
|
||||
js code:
|
||||
|
||||
```javascript
|
||||
import {zhipu, aurze} from 'llm'
|
||||
import console from 'console'
|
||||
|
||||
function main(...args) {
|
||||
let r = zhipu.fastAsk((args.length>0?args[0]:'你好', r => {
|
||||
console.print(r)
|
||||
})
|
||||
console.println()
|
||||
return r
|
||||
}
|
||||
```
|
86
llm.ts
Normal file
86
llm.ts
Normal file
@ -0,0 +1,86 @@
|
||||
// just for develop
|
||||
|
||||
{{range .}}
|
||||
let {{.}}: LLM
|
||||
{{- end}}
|
||||
|
||||
export default {
|
||||
similarity,
|
||||
{{- range .}}
|
||||
{{.}},
|
||||
{{- end}}
|
||||
}
|
||||
|
||||
function similarity(a: any, b: any): number{return 0}
|
||||
|
||||
interface ChatConfig {
|
||||
model: string
|
||||
ratio: number
|
||||
maxTokens: number
|
||||
temperature: number
|
||||
topP: number
|
||||
tools: Object
|
||||
}
|
||||
|
||||
interface ChatResult {
|
||||
result: string
|
||||
askTokens: number
|
||||
answerTokens: number
|
||||
totalTokens: number
|
||||
usedTime: number
|
||||
}
|
||||
|
||||
interface GCConfig {
|
||||
model: string
|
||||
size: string
|
||||
ref: string
|
||||
}
|
||||
|
||||
interface GCResult {
|
||||
result: string
|
||||
preview: string
|
||||
results: Array<string>
|
||||
previews: Array<string>
|
||||
usedTime: number
|
||||
}
|
||||
|
||||
interface EmbeddingResult {
|
||||
result: string
|
||||
askTokens: number
|
||||
answerTokens: number
|
||||
totalTokens: number
|
||||
usedTime: number
|
||||
}
|
||||
|
||||
interface Support {
|
||||
ask: boolean
|
||||
askWithImage: boolean
|
||||
askWithVideo: boolean
|
||||
askWithCodeInterpreter: boolean
|
||||
askWithWebSearch: boolean
|
||||
makeImage: boolean
|
||||
makeVideo: boolean
|
||||
models: Array<string>
|
||||
}
|
||||
|
||||
interface LLM {
|
||||
ask(messages: any, config?: ChatConfig, callback?: (answer: string) => void): ChatResult
|
||||
fastAsk(messages: any, callback?: (answer: string) => void): ChatResult
|
||||
longAsk(messages: any, callback?: (answer: string) => void): ChatResult
|
||||
batterAsk(messages: any, callback?: (answer: string) => void): ChatResult
|
||||
bestAsk(messages: any, callback?: (answer: string) => void): ChatResult
|
||||
multiAsk(messages: any, callback?: (answer: string) => void): ChatResult
|
||||
bestMultiAsk(messages: any, callback?: (answer: string) => void): ChatResult
|
||||
codeInterpreterAsk(messages: any, callback?: (answer: string) => void): ChatResult
|
||||
webSearchAsk(messages: any, callback?: (answer: string) => void): ChatResult
|
||||
makeImage(prompt: string, config?: GCConfig): GCResult
|
||||
fastMakeImage(prompt: string, config?: GCConfig): GCResult
|
||||
bestMakeImage(prompt: string, config?: GCConfig): GCResult
|
||||
makeVideo(prompt: string, config?: GCConfig): GCResult
|
||||
fastMakeVideo(prompt: string, config?: GCConfig): GCResult
|
||||
bestMakeVideo(prompt: string, config?: GCConfig): GCResult
|
||||
embedding(text: string, model: string): EmbeddingResult
|
||||
fastEmbedding(text: string): EmbeddingResult
|
||||
bestEmbedding(text: string): EmbeddingResult
|
||||
support: Support
|
||||
}
|
187
llm/chat.go
Normal file
187
llm/chat.go
Normal file
@ -0,0 +1,187 @@
|
||||
package llm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"math"
|
||||
)
|
||||
|
||||
type ChatMessage struct {
|
||||
Role string
|
||||
Contents []ChatMessageContent
|
||||
}
|
||||
|
||||
type ChatMessageContent struct {
|
||||
Type string // text, image, audio, video
|
||||
Content string
|
||||
}
|
||||
|
||||
type ChatConfig struct {
|
||||
defaultConfig *ChatConfig
|
||||
Model string
|
||||
Ratio float64
|
||||
MaxTokens int
|
||||
Temperature float64
|
||||
TopP float64
|
||||
Tools map[string]any
|
||||
}
|
||||
|
||||
func (chatConfig *ChatConfig) SetDefault(config *ChatConfig) {
|
||||
chatConfig.defaultConfig = config
|
||||
}
|
||||
|
||||
func (chatConfig *ChatConfig) GetModel() string {
|
||||
if chatConfig.Model == "" && chatConfig.defaultConfig != nil {
|
||||
return chatConfig.defaultConfig.Model
|
||||
}
|
||||
return chatConfig.Model
|
||||
}
|
||||
|
||||
func (chatConfig *ChatConfig) GetMaxTokens() int {
|
||||
if chatConfig.MaxTokens == 0 && chatConfig.defaultConfig != nil {
|
||||
return chatConfig.defaultConfig.MaxTokens
|
||||
}
|
||||
return chatConfig.MaxTokens
|
||||
}
|
||||
|
||||
func (chatConfig *ChatConfig) GetTemperature() float64 {
|
||||
if chatConfig.Temperature == 0 && chatConfig.defaultConfig != nil {
|
||||
return chatConfig.defaultConfig.Temperature
|
||||
}
|
||||
return chatConfig.Temperature
|
||||
}
|
||||
|
||||
func (chatConfig *ChatConfig) GetTopP() float64 {
|
||||
if chatConfig.TopP == 0 && chatConfig.defaultConfig != nil {
|
||||
return chatConfig.defaultConfig.TopP
|
||||
}
|
||||
return chatConfig.TopP
|
||||
}
|
||||
|
||||
func (chatConfig *ChatConfig) GetTools() map[string]any {
|
||||
if chatConfig.Tools == nil && chatConfig.defaultConfig != nil {
|
||||
return chatConfig.defaultConfig.Tools
|
||||
}
|
||||
return chatConfig.Tools
|
||||
}
|
||||
|
||||
type Usage struct {
|
||||
AskTokens int64
|
||||
AnswerTokens int64
|
||||
TotalTokens int64
|
||||
UsedTime int64
|
||||
}
|
||||
|
||||
type MessagesMaker struct {
|
||||
list []ChatMessage
|
||||
}
|
||||
|
||||
func Messages() *MessagesMaker {
|
||||
return &MessagesMaker{
|
||||
list: make([]ChatMessage, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MessagesMaker) Make() []ChatMessage {
|
||||
return m.list
|
||||
}
|
||||
|
||||
func (m *MessagesMaker) User(contents ...ChatMessageContent) *MessagesMaker {
|
||||
m.list = append(m.list, ChatMessage{
|
||||
Role: RoleUser,
|
||||
Contents: contents,
|
||||
})
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MessagesMaker) Assistant(contents ...ChatMessageContent) *MessagesMaker {
|
||||
m.list = append(m.list, ChatMessage{
|
||||
Role: RoleAssistant,
|
||||
Contents: contents,
|
||||
})
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MessagesMaker) System(contents ...ChatMessageContent) *MessagesMaker {
|
||||
m.list = append(m.list, ChatMessage{
|
||||
Role: RoleSystem,
|
||||
Contents: contents,
|
||||
})
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MessagesMaker) Tool(contents ...ChatMessageContent) *MessagesMaker {
|
||||
m.list = append(m.list, ChatMessage{
|
||||
Role: RoleTool,
|
||||
Contents: contents,
|
||||
})
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MessagesMaker) Text(text string) *MessagesMaker {
|
||||
if len(m.list) > 0 {
|
||||
lastIndex := len(m.list) - 1
|
||||
m.list[lastIndex].Contents = append(m.list[lastIndex].Contents, ChatMessageContent{
|
||||
Type: TypeText,
|
||||
Content: text,
|
||||
})
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MessagesMaker) Image(text string) *MessagesMaker {
|
||||
if len(m.list) > 0 {
|
||||
lastIndex := len(m.list) - 1
|
||||
m.list[lastIndex].Contents = append(m.list[lastIndex].Contents, ChatMessageContent{
|
||||
Type: TypeText,
|
||||
Content: text,
|
||||
})
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MessagesMaker) Video(text string) *MessagesMaker {
|
||||
if len(m.list) > 0 {
|
||||
lastIndex := len(m.list) - 1
|
||||
m.list[lastIndex].Contents = append(m.list[lastIndex].Contents, ChatMessageContent{
|
||||
Type: TypeText,
|
||||
Content: text,
|
||||
})
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func bin2float64(in []byte) []float64 {
|
||||
buf := bytes.NewBuffer(in)
|
||||
out := make([]float64, len(in)/4)
|
||||
for i := 0; i < len(out); i++ {
|
||||
var f float32
|
||||
_ = binary.Read(buf, binary.LittleEndian, &f)
|
||||
out[i] = float64(f)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func Similarity(buf1, buf2 []byte) float64 {
|
||||
a := bin2float64(buf1)
|
||||
b := bin2float64(buf2)
|
||||
if len(a) != len(b) {
|
||||
return 0
|
||||
}
|
||||
|
||||
var dotProduct, magnitudeA, magnitudeB float64
|
||||
for i := 0; i < len(a); i++ {
|
||||
dotProduct += a[i] * b[i]
|
||||
magnitudeA += a[i] * a[i]
|
||||
magnitudeB += b[i] * b[i]
|
||||
}
|
||||
|
||||
magnitudeA = math.Sqrt(magnitudeA)
|
||||
magnitudeB = math.Sqrt(magnitudeB)
|
||||
|
||||
if magnitudeA == 0 || magnitudeB == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return dotProduct / (magnitudeA * magnitudeB)
|
||||
}
|
33
llm/gc.go
Normal file
33
llm/gc.go
Normal file
@ -0,0 +1,33 @@
|
||||
package llm
|
||||
|
||||
type GCConfig struct {
|
||||
defaultConfig *GCConfig
|
||||
Model string
|
||||
Size string
|
||||
Ref string
|
||||
}
|
||||
|
||||
func (gcConfig *GCConfig) SetDefault(config *GCConfig) {
|
||||
gcConfig.defaultConfig = config
|
||||
}
|
||||
|
||||
func (gcConfig *GCConfig) GetModel() string {
|
||||
if gcConfig.Model == "" && gcConfig.defaultConfig != nil {
|
||||
return gcConfig.defaultConfig.Model
|
||||
}
|
||||
return gcConfig.Model
|
||||
}
|
||||
|
||||
func (gcConfig *GCConfig) GetSize() string {
|
||||
if gcConfig.Size == "" && gcConfig.defaultConfig != nil {
|
||||
return gcConfig.defaultConfig.Size
|
||||
}
|
||||
return gcConfig.Size
|
||||
}
|
||||
|
||||
func (gcConfig *GCConfig) GetRef() string {
|
||||
if gcConfig.Ref == "" && gcConfig.defaultConfig != nil {
|
||||
return gcConfig.defaultConfig.Ref
|
||||
}
|
||||
return gcConfig.Ref
|
||||
}
|
100
llm/llm.go
Normal file
100
llm/llm.go
Normal file
@ -0,0 +1,100 @@
|
||||
package llm
|
||||
|
||||
import "sync"
|
||||
|
||||
const (
|
||||
TypeText = "text"
|
||||
TypeImage = "image"
|
||||
TypeVideo = "video"
|
||||
RoleSystem = "system"
|
||||
RoleUser = "user"
|
||||
RoleAssistant = "assistant"
|
||||
RoleTool = "tool"
|
||||
ToolCodeInterpreter = "codeInterpreter"
|
||||
ToolWebSearch = "webSearch"
|
||||
)
|
||||
|
||||
type Support struct {
|
||||
Ask bool
|
||||
AskWithImage bool
|
||||
AskWithVideo bool
|
||||
AskWithCodeInterpreter bool
|
||||
AskWithWebSearch bool
|
||||
MakeImage bool
|
||||
MakeVideo bool
|
||||
Models []string
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Endpoint string
|
||||
ApiKey string
|
||||
ChatConfig ChatConfig
|
||||
GCConfig GCConfig
|
||||
Debug bool
|
||||
}
|
||||
|
||||
type LLM interface {
|
||||
Support() Support
|
||||
Ask(messages []ChatMessage, config ChatConfig, callback func(answer string)) (string, Usage, error)
|
||||
FastAsk(messages []ChatMessage, callback func(answer string)) (string, Usage, error)
|
||||
LongAsk(messages []ChatMessage, callback func(answer string)) (string, Usage, error)
|
||||
BatterAsk(messages []ChatMessage, callback func(answer string)) (string, Usage, error)
|
||||
BestAsk(messages []ChatMessage, callback func(answer string)) (string, Usage, error)
|
||||
MultiAsk(messages []ChatMessage, callback func(answer string)) (string, Usage, error)
|
||||
BestMultiAsk(messages []ChatMessage, callback func(answer string)) (string, Usage, error)
|
||||
CodeInterpreterAsk(messages []ChatMessage, callback func(answer string)) (string, Usage, error)
|
||||
WebSearchAsk(messages []ChatMessage, callback func(answer string)) (string, Usage, error)
|
||||
MakeImage(prompt string, config GCConfig) ([]string, Usage, error)
|
||||
FastMakeImage(prompt string, config GCConfig) ([]string, Usage, error)
|
||||
BestMakeImage(prompt string, config GCConfig) ([]string, Usage, error)
|
||||
MakeVideo(prompt string, config GCConfig) ([]string, []string, Usage, error)
|
||||
FastMakeVideo(prompt string, config GCConfig) ([]string, []string, Usage, error)
|
||||
BestMakeVideo(prompt string, config GCConfig) ([]string, []string, Usage, error)
|
||||
Embedding(text string, model string) ([]byte, Usage, error)
|
||||
FastEmbedding(text string) ([]byte, Usage, error)
|
||||
BestEmbedding(text string) ([]byte, Usage, error)
|
||||
}
|
||||
|
||||
var llmMakers = map[string]func(Config) LLM{}
|
||||
var llmMakersLock = sync.RWMutex{}
|
||||
|
||||
var llms = map[string]LLM{}
|
||||
var llmsLock = sync.RWMutex{}
|
||||
|
||||
func Register(llmId string, maker func(Config) LLM) {
|
||||
llmMakersLock.Lock()
|
||||
llmMakers[llmId] = maker
|
||||
llmMakersLock.Unlock()
|
||||
}
|
||||
|
||||
func Create(name, llmId string, config Config) LLM {
|
||||
llmMakersLock.RLock()
|
||||
maker := llmMakers[llmId]
|
||||
llmMakersLock.RUnlock()
|
||||
|
||||
if maker != nil {
|
||||
llm := maker(config)
|
||||
llmsLock.Lock()
|
||||
llms[name] = llm
|
||||
llmsLock.Unlock()
|
||||
return llm
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Get(name string) LLM {
|
||||
llmsLock.RLock()
|
||||
llm := llms[name]
|
||||
llmsLock.RUnlock()
|
||||
return llm
|
||||
}
|
||||
|
||||
func List() map[string]LLM {
|
||||
list := map[string]LLM{}
|
||||
llmsLock.RLock()
|
||||
for name, llm := range llms {
|
||||
list[name] = llm
|
||||
}
|
||||
llmsLock.RUnlock()
|
||||
return list
|
||||
}
|
39
llm_test.go
Normal file
39
llm_test.go
Normal file
@ -0,0 +1,39 @@
|
||||
package llm
|
||||
|
||||
import (
|
||||
_ "apigo.cc/ai/llm/zhipu"
|
||||
"apigo.cc/apigo/gojs"
|
||||
_ "apigo.cc/apigo/gojs/modules"
|
||||
"fmt"
|
||||
"github.com/ssgo/u"
|
||||
"testing"
|
||||
)
|
||||
|
||||
//func TestZhipu(t *testing.T) {
|
||||
// zp := llm.Create("zp", "zhipu", llm.Config{
|
||||
// ApiKey: "112dba40872699df44e07355f7de0c0c.ObK2rslHYBuxII5J",
|
||||
// })
|
||||
// r, usage, err := zp.FastAsk(llm.Messages().User().Text("你好").Make(), nil) //func(r string) {
|
||||
// // fmt.Print(r)
|
||||
// //})
|
||||
//
|
||||
// fmt.Println(11, r)
|
||||
// fmt.Println(22, usage)
|
||||
// fmt.Println(33, err)
|
||||
//}
|
||||
|
||||
func TestExport(t *testing.T) {
|
||||
gojs.ExportForDev()
|
||||
}
|
||||
|
||||
func TestLLM(t *testing.T) {
|
||||
r, err := gojs.RunFile("test.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if r == nil {
|
||||
t.Fatal("no answer")
|
||||
}
|
||||
fmt.Println()
|
||||
fmt.Println(u.BCyan(u.JsonP(r)))
|
||||
}
|
230
openai/chat.go
Normal file
230
openai/chat.go
Normal file
@ -0,0 +1,230 @@
|
||||
package openai
|
||||
|
||||
import (
|
||||
"apigo.cc/ai/llm/llm"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/sashabaranov/go-openai"
|
||||
"github.com/ssgo/log"
|
||||
"github.com/ssgo/u"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (lm *LLM) FastAsk(messages []llm.ChatMessage, callback func(answer string)) (string, llm.Usage, error) {
|
||||
return lm.Ask(messages, llm.ChatConfig{
|
||||
Model: ModelGPT_4o_mini_2024_07_18,
|
||||
}, callback)
|
||||
}
|
||||
|
||||
func (lm *LLM) LongAsk(messages []llm.ChatMessage, callback func(answer string)) (string, llm.Usage, error) {
|
||||
return lm.Ask(messages, llm.ChatConfig{
|
||||
Model: ModelGPT_4_32k_0613,
|
||||
}, callback)
|
||||
}
|
||||
|
||||
func (lm *LLM) BatterAsk(messages []llm.ChatMessage, callback func(answer string)) (string, llm.Usage, error) {
|
||||
return lm.Ask(messages, llm.ChatConfig{
|
||||
Model: ModelGPT_4_turbo,
|
||||
}, callback)
|
||||
}
|
||||
|
||||
func (lm *LLM) BestAsk(messages []llm.ChatMessage, callback func(answer string)) (string, llm.Usage, error) {
|
||||
return lm.Ask(messages, llm.ChatConfig{
|
||||
Model: ModelGPT_4o_2024_08_06,
|
||||
}, callback)
|
||||
}
|
||||
|
||||
func (lm *LLM) MultiAsk(messages []llm.ChatMessage, callback func(answer string)) (string, llm.Usage, error) {
|
||||
return lm.Ask(messages, llm.ChatConfig{
|
||||
Model: ModelGPT_4o_mini_2024_07_18,
|
||||
}, callback)
|
||||
}
|
||||
|
||||
func (lm *LLM) BestMultiAsk(messages []llm.ChatMessage, callback func(answer string)) (string, llm.Usage, error) {
|
||||
return lm.Ask(messages, llm.ChatConfig{
|
||||
Model: ModelGPT_4o_2024_08_06,
|
||||
}, callback)
|
||||
}
|
||||
|
||||
func (lm *LLM) CodeInterpreterAsk(messages []llm.ChatMessage, callback func(answer string)) (string, llm.Usage, error) {
|
||||
return lm.Ask(messages, llm.ChatConfig{
|
||||
Model: ModelGPT_4o,
|
||||
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: ModelGPT_4o_mini_2024_07_18,
|
||||
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) {
|
||||
openaiConf := openai.DefaultConfig(lm.config.ApiKey)
|
||||
if lm.config.Endpoint != "" {
|
||||
openaiConf.BaseURL = lm.config.Endpoint
|
||||
}
|
||||
|
||||
config.SetDefault(&lm.config.ChatConfig)
|
||||
|
||||
agentMessages := make([]openai.ChatCompletionMessage, len(messages))
|
||||
for i, msg := range messages {
|
||||
var contents []openai.ChatMessagePart
|
||||
if msg.Contents != nil {
|
||||
contents = make([]openai.ChatMessagePart, len(msg.Contents))
|
||||
for j, inPart := range msg.Contents {
|
||||
part := openai.ChatMessagePart{}
|
||||
part.Type = TypeMap[inPart.Type]
|
||||
switch inPart.Type {
|
||||
case llm.TypeText:
|
||||
part.Text = inPart.Content
|
||||
case llm.TypeImage:
|
||||
part.ImageURL = &openai.ChatMessageImageURL{
|
||||
URL: inPart.Content,
|
||||
Detail: openai.ImageURLDetailAuto,
|
||||
}
|
||||
}
|
||||
contents[j] = part
|
||||
}
|
||||
}
|
||||
if len(contents) == 1 && contents[0].Type == llm.TypeText {
|
||||
agentMessages[i] = openai.ChatCompletionMessage{
|
||||
Role: RoleMap[msg.Role],
|
||||
Content: contents[0].Text,
|
||||
}
|
||||
} else {
|
||||
agentMessages[i] = openai.ChatCompletionMessage{
|
||||
Role: RoleMap[msg.Role],
|
||||
MultiContent: contents,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
opt := openai.ChatCompletionRequest{
|
||||
Model: config.GetModel(),
|
||||
Messages: agentMessages,
|
||||
MaxTokens: config.GetMaxTokens(),
|
||||
Temperature: float32(config.GetTemperature()),
|
||||
TopP: float32(config.GetTopP()),
|
||||
StreamOptions: &openai.StreamOptions{
|
||||
IncludeUsage: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name := range config.GetTools() {
|
||||
switch name {
|
||||
case llm.ToolCodeInterpreter:
|
||||
opt.Tools = append(opt.Tools, openai.Tool{Type: "code_interpreter"})
|
||||
case llm.ToolWebSearch:
|
||||
}
|
||||
}
|
||||
|
||||
c := openai.NewClientWithConfig(openaiConf)
|
||||
if callback != nil {
|
||||
opt.Stream = true
|
||||
r, err := c.CreateChatCompletionStream(context.Background(), opt)
|
||||
if err == nil {
|
||||
results := make([]string, 0)
|
||||
usage := llm.Usage{}
|
||||
for {
|
||||
if r2, err := r.Recv(); err == nil {
|
||||
if r2.Choices != nil {
|
||||
for _, ch := range r2.Choices {
|
||||
text := ch.Delta.Content
|
||||
callback(text)
|
||||
results = append(results, text)
|
||||
}
|
||||
}
|
||||
if r2.Usage != nil {
|
||||
usage.AskTokens += int64(r2.Usage.PromptTokens)
|
||||
usage.AnswerTokens += int64(r2.Usage.CompletionTokens)
|
||||
usage.TotalTokens += int64(r2.Usage.TotalTokens)
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
_ = r.Close()
|
||||
return strings.Join(results, ""), usage, nil
|
||||
} else {
|
||||
log.DefaultLogger.Error(err.Error())
|
||||
return "", llm.Usage{}, err
|
||||
}
|
||||
} else {
|
||||
t1 := time.Now().UnixMilli()
|
||||
if r, err := c.CreateChatCompletion(context.Background(), opt); 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, ""), llm.Usage{
|
||||
AskTokens: int64(r.Usage.PromptTokens),
|
||||
AnswerTokens: int64(r.Usage.CompletionTokens),
|
||||
TotalTokens: int64(r.Usage.TotalTokens),
|
||||
UsedTime: t2,
|
||||
}, nil
|
||||
} else {
|
||||
//fmt.Println(u.BMagenta(err.Error()), u.BMagenta(u.JsonP(r)))
|
||||
return "", llm.Usage{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (lm *LLM) FastEmbedding(text string) ([]byte, llm.Usage, error) {
|
||||
return lm.Embedding(text, string(openai.AdaEmbeddingV2))
|
||||
}
|
||||
|
||||
func (lm *LLM) BestEmbedding(text string) ([]byte, llm.Usage, error) {
|
||||
return lm.Embedding(text, string(openai.LargeEmbedding3))
|
||||
}
|
||||
|
||||
func (lm *LLM) Embedding(text, model string) ([]byte, llm.Usage, error) {
|
||||
fmt.Println(111, model, text)
|
||||
openaiConf := openai.DefaultConfig(lm.config.ApiKey)
|
||||
if lm.config.Endpoint != "" {
|
||||
openaiConf.BaseURL = lm.config.Endpoint
|
||||
}
|
||||
|
||||
c := openai.NewClientWithConfig(openaiConf)
|
||||
req := openai.EmbeddingRequest{
|
||||
Input: text,
|
||||
Model: openai.EmbeddingModel(model),
|
||||
User: "",
|
||||
EncodingFormat: "",
|
||||
Dimensions: 0,
|
||||
}
|
||||
|
||||
if lm.config.Debug {
|
||||
fmt.Println(u.JsonP(req))
|
||||
}
|
||||
|
||||
t1 := time.Now().UnixMilli()
|
||||
if r, err := c.CreateEmbeddings(context.Background(), req); 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, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Println(len(buf.Bytes()))
|
||||
return buf.Bytes(), llm.Usage{
|
||||
AskTokens: int64(r.Usage.PromptTokens),
|
||||
AnswerTokens: int64(r.Usage.CompletionTokens),
|
||||
TotalTokens: int64(r.Usage.TotalTokens),
|
||||
UsedTime: t2,
|
||||
}, nil
|
||||
} else {
|
||||
fmt.Println(err.Error())
|
||||
return nil, llm.Usage{}, err
|
||||
}
|
||||
}
|
81
openai/config.go
Normal file
81
openai/config.go
Normal file
@ -0,0 +1,81 @@
|
||||
package openai
|
||||
|
||||
import (
|
||||
"apigo.cc/ai/llm/llm"
|
||||
"github.com/sashabaranov/go-openai"
|
||||
)
|
||||
|
||||
type LLM struct {
|
||||
config llm.Config
|
||||
}
|
||||
|
||||
var TypeMap = map[string]openai.ChatMessagePartType{
|
||||
llm.TypeText: openai.ChatMessagePartTypeText,
|
||||
llm.TypeImage: openai.ChatMessagePartTypeImageURL,
|
||||
//llm.TypeVideo: "video_url",
|
||||
}
|
||||
var RoleMap = map[string]string{
|
||||
llm.RoleSystem: openai.ChatMessageRoleSystem,
|
||||
llm.RoleUser: openai.ChatMessageRoleUser,
|
||||
llm.RoleAssistant: openai.ChatMessageRoleAssistant,
|
||||
llm.RoleTool: openai.ChatMessageRoleTool,
|
||||
}
|
||||
|
||||
const (
|
||||
ModelGPT_4_32k_0613 = "gpt-4-32k-0613"
|
||||
ModelGPT_4_32k_0314 = "gpt-4-32k-0314"
|
||||
ModelGPT_4_32k = "gpt-4-32k"
|
||||
ModelGPT_4_0613 = "gpt-4-0613"
|
||||
ModelGPT_4_0314 = "gpt-4-0314"
|
||||
ModelGPT_4o = "gpt-4o"
|
||||
ModelGPT_4o_2024_05_13 = "gpt-4o-2024-05-13"
|
||||
ModelGPT_4o_2024_08_06 = "gpt-4o-2024-08-06"
|
||||
ModelGPT_4o_mini = "gpt-4o-mini"
|
||||
ModelGPT_4o_mini_2024_07_18 = "gpt-4o-mini-2024-07-18"
|
||||
ModelGPT_4_turbo = "gpt-4-turbo"
|
||||
ModelGPT_4_turbo_2024_04_09 = "gpt-4-turbo-2024-04-09"
|
||||
ModelGPT_4_0125_preview = "gpt-4-0125-preview"
|
||||
ModelGPT_4_1106_preview = "gpt-4-1106-preview"
|
||||
ModelGPT_4_turbo_preview = "gpt-4-turbo-preview"
|
||||
ModelGPT_4_vision_preview = "gpt-4-vision-preview"
|
||||
ModelGPT_4 = "gpt-4"
|
||||
ModelGPT_3_5_turbo_0125 = "gpt-3.5-turbo-0125"
|
||||
ModelGPT_3_5_turbo_1106 = "gpt-3.5-turbo-1106"
|
||||
ModelGPT_3_5_turbo_0613 = "gpt-3.5-turbo-0613"
|
||||
ModelGPT_3_5_turbo_0301 = "gpt-3.5-turbo-0301"
|
||||
ModelGPT_3_5_turbo_16k = "gpt-3.5-turbo-16k"
|
||||
ModelGPT_3_5_turbo_16k_0613 = "gpt-3.5-turbo-16k-0613"
|
||||
ModelGPT_3_5_turbo = "gpt-3.5-turbo"
|
||||
ModelGPT_3_5_turbo_instruct = "gpt-3.5-turbo-instruct"
|
||||
ModelDavinci_002 = "davinci-002"
|
||||
ModelCurie = "curie"
|
||||
ModelCurie_002 = "curie-002"
|
||||
ModelAda_002 = "ada-002"
|
||||
ModelBabbage_002 = "babbage-002"
|
||||
ModelCode_davinci_002 = "code-davinci-002"
|
||||
ModelCode_cushman_001 = "code-cushman-001"
|
||||
ModelCode_davinci_001 = "code-davinci-001"
|
||||
ModelDallE2Std = "dall-e-2"
|
||||
ModelDallE2HD = "dall-e-2-hd"
|
||||
ModelDallE3Std = "dall-e-3"
|
||||
ModelDallE3HD = "dall-e-3-hd"
|
||||
)
|
||||
|
||||
func (ag *LLM) Support() llm.Support {
|
||||
return llm.Support{
|
||||
Ask: true,
|
||||
AskWithImage: true,
|
||||
AskWithVideo: false,
|
||||
AskWithCodeInterpreter: true,
|
||||
AskWithWebSearch: false,
|
||||
MakeImage: true,
|
||||
MakeVideo: false,
|
||||
Models: []string{ModelGPT_4_32k_0613, ModelGPT_4_32k_0314, ModelGPT_4_32k, ModelGPT_4_0613, ModelGPT_4_0314, ModelGPT_4o, ModelGPT_4o_2024_05_13, ModelGPT_4o_2024_08_06, ModelGPT_4o_mini, ModelGPT_4o_mini_2024_07_18, ModelGPT_4_turbo, ModelGPT_4_turbo_2024_04_09, ModelGPT_4_0125_preview, ModelGPT_4_1106_preview, ModelGPT_4_turbo_preview, ModelGPT_4_vision_preview, ModelGPT_4, ModelGPT_3_5_turbo_0125, ModelGPT_3_5_turbo_1106, ModelGPT_3_5_turbo_0613, ModelGPT_3_5_turbo_0301, ModelGPT_3_5_turbo_16k, ModelGPT_3_5_turbo_16k_0613, ModelGPT_3_5_turbo, ModelGPT_3_5_turbo_instruct, ModelDavinci_002, ModelCurie, ModelCurie_002, ModelAda_002, ModelBabbage_002, ModelCode_davinci_002, ModelCode_cushman_001, ModelCode_davinci_001, ModelDallE2Std, ModelDallE2HD, ModelDallE3Std, ModelDallE3HD},
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
llm.Register("openai", func(config llm.Config) llm.LLM {
|
||||
return &LLM{config: config}
|
||||
})
|
||||
}
|
83
openai/gc.go
Normal file
83
openai/gc.go
Normal file
@ -0,0 +1,83 @@
|
||||
package openai
|
||||
|
||||
import (
|
||||
"apigo.cc/ai/llm/llm"
|
||||
"context"
|
||||
"github.com/sashabaranov/go-openai"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// func (lm *LLM) FastMakeImage(prompt, size, refImage string) ([]string, llm.Usage, error) {
|
||||
// return lm.MakeImage(ModelDallE3Std, prompt, size, refImage)
|
||||
// }
|
||||
//
|
||||
// func (lm *LLM) BestMakeImage(prompt, size, refImage string) ([]string, llm.Usage, error) {
|
||||
// return lm.MakeImage(ModelDallE3HD, prompt, size, refImage)
|
||||
// }
|
||||
//
|
||||
// func (lm *LLM) MakeImage(model, prompt, size, refImage string) ([]string, llm.Usage, error) {
|
||||
func (lm *LLM) FastMakeImage(prompt string, config llm.GCConfig) ([]string, llm.Usage, error) {
|
||||
config.Model = ModelDallE3Std
|
||||
return lm.MakeImage(prompt, config)
|
||||
}
|
||||
|
||||
func (lm *LLM) BestMakeImage(prompt string, config llm.GCConfig) ([]string, llm.Usage, error) {
|
||||
config.Model = ModelDallE3HD
|
||||
return lm.MakeImage(prompt, config)
|
||||
}
|
||||
|
||||
func (lm *LLM) MakeImage(prompt string, config llm.GCConfig) ([]string, llm.Usage, error) {
|
||||
openaiConf := openai.DefaultConfig(lm.config.ApiKey)
|
||||
if lm.config.Endpoint != "" {
|
||||
openaiConf.BaseURL = lm.config.Endpoint
|
||||
}
|
||||
config.SetDefault(&lm.config.GCConfig)
|
||||
c := openai.NewClientWithConfig(openaiConf)
|
||||
style := openai.CreateImageStyleVivid
|
||||
if (!strings.Contains(prompt, "vivid") || !strings.Contains(prompt, "生动的")) && (strings.Contains(prompt, "natural") || strings.Contains(prompt, "自然的")) {
|
||||
style = openai.CreateImageStyleNatural
|
||||
}
|
||||
quality := openai.CreateImageQualityStandard
|
||||
model := config.GetModel()
|
||||
if strings.HasSuffix(model, "-hd") {
|
||||
quality = openai.CreateImageQualityHD
|
||||
model = model[0 : len(model)-3]
|
||||
}
|
||||
t1 := time.Now().UnixMilli()
|
||||
r, err := c.CreateImage(context.Background(), openai.ImageRequest{
|
||||
Prompt: prompt,
|
||||
Model: model,
|
||||
Quality: quality,
|
||||
Size: config.GetSize(),
|
||||
Style: style,
|
||||
ResponseFormat: openai.CreateImageResponseFormatURL,
|
||||
})
|
||||
t2 := time.Now().UnixMilli() - t1
|
||||
if err == nil {
|
||||
results := make([]string, 0)
|
||||
for _, item := range r.Data {
|
||||
results = append(results, item.URL)
|
||||
}
|
||||
return results, llm.Usage{
|
||||
AskTokens: 0,
|
||||
AnswerTokens: 0,
|
||||
TotalTokens: 0,
|
||||
UsedTime: t2,
|
||||
}, nil
|
||||
} else {
|
||||
return nil, llm.Usage{}, err
|
||||
}
|
||||
}
|
||||
|
||||
func (lm *LLM) FastMakeVideo(prompt string, config llm.GCConfig) ([]string, []string, llm.Usage, error) {
|
||||
return lm.MakeVideo(prompt, config)
|
||||
}
|
||||
|
||||
func (lm *LLM) BestMakeVideo(prompt string, config llm.GCConfig) ([]string, []string, llm.Usage, error) {
|
||||
return lm.MakeVideo(prompt, config)
|
||||
}
|
||||
|
||||
func (lm *LLM) MakeVideo(prompt string, config llm.GCConfig) ([]string, []string, llm.Usage, error) {
|
||||
return nil, nil, llm.Usage{}, nil
|
||||
}
|
4
test.js
Normal file
4
test.js
Normal file
@ -0,0 +1,4 @@
|
||||
import {zhipu, openai} from "llm"
|
||||
import co from "console"
|
||||
|
||||
return zhipu.fastAsk('你好', co.print)
|
195
zhipu/chat.go
Normal file
195
zhipu/chat.go
Normal file
@ -0,0 +1,195 @@
|
||||
package zhipu
|
||||
|
||||
import (
|
||||
"apigo.cc/ai/llm/llm"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/ssgo/u"
|
||||
"github.com/yankeguo/zhipu"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
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 (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))
|
||||
if err != nil {
|
||||
return "", llm.Usage{}, err
|
||||
}
|
||||
|
||||
cc := c.ChatCompletion(config.GetModel())
|
||||
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]
|
||||
switch inPart.Type {
|
||||
case llm.TypeText:
|
||||
part.Text = inPart.Content
|
||||
case llm.TypeImage:
|
||||
part.ImageURL = &zhipu.URLItem{URL: inPart.Content}
|
||||
//case llm.TypeVideo:
|
||||
// part.VideoURL = &zhipu.URLItem{URL: inPart.Content}
|
||||
}
|
||||
contents[j] = part
|
||||
}
|
||||
}
|
||||
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 llm.ToolCodeInterpreter:
|
||||
cc.AddTool(zhipu.ChatCompletionToolCodeInterpreter{})
|
||||
case llm.ToolWebSearch:
|
||||
cc.AddTool(zhipu.ChatCompletionToolWebBrowser{})
|
||||
}
|
||||
}
|
||||
|
||||
if config.GetMaxTokens() != 0 {
|
||||
cc.SetMaxTokens(config.GetMaxTokens())
|
||||
}
|
||||
if config.GetTemperature() != 0 {
|
||||
cc.SetTemperature(config.GetTemperature())
|
||||
}
|
||||
if config.GetTopP() != 0 {
|
||||
cc.SetTopP(config.GetTopP())
|
||||
}
|
||||
if callback != nil {
|
||||
cc.SetStreamHandler(func(r2 zhipu.ChatCompletionResponse) error {
|
||||
if r2.Choices != nil {
|
||||
for _, ch := range r2.Choices {
|
||||
text := ch.Delta.Content
|
||||
callback(text)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
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, ""), llm.Usage{
|
||||
AskTokens: r.Usage.PromptTokens,
|
||||
AnswerTokens: r.Usage.CompletionTokens,
|
||||
TotalTokens: r.Usage.TotalTokens,
|
||||
UsedTime: t2,
|
||||
}, nil
|
||||
} else {
|
||||
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
|
||||
}
|
||||
}
|
60
zhipu/config.go
Normal file
60
zhipu/config.go
Normal file
@ -0,0 +1,60 @@
|
||||
package zhipu
|
||||
|
||||
import (
|
||||
"apigo.cc/ai/llm/llm"
|
||||
"github.com/yankeguo/zhipu"
|
||||
)
|
||||
|
||||
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: true,
|
||||
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},
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
llm.Register("zhipu", func(config llm.Config) llm.LLM {
|
||||
return &LLM{config: config}
|
||||
})
|
||||
}
|
94
zhipu/gc.go
Normal file
94
zhipu/gc.go
Normal file
@ -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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user