Compare commits
7 Commits
v0.6.6-alp
...
v0.6.6-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d14e4aa01b | ||
|
|
541182102e | ||
|
|
b2679cca65 | ||
|
|
8572fac7a2 | ||
|
|
a2a00dfbc3 | ||
|
|
129282f4a9 | ||
|
|
a873cbd392 |
@@ -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)
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
2
main.go
2
main.go
@@ -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 {
|
||||||
|
|||||||
@@ -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",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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' },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user