├── .dockerignore ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── adapter ├── adapter.go ├── azure │ ├── chat.go │ ├── image.go │ ├── processor.go │ ├── struct.go │ └── types.go ├── baichuan │ ├── chat.go │ ├── processor.go │ ├── struct.go │ └── types.go ├── bing │ ├── chat.go │ ├── struct.go │ └── types.go ├── chatgpt │ ├── chat.go │ ├── image.go │ ├── processor.go │ ├── struct.go │ ├── test.go │ └── types.go ├── claude │ ├── chat.go │ ├── struct.go │ └── types.go ├── dashscope │ ├── chat.go │ ├── struct.go │ └── types.go ├── hunyuan │ ├── chat.go │ ├── sdk.go │ └── struct.go ├── midjourney │ ├── api.go │ ├── chat.go │ ├── expose.go │ ├── storage.go │ ├── struct.go │ └── types.go ├── oneapi │ ├── chat.go │ ├── processor.go │ ├── struct.go │ └── types.go ├── palm2 │ ├── chat.go │ ├── formatter.go │ ├── struct.go │ └── types.go ├── request.go ├── router.go ├── skylark │ ├── chat.go │ ├── formatter.go │ └── struct.go ├── slack │ ├── chat.go │ └── struct.go ├── sparkdesk │ ├── chat.go │ ├── struct.go │ └── types.go ├── zhinao │ ├── chat.go │ ├── processor.go │ ├── struct.go │ └── types.go └── zhipuai │ ├── chat.go │ ├── struct.go │ └── types.go ├── addition ├── article │ ├── api.go │ ├── data │ │ └── .gitkeep │ ├── generate.go │ ├── template.docx │ └── utils.go ├── card │ ├── .gitignore │ ├── card.go │ ├── card.php │ ├── error.php │ ├── favicon.ico │ └── utils.php ├── generation │ ├── api.go │ ├── build.go │ ├── data │ │ └── .gitkeep │ ├── generate.go │ └── prompt.go ├── router.go └── web │ ├── call.go │ ├── duckduckgo.go │ ├── parser.go │ ├── search.go │ ├── utils.go │ └── webpilot.go ├── admin ├── analysis.go ├── controller.go ├── format.go ├── instance.go ├── invitation.go ├── logger.go ├── market.go ├── redeem.go ├── router.go ├── statistic.go ├── types.go └── user.go ├── app ├── .env.deeptrain ├── .eslintrc.cjs ├── .gitignore ├── .prettierrc.json ├── components.json ├── index.html ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── postcss.config.js ├── public │ ├── favicon.ico │ ├── icons │ │ ├── 360gpt.png │ │ ├── baichuan.png │ │ ├── chatglm.png │ │ ├── claude.png │ │ ├── claude100k.png │ │ ├── dalle.jpeg │ │ ├── gemini.jpeg │ │ ├── gpt35turbo.png │ │ ├── gpt35turbo16k.webp │ │ ├── gpt4.png │ │ ├── gpt432k.webp │ │ ├── gpt4dalle.png │ │ ├── gpt4v.png │ │ ├── hunyuan.png │ │ ├── llama2.webp │ │ ├── llamacode.webp │ │ ├── midjourney.jpg │ │ ├── newbing.jpg │ │ ├── palm2.webp │ │ ├── skylark.jpg │ │ ├── sparkdesk.jpg │ │ ├── stablediffusion.jpeg │ │ └── tongyi.png │ ├── logo.png │ ├── robots.txt │ ├── service.js │ ├── service │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ └── favicon-64x64.png │ ├── site.webmanifest │ ├── source │ │ └── qq.jpg │ └── workbox.js ├── qodana.yaml ├── src-tauri │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── build.rs │ ├── icons │ │ ├── 128x128.png │ │ ├── 128x128@2x.png │ │ ├── 32x32.png │ │ ├── Square107x107Logo.png │ │ ├── Square142x142Logo.png │ │ ├── Square150x150Logo.png │ │ ├── Square284x284Logo.png │ │ ├── Square30x30Logo.png │ │ ├── Square310x310Logo.png │ │ ├── Square44x44Logo.png │ │ ├── Square71x71Logo.png │ │ ├── Square89x89Logo.png │ │ ├── StoreLogo.png │ │ ├── icon.icns │ │ ├── icon.ico │ │ └── icon.png │ ├── src │ │ └── main.rs │ └── tauri.conf.json ├── src │ ├── App.tsx │ ├── admin │ │ ├── api │ │ │ ├── channel.ts │ │ │ ├── charge.ts │ │ │ ├── chart.ts │ │ │ ├── info.ts │ │ │ ├── logger.ts │ │ │ ├── market.ts │ │ │ ├── plan.ts │ │ │ └── system.ts │ │ ├── channel.ts │ │ ├── charge.ts │ │ ├── colors.ts │ │ ├── market.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── api │ │ ├── addition.ts │ │ ├── auth.ts │ │ ├── broadcast.ts │ │ ├── connection.ts │ │ ├── conversation.ts │ │ ├── file.ts │ │ ├── generation.ts │ │ ├── history.ts │ │ ├── invitation.ts │ │ ├── manager.ts │ │ ├── quota.ts │ │ ├── redeem.ts │ │ ├── sharing.ts │ │ ├── types.ts │ │ └── v1.ts │ ├── assets │ │ ├── admin │ │ │ ├── all.less │ │ │ ├── broadcast.less │ │ │ ├── channel.less │ │ │ ├── charge.less │ │ │ ├── dashboard.less │ │ │ ├── logger.less │ │ │ ├── management.less │ │ │ ├── market.less │ │ │ ├── menu.less │ │ │ ├── subscription.less │ │ │ └── system.less │ │ ├── common │ │ │ ├── 404.less │ │ │ ├── editor.less │ │ │ ├── file.less │ │ │ └── loader.less │ │ ├── globals.less │ │ ├── main.less │ │ ├── markdown │ │ │ ├── all.less │ │ │ ├── highlight.less │ │ │ ├── style.less │ │ │ └── theme.less │ │ ├── pages │ │ │ ├── api.less │ │ │ ├── article.less │ │ │ ├── auth.less │ │ │ ├── chat.less │ │ │ ├── generation.less │ │ │ ├── home.less │ │ │ ├── mask.less │ │ │ ├── navbar.less │ │ │ ├── package.less │ │ │ ├── quota.less │ │ │ ├── settings.less │ │ │ ├── share-manager.less │ │ │ ├── sharing.less │ │ │ └── subscription.less │ │ └── ui.less │ ├── components │ │ ├── Avatar.tsx │ │ ├── Broadcast.tsx │ │ ├── EditorProvider.tsx │ │ ├── ErrorBoundary.tsx │ │ ├── FileProvider.tsx │ │ ├── I18nProvider.tsx │ │ ├── Loader.tsx │ │ ├── Markdown.tsx │ │ ├── Message.tsx │ │ ├── OperationAction.tsx │ │ ├── Paragraph.tsx │ │ ├── PopupDialog.tsx │ │ ├── ProjectLink.tsx │ │ ├── ReloadService.tsx │ │ ├── Require.tsx │ │ ├── SelectGroup.tsx │ │ ├── ThemeProvider.tsx │ │ ├── TickButton.tsx │ │ ├── Tips.tsx │ │ ├── admin │ │ │ ├── ChannelSettings.tsx │ │ │ ├── ChargeWidget.tsx │ │ │ ├── ChartBox.tsx │ │ │ ├── InfoBox.tsx │ │ │ ├── InvitationTable.tsx │ │ │ ├── MenuBar.tsx │ │ │ ├── RedeemTable.tsx │ │ │ ├── UserTable.tsx │ │ │ └── assemblies │ │ │ │ ├── BillingChart.tsx │ │ │ │ ├── BroadcastTable.tsx │ │ │ │ ├── ChannelEditor.tsx │ │ │ │ ├── ChannelTable.tsx │ │ │ │ ├── ErrorChart.tsx │ │ │ │ ├── ModelChart.tsx │ │ │ │ ├── ModelUsageChart.tsx │ │ │ │ ├── RequestChart.tsx │ │ │ │ └── UserTypeChart.tsx │ │ ├── app │ │ │ ├── Announcement.tsx │ │ │ ├── AppProvider.tsx │ │ │ ├── MenuBar.tsx │ │ │ └── NavBar.tsx │ │ ├── home │ │ │ ├── ChatInterface.tsx │ │ │ ├── ChatSpace.tsx │ │ │ ├── ChatWrapper.tsx │ │ │ ├── ConversationSegment.tsx │ │ │ ├── ModelFinder.tsx │ │ │ ├── ModelMarket.tsx │ │ │ ├── SideBar.tsx │ │ │ ├── assemblies │ │ │ │ ├── ActionButton.tsx │ │ │ │ ├── ChatAction.tsx │ │ │ │ ├── ChatInput.tsx │ │ │ │ └── ScrollAction.tsx │ │ │ └── subscription │ │ │ │ ├── BuyDialog.tsx │ │ │ │ └── SubscriptionUsage.tsx │ │ ├── plugins │ │ │ ├── file.tsx │ │ │ └── progress.tsx │ │ ├── ui │ │ │ ├── alert-dialog.tsx │ │ │ ├── alert.tsx │ │ │ ├── badge.tsx │ │ │ ├── button.tsx │ │ │ ├── card.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── combo-box.tsx │ │ │ ├── command.tsx │ │ │ ├── context-menu.tsx │ │ │ ├── dialog.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── icons │ │ │ │ └── Github.tsx │ │ │ ├── input.tsx │ │ │ ├── label.tsx │ │ │ ├── lib │ │ │ │ └── utils.ts │ │ │ ├── multi-combobox.tsx │ │ │ ├── number-input.tsx │ │ │ ├── popover.tsx │ │ │ ├── progress.tsx │ │ │ ├── radio-group.tsx │ │ │ ├── scroll-area.tsx │ │ │ ├── select.tsx │ │ │ ├── separator.tsx │ │ │ ├── sonner.tsx │ │ │ ├── switch.tsx │ │ │ ├── table.tsx │ │ │ ├── tabs.tsx │ │ │ ├── textarea.tsx │ │ │ ├── toast.tsx │ │ │ ├── toaster.tsx │ │ │ ├── toggle-group.tsx │ │ │ ├── toggle.tsx │ │ │ ├── tooltip.tsx │ │ │ └── use-toast.ts │ │ └── utils │ │ │ └── Icon.tsx │ ├── conf │ │ ├── api.ts │ │ ├── deeptrain.ts │ │ ├── env.ts │ │ ├── index.ts │ │ ├── model.ts │ │ ├── storage.ts │ │ └── subscription.tsx │ ├── dialogs │ │ ├── ApikeyDialog.tsx │ │ ├── InvitationDialog.tsx │ │ ├── MaskDialog.tsx │ │ ├── PackageDialog.tsx │ │ ├── QuotaDialog.tsx │ │ ├── SettingsDialog.tsx │ │ ├── ShareManagementDialog.tsx │ │ ├── SubscriptionDialog.tsx │ │ └── index.tsx │ ├── events │ │ ├── announcement.ts │ │ ├── chat.ts │ │ ├── connection.ts │ │ ├── market.ts │ │ ├── mask.ts │ │ ├── model.ts │ │ ├── sharing.ts │ │ ├── spinner.ts │ │ ├── struct.ts │ │ └── theme.ts │ ├── i18n.ts │ ├── main.tsx │ ├── masks │ │ ├── prompts.ts │ │ └── types.ts │ ├── resources │ │ └── i18n │ │ │ ├── cn.json │ │ │ ├── en.json │ │ │ ├── ja.json │ │ │ └── ru.json │ ├── router.tsx │ ├── routes │ │ ├── Admin.tsx │ │ ├── Article.tsx │ │ ├── Auth.tsx │ │ ├── Forgot.tsx │ │ ├── Generation.tsx │ │ ├── Home.tsx │ │ ├── NotFound.tsx │ │ ├── Register.tsx │ │ ├── Sharing.tsx │ │ └── admin │ │ │ ├── Broadcast.tsx │ │ │ ├── Channel.tsx │ │ │ ├── Charge.tsx │ │ │ ├── DashBoard.tsx │ │ │ ├── Logger.tsx │ │ │ ├── Market.tsx │ │ │ ├── Subscription.tsx │ │ │ ├── System.tsx │ │ │ └── Users.tsx │ ├── spinner.tsx │ ├── store │ │ ├── api.ts │ │ ├── auth.ts │ │ ├── chat.ts │ │ ├── globals.ts │ │ ├── index.ts │ │ ├── invitation.ts │ │ ├── menu.ts │ │ ├── package.ts │ │ ├── quota.ts │ │ ├── settings.ts │ │ ├── sharing.ts │ │ ├── subscription.ts │ │ └── utils.ts │ ├── translator │ │ ├── adapter.ts │ │ ├── index.ts │ │ ├── io.ts │ │ └── translator.ts │ ├── types │ │ ├── performance.d.ts │ │ ├── service.d.ts │ │ └── ui.d.ts │ ├── utils │ │ ├── app.ts │ │ ├── base.ts │ │ ├── dev.ts │ │ ├── device.ts │ │ ├── dom.ts │ │ ├── form.ts │ │ ├── hook.ts │ │ ├── loader.tsx │ │ ├── memory.ts │ │ ├── path.ts │ │ └── processor.ts │ └── vite-env.d.ts ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts ├── auth ├── analysis.go ├── apikey.go ├── auth.go ├── call.go ├── cert.go ├── controller.go ├── invitation.go ├── package.go ├── payment.go ├── quota.go ├── redeem.go ├── router.go ├── rule.go ├── struct.go ├── subscription.go ├── usage.go └── validators.go ├── channel ├── channel.go ├── charge.go ├── controller.go ├── manager.go ├── plan.go ├── router.go ├── sequence.go ├── system.go ├── ticker.go ├── types.go └── worker.go ├── cli ├── admin.go ├── exec.go ├── filter.go ├── help.go ├── invite.go ├── parser.go └── token.go ├── config.example.yaml ├── connection ├── cache.go ├── database.go └── worker.go ├── docker-compose.yaml ├── globals ├── constant.go ├── interface.go ├── logger.go ├── tools.go ├── types.go ├── usage.go └── variables.go ├── go.mod ├── go.sum ├── main.go ├── manager ├── broadcast │ ├── controller.go │ ├── manage.go │ ├── router.go │ ├── types.go │ └── view.go ├── cache.go ├── chat.go ├── chat_completions.go ├── completions.go ├── connection.go ├── conversation │ ├── api.go │ ├── conversation.go │ ├── mask.go │ ├── router.go │ ├── shared.go │ └── storage.go ├── images.go ├── manager.go ├── relay.go ├── router.go ├── types.go └── usage.go ├── middleware ├── auth.go ├── builtins.go ├── cors.go ├── middleware.go └── throttle.go ├── migration ├── 3.6.sql └── 3.8.sql ├── nginx.conf ├── screenshot ├── admin.png ├── channel.png ├── charge.png ├── code.png ├── generation.png ├── landspace.png ├── latex.jpg ├── shop.png └── subscription.png └── utils ├── base.go ├── buffer.go ├── cache.go ├── char.go ├── compress.go ├── config.go ├── ctx.go ├── encrypt.go ├── fs.go ├── image.go ├── net.go ├── smtp.go ├── sse.go ├── templates └── code.html ├── tokenizer.go └── websocket.go /.dockerignore: -------------------------------------------------------------------------------- 1 | app/node_modules 2 | app/src-tauri 3 | app/.idea 4 | app/.vscode 5 | app/dist 6 | app/dev-dist 7 | app/dist-ssr 8 | app/target 9 | app/tauri.conf.json 10 | app/tauri.js 11 | 12 | .vscode 13 | .idea 14 | config.yaml 15 | config.dev.yaml 16 | 17 | addition/generation/data/* 18 | !addition/generation/data/.gitkeep 19 | 20 | addition/article/data/* 21 | !addition/article/data/.gitkeep 22 | sdk 23 | logs 24 | 25 | chat 26 | chat.exe 27 | 28 | # for reverse engine 29 | reverse 30 | access.json 31 | access/*.json 32 | 33 | db 34 | cache 35 | config 36 | 37 | README.md 38 | .gitignore 39 | screenshot 40 | LICENSE 41 | 42 | .github 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | app/node_modules 2 | .vscode 3 | .idea 4 | config.yaml 5 | config.dev.yaml 6 | 7 | addition/generation/data/* 8 | !addition/generation/data/.gitkeep 9 | 10 | addition/article/data/* 11 | !addition/article/data/.gitkeep 12 | sdk 13 | logs 14 | 15 | chat 16 | chat.exe 17 | 18 | # for reverse engine 19 | reverse 20 | access.json 21 | access/*.json 22 | 23 | db 24 | redis 25 | config 26 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Author: ProgramZmh 2 | # License: Apache-2.0 3 | # Description: Dockerfile for chatnio 4 | 5 | FROM golang:1.20-alpine AS backend 6 | 7 | WORKDIR /backend 8 | COPY . . 9 | 10 | # Set go proxy to https://goproxy.cn (open for vps in China Mainland) 11 | # RUN go env -w GOPROXY=https://goproxy.cn,direct 12 | ENV GOOS=linux GO111MODULE=on CGO_ENABLED=1 13 | 14 | # Install dependencies for cgo 15 | RUN apk add --no-cache gcc musl-dev 16 | 17 | # Build backend 18 | RUN go install && \ 19 | go build . 20 | 21 | FROM node:18 AS frontend 22 | 23 | WORKDIR /app 24 | COPY ./app . 25 | 26 | RUN npm install -g pnpm && \ 27 | pnpm install && \ 28 | pnpm run build && \ 29 | rm -rf node_modules src 30 | 31 | 32 | FROM alpine 33 | 34 | # Install dependencies 35 | RUN apk update && \ 36 | apk upgrade && \ 37 | apk add --no-cache wget ca-certificates tzdata && \ 38 | update-ca-certificates 2>/dev/null || true && \ 39 | rm -rf /var/cache/apk/* 40 | 41 | # Set timezone 42 | RUN echo "Asia/Shanghai" > /etc/timezone && \ 43 | ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 44 | 45 | WORKDIR / 46 | 47 | # Copy dist 48 | COPY --from=backend /backend / 49 | COPY --from=frontend /app/dist /app/dist 50 | 51 | # Expose port 52 | EXPOSE 8094 53 | 54 | # Run application 55 | CMD ["./chat"] 56 | -------------------------------------------------------------------------------- /adapter/azure/struct.go: -------------------------------------------------------------------------------- 1 | package azure 2 | 3 | import ( 4 | "chat/globals" 5 | ) 6 | 7 | type ChatInstance struct { 8 | Endpoint string 9 | ApiKey string 10 | Resource string 11 | } 12 | 13 | type InstanceProps struct { 14 | Model string 15 | Plan bool 16 | } 17 | 18 | func (c *ChatInstance) GetEndpoint() string { 19 | return c.Endpoint 20 | } 21 | 22 | func (c *ChatInstance) GetApiKey() string { 23 | return c.ApiKey 24 | } 25 | 26 | func (c *ChatInstance) GetResource() string { 27 | return c.Resource 28 | } 29 | 30 | func (c *ChatInstance) GetHeader() map[string]string { 31 | return map[string]string{ 32 | "Content-Type": "application/json", 33 | "api-key": c.GetApiKey(), 34 | } 35 | } 36 | 37 | func NewChatInstance(endpoint, apiKey string, resource string) *ChatInstance { 38 | return &ChatInstance{ 39 | Endpoint: endpoint, 40 | ApiKey: apiKey, 41 | Resource: resource, 42 | } 43 | } 44 | 45 | func NewChatInstanceFromConfig(conf globals.ChannelConfig) *ChatInstance { 46 | param := conf.SplitRandomSecret(2) 47 | return NewChatInstance( 48 | conf.GetEndpoint(), 49 | param[0], 50 | param[1], 51 | ) 52 | } 53 | -------------------------------------------------------------------------------- /adapter/baichuan/struct.go: -------------------------------------------------------------------------------- 1 | package baichuan 2 | 3 | import ( 4 | "chat/globals" 5 | "fmt" 6 | ) 7 | 8 | type ChatInstance struct { 9 | Endpoint string 10 | ApiKey string 11 | } 12 | 13 | func (c *ChatInstance) GetEndpoint() string { 14 | return c.Endpoint 15 | } 16 | 17 | func (c *ChatInstance) GetApiKey() string { 18 | return c.ApiKey 19 | } 20 | 21 | func (c *ChatInstance) GetHeader() map[string]string { 22 | return map[string]string{ 23 | "Content-Type": "application/json", 24 | "Authorization": fmt.Sprintf("Bearer %s", c.GetApiKey()), 25 | } 26 | } 27 | 28 | func NewChatInstance(endpoint, apiKey string) *ChatInstance { 29 | return &ChatInstance{ 30 | Endpoint: endpoint, 31 | ApiKey: apiKey, 32 | } 33 | } 34 | 35 | func NewChatInstanceFromConfig(conf globals.ChannelConfig) *ChatInstance { 36 | return NewChatInstance( 37 | conf.GetEndpoint(), 38 | conf.GetRandomSecret(), 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /adapter/bing/chat.go: -------------------------------------------------------------------------------- 1 | package bing 2 | 3 | import ( 4 | "chat/globals" 5 | "chat/utils" 6 | "fmt" 7 | "strings" 8 | ) 9 | 10 | type ChatProps struct { 11 | Message []globals.Message 12 | Model string 13 | } 14 | 15 | func (c *ChatInstance) CreateStreamChatRequest(props *ChatProps, hook globals.Hook) error { 16 | var conn *utils.WebSocket 17 | if conn = utils.NewWebsocketClient(c.GetEndpoint()); conn == nil { 18 | return fmt.Errorf("bing error: websocket connection failed") 19 | } 20 | defer conn.DeferClose() 21 | 22 | model := strings.TrimPrefix(props.Model, "bing-") 23 | prompt := props.Message[len(props.Message)-1].Content 24 | if err := conn.SendJSON(&ChatRequest{ 25 | Prompt: prompt, 26 | Hash: c.Secret, 27 | Model: model, 28 | }); err != nil { 29 | return err 30 | } 31 | 32 | for { 33 | form := utils.ReadForm[ChatResponse](conn) 34 | if form == nil { 35 | return nil 36 | } 37 | 38 | if err := hook(form.Response); err != nil { 39 | return err 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /adapter/bing/struct.go: -------------------------------------------------------------------------------- 1 | package bing 2 | 3 | import ( 4 | "chat/globals" 5 | "fmt" 6 | ) 7 | 8 | type ChatInstance struct { 9 | Endpoint string 10 | Secret string 11 | } 12 | 13 | func (c *ChatInstance) GetEndpoint() string { 14 | return fmt.Sprintf("%s/chat", c.Endpoint) 15 | } 16 | 17 | func NewChatInstance(endpoint, secret string) *ChatInstance { 18 | return &ChatInstance{ 19 | Endpoint: endpoint, 20 | Secret: secret, 21 | } 22 | } 23 | 24 | func NewChatInstanceFromConfig(conf globals.ChannelConfig) *ChatInstance { 25 | return NewChatInstance( 26 | conf.GetEndpoint(), 27 | conf.GetRandomSecret(), 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /adapter/bing/types.go: -------------------------------------------------------------------------------- 1 | package bing 2 | 3 | // see https://github.com/Deeptrain-Community/chatnio-bing-service 4 | 5 | type ChatRequest struct { 6 | Prompt string `json:"prompt"` 7 | Hash string `json:"hash"` 8 | Model string `json:"model"` 9 | } 10 | 11 | type ChatResponse struct { 12 | Response string `json:"response"` 13 | } 14 | -------------------------------------------------------------------------------- /adapter/chatgpt/image.go: -------------------------------------------------------------------------------- 1 | package chatgpt 2 | 3 | import ( 4 | "chat/globals" 5 | "chat/utils" 6 | "fmt" 7 | "strings" 8 | ) 9 | 10 | type ImageProps struct { 11 | Model string 12 | Prompt string 13 | Size ImageSize 14 | } 15 | 16 | func (c *ChatInstance) GetImageEndpoint() string { 17 | return fmt.Sprintf("%s/v1/images/generations", c.GetEndpoint()) 18 | } 19 | 20 | // CreateImageRequest will create a dalle image from prompt, return url of image and error 21 | func (c *ChatInstance) CreateImageRequest(props ImageProps) (string, error) { 22 | res, err := utils.Post( 23 | c.GetImageEndpoint(), 24 | c.GetHeader(), ImageRequest{ 25 | Model: props.Model, 26 | Prompt: props.Prompt, 27 | Size: utils.Multi[ImageSize]( 28 | props.Model == globals.Dalle3, 29 | ImageSize1024, 30 | ImageSize512, 31 | ), 32 | N: 1, 33 | }) 34 | if err != nil || res == nil { 35 | return "", fmt.Errorf("chatgpt error: %s", err.Error()) 36 | } 37 | 38 | data := utils.MapToStruct[ImageResponse](res) 39 | if data == nil { 40 | return "", fmt.Errorf("chatgpt error: cannot parse response") 41 | } else if data.Error.Message != "" { 42 | return "", fmt.Errorf("chatgpt error: %s", data.Error.Message) 43 | } 44 | 45 | return data.Data[0].Url, nil 46 | } 47 | 48 | // CreateImage will create a dalle image from prompt, return markdown of image 49 | func (c *ChatInstance) CreateImage(props *ChatProps) (string, error) { 50 | url, err := c.CreateImageRequest(ImageProps{ 51 | Model: props.Model, 52 | Prompt: c.GetLatestPrompt(props), 53 | }) 54 | if err != nil { 55 | if strings.Contains(err.Error(), "safety") { 56 | return err.Error(), nil 57 | } 58 | return "", err 59 | } 60 | 61 | return utils.GetImageMarkdown(url), nil 62 | } 63 | -------------------------------------------------------------------------------- /adapter/chatgpt/struct.go: -------------------------------------------------------------------------------- 1 | package chatgpt 2 | 3 | import ( 4 | "chat/globals" 5 | "fmt" 6 | ) 7 | 8 | type ChatInstance struct { 9 | Endpoint string 10 | ApiKey string 11 | } 12 | 13 | type InstanceProps struct { 14 | Model string 15 | Plan bool 16 | } 17 | 18 | func (c *ChatInstance) GetEndpoint() string { 19 | return c.Endpoint 20 | } 21 | 22 | func (c *ChatInstance) GetApiKey() string { 23 | return c.ApiKey 24 | } 25 | 26 | func (c *ChatInstance) GetHeader() map[string]string { 27 | return map[string]string{ 28 | "Content-Type": "application/json", 29 | "Authorization": fmt.Sprintf("Bearer %s", c.GetApiKey()), 30 | } 31 | } 32 | 33 | func NewChatInstance(endpoint, apiKey string) *ChatInstance { 34 | return &ChatInstance{ 35 | Endpoint: endpoint, 36 | ApiKey: apiKey, 37 | } 38 | } 39 | 40 | func NewChatInstanceFromConfig(conf globals.ChannelConfig) *ChatInstance { 41 | return NewChatInstance( 42 | conf.GetEndpoint(), 43 | conf.GetRandomSecret(), 44 | ) 45 | } 46 | -------------------------------------------------------------------------------- /adapter/chatgpt/test.go: -------------------------------------------------------------------------------- 1 | package chatgpt 2 | 3 | import ( 4 | "chat/globals" 5 | "chat/utils" 6 | "fmt" 7 | "github.com/spf13/viper" 8 | "strings" 9 | ) 10 | 11 | func (c *ChatInstance) Test() bool { 12 | result, err := c.CreateChatRequest(&ChatProps{ 13 | Model: globals.GPT3Turbo, 14 | Message: []globals.Message{{Role: globals.User, Content: "hi"}}, 15 | Token: utils.ToPtr(1), 16 | }) 17 | if err != nil { 18 | fmt.Println(fmt.Sprintf("%s: test failed (%s)", c.GetApiKey(), err.Error())) 19 | } 20 | 21 | return err == nil && len(result) > 0 22 | } 23 | 24 | func FilterKeys(v string) []string { 25 | endpoint := viper.GetString(fmt.Sprintf("openai.%s.endpoint", v)) 26 | keys := strings.Split(viper.GetString(fmt.Sprintf("openai.%s.apikey", v)), "|") 27 | 28 | return FilterKeysNative(endpoint, keys) 29 | } 30 | 31 | func FilterKeysNative(endpoint string, keys []string) []string { 32 | stack := make(chan string, len(keys)) 33 | for _, key := range keys { 34 | go func(key string) { 35 | instance := NewChatInstance(endpoint, key) 36 | stack <- utils.Multi[string](instance.Test(), key, "") 37 | }(key) 38 | } 39 | 40 | var result []string 41 | for i := 0; i < len(keys); i++ { 42 | if res := <-stack; res != "" { 43 | result = append(result, res) 44 | } 45 | } 46 | return result 47 | } 48 | -------------------------------------------------------------------------------- /adapter/claude/struct.go: -------------------------------------------------------------------------------- 1 | package claude 2 | 3 | import ( 4 | "chat/globals" 5 | ) 6 | 7 | type ChatInstance struct { 8 | Endpoint string 9 | ApiKey string 10 | } 11 | 12 | func NewChatInstance(endpoint, apiKey string) *ChatInstance { 13 | return &ChatInstance{ 14 | Endpoint: endpoint, 15 | ApiKey: apiKey, 16 | } 17 | } 18 | 19 | func NewChatInstanceFromConfig(conf globals.ChannelConfig) *ChatInstance { 20 | return NewChatInstance( 21 | conf.GetEndpoint(), 22 | conf.GetRandomSecret(), 23 | ) 24 | } 25 | 26 | func (c *ChatInstance) GetEndpoint() string { 27 | return c.Endpoint 28 | } 29 | 30 | func (c *ChatInstance) GetApiKey() string { 31 | return c.ApiKey 32 | } 33 | -------------------------------------------------------------------------------- /adapter/claude/types.go: -------------------------------------------------------------------------------- 1 | package claude 2 | 3 | // ChatBody is the request body for anthropic claude 4 | type ChatBody struct { 5 | Prompt string `json:"prompt"` 6 | MaxTokensToSample int `json:"max_tokens_to_sample"` 7 | Model string `json:"model"` 8 | Stream bool `json:"stream"` 9 | Temperature *float32 `json:"temperature,omitempty"` 10 | TopP *float32 `json:"top_p,omitempty"` 11 | TopK *int `json:"top_k,omitempty"` 12 | } 13 | 14 | // ChatResponse is the native http request and stream response for anthropic claude 15 | type ChatResponse struct { 16 | Completion string `json:"completion"` 17 | LogId string `json:"log_id"` 18 | } 19 | -------------------------------------------------------------------------------- /adapter/dashscope/struct.go: -------------------------------------------------------------------------------- 1 | package dashscope 2 | 3 | import ( 4 | "chat/globals" 5 | ) 6 | 7 | type ChatInstance struct { 8 | Endpoint string 9 | ApiKey string 10 | } 11 | 12 | func (c *ChatInstance) GetApiKey() string { 13 | return c.ApiKey 14 | } 15 | 16 | func (c *ChatInstance) GetEndpoint() string { 17 | return c.Endpoint 18 | } 19 | 20 | func NewChatInstance(endpoint string, apiKey string) *ChatInstance { 21 | return &ChatInstance{ 22 | Endpoint: endpoint, 23 | ApiKey: apiKey, 24 | } 25 | } 26 | 27 | func NewChatInstanceFromConfig(conf globals.ChannelConfig) *ChatInstance { 28 | return NewChatInstance( 29 | conf.GetEndpoint(), 30 | conf.GetRandomSecret(), 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /adapter/dashscope/types.go: -------------------------------------------------------------------------------- 1 | package dashscope 2 | 3 | // ChatRequest is the request body for dashscope 4 | type ChatRequest struct { 5 | Model string `json:"model"` 6 | Input ChatInput `json:"input"` 7 | Parameters ChatParam `json:"parameters"` 8 | } 9 | 10 | type Message struct { 11 | Role string `json:"role"` 12 | Content string `json:"content"` 13 | } 14 | 15 | type ChatInput struct { 16 | Messages []Message `json:"messages"` 17 | } 18 | 19 | type ChatParam struct { 20 | IncrementalOutput bool `json:"incremental_output"` 21 | EnableSearch *bool `json:"enable_search,omitempty"` 22 | MaxTokens int `json:"max_tokens"` 23 | Temperature *float32 `json:"temperature,omitempty"` 24 | TopP *float32 `json:"top_p,omitempty"` 25 | TopK *int `json:"top_k,omitempty"` 26 | RepetitionPenalty *float32 `json:"repetition_penalty,omitempty"` 27 | } 28 | 29 | // ChatResponse is the response body for dashscope 30 | type ChatResponse struct { 31 | Output struct { 32 | FinishReason string `json:"finish_reason"` 33 | Text string `json:"text"` 34 | } `json:"output"` 35 | RequestId string `json:"request_id"` 36 | Usage struct { 37 | InputTokens int `json:"input_tokens"` 38 | OutputTokens int `json:"output_tokens"` 39 | } `json:"usage"` 40 | Message string `json:"message"` 41 | } 42 | -------------------------------------------------------------------------------- /adapter/hunyuan/chat.go: -------------------------------------------------------------------------------- 1 | package hunyuan 2 | 3 | import ( 4 | "chat/globals" 5 | "context" 6 | "fmt" 7 | ) 8 | 9 | type ChatProps struct { 10 | Model string 11 | Message []globals.Message 12 | Temperature *float32 13 | TopP *float32 14 | } 15 | 16 | func (c *ChatInstance) FormatMessages(messages []globals.Message) []globals.Message { 17 | var result []globals.Message 18 | for _, message := range messages { 19 | switch message.Role { 20 | case globals.System: 21 | result = append(result, globals.Message{Role: globals.User, Content: message.Content}) 22 | case globals.Assistant, globals.User: 23 | bound := len(result) > 0 && result[len(result)-1].Role == message.Role 24 | if bound { 25 | result[len(result)-1].Content += message.Content 26 | } else { 27 | result = append(result, message) 28 | } 29 | case globals.Tool: 30 | continue 31 | default: 32 | result = append(result, message) 33 | } 34 | } 35 | 36 | return result 37 | } 38 | 39 | func (c *ChatInstance) CreateStreamChatRequest(props *ChatProps, callback globals.Hook) error { 40 | credential := NewCredential(c.GetSecretId(), c.GetSecretKey()) 41 | client := NewInstance(c.GetAppId(), c.GetEndpoint(), credential) 42 | channel, err := client.Chat(context.Background(), NewRequest(Stream, c.FormatMessages(props.Message), props.Temperature, props.TopP)) 43 | if err != nil { 44 | return fmt.Errorf("tencent hunyuan error: %+v", err) 45 | } 46 | 47 | for chunk := range channel { 48 | if chunk.Error.Code != 0 { 49 | fmt.Printf("tencent hunyuan error: %+v\n", chunk.Error) 50 | break 51 | } 52 | 53 | if err := callback(chunk.Choices[0].Delta.Content); err != nil { 54 | return err 55 | } 56 | } 57 | 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /adapter/hunyuan/struct.go: -------------------------------------------------------------------------------- 1 | package hunyuan 2 | 3 | import ( 4 | "chat/globals" 5 | "chat/utils" 6 | ) 7 | 8 | type ChatInstance struct { 9 | Endpoint string 10 | AppId int64 11 | SecretId string 12 | SecretKey string 13 | } 14 | 15 | func (c *ChatInstance) GetAppId() int64 { 16 | return c.AppId 17 | } 18 | 19 | func (c *ChatInstance) GetEndpoint() string { 20 | return c.Endpoint 21 | } 22 | 23 | func (c *ChatInstance) GetSecretId() string { 24 | return c.SecretId 25 | } 26 | 27 | func (c *ChatInstance) GetSecretKey() string { 28 | return c.SecretKey 29 | } 30 | 31 | func NewChatInstance(endpoint, appId, secretId, secretKey string) *ChatInstance { 32 | return &ChatInstance{ 33 | Endpoint: endpoint, 34 | AppId: utils.ParseInt64(appId), 35 | SecretId: secretId, 36 | SecretKey: secretKey, 37 | } 38 | } 39 | 40 | func NewChatInstanceFromConfig(conf globals.ChannelConfig) *ChatInstance { 41 | params := conf.SplitRandomSecret(3) 42 | return NewChatInstance( 43 | conf.GetEndpoint(), 44 | params[0], params[1], params[2], 45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /adapter/midjourney/expose.go: -------------------------------------------------------------------------------- 1 | package midjourney 2 | 3 | import ( 4 | "chat/globals" 5 | "chat/utils" 6 | "fmt" 7 | "github.com/gin-gonic/gin" 8 | "net/http" 9 | "strings" 10 | ) 11 | 12 | var whiteList []string 13 | 14 | func SaveWhiteList(raw string) { 15 | arr := utils.Filter(strings.Split(raw, ","), func(s string) bool { 16 | return len(strings.TrimSpace(s)) > 0 17 | }) 18 | 19 | for _, ip := range arr { 20 | if !utils.Contains(ip, whiteList) { 21 | whiteList = append(whiteList, ip) 22 | } 23 | } 24 | } 25 | 26 | func InWhiteList(ip string) bool { 27 | if len(whiteList) == 0 { 28 | return true 29 | } 30 | return utils.Contains(ip, whiteList) 31 | } 32 | 33 | func NotifyAPI(c *gin.Context) { 34 | if !InWhiteList(c.ClientIP()) { 35 | globals.Info(fmt.Sprintf("[midjourney] notify api: banned request from %s", c.ClientIP())) 36 | c.AbortWithStatus(http.StatusForbidden) 37 | return 38 | } 39 | 40 | var form NotifyForm 41 | if err := c.ShouldBindJSON(&form); err != nil { 42 | c.AbortWithStatus(http.StatusBadRequest) 43 | return 44 | } 45 | globals.Debug(fmt.Sprintf("[midjourney] notify api: get notify: %s (from: %s)", utils.Marshal(form), c.ClientIP())) 46 | 47 | if !utils.Contains(form.Status, []string{InProgress, Success, Failure}) { 48 | // ignore 49 | return 50 | } 51 | 52 | reason, ok := form.FailReason.(string) 53 | if !ok { 54 | reason = "unknown" 55 | } 56 | 57 | err := setStorage(form.Id, StorageForm{ 58 | Url: form.ImageUrl, 59 | FailReason: reason, 60 | Progress: form.Progress, 61 | Status: form.Status, 62 | }) 63 | 64 | c.JSON(http.StatusOK, gin.H{ 65 | "status": err == nil, 66 | }) 67 | } 68 | -------------------------------------------------------------------------------- /adapter/midjourney/storage.go: -------------------------------------------------------------------------------- 1 | package midjourney 2 | 3 | import ( 4 | "chat/connection" 5 | "chat/utils" 6 | "fmt" 7 | ) 8 | 9 | func getTaskName(task string) string { 10 | return fmt.Sprintf("nio:mj-task:%s", task) 11 | } 12 | 13 | func setStorage(task string, form StorageForm) error { 14 | return utils.SetJson(connection.Cache, getTaskName(task), form, 60*60) 15 | } 16 | 17 | func getStorage(task string) *StorageForm { 18 | return utils.GetJson[StorageForm](connection.Cache, getTaskName(task)) 19 | } 20 | -------------------------------------------------------------------------------- /adapter/midjourney/struct.go: -------------------------------------------------------------------------------- 1 | package midjourney 2 | 3 | import ( 4 | "chat/globals" 5 | ) 6 | 7 | type ChatInstance struct { 8 | Endpoint string 9 | ApiSecret string 10 | } 11 | 12 | func (c *ChatInstance) GetApiSecret() string { 13 | return c.ApiSecret 14 | } 15 | 16 | func (c *ChatInstance) GetEndpoint() string { 17 | return c.Endpoint 18 | } 19 | 20 | func NewChatInstance(endpoint, apiSecret, whiteList string) *ChatInstance { 21 | SaveWhiteList(whiteList) 22 | 23 | return &ChatInstance{ 24 | Endpoint: endpoint, 25 | ApiSecret: apiSecret, 26 | } 27 | } 28 | 29 | func NewChatInstanceFromConfig(conf globals.ChannelConfig) *ChatInstance { 30 | params := conf.SplitRandomSecret(2) 31 | 32 | return NewChatInstance( 33 | conf.GetEndpoint(), 34 | params[0], params[1], 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /adapter/oneapi/struct.go: -------------------------------------------------------------------------------- 1 | package oneapi 2 | 3 | import ( 4 | "chat/globals" 5 | "fmt" 6 | ) 7 | 8 | type ChatInstance struct { 9 | Endpoint string 10 | ApiKey string 11 | } 12 | 13 | type InstanceProps struct { 14 | Model string 15 | Plan bool 16 | } 17 | 18 | func (c *ChatInstance) GetEndpoint() string { 19 | return c.Endpoint 20 | } 21 | 22 | func (c *ChatInstance) GetApiKey() string { 23 | return c.ApiKey 24 | } 25 | 26 | func (c *ChatInstance) GetHeader() map[string]string { 27 | return map[string]string{ 28 | "Content-Type": "application/json", 29 | "Authorization": fmt.Sprintf("Bearer %s", c.GetApiKey()), 30 | } 31 | } 32 | 33 | func NewChatInstance(endpoint, apiKey string) *ChatInstance { 34 | return &ChatInstance{ 35 | Endpoint: endpoint, 36 | ApiKey: apiKey, 37 | } 38 | } 39 | 40 | func NewChatInstanceFromConfig(conf globals.ChannelConfig) *ChatInstance { 41 | return NewChatInstance( 42 | conf.GetEndpoint(), 43 | conf.GetRandomSecret(), 44 | ) 45 | } 46 | -------------------------------------------------------------------------------- /adapter/palm2/struct.go: -------------------------------------------------------------------------------- 1 | package palm2 2 | 3 | import ( 4 | "chat/globals" 5 | ) 6 | 7 | type ChatInstance struct { 8 | Endpoint string 9 | ApiKey string 10 | } 11 | 12 | func (c *ChatInstance) GetApiKey() string { 13 | return c.ApiKey 14 | } 15 | 16 | func (c *ChatInstance) GetEndpoint() string { 17 | return c.Endpoint 18 | } 19 | 20 | func NewChatInstance(endpoint string, apiKey string) *ChatInstance { 21 | return &ChatInstance{ 22 | Endpoint: endpoint, 23 | ApiKey: apiKey, 24 | } 25 | } 26 | 27 | func NewChatInstanceFromConfig(conf globals.ChannelConfig) *ChatInstance { 28 | return NewChatInstance( 29 | conf.GetEndpoint(), 30 | conf.GetRandomSecret(), 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /adapter/request.go: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | import ( 4 | "chat/globals" 5 | "fmt" 6 | "strings" 7 | "time" 8 | ) 9 | 10 | func IsAvailableError(err error) bool { 11 | return err != nil && err.Error() != "signal" 12 | } 13 | 14 | func isQPSOverLimit(model string, err error) bool { 15 | switch model { 16 | case globals.SparkDesk, globals.SparkDeskV2, globals.SparkDeskV3: 17 | return strings.Contains(err.Error(), "AppIdQpsOverFlowError") 18 | default: 19 | return false 20 | } 21 | } 22 | 23 | func NewChatRequest(conf globals.ChannelConfig, props *ChatProps, hook globals.Hook) error { 24 | err := createChatRequest(conf, props, hook) 25 | 26 | retries := conf.GetRetry() 27 | props.Current++ 28 | 29 | if IsAvailableError(err) { 30 | if isQPSOverLimit(props.Model, err) { 31 | // sleep for 0.5s to avoid qps limit 32 | 33 | globals.Info(fmt.Sprintf("qps limit for %s, sleep and retry (times: %d)", props.Model, props.Current)) 34 | time.Sleep(500 * time.Millisecond) 35 | return NewChatRequest(conf, props, hook) 36 | } 37 | 38 | if props.Current < retries { 39 | content := strings.Replace(err.Error(), "\n", "", -1) 40 | globals.Warn(fmt.Sprintf("retrying chat request for %s (attempt %d/%d, error: %s)", props.Model, props.Current+1, retries, content)) 41 | return NewChatRequest(conf, props, hook) 42 | } 43 | } 44 | 45 | return conf.ProcessError(err) 46 | } 47 | -------------------------------------------------------------------------------- /adapter/router.go: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | import ( 4 | "chat/adapter/midjourney" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func Register(app *gin.RouterGroup) { 9 | app.POST("/mj/notify", midjourney.NotifyAPI) 10 | } 11 | -------------------------------------------------------------------------------- /adapter/skylark/struct.go: -------------------------------------------------------------------------------- 1 | package skylark 2 | 3 | import ( 4 | "chat/globals" 5 | "github.com/volcengine/volc-sdk-golang/service/maas" 6 | "strings" 7 | ) 8 | 9 | const ( 10 | defaultHost = "maas-api.ml-platform-cn-beijing.volces.com" 11 | defaultRegion = "cn-beijing" 12 | ) 13 | 14 | type ChatInstance struct { 15 | Instance *maas.MaaS 16 | } 17 | 18 | func getHost(endpoint string) string { 19 | seg := strings.Split(endpoint, "://") 20 | if len(seg) > 1 && seg[1] != "" { 21 | return seg[1] 22 | } 23 | 24 | return defaultHost 25 | } 26 | 27 | func getRegion(endpoint string) string { 28 | host := getHost(endpoint) 29 | seg := strings.TrimSuffix(strings.TrimPrefix(host, "maas-api.ml-platform-"), ".volces.com") 30 | if seg != "" { 31 | return seg 32 | } 33 | 34 | return defaultRegion 35 | } 36 | 37 | func NewChatInstance(endpoint, accessKey, secretKey string) *ChatInstance { 38 | instance := maas.NewInstance(getHost(endpoint), getRegion(endpoint)) 39 | instance.SetAccessKey(accessKey) 40 | instance.SetSecretKey(secretKey) 41 | return &ChatInstance{ 42 | Instance: instance, 43 | } 44 | } 45 | 46 | func NewChatInstanceFromConfig(conf globals.ChannelConfig) *ChatInstance { 47 | params := conf.SplitRandomSecret(2) 48 | 49 | return NewChatInstance( 50 | conf.GetEndpoint(), 51 | params[0], params[1], 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /adapter/slack/chat.go: -------------------------------------------------------------------------------- 1 | package slack 2 | 3 | import ( 4 | "chat/globals" 5 | "context" 6 | ) 7 | 8 | type ChatProps struct { 9 | Message []globals.Message 10 | } 11 | 12 | func (c *ChatInstance) CreateStreamChatRequest(props *ChatProps, hook globals.Hook) error { 13 | if err := c.Instance.NewChannel(c.GetChannel()); err != nil { 14 | return err 15 | } 16 | 17 | resp, err := c.Instance.Reply(context.Background(), c.FormatMessage(props.Message), nil) 18 | if err != nil { 19 | return err 20 | } 21 | 22 | return c.ProcessPartialResponse(resp, hook) 23 | } 24 | -------------------------------------------------------------------------------- /adapter/zhinao/struct.go: -------------------------------------------------------------------------------- 1 | package zhinao 2 | 3 | import ( 4 | "chat/globals" 5 | "fmt" 6 | ) 7 | 8 | type ChatInstance struct { 9 | Endpoint string 10 | ApiKey string 11 | } 12 | 13 | func (c *ChatInstance) GetEndpoint() string { 14 | return c.Endpoint 15 | } 16 | 17 | func (c *ChatInstance) GetApiKey() string { 18 | return c.ApiKey 19 | } 20 | 21 | func (c *ChatInstance) GetHeader() map[string]string { 22 | return map[string]string{ 23 | "Content-Type": "application/json", 24 | "Authorization": fmt.Sprintf("Bearer %s", c.GetApiKey()), 25 | } 26 | } 27 | 28 | func NewChatInstance(endpoint, apiKey string) *ChatInstance { 29 | return &ChatInstance{ 30 | Endpoint: endpoint, 31 | ApiKey: apiKey, 32 | } 33 | } 34 | 35 | func NewChatInstanceFromConfig(conf globals.ChannelConfig) *ChatInstance { 36 | return NewChatInstance( 37 | conf.GetEndpoint(), 38 | conf.GetRandomSecret(), 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /adapter/zhipuai/struct.go: -------------------------------------------------------------------------------- 1 | package zhipuai 2 | 3 | import ( 4 | "chat/globals" 5 | "chat/utils" 6 | "github.com/dgrijalva/jwt-go" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | type ChatInstance struct { 12 | Endpoint string 13 | ApiKey string 14 | } 15 | 16 | func (c *ChatInstance) GetToken() string { 17 | // get jwt token for zhipuai api 18 | segment := strings.Split(c.ApiKey, ".") 19 | if len(segment) != 2 { 20 | return "" 21 | } 22 | id, secret := segment[0], segment[1] 23 | 24 | payload := utils.MapToStruct[jwt.MapClaims](Payload{ 25 | ApiKey: id, 26 | Exp: time.Now().Add(time.Minute*5).Unix() * 1000, 27 | TimeStamp: time.Now().Unix() * 1000, 28 | }) 29 | 30 | instance := jwt.NewWithClaims(jwt.SigningMethodHS256, payload) 31 | instance.Header = map[string]interface{}{ 32 | "alg": "HS256", 33 | "sign_type": "SIGN", 34 | } 35 | token, _ := instance.SignedString([]byte(secret)) 36 | return token 37 | } 38 | 39 | func (c *ChatInstance) GetEndpoint() string { 40 | return c.Endpoint 41 | } 42 | 43 | func NewChatInstance(endpoint, apikey string) *ChatInstance { 44 | return &ChatInstance{ 45 | Endpoint: endpoint, 46 | ApiKey: apikey, 47 | } 48 | } 49 | 50 | func NewChatInstanceFromConfig(conf globals.ChannelConfig) *ChatInstance { 51 | return NewChatInstance(conf.GetEndpoint(), conf.GetRandomSecret()) 52 | } 53 | -------------------------------------------------------------------------------- /adapter/zhipuai/types.go: -------------------------------------------------------------------------------- 1 | package zhipuai 2 | 3 | import "chat/globals" 4 | 5 | const ( 6 | ChatGLMTurbo = "chatglm_turbo" 7 | ChatGLMPro = "chatglm_pro" 8 | ChatGLMStd = "chatglm_std" 9 | ChatGLMLite = "chatglm_lite" 10 | ) 11 | 12 | type Payload struct { 13 | ApiKey string `json:"api_key"` 14 | Exp int64 `json:"exp"` 15 | TimeStamp int64 `json:"timestamp"` 16 | } 17 | 18 | type ChatRequest struct { 19 | Prompt []globals.Message `json:"prompt"` 20 | Temperature *float32 `json:"temperature,omitempty"` 21 | TopP *float32 `json:"top_p,omitempty"` 22 | Ref *ChatRef `json:"ref,omitempty"` 23 | } 24 | 25 | type ChatRef struct { 26 | Enable *bool `json:"enable,omitempty"` 27 | SearchQuery *string `json:"search_query,omitempty"` 28 | } 29 | 30 | type Occurrence struct { 31 | Code int `json:"code"` 32 | Msg string `json:"msg"` 33 | Success bool `json:"success"` 34 | } 35 | -------------------------------------------------------------------------------- /addition/article/data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kapyan/woochatnio/24102a01d2ea48a7bda2870cea9c43ae4056e5dc/addition/article/data/.gitkeep -------------------------------------------------------------------------------- /addition/article/template.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kapyan/woochatnio/24102a01d2ea48a7bda2870cea9c43ae4056e5dc/addition/article/template.docx -------------------------------------------------------------------------------- /addition/article/utils.go: -------------------------------------------------------------------------------- 1 | package article 2 | 3 | import ( 4 | "chat/globals" 5 | "chat/utils" 6 | "fmt" 7 | "github.com/lukasjarosch/go-docx" 8 | ) 9 | 10 | func GenerateDocxFile(target, title, content string) error { 11 | data := docx.PlaceholderMap{ 12 | "title": title, 13 | "content": content, 14 | } 15 | 16 | doc, err := docx.Open("addition/article/template.docx") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | if err := doc.ReplaceAll(data); err != nil { 22 | return err 23 | } 24 | 25 | if err := doc.WriteToFile(target); err != nil { 26 | return err 27 | } 28 | 29 | return nil 30 | } 31 | 32 | func CreateArticleFile(hash, title, content string) string { 33 | target := fmt.Sprintf("addition/article/data/%s/%s.docx", hash, title) 34 | utils.CreateFolderOnFile(target) 35 | if err := GenerateDocxFile(target, title, content); err != nil { 36 | globals.Debug(fmt.Sprintf("[article] error during generate article %s: %s", title, err.Error())) 37 | } 38 | 39 | return target 40 | } 41 | -------------------------------------------------------------------------------- /addition/card/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | -------------------------------------------------------------------------------- /addition/card/error.php: -------------------------------------------------------------------------------- 1 | 10 | 11 | 44 | 45 | -------------------------------------------------------------------------------- /addition/card/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kapyan/woochatnio/24102a01d2ea48a7bda2870cea9c43ae4056e5dc/addition/card/favicon.ico -------------------------------------------------------------------------------- /addition/card/utils.php: -------------------------------------------------------------------------------- 1 | [^\S ]+/', '/[^\S ]+', '/(\s)+/', '/> ', '/:\s+/', '/\{\s+/', '/\s+}/'); 8 | $replace = array('>', '<', '\\1', '><', ':', '{', '}'); 9 | return preg_replace($search, $replace, $buffer); 10 | } 11 | 12 | function fetch($message, $web): array|string|null 13 | { 14 | $opts = array('http' => 15 | array( 16 | 'method' => 'POST', 17 | 'header' => 'Content-type: application/json', 18 | 'content' => json_encode(array('message' => $message, 'web' => $web)) 19 | ) 20 | ); 21 | 22 | $context = stream_context_create($opts); 23 | $response = @file_get_contents("http://localhost:8094/card", false, $context); 24 | $ok = $response !== false; 25 | return $ok ? json_decode($response, true) : null; 26 | } 27 | 28 | function get($param, $default = null) 29 | { 30 | return $_GET[$param] ?? $default; 31 | } 32 | -------------------------------------------------------------------------------- /addition/generation/build.go: -------------------------------------------------------------------------------- 1 | package generation 2 | 3 | import ( 4 | "chat/utils" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | func GetFolder(hash string) string { 10 | return fmt.Sprintf("addition/generation/data/%s", hash) 11 | } 12 | 13 | func GetFolderByHash(model string, prompt string) (string, string) { 14 | hash := utils.Sha2Encrypt(model + prompt + time.Now().Format("2006-01-02 15:04:05")) 15 | return hash, GetFolder(hash) 16 | } 17 | 18 | func GenerateProject(path string, instance ProjectResult) bool { 19 | for name, data := range instance.Result { 20 | current := fmt.Sprintf("%s/%s", path, name) 21 | if content, ok := data.(string); ok { 22 | if !utils.WriteFile(current, content, true) { 23 | return false 24 | } 25 | } else { 26 | GenerateProject(current, ProjectResult{ 27 | Result: data.(map[string]interface{}), 28 | }) 29 | } 30 | } 31 | return true 32 | } 33 | -------------------------------------------------------------------------------- /addition/generation/data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kapyan/woochatnio/24102a01d2ea48a7bda2870cea9c43ae4056e5dc/addition/generation/data/.gitkeep -------------------------------------------------------------------------------- /addition/generation/generate.go: -------------------------------------------------------------------------------- 1 | package generation 2 | 3 | import ( 4 | "chat/globals" 5 | "chat/utils" 6 | "fmt" 7 | ) 8 | 9 | func CreateGenerationWithCache(group, model, prompt string, enableReverse bool, hook func(buffer *utils.Buffer, data string)) (string, error) { 10 | hash, path := GetFolderByHash(model, prompt) 11 | if !utils.Exists(path) { 12 | if err := CreateGeneration(group, model, prompt, path, enableReverse, hook); err != nil { 13 | globals.Info(fmt.Sprintf("[project] error during generation %s (model %s): %s", prompt, model, err.Error())) 14 | return "", fmt.Errorf("error during generate project: %s", err.Error()) 15 | } 16 | } 17 | 18 | if _, _, err := utils.GenerateCompressTask(hash, "addition/generation/data/out", path, path); err != nil { 19 | return "", fmt.Errorf("error during generate compress task: %s", err.Error()) 20 | } 21 | 22 | return hash, nil 23 | } 24 | -------------------------------------------------------------------------------- /addition/router.go: -------------------------------------------------------------------------------- 1 | package addition 2 | 3 | import ( 4 | "chat/addition/article" 5 | "chat/addition/card" 6 | "chat/addition/generation" 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | func Register(app *gin.RouterGroup) { 11 | { 12 | app.POST("/card", card.HandlerAPI) 13 | 14 | app.GET("/generation/create", generation.GenerateAPI) 15 | app.GET("/generation/download/tar", generation.ProjectTarDownloadAPI) 16 | app.GET("/generation/download/zip", generation.ProjectZipDownloadAPI) 17 | 18 | app.GET("/article/create", article.GenerateAPI) 19 | app.GET("/article/download/tar", article.ProjectTarDownloadAPI) 20 | app.GET("/article/download/zip", article.ProjectZipDownloadAPI) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /addition/web/duckduckgo.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "chat/channel" 5 | "chat/utils" 6 | "fmt" 7 | "net/url" 8 | "strings" 9 | ) 10 | 11 | type DDGResponse struct { 12 | Results []struct { 13 | Body string `json:"body"` 14 | Href string `json:"href"` 15 | Title string `json:"title"` 16 | } `json:"results"` 17 | } 18 | 19 | func formatResponse(data *DDGResponse) string { 20 | res := make([]string, 0) 21 | for _, item := range data.Results { 22 | if item.Body == "" || item.Href == "" || item.Title == "" { 23 | continue 24 | } 25 | 26 | res = append(res, fmt.Sprintf("%s (%s): %s", item.Title, item.Href, item.Body)) 27 | } 28 | 29 | return strings.Join(res, "\n") 30 | } 31 | 32 | func CallDuckDuckGoAPI(query string) *DDGResponse { 33 | data, err := utils.Get(fmt.Sprintf( 34 | "%s/search?q=%s&max_results=%d", 35 | channel.SystemInstance.GetSearchEndpoint(), 36 | url.QueryEscape(query), 37 | channel.SystemInstance.GetSearchQuery(), 38 | ), nil) 39 | 40 | if err != nil { 41 | return nil 42 | } 43 | 44 | return utils.MapToStruct[DDGResponse](data) 45 | } 46 | -------------------------------------------------------------------------------- /addition/web/search.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "chat/utils" 5 | "net/url" 6 | ) 7 | 8 | func GetBingUrl(q string) string { 9 | return "https://bing.com/search?q=" + url.QueryEscape(q) 10 | } 11 | 12 | func RequestWithUA(url string) string { 13 | data, err := utils.GetRaw(url, map[string]string{ 14 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/116.0", 15 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 16 | }) 17 | 18 | if err != nil { 19 | return "" 20 | } 21 | 22 | return data 23 | } 24 | 25 | func SearchWebResult(q string) string { 26 | if res := CallDuckDuckGoAPI(q); res != nil { 27 | if resp := formatResponse(res); resp != "" { 28 | return resp 29 | } 30 | } 31 | 32 | uri := GetBingUrl(q) 33 | if res := CallPilotAPI(uri); res != nil { 34 | return utils.Marshal(res.Results) 35 | } 36 | data := RequestWithUA(uri) 37 | return ParseBing(data) 38 | } 39 | -------------------------------------------------------------------------------- /addition/web/utils.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "chat/globals" 5 | "chat/manager/conversation" 6 | ) 7 | 8 | func UsingWebSegment(instance *conversation.Conversation) []globals.Message { 9 | segment := conversation.CopyMessage(instance.GetChatMessage()) 10 | 11 | if instance.IsEnableWeb() { 12 | segment = ChatWithWeb(segment) 13 | } 14 | 15 | return segment 16 | } 17 | 18 | func UsingWebNativeSegment(enable bool, message []globals.Message) []globals.Message { 19 | if enable { 20 | return ChatWithWeb(message) 21 | } else { 22 | return message 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /addition/web/webpilot.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "chat/utils" 5 | "github.com/google/uuid" 6 | ) 7 | 8 | type PilotResponseResult struct { 9 | Title string `json:"title"` 10 | Link string `json:"link"` 11 | Snippet string `json:"snippet"` 12 | } 13 | 14 | type PilotResponse struct { 15 | Results []PilotResponseResult `json:"extra_search_results" required:"true"` 16 | } 17 | 18 | func GenerateFriendUID() string { 19 | return uuid.New().String() 20 | } 21 | 22 | func CallPilotAPI(url string) *PilotResponse { 23 | data, err := utils.Post("https://webreader.webpilotai.com/api/visit-web", map[string]string{ 24 | "Content-Type": "application/json", 25 | "WebPilot-Friend-UID": GenerateFriendUID(), 26 | }, map[string]interface{}{ 27 | "link": url, 28 | "user_has_request": false, 29 | }) 30 | 31 | if err != nil { 32 | return nil 33 | } 34 | 35 | return utils.MapToStruct[PilotResponse](data) 36 | } 37 | -------------------------------------------------------------------------------- /admin/format.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func getMonth() string { 9 | date := time.Now() 10 | return date.Format("2006-01") 11 | } 12 | 13 | func getDay() string { 14 | date := time.Now() 15 | return date.Format("2006-01-02") 16 | } 17 | 18 | func getDays(n int) []time.Time { 19 | current := time.Now() 20 | var days []time.Time 21 | for i := n; i > 0; i-- { 22 | days = append(days, current.AddDate(0, 0, -i+1)) 23 | } 24 | 25 | return days 26 | } 27 | 28 | func getErrorFormat(t string) string { 29 | return fmt.Sprintf("nio:err-analysis-%s", t) 30 | } 31 | 32 | func getBillingFormat(t string) string { 33 | return fmt.Sprintf("nio:billing-analysis-%s", t) 34 | } 35 | 36 | func getMonthBillingFormat(t string) string { 37 | return fmt.Sprintf("nio:billing-analysis-%s", t) 38 | } 39 | 40 | func getRequestFormat(t string) string { 41 | return fmt.Sprintf("nio:request-analysis-%s", t) 42 | } 43 | 44 | func getModelFormat(t string, model string) string { 45 | return fmt.Sprintf("nio:model-analysis-%s-%s", model, t) 46 | } 47 | -------------------------------------------------------------------------------- /admin/instance.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | var MarketInstance *Market 4 | 5 | func InitInstance() { 6 | MarketInstance = NewMarket() 7 | } 8 | -------------------------------------------------------------------------------- /admin/logger.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "chat/globals" 5 | "chat/utils" 6 | "fmt" 7 | "github.com/gin-gonic/gin" 8 | "strings" 9 | ) 10 | 11 | type LogFile struct { 12 | Path string `json:"path"` 13 | Size int64 `json:"size"` 14 | } 15 | 16 | func ListLogs() []LogFile { 17 | return utils.Each(utils.Walk("logs"), func(path string) LogFile { 18 | return LogFile{ 19 | Path: strings.TrimLeft(path, "logs/"), 20 | Size: utils.GetFileSize(path), 21 | } 22 | }) 23 | } 24 | 25 | func getLogPath(path string) string { 26 | return fmt.Sprintf("logs/%s", path) 27 | } 28 | 29 | func getBlobFile(c *gin.Context, path string) { 30 | c.File(getLogPath(path)) 31 | } 32 | 33 | func deleteLogFile(path string) error { 34 | return utils.DeleteFile(getLogPath(path)) 35 | } 36 | 37 | func getLatestLogs(n int) string { 38 | if n <= 0 { 39 | n = 100 40 | } 41 | 42 | content, err := utils.ReadFileLatestLines(getLogPath(globals.DefaultLoggerFile), n) 43 | 44 | if err != nil { 45 | return fmt.Sprintf("read error: %s", err.Error()) 46 | } 47 | 48 | return content 49 | } 50 | -------------------------------------------------------------------------------- /admin/market.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "chat/globals" 5 | "fmt" 6 | "github.com/spf13/viper" 7 | ) 8 | 9 | type ModelTag []string 10 | type MarketModel struct { 11 | Id string `json:"id" mapstructure:"id" required:"true"` 12 | Name string `json:"name" mapstructure:"name" required:"true"` 13 | Description string `json:"description" mapstructure:"description"` 14 | Default bool `json:"default" mapstructure:"default"` 15 | HighContext bool `json:"high_context" mapstructure:"high_context"` 16 | Avatar string `json:"avatar" mapstructure:"avatar"` 17 | Tag ModelTag `json:"tag" mapstructure:"tag"` 18 | } 19 | type MarketModelList []MarketModel 20 | 21 | type Market struct { 22 | Models MarketModelList `json:"models" mapstructure:"models"` 23 | } 24 | 25 | func NewMarket() *Market { 26 | var models MarketModelList 27 | if err := viper.UnmarshalKey("market", &models); err != nil { 28 | globals.Warn(fmt.Sprintf("[market] read config error: %s, use default config", err.Error())) 29 | models = MarketModelList{} 30 | } 31 | 32 | return &Market{ 33 | Models: models, 34 | } 35 | } 36 | 37 | func (m *Market) GetModels() MarketModelList { 38 | return m.Models 39 | } 40 | 41 | func (m *Market) GetModel(id string) *MarketModel { 42 | for _, model := range m.Models { 43 | if model.Id == id { 44 | return &model 45 | } 46 | } 47 | return nil 48 | } 49 | 50 | func (m *Market) SaveConfig() error { 51 | viper.Set("market", m.Models) 52 | return viper.WriteConfig() 53 | } 54 | 55 | func (m *Market) SetModels(models MarketModelList) error { 56 | m.Models = models 57 | return m.SaveConfig() 58 | } 59 | -------------------------------------------------------------------------------- /admin/redeem.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "chat/utils" 5 | "database/sql" 6 | "fmt" 7 | "strings" 8 | ) 9 | 10 | func GetRedeemData(db *sql.DB) []RedeemData { 11 | var data []RedeemData 12 | 13 | rows, err := db.Query(` 14 | SELECT quota, COUNT(*) AS total, SUM(IF(used = 0, 0, 1)) AS used 15 | FROM redeem 16 | GROUP BY quota 17 | `) 18 | if err != nil { 19 | return data 20 | } 21 | 22 | for rows.Next() { 23 | var d RedeemData 24 | if err := rows.Scan(&d.Quota, &d.Total, &d.Used); err != nil { 25 | return data 26 | } 27 | data = append(data, d) 28 | } 29 | 30 | return data 31 | } 32 | 33 | func GenerateRedeemCodes(db *sql.DB, num int, quota float32) RedeemGenerateResponse { 34 | arr := make([]string, 0) 35 | idx := 0 36 | for idx < num { 37 | code, err := CreateRedeemCode(db, quota) 38 | 39 | if err != nil { 40 | return RedeemGenerateResponse{ 41 | Status: false, 42 | Message: err.Error(), 43 | } 44 | } 45 | arr = append(arr, code) 46 | idx++ 47 | } 48 | 49 | return RedeemGenerateResponse{ 50 | Status: true, 51 | Data: arr, 52 | } 53 | } 54 | 55 | func CreateRedeemCode(db *sql.DB, quota float32) (string, error) { 56 | code := fmt.Sprintf("nio-%s", utils.GenerateChar(32)) 57 | _, err := db.Exec(` 58 | INSERT INTO redeem (code, quota) VALUES (?, ?) 59 | `, code, quota) 60 | 61 | if err != nil && strings.Contains(err.Error(), "Duplicate entry") { 62 | // code name is duplicate 63 | return CreateRedeemCode(db, quota) 64 | } 65 | 66 | return code, err 67 | } 68 | -------------------------------------------------------------------------------- /admin/router.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "chat/channel" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func Register(app *gin.RouterGroup) { 9 | channel.Register(app) 10 | 11 | app.GET("/admin/analytics/info", InfoAPI) 12 | app.GET("/admin/analytics/model", ModelAnalysisAPI) 13 | app.GET("/admin/analytics/request", RequestAnalysisAPI) 14 | app.GET("/admin/analytics/billing", BillingAnalysisAPI) 15 | app.GET("/admin/analytics/error", ErrorAnalysisAPI) 16 | app.GET("/admin/analytics/user", UserTypeAnalysisAPI) 17 | 18 | app.GET("/admin/invitation/list", InvitationPaginationAPI) 19 | app.POST("/admin/invitation/generate", GenerateInvitationAPI) 20 | 21 | app.GET("/admin/redeem/list", RedeemListAPI) 22 | app.POST("/admin/redeem/generate", GenerateRedeemAPI) 23 | 24 | app.GET("/admin/user/list", UserPaginationAPI) 25 | app.POST("/admin/user/quota", UserQuotaAPI) 26 | app.POST("/admin/user/subscription", UserSubscriptionAPI) 27 | app.POST("/admin/user/root", UpdateRootPasswordAPI) 28 | 29 | app.POST("/admin/market/update", UpdateMarketAPI) 30 | 31 | app.GET("/admin/logger/list", ListLoggerAPI) 32 | app.GET("/admin/logger/download", DownloadLoggerAPI) 33 | app.GET("/admin/logger/console", ConsoleLoggerAPI) 34 | app.POST("/admin/logger/delete", DeleteLoggerAPI) 35 | } 36 | -------------------------------------------------------------------------------- /admin/statistic.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "chat/connection" 5 | "chat/utils" 6 | "github.com/go-redis/redis/v8" 7 | "time" 8 | ) 9 | 10 | func IncrErrorRequest(cache *redis.Client) { 11 | utils.IncrOnce(cache, getErrorFormat(getDay()), time.Hour*24*7*2) 12 | } 13 | 14 | func IncrBillingRequest(cache *redis.Client, amount int64) { 15 | utils.IncrWithExpire(cache, getBillingFormat(getDay()), amount, time.Hour*24*30*2) 16 | utils.IncrWithExpire(cache, getMonthBillingFormat(getMonth()), amount, time.Hour*24*30*2) 17 | } 18 | 19 | func IncrRequest(cache *redis.Client) { 20 | utils.IncrOnce(cache, getRequestFormat(getDay()), time.Hour*24*7*2) 21 | } 22 | 23 | func IncrModelRequest(cache *redis.Client, model string, tokens int64) { 24 | utils.IncrWithExpire(cache, getModelFormat(getDay(), model), tokens, time.Hour*24*7*2) 25 | } 26 | 27 | func AnalysisRequest(model string, buffer *utils.Buffer, err error) { 28 | instance := connection.Cache 29 | 30 | if err != nil && err.Error() != "signal" { 31 | IncrErrorRequest(instance) 32 | return 33 | } 34 | 35 | IncrRequest(instance) 36 | IncrModelRequest(instance, model, int64(buffer.CountToken())) 37 | } 38 | -------------------------------------------------------------------------------- /app/.env.deeptrain: -------------------------------------------------------------------------------- 1 | VITE_USE_DEEPTRAIN=true 2 | VITE_BACKEND_ENDPOINT=https://api.chatnio.net 3 | -------------------------------------------------------------------------------- /app/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | dev-dist 14 | *.local 15 | 16 | # Editor directories and files 17 | .vscode/* 18 | !.vscode/extensions.json 19 | .idea 20 | .DS_Store 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | 27 | # Libre 28 | db 29 | -------------------------------------------------------------------------------- /app/.prettierrc.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /app/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true 11 | }, 12 | "aliases": { 13 | "components": "src/components", 14 | "utils": "@/components/ui/lib/utils" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |{code}
34 |{prompt}
13 |