refactor: Introduce pre-consume quota and unify relay handlers
This commit introduces a major architectural refactoring to improve quota management, centralize logging, and streamline the relay handling logic. Key changes: - **Pre-consume Quota:** Implements a new mechanism to check and reserve user quota *before* making the request to the upstream provider. This ensures more accurate quota deduction and prevents users from exceeding their limits due to concurrent requests. - **Unified Relay Handlers:** Refactors the relay logic to use generic handlers (e.g., `ChatHandler`, `ImageHandler`) instead of provider-specific implementations. This significantly reduces code duplication and simplifies adding new channels. - **Centralized Logger:** A new dedicated `logger` package is introduced, and all system logging calls are migrated to use it, moving this responsibility out of the `common` package. - **Code Reorganization:** DTOs are generalized (e.g., `dalle.go` -> `openai_image.go`) and utility code is moved to more appropriate packages (e.g., `common/http.go` -> `service/http.go`) for better code structure.
This commit is contained in:
128
dto/claude.go
128
dto/claude.go
@@ -5,6 +5,9 @@ import (
|
||||
"fmt"
|
||||
"one-api/common"
|
||||
"one-api/types"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type ClaudeMetadata struct {
|
||||
@@ -81,7 +84,7 @@ func (c *ClaudeMediaMessage) GetStringContent() string {
|
||||
}
|
||||
|
||||
func (c *ClaudeMediaMessage) GetJsonRowString() string {
|
||||
jsonContent, _ := json.Marshal(c)
|
||||
jsonContent, _ := common.Marshal(c)
|
||||
return string(jsonContent)
|
||||
}
|
||||
|
||||
@@ -199,6 +202,129 @@ type ClaudeRequest struct {
|
||||
Thinking *Thinking `json:"thinking,omitempty"`
|
||||
}
|
||||
|
||||
func (c *ClaudeRequest) GetTokenCountMeta() *types.TokenCountMeta {
|
||||
var tokenCountMeta = types.TokenCountMeta{
|
||||
TokenType: types.TokenTypeTextNumber,
|
||||
MaxTokens: int(c.MaxTokens),
|
||||
}
|
||||
|
||||
var texts = make([]string, 0)
|
||||
var fileMeta = make([]*types.FileMeta, 0)
|
||||
|
||||
// system
|
||||
if c.System != nil {
|
||||
if c.IsStringSystem() {
|
||||
sys := c.GetStringSystem()
|
||||
if sys != "" {
|
||||
texts = append(texts, sys)
|
||||
}
|
||||
} else {
|
||||
systemMedia := c.ParseSystem()
|
||||
for _, media := range systemMedia {
|
||||
switch media.Type {
|
||||
case "text":
|
||||
texts = append(texts, media.GetText())
|
||||
case "image":
|
||||
if media.Source != nil {
|
||||
data := media.Source.Url
|
||||
if data == "" {
|
||||
data = common.Interface2String(media.Source.Data)
|
||||
}
|
||||
if data != "" {
|
||||
fileMeta = append(fileMeta, &types.FileMeta{FileType: types.FileTypeImage, Data: data})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// messages
|
||||
for _, message := range c.Messages {
|
||||
tokenCountMeta.MessagesCount++
|
||||
texts = append(texts, message.Role)
|
||||
if message.IsStringContent() {
|
||||
content := message.GetStringContent()
|
||||
if content != "" {
|
||||
texts = append(texts, content)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
content, _ := message.ParseContent()
|
||||
for _, media := range content {
|
||||
switch media.Type {
|
||||
case "text":
|
||||
texts = append(texts, media.GetText())
|
||||
case "image":
|
||||
if media.Source != nil {
|
||||
data := media.Source.Url
|
||||
if data == "" {
|
||||
data = common.Interface2String(media.Source.Data)
|
||||
}
|
||||
if data != "" {
|
||||
fileMeta = append(fileMeta, &types.FileMeta{FileType: types.FileTypeImage, Data: data})
|
||||
}
|
||||
}
|
||||
case "tool_use":
|
||||
if media.Name != "" {
|
||||
texts = append(texts, media.Name)
|
||||
}
|
||||
if media.Input != nil {
|
||||
b, _ := common.Marshal(media.Input)
|
||||
texts = append(texts, string(b))
|
||||
}
|
||||
case "tool_result":
|
||||
if media.Content != nil {
|
||||
b, _ := common.Marshal(media.Content)
|
||||
texts = append(texts, string(b))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tools
|
||||
if c.Tools != nil {
|
||||
tools := c.GetTools()
|
||||
normalTools, webSearchTools := ProcessTools(tools)
|
||||
if normalTools != nil {
|
||||
for _, t := range normalTools {
|
||||
tokenCountMeta.ToolsCount++
|
||||
if t.Name != "" {
|
||||
texts = append(texts, t.Name)
|
||||
}
|
||||
if t.Description != "" {
|
||||
texts = append(texts, t.Description)
|
||||
}
|
||||
if t.InputSchema != nil {
|
||||
b, _ := common.Marshal(t.InputSchema)
|
||||
texts = append(texts, string(b))
|
||||
}
|
||||
}
|
||||
}
|
||||
if webSearchTools != nil {
|
||||
for _, t := range webSearchTools {
|
||||
tokenCountMeta.ToolsCount++
|
||||
if t.Name != "" {
|
||||
texts = append(texts, t.Name)
|
||||
}
|
||||
if t.UserLocation != nil {
|
||||
b, _ := common.Marshal(t.UserLocation)
|
||||
texts = append(texts, string(b))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tokenCountMeta.CombineText = strings.Join(texts, "\n")
|
||||
tokenCountMeta.Files = fileMeta
|
||||
return &tokenCountMeta
|
||||
}
|
||||
|
||||
func (claudeRequest *ClaudeRequest) IsStream(c *gin.Context) bool {
|
||||
return claudeRequest.Stream
|
||||
}
|
||||
|
||||
func (c *ClaudeRequest) SearchToolNameByToolCallId(toolCallId string) string {
|
||||
for _, message := range c.Messages {
|
||||
content, _ := message.ParseContent()
|
||||
|
||||
Reference in New Issue
Block a user