Merge pull request 'fix: 修复返回给前端的 totalTokens 为0的问题并提供示例 .env' (#49) from fix/config-file into develop
Reviewed-on: http://8.161.227.145:3000/XEngineers/CamTalk/pulls/49
This commit was merged in pull request #49.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,7 +7,6 @@ backend/bin/
|
|||||||
backend/server
|
backend/server
|
||||||
|
|
||||||
# ---- 环境变量 ----
|
# ---- 环境变量 ----
|
||||||
.env
|
|
||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
|
|
||||||
|
|||||||
17
backend/.env
Normal file
17
backend/.env
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# ---- AI 服务 API Key ----
|
||||||
|
CAMTALK_AI_LLM_API_KEY=sk-ws-H.REHELLY.C4s3.MEUCIQCRee37XWEKp2szaxVLFDtR1rxNNsf372zMvCR0Xl6UvQIgZgvhRTvaa1FmhbCQJgaHu4Jny29AQkn01-3hX9CWBOg
|
||||||
|
CAMTALK_AI_STT_API_KEY=tp-c9e7scwfx94qvqyhpnahnw8uaiya01za2qzvg4xe24rp3xiv
|
||||||
|
CAMTALK_AI_TTS_API_KEY=tp-c9e7scwfx94qvqyhpnahnw8uaiya01za2qzvg4xe24rp3xiv
|
||||||
|
|
||||||
|
# ---- 可选覆盖(默认值见 config.yaml)----
|
||||||
|
# CAMTALK_AI_LLM_MODEL=qwen3-vl-plus
|
||||||
|
# CAMTALK_AI_LLM_ENDPOINT=https://api.openai.com/v1
|
||||||
|
# CAMTALK_AI_LLM_TIMEOUT=10
|
||||||
|
# CAMTALK_AI_STT_ENDPOINT=wss://api.deepgram.com/v1/listen
|
||||||
|
# CAMTALK_AI_TTS_ENDPOINT=https://api.openai.com/v1
|
||||||
|
# CAMTALK_AI_TTS_VOICE=alloy
|
||||||
|
# CAMTALK_AI_TTS_SPEED=1.0
|
||||||
|
# CAMTALK_AI_TTS_TIMEOUT=5
|
||||||
|
|
||||||
|
# ---- 应用 ----
|
||||||
|
# APP_ENV=dev
|
||||||
1
backend/.gitignore
vendored
1
backend/.gitignore
vendored
@@ -3,7 +3,6 @@
|
|||||||
bin/
|
bin/
|
||||||
|
|
||||||
# 环境配置
|
# 环境配置
|
||||||
.env
|
|
||||||
config.dev.yaml
|
config.dev.yaml
|
||||||
config.prod.yaml
|
config.prod.yaml
|
||||||
|
|
||||||
|
|||||||
@@ -166,11 +166,12 @@ func (p *Pipeline) ProcessQuery(
|
|||||||
var ttsErr error
|
var ttsErr error
|
||||||
|
|
||||||
// goroutine 1: 消费 LLM token + 句子切分
|
// goroutine 1: 消费 LLM token + 句子切分
|
||||||
|
var tokenUsage *llm.TokenUsage
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
defer close(sentenceCh)
|
defer close(sentenceCh)
|
||||||
fullText = p.consumeLLMStream(ctx, llmStream, req.RequestID, sender, splitter)
|
fullText, tokenUsage = p.consumeLLMStream(ctx, llmStream, req.RequestID, sender, splitter)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// goroutine 2: TTS 合成(如果启用)
|
// goroutine 2: TTS 合成(如果启用)
|
||||||
@@ -205,13 +206,25 @@ func (p *Pipeline) ProcessQuery(
|
|||||||
|
|
||||||
// 发送 llm_done
|
// 发送 llm_done
|
||||||
latency := time.Since(startTime).Milliseconds()
|
latency := time.Since(startTime).Milliseconds()
|
||||||
if err := sender.SendLLMDone(models.WsLLMDone{
|
done := models.WsLLMDone{
|
||||||
Type: "llm_done",
|
Type: "llm_done",
|
||||||
RequestID: req.RequestID,
|
RequestID: req.RequestID,
|
||||||
FullText: fullText,
|
FullText: fullText,
|
||||||
Model: p.model,
|
Model: p.model,
|
||||||
LatencyMs: latency,
|
LatencyMs: latency,
|
||||||
}); err != nil {
|
}
|
||||||
|
if tokenUsage != nil {
|
||||||
|
done.TokensUsed = struct {
|
||||||
|
Prompt int `json:"prompt"`
|
||||||
|
Completion int `json:"completion"`
|
||||||
|
Total int `json:"total"`
|
||||||
|
}{
|
||||||
|
Prompt: tokenUsage.Prompt,
|
||||||
|
Completion: tokenUsage.Completion,
|
||||||
|
Total: tokenUsage.Total,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := sender.SendLLMDone(done); err != nil {
|
||||||
log.Errorw("发送 llm_done 失败", "error", err)
|
log.Errorw("发送 llm_done 失败", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,33 +238,36 @@ func (p *Pipeline) ProcessQuery(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// consumeLLMStream 消费 LLM 流式输出,发送 llm_chunk 并进行句子切分。
|
// consumeLLMStream 消费 LLM 流式输出,发送 llm_chunk 并进行句子切分。
|
||||||
|
// 返回完整文本和 token 用量。
|
||||||
func (p *Pipeline) consumeLLMStream(
|
func (p *Pipeline) consumeLLMStream(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
stream <-chan llm.Chunk,
|
stream <-chan llm.Chunk,
|
||||||
requestID string,
|
requestID string,
|
||||||
sender Sender,
|
sender Sender,
|
||||||
splitter *Splitter,
|
splitter *Splitter,
|
||||||
) string {
|
) (string, *llm.TokenUsage) {
|
||||||
log := logger.Log
|
log := logger.Log
|
||||||
var fullText strings.Builder
|
var fullText strings.Builder
|
||||||
|
var tokenUsage *llm.TokenUsage
|
||||||
|
|
||||||
for chunk := range stream {
|
for chunk := range stream {
|
||||||
// 检查上下文是否已取消
|
// 检查上下文是否已取消
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
log.Infow("LLM 流被中断", "request_id", requestID)
|
log.Infow("LLM 流被中断", "request_id", requestID)
|
||||||
return fullText.String()
|
return fullText.String(), tokenUsage
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
if chunk.Done {
|
if chunk.Done {
|
||||||
// 流结束
|
// 流结束,记录 token 用量
|
||||||
if chunk.TokensUsed != nil {
|
if chunk.TokensUsed != nil {
|
||||||
|
tokenUsage = chunk.TokensUsed
|
||||||
log.Infow("LLM 用量统计",
|
log.Infow("LLM 用量统计",
|
||||||
"request_id", requestID,
|
"request_id", requestID,
|
||||||
"prompt_tokens", chunk.TokensUsed.Prompt,
|
"prompt_tokens", tokenUsage.Prompt,
|
||||||
"completion_tokens", chunk.TokensUsed.Completion,
|
"completion_tokens", tokenUsage.Completion,
|
||||||
"total_tokens", chunk.TokensUsed.Total,
|
"total_tokens", tokenUsage.Total,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
@@ -277,7 +293,7 @@ func (p *Pipeline) consumeLLMStream(
|
|||||||
// 刷新切分器中的剩余文本
|
// 刷新切分器中的剩余文本
|
||||||
splitter.Flush()
|
splitter.Flush()
|
||||||
|
|
||||||
return fullText.String()
|
return fullText.String(), tokenUsage
|
||||||
}
|
}
|
||||||
|
|
||||||
// synthesizeTTS 从句子 channel 读取文本,进行 TTS 合成并发送音频。
|
// synthesizeTTS 从句子 channel 读取文本,进行 TTS 合成并发送音频。
|
||||||
|
|||||||
Reference in New Issue
Block a user