mirror of
https://github.com/songquanpeng/one-api.git
synced 2026-04-23 10:14:33 +08:00
Compare commits
15 Commits
0255696b2f
...
d159b6c4a2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d159b6c4a2 | ||
|
|
2552c68249 | ||
|
|
5c81e40612 | ||
|
|
0d5318b1b7 | ||
|
|
db65db2807 | ||
|
|
e0b7e6a9e2 | ||
|
|
27c2abe80f | ||
|
|
2c867251b5 | ||
|
|
108111ebd3 | ||
|
|
293ba93ad6 | ||
|
|
faced40d5b | ||
|
|
2ae9997f29 | ||
|
|
e146b14d46 | ||
|
|
e19045f925 | ||
|
|
cc367dd95b |
11
.github/workflows/docker-image.yml
vendored
11
.github/workflows/docker-image.yml
vendored
@@ -32,10 +32,10 @@ jobs:
|
||||
git describe --tags > VERSION
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
@@ -62,8 +62,9 @@ jobs:
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
# platforms: linux/amd64,linux/arm64
|
||||
platforms: linux/amd64 # TODO disable arm64 for now, because it cause error
|
||||
platforms: ${{ contains(github.ref, 'alpha') && 'linux/amd64' || 'linux/amd64,linux/arm64' }}
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
TARGETARCH=${{ startsWith(matrix.platform, 'linux/arm64') && 'arm64' || 'amd64' }}
|
||||
1
.github/workflows/linux-release.yml
vendored
1
.github/workflows/linux-release.yml
vendored
@@ -7,6 +7,7 @@ on:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
- '!*-alpha*'
|
||||
- '!*-preview*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
name:
|
||||
|
||||
1
.github/workflows/macos-release.yml
vendored
1
.github/workflows/macos-release.yml
vendored
@@ -7,6 +7,7 @@ on:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
- '!*-alpha*'
|
||||
- '!*-preview*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
name:
|
||||
|
||||
1
.github/workflows/windows-release.yml
vendored
1
.github/workflows/windows-release.yml
vendored
@@ -7,6 +7,7 @@ on:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
- '!*-alpha*'
|
||||
- '!*-preview*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
name:
|
||||
|
||||
29
Dockerfile
29
Dockerfile
@@ -9,23 +9,23 @@ RUN npm install --prefix /web/default & \
|
||||
npm install --prefix /web/air & \
|
||||
wait
|
||||
|
||||
RUN DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat /web/default/VERSION) npm run build --prefix /web/default & \
|
||||
DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat /web/berry/VERSION) npm run build --prefix /web/berry & \
|
||||
DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat /web/air/VERSION) npm run build --prefix /web/air & \
|
||||
RUN DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat ./VERSION) npm run build --prefix /web/default & \
|
||||
DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat ./VERSION) npm run build --prefix /web/berry & \
|
||||
DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat ./VERSION) npm run build --prefix /web/air & \
|
||||
wait
|
||||
|
||||
FROM golang AS builder2
|
||||
FROM golang:alpine AS builder2
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
sqlite3 libsqlite3-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
RUN apk add --no-cache \
|
||||
gcc \
|
||||
musl-dev \
|
||||
sqlite-dev \
|
||||
build-base
|
||||
|
||||
ENV GO111MODULE=on \
|
||||
CGO_ENABLED=1 \
|
||||
GOOS=linux \
|
||||
CGO_CFLAGS="-I/usr/include" \
|
||||
CGO_LDFLAGS="-L/usr/lib"
|
||||
GOARCH=$TARGETARCH
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
@@ -35,14 +35,11 @@ RUN go mod download
|
||||
COPY . .
|
||||
COPY --from=builder /web/build ./web/build
|
||||
|
||||
RUN go build -trimpath -ldflags "-s -w -X 'github.com/songquanpeng/one-api/common.Version=$(cat VERSION)'" -o one-api
|
||||
RUN go build -trimpath -ldflags "-s -w -X 'github.com/songquanpeng/one-api/common.Version=$(cat VERSION)' -linkmode external -extldflags '-static'" -o one-api
|
||||
|
||||
# Final runtime image
|
||||
FROM ubuntu:22.04
|
||||
FROM alpine:latest
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
ca-certificates tzdata bash \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
RUN apk add --no-cache ca-certificates tzdata
|
||||
|
||||
COPY --from=builder2 /build/one-api /
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@ func ShouldDisableChannel(err *model.Error, statusCode int) bool {
|
||||
strings.Contains(lowerMessage, "balance") ||
|
||||
strings.Contains(lowerMessage, "permission denied") ||
|
||||
strings.Contains(lowerMessage, "organization has been restricted") || // groq
|
||||
strings.Contains(lowerMessage, "api key not valid") || // gemini
|
||||
strings.Contains(lowerMessage, "api key expired") || // gemini
|
||||
strings.Contains(lowerMessage, "已欠费") {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -2,5 +2,5 @@ package deepseek
|
||||
|
||||
var ModelList = []string{
|
||||
"deepseek-chat",
|
||||
"deepseek-coder",
|
||||
"deepseek-reasoner",
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/songquanpeng/one-api/common/helper"
|
||||
channelhelper "github.com/songquanpeng/one-api/relay/adaptor"
|
||||
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
||||
@@ -29,6 +30,8 @@ func (a *Adaptor) GetRequestURL(meta *meta.Meta) (string, error) {
|
||||
"gemini-2.0-flash-thinking-exp",
|
||||
"gemini-2.0-flash-thinking-exp-01-21":
|
||||
defaultVersion = "v1beta"
|
||||
default:
|
||||
defaultVersion = "v1beta"
|
||||
}
|
||||
|
||||
version := helper.AssignOrDefault(meta.Config.APIVersion, defaultVersion)
|
||||
|
||||
@@ -3,7 +3,6 @@ package groq
|
||||
// https://console.groq.com/docs/models
|
||||
|
||||
var ModelList = []string{
|
||||
"gemma-7b-it",
|
||||
"gemma2-9b-it",
|
||||
"llama-3.1-70b-versatile",
|
||||
"llama-3.1-8b-instant",
|
||||
@@ -23,4 +22,6 @@ var ModelList = []string{
|
||||
"distil-whisper-large-v3-en",
|
||||
"whisper-large-v3",
|
||||
"whisper-large-v3-turbo",
|
||||
"deepseek-r1-distill-llama-70b-specdec",
|
||||
"deepseek-r1-distill-llama-70b",
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export const CHANNEL_OPTIONS = [
|
||||
{ key: 24, text: 'Google Gemini', value: 24, color: 'orange' },
|
||||
{ key: 28, text: 'Mistral AI', value: 28, color: 'orange' },
|
||||
{ key: 41, text: 'Novita', value: 41, color: 'purple' },
|
||||
{ key: 40, text: '字节跳动豆包', value: 40, color: 'blue' },
|
||||
{key: 40, text: '火山引擎', value: 40, color: 'blue'},
|
||||
{ key: 15, text: '百度文心千帆', value: 15, color: 'blue' },
|
||||
{ key: 17, text: '阿里通义千问', value: 17, color: 'orange' },
|
||||
{ key: 18, text: '讯飞星火认知', value: 18, color: 'blue' },
|
||||
|
||||
@@ -49,7 +49,7 @@ export const CHANNEL_OPTIONS = {
|
||||
},
|
||||
40: {
|
||||
key: 40,
|
||||
text: '字节跳动豆包',
|
||||
text: '火山引擎',
|
||||
value: 40,
|
||||
color: 'primary'
|
||||
},
|
||||
|
||||
@@ -8,11 +8,12 @@ import {
|
||||
IconKey,
|
||||
IconGardenCart,
|
||||
IconUser,
|
||||
IconUserScan
|
||||
IconUserScan,
|
||||
IconMessageCircle
|
||||
} from '@tabler/icons-react';
|
||||
|
||||
// constant
|
||||
const icons = { IconDashboard, IconSitemap, IconArticle, IconCoin, IconAdjustments, IconKey, IconGardenCart, IconUser, IconUserScan };
|
||||
const icons = { IconDashboard, IconSitemap, IconArticle, IconCoin, IconAdjustments, IconKey, IconGardenCart, IconUser, IconUserScan,IconMessageCircle };
|
||||
|
||||
// ==============================|| DASHBOARD MENU ITEMS ||============================== //
|
||||
|
||||
@@ -29,6 +30,15 @@ const panel = {
|
||||
breadcrumbs: false,
|
||||
isAdmin: false
|
||||
},
|
||||
{
|
||||
id: 'chat',
|
||||
title: '聊天',
|
||||
type: 'item',
|
||||
url: '/panel/chat',
|
||||
icon: icons.IconMessageCircle,
|
||||
breadcrumbs: false,
|
||||
isAdmin: false
|
||||
},
|
||||
{
|
||||
id: 'channel',
|
||||
title: '渠道',
|
||||
|
||||
@@ -3,6 +3,7 @@ import { lazy } from 'react';
|
||||
// project imports
|
||||
import MainLayout from 'layout/MainLayout';
|
||||
import Loadable from 'ui-component/Loadable';
|
||||
import Chat from "../views/Chat";
|
||||
|
||||
const Channel = Loadable(lazy(() => import('views/Channel')));
|
||||
const Log = Loadable(lazy(() => import('views/Log')));
|
||||
@@ -31,6 +32,10 @@ const MainRoutes = {
|
||||
path: 'dashboard',
|
||||
element: <Dashboard />
|
||||
},
|
||||
{
|
||||
path: 'chat',
|
||||
element: <Chat />
|
||||
},
|
||||
{
|
||||
path: 'channel',
|
||||
element: <Channel />
|
||||
|
||||
28
web/berry/src/views/Chat/index.css
Normal file
28
web/berry/src/views/Chat/index.css
Normal file
@@ -0,0 +1,28 @@
|
||||
.MuiContainer-root {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
height: calc(100% - 1px);
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
.css-1xnbu7n-MuiContainer-root {
|
||||
/* 如果有特定样式,请在此处添加 */
|
||||
}
|
||||
|
||||
.css-9d4wr9 {
|
||||
background-color: #eef2f6;
|
||||
width: 100%;
|
||||
min-height: calc(100vh - 88px);
|
||||
flex-grow: 1;
|
||||
padding: 0;
|
||||
margin-top: 83.746px;
|
||||
margin-right: 0;
|
||||
border-radius: 12px;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
transition: margin 225ms cubic-bezier(0.0, 0, 0.2, 1) 0ms;
|
||||
}
|
||||
|
||||
.chat-container {
|
||||
height: 100%;
|
||||
}
|
||||
69
web/berry/src/views/Chat/index.js
Normal file
69
web/berry/src/views/Chat/index.js
Normal file
@@ -0,0 +1,69 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { API } from "../../utils/api";
|
||||
import "./index.css";
|
||||
|
||||
const useIsSmallScreen = () => {
|
||||
const [isSmallScreen, setIsSmallScreen] = useState(window.innerWidth <= 768);
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
setIsSmallScreen(window.innerWidth <= 768);
|
||||
};
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return isSmallScreen;
|
||||
};
|
||||
|
||||
const Chat = () => {
|
||||
const [chatUrl, setChatUrl] = useState("");
|
||||
const [loading, setLoading] = useState(true);
|
||||
// const isSmallScreen = useIsSmallScreen();
|
||||
|
||||
const loadTokens = async () => {
|
||||
try {
|
||||
const res = await API.get(`/api/token/`);
|
||||
const siteInfo = JSON.parse(localStorage.getItem('siteInfo'));
|
||||
if (!siteInfo) {
|
||||
console.error("siteInfo not found in localStorage");
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
// const url = `https://like.chatapi.asia/#/?settings={"key":"sk-xxx","url":"https://chat.chatapi.asia"}`;
|
||||
const serverAddress = siteInfo.server_address;
|
||||
const key = res.data.data[0].key;
|
||||
const url = `${siteInfo.chat_link}/#/?settings={"key":"sk-${key}","url":"${serverAddress}"}`;
|
||||
|
||||
setChatUrl(url);
|
||||
} catch (error) {
|
||||
console.error("Error loading tokens:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadTokens();
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return <div className="chat-container">Loading...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="chat-container">
|
||||
<iframe
|
||||
src={chatUrl}
|
||||
style={{ height: '100%', width: '100%', padding: 0, border: 'none' }}
|
||||
title="Chat"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Chat;
|
||||
@@ -7,7 +7,7 @@ export const CHANNEL_OPTIONS = [
|
||||
{ key: 24, text: 'Google Gemini', value: 24, color: 'orange' },
|
||||
{ key: 28, text: 'Mistral AI', value: 28, color: 'orange' },
|
||||
{ key: 41, text: 'Novita', value: 41, color: 'purple' },
|
||||
{ key: 40, text: '字节跳动豆包', value: 40, color: 'blue' },
|
||||
{key: 40, text: '火山引擎', value: 40, color: 'blue'},
|
||||
{ key: 15, text: '百度文心千帆', value: 15, color: 'blue' },
|
||||
{ key: 17, text: '阿里通义千问', value: 17, color: 'orange' },
|
||||
{ key: 18, text: '讯飞星火认知', value: 18, color: 'blue' },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Card, Grid} from 'semantic-ui-react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Card, Grid } from 'semantic-ui-react';
|
||||
import {
|
||||
Bar,
|
||||
BarChart,
|
||||
@@ -242,7 +242,7 @@ const Dashboard = () => {
|
||||
<Card.Content>
|
||||
<Card.Header>
|
||||
{t('dashboard.charts.requests.title')}
|
||||
{/* <span className='stat-value'>{summaryData.todayRequests}</span> */}
|
||||
{/* <span className='stat-value'>{summaryData.todayRequests}</span> */}
|
||||
</Card.Header>
|
||||
<div className='chart-container'>
|
||||
<ResponsiveContainer
|
||||
@@ -271,7 +271,9 @@ const Dashboard = () => {
|
||||
t('dashboard.charts.requests.tooltip'),
|
||||
]}
|
||||
labelFormatter={(label) =>
|
||||
`${t('dashboard.statistics.tooltip.date')}: ${formatDate(label)}`
|
||||
`${t(
|
||||
'dashboard.statistics.tooltip.date'
|
||||
)}: ${formatDate(label)}`
|
||||
}
|
||||
/>
|
||||
<Line
|
||||
@@ -294,7 +296,7 @@ const Dashboard = () => {
|
||||
<Card.Content>
|
||||
<Card.Header>
|
||||
{t('dashboard.charts.quota.title')}
|
||||
{/* <span className='stat-value'>
|
||||
{/* <span className='stat-value'>
|
||||
${summaryData.todayQuota.toFixed(3)}
|
||||
</span> */}
|
||||
</Card.Header>
|
||||
@@ -321,11 +323,13 @@ const Dashboard = () => {
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
|
||||
}}
|
||||
formatter={(value) => [
|
||||
value,
|
||||
value.toFixed(6),
|
||||
t('dashboard.charts.quota.tooltip'),
|
||||
]}
|
||||
labelFormatter={(label) =>
|
||||
`${t('dashboard.statistics.tooltip.date')}: ${formatDate(label)}`
|
||||
`${t(
|
||||
'dashboard.statistics.tooltip.date'
|
||||
)}: ${formatDate(label)}`
|
||||
}
|
||||
/>
|
||||
<Line
|
||||
@@ -348,7 +352,7 @@ const Dashboard = () => {
|
||||
<Card.Content>
|
||||
<Card.Header>
|
||||
{t('dashboard.charts.tokens.title')}
|
||||
{/* <span className='stat-value'>{summaryData.todayTokens}</span> */}
|
||||
{/* <span className='stat-value'>{summaryData.todayTokens}</span> */}
|
||||
</Card.Header>
|
||||
<div className='chart-container'>
|
||||
<ResponsiveContainer
|
||||
@@ -377,7 +381,9 @@ const Dashboard = () => {
|
||||
t('dashboard.charts.tokens.tooltip'),
|
||||
]}
|
||||
labelFormatter={(label) =>
|
||||
`${t('dashboard.statistics.tooltip.date')}: ${formatDate(label)}`
|
||||
`${t(
|
||||
'dashboard.statistics.tooltip.date'
|
||||
)}: ${formatDate(label)}`
|
||||
}
|
||||
/>
|
||||
<Line
|
||||
@@ -422,7 +428,9 @@ const Dashboard = () => {
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
|
||||
}}
|
||||
labelFormatter={(label) =>
|
||||
`${t('dashboard.statistics.tooltip.date')}: ${formatDate(label)}`
|
||||
`${t('dashboard.statistics.tooltip.date')}: ${formatDate(
|
||||
label
|
||||
)}`
|
||||
}
|
||||
/>
|
||||
<Legend
|
||||
|
||||
Reference in New Issue
Block a user