enhanced online search function and implementation of the word intelligent segmentation function

This commit is contained in:
Zhang Minghan 2023-08-10 21:33:47 +08:00
parent 121ed8e9a2
commit f7f467772c
9 changed files with 198 additions and 35 deletions

View File

@ -16,39 +16,50 @@ type AnonymousRequestBody struct {
Message string `json:"message" required:"true"`
}
func GetAnonymousResponse(message string) (string, error) {
type AnonymousResponseCache struct {
Keyword string `json:"keyword"`
Message string `json:"message"`
}
func GetChatGPTResponse(message []types.ChatGPTMessage, token int) (string, error) {
res, err := utils.Post(viper.GetString("openai.anonymous_endpoint")+"/chat/completions", map[string]string{
"Content-Type": "application/json",
"Authorization": "Bearer " + viper.GetString("openai.anonymous"),
"Authorization": "Bearer " + GetRandomKey(viper.GetString("openai.anonymous")),
}, types.ChatGPTRequest{
Model: "gpt-3.5-turbo",
Messages: ChatWithWeb([]types.ChatGPTMessage{
{
Role: "user",
Content: message,
},
}, message),
MaxToken: 1000,
Model: "gpt-3.5-turbo",
Messages: message,
MaxToken: token,
})
if err != nil {
if err != nil || res == nil {
return "", err
}
data := res.(map[string]interface{})["choices"].([]interface{})[0].(map[string]interface{})["message"].(map[string]interface{})["content"]
return data.(string), nil
}
func GetAnonymousResponseWithCache(c *gin.Context, message string) (string, error) {
func GetAnonymousResponse(message string) (string, string, error) {
keyword, source := ChatWithWeb([]types.ChatGPTMessage{{Role: "user", Content: message}})
resp, err := GetChatGPTResponse(source, 1000)
return keyword, resp, err
}
func GetAnonymousResponseWithCache(c *gin.Context, message string) (string, string, error) {
cache := c.MustGet("cache").(*redis.Client)
res, err := cache.Get(c, fmt.Sprintf(":chatgpt:%s", message)).Result()
if err != nil || len(res) == 0 {
res, err := GetAnonymousResponse(message)
form := utils.UnmarshalJson[AnonymousResponseCache](res)
if err != nil || len(res) == 0 || res == "{}" || form.Message == "" {
key, res, err := GetAnonymousResponse(message)
if err != nil {
return "There was something wrong...", err
return "", "There was something wrong...", err
}
cache.Set(c, fmt.Sprintf(":chatgpt:%s", message), res, time.Hour*6)
return res, nil
cache.Set(c, fmt.Sprintf(":chatgpt:%s", message), utils.ToJson(AnonymousResponseCache{
Keyword: key,
Message: res,
}), time.Hour*6)
return key, res, nil
}
return res, nil
return form.Keyword, form.Message, nil
}
func AnonymousAPI(c *gin.Context) {
@ -57,6 +68,7 @@ func AnonymousAPI(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": false,
"message": "",
"keyword": "",
"reason": err.Error(),
})
return
@ -66,15 +78,17 @@ func AnonymousAPI(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": false,
"message": "",
"keyword": "",
"reason": "message is empty",
})
return
}
res, err := GetAnonymousResponseWithCache(c, message)
key, res, err := GetAnonymousResponseWithCache(c, message)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"status": false,
"message": res,
"keyword": key,
"reason": err.Error(),
})
return
@ -82,6 +96,7 @@ func AnonymousAPI(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": true,
"message": res,
"keyword": key,
"reason": "",
})
}

View File

@ -63,9 +63,11 @@ func ChatAPI(c *gin.Context) {
if err != nil {
return
}
if msg, err := instance.AddMessageFromUserForm(message); err == nil {
StreamRequest("gpt-3.5-turbo", ChatWithWeb(instance.GetMessageSegment(5), msg), 1500, func(resp string) {
if _, err := instance.AddMessageFromUserForm(message); err == nil {
keyword, segment := ChatWithWeb(instance.GetMessageSegment(12))
StreamRequest("gpt-3.5-turbo-16k", segment, 1500, func(resp string) {
data, _ := json.Marshal(map[string]interface{}{
"keyword": keyword,
"message": resp,
"end": false,
})

View File

@ -30,12 +30,17 @@ func processLine(buf []byte) []string {
item = item + "}"
}
if item == "{data: [DONE]}" {
if item == "{data: [DONE]}" || item == "{data: [DONE]}}" || item == "{[DONE]}" {
break
} else if item == "{data:}" || item == "{data:}}" {
continue
}
var form types.ChatGPTStreamResponse
if err := json.Unmarshal([]byte(item), &form); err != nil {
log.Println(item, err)
if err := json.Unmarshal([]byte(item[:len(item)-1]), &form); err != nil {
log.Println(item, err)
}
}
choices := form.Data.Choices
if len(choices) > 0 {
@ -60,7 +65,7 @@ func StreamRequest(model string, messages []types.ChatGPTMessage, token int, cal
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+viper.GetString("openai.user"))
req.Header.Set("Authorization", "Bearer "+GetRandomKey(viper.GetString("openai.user")))
res, err := client.Do(req)
if err != nil {

View File

@ -5,12 +5,55 @@ import (
"chat/utils"
"chat/web"
"fmt"
"math/rand"
"strings"
"time"
)
func ChatWithWeb(message []types.ChatGPTMessage, current string) []types.ChatGPTMessage {
return utils.Insert(message, 0, types.ChatGPTMessage{
Role: "system",
Content: fmt.Sprintf("你将扮演AI问答助手你的知识库不是截止于2021年9月而是可以实时联网的你可以提供实时联网的知识库。当前时间: %s, 你的知识库:%s", time.Now().Format("2006-01-02 15:04:05"), web.SearchBing(current)),
func ChatWithWeb(message []types.ChatGPTMessage) (string, []types.ChatGPTMessage) {
keyword := SearchWeb(message)
return keyword, utils.Insert(message, 0, types.ChatGPTMessage{
Role: "system",
Content: fmt.Sprintf("你将扮演AI问答助手你的知识库不是截止于2021年9月而是可以实时联网的你可以提供实时联网的知识库。"+
"官网网站使用链接包裹,给予用户精确的答复。"+
"当前时间: %s, 你的知识库:%s",
time.Now().Format("2006-01-02 15:04:05"), web.SearchBing(keyword),
),
})
}
func GetRandomKey(apikey string) string {
arr := strings.Split(apikey, "|")
idx := rand.Intn(len(arr))
return arr[idx]
}
func StringCleaner(content string) string {
for _, replacer := range []string{",", "、", "", "。", "", ":", "", ";", "", "!", "", "?", "", "", "(", ")"} {
content = strings.ReplaceAll(content, replacer, " ")
}
return content
}
func SearchWeb(message []types.ChatGPTMessage) string {
source := make([]string, 0)
for _, item := range message {
if item.Role == "user" && item.Content != "" {
source = append(source, item.Content)
}
}
if len(source) == 0 {
return ""
}
keyword, _ := GetChatGPTResponse([]types.ChatGPTMessage{{
Role: "system",
Content: "在接下来的对话中,不要回答问题,你需要总结接下来的对话中出现的内容," +
"然后用关键字和空格分词,输出精简,请不要输出冗余内容," +
"不能出现逗号顿号等特殊字符,仅回答出现的关键词。",
}, {
Role: "user",
Content: strings.Join(source, " "),
}}, 40)
return StringCleaner(keyword)
}

View File

@ -10,10 +10,12 @@ type Message = {
role: string;
time: string;
stamp: number;
keyword?: string;
gpt4?: boolean;
}
type StreamMessage = {
keyword?: string;
message: string;
end: boolean;
}
@ -102,16 +104,17 @@ export class Conversation {
public async sendAuthenticated(content: string): Promise<void> {
this.state.value = true;
this.addMessageFromUser(content);
let message = ref(""), end = ref(false);
let message = ref(""), end = ref(false), keyword = ref("");
this.connection?.setCallback((res: StreamMessage) => {
message.value += res.message;
res.keyword && (keyword.value = res.keyword);
end.value = res.end;
})
const status = this.connection?.send({
message: content,
});
if (status) {
this.addDynamicMessageFromAI(message, end);
this.addDynamicMessageFromAI(message, keyword, end);
} else {
this.addMessageFromAI("网络错误,请稍后再试");
}
@ -152,9 +155,10 @@ export class Conversation {
}).then(r => 0);
}
public addMessageFromAI(content: string): void {
public addMessageFromAI(content: string, keyword?: string): void {
this.addMessage({
content: "",
keyword: keyword ? keyword : "",
role: "bot",
time: new Date().toLocaleTimeString(),
stamp: new Date().getTime(),
@ -163,15 +167,16 @@ export class Conversation {
this.typingEffect(this.len.value - 1, content);
}
public addDynamicMessageFromAI(content: Ref<string>, end: Ref<boolean>): void {
public addDynamicMessageFromAI(content: Ref<string>, keyword: Ref<string>, end: Ref<boolean>): void {
this.addMessage({
content: "",
role: "bot",
keyword: keyword.value || "",
time: new Date().toLocaleTimeString(),
stamp: new Date().getTime(),
gpt4: gpt4.value,
})
this.dynamicTypingEffect(this.len.value - 1, content, end);
this.dynamicTypingEffect(this.len.value - 1, content, keyword, end);
}
public typingEffect(index: number, content: string): void {
@ -187,9 +192,10 @@ export class Conversation {
}, 20);
}
public dynamicTypingEffect(index: number, content: Ref<string>, end: Ref<boolean>): void {
public dynamicTypingEffect(index: number, content: Ref<string>, keyword: Ref<string>, end: Ref<boolean>): void {
let cursor = 0;
const interval = setInterval(() => {
keyword.value && (this.messages[index].keyword = keyword.value);
if (end.value && cursor >= content.value.length) {
this.messages[index].content = content.value;
this.state.value = false;

View File

@ -39,3 +39,12 @@
transform: rotate(359deg);
}
}
@keyframes FadeInAnimation {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}

View File

@ -0,0 +1,24 @@
<template>
<svg width="256px" height="388px" viewBox="0 0 256 388" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<title>bing</title>
<defs>
<radialGradient cx="93.7173476%" cy="77.8181394%" fx="93.7173476%" fy="77.8181394%" r="143.12136%" gradientTransform="translate(0.937173,0.778181),scale(1.000000,0.719543),rotate(-130.909000),translate(-0.937173,-0.778181)" id="bingRadialGradient-1">
<stop stop-color="#00CACC" offset="0%"></stop>
<stop stop-color="#048FCE" offset="100%"></stop>
</radialGradient>
<radialGradient cx="13.8926614%" cy="71.4480037%" fx="13.8926614%" fy="71.4480037%" r="150.086127%" gradientTransform="translate(0.138927,0.714480),scale(0.600049,1.000000),rotate(-23.195400),translate(-0.138927,-0.714480)" id="bingRadialGradient-2">
<stop stop-color="#00BBEC" offset="0%"></stop>
<stop stop-color="#2756A9" offset="100%"></stop>
</radialGradient>
<linearGradient x1="50.0002111%" y1="-3.51563173e-08%" x2="50.0002111%" y2="99.9999984%" id="bingLinearGradient-3">
<stop stop-color="#00BBEC" offset="0%"></stop>
<stop stop-color="#2756A9" offset="100%"></stop>
</linearGradient>
</defs>
<g>
<path d="M129.424369,122.046918 C122.29107,122.875748 116.850956,128.668861 116.345268,135.974502 C116.127195,139.122391 116.195602,139.335928 123.330791,157.696785 C139.564206,199.470843 143.497083,209.524887 144.158483,210.939907 C145.760962,214.366717 148.01426,217.590572 150.829558,220.484105 C152.989881,222.704521 154.414727,223.898444 156.824493,225.508104 C161.059724,228.33663 163.161466,229.118595 179.641677,233.998219 C195.695191,238.75161 204.46574,241.910837 212.02347,245.661923 C221.814465,250.521516 228.645788,256.049313 232.966812,262.608139 C236.06708,267.314287 238.812837,275.75338 240.007515,284.248408 C240.474653,287.569017 240.477677,294.909429 240.013185,297.911432 C239.00521,304.426794 236.991907,309.886561 233.912048,314.455516 C232.274042,316.885312 232.843981,316.478646 235.225401,313.517461 C241.964883,305.138083 248.830221,290.816683 252.333376,277.8298 C256.572764,262.112277 257.149506,245.234469 253.992924,229.259946 C247.845679,198.151822 228.207374,171.305385 200.548737,156.199752 C198.810955,155.250359 192.191658,151.780841 183.217776,147.115133 C181.856046,146.406867 179.999212,145.437443 179.091392,144.960857 C178.183573,144.483892 176.326738,143.514468 174.965009,142.806581 C173.603279,142.098693 169.683253,140.056288 166.253797,138.268239 C162.82434,136.479812 158.987083,134.478981 157.726265,133.821738 C153.882961,131.817883 151.304255,130.471649 149.381658,129.465187 C140.489411,124.810061 136.725475,122.928282 135.652494,122.601739 C134.52698,122.259322 131.66784,121.819775 130.950504,121.878734 C130.799326,121.891206 130.112604,121.966794 129.424369,122.046918 Z" fill="url(#bingRadialGradient-1)"></path>
<path d="M148.810208,277.993827 C148.31737,278.285599 147.625734,278.70814 147.272735,278.93226 C146.919358,279.156758 146.135504,279.643927 145.530417,280.015445 C143.309245,281.378686 137.401993,285.018657 132.325839,288.152183 C128.989734,290.211596 128.494629,290.518486 124.256752,293.148592 C122.743468,294.087403 121.134186,295.076858 120.680276,295.347465 C120.226366,295.618073 118.28714,296.812373 116.37059,298.001382 C114.45404,299.190013 111.111889,301.253583 108.943251,302.586589 C106.774613,303.919216 102.895782,306.311974 100.323879,307.903493 C97.751598,309.494634 94.3678729,311.581258 92.8047,312.5401 C91.2411491,313.498564 89.7970283,314.424524 89.5952066,314.597622 C89.2954977,314.854624 75.3902128,323.468326 68.413004,327.719053 C63.1138629,330.947066 56.9836248,333.106255 50.7082565,333.95436 C47.7867558,334.348932 42.2582032,334.350444 39.3450173,333.956627 C31.445665,332.890072 24.1685583,329.944005 17.9362755,325.290768 C15.4916258,323.465303 10.8891851,318.866491 9.12970847,316.49074 C4.9833696,310.893024 2.30102759,304.888641 0.91181812,298.094734 C0.59216513,296.531183 0.289753151,295.211028 0.240053625,295.160383 C0.110210494,295.029237 0.344758621,297.391004 0.76784823,300.4788 C1.20781187,303.690183 2.14533768,308.335104 3.15510733,312.307665 C10.9691579,343.051452 33.2044235,368.056927 63.3054801,379.953822 C71.9732286,383.377987 80.7199673,385.536043 90.2369541,386.594284 C93.8130523,386.994903 103.935504,387.153639 107.667693,386.870182 C124.783605,385.573837 139.68666,380.535855 154.975217,370.873738 C156.336946,370.013161 158.895243,368.4001 160.660238,367.288569 C162.42561,366.177416 164.653585,364.764664 165.612049,364.149751 C166.570135,363.534459 167.725507,362.807675 168.179417,362.5348 C168.633327,362.261547 169.541146,361.69123 170.196878,361.2668 C170.852609,360.842748 173.658459,359.067927 176.432184,357.322963 L187.52406,350.317031 L191.332593,347.911423 L191.469787,347.824874 L191.889304,347.559936 L192.088858,347.433703 L194.892818,345.662661 L204.583281,339.541871 C216.930684,331.783076 220.612606,329.05924 226.349027,323.439981 C228.740652,321.097867 232.34623,317.099228 232.525375,316.591651 C232.561657,316.488472 233.202649,315.499773 233.949465,314.394667 C236.985104,309.902812 239.009368,304.400338 240.013185,297.911432 C240.477677,294.909429 240.474653,287.569017 240.007515,284.248408 C239.104609,277.828288 237.053134,270.546079 234.84141,265.909094 C231.21429,258.305634 223.48762,251.396833 212.387807,245.832753 C209.323066,244.296414 206.15817,242.890466 205.804793,242.908804 C205.637364,242.917678 195.308177,249.231218 182.851171,256.939747 C170.394164,264.648276 159.582722,271.339004 158.826458,271.808409 C158.069815,272.278192 156.770069,273.072251 155.937838,273.572648 L148.810208,277.993827 Z" fill="url(#bingRadialGradient-2)"></path>
<path d="M0.0533481895,241.013222 L0.106718678,294.701938 L0.801609893,297.819592 C2.97446184,307.567124 6.73918016,314.594977 13.2839464,321.122433 C16.3624068,324.192844 18.7164636,326.044009 22.0524167,328.018006 C29.1114124,332.195412 36.7087125,334.256336 45.0304163,334.252562 C53.7461636,334.248021 61.2857518,332.074092 69.0551294,327.325236 C70.3662143,326.523997 75.5035957,323.360991 80.4712807,320.296249 L89.5033664,314.724233 L89.5033664,251.024559 L89.5033664,187.324884 L89.5007208,129.051339 C89.4988311,91.8738258 89.4308013,69.7947641 89.313261,68.0626506 C88.5724924,57.1775095 84.020167,47.1707083 76.3653061,39.6016406 C74.0160114,37.2786885 72.0087553,35.7271562 66.0289385,31.6121866 C63.053392,29.5643772 57.6064751,25.8130645 53.9249307,23.2760076 C50.2433864,20.7387618 44.1777765,16.5590127 40.4455878,13.9874496 C36.7134746,11.4160755 31.3904475,7.74730564 28.6166092,5.83487543 C22.8380472,1.85062504 22.3858004,1.57350956 20.6389849,0.948501166 C18.366904,0.13569962 15.9591415,-0.162861118 13.6700153,0.0843541512 C6.99810902,0.80490922 1.65713716,5.62213122 0.268845335,12.1713194 C0.0528069748,13.1902541 0.013004155,26.7505903 0.0102532201,100.349957 L0.00714146286,187.324884 L0,187.324884 L0.0533481895,241.013222 Z" fill="url(#bingLinearGradient-3)"></path>
</g>
</svg>
</template>

View File

@ -7,6 +7,7 @@ import {Conversation} from "../assets/script/conversation";
import {nextTick, onMounted, ref} from "vue";
import {auth, username} from "../assets/script/auth";
import Loading from "../components/icons/loading.vue";
import Bing from "../components/icons/bing.vue";
const conversation = new Conversation(1, refreshScrollbar);
const state = conversation.getState(), length = conversation.getLength(), messages = conversation.getMessages();
@ -57,6 +58,10 @@ onMounted(() => {
<div class="grow" v-if="message.role === 'user'"></div>
<div class="avatar openai" :class="{'gpt4': message.gpt4}" v-else><openai /></div>
<div class="content">
<div v-if="message.role === 'bot' && message.keyword !== ''" class="bing">
<bing />
{{ message.keyword }}
</div>
<div class="loader" v-if="!message.content" />
<span v-if="message.role === 'user'">{{ message.content }}</span>
<md-preview v-model="message.content" theme="dark" v-else />
@ -179,6 +184,25 @@ onMounted(() => {
transform: translateY(10px);
}
.bing {
display: inline-block;
color: #2f7eee;
background: #e8f2ff;
border-radius: 12px;
padding: 6px 12px;
font-size: 16px;
margin: 4px 0;
user-select: none;
animation: FadeInAnimation .5s cubic-bezier(0.18, 0.89, 0.32, 1.28) both;
}
.bing svg {
width: 20px;
height: 20px;
margin-right: 6px;
transform: translate(3px, 3px);
}
.input .button:hover {
cursor: pointer;
}

View File

@ -1,6 +1,9 @@
package utils
import "fmt"
import (
"encoding/json"
"fmt"
)
func Contains[T comparable](value T, slice []T) bool {
for _, item := range slice {
@ -29,3 +32,35 @@ func Insert[T any](arr []T, index int, value T) []T {
arr[index] = value
return arr
}
func InsertSlice[T any](arr []T, index int, value []T) []T {
arr = append(arr, value...)
copy(arr[index+len(value):], arr[index:])
copy(arr[index:], value)
return arr
}
func Remove[T any](arr []T, index int) []T {
return append(arr[:index], arr[index+1:]...)
}
func RemoveSlice[T any](arr []T, index int, length int) []T {
return append(arr[:index], arr[index+length:]...)
}
func ToJson(value interface{}) string {
if res, err := json.Marshal(value); err == nil {
return string(res)
} else {
return "{}"
}
}
func UnmarshalJson[T any](value string) T {
var res T
if err := json.Unmarshal([]byte(value), &res); err == nil {
return res
} else {
return res
}
}