diff --git a/relay/channel/ollama/adaptor.go b/relay/channel/ollama/adaptor.go index 8997889..e1225ee 100644 --- a/relay/channel/ollama/adaptor.go +++ b/relay/channel/ollama/adaptor.go @@ -52,7 +52,7 @@ func (a *Adaptor) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, request func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (usage *dto.Usage, err *dto.OpenAIErrorWithStatusCode) { if info.IsStream { var responseText string - err, responseText, _ = openai.OpenaiStreamHandler(c, resp, info.RelayMode) + err, responseText, _ = openai.OpenaiStreamHandler(c, resp, info) usage, _ = service.ResponseText2Usage(responseText, info.UpstreamModelName, info.PromptTokens) } else { if info.RelayMode == relayconstant.RelayModeEmbeddings { diff --git a/relay/channel/openai/adaptor.go b/relay/channel/openai/adaptor.go index a0a5b07..58f5ab5 100644 --- a/relay/channel/openai/adaptor.go +++ b/relay/channel/openai/adaptor.go @@ -82,7 +82,7 @@ func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycom if info.IsStream { var responseText string var toolCount int - err, responseText, toolCount = OpenaiStreamHandler(c, resp, info.RelayMode) + err, responseText, toolCount = OpenaiStreamHandler(c, resp, info) usage, _ = service.ResponseText2Usage(responseText, info.UpstreamModelName, info.PromptTokens) usage.CompletionTokens += toolCount * 7 } else { diff --git a/relay/channel/openai/relay-openai.go b/relay/channel/openai/relay-openai.go index a41f645..8733cce 100644 --- a/relay/channel/openai/relay-openai.go +++ b/relay/channel/openai/relay-openai.go @@ -9,6 +9,7 @@ import ( "net/http" "one-api/common" "one-api/dto" + relaycommon "one-api/relay/common" relayconstant "one-api/relay/constant" "one-api/service" "strings" @@ -16,7 +17,7 @@ import ( "time" ) -func OpenaiStreamHandler(c *gin.Context, resp *http.Response, relayMode int) (*dto.OpenAIErrorWithStatusCode, string, int) { +func OpenaiStreamHandler(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (*dto.OpenAIErrorWithStatusCode, string, int) { //checkSensitive := constant.ShouldCheckCompletionSensitive() var responseTextBuilder strings.Builder toolCount := 0 @@ -57,7 +58,7 @@ func OpenaiStreamHandler(c *gin.Context, resp *http.Response, relayMode int) (*d } } streamResp := "[" + strings.Join(streamItems, ",") + "]" - switch relayMode { + switch info.RelayMode { case relayconstant.RelayModeChatCompletions: var streamResponses []dto.ChatCompletionsStreamResponseSimple err := json.Unmarshal(common.StringToByteSlice(streamResp), &streamResponses) @@ -126,9 +127,14 @@ func OpenaiStreamHandler(c *gin.Context, resp *http.Response, relayMode int) (*d common.SafeSendBool(stopChan, true) }() service.SetEventStreamHeaders(c) + isFirst := true c.Stream(func(w io.Writer) bool { select { case data := <-dataChan: + if isFirst { + isFirst = false + info.FirstResponseTime = time.Now() + } if strings.HasPrefix(data, "data: [DONE]") { data = data[:12] } diff --git a/relay/channel/perplexity/adaptor.go b/relay/channel/perplexity/adaptor.go index 00d7710..ef87df8 100644 --- a/relay/channel/perplexity/adaptor.go +++ b/relay/channel/perplexity/adaptor.go @@ -46,7 +46,7 @@ func (a *Adaptor) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, request func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (usage *dto.Usage, err *dto.OpenAIErrorWithStatusCode) { if info.IsStream { var responseText string - err, responseText, _ = openai.OpenaiStreamHandler(c, resp, info.RelayMode) + err, responseText, _ = openai.OpenaiStreamHandler(c, resp, info) usage, _ = service.ResponseText2Usage(responseText, info.UpstreamModelName, info.PromptTokens) } else { err, usage = openai.OpenaiHandler(c, resp, info.PromptTokens, info.UpstreamModelName) diff --git a/relay/channel/zhipu_4v/adaptor.go b/relay/channel/zhipu_4v/adaptor.go index fe89ff4..040cf38 100644 --- a/relay/channel/zhipu_4v/adaptor.go +++ b/relay/channel/zhipu_4v/adaptor.go @@ -48,7 +48,7 @@ func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycom if info.IsStream { var responseText string var toolCount int - err, responseText, toolCount = openai.OpenaiStreamHandler(c, resp, info.RelayMode) + err, responseText, toolCount = openai.OpenaiStreamHandler(c, resp, info) usage, _ = service.ResponseText2Usage(responseText, info.UpstreamModelName, info.PromptTokens) usage.CompletionTokens += toolCount * 7 } else { diff --git a/relay/common/relay_info.go b/relay/common/relay_info.go index f93d36a..2a6872e 100644 --- a/relay/common/relay_info.go +++ b/relay/common/relay_info.go @@ -16,6 +16,7 @@ type RelayInfo struct { Group string TokenUnlimited bool StartTime time.Time + FirstResponseTime time.Time ApiType int IsStream bool RelayMode int diff --git a/relay/relay-text.go b/relay/relay-text.go index 02dcaaa..8673286 100644 --- a/relay/relay-text.go +++ b/relay/relay-text.go @@ -332,14 +332,7 @@ func postConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, textRe logModel = "gpt-4-gizmo-*" logContent += fmt.Sprintf(",模型 %s", textRequest.Model) } - other := make(map[string]interface{}) - other["model_ratio"] = modelRatio - other["group_ratio"] = groupRatio - other["completion_ratio"] = completionRatio - other["model_price"] = modelPrice - adminInfo := make(map[string]interface{}) - adminInfo["use_channel"] = ctx.GetStringSlice("use_channel") - other["admin_info"] = adminInfo + other := service.GenerateTextOtherInfo(ctx, relayInfo, modelRatio, groupRatio, completionRatio, modelPrice) model.RecordConsumeLog(ctx, relayInfo.UserId, relayInfo.ChannelId, promptTokens, completionTokens, logModel, tokenName, quota, logContent, relayInfo.TokenId, userQuota, int(useTimeSeconds), relayInfo.IsStream, other) //if quota != 0 { diff --git a/service/log.go b/service/log.go new file mode 100644 index 0000000..506effb --- /dev/null +++ b/service/log.go @@ -0,0 +1,19 @@ +package service + +import ( + "github.com/gin-gonic/gin" + relaycommon "one-api/relay/common" +) + +func GenerateTextOtherInfo(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, modelRatio, groupRatio, completionRatio, modelPrice float64) map[string]interface{} { + other := make(map[string]interface{}) + other["model_ratio"] = modelRatio + other["group_ratio"] = groupRatio + other["completion_ratio"] = completionRatio + other["model_price"] = modelPrice + other["frt"] = float64(relayInfo.FirstResponseTime.UnixMilli() - relayInfo.StartTime.UnixMilli()) + adminInfo := make(map[string]interface{}) + adminInfo["use_channel"] = ctx.GetStringSlice("use_channel") + other["admin_info"] = adminInfo + return other +} diff --git a/web/src/components/LogsTable.js b/web/src/components/LogsTable.js index ba4df1b..4bbacf0 100644 --- a/web/src/components/LogsTable.js +++ b/web/src/components/LogsTable.js @@ -29,6 +29,7 @@ import { stringToColor, } from '../helpers/render'; import Paragraph from '@douyinfe/semi-ui/lib/es/typography/paragraph'; +import {getLogOther} from "../helpers/other.js"; const { Header } = Layout; @@ -141,6 +142,33 @@ function renderUseTime(type) { } } +function renderFirstUseTime(type) { + let time = parseFloat(type) / 1000.0; + time = time.toFixed(1) + if (time < 3) { + return ( + + {' '} + {time} s{' '} + + ); + } else if (time < 10) { + return ( + + {' '} + {time} s{' '} + + ); + } else { + return ( + + {' '} + {time} s{' '} + + ); + } +} + const LogsTable = () => { const columns = [ { @@ -247,17 +275,30 @@ const LogsTable = () => { }, }, { - title: '用时', + title: '用时/首字', dataIndex: 'use_time', render: (text, record, index) => { - return ( -
- - {renderUseTime(text)} - {renderIsStream(record.is_stream)} - -
- ); + if (record.is_stream) { + let other = getLogOther(record.other); + return ( +
+ + {renderUseTime(text)} + {renderFirstUseTime(other.frt)} + {renderIsStream(record.is_stream)} + +
+ ); + } else { + return ( +
+ + {renderUseTime(text)} + {renderIsStream(record.is_stream)} + +
+ ); + } }, }, { @@ -325,10 +366,7 @@ const LogsTable = () => { title: '详情', dataIndex: 'content', render: (text, record, index) => { - if (record.other === '') { - record.other = '{}' - } - let other = JSON.parse(record.other); + let other = getLogOther(record.other); if (other == null) { return (