626 lines
20 KiB
Go
626 lines
20 KiB
Go
package handler
|
||
|
||
import (
|
||
"fmt"
|
||
"log"
|
||
"net/http"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
|
||
"infogenie-backend/config"
|
||
"infogenie-backend/internal/service"
|
||
)
|
||
|
||
type AIModelHandler struct{}
|
||
|
||
func NewAIModelHandler() *AIModelHandler { return &AIModelHandler{} }
|
||
|
||
// 输入长度限制
|
||
const (
|
||
maxInputLen = 5000
|
||
maxChatMsgCount = 20
|
||
)
|
||
|
||
// 允许用户选择的模型白名单
|
||
var allowedModels = map[string]map[string]bool{
|
||
"deepseek": {
|
||
"deepseek-chat": true,
|
||
"deepseek-reasoner": true,
|
||
},
|
||
"kimi": {
|
||
"kimi-k2-0905-preview": true,
|
||
"kimi-k2-0711-preview": true,
|
||
},
|
||
}
|
||
|
||
func safeAIError(err error) string {
|
||
log.Printf("AI调用失败: %v", err)
|
||
return "AI 服务暂时不可用,请稍后重试"
|
||
}
|
||
|
||
func validateTextLen(text string, label string) (string, error) {
|
||
t := strings.TrimSpace(text)
|
||
if t == "" {
|
||
return "", fmt.Errorf("%s不能为空", label)
|
||
}
|
||
if len(t) > maxInputLen {
|
||
return "", fmt.Errorf("%s超出长度限制(最大 %d 字符)", label, maxInputLen)
|
||
}
|
||
return t, nil
|
||
}
|
||
|
||
// POST /api/aimodelapp/chat
|
||
func (h *AIModelHandler) Chat(c *gin.Context) {
|
||
var req struct {
|
||
Messages []service.ChatMessage `json:"messages"`
|
||
Provider string `json:"provider"`
|
||
Model string `json:"model"`
|
||
}
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "请求数据为空"})
|
||
return
|
||
}
|
||
if req.Provider == "" {
|
||
req.Provider = "deepseek"
|
||
}
|
||
if req.Model == "" {
|
||
req.Model = "deepseek-chat"
|
||
}
|
||
|
||
// 模型白名单校验
|
||
if models, ok := allowedModels[req.Provider]; !ok || !models[req.Model] {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "不支持的模型"})
|
||
return
|
||
}
|
||
|
||
if len(req.Messages) == 0 {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "消息内容不能为空"})
|
||
return
|
||
}
|
||
if len(req.Messages) > maxChatMsgCount {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("消息数量不能超过 %d 条", maxChatMsgCount)})
|
||
return
|
||
}
|
||
// 校验每条消息的长度
|
||
for _, m := range req.Messages {
|
||
if len(m.Content) > maxInputLen {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("单条消息长度不能超过 %d 字符", maxInputLen)})
|
||
return
|
||
}
|
||
}
|
||
|
||
content, err := service.CallAI(req.Provider, req.Model, req.Messages)
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": safeAIError(err)})
|
||
return
|
||
}
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"content": content,
|
||
"provider": req.Provider,
|
||
"model": req.Model,
|
||
"timestamp": time.Now().Format(time.RFC3339),
|
||
})
|
||
}
|
||
|
||
// POST /api/aimodelapp/name-analysis
|
||
func (h *AIModelHandler) NameAnalysis(c *gin.Context) {
|
||
var req struct {
|
||
Name string `json:"name"`
|
||
}
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "姓名不能为空"})
|
||
return
|
||
}
|
||
name, err := validateTextLen(req.Name, "姓名")
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
|
||
prompt := fmt.Sprintf(`你是一位专业的姓名学专家和语言学家,请对输入的姓名进行全面分析。请直接输出分析结果,不要包含任何思考过程或<think>标签。
|
||
|
||
姓名:%s
|
||
|
||
请按照以下格式严格输出分析结果:
|
||
|
||
【稀有度评分】
|
||
评分:X%%
|
||
评价:[对稀有度的详细说明,包括姓氏和名字的常见程度分析]
|
||
|
||
【音韵评价】
|
||
评分:X%%
|
||
评价:[对音韵美感的分析,包括声调搭配、读音流畅度、音律和谐度等]
|
||
|
||
【含义解读】
|
||
[详细分析姓名的寓意内涵,包括:
|
||
1. 姓氏的历史渊源和文化背景
|
||
2. 名字各字的含义和象征
|
||
3. 整体姓名的寓意组合
|
||
4. 可能体现的父母期望或文化内涵
|
||
5. 与传统文化、诗词典故的关联等]
|
||
|
||
要求:
|
||
1. 评分必须是1-100的整数百分比,要有明显区分度,避免雷同
|
||
2. 分析要专业、客观、有依据,评分要根据实际情况有所差异
|
||
3. 含义解读要详细深入,至少150字
|
||
4. 严格按照上述格式输出,不要添加思考过程、<think>标签或其他内容
|
||
5. 如果是生僻字或罕见姓名,要特别说明
|
||
6. 直接输出最终结果,不要显示推理过程`, name)
|
||
|
||
messages := []service.ChatMessage{{Role: "user", Content: prompt}}
|
||
content, err := service.CallDeepSeek(messages, "", 3)
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": safeAIError(err)})
|
||
return
|
||
}
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"analysis": content,
|
||
"name": name,
|
||
"timestamp": time.Now().Format(time.RFC3339),
|
||
})
|
||
}
|
||
|
||
// POST /api/aimodelapp/variable-naming
|
||
func (h *AIModelHandler) VariableNaming(c *gin.Context) {
|
||
var req struct {
|
||
Description string `json:"description"`
|
||
Language string `json:"language"`
|
||
}
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "变量描述不能为空"})
|
||
return
|
||
}
|
||
desc, err := validateTextLen(req.Description, "变量描述")
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
lang := req.Language
|
||
if lang == "" {
|
||
lang = "javascript"
|
||
}
|
||
|
||
prompt := fmt.Sprintf(`你是一个专业的变量命名助手。请根据以下描述为变量生成合适的名称:
|
||
|
||
描述:%s
|
||
|
||
请为每种命名规范生成3个变量名建议:
|
||
1. camelCase (驼峰命名法)
|
||
2. PascalCase (帕斯卡命名法)
|
||
3. snake_case (下划线命名法)
|
||
4. kebab-case (短横线命名法)
|
||
5. CONSTANT_CASE (常量命名法)
|
||
|
||
请按JSON格式返回:
|
||
{"suggestions":{"camelCase":[{"name":"变量名","description":"说明"}],"PascalCase":[{"name":"变量名","description":"说明"}],"snake_case":[{"name":"变量名","description":"说明"}],"kebab-case":[{"name":"变量名","description":"说明"}],"CONSTANT_CASE":[{"name":"变量名","description":"说明"}]}}
|
||
|
||
只返回JSON格式的结果,不要包含其他文字。`, desc)
|
||
|
||
messages := []service.ChatMessage{{Role: "user", Content: prompt}}
|
||
content, err := service.CallDeepSeek(messages, "", 3)
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": safeAIError(err)})
|
||
return
|
||
}
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"suggestions": extractOrRaw(content),
|
||
"description": desc,
|
||
"language": lang,
|
||
"timestamp": time.Now().Format(time.RFC3339),
|
||
})
|
||
}
|
||
|
||
// POST /api/aimodelapp/poetry
|
||
func (h *AIModelHandler) Poetry(c *gin.Context) {
|
||
var req struct {
|
||
Theme string `json:"theme"`
|
||
Style string `json:"style"`
|
||
Mood string `json:"mood"`
|
||
}
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "诗歌主题不能为空"})
|
||
return
|
||
}
|
||
theme, err := validateTextLen(req.Theme, "诗歌主题")
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
style := req.Style
|
||
if style == "" {
|
||
style = "现代诗"
|
||
}
|
||
mood := req.Mood
|
||
if mood == "" {
|
||
mood = "自由发挥"
|
||
}
|
||
|
||
prompt := fmt.Sprintf(`你是一位才华横溢的诗人,请根据以下要求创作一首诗歌。
|
||
|
||
主题:%s
|
||
风格:%s
|
||
情感基调:%s
|
||
|
||
创作要求:
|
||
1. 紧扣主题,情感真挚
|
||
2. 语言优美,意境深远
|
||
3. 符合指定的诗歌风格
|
||
4. 长度适中,朗朗上口
|
||
5. 如果是古体诗,注意平仄和韵律
|
||
|
||
请直接输出诗歌作品,不需要额外的解释或分析。`, theme, style, mood)
|
||
|
||
messages := []service.ChatMessage{{Role: "user", Content: prompt}}
|
||
content, err := service.CallDeepSeek(messages, "", 3)
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": safeAIError(err)})
|
||
return
|
||
}
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"poem": content,
|
||
"theme": theme,
|
||
"style": style,
|
||
"mood": mood,
|
||
"timestamp": time.Now().Format(time.RFC3339),
|
||
})
|
||
}
|
||
|
||
// POST /api/aimodelapp/translation
|
||
func (h *AIModelHandler) Translation(c *gin.Context) {
|
||
var req struct {
|
||
SourceText string `json:"source_text"`
|
||
TargetLanguage string `json:"target_language"`
|
||
}
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "翻译内容不能为空"})
|
||
return
|
||
}
|
||
text, err := validateTextLen(req.SourceText, "翻译内容")
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
targetLang := req.TargetLanguage
|
||
if targetLang == "" {
|
||
targetLang = "zh-CN"
|
||
}
|
||
|
||
langMap := map[string]string{
|
||
"zh-CN": "中文(简体)", "zh-TW": "中文(繁体)", "en": "英语",
|
||
"ja": "日语", "ko": "韩语", "fr": "法语", "de": "德语",
|
||
"es": "西班牙语", "it": "意大利语", "pt": "葡萄牙语",
|
||
"ru": "俄语", "ar": "阿拉伯语", "hi": "印地语",
|
||
"th": "泰语", "vi": "越南语",
|
||
}
|
||
langName := langMap[targetLang]
|
||
if langName == "" {
|
||
langName = targetLang
|
||
}
|
||
|
||
prompt := fmt.Sprintf(`你是一位专业的翻译专家,精通多种语言的翻译工作。请将以下文本翻译成%s。
|
||
|
||
原文:%s
|
||
|
||
翻译要求:
|
||
1. 【信】- 忠实原文,准确传达原意
|
||
2. 【达】- 译文通顺流畅,符合目标语言的表达习惯
|
||
3. 【雅】- 用词优美得体,风格与原文相符
|
||
|
||
请按以下JSON格式返回翻译结果:
|
||
{"detected_language":"检测到的源语言","target_language":"%s","translation":"翻译结果","alternative_translations":["备选翻译1","备选翻译2"],"explanation":"翻译说明","pronunciation":"发音指导"}
|
||
|
||
只返回JSON格式的结果,不要包含其他文字。`, langName, text, langName)
|
||
|
||
messages := []service.ChatMessage{{Role: "user", Content: prompt}}
|
||
content, err := service.CallDeepSeek(messages, "", 3)
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": safeAIError(err)})
|
||
return
|
||
}
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"translation_result": content,
|
||
"source_text": text,
|
||
"target_language": targetLang,
|
||
"timestamp": time.Now().Format(time.RFC3339),
|
||
})
|
||
}
|
||
|
||
// POST /api/aimodelapp/classical_conversion
|
||
func (h *AIModelHandler) ClassicalConversion(c *gin.Context) {
|
||
var req struct {
|
||
ModernText string `json:"modern_text"`
|
||
Style string `json:"style"`
|
||
ArticleType string `json:"article_type"`
|
||
}
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "现代文内容不能为空"})
|
||
return
|
||
}
|
||
text, err := validateTextLen(req.ModernText, "现代文内容")
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
style := req.Style
|
||
if style == "" {
|
||
style = "古雅"
|
||
}
|
||
artType := req.ArticleType
|
||
if artType == "" {
|
||
artType = "散文"
|
||
}
|
||
|
||
prompt := fmt.Sprintf(`你是一位精通古代文言文的文学大师,擅长将现代文转换为优美的文言文。请将以下现代文转换为文言文。
|
||
|
||
现代文:%s
|
||
风格:%s
|
||
文体:%s
|
||
|
||
请按以下JSON格式返回转换结果:
|
||
{"classical_text":"转换后的文言文","translation_notes":"转换说明","style_analysis":"风格分析","difficulty_level":"难度等级","key_phrases":[{"modern":"现代词汇","classical":"文言文词汇","explanation":"转换说明"}],"cultural_elements":"文化内涵说明"}
|
||
|
||
只返回JSON格式的结果,不要包含其他文字。`, text, style, artType)
|
||
|
||
messages := []service.ChatMessage{{Role: "user", Content: prompt}}
|
||
content, err := service.CallDeepSeek(messages, "", 3)
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": safeAIError(err)})
|
||
return
|
||
}
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"conversion_result": content,
|
||
"modern_text": text,
|
||
"style": style,
|
||
"article_type": artType,
|
||
"timestamp": time.Now().Format(time.RFC3339),
|
||
})
|
||
}
|
||
|
||
// POST /api/aimodelapp/expression-maker
|
||
func (h *AIModelHandler) ExpressionMaker(c *gin.Context) {
|
||
var req struct {
|
||
Text string `json:"text"`
|
||
Style string `json:"style"`
|
||
}
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "文字内容不能为空"})
|
||
return
|
||
}
|
||
text, err := validateTextLen(req.Text, "文字内容")
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
style := req.Style
|
||
if style == "" {
|
||
style = "mixed"
|
||
}
|
||
|
||
styleMap := map[string]string{
|
||
"mixed": "混合使用Emoji表情和颜文字", "emoji": "仅使用Emoji表情符号",
|
||
"kaomoji": "仅使用颜文字", "cute": "使用可爱风格的表情符号",
|
||
"cool": "使用酷炫风格的表情符号",
|
||
}
|
||
styleDesc := styleMap[style]
|
||
if styleDesc == "" {
|
||
styleDesc = styleMap["mixed"]
|
||
}
|
||
|
||
prompt := fmt.Sprintf(`你是一个专业的表情符号专家。请根据以下文字内容生成相应的表情符号:
|
||
|
||
文字内容:%s
|
||
表情风格:%s
|
||
|
||
请按JSON格式返回:
|
||
{"expressions":{"emoji":[{"symbol":"😊","description":"场景说明","intensity":"中等","usage":"使用建议"}],"kaomoji":[{"symbol":"(^_^)","description":"场景说明","intensity":"轻微","usage":"使用建议"}],"combination":[{"symbol":"🎉✨","description":"场景说明","intensity":"强烈","usage":"使用建议"}]},"summary":{"emotion_analysis":"情感分析","recommended_usage":"推荐使用场景","style_notes":"风格特点"}}
|
||
|
||
只返回JSON格式的结果,不要包含其他文字。`, text, styleDesc)
|
||
|
||
messages := []service.ChatMessage{{Role: "user", Content: prompt}}
|
||
content, err := service.CallDeepSeek(messages, "", 3)
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": safeAIError(err)})
|
||
return
|
||
}
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"expressions": extractOrRaw(content),
|
||
"text": text,
|
||
"style": style,
|
||
"timestamp": time.Now().Format(time.RFC3339),
|
||
})
|
||
}
|
||
|
||
// POST /api/aimodelapp/linux-command
|
||
func (h *AIModelHandler) LinuxCommand(c *gin.Context) {
|
||
var req struct {
|
||
TaskDescription string `json:"task_description"`
|
||
DifficultyLevel string `json:"difficulty_level"`
|
||
}
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "任务描述不能为空"})
|
||
return
|
||
}
|
||
desc, err := validateTextLen(req.TaskDescription, "任务描述")
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
level := req.DifficultyLevel
|
||
if level == "" {
|
||
level = "beginner"
|
||
}
|
||
|
||
prompt := fmt.Sprintf(`你是一位Linux系统专家,请根据用户的任务描述生成相应的Linux命令。
|
||
|
||
任务描述:%s
|
||
用户水平:%s
|
||
|
||
请按JSON格式返回:
|
||
{"commands":[{"command":"具体命令","description":"说明","safety_level":"safe","explanation":"解释","example_output":"示例输出","alternatives":["替代命令"]}],"safety_warnings":["安全提示"],"prerequisites":["前置条件"],"related_concepts":["相关概念"]}
|
||
|
||
只返回JSON格式的结果,不要包含其他文字。`, desc, level)
|
||
|
||
messages := []service.ChatMessage{{Role: "user", Content: prompt}}
|
||
content, err := service.CallDeepSeek(messages, "", 3)
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": safeAIError(err)})
|
||
return
|
||
}
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"command_result": content,
|
||
"task_description": desc,
|
||
"difficulty_level": level,
|
||
"timestamp": time.Now().Format(time.RFC3339),
|
||
})
|
||
}
|
||
|
||
// POST /api/aimodelapp/markdown_formatting
|
||
func (h *AIModelHandler) MarkdownFormatting(c *gin.Context) {
|
||
var req struct {
|
||
ArticleText string `json:"article_text"`
|
||
EmojiStyle string `json:"emoji_style"`
|
||
MarkdownOption string `json:"markdown_option"`
|
||
}
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "文章内容不能为空"})
|
||
return
|
||
}
|
||
text, err := validateTextLen(req.ArticleText, "文章内容")
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
emojiStyle := req.EmojiStyle
|
||
if emojiStyle == "" {
|
||
emojiStyle = "balanced"
|
||
}
|
||
mdOption := req.MarkdownOption
|
||
if mdOption == "" {
|
||
mdOption = "standard"
|
||
}
|
||
|
||
prompt := fmt.Sprintf(`你是一位专业的文档排版助手。请将用户提供的全文按"标准Markdown格式"进行排版,并在不改变任何原文内容的前提下进行结构化呈现。严格遵守以下规则:
|
||
|
||
1) 保留所有原始内容,严禁改写、删减或添加新内容。
|
||
2) 使用合理的Markdown结构(标题、分节、段落、列表、引用、表格如有必要、代码块仅当原文包含)。
|
||
3) 智能添加适量Emoji以增强可读性(%s),在标题、关键句、列表项等处点缀;避免过度使用,保持专业。
|
||
4) 保持语言与语气不变,只优化排版和表现形式。
|
||
5) 输出"纯Markdown文本",不要包含任何JSON、HTML、XML、解释文字、或代码块围栏标记。
|
||
|
||
如果原文本较长,可在开头自动生成简洁的"目录"以便阅读。
|
||
|
||
原文如下:
|
||
%s`, emojiStyle, text)
|
||
|
||
messages := []service.ChatMessage{{Role: "user", Content: prompt}}
|
||
content, err := service.CallDeepSeek(messages, "", 3)
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": safeAIError(err)})
|
||
return
|
||
}
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"formatted_markdown": content,
|
||
"source_text": text,
|
||
"emoji_style": emojiStyle,
|
||
"markdown_option": mdOption,
|
||
"timestamp": time.Now().Format(time.RFC3339),
|
||
})
|
||
}
|
||
|
||
// POST /api/aimodelapp/kinship-calculator
|
||
func (h *AIModelHandler) KinshipCalculator(c *gin.Context) {
|
||
var req struct {
|
||
RelationChain string `json:"relation_chain"`
|
||
Dialects []string `json:"dialects"`
|
||
}
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "亲属关系链不能为空"})
|
||
return
|
||
}
|
||
chain, err := validateTextLen(req.RelationChain, "亲属关系链")
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
dialects := req.Dialects
|
||
if len(dialects) == 0 {
|
||
dialects = []string{"粤语", "闽南语", "上海话", "四川话", "东北话", "客家话"}
|
||
}
|
||
|
||
prompt := fmt.Sprintf(`你是一位中国亲属称呼专家。请解析下面的亲属关系链,给出最终的亲属称呼。
|
||
|
||
请遵循:
|
||
1) 以中国大陆通行的标准普通话称呼为准。
|
||
2) 同时给出若干方言的对应称呼:%s。
|
||
3) 如存在地区差异或性别歧义,请在notes中说明。
|
||
4) 不要展示推理过程;只输出JSON。
|
||
|
||
严格按以下JSON结构输出:
|
||
{"mandarin_title":"标准普通话称呼","dialect_titles":{"粤语":{"title":"称呼","romanization":"粤拼","notes":"说明"}},"notes":"总体说明"}
|
||
|
||
关系链:%s`, strings.Join(dialects, "、"), chain)
|
||
|
||
messages := []service.ChatMessage{{Role: "user", Content: prompt}}
|
||
content, err := service.CallDeepSeek(messages, "", 3)
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": safeAIError(err)})
|
||
return
|
||
}
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"kinship_result": content,
|
||
"relation_chain": chain,
|
||
"timestamp": time.Now().Format(time.RFC3339),
|
||
})
|
||
}
|
||
|
||
// GET /api/aimodelapp/models — 仅返回允许的模型列表
|
||
func (h *AIModelHandler) GetModels(c *gin.Context) {
|
||
models := make(map[string][]string)
|
||
for provider, modelSet := range allowedModels {
|
||
for m := range modelSet {
|
||
models[provider] = append(models[provider], m)
|
||
}
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"models": models,
|
||
"default_provider": "deepseek",
|
||
"default_model": "deepseek-chat",
|
||
})
|
||
}
|
||
|
||
func extractOrRaw(content string) interface{} {
|
||
return content
|
||
}
|
||
|
||
// Ping 用于健康检查(从 config 引用原始配置可选)
|
||
func Ping(c *gin.Context) {
|
||
_ = config.Cfg
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"status": "running",
|
||
"timestamp": time.Now().Format(time.RFC3339),
|
||
})
|
||
}
|