Skip to content

Commit

Permalink
feat(frontend): 实现 AI聊天流功能
Browse files Browse the repository at this point in the history
- 新增 NewChatStream 函数,用于接收实时聊天流数据
- 在 App 组件中添加 NewChatStream 方法处理聊天流
- 修改前端 Stock 组件,支持实时显示 AI 聊天流结果
- 优化后端 OpenAi 结构,增加 NewChatStream 方法获取流式响应
spark committed Jan 22, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 9cd6761 commit dadfe1c
Showing 6 changed files with 128 additions and 10 deletions.
7 changes: 7 additions & 0 deletions app.go
Original file line number Diff line number Diff line change
@@ -377,6 +377,13 @@ func (a *App) NewChat(stock string) string {
return data.NewDeepSeekOpenAi().NewChat(stock)
}

func (a *App) NewChatStream(stock string) {
msgs := data.NewDeepSeekOpenAi().NewChatStream(stock)
for msg := range msgs {
runtime.EventsEmit(a.ctx, "newChatStream", msg)
}
}

func GenNotificationMsg(stockInfo *data.StockInfo) string {
Price, err := convertor.ToFloat(stockInfo.Price)
if err != nil {
89 changes: 89 additions & 0 deletions backend/data/openai_api.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package data

import (
"bufio"
"encoding/json"
"github.com/go-resty/resty/v2"
"strings"
)

// @Author spark
@@ -105,3 +108,89 @@ func (o OpenAi) NewChat(stock string) string {
//logger.SugaredLogger.Infof("%v", res.Choices[0].Message.Content)
return res.Choices[0].Message.Content
}
func (o OpenAi) NewChatStream(stock string) <-chan string {
ch := make(chan string)
go func() {
defer close(ch)
client := resty.New()
client.SetBaseURL(o.BaseUrl)
client.SetHeader("Authorization", "Bearer "+o.ApiKey)
client.SetHeader("Content-Type", "application/json")

resp, err := client.R().
SetDoNotParseResponse(true).
SetBody(map[string]interface{}{
"model": o.Model,
"max_tokens": o.MaxTokens,
"temperature": o.Temperature,
"stream": true,
"messages": []map[string]interface{}{
{
"role": "system",
"content": "作为一位专业的A股市场分析师和投资顾问,请你根据以下信息提供详细的技术分析和投资策略建议:" +
"1. 市场背景:\n" +
"- 当前A股市场整体走势(如:牛市、熊市、震荡市)\n " +
"- 近期影响市场的主要宏观经济因素\n " +
"- 市场情绪指标(如:融资融券余额、成交量变化) " +
"2. 技术指标分析: " +
"- 当前股价水平" +
"- 所在boll区间" +
"- 上证指数的MA(移动平均线)、MACD、KDJ指标分析\n " +
"- 行业板块轮动情况\n " +
"- 近期市场热点和龙头股票的技术形态 " +
"3. 风险评估:\n " +
"- 当前市场主要风险因素\n " +
"- 如何设置止损和止盈位\n " +
"- 资金管理建议(如:仓位控制) " +
"4. 投资策略:\n " +
"- 短期(1-2周)、中期(1-3月)和长期(3-6月)的市场预期\n " +
"- 不同风险偏好投资者的策略建议\n " +
"- 值得关注的行业板块和个股推荐(请给出2-3个具体例子,包括股票代码和名称) " +
"5. 技术面和基本面结合:\n " +
"- 如何将技术分析与公司基本面分析相结合\n " +
"- 识别高质量股票的关键指标 " +
"请提供详细的分析和具体的操作建议,包括入场时机、持仓周期和退出策略。同时,请强调风险控制的重要性,并提醒投资者需要根据自身情况做出决策。 " +
"你的分析和建议应当客观、全面,并基于当前可获得的市场数据。如果某些信息无法确定,请明确指出并解释原因。",
},
{
"role": "user",
"content": "点评一下" + stock,
},
},
}).
Post("/chat/completions")

if err != nil {
return
}
defer resp.RawBody().Close()

scanner := bufio.NewScanner(resp.RawBody())
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "data: ") {
data := strings.TrimPrefix(line, "data: ")
if data == "[DONE]" {
return
}

var streamResponse struct {
Choices []struct {
Delta struct {
Content string `json:"content"`
} `json:"delta"`
} `json:"choices"`
}

if err := json.Unmarshal([]byte(data), &streamResponse); err == nil {
for _, choice := range streamResponse.Choices {
if content := choice.Delta.Content; content != "" {
ch <- content
}
}
}
}
}
}()
return ch
}
14 changes: 12 additions & 2 deletions backend/data/openai_api_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
package data

import (
"go-stock/backend/db"
"testing"
)

func TestNewDeepSeekOpenAiConfig(t *testing.T) {
db.Init("../../data/stock.db")
ai := NewDeepSeekOpenAi()
ai.NewChat("闻泰科技")

res := ai.NewChatStream("闻泰科技")
for {
select {
case msg := <-res:
if msg == "" {
return
}
t.Log(msg)
}
}
}
22 changes: 14 additions & 8 deletions frontend/src/components/stock.vue
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ import {
Follow,
GetFollowList,
GetStockList,
Greet, NewChat,
Greet, NewChat, NewChatStream,
SendDingDingMessage, SendDingDingMessageByType,
SetAlarmChangePercent,
SetCostPriceAndVolume, SetStockSort,
@@ -16,7 +16,7 @@ import {Add, Search,StarOutline} from '@vicons/ionicons5'
import { MdPreview } from 'md-editor-v3';
// preview.css相比style.css少了编辑器那部分样式
import 'md-editor-v3/lib/preview.css';
const mdPreviewRef = ref(null)
const message = useMessage()
const modal = useModal()
const notify = useNotification()
@@ -130,6 +130,11 @@ EventsOn("refreshFollowList",(data)=>{
// })
})
EventsOn("newChatStream",(msg)=>{
//console.log("newChatStream:->",data.airesult)
data.airesult=data.airesult+msg
})
//判断是否是A股交易时间
@@ -368,12 +373,11 @@ function SendMessage(result,type){
}
function aiCheckStock(stock){
data.airesult=""
data.name=stock
modalShow4.value=true
message.loading("ai检测中...")
NewChat(stock).then(result => {
data.name=stock
data.airesult=(result)
modalShow4.value=true
})
NewChatStream(stock)
}
function getTypeName(type){
@@ -394,6 +398,8 @@ function getTypeName(type){
function getHeight() {
return document.documentElement.clientHeight
}
</script>

<template>
@@ -523,7 +529,7 @@ function getHeight() {
</n-modal>

<n-modal transform-origin="center" v-model:show="modalShow4" preset="card" style="width: 800px;height: 480px" :title="'['+data.name+']AI分析结果'" >
<MdPreview style="height: 380px" :modelValue="data.airesult" :theme="'dark'"/>
<MdPreview ref="mdPreviewRef" style="height: 380px" :modelValue="data.airesult" :theme="'dark'"/>
</n-modal>
</template>

2 changes: 2 additions & 0 deletions frontend/wailsjs/go/main/App.d.ts
Original file line number Diff line number Diff line change
@@ -14,6 +14,8 @@ export function Greet(arg1:string):Promise<data.StockInfo>;

export function NewChat(arg1:string):Promise<string>;

export function NewChatStream(arg1:string):Promise<void>;

export function SendDingDingMessage(arg1:string,arg2:string):Promise<string>;

export function SendDingDingMessageByType(arg1:string,arg2:string,arg3:number):Promise<string>;
4 changes: 4 additions & 0 deletions frontend/wailsjs/go/main/App.js
Original file line number Diff line number Diff line change
@@ -26,6 +26,10 @@ export function NewChat(arg1) {
return window['go']['main']['App']['NewChat'](arg1);
}

export function NewChatStream(arg1) {
return window['go']['main']['App']['NewChatStream'](arg1);
}

export function SendDingDingMessage(arg1, arg2) {
return window['go']['main']['App']['SendDingDingMessage'](arg1, arg2);
}

0 comments on commit dadfe1c

Please sign in to comment.