#3726 V20230215.patch

Merged
ychao_1983 merged 73 commits from V20230215.patch into develop 1 year ago
  1. +66
    -0
      models/modelapp.go
  2. +1
    -0
      models/models.go
  3. +80
    -0
      modules/modelappservice/modelsevice.go
  4. +5
    -0
      modules/modelarts/resty.go
  5. +231
    -0
      modules/modelarts/wenxinresty.go
  6. +22
    -0
      modules/setting/setting.go
  7. +9
    -0
      options/locale/locale_en-US.ini
  8. +9
    -0
      options/locale/locale_zh-CN.ini
  9. BIN
      public/img/model/icon_load.png
  10. BIN
      public/img/model/loading.gif
  11. BIN
      public/img/model/tuomin.png
  12. BIN
      public/img/model/v2_rpn4lo.png
  13. BIN
      public/img/model/v2_rpn4lu.png
  14. BIN
      public/img/model/v2_rpn4lw.png
  15. BIN
      public/img/model/v2_rpn4lx.png
  16. BIN
      public/img/model/wenxin1.jpeg
  17. BIN
      public/img/model/wenxin2.jpeg
  18. BIN
      public/img/model/wenxin3.jpeg
  19. BIN
      public/img/model/wenxin4.jpeg
  20. +16
    -12
      routers/api/v1/repo/file.go
  21. +4
    -1
      routers/home.go
  22. +4
    -0
      routers/init.go
  23. +62
    -0
      routers/modelapp/wenxin.go
  24. +12
    -2
      routers/routes/routes.go
  25. +3
    -3
      templates/base/head_navbar.tmpl
  26. +4
    -4
      templates/base/head_navbar_fluid.tmpl
  27. +4
    -4
      templates/base/head_navbar_home.tmpl
  28. +4
    -4
      templates/base/head_navbar_pro.tmpl
  29. +39
    -0
      templates/model/index.tmpl
  30. +7
    -0
      templates/model/wenxin/index.tmpl
  31. +53
    -0
      templates/wenxin_privacy.tmpl
  32. +11
    -10
      web_src/js/components/IdeProject.vue
  33. +3
    -12
      web_src/js/components/monacoeditor.vue
  34. +352
    -0
      web_src/less/_model.less
  35. +1
    -0
      web_src/less/index.less
  36. +22
    -0
      web_src/vuepages/apis/modules/desensitization.js
  37. +169
    -0
      web_src/vuepages/pages/model/wenxin/constant.js
  38. +273
    -0
      web_src/vuepages/pages/model/wenxin/index.vue
  39. +10
    -0
      web_src/vuepages/pages/model/wenxin/vp-model-wenxin.js

+ 66
- 0
models/modelapp.go View File

@@ -0,0 +1,66 @@
package models

import (
"fmt"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
)

type ModelApp struct {
ID string `xorm:"pk"`
Desc string `xorm:"varchar(1000)"`
Count int
UserId int64 `xorm:"INDEX"`
ExternalID string
Picture string `xorm:"text NULL"` //picture base64
Status int
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}

func SaveModelApp(modelApp *ModelApp) error {
statictisSess := xStatistic.NewSession()
defer statictisSess.Close()
re, err := statictisSess.Insert(modelApp)
if err != nil {
log.Info("insert modelApp error." + err.Error())
return err
}
log.Info("success to save modelApp db.re=" + fmt.Sprint((re)))
return nil
}

func UpdateModelApp(modelApp *ModelApp) error {
statictisSess := xStatistic.ID(modelApp.ID)
defer statictisSess.Close()
re, err := statictisSess.Cols("picture", "status").Update(modelApp)
if err != nil {
return err
}
log.Info("update modelApp db.re=" + fmt.Sprint((re)))
return nil
}

func QueryModelAppById(id string) *ModelApp {
statictisSess := xStatistic.NewSession()
defer statictisSess.Close()
re := new(ModelApp)
isExist, err := statictisSess.Table(new(ModelApp)).ID(id).Get(re)
if err == nil && isExist {
return re
}
return nil
}

func QueryModelAppCount(userId int64) int64 {
statictisSess := xStatistic.NewSession()
defer statictisSess.Close()
sumList, err := statictisSess.QueryInterface("select sum(count) as count from public.model_app where user_id=" + fmt.Sprint(userId))
if err == nil {
if len(sumList) == 1 {
return convertInterfaceToInt64(sumList[0]["count"])
}
}
return 0
}

+ 1
- 0
models/models.go View File

@@ -190,6 +190,7 @@ func init() {
new(Invitation),
new(CloudbrainDurationStatistic),
new(UserSummaryCurrentYear),
new(ModelApp),
)

gonicNames := []string{"SSL", "UID"}


+ 80
- 0
modules/modelappservice/modelsevice.go View File

@@ -0,0 +1,80 @@
package modelappservice

import (
"bytes"
"fmt"
"runtime"
"strconv"
"strings"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/modelarts"
"code.gitea.io/gitea/modules/setting"
)

var wenxinChannel = make(chan *models.ModelApp, 10000)

func Init() {
urls := setting.BaiduWenXin.ModelArtsWenXinURL
urlarray := strings.Split(urls, ",")
urlNums := len(urlarray)
log.Info("url nums=" + fmt.Sprint(urlNums))
for i := 0; i < setting.BaiduWenXin.RUN_WORKERS; i++ {
go consumerOrder(wenxinChannel, urlarray[i%urlNums])
}
}

func ProducerOrder(modelApp *models.ModelApp) {
wenxinChannel <- modelApp
}

func GetWaitTime() int {
dvid := setting.BaiduWenXin.MODEL_SERVERS
return ((len(wenxinChannel) / dvid) + 1) * 15
}

func consumerOrder(in <-chan *models.ModelApp, url string) {
goroutine_id := GetGid()
log.Info("goroutine id=" + fmt.Sprint(goroutine_id) + "consumer order start...")
for modelApp := range in {
if !modelarts.SendTextReview(modelApp.Desc) {
modelApp.Status = -1
modelApp.Picture = "文本内容可能包括敏感关键字,请重新输入。"
models.UpdateModelApp(modelApp)
continue
}
log.Info("goroutine id=" + fmt.Sprint(goroutine_id) + " wenxin text=" + modelApp.Desc)
result, err := modelarts.CreateWenXinJob(modelApp, url)
if err == nil {
if !modelarts.SendPictureReivew(result.Result) {
modelApp.Status = -1
modelApp.Picture = "生成的图像内容不合法,请重新输入关键字信息。"
models.UpdateModelApp(modelApp)
} else {
modelApp.Status = 0
modelApp.Picture = result.Result
models.UpdateModelApp(modelApp)
}
} else {
log.Info("err=" + err.Error())
modelApp.Status = -1
modelApp.Picture = "哇哦,服务暂时开小差了,请尝试重新生成。"
models.UpdateModelApp(modelApp)
}
}
log.Info("consumer order end...")
}

func GetGid() (gid uint64) {
b := make([]byte, 64)
b = b[:runtime.Stack(b, false)]
b = bytes.TrimPrefix(b, []byte("goroutine "))
b = b[:bytes.IndexByte(b, ' ')]
n, err := strconv.ParseUint(string(b), 10, 64)
if err != nil {
return 0
//panic(err)
}
return n
}

+ 5
- 0
modules/modelarts/resty.go View File

@@ -1487,3 +1487,8 @@ sendjob:

return &result, nil
}

func ProducePicture() {
//https://modelarts-inference.cloudbrain2.pcl.ac.cn/v1/infers/ba901d47-25bf-4385-8d89-e3a311b85d40

}

+ 231
- 0
modules/modelarts/wenxinresty.go View File

@@ -0,0 +1,231 @@
package modelarts

import (
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"sync"
"time"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)

type CreateWenXinParams struct {
Data WenXinText `json:"data"`
Parameters map[string]string `json:"parameters"`
}

type WenXinText struct {
Prompt string `json:"prompt"`
}

type WenXinResult struct {
Result string `json:"result"`
}

func CreateWenXinJob(modelapp *models.ModelApp, url string) (*WenXinResult, error) {
createJobParams := &CreateWenXinParams{
Data: WenXinText{
Prompt: modelapp.Desc,
},
Parameters: make(map[string]string),
}
checkSetting()
client := getRestyClient()
var result WenXinResult

retry := 0

sendjob:
log.Info("token=" + TOKEN + " url=" + url)
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetHeader("X-Auth-Token", TOKEN).
SetAuthToken(TOKEN).
SetBody(createJobParams).
SetResult(&result).
Post(url)

if err != nil {
return nil, fmt.Errorf("resty CreateWenXinJob: %s", err)
}
log.Info("re status=" + res.Status())
if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}
if res.StatusCode() == 200 {
var response WenXinResult
err = json.Unmarshal(res.Body(), &response)
if err != nil {
log.Error("json.Unmarshal failed: %s", err.Error())
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error())
}
return &result, nil
} else {
return nil, fmt.Errorf("Service unavailable")
}
}

var expire_time int64
var baidu_token string

func SendPictureReivew(picture_base64 string) bool {
weburl := setting.BaiduWenXin.PICTURE_BAIDU_REVIEW_URL + GetBaiDuAccessToken()
picture_base64 = url.QueryEscape(picture_base64)
// image 可以通过 GetFileContentAsBase64("C:\fakepath\框图.jpg") 方法获取
payload := strings.NewReader("image=" + picture_base64)
client := &http.Client{}
req, err := http.NewRequest("POST", weburl, payload)

if err != nil {
fmt.Println(err)
return false
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Accept", "application/json")

res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return false
}
defer res.Body.Close()

body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return false
}
bodystr := string(body)
log.Info("img bodystr=" + bodystr)
if strings.Index(bodystr, "\"conclusionType\": 1") > 0 || strings.Index(bodystr, "\"conclusionType\":1") > 0 {
return true
}
if strings.Index(bodystr, "\"error_code\": 18") > 0 || strings.Index(bodystr, "\"error_code\":18") > 0 {
return true
}
return false
}

/**
* 获取文件base64编码
* @param string path 文件路径
* @return string base64编码信息,不带文件头
*/
func GetFileContentAsBase64(path string) string {
srcByte, err := ioutil.ReadFile(path)
if err != nil {
fmt.Println(err)
return ""
}
return base64.StdEncoding.EncodeToString(srcByte)
}

var lock sync.Mutex
var textCache = make(map[string]bool, 2000)

func SendTextReview(text string) bool {
if textCache[text] {
return true
}
bodystr := sendTextReviewToBaidu(text)
log.Info("text bodystr=" + bodystr)
if strings.Index(bodystr, "\"conclusionType\": 1") > 0 || strings.Index(bodystr, "\"conclusionType\":1") > 0 {
setCacheTrue(text, true)
return true
}
if strings.Index(bodystr, "\"error_code\": 18") > 0 || strings.Index(bodystr, "\"error_code\":18") > 0 {
for i := 1; i <= 3; i++ {
log.Info("sleep seconds " + fmt.Sprint(i))
time.Sleep(time.Duration(i) * time.Second)
bodystr := sendTextReviewToBaidu(text)
log.Info("text bodystr=" + bodystr)
if strings.Index(bodystr, "\"conclusionType\": 1") > 0 || strings.Index(bodystr, "\"conclusionType\":1") > 0 {
setCacheTrue(text, true)
return true
}
}
}
setCacheTrue(text, false)
return false
}

func setCacheTrue(text string, isRight bool) {
lock.Lock()
defer lock.Unlock()
if len(textCache) >= 1500 {
for k := range textCache {
delete(textCache, k) //delete only one
break
}
}
textCache[text] = isRight
}

func sendTextReviewToBaidu(text string) string {
url := setting.BaiduWenXin.TEXT_BAIDU_REVIEW_URL + GetBaiDuAccessToken()
payload := strings.NewReader("text=" + text)
client := &http.Client{}
req, err := http.NewRequest("POST", url, payload)

if err != nil {
fmt.Println(err)
return ""
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Accept", "application/json")

res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return ""
}
defer res.Body.Close()

body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return ""
}
bodystr := string(body)
return bodystr
}

/**
* 使用 AK,SK 生成鉴权签名(Access Token)
* @return string 鉴权签名信息(Access Token)
*/
func GetBaiDuAccessToken() string {
if baidu_token != "" {
if time.Now().Unix()-expire_time < 20*24*3600 {
return baidu_token
}
}

url := setting.BaiduWenXin.BAIDU_TOKEN_URL
postData := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s", setting.BaiduWenXin.BAIDU_API_KEY, setting.BaiduWenXin.BAIDU_SECRET_KEY)
resp, err := http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(postData))
if err != nil {
fmt.Println(err)
return ""
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return ""
}
accessTokenObj := map[string]string{}
json.Unmarshal([]byte(body), &accessTokenObj)
baidu_token = accessTokenObj["access_token"]
expire_time = time.Now().Unix()
return accessTokenObj["access_token"]
}

+ 22
- 0
modules/setting/setting.go View File

@@ -709,6 +709,18 @@ var (
CloudbrainUniquenessLockTime time.Duration
SyncCloudbrainStatusDuration string

//wenxin url
BaiduWenXin = struct {
ModelArtsWenXinURL string
BAIDU_API_KEY string
BAIDU_SECRET_KEY string
BAIDU_TOKEN_URL string
PICTURE_BAIDU_REVIEW_URL string
TEXT_BAIDU_REVIEW_URL string
RUN_WORKERS int
MODEL_SERVERS int
}{}

//nginx proxy
PROXYURL string
RadarMap = struct {
@@ -1694,6 +1706,16 @@ func NewContext() {
sec = Cfg.Section("kanban")
IsCloudbrainTimingEnabled = sec.Key("ENABLED").MustBool(false)

sec = Cfg.Section("wenxin")
BaiduWenXin.ModelArtsWenXinURL = sec.Key("ModelArts_WenXinURL").MustString("")
BaiduWenXin.BAIDU_API_KEY = sec.Key("BAIDU_API_KEY").MustString("")
BaiduWenXin.BAIDU_SECRET_KEY = sec.Key("BAUDY_SECRET_KEY").MustString("")
BaiduWenXin.BAIDU_TOKEN_URL = sec.Key("BAIDU_TOKEN_URL").MustString("https://aip.baidubce.com/oauth/2.0/token")
BaiduWenXin.PICTURE_BAIDU_REVIEW_URL = sec.Key("PICTURE_BAIDU_REVIEW_URL").MustString("https://aip.baidubce.com/rest/2.0/solution/v1/img_censor/v2/user_defined?access_token=")
BaiduWenXin.TEXT_BAIDU_REVIEW_URL = sec.Key("TEXT_BAIDU_REVIEW_URL").MustString("https://aip.baidubce.com/rest/2.0/solution/v1/text_censor/v2/user_defined?access_token=")
BaiduWenXin.RUN_WORKERS = sec.Key("RUN_WORKERS").MustInt(1)
BaiduWenXin.MODEL_SERVERS = sec.Key("MODEL_SERVERS").MustInt(1)

getGrampusConfig()
getModelartsCDConfig()
getModelConvertConfig()


+ 9
- 0
options/locale/locale_en-US.ini View File

@@ -3359,6 +3359,15 @@ targetted = targetted
new_model_security_evaluation_tips = Model Security Evaluation just used for image classification

[model_app]
get_file_fail = Can not get the image content, please try again later.
content_type_unsupported = Allowed image type is jpg, jpeg or png.
process_image_fail = Fail to process image, please try again later.
model_case = Model Case
wenxin_paint_model = ERNIE-ViLGAI painting large model
wenxin_paint_model_desc = The world largest Chinese cross-modal generation model can realize image generation and editing through natural language
desensitize_model = Data desensitization model experience
desensitize_model_desc = Use AI technology to desensitize the face and license plate number in the picture
more_model_case = More experiences are continuously updated...
get_file_fail= Can not get the image content, please try again later.
content_type_unsupported=Allowed image type is jpg, jpeg or png.
process_image_fail=Fail to process image, please try again later.


+ 9
- 0
options/locale/locale_zh-CN.ini View File

@@ -3380,6 +3380,15 @@ targetted = 定向
new_model_security_evaluation_tips = 模型安全评测只适用于图像分类

[model_app]
get_file_fail = 获取上传文件失败,请稍后再试。
content_type_unsupported = 请上传jpg、jpeg或png图片。
process_image_fail = 图片处理失败,请稍后再试。
model_case = 模型体验
wenxin_paint_model = ERNIE-ViLGAI 作画大模型
wenxin_paint_model_desc = 全球规模最大的中文跨模态生成模型,可通过自然语言实现图像生成与编辑
desensitize_model = 数据脱敏模型体验
desensitize_model_desc = 利用人工智能AI技术,把图片中的人脸、车牌号码进行脱敏处理
more_model_case = 更多体验持续更新中…
get_file_fail= 获取上传文件失败,请稍后再试。
content_type_unsupported=请上传jpg、jpeg或png图片。
process_image_fail=图片处理失败,请稍后再试。


BIN
public/img/model/icon_load.png View File

Before After
Width: 36  |  Height: 36  |  Size: 917 B

BIN
public/img/model/loading.gif View File

Before After
Width: 610  |  Height: 610  |  Size: 223 KiB

BIN
public/img/model/tuomin.png View File

Before After
Width: 400  |  Height: 522  |  Size: 105 KiB

BIN
public/img/model/v2_rpn4lo.png View File

Before After
Width: 600  |  Height: 600  |  Size: 155 KiB

BIN
public/img/model/v2_rpn4lu.png View File

Before After
Width: 600  |  Height: 600  |  Size: 186 KiB

BIN
public/img/model/v2_rpn4lw.png View File

Before After
Width: 600  |  Height: 600  |  Size: 121 KiB

BIN
public/img/model/v2_rpn4lx.png View File

Before After
Width: 600  |  Height: 600  |  Size: 118 KiB

BIN
public/img/model/wenxin1.jpeg View File

Before After
Width: 1024  |  Height: 1024  |  Size: 140 KiB

BIN
public/img/model/wenxin2.jpeg View File

Before After
Width: 1024  |  Height: 1024  |  Size: 192 KiB

BIN
public/img/model/wenxin3.jpeg View File

Before After
Width: 1024  |  Height: 1024  |  Size: 108 KiB

BIN
public/img/model/wenxin4.jpeg View File

Before After
Width: 1024  |  Height: 1024  |  Size: 156 KiB

+ 16
- 12
routers/api/v1/repo/file.go View File

@@ -446,10 +446,10 @@ func CommitFiles(ctx *context.APIContext, commitMultiFileOptions api.CommitMulti
apiOpts := commitMultiFileOptions.Files

// 分支不存在,checkout
decodeCurrent,_ :=url.QueryUnescape(commitMultiFileOptions.CurrentBranch)
decodeCurrent, _ := url.QueryUnescape(commitMultiFileOptions.CurrentBranch)

currentBranch := decodeCurrent
decodeCommit,_ :=url.QueryUnescape(commitMultiFileOptions.CommitBranch)
decodeCommit, _ := url.QueryUnescape(commitMultiFileOptions.CommitBranch)
commitBranch := decodeCommit

// 当前分支不存在或被删除了
@@ -463,7 +463,7 @@ func CommitFiles(ctx *context.APIContext, commitMultiFileOptions api.CommitMulti
ctx.Repo.GitRepo.CreateBranch(commitBranch, currentBranch)
}

commitFiles := [] *repofiles.UpdateRepoFileOptions{}
commitFiles := []*repofiles.UpdateRepoFileOptions{}

for _, apiOpt := range apiOpts {
switch apiOpt.Operation {
@@ -473,7 +473,7 @@ func CommitFiles(ctx *context.APIContext, commitMultiFileOptions api.CommitMulti
}

opts := &repofiles.UpdateRepoFileOptions{
Type: "add",
Type: "add",
Content: apiOpt.Content,
IsNewFile: true,
Message: apiOpt.Message,
@@ -504,7 +504,7 @@ func CommitFiles(ctx *context.APIContext, commitMultiFileOptions api.CommitMulti
opts.Message = commitMultiFileOptions.Msg
}

commitFiles = append(commitFiles,opts)
commitFiles = append(commitFiles, opts)

case "update":
if apiOpt.BranchName == "" {
@@ -512,7 +512,7 @@ func CommitFiles(ctx *context.APIContext, commitMultiFileOptions api.CommitMulti
}

opts := &repofiles.UpdateRepoFileOptions{
Type: "update",
Type: "update",
Content: apiOpt.Content,
SHA: apiOpt.SHA,
IsNewFile: false,
@@ -545,7 +545,7 @@ func CommitFiles(ctx *context.APIContext, commitMultiFileOptions api.CommitMulti
opts.Message = commitMultiFileOptions.Msg
}

commitFiles = append(commitFiles,opts)
commitFiles = append(commitFiles, opts)
case "delete":
if !canWriteFiles(ctx.Repo) {
ctx.Error(http.StatusForbidden, "DeleteFile", models.ErrUserDoesNotHaveAccessToRepo{
@@ -560,7 +560,7 @@ func CommitFiles(ctx *context.APIContext, commitMultiFileOptions api.CommitMulti
}

opts := &repofiles.UpdateRepoFileOptions{
Type: "delete",
Type: "delete",
Message: apiOpt.Message,
OldBranch: apiOpt.BranchName,
NewBranch: apiOpt.NewBranchName,
@@ -590,13 +590,17 @@ func CommitFiles(ctx *context.APIContext, commitMultiFileOptions api.CommitMulti
opts.Message = commitMultiFileOptions.Msg
}

commitFiles = append(commitFiles,opts)
commitFiles = append(commitFiles, opts)
}
}
err := repofiles.OneTimeSubmission(ctx.Repo.Repository, ctx.User, commitFiles)
if err != nil {
ctx.Error(http.StatusInternalServerError, "CommitFiles", err)
}else{
if models.IsErrUserCannotCommit(err) {
ctx.Error(http.StatusBadRequest, "Error", "无权限提交到当前分支")
} else {
ctx.Error(http.StatusInternalServerError, "CommitFiles", err)
}
} else {
ctx.JSON(http.StatusOK, struct{ msg string }{"success"})
}
}
@@ -646,7 +650,7 @@ func GetContents(ctx *context.APIContext) {
treePath := ctx.Params("*")
ref := ctx.QueryTrim("ref")

decode,_ := url.QueryUnescape(ref)
decode, _ := url.QueryUnescape(ref)
if fileList, err := repofiles.GetContentsOrList(ctx.Repo.Repository, treePath, decode); err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound("项目路径不存在,请重新打开web IDE", err)


+ 4
- 1
routers/home.go View File

@@ -45,6 +45,7 @@ const (
tplIde base.TplName = "explore/ide"
tplHomeTerm base.TplName = "terms"
tplHomeAnnual base.TplName = "annual_privacy"
tplHomeWenxin base.TplName = "wenxin_privacy"
tplHomePrivacy base.TplName = "privacy"
tplResoruceDesc base.TplName = "resource_desc"
tplRepoSquare base.TplName = "explore/repos/square"
@@ -983,7 +984,9 @@ func HomeTerm(ctx *context.Context) {
func HomeAnnual(ctx *context.Context) {
ctx.HTML(200, tplHomeAnnual)
}

func HomeWenxin(ctx *context.Context) {
ctx.HTML(200, tplHomeWenxin)
}
func HomePrivacy(ctx *context.Context) {
ctx.HTML(200, tplHomePrivacy)
}


+ 4
- 0
routers/init.go View File

@@ -19,6 +19,8 @@ import (
"code.gitea.io/gitea/modules/cron"
"code.gitea.io/gitea/modules/decompression"
"code.gitea.io/gitea/modules/eventsource"
"code.gitea.io/gitea/modules/modelappservice"

"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/highlight"
code_indexer "code.gitea.io/gitea/modules/indexer/code"
@@ -74,6 +76,8 @@ func NewServices() {
log.Info("decompression.NewContext() succeed.")
labelmsg.Init()
log.Info("labelmsg.Init() succeed.")
modelappservice.Init()
log.Info("modelappservice.Init() succeed.")
InitESClient()
log.Info("ES Client succeed.")
}


+ 62
- 0
routers/modelapp/wenxin.go View File

@@ -0,0 +1,62 @@
package modelapp

import (
"fmt"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/modelappservice"
uuid "github.com/satori/go.uuid"
)

var modelMainTpl base.TplName = "model/index"
var modelWenXinTpl base.TplName = "model/wenxin/index"

const WAIT_TIME int = 7

func ModelMainPage(ctx *context.Context) {
ctx.HTML(200, modelMainTpl)
}

func WenXinPage(ctx *context.Context) {
ctx.HTML(200, modelWenXinTpl)
}

func WenXinPaintNew(ctx *context.Context) {
textDesc := ctx.Query("textDesc")
uuid := uuid.NewV4()
id := uuid.String()

count := models.QueryModelAppCount(ctx.User.ID)
if count >= 200 {
ctx.JSON(200, map[string]string{
"result": "-1",
"msg": "Max free times exceed 200.",
})
return
}
modelApp := &models.ModelApp{
ID: id,
Desc: textDesc,
Count: 1,
UserId: ctx.User.ID,
}
models.SaveModelApp(modelApp)
modelappservice.ProducerOrder(modelApp)
ctx.JSON(200, map[string]string{
"result": id,
"wait": fmt.Sprint(modelappservice.GetWaitTime()),
})
}

func QueryWenXinPaintResult(ctx *context.Context) {
//ctx.HTML(200, modelMainTpl)
count := models.QueryModelAppCount(ctx.User.ID)
ctx.JSON(200, count)
}

func QueryWenXinPaintById(ctx *context.Context) {
result := models.QueryModelAppById(ctx.Query("id"))
ctx.JSON(200, result)
}

+ 12
- 2
routers/routes/routes.go View File

@@ -362,10 +362,20 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/user/login/kanban", user.SignInPostAPI)
m.Get("/home/term", routers.HomeTerm)
m.Get("/home/annual_privacy", routers.HomeAnnual)
m.Get("/home/wenxin_privacy", routers.HomeWenxin)
m.Get("/home/notice", routers.HomeNoticeTmpl)
m.Get("/home/privacy", routers.HomePrivacy)
m.Get("/extension/tuomin/upload", modelapp.ProcessImageUI)
m.Post("/extension/tuomin/upload", reqSignIn, modelapp.ProcessImage)

m.Group("/extension", func() {
m.Get("", modelapp.ModelMainPage)
m.Get("/tuomin/upload", modelapp.ProcessImageUI)
m.Post("/tuomin/upload", reqSignIn, modelapp.ProcessImage)
m.Get("/wenxin", modelapp.WenXinPage)
m.Get("/wenxin/paint_new", reqSignIn, modelapp.WenXinPaintNew)
m.Get("/wenxin/query_paint_result", reqSignIn, modelapp.QueryWenXinPaintResult)
m.Get("/wenxin/query_paint_image", reqSignIn, modelapp.QueryWenXinPaintById)
})

m.Group("/explore", func() {
m.Get("", func(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + "/explore/repos")


+ 3
- 3
templates/base/head_navbar.tmpl View File

@@ -42,7 +42,7 @@
{{.i18n.Tr "repo.model_manager"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/extension/tuomin/upload">{{.i18n.Tr "repo.model_experience"}}</a>
<a class="item" href="{{AppSubUrl}}/extension/wenxin">{{.i18n.Tr "repo.model_experience"}}</a>
</div>
</div>
<div class="ui simple dropdown item" id='dropdown_explore'>
@@ -84,9 +84,9 @@
{{.i18n.Tr "repo.model_manager"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/extension/tuomin/upload">{{.i18n.Tr "repo.model_experience"}}</a>
<a class="item" href="{{AppSubUrl}}/extension/wenxin">{{.i18n.Tr "repo.model_experience"}}</a>
</div>
</div>
</div>
<div class="ui simple dropdown item" id='dropdown_PageHome'>
{{.i18n.Tr "explore"}}


+ 4
- 4
templates/base/head_navbar_fluid.tmpl View File

@@ -39,9 +39,9 @@
{{.i18n.Tr "repo.model_manager"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/extension/tuomin/upload">{{.i18n.Tr "repo.model_experience"}}</a>
<a class="item" href="{{AppSubUrl}}/extension/wenxin">{{.i18n.Tr "repo.model_experience"}}</a>
</div>
</div>
</div>
<div class="ui dropdown item" id='dropdown_explore'>
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
@@ -80,9 +80,9 @@
{{.i18n.Tr "repo.model_manager"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/extension/tuomin/upload">{{.i18n.Tr "repo.model_experience"}}</a>
<a class="item" href="{{AppSubUrl}}/extension/wenxin">{{.i18n.Tr "repo.model_experience"}}</a>
</div>
</div>
</div>
<div class="ui dropdown item" id='dropdown_PageHome'>
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>


+ 4
- 4
templates/base/head_navbar_home.tmpl View File

@@ -31,9 +31,9 @@
{{.i18n.Tr "repo.model_manager"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/extension/tuomin/upload">{{.i18n.Tr "repo.model_experience"}}</a>
<a class="item" href="{{AppSubUrl}}/extension/wenxin">{{.i18n.Tr "repo.model_experience"}}</a>
</div>
</div>
</div>
<div class="ui dropdown item" id='dropdown_explore'>
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
@@ -73,9 +73,9 @@
{{.i18n.Tr "repo.model_manager"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/extension/tuomin/upload">{{.i18n.Tr "repo.model_experience"}}</a>
<a class="item" href="{{AppSubUrl}}/extension/wenxin">{{.i18n.Tr "repo.model_experience"}}</a>
</div>
</div>
</div>
<div class="ui dropdown item" id='dropdown_PageHome'>
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>


+ 4
- 4
templates/base/head_navbar_pro.tmpl View File

@@ -41,9 +41,9 @@
{{.i18n.Tr "repo.model_manager"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/extension/tuomin/upload">{{.i18n.Tr "repo.model_experience"}}</a>
<a class="item" href="{{AppSubUrl}}/extension/wenxin">{{.i18n.Tr "repo.model_experience"}}</a>
</div>
</div>
</div>
<div class="ui dropdown item" id='dropdown_explore'>
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
@@ -83,9 +83,9 @@
{{.i18n.Tr "repo.model_manager"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/extension/tuomin/upload">{{.i18n.Tr "repo.model_experience"}}</a>
<a class="item" href="{{AppSubUrl}}/extension/wenxin">{{.i18n.Tr "repo.model_experience"}}</a>
</div>
</div>
</div>
<div class="ui dropdown item" id='dropdown_PageHome'>
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>


+ 39
- 0
templates/model/index.tmpl View File

@@ -0,0 +1,39 @@
{{template "base/head" .}}
<div class="extension model" >
<div class="ui container model_app_wrap">
<h1 class="title">{{.i18n.Tr "model_app.model_case"}}</h1>
<div class="ui stackable two column grid" style="margin: 0;">
<div class="column" style="padding-right: 3rem;">
<a href="/extension/wenxin" class="model_app_instance model_bg_1">
<div class="img_container">
<img src="/img/model/wenxin.jpg">
</div>
</a>
<div class="model_app_text">
<span class="desc_header">{{.i18n.Tr "model_app.wenxin_paint_model"}}</span>
<span class="desc_content">{{.i18n.Tr "model_app.wenxin_paint_model_desc"}}</span>
</div>
</div>
<div class="column" style="padding-left: 3rem;">
<a href="/extension/tuomin/upload" class="model_app_instance model_bg_1">
<div class="img_container">
<img src="/img/model/tuomin.png">
</div>
</a>
<div class="model_app_text">
<span class="desc_header">{{.i18n.Tr "model_app.desensitize_model"}}</span>
<span class="desc_content">{{.i18n.Tr "model_app.desensitize_model_desc"}}</span>
</div>
</div>
</div>
<div>
<div class="model_app_more">
<div class="content">
<span>{{.i18n.Tr "model_app.more_model_case"}}</span>
</div>
</div>
</div>
</div>
</div>
</div>
{{template "base/footer" .}}

+ 7
- 0
templates/model/wenxin/index.tmpl View File

@@ -0,0 +1,7 @@
{{template "base/head" .}}
<div>
<div id="isSignd" data-sign="{{$.IsSigned}}" style="display: none;"></div>
<div id="__vue-root"></div>
</div>
<script src="{{StaticUrlPrefix}}/js/vp-model-wenxin.js?v={{MD5 AppVer}}"></script>
{{template "base/footer" .}}

+ 53
- 0
templates/wenxin_privacy.tmpl View File

@@ -0,0 +1,53 @@
{{template "base/head_home" .}}
<style>
p{display: flex;}
p span:first-child{
font-weight: 600;margin-right:5px;
}
</style>
<div class="ui container">
<h1></h1>
<h1 style="text-align: center;">OpenI启智社区AI协作平台免责声明和服务使用规范</h1>
<p style="font-size: 16px;">亲爱的用户您好!您在使用启智文心作画大模型服务(以下简称“本服务”)前,请您务必仔细阅读并理解透彻本 《免责声明和服务使用规范》(以下简称“本声明”)。请您知悉,如果您选择继续使用本服务,意味着您充分知悉并接受以下使用条件:</p>
<h1>一、免责声明</h1>
<p><span >1.</span><span>请您确保您所输入的内容未侵害他人权益,未涉及不良信息,同时未输入与政治、暴力、色情相关的内容,且所有输入内容均合法合规。</span></p>
<p><span>2.</span><span>您确认并知悉本服务生成的所有内容均由人工智能模型生成,生成内容具有不完全理性,我方对其生成内容的准确性、完整性和功能性不做任何保证,亦不承担任何法律责任。我们会尽可能为您提供准确且优质稳定的技术服务,但由人工智能模型生成的内容均不代表我们的态度、观点或立场。</span></p>
<p><span>3.</span><span>我们的服务在法律法规允许的范围内进行数据训练,内容包括但不限于公开互联网的信息积累,且所有内容均已在不断的进行自动及人工敏感数据库过滤来确保内容理解和生成的准确性,但仍不排除其中有部分信息存在一定的敏感性、非合理性或导致理解歧义的问题存在,如您将生成的敏感性内容或非理性及有理解歧义的内容进行公开传播,由此造成的侵权、纠纷、损失将由您自行承担,我们对此概不负责,亦不承担任何法律责任,如引发法律追责,您将负全部主要责任。</span></p>
<p><span>4.</span><span>本服务会根据您输入的内容进行生成,如生成内容引起您的质疑或引发不适,欢迎您随时反馈至邮箱secretariat@openi.org.cn,我们将对您的意见进行高度重视并采取及时有效的处理措施,同时对您表示十分感谢,但您对有质疑或引发不适的生成内容进行恶意传播并产生负面影响时,我方无须承担任何法律责任。</span></p>
<p><span>5.</span><span>您充分知晓并保证您在本服务时输入的内容,以及生成的内容均享有合法的使用权,若在发布或使用时发生权利纠纷或侵犯了任何第三方的合法权益,由您承担全部的法律责任。</span></p>
<p><span>6.</span><span>您确保在阅读本服务协议时已详尽知晓并理解本服务协议的全部内容,在使用时须严格按照“服务使用规范”的相关说明进行服务调用及输入,若您在使用过程中违反相关规范并被检测到,将会立即暂停您的服务使用权限,由此带来的相关后果将由您自行承担,我们无须承担任何法律责任及赔偿责任。</span></p>
<p><span>7.</span><span>请您确认使用本服务时输入的内容将不包含您本人及他人的个人信息,且您的输入内容将不被认为是您的个人隐私资料。</span></p>
<p><span>8.</span><span>由于系统发生故障导致影响本服务的正常运行,我们承诺及时处理并进行修复,但用户因此而产生的纠纷和损失,我们概不负责,亦不承担任何法律责任。</span></p>
<p><span>9.</span><span>我们有权因业务发展或法律法规的变动进而对本平台所提供的内容或使用方式进行变动,也可暂停或终止本平台的相关服务。在暂停或终止本平台的相关服务前,将会在平台发布公告,因您未及时关注公告详情而导致的一切后果,我们不承担任何法律责任。</span></p>
<p><span>10.</span><span>在任何情况下,我们均不会因本平台的相关服务所发生的任何直接性、间接性、后果性、惩戒性、偶然性、特殊性的损害(包括但不限于:您使用本服务而遭受的利润损失),承担任何责任(即使您已事先被告知该损害发生的可能性)。</span></p>
<p><span>11.</span><span>若您违反本协议中的任一条款,则我们有权随时单方面终止本服务协议,且我们无需承担任何责任。同时我们有权根据实际遭受的损失向您请求赔偿。</span></p>
<h1>二、服务使用规范</h1>
<p ><span>1.</span><span>请您确保您所输入的内容未侵害他人权益,未涉及不良信息,同时未输入与政治、暴力、色情相关的内容,且所有输入内容均合法合规。</span></p>
<p>
(1)反对宪法所确定的基本原则的;<br>
(2)危害国家安全,泄露国家秘密,颠覆国家政权,破坏国家统一的;<br>
(3)损害国家荣誉和利益的;<br>
(4)煽动民族仇恨、民族歧视、破坏民族团结的;<br>
(5)破坏国家宗教政策,宣扬邪教和封建迷信的;<br>
(6)散布谣言,扰乱社会秩序,破坏社会稳定的;<br>
(7)散布淫秽、色情、赌博、暴力、凶杀、恐怖或者教唆犯罪的;<br>
(8)侮辱或者诽谤他人,侵害他人合法权利的;<br>
(9)含有虚假、诈骗、有害、胁迫、侵害他人隐私、骚扰、侵害、中伤、粗俗、猥亵、或其它道德上令人反感的内容;<br>
(10)含有中国或其它您所在国国家管辖范围内,所适用的法律、法规、规章、条例以及任何具有法律效力的规范所限制或禁止的其它内容的。
</p>
<p ><span>2.</span><span>您保证,不得并禁止直接或间接的</span></p>
<p>
(1)删除、隐匿、改变本服务平台显示或其中包含的任何专利、版权、商标或其他所有权声明;<br>
(2)以任何方式干扰或企图干扰本服务平台任何部分或功能的正常运行;<br>
(3)避开、尝试避开或声称能够避开任何内容保护机制或者本服务平台的数据度量工具;
</p>
<p ><span>3.</span><span>用户违反上述任何一款的保证,我方均有权就其情节,对其做出警告、屏蔽、直至取消资格的处罚;如因用户违反上述保证而给本服务平台及平台所服务的用户或任何合作伙伴造成损失,用户自行负责承担一切法律责任并赔偿损失。</span></p>
<p ><span>4.</span><span>如您在使用本服务时,输入内容不符合规范且生成了不符合规范的图像或文本,我们将在后台对您的图像或文本进行筛查并删除,如您将该图像或文本用于不良传播,您将承担相应的法律责任,而平台无须承担任何责任。</span></p>
<p ><span>5.</span><span>您使用本服务时不得侵害他人权益(包括但不限于著作权、专利权、商标权、肖像权等知识产权与其他权益)。您同意并承诺,您使用本服务所提供、使用的文本内容已获得了充分、必要且有效的合法许可及授权,同时您同意并承诺在使用本服务时,不会披露任何保密、敏感或个人隐私等信息。</span></p>
<p ><span>6.</span><span>您同意并承诺,本服务以及因使用本服务所取得的任何产出或成果,仅限您本人用于学术测试目的,未经我方事前书面同意,您不得将本服务产出或成果用于任何商业或其他目的与用途,且不得自行或透过他人以任何方式或载体向第三方披露、提供、转发、传播或公开。</span></p>
<p ><span>7.</span><span>您在本服务平台上的全部行为应符合法律法规底线、社会主义制度底线、国家利益底线、公民合法权益底线、社会公共秩序底线、道德风尚底线、信息真实性底线。 您必须为自己注册帐号下的一切行为负责,包括您所发表的任何内容以及由此产生的任何后果。您应对本服务中的内容自行加以判断,并承担因使用内容而引起的所有风险,包括因对内容的合法性、正确性、完整性或实用性的依赖而产生的风险。我方无法且不会对因前述风险而导致的任何损失或损害承担责任。</span></p>
<p ><span>8.</span><span>如果本服务平台发现或收到他人举报或投诉用户违反本服务协议约定的,本服务平台有权不经通知随时对相关内容进行删除,并视行为情节对违规帐号处以包括但不限于警告、限制或禁止使用部分或全部功能、帐号封禁直至注销的处罚,并公告处理结果。</span></p>
<p ><span>9.</span><span>您理解并同意,本服务平台有权依合理判断对违反有关法律法规或本协议规定的行为进行处罚,对违法违规的任何用户采取适当的法律行动,并依据法律法规保存有关信息向有关部门报告等,用户应独自承担由此而产生的一切法律责任。</span></p>
<p ><span>10.</span><span>您理解并同意,因您违反本协议或相关服务条款的规定,导致或产生第三方主张的任何索赔、要求或损失,您应当独立承担责任;本平台因此遭受损失的,您也应当一并赔偿。</span></p>
</div>
{{template "base/footer" .}}

+ 11
- 10
web_src/js/components/IdeProject.vue View File

@@ -69,16 +69,17 @@
<div v-show="!submitAarry.length" class="no-commit">
当前没有可提交的文件
</div>
<div v-show="(!!submitAarry.length && item.type === 'blob')" v-for="item in submitAarry" :key="item.sha"
class="leftPaneBase-wrap-fileName-item">
<span @click="handleDiff(item)">{{ item.name }}</span>
<el-tooltip class="item" effect="dark" content="放弃更改" placement="bottom-end">
<!-- <img :src="abandon" alt="" @click="handleload(item)" />-->
<svg class="icon leftPaneBase-wrap-fileName-item-img leftPaneBase-wrap-change-svg"
@click="handleload(item)" aria-hidden="true">
<use xlink:href="#icon-zhongzhi3"></use>
</svg>
</el-tooltip>
<div v-show="(!!submitAarry.length)" v-for="item in submitAarry" :key="item && item.sha">
<div v-if="!!item && item.type === 'blob'" class="leftPaneBase-wrap-fileName-item">
<span @click="handleDiff(item)">{{ item.name }}</span>
<el-tooltip class="item" effect="dark" content="放弃更改" placement="bottom-end">
<!-- <img :src="abandon" alt="" @click="handleload(item)" />-->
<svg class="icon leftPaneBase-wrap-fileName-item-img leftPaneBase-wrap-change-svg"
@click="handleload(item)" aria-hidden="true">
<use xlink:href="#icon-zhongzhi3"></use>
</svg>
</el-tooltip>
</div>
</div>
</div>
</div>


+ 3
- 12
web_src/js/components/monacoeditor.vue View File

@@ -4,16 +4,7 @@
</template>

<script>
// import * as monaco from "monaco-editor";

import "monaco-editor/min/vs/loader.js";
import "monaco-editor/min/vs/editor/editor.main.nls.js";
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js';
// import "monaco-editor/esm/vs/basic-languages/mysql/mysql.contribution";
// import "monaco-editor/esm/vs/editor/contrib/suggest/suggestController.js";
// import "monaco-editor/esm/vs/editor/contrib/bracketMatching/bracketMatching.js";
// import "monaco-editor/esm/vs/editor/contrib/hover/hover.js";
import { isBase64 } from "../utils.js";
let monaco;
import configSuggest, { tipTxt } from './monaco-hint';
export default {
name: "AcMonaco",
@@ -117,8 +108,8 @@ export default {
this.monacoEditor.layout()
}
},
init() {
async init() {
monaco = await import( 'monaco-editor')
try {
// 初始化container的内容,销毁之前生成的编辑器
this.$refs.container.innerHTML = "";


+ 352
- 0
web_src/less/_model.less View File

@@ -0,0 +1,352 @@
.model_app_wrap{
.title{
text-align: center;margin:3rem 0;
}
.model_app_instance{
display: flex;
justify-content: center;
padding: 2rem 0;
cursor: pointer;
.img_container{
max-width: 290px;
max-height:290px;
img{
width: 100%;
height: 100%;
}
}
}
.model_bg_1{
background-color: rgba(22, 206, 255, 1);
}
.model_bg_2{
background-color: rgba(24, 52, 155, 1);
}
.model_app_text{
margin-top: 1rem;
.desc_header{
display: block;
color: #101010;
font-size: 16px;
font-family: SourceHanSansSC;
line-height: 32px;
font-weight: 600;
}
.desc_content{
color: rgba(136, 136, 136, 1);
font-size: 14px;
}
}
.model_app_more{
display: flex;
justify-content: center;
margin-top: 3rem;
.content{
background-color: rgba(208, 231, 255, 0.3);
border-radius: 10px;
display: inline-block;
padding: 1rem 6rem;
}
}
}

.wenxin_model_wrap{
.wenxin_title_wrap{
margin: 3rem 0 ;
text-align: center;
p{
color: rgba(136, 136, 136, 0.87);
}
}
.wenxin_search_wrap{
height: 60px;
border-radius: 30px;
border: 1px solid rgba(47, 9, 69, 0.64);
background: linear-gradient(0deg, rgba(238,234,222,0.2) 75.7%,rgba(236,224,233,1) 99.7%);
width: 80%;
margin: auto;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.wenxin_search_input{
font-size: 18px!important;
color: rgba(0,0,0,.9)!important;
height: 40px!important;
letter-spacing: 0;
line-height: 30px!important;
padding: 0!important;
width: 100%;
background: transparent;
border: none;
}
.wenxin_search_input:focus{
border: none;
box-shadow: none;
outline: none;
}
}
.wenxin_tips_wrap{
width: 80%;
margin: auto;
.wenxin_tip_box{
display:flex;
margin:10px 20px;
.wenxin_replace_tip{
color: #275bff;
cursor: pointer;
}
}
.wenxin_button_box{
display: flex;
justify-content: center;
margin-top:2rem;
margin-bottom: 3rem;
.wenxin_button_content{
background: linear-gradient(180deg, rgba(47,9,69,1) 0%,rgba(52,26,123,1) 100%);
border-radius: 30px;
height: 50px;
color: rgba(255, 255, 255, 1);
font-size: 18px;
display: inline-block;
padding: 1rem 3rem;
cursor: pointer;
display: flex;
align-items: center;
width: 160px;
justify-content: center;
border:none
}
}
}
.wenxin_wait{
align-items: center;
color: #000;
display: flex;
font-family: PingFang-SC-Regular;
font-size: 16px;
font-weight: 400;
justify-content: center;
letter-spacing: 0;
margin-bottom: 2rem;
text-align: center;
color:#ff5e00;
}
.wenxin_wait:before {
background: linear-gradient(90deg,#eee,#9aa0af);
content: "";
display: inline-block;
height: 1px;
margin-right: 20px;
width: 68px;
}
.wenxin_wait:after {
background: linear-gradient(90deg,#9aa0af,#eee);
content: "";
display: inline-block;
height: 1px;
margin-left: 20px;
width: 68px;

}
.wenxin_result_wrap{
display: flex;
justify-content: space-between;
flex: 1;
flex-wrap: wrap;
width: 80%;
margin: auto;
.wenxin_result_left{
display: flex;
flex-direction: column;
justify-content: space-between;
border:1px solid #888888;
border-left: none;
border-right: none;
width: 58%;
.content{
margin-top: 2rem;
.desc_header{
color: rgba(136, 136, 136, 1);
height: 20px;
line-height: 20px;
}
.desc_text{
margin-top: 10px;
color: rgba(16, 16, 16, 1);
font-size: 18px;
line-height: 26px;
word-wrap: break-word;
}
}
.wenxin_use_content{
display: flex;
justify-content: space-between;
margin-bottom: 1rem;
color: rgba(136, 136, 136, 1);
}
}
.wenxin_result_right{
width:40%;
height:0;
position: relative;
padding-bottom: 40%;
border-radius: 10px;
img{
width:100%; height:100%; position: absolute;
}
.wenxin_mask {
background: rgba(0,0,0,.2);
bottom: 0;
height: 100%;
left: 0;
position: absolute;
transition: all .16s;
width: 100%;
border-radius: 1rem;
.wenxin_down_wrap {
align-items: center;
background: rgba(8,8,8,.5);
border-radius: 24px;
display: inline-flex;
padding: 6px 13px;
position: absolute;
right: 10px;
top: 10px;
z-index: 99;
.download_img{
color: #fff;
cursor: pointer;
font-family: PingFang-SC-Regular;
font-size: 12px;
font-weight: 400;
letter-spacing: 0;
text-align: center;
}
.download_img::before{
background: url('/img/model/icon_load.png') 50% no-repeat;
background-size: cover;
content: "";
display: inline-block;
height: 16px;
margin-right: 5px;
vertical-align: middle;
width: 16px;
}
}
}
}
@media only screen and (max-width: 768px) {
.wenxin_result_right {
width:100%; height:0; position: relative; padding-bottom: 100%;
img{
width:100%; height:100%; position: absolute;
}
}
.wenxin_result_left{
width: 100%;
}
}
}
.wenxin_example_wrap{
margin-top: 5rem;
p {
font-size: 18px;
font-weight: 600 !important;
}
.wenxin_img_desc{
display: flex;
align-items: baseline;
.wenxin_img_tips{
color: #3c425a;
display: inline-block;
font-family: PingFangSC-Regular;
font-size: 13px;
font-weight: 400;
letter-spacing: 0;
line-height: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
}
.wenxin_img_copy{
cursor: pointer;
color: #396fff;
font-size: 12px;
margin-left: 10px;
}

}
}
.wenxin_alert_wrap{
display: flex;
justify-content: center;
color: rgba(136, 136, 136, 1);
margin-top: 2rem;
}
.wenxin_img_box{
padding: 0;
border: none;
img{
width: 100%;
height: 100%;
}
}

}


/* ----------------------------------------------
* Generated by Animista on 2023-2-8 17:27:38
* Licensed under FreeBSD License.
* See http://animista.net/license for more info.
* w: http://animista.net, t: @cssanimista
* ---------------------------------------------- */

/**
* ----------------------------------------
* animation scale-in-ver-top
* ----------------------------------------
*/
.scale-in-ver-top {
-webkit-animation: scale-in-ver-top 0.8s linear both;
animation: scale-in-ver-top 0.8s linear both;
}
@-webkit-keyframes scale-in-ver-top {
0% {
-webkit-transform: scaleY(0);
transform: scaleY(0);
-webkit-transform-origin: 100% 0%;
transform-origin: 100% 0%;
opacity: 1;
}
100% {
-webkit-transform: scaleY(1);
transform: scaleY(1);
-webkit-transform-origin: 100% 0%;
transform-origin: 100% 0%;
opacity: 1;
}
}
@keyframes scale-in-ver-top {
0% {
-webkit-transform: scaleY(0);
transform: scaleY(0);
-webkit-transform-origin: 100% 0%;
transform-origin: 100% 0%;
opacity: 1;
}
100% {
-webkit-transform: scaleY(1);
transform: scaleY(1);
-webkit-transform-origin: 100% 0%;
transform-origin: 100% 0%;
opacity: 1;
}
}

+ 1
- 0
web_src/less/index.less View File

@@ -19,3 +19,4 @@
@import "_explore";
@import "_review";
@import "openi";
@import "_model";

+ 22
- 0
web_src/vuepages/apis/modules/desensitization.js View File

@@ -11,3 +11,25 @@ export const postImgDesensitization = (params, data) => {
data: data,
});
};
// 文心接口
export const postERNIEPaintNew = (params) => {
return service({
url: "/extension/wenxin/paint_new",
method: "get",
params: params
});
};
export const postERNIEPaintResult = (params) => {
return service({
url: "/extension/wenxin/query_paint_image",
method: "get",
params: params
});
};
export const getERNIEPaintCount = () => {
return service({
url: "/extension/wenxin/query_paint_result",
method: "get",
params: {}
});
};

+ 169
- 0
web_src/vuepages/pages/model/wenxin/constant.js View File

@@ -0,0 +1,169 @@
export const ERNIE_CASE = [
'中国山水画,青山绿水,溪水长流,古风,青山相看两不厌,丹青水墨,中国风',
'这是一幅中国画风格的画,画的是山上一个宁静的湖泊,头顶上有一片清澈的蓝天',
'云上的繁华都市中,世界传说,仙境,插画作品,超现实主义',
'一只带着酷酷眼镜的蒸汽朋克猫portait,在艺术界大行其道,蒸汽波艺术',
'心脏,人类的器官,钢铁材质,机械感,精致,细节清晰,未来科技,未来主义',
'天空中有颗巨大的心,光线柔和,心形光圈,写实风格',
'泰迪熊穿着西服在时代广场跳舞,细节清晰,高清,8k',
'钛合金,机甲兔子,赛博朋克,未来感,设计作品,cg感,摄影棚光照,未来主义',
'青绿色草地连成一片,山间起伏,有倾泻的流水,星星点缀天空,蓝天白云,天色泛白,巨幅画面感,遥拍视角,4K,高清',
'抹茶味奶茶,古香书桌,安静的环境,落地窗外天清云淡,写实主义',
'蓝色星体漂浮在破碎的红色城堡之上,概念艺术,吉姆·马霍德风格',
'金凤凰,背景绚烂,高饱和,古风,仙境,高清,4K,古风',
'红色灯笼,长城,秋天,高清,奇幻风格,史诗感,艺术站,超现实主义',
'海滩,落日,棕榈树,莫奈风格',
'古长城,烽火台,山河无恙,国泰民安,超现实主义',
'短发,高清,精致面容,细节清晰,少年,二次元,cg感',
'戴贝雷帽的女孩,银色的长发,白色的斗篷,手拿气球,日本动漫风格',
'穿着时尚潮牌的一只大熊猫,日出,大步向我们走来,赛博朋克,烟雾',
'超级逼真的未来世界,真实照片,虚幻引擎',
]
export const ERNIE_CASE_IMG = [
'桃花海,桃花源,二次元,古风,pixiv,浪漫,唯美,户外场景,厚涂',
'机械姬,头像,赛博朋克,精致妆容,二次元',
'神话般的水晶城堡,艺术站,虚拟现实,高清,手绘,厚涂,闪闪发光',
'水粉,动漫,插画,古风建筑群,莲花池,府邸,明月夜,蓝色配色,国风国潮,体积照明,光影效果,特写镜头,细节丰富,超广角,超宽画幅'
]
/**
* 轮询类封装
* @param config 配置对象
* @constructor
*/
export function Thread(config){
this.params = this.init(config);
}

/**
* 初始化对象参数
* @param config 配置对象
*/
Thread.prototype.init = function (config) {
const params = config ? config : {
start: function () {},
stop: function () {},
number: 0,
time: 300
};
params.start = params.start ? params.start : function (){};
params.stop = params.stop ? params.stop : function (){};
params.number = params.number ? Math.abs(parseInt(params.number)) : 0;
params.time = params.time ? Math.abs(parseInt(params.time)) : 300;
this.time = params.time
return params;
}

/**
* 轮询实现
* @param start
* @param stop
* @param number
* @param time
*/
Thread.prototype.run = function () {
this.timer = setTimeout(this.runTime.bind(this), this.time)
}

/**
* 执行过程函数
*/
Thread.prototype.runTime = function () {
try {
this.params.start()
} finally {
if (!this.params.number == 0) {
if (this.total >= this.params.number) {
this.params.stop()
clearTimeout(this.timer)
return
}
if (!this.total) {
this.total = 1
}
this.total++
}
clearTimeout(this.timer)
}
this.timer = setTimeout(this.runTime.bind(this), this.time)
}

/**
* 停止轮询
*/
Thread.prototype.stop = function () {
clearTimeout(this.timer)
this.params.stop()
}
export default {
/**
* desc: base64对象转blob文件对象
* @param urlData :数据的base64对象
* @param type :类型 png,pdf,doc,mp3等;
* @returns {Blob}:Blob文件对象
*/
base64ToBlob (urlData, type) {
let arr = urlData.split(',');
let array = arr[0].match(/:(.*?);/);
let mime = (array && array.length > 1 ? array[1] : type) || type;
// 去掉url的头,并转化为byte
let bytes = window.atob(arr[1]);
// 处理异常,将ascii码小于0的转换为大于0
let ab = new ArrayBuffer(bytes.length);
// 生成视图(直接针对内存):8位无符号整数,长度1个字节
let ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], {
type: mime
});
},
/**
* desc: 下载导出文件
* @param blob :返回数据的blob对象或链接
* @param fileName :下载后文件名标记
* @param fileType :文件类 word(docx) excel(xlsx) ppt等
*/
downloadExportFile (blob, fileName, fileType) {
let downloadElement = document.createElement('a');
let href = blob;
if (typeof blob == 'string') {
downloadElement.target = '_blank';
} else {
href = window.URL.createObjectURL(blob); //创建下载的链接
}
downloadElement.href = href;
downloadElement.download = fileName + '.' + fileType; //下载后文件名
document.body.appendChild(downloadElement);
downloadElement.click(); //触发点击下载
document.body.removeChild(downloadElement); //下载完成移除元素
if (typeof blob != 'string') {
window.URL.revokeObjectURL(href); //释放掉blob对象
}
},
/**
* desc: base64转文件并下载
* @param base64 {String} : base64数据
* @param fileType {String} : 要导出的文件类型png,pdf,doc,mp3等
* @param fileName {String} : 文件名
*/
downloadFile(base64, fileName, fileType) {
let typeHeader = 'data:image/' + fileType + ';base64,' // 定义base64 头部文件类型
let converedBase64 = typeHeader + base64; // 拼接最终的base64
let blob = this.base64ToBlob(converedBase64, fileType) // 转成blob对象
this.downloadExportFile(blob, fileName, fileType) // 下载文件
},

timestampToTime(times) {
let mdy = times[0]
mdy = mdy.split('/')
let month = parseInt(mdy[0])
let day = parseInt(mdy[1])
let year = parseInt(mdy[2])
return year + '-' + month + '-' + day
},
}

+ 273
- 0
web_src/vuepages/pages/model/wenxin/index.vue View File

@@ -0,0 +1,273 @@
<template>
<div class="ui container wenxin_model_wrap">
<div>
<div class="wenxin_title_wrap">
<h1>ERNIE-ViLG AI 作画大模型</h1>
<p>全球规模最大的中文跨模态生成模型,可通过自然语言实现图像生成与编辑。</p>
</div>
</div>
<div class="wenxin_search_wrap">
<div style="width: 94%;">
<input class="wenxin_search_input" ref="inputSearch" @keyup.enter="erniePaint" v-model="searchValue" autocomplete="off" maxlength="100" type="text" placeholder="请用中文输入Prompt,参考形式:画面主体,细节描述,修饰词"></input>
</div>
</div>
<div class="wenxin_tips_wrap">
<div class="wenxin_tip_box">
<span style="flex:1"> <span style="color: #888;">Prompt示例:</span><span style="cursor: pointer;" @click="copyText(tipCase)">{{ tipCase }}</span></span>
<span class="wenxin_replace_tip" @click="changeCase">换示例</span>
</div>
<div class="wenxin_button_box">
<button class="wenxin_button_content" @click="erniePaint" :disabled="loading" :style="{cursor:(loading?'not-allowed':'pointer')}">
<span v-if="!loading">立即生成</span>
<div v-else class="ui active primary inline loader"></div>
</button>
</div>
</div>
<div v-if="showResult">
<div class="wenxin_wait" v-if="resultLoading">
正在生成中,预计需要 {{waitTime}}s
</div>
<div class="wenxin_result_wrap scale-in-ver-top">
<div class="wenxin_result_left">
<div>
<div class="content">
<div class="desc_header">创意灵感:</div>
<div class="desc_text">{{searchValue}}</div>
</div>
<div class="content">
<div class="desc_header">规格:</div>
<div class="desc_text">1024 x 1024</div>
</div>
</div>
<div class="wenxin_use_content">
<span> <span>{{userCount}}/200</span> <span style="color:#ff5e00">限量体验200次</span> </span>
</div>
</div>
<div class="wenxin_result_right">
<img src="/img/model/loading.gif" style="width: 100%;height:100%;" v-if="resultLoading">
<div v-else>
<div class="gallery">
<img :src="'data:image/Jpeg;base64,'+resultImg" alt="" style="border-radius: 1rem;z-index: 9;">
</div>
<span class="wenxin_mask">
<span class="wenxin_down_wrap" @click="downloadImg">
<span class="download_img">
下载
</span>
</span>
</span>
</div>
</div>

</div>
</div>
<div class="wenxin_example_wrap gallery">
<p>ERNIE-ViLG应用场景示例</p>
<div class="ui stackable four column grid">
<div class="column">
<div class="ui segment wenxin_img_box">
<img src="/img/model/wenxin1.jpeg">
</div>
<div class="wenxin_img_desc">
<span class="wenxin_img_tips" :title="caseText[0]">{{caseText[0]}}</span>
<span class="wenxin_img_copy" :data-text="caseText[0]">复制</span>
</div>
</div>
<div class="column">
<div class="ui segment wenxin_img_box">
<img src="/img/model/wenxin2.jpeg">
</div>
<div class="wenxin_img_desc">
<span class="wenxin_img_tips" :title="caseText[1]">{{caseText[1]}}</span>
<span class="wenxin_img_copy" :data-text="caseText[1]">复制</span>
</div>
</div>
<div class="column">
<div class="ui segment wenxin_img_box">
<img src="/img/model/wenxin3.jpeg">
</div>
<div class="wenxin_img_desc">
<span class="wenxin_img_tips" :title="caseText[2]">{{caseText[2]}}</span>
<span class="wenxin_img_copy" :data-text="caseText[2]">复制</span>
</div>
</div>
<div class="column">
<div class="ui segment wenxin_img_box">
<img src="/img/model/wenxin4.jpeg">
</div>
<div class="wenxin_img_desc">
<span class="ui wenxin_img_tips" :title="caseText[3]">{{caseText[3]}}</span>
<span class="wenxin_img_copy" :data-text="caseText[3]">复制</span>
</div>
</div>
</div>
</div>
<div class="wenxin_alert_wrap">
<span> <a href="/home/wenxin_privacy" target="_blank">《免责声明和服务使用规范》</a></span>
<span>访问更多关于 <a href="https://wenxin.baidu.com/ernie-vilg" target="_blank"> ERNIE-ViLG AI</a> 作画大模型的内容</span>
</div>
</div>
</template>
<script>
import Clipboard from 'clipboard';
import base64Img,{ ERNIE_CASE, ERNIE_CASE_IMG,Thread } from './constant.js'
import { postERNIEPaintNew,postERNIEPaintResult,getERNIEPaintCount } from "~/apis/modules/desensitization";
import { Message } from "element-ui";

export default {
data() {
return {
caseIndex: 0,
caseText: ERNIE_CASE_IMG,
searchValue: "",
loading: false,
showResult: false,
resultLoading: false,
resultImg: '',
userCount: 0,
startFlag:false,
resultImgName: '',
waitTime:0
}
},
computed: {
tipCase() {
return ERNIE_CASE[this.caseIndex]
}
},
methods: {
initCopy() {
const clipboard = new Clipboard('.wenxin_img_copy', {
text: (e) => {
console.log(e.getAttribute('data-text'))
return e.getAttribute('data-text');
},
});
clipboard.on('success', (e) => {
Message.success('复制成功')
});
clipboard.on('error', (e) => {
Message.error('复制失败')
});
},
changeCase() {
if (this.caseIndex === ERNIE_CASE.length-1) {
this.caseIndex=0
} else {
this.caseIndex++
}
},
erniePaint() {
if (!this.startFlag) {
window.location.href = `/user/login?redirect_to=${encodeURIComponent(location.href)}`;
return;
}
if (!this.searchValue.split(" ").join("").length) {
Message.error('输入不能为空')
return
}
this.loading = true
this.resultLoading = true
getERNIEPaintCount().then((res) => {
if (res.data >= 200) {
Message.error('已达最大生成图片量')
this.resultLoading = false
this.loading = false
return
}
this.userCount = res.data
this.getPaintResult()
}).catch((err) => {
Message.error(err)
})
},
copyText(value) {
this.searchValue = value
},
getPaintResult() {
let params = { 'textDesc': this.searchValue }
this.showResult = true
postERNIEPaintNew(params).then((res) => {
if (res.data.result == -1) {
Message.error(res.data.msg)
this.showResult = false
this.loading = false
this.resultLoading = false
this.resultLoading = false
return
} else {
let { result, wait } = res.data
this.waitTime = wait
this.main(result,wait)
}
}).catch((err) => {
Message.error(err)
this.showResult = false
})

},
main(id, count) {
// 示例2- 通过轮询次数结束轮询
let vm = this
let countRequest=0
const thread = new Thread({
start: function () {
postERNIEPaintResult({ id: id }).then((res => {
if (res.data.Status === 0) {
if (res.data.Picture) {
vm.resultImg = res.data.Picture
vm.resultImgName = vm.searchValue
vm.resultLoading = false
vm.loading = false
thread.stop()
}
} else {
Message.error(res.data.Picture||'发生错误')
vm.showResult = false
vm.resultLoading = false
vm.loading = false
thread.stop()
}
countRequest++
})).catch((err) => {
Message.error(err)
vm.resultLoading = false
vm.loading = false
})
},
stop: function () {
if (countRequest === Math.ceil(count / 2) - 1) {
Message.error('请求超时,,请稍后再试')
vm.showResult = false
vm.resultLoading = false
vm.loading = false
}
},
number: Math.ceil(count/2), //这里是轮询次数配置,不配置默认无线轮询
time: 2000 //这里是轮询的时间 不配置默认 300ms
})
// 开始轮询
thread.run();
},
downloadImg() {
let time = new Date()
let nowTime = base64Img.timestampToTime(time.toLocaleString('en-US', { hours12: false }).split(' '))
let fileName = nowTime + '_' + this.resultImgName
base64Img.downloadFile(this.resultImg,fileName,'jpeg')
}
},
mounted() {
this.initCopy()
const signEl = document.querySelector("#isSignd");
this.startFlag = !!signEl.getAttribute("data-sign");
},
}
</script>

+ 10
- 0
web_src/vuepages/pages/model/wenxin/vp-model-wenxin.js View File

@@ -0,0 +1,10 @@
import Vue from 'vue';
import { i18n } from '~/langs';
import App from './index.vue';



new Vue({
i18n,
render: (h) => h(App),
}).$mount('#__vue-root');

Loading…
Cancel
Save