move js to root
This commit is contained in:
Star 2024-09-18 18:29:21 +08:00
parent b4cddad489
commit 266e15d459
13 changed files with 180 additions and 131 deletions

View File

@ -1,7 +1,7 @@
package main
import (
"apigo.cc/ai/ai/js"
"apigo.cc/ai/ai"
"fmt"
"github.com/ssgo/u"
"os"
@ -10,7 +10,7 @@ import (
func main() {
if len(os.Args) > 1 && (os.Args[1] == "-e" || os.Args[1] == "export") {
imports, err := js.ExportForDev()
imports, err := ai.ExportForDev()
if err != nil {
fmt.Println(err.Error())
} else {
@ -40,7 +40,7 @@ function main(...args) {
for i := 2; i < len(os.Args); i++ {
args[i-2] = os.Args[i]
}
result, err := js.RunFile(jsFile, args...)
result, err := ai.RunFile(jsFile, args...)
if err != nil {
fmt.Println(err.Error())
} else if result != nil {

6
go.mod
View File

@ -4,6 +4,7 @@ go 1.22
require (
github.com/dop251/goja v0.0.0-20240828124009-016eb7256539
github.com/dop251/goja_nodejs v0.0.0-20240728170619-29b559befffc
github.com/go-resty/resty/v2 v2.15.0
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/sashabaranov/go-openai v1.29.2
@ -16,7 +17,6 @@ require (
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2 v1.11.4 // indirect
github.com/dop251/goja_nodejs v0.0.0-20240728170619-29b559befffc // indirect
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
@ -25,7 +25,3 @@ require (
golang.org/x/text v0.18.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace (
github.com/ssgo/config v1.7.7 => ../../ssgo/config
)

View File

@ -1,7 +1,7 @@
package js
package ai
import (
"apigo.cc/ai/ai"
"apigo.cc/ai/ai/js"
"apigo.cc/ai/ai/llm"
"bytes"
_ "embed"
@ -17,24 +17,27 @@ import (
"text/template"
)
//go:embed lib/ai.ts
//go:embed js/lib/ai.ts
var aiTS string
//go:embed lib/console.ts
//go:embed js/lib/console.ts
var consoleTS string
//go:embed lib/file.ts
//go:embed js/lib/file.ts
var fileTS string
//go:embed js/lib/util.ts
var utilTS string
func RunFile(file string, args ...any) (any, error) {
return Run(u.ReadFileN(file), file, args...)
}
func Run(code string, refFile string, args ...any) (any, error) {
var r any
js, err := StartFromCode(code, refFile)
rt, err := StartFromCode(code, refFile)
if err == nil {
r, err = js.Run(args...)
r, err = rt.Run(args...)
}
return r, err
}
@ -44,7 +47,7 @@ var importLibMatcher = regexp.MustCompile(`(?im)^\s*(import)\s+(.+?)\s+from\s+['
var requireLibMatcher = regexp.MustCompile(`(?im)^\s*(const|let|var)\s+(.+?)\s*=\s*require\s*\(\s*['"][./\\\w:]+lib[/\\](.+?)(\.ts)?['"]\s*\)`)
var checkMainMatcher = regexp.MustCompile(`(?im)^\s*function\s+main\s*\(`)
type JS struct {
type Runtime struct {
vm *goja.Runtime
required map[string]bool
file string
@ -52,34 +55,40 @@ type JS struct {
code string
}
func (js *JS) requireMod(name string) error {
func (rt *Runtime) requireMod(name string) error {
var err error
if name == "console" || name == "" {
if !js.required["console"] {
js.required["console"] = true
err = js.vm.Set("console", requireConsole())
if !rt.required["console"] {
rt.required["console"] = true
err = rt.vm.Set("console", js.RequireConsole())
}
}
if err == nil && (name == "file" || name == "") {
if !js.required["file"] {
js.required["file"] = true
err = js.vm.Set("file", requireFile())
if !rt.required["file"] {
rt.required["file"] = true
err = rt.vm.Set("file", js.RequireFile())
}
}
if err == nil && (name == "util" || name == "") {
if !rt.required["util"] {
rt.required["util"] = true
err = rt.vm.Set("util", js.RequireUtil())
}
}
if err == nil && (name == "ai" || name == "") {
if !js.required["ai"] {
js.required["ai"] = true
if !rt.required["ai"] {
rt.required["ai"] = true
aiList := make(map[string]any)
for name, lm := range llm.List() {
aiList[name] = requireAI(lm)
aiList[name] = js.RequireAI(lm)
}
err = js.vm.Set("ai", aiList)
err = rt.vm.Set("ai", aiList)
}
}
return err
}
func (js *JS) makeImport(matcher *regexp.Regexp, code string) (string, int, error) {
func (rt *Runtime) makeImport(matcher *regexp.Regexp, code string) (string, int, error) {
var modErr error
importCount := 0
code = matcher.ReplaceAllStringFunc(code, func(str string) string {
@ -92,7 +101,7 @@ func (js *JS) makeImport(matcher *regexp.Regexp, code string) (string, int, erro
modName := m[3]
importCount++
if modErr == nil {
if err := js.requireMod(modName); err != nil {
if err := rt.requireMod(modName); err != nil {
modErr = err
}
}
@ -105,11 +114,11 @@ func (js *JS) makeImport(matcher *regexp.Regexp, code string) (string, int, erro
return code, importCount, modErr
}
func StartFromFile(file string) (*JS, error) {
func StartFromFile(file string) (*Runtime, error) {
return StartFromCode(u.ReadFileN(file), file)
}
func StartFromCode(code, refFile string) (*JS, error) {
func StartFromCode(code, refFile string) (*Runtime, error) {
if refFile == "" {
refFile = "main.js"
}
@ -118,9 +127,9 @@ func StartFromCode(code, refFile string) (*JS, error) {
refFile = absFile
}
ai.InitFrom(filepath.Dir(refFile))
InitFrom(filepath.Dir(refFile))
js := &JS{
rt := &Runtime{
vm: goja.New(),
required: map[string]bool{},
file: refFile,
@ -131,29 +140,29 @@ func StartFromCode(code, refFile string) (*JS, error) {
// 按需加载引用
var importCount int
var modErr error
js.code, importCount, modErr = js.makeImport(importLibMatcher, js.code)
rt.code, importCount, modErr = rt.makeImport(importLibMatcher, rt.code)
if modErr == nil {
importCount1 := importCount
js.code, importCount, modErr = js.makeImport(requireLibMatcher, js.code)
rt.code, importCount, modErr = rt.makeImport(requireLibMatcher, rt.code)
importCount += importCount1
}
// 将 import 转换为 require
js.code = importModMatcher.ReplaceAllString(js.code, "let $1 = require('$2')")
rt.code = importModMatcher.ReplaceAllString(rt.code, "let $1 = require('$2')")
// 如果没有import默认import所有
if modErr == nil && importCount == 0 {
modErr = js.requireMod("")
modErr = rt.requireMod("")
}
if modErr != nil {
return nil, modErr
}
//fmt.Println(u.BCyan(js.code))
//fmt.Println(u.BCyan(rt.code))
// 处理模块引用
require.NewRegistryWithLoader(func(path string) ([]byte, error) {
refPath := filepath.Join(filepath.Dir(js.file), path)
refPath := filepath.Join(filepath.Dir(rt.file), path)
if !strings.HasSuffix(refPath, ".js") && !u.FileExists(refPath) {
refPath += ".js"
}
@ -161,23 +170,23 @@ func StartFromCode(code, refFile string) (*JS, error) {
if err != nil {
return nil, err
}
modCode, _, _ = js.makeImport(importLibMatcher, modCode)
modCode, _, _ = js.makeImport(requireLibMatcher, modCode)
modCode, _, _ = rt.makeImport(importLibMatcher, modCode)
modCode, _, _ = rt.makeImport(requireLibMatcher, modCode)
return []byte(modCode), modErr
}).Enable(js.vm)
}).Enable(rt.vm)
// 初始化主函数
if !checkMainMatcher.MatchString(js.code) {
js.code = "function main(...args){" + js.code + "}"
if !checkMainMatcher.MatchString(rt.code) {
rt.code = "function main(...args){" + rt.code + "}"
}
if _, err := js.vm.RunScript("main", js.code); err != nil {
if _, err := rt.vm.RunScript("main", rt.code); err != nil {
return nil, err
}
return js, nil
return rt, nil
}
func (js *JS) Run(args ...any) (any, error) {
func (rt *Runtime) Run(args ...any) (any, error) {
// 解析参数
for i, arg := range args {
if str, ok := arg.(string); ok {
@ -188,10 +197,10 @@ func (js *JS) Run(args ...any) (any, error) {
}
}
if err := js.vm.Set("__args", args); err != nil {
if err := rt.vm.Set("__args", args); err != nil {
return nil, err
}
jsResult, err := js.vm.RunScript(js.file, "main(...__args)")
jsResult, err := rt.vm.RunScript(rt.file, "main(...__args)")
var result any
if err == nil {
@ -207,7 +216,7 @@ type Exports struct {
}
func ExportForDev() (string, error) {
ai.Init()
Init()
if len(llm.List()) == 0 && !u.FileExists("env.yml") && !u.FileExists("env.json") && !u.FileExists("llm.yml") && !u.FileExists("llm.json") {
return "", errors.New("no llm config found, please run `ai -e` on env.yml or llm.yml path")
}
@ -231,8 +240,10 @@ func ExportForDev() (string, error) {
_ = u.WriteFile(filepath.Join("lib", "console.ts"), consoleTS)
_ = u.WriteFile(filepath.Join("lib", "file.ts"), fileTS)
_ = u.WriteFile(filepath.Join("lib", "util.ts"), utilTS)
return `import {` + strings.Join(exports.LLMList, ", ") + `} from './lib/ai'
import console from './lib/console'
import util from './lib/util'
import file from './lib/file'`, nil
}

View File

@ -22,7 +22,7 @@ type AIGCResult struct {
Error string
}
func requireAI(lm llm.LLM) map[string]any {
func RequireAI(lm llm.LLM) map[string]any {
return map[string]any{
"ask": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
conf, cb := getAskArgs(args.This, vm, args.Arguments)

View File

@ -7,7 +7,7 @@ import (
"strings"
)
func requireConsole() map[string]any {
func RequireConsole() map[string]any {
return map[string]any{
"print": func(args goja.FunctionCall) goja.Value {
consolePrint(args, "print", nil)

View File

@ -6,7 +6,7 @@ import (
"github.com/ssgo/u"
)
func requireFile() map[string]any {
func RequireFile() map[string]any {
return map[string]any{
"read": func(args goja.FunctionCall, vm *goja.Runtime) goja.Value {
if len(args.Arguments) < 1 {

View File

@ -2,15 +2,15 @@
{{range .LLMList}}
let {{.}}: LLM
{{end}}
{{- end}}
export default {
{{range .LLMList}}
{{- range .LLMList}}
{{.}},
{{end}}
{{- end}}
}
interface ChatModelConfig {
interface ChatConfig {
model: string
ratio: number
maxTokens: number
@ -27,6 +27,12 @@ interface ChatResult {
error: string
}
interface GCConfig {
model: string
size: string
ref: string
}
interface GCResult {
result: string
preview: string
@ -47,35 +53,20 @@ interface Support {
}
interface LLM {
ask(messages: any, config?: ChatModelConfig, callback?: (answer: string) => void): ChatResult
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(model: string, prompt: string, size?: string, refImage?: string): GCResult
fastMakeImage(prompt: string, size?: string, refImage?: string): GCResult
bestMakeImage(prompt: string, size?: string, refImage?: string): GCResult
makeVideo(arg2: string, arg3: string, arg4: string, arg5: string): GCResult
fastMakeVideo(prompt: string, size?: string, refImage?: string): GCResult
bestMakeVideo(prompt: string, size?: string, refImage?: string): GCResult
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
support: Support
}

View File

@ -11,27 +11,11 @@ export default {
input,
}
function print(...data: any[]): void {
}
function println(...data: any[]): void {
}
function log(...data: any[]): void {
}
function debug(...data: any[]): void {
}
function info(...data: any[]): void {
}
function warn(...data: any[]): void {
}
function error(...data: any[]): void {
}
function input(...data: any[]): string {
return ''
}
function print(...data: any[]): void {}
function println(...data: any[]): void {}
function log(...data: any[]): void {}
function debug(...data: any[]): void {}
function info(...data: any[]): void {}
function warn(...data: any[]): void {}
function error(...data: any[]): void {}
function input(...data: any[]): string {return ''}

View File

@ -7,21 +7,10 @@ export default {
stat
}
function read(filename: string): string {
return ''
}
function write(filename: string, data: any): void {
}
function dir(filename: string): Array<FileInfo> {
return null
}
function stat(filename: string): FileInfo {
return null
}
function read(filename: string): string {return ''}
function write(filename: string, data: any): void {}
function dir(filename: string): Array<FileInfo> {return null}
function stat(filename: string): FileInfo {return null}
interface FileInfo {
Name: string

51
js/lib/util.ts Normal file
View File

@ -0,0 +1,51 @@
// just for develop
export default {
json,
jsonP,
unJson,
yaml,
unYaml,
base64,
unBase64,
urlBase64,
unUrlBase64,
hex,
unHex,
aes,
unAes,
gzip,
gunzip,
id,
uniqueId,
token,
md5,
sha1,
sha256,
sha512,
tpl
}
function json(data:any): string {return ''}
function jsonP(data:any): string {return ''}
function unJson(data:string): any {return null}
function yaml(data:any): string {return ''}
function unYaml(data:string): any {return null}
function base64(data:any): string {return ''}
function unBase64(data:string): any {return null}
function urlBase64(data:any): string {return ''}
function unUrlBase64(data:string): any {return null}
function hex(data:any): string {return ''}
function unHex(data:string): any {return null}
function aes(data:any, key:string, iv:string): string {return ''}
function unAes(data:string, key:string, iv:string): any {return null}
function gzip(data:any): string {return ''}
function gunzip(data:string): any {return null}
function id(): string {return ''}
function uniqueId(): string {return ''}
function token(size:number): string {return ''}
function md5(data:any): string {return ''}
function sha1(data:any): string {return ''}
function sha256(data:any): string {return ''}
function sha512(data:any): string {return ''}
function tpl(text:string, data:any): string {return ''}

View File

@ -31,21 +31,23 @@ func (lm *LLM) MakeImage(prompt string, config llm.GCConfig) ([]string, error) {
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
if strings.HasSuffix(config.Model, "-hd") {
model := config.GetModel()
if strings.HasSuffix(model, "-hd") {
quality = openai.CreateImageQualityHD
config.Model = config.Model[0 : len(config.Model)-3]
model = model[0 : len(model)-3]
}
r, err := c.CreateImage(context.Background(), openai.ImageRequest{
Prompt: prompt,
Model: config.Model,
Model: model,
Quality: quality,
Size: config.Size,
Size: config.GetSize(),
Style: style,
ResponseFormat: openai.CreateImageResponseFormatURL,
})

View File

@ -24,10 +24,9 @@ func (lm *LLM) MakeImage(prompt string, config llm.GCConfig) ([]string, error) {
return nil, err
}
config.SetDefault(&lm.config.GCConfig)
cc := c.ImageGeneration(config.Model).SetPrompt(prompt)
if config.Size != "" {
cc.SetSize(config.Size)
}
cc.SetSize(config.GetSize())
if r, err := cc.Do(context.Background()); err == nil {
results := make([]string, 0)
@ -56,10 +55,9 @@ func (lm *LLM) MakeVideo(prompt string, config llm.GCConfig) ([]string, []string
return nil, nil, err
}
config.SetDefault(&lm.config.GCConfig)
cc := c.VideoGeneration(config.Model).SetPrompt(prompt)
if config.Ref != "" {
cc.SetImageURL(config.Ref)
}
cc.SetImageURL(config.GetRef())
if resp, err := cc.Do(context.Background()); err == nil {
for i := 0; i < 1200; i++ {

View File

@ -4,6 +4,7 @@ import (
"apigo.cc/ai/ai"
"apigo.cc/ai/ai/js"
"apigo.cc/ai/ai/llm"
"encoding/hex"
"fmt"
"github.com/ssgo/u"
"testing"
@ -32,8 +33,9 @@ func TestAgent(t *testing.T) {
//testMakeImage(t, "glm", "", keys.Zhipu, "冬天大雪纷飞,一个男人身穿军绿色棉大衣,戴着红色围巾和绿色帽子走在铺面大雪的小镇路上", "")
//testMakeVideo(t, "glm", "", keys.Zhipu, "大雪纷飞,男人蹦蹦跳跳", "https://aigc-files.bigmodel.cn/api/cogview/20240904133130c4b7121019724aa3_0.png")
testJS(t)
//testJS(t)
//testFile(t)
testUtil(t)
}
func testChat(t *testing.T, llmName string) {
@ -196,7 +198,7 @@ func testMakeVideo(t *testing.T, llmName, prompt, refImage string) {
}
func testJS(t *testing.T) {
r1, err := js.RunFile("test.js", "1+2=4吗")
r1, err := ai.RunFile("test.js", "1+2=4吗")
if err != nil {
t.Fatal("发生错误", err.Error())
}
@ -209,7 +211,7 @@ func testJS(t *testing.T) {
}
func testFile(t *testing.T) {
r1, err := js.Run(`
r1, err := ai.Run(`
import fs from './lib/file'
import out from './lib/console'
let r = fs.read('test.js')
@ -222,3 +224,28 @@ return r
fmt.Println()
fmt.Println("result:", r1)
}
func testUtil(t *testing.T) {
fmt.Println(hex.EncodeToString(u.Sha256([]byte{0, 98, 2})))
r, err := ai.Run(`
let s = String.fromCharCode(0, 98, 2)
//let r = util.base64(s)
//let r2 = util.unBase64(r)
//console.info(r)
//console.info(r2.length, r2)
//console.info(s.charCodeAt(0), r2.charCodeAt(0))
//return r2 === s
let r = util.hex(util.sha256(s))
console.info(r)
return r == '278f25d5e7c09b2cdde1ed72dd83fbb7a7d7dd668ef967c9af65f68aa04049cd'
`, "test.js")
if err != nil {
t.Fatal("发生错误", err.Error())
}
if r != true {
t.Fatal("运行结果不正确", r)
}
fmt.Println()
fmt.Println("result:", r)
}