feat: 新增语音转文字(ASR)功能

- 后端:新增 /api/audio/transcribe 接口,调用 Ollama whisper 进行语音识别
- 前端:新增 AudioTranscribePage.vue 页面,支持 MP3/WAV/M4A/OGG/FLAC 等格式
- 注册路由、工具卡片、智能助手欢迎语更新

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
eaiadmin
2026-09-14 00:53:36 +08:00
co-authored by Claude Code
parent 752c7837ef
commit 0455f064ac
299 changed files with 1588 additions and 432 deletions
@@ -0,0 +1,84 @@
package ai
import (
"time"
"gorm.io/gorm"
"eai_agentplatform/backend/internal/model"
"eai_agentplatform/backend/internal/store"
)
// ──────────────────────────────────────────────
// 按用户算力点计费(对齐 pj034 router.py 的 compute_credits / log_ai_call)
// ──────────────────────────────────────────────
// AI 能力常量(pj034 AiCapability 的精简子集)
const (
CapabilityAIChat = "ai_chat" // PathCoach 对话(每轮扣 1 点)
CapabilityTextGen = "text_gen" // 快捷动作(情景演练/查佣金/产品对比,扣 1 点)
CapabilityImageGen = "image_gen" // 文生图(按次扣点)
CapabilityEmbed = "embed" // 知识检索内部 embedding(不扣点,仅审计)
CapabilityEssayGrade = "essay_grade" // 简答题 LLM 评分(系统自动,不扣点,仅审计)
)
// CapabilityCredits 各能力扣点成本
var CapabilityCredits = map[string]int{
CapabilityAIChat: 1,
CapabilityTextGen: 1,
CapabilityImageGen: 1,
CapabilityEmbed: 0,
CapabilityEssayGrade: 0,
}
// ComputeCredits 扣点决策:仅「成功」才扣点;失败不扣。
func ComputeCredits(capability string, success bool) int {
if !success {
return 0
}
return CapabilityCredits[capability]
}
// LogEntry 一次 AI 调用的审计入参
type LogEntry struct {
UserID uint
Capability string
Provider string
RouteID string
Model string
TokensInput int
TokensOutput int
Success bool
ErrorMessage string
LatencyMs int
}
// LogCall 写 ai_call_log;成功且需扣点时从用户余额扣点。审计写入失败不阻断主流程。
func LogCall(e LogEntry) {
credits := ComputeCredits(e.Capability, e.Success)
status := "success"
if !e.Success {
status = "failed"
}
rec := model.AiCallLog{
UserID: e.UserID,
Capability: e.Capability,
Provider: e.Provider,
RouteID: e.RouteID,
Model: e.Model,
TokensInput: e.TokensInput,
TokensOutput: e.TokensOutput,
CreditsCharged: credits,
Status: status,
ErrorMessage: e.ErrorMessage,
LatencyMs: e.LatencyMs,
CreatedAt: time.Now(),
}
if err := store.DB.Create(&rec).Error; err != nil {
return
}
if credits > 0 {
store.DB.Model(&model.User{}).Where("id = ?", e.UserID).
UpdateColumn("ai_points", gorm.Expr("ai_points - ?", credits))
}
}