Compare commits

...

7 Commits

Author SHA1 Message Date
JustSong
d14e4aa01b fix: key is wrongly updated 2024-04-21 17:38:39 +08:00
JustSong
541182102e fix: ignore empty choice response for azure (close #1324) 2024-04-21 16:22:28 +08:00
JustSong
b2679cca65 fix: fix preview completion ratio (close #1326) 2024-04-21 15:57:01 +08:00
JustSong
8572fac7a2 fix: add back chat dropdown item for chatgpt next web (close #1330) 2024-04-21 15:50:35 +08:00
tylinux
a2a00dfbc3 feat: groq support Llama3 now (#1333)
* feat: groq support Llama3 now

* fix: update model ratio

---------

Co-authored-by: JustSong <songquanpeng@foxmail.com>
2024-04-21 14:53:03 +08:00
JustSong
129282f4a9 fix: fix wrong log type 2024-04-21 14:36:48 +08:00
Laisky.Cai
a873cbd392 fix: logger race (#1339)
- Refactor logger using sync.Once to improve performance
- Initiate log setup in a goroutine to prevent blocking
- Integrate gin.DefaultErrorWriter and gin.DefaultWriter for logging
- Introduce request ID generation for better request tracking
- Simplify setup logic by removing redundant variables and code
2024-04-21 14:35:51 +08:00
8 changed files with 92 additions and 67 deletions

View File

@@ -16,7 +16,7 @@ import (
) )
// Regex to match data URL pattern // Regex to match data URL pattern
var dataURLPattern = regexp.MustCompile(`data:image/([^;]+);base64,(.*)`) var dataURLPattern = regexp.MustCompile(`data:image/([^;]+);base64,(.*)`)
func IsImageUrl(url string) (bool, error) { func IsImageUrl(url string) (bool, error) {
resp, err := http.Head(url) resp, err := http.Head(url)

View File

@@ -3,15 +3,16 @@ package logger
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/gin-gonic/gin"
"github.com/songquanpeng/one-api/common/config"
"github.com/songquanpeng/one-api/common/helper"
"io" "io"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
"sync" "sync"
"time" "time"
"github.com/gin-gonic/gin"
"github.com/songquanpeng/one-api/common/config"
"github.com/songquanpeng/one-api/common/helper"
) )
const ( const (
@@ -21,28 +22,20 @@ const (
loggerError = "ERR" loggerError = "ERR"
) )
var setupLogLock sync.Mutex var setupLogOnce sync.Once
var setupLogWorking bool
func SetupLogger() { func SetupLogger() {
if LogDir != "" { setupLogOnce.Do(func() {
ok := setupLogLock.TryLock() if LogDir != "" {
if !ok { logPath := filepath.Join(LogDir, fmt.Sprintf("oneapi-%s.log", time.Now().Format("20060102")))
log.Println("setup log is already working") fd, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
return if err != nil {
log.Fatal("failed to open log file")
}
gin.DefaultWriter = io.MultiWriter(os.Stdout, fd)
gin.DefaultErrorWriter = io.MultiWriter(os.Stderr, fd)
} }
defer func() { })
setupLogLock.Unlock()
setupLogWorking = false
}()
logPath := filepath.Join(LogDir, fmt.Sprintf("oneapi-%s.log", time.Now().Format("20060102")))
fd, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal("failed to open log file")
}
gin.DefaultWriter = io.MultiWriter(os.Stdout, fd)
gin.DefaultErrorWriter = io.MultiWriter(os.Stderr, fd)
}
} }
func SysLog(s string) { func SysLog(s string) {
@@ -100,12 +93,7 @@ func logHelper(ctx context.Context, level string, msg string) {
} }
now := time.Now() now := time.Now()
_, _ = fmt.Fprintf(writer, "[%s] %v | %s | %s \n", level, now.Format("2006/01/02 - 15:04:05"), id, msg) _, _ = fmt.Fprintf(writer, "[%s] %v | %s | %s \n", level, now.Format("2006/01/02 - 15:04:05"), id, msg)
if !setupLogWorking { SetupLogger()
setupLogWorking = true
go func() {
SetupLogger()
}()
}
} }
func FatalLog(v ...any) { func FatalLog(v ...any) {

View File

@@ -71,7 +71,7 @@ func main() {
} }
if config.MemoryCacheEnabled { if config.MemoryCacheEnabled {
logger.SysLog("memory cache enabled") logger.SysLog("memory cache enabled")
logger.SysError(fmt.Sprintf("sync frequency: %d seconds", config.SyncFrequency)) logger.SysLog(fmt.Sprintf("sync frequency: %d seconds", config.SyncFrequency))
model.InitChannelCache() model.InitChannelCache()
} }
if config.MemoryCacheEnabled { if config.MemoryCacheEnabled {

View File

@@ -7,4 +7,6 @@ var ModelList = []string{
"llama2-7b-2048", "llama2-7b-2048",
"llama2-70b-4096", "llama2-70b-4096",
"mixtral-8x7b-32768", "mixtral-8x7b-32768",
"llama3-8b-8192",
"llama3-70b-8192",
} }

View File

@@ -15,6 +15,12 @@ import (
"strings" "strings"
) )
const (
dataPrefix = "data: "
done = "[DONE]"
dataPrefixLength = len(dataPrefix)
)
func StreamHandler(c *gin.Context, resp *http.Response, relayMode int) (*model.ErrorWithStatusCode, string, *model.Usage) { func StreamHandler(c *gin.Context, resp *http.Response, relayMode int) (*model.ErrorWithStatusCode, string, *model.Usage) {
responseText := "" responseText := ""
scanner := bufio.NewScanner(resp.Body) scanner := bufio.NewScanner(resp.Body)
@@ -36,39 +42,46 @@ func StreamHandler(c *gin.Context, resp *http.Response, relayMode int) (*model.E
go func() { go func() {
for scanner.Scan() { for scanner.Scan() {
data := scanner.Text() data := scanner.Text()
if len(data) < 6 { // ignore blank line or wrong format if len(data) < dataPrefixLength { // ignore blank line or wrong format
continue continue
} }
if data[:6] != "data: " && data[:6] != "[DONE]" { if data[:dataPrefixLength] != dataPrefix && data[:dataPrefixLength] != done {
continue continue
} }
dataChan <- data if strings.HasPrefix(data[dataPrefixLength:], done) {
data = data[6:] dataChan <- data
if !strings.HasPrefix(data, "[DONE]") { continue
switch relayMode { }
case relaymode.ChatCompletions: switch relayMode {
var streamResponse ChatCompletionsStreamResponse case relaymode.ChatCompletions:
err := json.Unmarshal([]byte(data), &streamResponse) var streamResponse ChatCompletionsStreamResponse
if err != nil { err := json.Unmarshal([]byte(data[dataPrefixLength:]), &streamResponse)
logger.SysError("error unmarshalling stream response: " + err.Error()) if err != nil {
continue // just ignore the error logger.SysError("error unmarshalling stream response: " + err.Error())
} dataChan <- data // if error happened, pass the data to client
for _, choice := range streamResponse.Choices { continue // just ignore the error
responseText += conv.AsString(choice.Delta.Content) }
} if len(streamResponse.Choices) == 0 {
if streamResponse.Usage != nil { // but for empty choice, we should not pass it to client, this is for azure
usage = streamResponse.Usage continue // just ignore empty choice
} }
case relaymode.Completions: dataChan <- data
var streamResponse CompletionsStreamResponse for _, choice := range streamResponse.Choices {
err := json.Unmarshal([]byte(data), &streamResponse) responseText += conv.AsString(choice.Delta.Content)
if err != nil { }
logger.SysError("error unmarshalling stream response: " + err.Error()) if streamResponse.Usage != nil {
continue usage = streamResponse.Usage
} }
for _, choice := range streamResponse.Choices { case relaymode.Completions:
responseText += choice.Text dataChan <- data
} var streamResponse CompletionsStreamResponse
err := json.Unmarshal([]byte(data[dataPrefixLength:]), &streamResponse)
if err != nil {
logger.SysError("error unmarshalling stream response: " + err.Error())
continue
}
for _, choice := range streamResponse.Choices {
responseText += choice.Text
} }
} }
} }

View File

@@ -147,11 +147,13 @@ var ModelRatio = map[string]float64{
"mistral-medium-latest": 2.7 / 1000 * USD, "mistral-medium-latest": 2.7 / 1000 * USD,
"mistral-large-latest": 8.0 / 1000 * USD, "mistral-large-latest": 8.0 / 1000 * USD,
"mistral-embed": 0.1 / 1000 * USD, "mistral-embed": 0.1 / 1000 * USD,
// https://wow.groq.com/ // https://wow.groq.com/#:~:text=inquiries%C2%A0here.-,Model,-Current%20Speed
"llama2-70b-4096": 0.7 / 1000 * USD, "llama3-70b-8192": 0.59 / 1000 * USD,
"llama2-7b-2048": 0.1 / 1000 * USD,
"mixtral-8x7b-32768": 0.27 / 1000 * USD, "mixtral-8x7b-32768": 0.27 / 1000 * USD,
"llama3-8b-8192": 0.05 / 1000 * USD,
"gemma-7b-it": 0.1 / 1000 * USD, "gemma-7b-it": 0.1 / 1000 * USD,
"llama2-70b-4096": 0.64 / 1000 * USD,
"llama2-7b-2048": 0.1 / 1000 * USD,
// https://platform.lingyiwanwu.com/docs#-计费单元 // https://platform.lingyiwanwu.com/docs#-计费单元
"yi-34b-chat-0205": 2.5 / 1000 * RMB, "yi-34b-chat-0205": 2.5 / 1000 * RMB,
"yi-34b-chat-200k": 12.0 / 1000 * RMB, "yi-34b-chat-200k": 12.0 / 1000 * RMB,
@@ -258,7 +260,7 @@ func GetCompletionRatio(name string) float64 {
return 4.0 / 3.0 return 4.0 / 3.0
} }
if strings.HasPrefix(name, "gpt-4") { if strings.HasPrefix(name, "gpt-4") {
if strings.HasPrefix(name, "gpt-4-turbo") { if strings.HasPrefix(name, "gpt-4-turbo") || strings.HasSuffix(name, "preview") {
return 3 return 3
} }
return 2 return 2
@@ -277,7 +279,11 @@ func GetCompletionRatio(name string) float64 {
} }
switch name { switch name {
case "llama2-70b-4096": case "llama2-70b-4096":
return 0.8 / 0.7 return 0.8 / 0.64
case "llama3-8b-8192":
return 2
case "llama3-70b-8192":
return 0.79 / 0.59
} }
return 1 return 1
} }

View File

@@ -13,6 +13,7 @@ const COPY_OPTIONS = [
]; ];
const OPEN_LINK_OPTIONS = [ const OPEN_LINK_OPTIONS = [
{ key: 'next', text: 'ChatGPT Next Web', value: 'next' },
{ key: 'ama', text: 'BotGem', value: 'ama' }, { key: 'ama', text: 'BotGem', value: 'ama' },
{ key: 'opencat', text: 'OpenCat', value: 'opencat' }, { key: 'opencat', text: 'OpenCat', value: 'opencat' },
]; ];

View File

@@ -57,7 +57,8 @@ const EditChannel = () => {
const [config, setConfig] = useState({ const [config, setConfig] = useState({
region: '', region: '',
sk: '', sk: '',
ak: '' ak: '',
user_id: ''
}); });
const handleInputChange = (e, { name, value }) => { const handleInputChange = (e, { name, value }) => {
setInputs((inputs) => ({ ...inputs, [name]: value })); setInputs((inputs) => ({ ...inputs, [name]: value }));
@@ -156,8 +157,10 @@ const EditChannel = () => {
}, []); }, []);
const submit = async () => { const submit = async () => {
if (inputs.key === "") { if (inputs.key === '') {
inputs.key = `${config.ak}|${config.sk}|${config.region}`; if (config.ak !== '' && config.sk !== '' && config.region !== '') {
inputs.key = `${config.ak}|${config.sk}|${config.region}`;
}
} }
if (!isEdit && (inputs.name === '' || inputs.key === '')) { if (!isEdit && (inputs.name === '' || inputs.key === '')) {
showInfo('请填写渠道名称和渠道密钥!'); showInfo('请填写渠道名称和渠道密钥!');
@@ -442,6 +445,18 @@ const EditChannel = () => {
</Form.Field> </Form.Field>
) )
} }
{
inputs.type === 34 && (
<Form.Input
label='User ID'
name='user_id'
required
placeholder={'生成该密钥的用户 ID'}
onChange={handleConfigChange}
value={config.user_id}
autoComplete=''
/>)
}
{ {
inputs.type !== 33 && (batch ? <Form.Field> inputs.type !== 33 && (batch ? <Form.Field>
<Form.TextArea <Form.TextArea