272 lines
10 KiB
Go
272 lines
10 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/Azure/azure-sdk-for-go/sdk/ai/azopenai"
|
|
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
|
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
|
"github.com/labstack/echo/v4"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// 处理翻译请求
|
|
func TranslateHandler(c echo.Context) error {
|
|
// 验证参数
|
|
var request struct {
|
|
Input string `json:"input" form:"input"`
|
|
Lang string `json:"lang" form:"lang"`
|
|
}
|
|
if err := c.Bind(&request); err != nil {
|
|
logger.Error("bind request error.", zap.Error(err))
|
|
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
|
}
|
|
if request.Lang == "" {
|
|
request.Lang = "Chinese" // 默认语言为中文
|
|
}
|
|
prompt := fmt.Sprintf(TranslatePromptTemplate, request.Lang)
|
|
GID, _ := c.Get(KEY_GID).(int)
|
|
|
|
// 检查是否有权限
|
|
if can, _ := queryUserBenefits(c); !can {
|
|
logger.Error("user beyond limit.", zap.Int("ID", GID), zap.String("input", request.Input))
|
|
setErrResponse(c, ERR_BENIFIT_FREE_LIMIT, "no benifits left.")
|
|
return nil
|
|
}
|
|
|
|
translation, err, errcode := gTranslate(request.Input, prompt)
|
|
if err != nil {
|
|
logger.Error("query error.", zap.Int("ID", GID), zap.Error(err))
|
|
setErrResponse(c, errcode, "server timeout. please try again.")
|
|
return nil
|
|
}
|
|
logger.Info("translation", zap.Int("ID", GID), zap.String("input", request.Input), zap.String("output", translation))
|
|
|
|
// 返回结果
|
|
setResponse(c, map[string]string{"translation": translation})
|
|
return nil
|
|
}
|
|
|
|
// 处理语法改错请求
|
|
func GrammarHandler(c echo.Context) error {
|
|
var request struct {
|
|
Input string `json:"input" form:"input"`
|
|
Lang string `json:"lang" form:"lang"`
|
|
}
|
|
|
|
if err := c.Bind(&request); err != nil {
|
|
logger.Error("bind request error.", zap.Error(err))
|
|
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
|
}
|
|
|
|
GID, _ := c.Get(KEY_GID).(int)
|
|
// 检查是否有权限
|
|
if can, _ := queryUserBenefits(c); !can {
|
|
logger.Error("user beyond limit.", zap.Int("ID", GID), zap.String("input", request.Input))
|
|
setErrResponse(c, ERR_BENIFIT_FREE_LIMIT, "no benifits left.")
|
|
return nil
|
|
}
|
|
|
|
translation, err, errcode := gTranslate(request.Input, GrammarPromptTemplate)
|
|
if err != nil {
|
|
logger.Error("query error.", zap.Int("ID", GID), zap.Error(err))
|
|
setErrResponse(c, errcode, "server timeout. please try again.")
|
|
return nil
|
|
}
|
|
|
|
// 判断 translation 是否为 "OK"
|
|
if strings.EqualFold(translation, "OK") {
|
|
logger.Info("grammar ok", zap.Int("ID", GID), zap.String("input", request.Input), zap.Any("output", translation))
|
|
setErrResponse(c, ERR_GRAMMAR_OK, "grammar ok")
|
|
return nil
|
|
}
|
|
|
|
// 验证 translation 是否为有效的 JSON 字符串
|
|
var jsonObj interface{}
|
|
if err := json.Unmarshal([]byte(translation), &jsonObj); err != nil {
|
|
// 记录日志
|
|
logger.Error("not json format", zap.Int("ID", GID), zap.String("input", request.Input), zap.String("output", translation))
|
|
return echo.NewHTTPError(http.StatusInternalServerError, "Translation is not a valid JSON string: "+err.Error())
|
|
}
|
|
|
|
// 由于 translation 已经是 JSON 字符串,直接以原样返回
|
|
logger.Info("grammar", zap.Int("ID", GID), zap.String("input", request.Input), zap.Any("output", jsonObj))
|
|
setResponse(c, jsonObj)
|
|
return nil
|
|
//return c.JSONBlob(http.StatusOK, []byte(translation))
|
|
}
|
|
|
|
// 处理单词解释请求
|
|
func WordsHandler(c echo.Context) error {
|
|
var request struct {
|
|
Input string `json:"input" form:"input"`
|
|
Lang string `json:"lang" form:"lang"`
|
|
}
|
|
|
|
if err := c.Bind(&request); err != nil {
|
|
logger.Error("bind request error.", zap.Error(err))
|
|
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
|
}
|
|
|
|
GID, _ := c.Get(KEY_GID).(int)
|
|
// 检查是否有权限
|
|
if can, _ := queryUserBenefits(c); !can {
|
|
logger.Error("user beyond limit.", zap.Int("ID", GID), zap.String("input", request.Input))
|
|
setErrResponse(c, ERR_BENIFIT_FREE_LIMIT, "no benifits left.")
|
|
return nil
|
|
}
|
|
|
|
translation, err, errcode := gTranslate(request.Input, WordsPromptTemplate)
|
|
if err != nil {
|
|
logger.Error("query error.", zap.Int("ID", GID), zap.Error(err))
|
|
setErrResponse(c, errcode, "server timeout. please try again.")
|
|
return nil
|
|
}
|
|
|
|
// 验证 translation 是否为有效的 JSON 字符串
|
|
var jsonObj interface{}
|
|
if err := json.Unmarshal([]byte(translation), &jsonObj); err != nil {
|
|
// 记录日志
|
|
logger.Error("not json format", zap.Int("ID", GID), zap.String("input", request.Input), zap.String("output", translation))
|
|
return echo.NewHTTPError(http.StatusInternalServerError, "Translation is not a valid JSON string: "+err.Error())
|
|
}
|
|
logger.Info("words", zap.Int("ID", GID), zap.String("input", request.Input), zap.Any("output", jsonObj))
|
|
|
|
// 由于 translation 已经是 JSON 字符串,直接以原样返回
|
|
setResponse(c, jsonObj)
|
|
return nil
|
|
|
|
//return c.JSONBlob(http.StatusOK, []byte(translation))
|
|
}
|
|
|
|
// 处理用户的反馈
|
|
func TranslateFeedBackHandler(c echo.Context) error {
|
|
var request struct {
|
|
Product string `json:"product" form:"product"`
|
|
Input string `json:"input" form:"input"`
|
|
Output string `json:"output" form:"output"`
|
|
Result string `json:"res" form:"res"`
|
|
}
|
|
|
|
if err := c.Bind(&request); err != nil {
|
|
logger.Error("bind request error.", zap.Error(err))
|
|
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
|
}
|
|
GID, _ := c.Get(KEY_GID).(int)
|
|
|
|
logger.Info("feedback", zap.Int("ID", GID), zap.Any("feedback", request))
|
|
// TODO: 写入到 feedback_log 表中
|
|
|
|
setResponse(c, nil)
|
|
return nil
|
|
|
|
}
|
|
|
|
// gTranslate 调用Azure OpenAI的翻译接口
|
|
func gTranslate(input string, prompt string) (string, error, int) {
|
|
// get azure openai config
|
|
configManager, err := GetConfigManager()
|
|
if err != nil {
|
|
logger.Error("GetConfigManager error.", zap.Error(err))
|
|
return "", errors.New("Get Config error."), ERR_COMM_SVR_WRONG
|
|
}
|
|
azureConfig := configManager.GetAzureConfig()
|
|
|
|
azureOpenAIKey := azureConfig.Keys[0]
|
|
modelDeploymentID := azureConfig.GPT4Model
|
|
azureOpenAIEndpoint := azureConfig.Endpoint
|
|
|
|
// API密钥认证
|
|
cred := azcore.NewKeyCredential(azureOpenAIKey)
|
|
|
|
// 创建客户端
|
|
client, err := azopenai.NewClientWithKeyCredential(azureOpenAIEndpoint, cred, nil)
|
|
if err != nil {
|
|
logger.Error("NewClientWithKeyCredential error", zap.Error(err))
|
|
return "", err, ERR_COMM_SVR_WRONG
|
|
}
|
|
|
|
// This is a conversation in progress.
|
|
// NOTE: all messages, regardless of role, count against token usage for this API.
|
|
messages := []azopenai.ChatRequestMessageClassification{
|
|
// You set the tone and rules of the conversation with a prompt as the system role.
|
|
&azopenai.ChatRequestSystemMessage{Content: to.Ptr(prompt)},
|
|
|
|
// The user asks a question
|
|
//&azopenai.ChatRequestUserMessage{Content: azopenai.NewChatRequestUserMessageContent("Can you help me?")},
|
|
|
|
// The reply would come back from the ChatGPT. You'd add it to the conversation so we can maintain context.
|
|
//&azopenai.ChatRequestAssistantMessage{Content: to.Ptr("the user's text is shown below.")},
|
|
|
|
// The user answers the question based on the latest reply.
|
|
&azopenai.ChatRequestUserMessage{Content: azopenai.NewChatRequestUserMessageContent(input)},
|
|
|
|
// from here you'd keep iterating, sending responses back from ChatGPT
|
|
}
|
|
|
|
gotReply := false
|
|
var resultTextBuilder strings.Builder
|
|
|
|
resp, err := client.GetChatCompletions(context.TODO(), azopenai.ChatCompletionsOptions{
|
|
// This is a conversation in progress.
|
|
// NOTE: all messages count against token usage for this API.
|
|
Messages: messages,
|
|
DeploymentName: &modelDeploymentID,
|
|
}, nil)
|
|
|
|
if err != nil {
|
|
logger.Error("GetChatCompletions error", zap.String("input", input), zap.Error(err))
|
|
return "", err, ERR_COMM_SVR_WRONG
|
|
}
|
|
|
|
for _, choice := range resp.Choices {
|
|
gotReply = true
|
|
|
|
// 被过滤了,需要做个判断
|
|
if choice.ContentFilterResults != nil {
|
|
var filter_err = errors.New("no content")
|
|
|
|
if choice.ContentFilterResults.Error != nil {
|
|
filter_err = choice.ContentFilterResults.Error
|
|
//fmt.Fprintf(os.Stderr, " Error:%v\n", choice.ContentFilterResults.Error)
|
|
}
|
|
|
|
if *choice.ContentFilterResults.Sexual.Filtered || *choice.ContentFilterResults.Violence.Filtered {
|
|
filter_err = errors.New("Sexual or Violence input")
|
|
return "", filter_err, ERR_DIRTY_CONTENT
|
|
}
|
|
logger.Warn("filterd", zap.Any("Hate", *choice.ContentFilterResults.Hate.Severity), zap.Any("Hate-filtered", *choice.ContentFilterResults.Hate.Filtered),
|
|
zap.Any("SelfHarm", *choice.ContentFilterResults.SelfHarm.Severity), zap.Any("SelfHarm-filtered", *choice.ContentFilterResults.Hate.Filtered),
|
|
zap.Any("Sexual", *choice.ContentFilterResults.Sexual.Severity), zap.Any("Sexual-filtered", *choice.ContentFilterResults.Sexual.Filtered),
|
|
zap.Any("Violence", *choice.ContentFilterResults.Violence.Severity), zap.Any("Violence-filtered", *choice.ContentFilterResults.Violence.Filtered),
|
|
zap.Any("Error", filter_err), zap.String("input", input))
|
|
|
|
//fmt.Fprintf(os.Stderr, " Hate: sev: %v, filtered: %v\n", *choice.ContentFilterResults.Hate.Severity, *choice.ContentFilterResults.Hate.Filtered)
|
|
//fmt.Fprintf(os.Stderr, " SelfHarm: sev: %v, filtered: %v\n", *choice.ContentFilterResults.SelfHarm.Severity, *choice.ContentFilterResults.SelfHarm.Filtered)
|
|
//fmt.Fprintf(os.Stderr, " Sexual: sev: %v, filtered: %v\n", *choice.ContentFilterResults.Sexual.Severity, *choice.ContentFilterResults.Sexual.Filtered)
|
|
//fmt.Fprintf(os.Stderr, " Violence: sev: %v, filtered: %v\n", *choice.ContentFilterResults.Violence.Severity, *choice.ContentFilterResults.Violence.Filtered)
|
|
}
|
|
|
|
if choice.Message != nil && choice.Message.Content != nil {
|
|
//fmt.Fprintf(os.Stderr, "Content[%d]: %s\n", *choice.Index, *choice.Message.Content)
|
|
resultTextBuilder.WriteString(*choice.Message.Content)
|
|
}
|
|
|
|
if choice.FinishReason != nil {
|
|
// this choice's conversation is complete.
|
|
logger.Debug("Finish Reason", zap.Any("index", *choice.Index), zap.Any("reason", *choice.FinishReason))
|
|
//fmt.Fprintf(os.Stderr, "Finish reason[%d]: %s\n", *choice.Index, *choice.FinishReason)
|
|
}
|
|
}
|
|
|
|
if !gotReply {
|
|
return "", errors.New("Got chat completions reply"), ERR_COMM_SVR_WRONG
|
|
}
|
|
return resultTextBuilder.String(), nil, 0
|
|
}
|