Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
1c4932d005 | |||
![]() |
db308fe42b | ||
![]() |
ee2775d7fa | ||
b8641271be | |||
a80b493b51 | |||
f9c67b6d50 | |||
459a235055 | |||
b75229ee0a |
56
README.md
56
README.md
@ -1,2 +1,56 @@
|
||||
# huoshan
|
||||
# 火山引擎AI能力
|
||||
|
||||
### 配置 Access Key ID & Secret Access Key
|
||||
|
||||
在火山引擎的[控制台中](https://console.volcengine.com/iam/keymanage/),获取Access Key ID & Secret Access Key(建议使用子账号)
|
||||
|
||||
将 AK 和 SK 以逗号分隔的形式填入 ai.yml 或 env.yml 中
|
||||
|
||||
```yaml
|
||||
ai:
|
||||
huoshan:
|
||||
apiKey: AK,SK
|
||||
```
|
||||
|
||||
建议将 AK&SK 加密,使用工具[sskey](https://github.com/ssgo/tool)
|
||||
|
||||
```shell
|
||||
sskey -e 'AK,SK'
|
||||
```
|
||||
|
||||
复制url base64编码后的字符串填入 ai.yml 或 env.yml 中
|
||||
|
||||
|
||||
## 火山方舟大模型
|
||||
|
||||
在[控制台](https://console.volcengine.com/ark/)的在线推理中创建接入点,使用接入点ID(ep-开头)作为模型名称配置到chat中并赋予一个名称
|
||||
|
||||
```yaml
|
||||
ai:
|
||||
huoshan:
|
||||
chat:
|
||||
fastAsk:
|
||||
model: ep-20241030164919-wql26
|
||||
```
|
||||
|
||||
调用时的代码为:
|
||||
|
||||
```javascript
|
||||
import co from 'apigo.cc/gojs/console'
|
||||
import ai from 'apigo.cc/ai'
|
||||
|
||||
return ai.huoshan.fastAsk('用一句话介绍一下你的主人', co.info, {
|
||||
systemPrompt: '你的主人叫张三,是个程序员'
|
||||
})
|
||||
```
|
||||
|
||||
## 图片生成
|
||||
|
||||
```javascript
|
||||
import co from 'apigo.cc/gojs/console'
|
||||
import ai from 'apigo.cc/ai'
|
||||
|
||||
return ai.huoshan.makeImage({
|
||||
prompt: '在山顶,一位美丽的主人,头戴精致帽子,优雅地坐在藤椅上,手中端着一杯香浓的咖啡,身旁是云雾缭绕的美景,阳光洒在她身上,仿佛一幅宁静而美好的画面'
|
||||
})
|
||||
```
|
||||
|
22
ai_test.go
Normal file
22
ai_test.go
Normal file
@ -0,0 +1,22 @@
|
||||
package huoshan_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
_ "apigo.cc/ai"
|
||||
_ "apigo.cc/ai/huoshan"
|
||||
"apigo.cc/gojs"
|
||||
_ "apigo.cc/gojs/console"
|
||||
_ "apigo.cc/gojs/file"
|
||||
_ "apigo.cc/gojs/util"
|
||||
"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)))
|
||||
}
|
37
ai_test.js
Normal file
37
ai_test.js
Normal file
@ -0,0 +1,37 @@
|
||||
import co from 'apigo.cc/gojs/console'
|
||||
import file from 'apigo.cc/gojs/file'
|
||||
import u from 'apigo.cc/gojs/util'
|
||||
import ai from 'apigo.cc/ai'
|
||||
|
||||
let r = ai.huoshan.fastAsk('用一句话为你的主人写一段生成在山顶喝咖啡的图片的提示词', co.print, {
|
||||
systemPrompt: '你的主人是个年轻姑娘,很美丽,喜欢戴帽子'
|
||||
})
|
||||
co.println()
|
||||
co.info('生成的提示词:', r.result)
|
||||
|
||||
let r2 = ai.huoshan.makeImage({
|
||||
prompt: r.result,
|
||||
systemPrompt: '3D卡通,高清,4K,',
|
||||
width: 1280,
|
||||
height: 720,
|
||||
})
|
||||
co.info('生成的图片:', r2.results[0])
|
||||
|
||||
let r3 = ai.huoshan.makeImageKeepIP({
|
||||
prompt: '年轻姑娘,表情悲伤,骑马在草原上狂奔,天上的云朵,有太阳,有小鸟',
|
||||
width: 1280,
|
||||
height: 720,
|
||||
ref: [r2.results[0]],
|
||||
})
|
||||
co.info('生成角色一致的图片:', r3.results[0])
|
||||
|
||||
let r3s = ai.huoshan.changeEmotion(r3.results[0], { target_emotion: 'liwo' })
|
||||
co.info('梨涡笑:', r3s.result)
|
||||
|
||||
let r3p = ai.huoshan.makeFacePretty(r3s.result)
|
||||
file.write('test.jpg', u.unBase64(r3p.result))
|
||||
co.info('美颜后:', 'test.jpg')
|
||||
|
||||
let r4 = ai.huoshan.getHumanSegment(r3p.result)
|
||||
file.write('test_mask.jpg', u.unBase64(r4.result))
|
||||
co.info('角色主体:', 'test_mask.jpg')
|
198
chat.go
Normal file
198
chat.go
Normal file
@ -0,0 +1,198 @@
|
||||
package huoshan
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"apigo.cc/ai"
|
||||
"github.com/ssgo/u"
|
||||
"github.com/volcengine/volcengine-go-sdk/service/arkruntime"
|
||||
"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
|
||||
)
|
||||
|
||||
func getAKSK(aiConf *ai.AIConfig) (string, string) {
|
||||
keys := strings.SplitN(aiConf.ApiKey, ",", 2)
|
||||
if len(keys) == 1 {
|
||||
keys = append(keys, "")
|
||||
}
|
||||
return keys[0], keys[1]
|
||||
}
|
||||
|
||||
func getChatClient(aiConf *ai.AIConfig) *arkruntime.Client {
|
||||
opt := make([]arkruntime.ConfigOption, 0)
|
||||
if aiConf.Endpoint != "" {
|
||||
opt = append(opt, arkruntime.WithBaseUrl(aiConf.Endpoint))
|
||||
}
|
||||
if aiConf.Extra["region"] != nil {
|
||||
opt = append(opt, arkruntime.WithRegion(u.String(aiConf.Extra["region"])))
|
||||
}
|
||||
ak, sk := getAKSK(aiConf)
|
||||
return arkruntime.NewClientWithAkSk(ak, sk, opt...)
|
||||
}
|
||||
|
||||
func Chat(aiConf *ai.AIConfig, messages []ai.ChatMessage, callback func(string), conf ai.ChatConfig) (ai.ChatResult, error) {
|
||||
req := model.ChatCompletionRequest{
|
||||
Model: conf.Model,
|
||||
}
|
||||
|
||||
req.Messages = make([]*model.ChatCompletionMessage, len(messages))
|
||||
for i, msg := range messages {
|
||||
var contents []*model.ChatCompletionMessageContentPart
|
||||
if msg.Contents != nil {
|
||||
contents = make([]*model.ChatCompletionMessageContentPart, len(msg.Contents))
|
||||
for j, inPart := range msg.Contents {
|
||||
part := model.ChatCompletionMessageContentPart{}
|
||||
part.Type = model.ChatCompletionMessageContentPartType(inPart.Type)
|
||||
switch inPart.Type {
|
||||
case ai.TypeText:
|
||||
part.Text = inPart.Content
|
||||
case ai.TypeImage:
|
||||
part.ImageURL = &model.ChatMessageImageURL{URL: inPart.Content}
|
||||
//case ai.TypeVideo:
|
||||
// part.VideoURL = &model.URLItem{URL: inPart.Content}
|
||||
}
|
||||
contents[j] = &part
|
||||
}
|
||||
}
|
||||
if len(contents) == 1 && contents[0].Type == ai.TypeText {
|
||||
req.Messages[i] = &model.ChatCompletionMessage{
|
||||
Role: msg.Role,
|
||||
Content: &model.ChatCompletionMessageContent{
|
||||
StringValue: &contents[0].Text,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
req.Messages[i] = &model.ChatCompletionMessage{
|
||||
Role: msg.Role,
|
||||
Content: &model.ChatCompletionMessageContent{
|
||||
ListValue: contents,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
if conf.SystemPrompt != "" {
|
||||
req.Messages = append([]*model.ChatCompletionMessage{{
|
||||
Role: ai.RoleSystem,
|
||||
Content: &model.ChatCompletionMessageContent{
|
||||
StringValue: &conf.SystemPrompt,
|
||||
},
|
||||
}}, req.Messages...)
|
||||
}
|
||||
|
||||
tools := conf.Tools
|
||||
if len(tools) > 0 {
|
||||
req.Tools = make([]*model.Tool, 0)
|
||||
for name, toolConf := range tools {
|
||||
switch name {
|
||||
case ai.ToolFunction:
|
||||
conf := model.FunctionDefinition{}
|
||||
u.Convert(toolConf, &conf)
|
||||
req.Tools = append(req.Tools, &model.Tool{
|
||||
Type: model.ToolTypeFunction,
|
||||
Function: &conf,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
if conf.MaxTokens != 0 {
|
||||
req.MaxTokens = conf.MaxTokens
|
||||
}
|
||||
if conf.Temperature != 0 {
|
||||
req.Temperature = float32(conf.Temperature)
|
||||
}
|
||||
if conf.TopP != 0 {
|
||||
req.TopP = float32(conf.TopP)
|
||||
}
|
||||
|
||||
c := getChatClient(aiConf)
|
||||
t1 := time.Now().UnixMilli()
|
||||
if callback != nil {
|
||||
stream, err := c.CreateChatCompletionStream(context.Background(), req)
|
||||
if err != nil {
|
||||
return ai.ChatResult{}, err
|
||||
}
|
||||
results := make([]string, 0)
|
||||
var outErr error
|
||||
out := ai.ChatResult{}
|
||||
for {
|
||||
recv, err := stream.Recv()
|
||||
if recv.Usage != nil {
|
||||
out.AskTokens += int64(recv.Usage.PromptTokens)
|
||||
out.AnswerTokens += int64(recv.Usage.CompletionTokens)
|
||||
out.TotalTokens += int64(recv.Usage.TotalTokens)
|
||||
}
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
outErr = err
|
||||
break
|
||||
}
|
||||
|
||||
if len(recv.Choices) > 0 {
|
||||
for _, ch := range recv.Choices {
|
||||
text := ch.Delta.Content
|
||||
results = append(results, text)
|
||||
callback(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
stream.Close()
|
||||
out.UsedTime = time.Now().UnixMilli() - t1
|
||||
out.Result = strings.Join(results, "")
|
||||
return out, outErr
|
||||
} else {
|
||||
r, err := c.CreateChatCompletion(context.Background(), req)
|
||||
if err != nil {
|
||||
return ai.ChatResult{}, err
|
||||
}
|
||||
t2 := time.Now().UnixMilli() - t1
|
||||
results := make([]string, 0)
|
||||
if r.Choices != nil {
|
||||
for _, ch := range r.Choices {
|
||||
results = append(results, *ch.Message.Content.StringValue)
|
||||
}
|
||||
}
|
||||
return ai.ChatResult{
|
||||
Result: strings.Join(results, ""),
|
||||
AskTokens: int64(r.Usage.PromptTokens),
|
||||
AnswerTokens: int64(r.Usage.CompletionTokens),
|
||||
TotalTokens: int64(r.Usage.TotalTokens),
|
||||
UsedTime: t2,
|
||||
}, nil
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func Embedding(aiConf *ai.AIConfig, text string, embeddingConf ai.EmbeddingConfig) (ai.EmbeddingResult, error) {
|
||||
c := getChatClient(aiConf)
|
||||
req := model.EmbeddingRequestStrings{
|
||||
Input: []string{text},
|
||||
Model: embeddingConf.Model,
|
||||
}
|
||||
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, float32(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
return ai.EmbeddingResult{
|
||||
Result: buf.Bytes(),
|
||||
AskTokens: int64(r.Usage.PromptTokens),
|
||||
AnswerTokens: int64(r.Usage.CompletionTokens),
|
||||
TotalTokens: int64(r.Usage.TotalTokens),
|
||||
UsedTime: t2,
|
||||
}, nil
|
||||
} else {
|
||||
return ai.EmbeddingResult{}, err
|
||||
}
|
||||
}
|
34
config.go
Normal file
34
config.go
Normal file
@ -0,0 +1,34 @@
|
||||
package huoshan
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
|
||||
"apigo.cc/ai"
|
||||
"github.com/ssgo/u"
|
||||
)
|
||||
|
||||
//go:embed default.yml
|
||||
var defaultYml string
|
||||
|
||||
func init() {
|
||||
defaultConf := ai.AILoadConfig{}
|
||||
u.Convert(u.UnYamlMap(defaultYml), &defaultConf)
|
||||
ai.Register("huoshan", &ai.Agent{
|
||||
ChatConfigs: defaultConf.Chat,
|
||||
EmbeddingConfigs: defaultConf.Embedding,
|
||||
ImageConfigs: defaultConf.Image,
|
||||
VideoConfigs: defaultConf.Video,
|
||||
EditConfigs: defaultConf.Edit,
|
||||
AsrConfigs: defaultConf.Asr,
|
||||
TtsConfigs: defaultConf.Tts,
|
||||
Chat: Chat,
|
||||
Embedding: Embedding,
|
||||
MakeImage: MakeImage,
|
||||
// MakeVideo: MakeVideo,
|
||||
// GetVideoResult: GetVideoResult,
|
||||
Edit: Edit,
|
||||
// Scan: Scan,
|
||||
// Asr: Asr,
|
||||
// Tts: Tts,
|
||||
})
|
||||
}
|
85
default.yml
Normal file
85
default.yml
Normal file
@ -0,0 +1,85 @@
|
||||
chat:
|
||||
embedding:
|
||||
image:
|
||||
makeImage:
|
||||
model: t2i_xl_sft
|
||||
width: 1024
|
||||
height: 1024
|
||||
makeImageHD:
|
||||
model: high_aes_general_v20_L:general_v2.0_L
|
||||
width: 512
|
||||
height: 512
|
||||
extra:
|
||||
req_schedule_conf: general_v20_9B_rephraser
|
||||
use_sr: true
|
||||
use_pre_llm: true
|
||||
makeCartoon:
|
||||
model: high_aes:anime_v1.3.1
|
||||
width: 1024
|
||||
height: 1024
|
||||
extra:
|
||||
req_schedule_conf: general_v20_9B_rephraser
|
||||
use_sr: true
|
||||
use_pre_llm: true
|
||||
makeImageKeepIP:
|
||||
model: high_aes_general_v14_ip_keep:general_v1.4_ip
|
||||
width: 1024
|
||||
height: 1024
|
||||
makeAmericanComics:
|
||||
model: img2img_photoverse_american_comics
|
||||
make3DWeird:
|
||||
model: img2img_photoverse_3d_weird
|
||||
makeCyberpunk:
|
||||
model: img2img_photoverse_cyberpunk
|
||||
makeBabi:
|
||||
model: img2img_xiezhen_babi_niuzai
|
||||
extra:
|
||||
beautify_info:
|
||||
whitening: 2
|
||||
dermabrasion: 2
|
||||
makeIDPhoto:
|
||||
model: img2img_photoverse_executive_ID_photo
|
||||
makeOutpainting:
|
||||
model: i2i_outpainting
|
||||
make3DStyle:
|
||||
model: img2img_disney_3d_style
|
||||
makeCartoonStyle:
|
||||
model: img2img_cartoon_style
|
||||
edit:
|
||||
getHumanSegment:
|
||||
action: HumanSegment
|
||||
refine: 1
|
||||
return_foreground_image: 1
|
||||
changeEmotion:
|
||||
action: EmotionPortrait
|
||||
# jiuwo: 酒窝笑
|
||||
# liwo: 梨窝笑
|
||||
# big_smile_white_teeth: 露牙大笑
|
||||
# classic_white_teeth: 露牙标准笑
|
||||
# cool: 耍酷
|
||||
# sad: 悲伤
|
||||
# tight_smile: 勉强笑
|
||||
target_emotion: big_smile_white_teeth
|
||||
changeHair:
|
||||
action: EmotionPortrait
|
||||
# 101: 刘海(默认)
|
||||
# 201: 长发
|
||||
# 301: 刘海加长发
|
||||
# 401: 中程度增发
|
||||
# 402: 轻程度增发
|
||||
# 403: 重程度增发
|
||||
# 502: 轻程度卷发
|
||||
# 503: 重程度卷发
|
||||
# 603: 短发(要求输入尺寸<2048x2048,若输入大于2048x2048的图片,输出的图片会被resize成最长边2048;)
|
||||
# 801: 金发
|
||||
# 901: 直发
|
||||
# 1001: 头发去油(轻微效果,原图中此问题建议较为明显)
|
||||
# 1101: 补发际线(调整过高的发际线,轻微效果,原图中此问题建议较为明显)
|
||||
# 1201: 头发柔顺 (轻微效果,原图需有较大发型区域,原图中此问题建议较为明显)
|
||||
# 1301: 补发缝(轻微效果,填补头发区域中的头皮部分,原图中此问题建议较为明显)
|
||||
hair_type: 101
|
||||
makeFacePretty:
|
||||
action: FacePretty
|
||||
do_risk: false
|
||||
multi_face: 1
|
||||
beauty_level: 1
|
241
gc.go
Normal file
241
gc.go
Normal file
@ -0,0 +1,241 @@
|
||||
package huoshan
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"apigo.cc/ai"
|
||||
"github.com/ssgo/u"
|
||||
"github.com/volcengine/volc-sdk-golang/service/visual"
|
||||
"github.com/volcengine/volc-sdk-golang/service/visual/model"
|
||||
)
|
||||
|
||||
func getVisualClient(aiConf *ai.AIConfig) *visual.Visual {
|
||||
ak, sk := getAKSK(aiConf)
|
||||
vis := visual.NewInstance()
|
||||
vis.Client.SetAccessKey(ak)
|
||||
vis.Client.SetSecretKey(sk)
|
||||
if aiConf.Extra["region"] != nil {
|
||||
vis.SetRegion(u.String(aiConf.Extra["region"]))
|
||||
}
|
||||
if aiConf.Extra["host"] != nil {
|
||||
vis.SetHost(u.String(aiConf.Extra["host"]))
|
||||
}
|
||||
return vis
|
||||
}
|
||||
|
||||
func MakeImage(aiConf *ai.AIConfig, conf ai.ImageConfig) (ai.ImageResult, error) {
|
||||
modelA := strings.SplitN(conf.Model, ":", 2)
|
||||
data := map[string]any{
|
||||
"req_key": modelA[0],
|
||||
"prompt": conf.SystemPrompt + conf.Prompt,
|
||||
"return_url": true,
|
||||
}
|
||||
if len(modelA) > 1 {
|
||||
data["model_version"] = modelA[1]
|
||||
}
|
||||
if conf.NegativePrompt != "" {
|
||||
data["negative_prompt"] = conf.NegativePrompt
|
||||
}
|
||||
if conf.Width > 0 {
|
||||
data["width"] = conf.Width
|
||||
}
|
||||
if conf.Height > 0 {
|
||||
data["height"] = conf.Height
|
||||
}
|
||||
if conf.Scale > 0 {
|
||||
if conf.Scale < 1 {
|
||||
// 取值 0-1 放大到 1-30
|
||||
data["scale"] = conf.Scale * 30
|
||||
} else {
|
||||
data["scale"] = conf.Scale
|
||||
}
|
||||
}
|
||||
if conf.Steps > 0 {
|
||||
data["ddim_steps"] = conf.Steps
|
||||
}
|
||||
if len(conf.Ref) > 0 {
|
||||
// 如果有参考图,自动切换到图生图模型
|
||||
if strings.Contains(modelA[0], "t2i") {
|
||||
data["req_key"] = strings.ReplaceAll(modelA[0], "t2i", "i2i")
|
||||
}
|
||||
// 根据参考图类型设置(url和base64只能2选1)
|
||||
image_url := make([]string, 0)
|
||||
binary_data_base64 := make([]string, 0)
|
||||
for _, ref := range conf.Ref {
|
||||
if strings.Contains(ref, "://") {
|
||||
image_url = append(image_url, ref)
|
||||
} else {
|
||||
binary_data_base64 = append(binary_data_base64, ref)
|
||||
}
|
||||
}
|
||||
if len(image_url) > 0 {
|
||||
data["image_urls"] = conf.Ref
|
||||
} else {
|
||||
data["binary_data_base64"] = binary_data_base64
|
||||
}
|
||||
|
||||
// 参考图权重设置
|
||||
if conf.Cref > 0 || conf.Sref > 0 {
|
||||
if strings.Contains(conf.Model, "ip_keep") {
|
||||
// 人脸保持模型
|
||||
if conf.Cref > 0 {
|
||||
data["ref_id_weight"] = conf.Cref
|
||||
}
|
||||
if conf.Sref > 0 {
|
||||
data["ref_ip_weight"] = conf.Sref
|
||||
}
|
||||
} else if strings.Contains(conf.Model, ":anime_") {
|
||||
// 动漫模型
|
||||
if conf.Sref > 0 {
|
||||
data["strength"] = conf.Sref
|
||||
}
|
||||
} else {
|
||||
// 图生图模型
|
||||
style_reference_args := map[string]any{"binary_data_index": 0}
|
||||
if conf.Cref > 0 {
|
||||
style_reference_args["id_weight"] = conf.Cref
|
||||
}
|
||||
if conf.Sref > 0 {
|
||||
style_reference_args["style_weight"] = conf.Sref
|
||||
}
|
||||
data["style_reference_args"] = style_reference_args
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 其他参数
|
||||
for k, v := range conf.Extra {
|
||||
data[k] = v
|
||||
}
|
||||
|
||||
// fmt.Println(u.BMagenta(u.JsonP(data)), 111)
|
||||
t1 := time.Now().UnixMilli()
|
||||
c := getVisualClient(aiConf)
|
||||
respMap, status, err := c.CVProcess(data)
|
||||
// fmt.Println(u.BCyan(u.JsonP(respMap)), 222)
|
||||
resp := &model.VisualPubResult{}
|
||||
u.Convert(respMap, resp)
|
||||
t2 := time.Now().UnixMilli() - t1
|
||||
|
||||
if err != nil {
|
||||
return ai.ImageResult{}, err
|
||||
}
|
||||
if status != 200 {
|
||||
return ai.ImageResult{}, errors.New(u.String(resp.Message))
|
||||
}
|
||||
return ai.ImageResult{
|
||||
Results: resp.Data.ImageUrls,
|
||||
UsedTime: t2,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Edit(aiConf *ai.AIConfig, from string, conf map[string]any) (ai.StringResult, error) {
|
||||
action := u.String(conf["action"])
|
||||
c := getVisualClient(aiConf)
|
||||
t1 := time.Now().UnixMilli()
|
||||
switch action {
|
||||
case "EmotionPortrait":
|
||||
// 修改表情
|
||||
req := map[string]any{"req_key": "emotion_portrait", "return_url": true}
|
||||
for k, v := range conf {
|
||||
if k != "action" {
|
||||
req[k] = v
|
||||
}
|
||||
}
|
||||
if strings.Contains(from, "://") {
|
||||
req["image_urls"] = []string{from}
|
||||
} else {
|
||||
req["binary_data_base64"] = []string{from}
|
||||
}
|
||||
resp, status, err := c.EmotionPortrait(req)
|
||||
if err != nil {
|
||||
return ai.StringResult{}, err
|
||||
}
|
||||
if status != 200 {
|
||||
return ai.StringResult{}, errors.New(u.String(resp.Message))
|
||||
}
|
||||
result := resp.Data.ImageUrls[0]
|
||||
return ai.StringResult{
|
||||
Result: result,
|
||||
UsedTime: time.Now().UnixMilli() - t1,
|
||||
}, nil
|
||||
case "HumanSegment":
|
||||
// 抠出人像
|
||||
req := url.Values{
|
||||
"refine": {u.String(u.Int(conf["refine"]))},
|
||||
"return_foreground_image": {u.String(u.Int(conf["return_foreground_image"]))},
|
||||
}
|
||||
if strings.Contains(from, "://") {
|
||||
req["image_url"] = []string{from}
|
||||
} else {
|
||||
req["image_base64"] = []string{from}
|
||||
}
|
||||
resp, status, err := c.HumanSegment(req)
|
||||
if err != nil {
|
||||
return ai.StringResult{}, err
|
||||
}
|
||||
if status != 200 {
|
||||
return ai.StringResult{}, errors.New(u.String(resp.Message))
|
||||
}
|
||||
// u.WriteFileBytes("mask.jpg", u.UnBase64(resp.Data.Mask))
|
||||
// u.WriteFileBytes("body.jpg", u.UnBase64(resp.Data.ForegroundImage))
|
||||
result := resp.Data.ForegroundImage
|
||||
if result == "" {
|
||||
result = resp.Data.Mask
|
||||
}
|
||||
return ai.StringResult{
|
||||
Result: result,
|
||||
UsedTime: time.Now().UnixMilli() - t1,
|
||||
}, nil
|
||||
case "FacePretty":
|
||||
// 美颜
|
||||
req := url.Values{
|
||||
"do_risk": {u.GetUpperName(u.String(u.Bool(conf["do_risk"])))},
|
||||
"multi_face": {u.String(u.Int(conf["multi_face"]))},
|
||||
"beauty_level": {u.String(u.Float(conf["beauty_level"]))},
|
||||
}
|
||||
if strings.Contains(from, "://") {
|
||||
req["image_url"] = []string{from}
|
||||
} else {
|
||||
req["image_base64"] = []string{from}
|
||||
}
|
||||
resp, status, err := c.FacePretty(req)
|
||||
if err != nil {
|
||||
return ai.StringResult{}, err
|
||||
}
|
||||
if status != 200 {
|
||||
return ai.StringResult{}, errors.New(u.String(resp.Message))
|
||||
}
|
||||
return ai.StringResult{
|
||||
Result: resp.Data.Image,
|
||||
UsedTime: time.Now().UnixMilli() - t1,
|
||||
}, nil
|
||||
case "HairStyle":
|
||||
// 改变发型
|
||||
req := url.Values{
|
||||
"req_key": {"hair_style"},
|
||||
"return_url": {"true"},
|
||||
"hair_type": {u.String(conf["hair_type"])},
|
||||
}
|
||||
if strings.Contains(from, "://") {
|
||||
req["image_urls"] = []string{from}
|
||||
} else {
|
||||
req["binary_data_base64"] = []string{from}
|
||||
}
|
||||
resp, status, err := c.HairStyle(req)
|
||||
if err != nil {
|
||||
return ai.StringResult{}, err
|
||||
}
|
||||
if status != 200 {
|
||||
return ai.StringResult{}, errors.New(u.String(resp.Message))
|
||||
}
|
||||
return ai.StringResult{
|
||||
Result: resp.Data.Image,
|
||||
UsedTime: time.Now().UnixMilli() - t1,
|
||||
}, nil
|
||||
}
|
||||
return ai.StringResult{}, nil
|
||||
}
|
32
go.mod
32
go.mod
@ -1,35 +1,35 @@
|
||||
module Huoshan
|
||||
module apigo.cc/ai/huoshan
|
||||
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
apigo.cc/apigo/gojs v0.1.1
|
||||
github.com/ssgo/config v1.7.7
|
||||
github.com/ssgo/log v1.7.7
|
||||
github.com/ssgo/u v1.7.7
|
||||
github.com/volcengine/volc-sdk-golang v1.0.178
|
||||
apigo.cc/ai v0.0.2
|
||||
apigo.cc/gojs v0.0.4
|
||||
apigo.cc/gojs/console v0.0.1
|
||||
apigo.cc/gojs/file v0.0.2
|
||||
apigo.cc/gojs/util v0.0.3
|
||||
github.com/ssgo/u v1.7.9
|
||||
github.com/volcengine/volc-sdk-golang v1.0.182
|
||||
github.com/volcengine/volcengine-go-sdk v1.0.162
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/net v0.29.0 // indirect
|
||||
golang.org/x/text v0.18.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.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/google/uuid v1.6.0 // 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/jmespath/go-jmespath v0.4.0 // 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/sys v0.25.0 // indirect
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
)
|
||||
|
44
huoshan.go
44
huoshan.go
@ -1,44 +0,0 @@
|
||||
package huoshan
|
||||
|
||||
import (
|
||||
"apigo.cc/apigo/gojs"
|
||||
_ "embed"
|
||||
"github.com/ssgo/config"
|
||||
"github.com/ssgo/log"
|
||||
"github.com/ssgo/u"
|
||||
)
|
||||
|
||||
//go:embed huoshan.ts
|
||||
var huoshanTS string
|
||||
|
||||
type Conf struct {
|
||||
AKey string
|
||||
SKey string
|
||||
}
|
||||
|
||||
var conf = Conf{}
|
||||
|
||||
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() {
|
||||
obj := gojs.Map{
|
||||
"image": RequireImage(),
|
||||
}
|
||||
config.LoadConfig("huoshan", &conf)
|
||||
conf.AKey = confAes.DecryptUrlBase64ToString(conf.AKey)
|
||||
conf.SKey = confAes.DecryptUrlBase64ToString(conf.SKey)
|
||||
log.DefaultLogger.Info("Conf", "", conf)
|
||||
gojs.Register("huoshan", gojs.Module{
|
||||
Object: obj,
|
||||
TsCode: huoshanTS,
|
||||
Example: "",
|
||||
})
|
||||
}
|
10
huoshan.ts
10
huoshan.ts
@ -1,10 +0,0 @@
|
||||
export default {
|
||||
image: {
|
||||
text2image,
|
||||
image2image,
|
||||
readImage
|
||||
}
|
||||
}
|
||||
function text2image(prompt:string, option?:Object):string {return ""}
|
||||
function image2image(image:string[], prompt:string, option?:Object):string {return ""}
|
||||
function readImage(path:string):string {return ""}
|
117
image.go
117
image.go
@ -1,117 +0,0 @@
|
||||
package huoshan
|
||||
|
||||
import (
|
||||
"apigo.cc/apigo/gojs"
|
||||
"apigo.cc/apigo/gojs/dop251/goja"
|
||||
"errors"
|
||||
"github.com/ssgo/log"
|
||||
"github.com/ssgo/u"
|
||||
"github.com/volcengine/volc-sdk-golang/service/visual"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func RequireImage() gojs.Map {
|
||||
return map[string]interface{}{
|
||||
"text2image": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
return vm.ToValue(T2IXL(args.Str(0), args.Map(1), vm))
|
||||
},
|
||||
"image2image": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(2)
|
||||
v := u.FinalValue(reflect.ValueOf(args.Arguments[0].Export()))
|
||||
switch v.Type().Kind() {
|
||||
case reflect.String:
|
||||
option := args.Map(2)
|
||||
if option == nil {
|
||||
option = map[string]interface{}{}
|
||||
}
|
||||
option["binary_data_base64"] = []string{
|
||||
args.Str(0),
|
||||
}
|
||||
return vm.ToValue(I2IXLbyUrl(nil, args.Str(1), option, vm))
|
||||
case reflect.Slice:
|
||||
vv := u.FinalValue(v.Index(0))
|
||||
if vv.Type().Kind() == reflect.Uint8 {
|
||||
option := args.Map(2)
|
||||
if option == nil {
|
||||
option = map[string]interface{}{}
|
||||
}
|
||||
option["binary_data_base64"] = []string{
|
||||
u.Base64(args.Bytes(0)),
|
||||
}
|
||||
return vm.ToValue(I2IXLbyUrl(nil, args.Str(1), option, vm))
|
||||
} else if vv.Type().Kind() == reflect.String {
|
||||
arr := make([]string, 0)
|
||||
u.Convert(args.Any(0), &arr)
|
||||
return vm.ToValue(I2IXLbyUrl(arr, args.Str(1), args.Map(2), vm))
|
||||
} else {
|
||||
log.DefaultLogger.Error("image type is slice but not supported", "type", args.Arguments[0].ExportType().Kind().String(), "elemType", vv.Type().Kind().String())
|
||||
panic(vm.NewGoError(errors.New(u.Red("image type is slice but not supported"))))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
log.DefaultLogger.Error("image type not supported", "type", args.Arguments[0].ExportType().Kind().String())
|
||||
panic(vm.NewGoError(errors.New(u.Red("image type not supported"))))
|
||||
return nil
|
||||
},
|
||||
"readImage": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||
if r, err := u.ReadFileBytes(args.Path(0)); err == nil {
|
||||
return vm.ToValue(r)
|
||||
} else {
|
||||
panic(vm.NewGoError(err))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// T2IXL used t2i_xl_sft
|
||||
//
|
||||
// option 会覆盖已有参数,可传空,return_url不可更改
|
||||
func T2IXL(prompt string, option map[string]interface{}, vm *goja.Runtime) []string {
|
||||
vis := visual.NewInstance()
|
||||
vis.Client.SetAccessKey(conf.AKey)
|
||||
vis.Client.SetSecretKey(conf.SKey)
|
||||
data := map[string]interface{}{
|
||||
"req_key": "t2i_xl_sft",
|
||||
"prompt": prompt,
|
||||
}
|
||||
if option != nil {
|
||||
u.Convert(option, &data)
|
||||
}
|
||||
data["return_url"] = true
|
||||
resp, status, err := vis.Text2ImgXLSft(data)
|
||||
if status != 200 || err != nil {
|
||||
log.DefaultLogger.Error("request error", status, err)
|
||||
panic(vm.NewGoError(err))
|
||||
return []string{}
|
||||
}
|
||||
return resp.Data.ImageUrls
|
||||
}
|
||||
|
||||
// I2IXLbyUrl used t2i_xl_sft
|
||||
//
|
||||
// option 会覆盖已有参数,可传空,return_url不可更改
|
||||
func I2IXLbyUrl(imageUrls []string, prompt string, option map[string]interface{}, vm *goja.Runtime) []string {
|
||||
vis := visual.NewInstance()
|
||||
vis.Client.SetAccessKey(conf.AKey)
|
||||
vis.Client.SetSecretKey(conf.SKey)
|
||||
data := map[string]interface{}{
|
||||
"req_key": "i2i_xl_sft",
|
||||
"prompt": prompt,
|
||||
}
|
||||
if imageUrls != nil {
|
||||
data["image_urls"] = imageUrls
|
||||
}
|
||||
if option != nil {
|
||||
u.Convert(option, &data)
|
||||
}
|
||||
data["return_url"] = true
|
||||
resp, status, err := vis.Img2ImgXLSft(data)
|
||||
if status != 200 || err != nil {
|
||||
log.DefaultLogger.Error("request error", "status", status, "err", err, "resp", resp)
|
||||
panic(vm.NewGoError(err))
|
||||
return []string{}
|
||||
}
|
||||
return resp.Data.ImageUrls
|
||||
}
|
25
test.js
25
test.js
@ -1,25 +0,0 @@
|
||||
import {image} from "huoshan"
|
||||
import u from "util"
|
||||
|
||||
function main() {
|
||||
let a = image.image2image(u.base64(image.readImage("img_2.jpeg")), "在吃冰淇淋", {
|
||||
style_reference_args: {
|
||||
id_weight: 1.0
|
||||
}
|
||||
})
|
||||
let b = image.image2image(image.readImage("img_2.jpeg"), "在吃冰淇淋", {
|
||||
style_reference_args: {
|
||||
id_weight: 1.0
|
||||
}
|
||||
})
|
||||
let c = image.image2image([""], "在吃冰淇淋", {
|
||||
style_reference_args: {
|
||||
id_weight: 1.0
|
||||
}
|
||||
})
|
||||
return {
|
||||
a: a,
|
||||
b: b,
|
||||
c: c
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package huoshan
|
||||
|
||||
import (
|
||||
"apigo.cc/apigo/gojs"
|
||||
_ "apigo.cc/apigo/gojs/modules"
|
||||
"fmt"
|
||||
"github.com/ssgo/u"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExport(t *testing.T) {
|
||||
gojs.ExportForDev()
|
||||
}
|
||||
|
||||
func TestLLM(t *testing.T) {
|
||||
r, err := gojs.RunFile("test.js")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println()
|
||||
fmt.Println(u.JsonP(r))
|
||||
}
|
Loading…
Reference in New Issue
Block a user