mirror of
https://github.com/yangjian102621/geekai.git
synced 2026-08-13 03:00:59 +00:00
feat(release): migrate GeekAI v4.3.0 to open source
- Sync backend and frontend from GeekAI Plus v4.3.0 - Remove commercial License flows and update open-source deployment defaults - Preserve Docker Compose deployment and bump image tags to v4.3.0 BREAKING CHANGE: commercial License configuration and related endpoints are removed
This commit is contained in:
+63
-5
@@ -14,6 +14,7 @@ import (
|
||||
"geekai/store/model"
|
||||
"io"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/imroc/req/v3"
|
||||
@@ -35,18 +36,63 @@ func CalcTokens(text string, model string) (int, error) {
|
||||
return len(token), nil
|
||||
}
|
||||
|
||||
// OpenAIResponse 非流式 chat/completions 响应;content 使用 RawMessage 以兼容 string 与多段式数组。
|
||||
type OpenAIResponse struct {
|
||||
Model string `json:"model"`
|
||||
Choices []struct {
|
||||
Index int `json:"index"`
|
||||
Message struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
Role string `json:"role"`
|
||||
Content json.RawMessage `json:"content"`
|
||||
} `json:"message"`
|
||||
FinishReason string `json:"finish_reason"`
|
||||
} `json:"choices"`
|
||||
}
|
||||
|
||||
// assistantContentPart 兼容 OpenAI 多模态 / 多段文本:{"type":"text","text":"..."} 等。
|
||||
type assistantContentPart struct {
|
||||
Type string `json:"type"`
|
||||
Text string `json:"text"`
|
||||
// 少数网关使用 content 字段承载文本
|
||||
Inner string `json:"content"`
|
||||
}
|
||||
|
||||
// NormalizeAssistantContent 将 message.content 规范为纯文本。
|
||||
// 兼容:JSON string、null、OpenAI 数组段、以及单段对象。
|
||||
func NormalizeAssistantContent(raw json.RawMessage) string {
|
||||
s := strings.TrimSpace(string(raw))
|
||||
if s == "" || s == "null" {
|
||||
return ""
|
||||
}
|
||||
var plain string
|
||||
if err := json.Unmarshal(raw, &plain); err == nil {
|
||||
return plain
|
||||
}
|
||||
var parts []assistantContentPart
|
||||
if err := json.Unmarshal(raw, &parts); err == nil && len(parts) > 0 {
|
||||
var b strings.Builder
|
||||
for _, p := range parts {
|
||||
switch {
|
||||
case p.Text != "":
|
||||
b.WriteString(p.Text)
|
||||
case p.Inner != "" && p.Type != "image_url" && p.Type != "image":
|
||||
b.WriteString(p.Inner)
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
var one assistantContentPart
|
||||
if err := json.Unmarshal(raw, &one); err == nil {
|
||||
if one.Text != "" {
|
||||
return one.Text
|
||||
}
|
||||
if one.Inner != "" {
|
||||
return one.Inner
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func OpenAIRequest(db *gorm.DB, prompt string, modelId int) (string, error) {
|
||||
messages := make([]any, 1)
|
||||
messages[0] = types.Message{
|
||||
@@ -86,12 +132,16 @@ func SendOpenAIMessage(db *gorm.DB, messages []any, modelId int) (string, error)
|
||||
apiURL = apiKey.ApiURL
|
||||
}
|
||||
logger.Infof("Sending %s request, API KEY:%s, PROXY: %s, Model: %s", apiURL, apiKey.ApiURL, apiKey.ProxyURL, chatModel.Name)
|
||||
r, err := client.R().SetHeader("Body-Type", "application/json").
|
||||
r, err := client.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("Authorization", "Bearer "+apiKey.Value).
|
||||
SetBody(types.ApiRequest{
|
||||
Model: chatModel.Value,
|
||||
Temperature: 0.9,
|
||||
MaxTokens: 1024,
|
||||
// gpt-5/o 系模型在复杂提示下可能先消耗大量 token 于推理过程,
|
||||
// 1024 容易导致 finish_reason=length 且 content 为空。
|
||||
MaxTokens: 4096,
|
||||
MaxCompletionTokens: 4096,
|
||||
Stream: false,
|
||||
Messages: messages,
|
||||
}).Post(apiURL)
|
||||
@@ -108,9 +158,17 @@ func SendOpenAIMessage(db *gorm.DB, messages []any, modelId int) (string, error)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("解析API数据失败:%v, %s", err, string(body))
|
||||
}
|
||||
if len(response.Choices) == 0 {
|
||||
return "", fmt.Errorf("模型返回 choices 为空:%s", string(body))
|
||||
}
|
||||
|
||||
out := strings.TrimSpace(NormalizeAssistantContent(response.Choices[0].Message.Content))
|
||||
if out == "" {
|
||||
return "", fmt.Errorf("模型返回内容为空,请重试或更换模型(若使用推理模型,请确认网关已返回 content 或 reasoning_content)")
|
||||
}
|
||||
|
||||
// 更新 API KEY 的最后使用时间
|
||||
db.Model(&apiKey).UpdateColumn("last_used_at", time.Now().Unix())
|
||||
|
||||
return response.Choices[0].Message.Content, nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user