mirror of
https://github.com/coaidev/coai.git
synced 2025-05-19 04:50:14 +09:00
feat: support for channel configuration proxy (#101)
This commit is contained in:
parent
9323abb318
commit
e037276387
@ -42,6 +42,7 @@ var channelFactories = map[string]adaptercommon.FactoryCreator{
|
||||
|
||||
func createChatRequest(conf globals.ChannelConfig, props *adaptercommon.ChatProps, hook globals.Hook) error {
|
||||
props.Model = conf.GetModelReflect(props.OriginalModel)
|
||||
props.Proxy = conf.GetProxy()
|
||||
|
||||
factoryType := conf.GetType()
|
||||
if factory, ok := channelFactories[factoryType]; ok {
|
||||
|
@ -66,6 +66,7 @@ func (c *ChatInstance) CreateChatRequest(props *adaptercommon.ChatProps) (string
|
||||
c.GetChatEndpoint(props),
|
||||
c.GetHeader(),
|
||||
c.GetChatBody(props, false),
|
||||
props.Proxy,
|
||||
)
|
||||
|
||||
if err != nil || res == nil {
|
||||
@ -108,7 +109,7 @@ func (c *ChatInstance) CreateStreamChatRequest(props *adaptercommon.ChatProps, c
|
||||
}
|
||||
return callback(partial)
|
||||
},
|
||||
})
|
||||
}, props.Proxy)
|
||||
|
||||
if err != nil {
|
||||
if form := processChatErrorResponse(err.Body); form != nil {
|
||||
|
@ -12,6 +12,7 @@ type ImageProps struct {
|
||||
Model string
|
||||
Prompt string
|
||||
Size ImageSize
|
||||
Proxy globals.ProxyConfig
|
||||
}
|
||||
|
||||
func (c *ChatInstance) GetImageEndpoint(model string) string {
|
||||
@ -31,7 +32,7 @@ func (c *ChatInstance) CreateImageRequest(props ImageProps) (string, error) {
|
||||
ImageSize512,
|
||||
),
|
||||
N: 1,
|
||||
})
|
||||
}, props.Proxy)
|
||||
if err != nil || res == nil {
|
||||
return "", fmt.Errorf("openai error: %s", err.Error())
|
||||
}
|
||||
@ -51,6 +52,7 @@ func (c *ChatInstance) CreateImage(props *adaptercommon.ChatProps) (string, erro
|
||||
url, err := c.CreateImageRequest(ImageProps{
|
||||
Model: props.Model,
|
||||
Prompt: c.GetLatestPrompt(props),
|
||||
Proxy: props.Proxy,
|
||||
})
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "safety") {
|
||||
|
@ -48,6 +48,7 @@ func (c *ChatInstance) CreateChatRequest(props *adaptercommon.ChatProps) (string
|
||||
c.GetChatEndpoint(),
|
||||
c.GetHeader(),
|
||||
c.GetChatBody(props, false),
|
||||
props.Proxy,
|
||||
)
|
||||
|
||||
if err != nil || res == nil {
|
||||
@ -77,7 +78,7 @@ func (c *ChatInstance) CreateStreamChatRequest(props *adaptercommon.ChatProps, c
|
||||
}
|
||||
return callback(partial)
|
||||
},
|
||||
})
|
||||
}, props.Proxy)
|
||||
|
||||
if err != nil {
|
||||
if form := processChatErrorResponse(err.Body); form != nil {
|
||||
|
@ -11,18 +11,19 @@ import (
|
||||
const defaultTokens = 2500
|
||||
|
||||
func (c *ChatInstance) GetChatEndpoint() string {
|
||||
return fmt.Sprintf("%s/v1/complete", c.GetEndpoint())
|
||||
return fmt.Sprintf("%s/v1/messages", c.GetEndpoint())
|
||||
}
|
||||
|
||||
func (c *ChatInstance) GetChatHeaders() map[string]string {
|
||||
return map[string]string{
|
||||
"content-type": "application/json",
|
||||
"accept": "application/json",
|
||||
"x-api-key": c.GetApiKey(),
|
||||
"content-type": "application/json",
|
||||
"anthropic-version": "2023-06-01",
|
||||
"x-api-key": c.GetApiKey(),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ChatInstance) ConvertMessage(message []globals.Message) string {
|
||||
// ConvertCompletionMessage converts the completion message to anthropic complete format (deprecated)
|
||||
func (c *ChatInstance) ConvertCompletionMessage(message []globals.Message) string {
|
||||
mapper := map[string]string{
|
||||
globals.System: "Assistant",
|
||||
globals.User: "Human",
|
||||
@ -54,19 +55,19 @@ func (c *ChatInstance) GetTokens(props *adaptercommon.ChatProps) int {
|
||||
|
||||
func (c *ChatInstance) GetChatBody(props *adaptercommon.ChatProps, stream bool) *ChatBody {
|
||||
return &ChatBody{
|
||||
Prompt: c.ConvertMessage(props.Message),
|
||||
MaxTokensToSample: c.GetTokens(props),
|
||||
Model: props.Model,
|
||||
Stream: stream,
|
||||
Temperature: props.Temperature,
|
||||
TopP: props.TopP,
|
||||
TopK: props.TopK,
|
||||
Messages: props.Message,
|
||||
MaxTokens: c.GetTokens(props),
|
||||
Model: props.Model,
|
||||
Stream: stream,
|
||||
Temperature: props.Temperature,
|
||||
TopP: props.TopP,
|
||||
TopK: props.TopK,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateChatRequest is the request for anthropic claude
|
||||
func (c *ChatInstance) CreateChatRequest(props *adaptercommon.ChatProps) (string, error) {
|
||||
data, err := utils.Post(c.GetChatEndpoint(), c.GetChatHeaders(), c.GetChatBody(props, false))
|
||||
data, err := utils.Post(c.GetChatEndpoint(), c.GetChatHeaders(), c.GetChatBody(props, false), props.Proxy)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("claude error: %s", err.Error())
|
||||
}
|
||||
@ -126,5 +127,7 @@ func (c *ChatInstance) CreateStreamChatRequest(props *adaptercommon.ChatProps, h
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
},
|
||||
props.Proxy,
|
||||
)
|
||||
}
|
||||
|
@ -1,14 +1,16 @@
|
||||
package claude
|
||||
|
||||
import "chat/globals"
|
||||
|
||||
// ChatBody is the request body for anthropic claude
|
||||
type ChatBody struct {
|
||||
Prompt string `json:"prompt"`
|
||||
MaxTokensToSample int `json:"max_tokens_to_sample"`
|
||||
Model string `json:"model"`
|
||||
Stream bool `json:"stream"`
|
||||
Temperature *float32 `json:"temperature,omitempty"`
|
||||
TopP *float32 `json:"top_p,omitempty"`
|
||||
TopK *int `json:"top_k,omitempty"`
|
||||
Messages []globals.Message `json:"messages"`
|
||||
MaxTokens int `json:"max_tokens"`
|
||||
Model string `json:"model"`
|
||||
Stream bool `json:"stream"`
|
||||
Temperature *float32 `json:"temperature,omitempty"`
|
||||
TopP *float32 `json:"top_p,omitempty"`
|
||||
TopK *int `json:"top_k,omitempty"`
|
||||
}
|
||||
|
||||
// ChatResponse is the native http request and stream response for anthropic claude
|
||||
|
@ -9,6 +9,8 @@ type RequestProps struct {
|
||||
MaxRetries *int
|
||||
Current int
|
||||
Group string
|
||||
|
||||
Proxy globals.ProxyConfig
|
||||
}
|
||||
|
||||
type ChatProps struct {
|
||||
|
@ -127,5 +127,6 @@ func (c *ChatInstance) CreateStreamChatRequest(props *adaptercommon.ChatProps, c
|
||||
|
||||
return nil
|
||||
},
|
||||
props.Proxy,
|
||||
)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package midjourney
|
||||
|
||||
import (
|
||||
"chat/globals"
|
||||
"chat/utils"
|
||||
"fmt"
|
||||
)
|
||||
@ -29,11 +30,12 @@ func (c *ChatInstance) GetChangeRequest(action string, task string, index *int)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ChatInstance) CreateImagineRequest(prompt string) (*CommonResponse, error) {
|
||||
func (c *ChatInstance) CreateImagineRequest(proxy globals.ProxyConfig, prompt string) (*CommonResponse, error) {
|
||||
content, err := utils.PostRaw(
|
||||
c.GetImagineEndpoint(),
|
||||
c.GetMidjourneyHeaders(),
|
||||
c.GetImagineRequest(prompt),
|
||||
proxy,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
@ -47,11 +49,12 @@ func (c *ChatInstance) CreateImagineRequest(prompt string) (*CommonResponse, err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ChatInstance) CreateChangeRequest(action string, task string, index *int) (*CommonResponse, error) {
|
||||
func (c *ChatInstance) CreateChangeRequest(proxy globals.ProxyConfig, action string, task string, index *int) (*CommonResponse, error) {
|
||||
res, err := utils.Post(
|
||||
c.GetChangeEndpoint(),
|
||||
c.GetMidjourneyHeaders(),
|
||||
c.GetChangeRequest(action, task, index),
|
||||
proxy,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
|
@ -95,7 +95,7 @@ func (c *ChatInstance) CreateStreamChatRequest(props *adaptercommon.ChatProps, c
|
||||
|
||||
var begin bool
|
||||
|
||||
form, err := c.CreateStreamTask(action, prompt, func(form *StorageForm, progress int) error {
|
||||
form, err := c.CreateStreamTask(props, action, prompt, func(form *StorageForm, progress int) error {
|
||||
if progress == -1 {
|
||||
// ping event
|
||||
return callback(&globals.Chunk{Content: ""})
|
||||
|
@ -1,6 +1,8 @@
|
||||
package midjourney
|
||||
|
||||
import (
|
||||
adaptercommon "chat/adapter/common"
|
||||
"chat/globals"
|
||||
"chat/utils"
|
||||
"fmt"
|
||||
"strings"
|
||||
@ -66,21 +68,21 @@ func (c *ChatInstance) ExtractCommand(input string) (task string, index *int) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ChatInstance) CreateRequest(action string, prompt string) (*CommonResponse, error) {
|
||||
func (c *ChatInstance) CreateRequest(proxy globals.ProxyConfig, action string, prompt string) (*CommonResponse, error) {
|
||||
switch action {
|
||||
case ImagineCommand:
|
||||
return c.CreateImagineRequest(prompt)
|
||||
return c.CreateImagineRequest(proxy, prompt)
|
||||
case VariationCommand, UpscaleCommand, RerollCommand:
|
||||
task, index := c.ExtractCommand(prompt)
|
||||
|
||||
return c.CreateChangeRequest(c.GetAction(action), task, index)
|
||||
return c.CreateChangeRequest(proxy, c.GetAction(action), task, index)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown action: %s", action)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ChatInstance) CreateStreamTask(action string, prompt string, hook func(form *StorageForm, progress int) error) (*StorageForm, error) {
|
||||
res, err := c.CreateRequest(action, prompt)
|
||||
func (c *ChatInstance) CreateStreamTask(props *adaptercommon.ChatProps, action string, prompt string, hook func(form *StorageForm, progress int) error) (*StorageForm, error) {
|
||||
res, err := c.CreateRequest(props.Proxy, action, prompt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ func (c *ChatInstance) CreateChatRequest(props *adaptercommon.ChatProps) (string
|
||||
c.GetChatEndpoint(props),
|
||||
c.GetHeader(),
|
||||
c.GetChatBody(props, false),
|
||||
props.Proxy,
|
||||
)
|
||||
|
||||
if err != nil || res == nil {
|
||||
@ -120,7 +121,7 @@ func (c *ChatInstance) CreateStreamChatRequest(props *adaptercommon.ChatProps, c
|
||||
}
|
||||
return callback(partial)
|
||||
},
|
||||
})
|
||||
}, props.Proxy)
|
||||
|
||||
if err != nil {
|
||||
if form := processChatErrorResponse(err.Body); form != nil {
|
||||
|
@ -12,6 +12,7 @@ type ImageProps struct {
|
||||
Model string
|
||||
Prompt string
|
||||
Size ImageSize
|
||||
Proxy globals.ProxyConfig
|
||||
}
|
||||
|
||||
func (c *ChatInstance) GetImageEndpoint() string {
|
||||
@ -31,7 +32,7 @@ func (c *ChatInstance) CreateImageRequest(props ImageProps) (string, error) {
|
||||
ImageSize512,
|
||||
),
|
||||
N: 1,
|
||||
})
|
||||
}, props.Proxy)
|
||||
if err != nil || res == nil {
|
||||
return "", fmt.Errorf(err.Error())
|
||||
}
|
||||
@ -51,6 +52,7 @@ func (c *ChatInstance) CreateImage(props *adaptercommon.ChatProps) (string, erro
|
||||
original, err := c.CreateImageRequest(ImageProps{
|
||||
Model: props.Model,
|
||||
Prompt: c.GetLatestPrompt(props),
|
||||
Proxy: props.Proxy,
|
||||
})
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "safety") {
|
||||
|
@ -93,7 +93,7 @@ func (c *ChatInstance) CreateChatRequest(props *adaptercommon.ChatProps) (string
|
||||
if props.Model == globals.ChatBison001 {
|
||||
data, err := utils.Post(uri, map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
}, c.GetPalm2ChatBody(props))
|
||||
}, c.GetPalm2ChatBody(props), props.Proxy)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("palm2 error: %s", err.Error())
|
||||
@ -103,7 +103,7 @@ func (c *ChatInstance) CreateChatRequest(props *adaptercommon.ChatProps) (string
|
||||
|
||||
data, err := utils.Post(uri, map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
}, c.GetGeminiChatBody(props))
|
||||
}, c.GetGeminiChatBody(props), props.Proxy)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("gemini error: %s", err.Error())
|
||||
|
@ -51,6 +51,7 @@ func (c *ChatInstance) CreateChatRequest(props *adaptercommon.ChatProps) (string
|
||||
c.GetChatEndpoint(),
|
||||
c.GetHeader(),
|
||||
c.GetChatBody(props, false),
|
||||
props.Proxy,
|
||||
)
|
||||
|
||||
if err != nil || res == nil {
|
||||
@ -100,6 +101,7 @@ func (c *ChatInstance) CreateStreamChatRequest(props *adaptercommon.ChatProps, c
|
||||
}
|
||||
return nil
|
||||
},
|
||||
props.Proxy,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
|
@ -69,5 +69,6 @@ func (c *ChatInstance) CreateStreamChatRequest(props *adaptercommon.ChatProps, h
|
||||
data = strings.TrimPrefix(data, "data:")
|
||||
return hook(&globals.Chunk{Content: data})
|
||||
},
|
||||
props.Proxy,
|
||||
)
|
||||
}
|
||||
|
@ -20,6 +20,24 @@ export type Channel = {
|
||||
mapper: string;
|
||||
state: boolean;
|
||||
group?: string[];
|
||||
proxy?: {
|
||||
proxy: string;
|
||||
proxy_type: number;
|
||||
};
|
||||
};
|
||||
|
||||
export enum proxyType {
|
||||
NoneProxy = 0,
|
||||
HttpProxy = 1,
|
||||
HttpsProxy = 2,
|
||||
Socks5Proxy = 3,
|
||||
}
|
||||
|
||||
export const ProxyTypes: Record<number, string> = {
|
||||
[proxyType.NoneProxy]: "None Proxy",
|
||||
[proxyType.HttpProxy]: "HTTP Proxy",
|
||||
[proxyType.HttpsProxy]: "HTTPS Proxy",
|
||||
[proxyType.Socks5Proxy]: "SOCKS5 Proxy",
|
||||
};
|
||||
|
||||
export type ChannelInfo = {
|
||||
@ -133,7 +151,16 @@ export const ChannelInfos: Record<string, ChannelInfo> = {
|
||||
claude: {
|
||||
endpoint: "https://api.anthropic.com",
|
||||
format: "<x-api-key>",
|
||||
models: ["claude-instant-1", "claude-2", "claude-2.1"],
|
||||
description:
|
||||
"> Anthropic Claude 密钥格式为 **x-api-key**,Anthropic 对请求 IP 地域有限制,可能出现 **Request not allowed** 的错误,请尝试更换 IP 或者使用代理。\n",
|
||||
models: [
|
||||
"claude-instant-1.2",
|
||||
"claude-2",
|
||||
"claude-2.1",
|
||||
"claude-3-opus-20240229",
|
||||
"claude-3-sonnet-20240229",
|
||||
"claude-3-haiku-20240307",
|
||||
],
|
||||
},
|
||||
slack: {
|
||||
endpoint: "your-channel",
|
||||
|
@ -40,6 +40,10 @@ export const modelColorMapper: Record<string, string> = {
|
||||
"claude-2": "orange-400",
|
||||
"claude-2.1": "orange-400",
|
||||
"claude-2-100k": "orange-400",
|
||||
"claude-instant-1.2": "orange-400",
|
||||
"claude-3-opus-20240229": "orange-400",
|
||||
"claude-3-sonnet-20240229": "orange-400",
|
||||
"claude-3-haiku-20240307": "orange-400",
|
||||
|
||||
"spark-desk-v1.5": "blue-400",
|
||||
"spark-desk-v2": "blue-400",
|
||||
|
@ -88,6 +88,7 @@ export const pricing: PricingDataset = [
|
||||
"claude-1.2",
|
||||
"claude-1.3",
|
||||
"claude-instant",
|
||||
"claude-instant-1.2",
|
||||
"claude-slack",
|
||||
],
|
||||
input: 0.0008,
|
||||
|
@ -91,7 +91,7 @@ strong {
|
||||
|
||||
.flex-dialog {
|
||||
border-radius: var(--radius) !important;
|
||||
max-height: calc(95vh - 2rem) !important;
|
||||
max-height: calc(95% - 2rem) !important;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none;
|
||||
@ -128,7 +128,7 @@ strong {
|
||||
|
||||
.fixed-dialog {
|
||||
border-radius: var(--radius) !important;
|
||||
max-height: calc(95vh - 2rem) !important;
|
||||
max-height: calc(95% - 2rem) !important;
|
||||
min-height: 60vh;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
@ -237,6 +237,7 @@
|
||||
border: 1px solid var(--assistant-border);
|
||||
border-radius: var(--radius);
|
||||
margin: 0.25rem 0;
|
||||
white-space: nowrap;
|
||||
|
||||
.grow {
|
||||
min-width: 0.75rem;
|
||||
|
@ -1,4 +1,4 @@
|
||||
.share-table {
|
||||
max-width: 85vw;
|
||||
width: 100%;
|
||||
height: max-content;
|
||||
}
|
||||
|
@ -16,6 +16,10 @@ const initialState: Channel = {
|
||||
mapper: "",
|
||||
state: true,
|
||||
group: [],
|
||||
proxy: {
|
||||
proxy: "",
|
||||
proxy_type: 0,
|
||||
},
|
||||
};
|
||||
|
||||
function reducer(state: Channel, action: any): Channel {
|
||||
@ -77,6 +81,22 @@ function reducer(state: Channel, action: any): Channel {
|
||||
};
|
||||
case "set-group":
|
||||
return { ...state, group: action.value };
|
||||
case "set-proxy":
|
||||
return {
|
||||
...state,
|
||||
proxy: {
|
||||
proxy: action.value as string,
|
||||
proxy_type: state?.proxy?.proxy_type || 0,
|
||||
},
|
||||
};
|
||||
case "set-proxy-type":
|
||||
return {
|
||||
...state,
|
||||
proxy: {
|
||||
proxy: state?.proxy?.proxy || "",
|
||||
proxy_type: action.value as number,
|
||||
},
|
||||
};
|
||||
case "set":
|
||||
return { ...state, ...action.value };
|
||||
default:
|
||||
|
@ -13,9 +13,11 @@ import {
|
||||
channelGroups,
|
||||
ChannelTypes,
|
||||
getChannelInfo,
|
||||
proxyType,
|
||||
ProxyTypes,
|
||||
} from "@/admin/channel.ts";
|
||||
import { CommonResponse, toastState } from "@/api/common.ts";
|
||||
import { Textarea } from "@/components/ui/textarea.tsx";
|
||||
import { FlexibleTextarea, Textarea } from "@/components/ui/textarea.tsx";
|
||||
import { NumberInput } from "@/components/ui/number-input.tsx";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@ -44,6 +46,7 @@ import { useEffectAsync } from "@/utils/hook.ts";
|
||||
import Paragraph, {
|
||||
ParagraphDescription,
|
||||
ParagraphItem,
|
||||
ParagraphSpace,
|
||||
} from "@/components/Paragraph.tsx";
|
||||
import { MultiCombobox } from "@/components/ui/multi-combobox.tsx";
|
||||
import { useChannelModels } from "@/admin/hook.tsx";
|
||||
@ -116,6 +119,14 @@ function handler(data: Channel): Channel {
|
||||
data.group = data.group
|
||||
? data.group.filter((group) => group.trim() !== "")
|
||||
: [];
|
||||
|
||||
if (
|
||||
data.proxy &&
|
||||
data.proxy.proxy.trim() === "" &&
|
||||
data.proxy.proxy_type !== 0
|
||||
) {
|
||||
data.proxy.proxy_type = 0;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -361,7 +372,8 @@ function ChannelEditor({
|
||||
{t("admin.channels.mapper")}
|
||||
<Tips content={t("admin.channels.mapper-tip")} />
|
||||
</div>
|
||||
<Textarea
|
||||
<FlexibleTextarea
|
||||
rows={5}
|
||||
value={edit.mapper}
|
||||
placeholder={t("admin.channels.mapper-placeholder")}
|
||||
onChange={(e) =>
|
||||
@ -394,6 +406,56 @@ function ChannelEditor({
|
||||
<ParagraphDescription>
|
||||
{t("admin.channels.group-desc")}
|
||||
</ParagraphDescription>
|
||||
<ParagraphSpace />
|
||||
<ParagraphItem>
|
||||
<div className={`channel-row column-layout`}>
|
||||
<div className={`channel-content`}>
|
||||
{t("admin.channels.proxy-type")}
|
||||
</div>
|
||||
<Select
|
||||
value={(edit.proxy?.proxy_type || 0).toString()}
|
||||
onValueChange={(value: string) =>
|
||||
dispatch({ type: "set-proxy-type", value: parseInt(value) })
|
||||
}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value={"0"}>
|
||||
{ProxyTypes[proxyType.NoneProxy]}
|
||||
</SelectItem>
|
||||
<SelectItem value={"1"}>
|
||||
{ProxyTypes[proxyType.HttpProxy]}
|
||||
</SelectItem>
|
||||
<SelectItem value={"2"}>
|
||||
{ProxyTypes[proxyType.HttpsProxy]}
|
||||
</SelectItem>
|
||||
<SelectItem value={"3"}>
|
||||
{ProxyTypes[proxyType.Socks5Proxy]}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</ParagraphItem>
|
||||
<ParagraphItem>
|
||||
<div className={`channel-content`}>
|
||||
{t("admin.channels.proxy-endpoint")}
|
||||
</div>
|
||||
<Input
|
||||
value={edit.proxy?.proxy || ""}
|
||||
placeholder={t("admin.channels.proxy-endpoint-placeholder")}
|
||||
onChange={(e) =>
|
||||
dispatch({ type: "set-proxy", value: e.target.value })
|
||||
}
|
||||
disabled={edit.proxy?.proxy_type === 0}
|
||||
/>
|
||||
</ParagraphItem>
|
||||
<ParagraphDescription>
|
||||
{t("admin.channels.proxy-desc")}
|
||||
</ParagraphDescription>
|
||||
</Paragraph>
|
||||
</div>
|
||||
<div className={`mt-4 flex flex-row w-full h-max pr-2 items-center`}>
|
||||
|
@ -100,6 +100,7 @@ function SyncDialog({ dispatch, open, setOpen }: SyncDialogProps) {
|
||||
mapper: "",
|
||||
state: true,
|
||||
group: [],
|
||||
proxy: { proxy: "", proxy_type: 0 },
|
||||
};
|
||||
|
||||
dispatch({ type: "set", value: data });
|
||||
|
@ -32,7 +32,7 @@ function QuotaExceededForm({
|
||||
const dispatch = useDispatch();
|
||||
|
||||
return (
|
||||
<div className={`flex flex-col items-center w-[40vw] max-w-[320px] py-2`}>
|
||||
<div className={`flex flex-col items-center min-w-[40vw] p-2`}>
|
||||
<img src={appLogo} alt={""} className={`w-16 h-16 m-6 inline-block`} />
|
||||
<div
|
||||
className={`prompt-row flex flex-row w-full items-center justify-center px-4 py-2`}
|
||||
|
@ -31,12 +31,12 @@ export interface FlexibleTextareaProps extends TextareaProps {
|
||||
const FlexibleTextarea = React.forwardRef<
|
||||
HTMLTextAreaElement,
|
||||
FlexibleTextareaProps
|
||||
>(({ rows = 1, className, ...props }, ref) => {
|
||||
>(({ rows = 1, minRows, className, ...props }, ref) => {
|
||||
const lines = useMemo(() => {
|
||||
const value = props.value?.toString() || "";
|
||||
const count = value.split("\n").length + 1;
|
||||
return Math.max(rows, count);
|
||||
}, [props.value]);
|
||||
return Math.max(rows, count, minRows || 1);
|
||||
}, [props.value, rows, minRows]);
|
||||
|
||||
return (
|
||||
<Textarea
|
||||
|
@ -14,7 +14,6 @@ import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog.tsx";
|
||||
@ -26,7 +25,7 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table.tsx";
|
||||
import { closeDialog, setDialog } from "@/store/sharing.ts";
|
||||
import { setDialog } from "@/store/sharing.ts";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { useMemo } from "react";
|
||||
import { Eye, MoreHorizontal, Trash2 } from "lucide-react";
|
||||
@ -60,7 +59,9 @@ function ShareTable({ data }: ShareTableProps) {
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>ID</TableHead>
|
||||
<TableHead>{t("share.name")}</TableHead>
|
||||
<TableHead className={`whitespace-nowrap`}>
|
||||
{t("share.name")}
|
||||
</TableHead>
|
||||
<TableHead>{t("share.time")}</TableHead>
|
||||
<TableHead>{t("share.action")}</TableHead>
|
||||
</TableRow>
|
||||
@ -69,7 +70,7 @@ function ShareTable({ data }: ShareTableProps) {
|
||||
{data.map((row, idx) => (
|
||||
<TableRow key={idx}>
|
||||
<TableCell>{row.conversation_id}</TableCell>
|
||||
<TableCell>{row.name}</TableCell>
|
||||
<TableCell className={`whitespace-nowrap`}>{row.name}</TableCell>
|
||||
<TableCell className={`whitespace-nowrap`}>{time[idx]}</TableCell>
|
||||
<TableCell>
|
||||
<DropdownMenu>
|
||||
@ -146,11 +147,6 @@ function ShareManagementDialog() {
|
||||
</DialogDescription>
|
||||
)}
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<Button variant={`outline`} onClick={() => dispatch(closeDialog())}>
|
||||
{t("close")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
|
@ -629,7 +629,11 @@
|
||||
"standard": "标准版订阅用户",
|
||||
"pro": "专业版订阅用户",
|
||||
"admin": "管理员用户"
|
||||
}
|
||||
},
|
||||
"proxy-type": "代理类型",
|
||||
"proxy-endpoint": "代理地址",
|
||||
"proxy-endpoint-placeholder": "请输入正向代理地址,如:socks5://example.com:1080",
|
||||
"proxy-desc": "正向代理,支持 HTTP/HTTPS/SOCKS5 代理 (反向代理请填写接入点, 非特殊情况无需设置正向代理)"
|
||||
},
|
||||
"charge": {
|
||||
"id": "ID",
|
||||
|
@ -457,7 +457,11 @@
|
||||
"sync-success": "Sync successfully.",
|
||||
"sync-success-prompt": "{{length}} models were added from upstream synchronization.",
|
||||
"upstream-endpoint-placeholder": "Please enter the upstream OpenAI address, e.g. https://api.openai.com",
|
||||
"sync-secret-placeholder": "Please enter API key for upstream channel"
|
||||
"sync-secret-placeholder": "Please enter API key for upstream channel",
|
||||
"proxy-type": "Delegate Type",
|
||||
"proxy-endpoint": "proxy address",
|
||||
"proxy-endpoint-placeholder": "Please enter forward proxy address, ex: socks5://example.com: 1080",
|
||||
"proxy-desc": "Forward proxy, supports HTTP/HTTPS/SOCKS5 proxy (reverse proxy please fill in the access point, no need to set forward proxy in non-special cases)"
|
||||
},
|
||||
"charge": {
|
||||
"id": "ID",
|
||||
|
@ -457,7 +457,11 @@
|
||||
"sync-success": "同期成功",
|
||||
"sync-success-prompt": "{{length}}モデルがアップストリーム同期から追加されました。",
|
||||
"upstream-endpoint-placeholder": "上流のOpenAIアドレスを入力してください。例: https://api.openai.com",
|
||||
"sync-secret-placeholder": "アップストリームチャネルのAPIキーを入力してください"
|
||||
"sync-secret-placeholder": "アップストリームチャネルのAPIキーを入力してください",
|
||||
"proxy-type": "プロキシのタイプ",
|
||||
"proxy-endpoint": "プロキシアドレス",
|
||||
"proxy-endpoint-placeholder": "転送プロキシアドレスを入力してください。例: socks 5 :// example.com: 1080",
|
||||
"proxy-desc": "フォワードプロキシ、HTTP/HTTPS/SOCKS 5プロキシをサポート(リバースプロキシはアクセスポイントに記入してください。特別な場合以外はフォワードプロキシを設定する必要はありません)"
|
||||
},
|
||||
"charge": {
|
||||
"id": "ID",
|
||||
|
@ -457,7 +457,11 @@
|
||||
"sync-success": "Успешная синхронизация",
|
||||
"sync-success-prompt": "{{length}} модели были добавлены из синхронизации восходящего потока.",
|
||||
"upstream-endpoint-placeholder": "Введите вышестоящий адрес OpenAI, например, https://api.openai.com",
|
||||
"sync-secret-placeholder": "Пожалуйста, введите ключ API для восходящего канала"
|
||||
"sync-secret-placeholder": "Пожалуйста, введите ключ API для восходящего канала",
|
||||
"proxy-type": "Тип прокси- сервера",
|
||||
"proxy-endpoint": "Адрес прокси-сервера",
|
||||
"proxy-endpoint-placeholder": "Введите адрес прямого прокси-сервера, например: socks5://example.com: 1080",
|
||||
"proxy-desc": "Прямой прокси-сервер, поддерживает HTTP/HTTPS/Socks5 прокси-сервер (обратный прокси-сервер, пожалуйста, заполните точку доступа, нет необходимости устанавливать прокси-сервер в неспециальных случаях)"
|
||||
},
|
||||
"charge": {
|
||||
"id": "ID",
|
||||
|
@ -1,6 +1,7 @@
|
||||
package channel
|
||||
|
||||
import (
|
||||
"chat/globals"
|
||||
"chat/utils"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -174,6 +175,10 @@ func (c *Channel) GetGroup() []string {
|
||||
return c.Group
|
||||
}
|
||||
|
||||
func (c *Channel) GetProxy() globals.ProxyConfig {
|
||||
return c.Proxy
|
||||
}
|
||||
|
||||
func (c *Channel) IsHitGroup(group string) bool {
|
||||
if len(c.GetGroup()) == 0 {
|
||||
return true
|
||||
|
@ -1,22 +1,27 @@
|
||||
package channel
|
||||
|
||||
import (
|
||||
"chat/globals"
|
||||
)
|
||||
|
||||
type Channel struct {
|
||||
Id int `json:"id" mapstructure:"id"`
|
||||
Name string `json:"name" mapstructure:"name"`
|
||||
Type string `json:"type" mapstructure:"type"`
|
||||
Priority int `json:"priority" mapstructure:"priority"`
|
||||
Weight int `json:"weight" mapstructure:"weight"`
|
||||
Models []string `json:"models" mapstructure:"models"`
|
||||
Retry int `json:"retry" mapstructure:"retry"`
|
||||
Secret string `json:"secret" mapstructure:"secret"`
|
||||
Endpoint string `json:"endpoint" mapstructure:"endpoint"`
|
||||
Mapper string `json:"mapper" mapstructure:"mapper"`
|
||||
State bool `json:"state" mapstructure:"state"`
|
||||
Group []string `json:"group" mapstructure:"group"`
|
||||
Reflect *map[string]string `json:"-"`
|
||||
HitModels *[]string `json:"-"`
|
||||
ExcludeModels *[]string `json:"-"`
|
||||
CurrentSecret *string `json:"-"`
|
||||
Id int `json:"id" mapstructure:"id"`
|
||||
Name string `json:"name" mapstructure:"name"`
|
||||
Type string `json:"type" mapstructure:"type"`
|
||||
Priority int `json:"priority" mapstructure:"priority"`
|
||||
Weight int `json:"weight" mapstructure:"weight"`
|
||||
Models []string `json:"models" mapstructure:"models"`
|
||||
Retry int `json:"retry" mapstructure:"retry"`
|
||||
Secret string `json:"secret" mapstructure:"secret"`
|
||||
Endpoint string `json:"endpoint" mapstructure:"endpoint"`
|
||||
Mapper string `json:"mapper" mapstructure:"mapper"`
|
||||
State bool `json:"state" mapstructure:"state"`
|
||||
Group []string `json:"group" mapstructure:"group"`
|
||||
Proxy globals.ProxyConfig `json:"proxy" mapstructure:"proxy"`
|
||||
Reflect *map[string]string `json:"-"`
|
||||
HitModels *[]string `json:"-"`
|
||||
ExcludeModels *[]string `json:"-"`
|
||||
CurrentSecret *string `json:"-"`
|
||||
}
|
||||
|
||||
type Sequence []*Channel
|
||||
|
@ -41,3 +41,10 @@ const (
|
||||
ProType = "pro" // pro subscription
|
||||
AdminType = "admin"
|
||||
)
|
||||
|
||||
const (
|
||||
NoneProxyType = iota
|
||||
HttpProxyType
|
||||
HttpsProxyType
|
||||
Socks5ProxyType
|
||||
)
|
||||
|
@ -11,6 +11,7 @@ type ChannelConfig interface {
|
||||
GetEndpoint() string
|
||||
ProcessError(err error) error
|
||||
GetId() int
|
||||
GetProxy() ProxyConfig
|
||||
}
|
||||
|
||||
type AuthLike interface {
|
||||
|
@ -45,3 +45,8 @@ type ListModelsItem struct {
|
||||
Created int64 `json:"created"`
|
||||
OwnedBy string `json:"owned_by"`
|
||||
}
|
||||
|
||||
type ProxyConfig struct {
|
||||
ProxyType int `json:"proxy_type" mapstructure:"proxytype"`
|
||||
Proxy string `json:"proxy" mapstructure:"proxy"`
|
||||
}
|
||||
|
75
utils/net.go
75
utils/net.go
@ -3,11 +3,15 @@ package utils
|
||||
import (
|
||||
"bytes"
|
||||
"chat/globals"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"github.com/goccy/go-json"
|
||||
"golang.org/x/net/proxy"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
@ -15,13 +19,52 @@ import (
|
||||
|
||||
var maxTimeout = 30 * time.Minute
|
||||
|
||||
func newClient() *http.Client {
|
||||
return &http.Client{
|
||||
func newClient(c []globals.ProxyConfig) *http.Client {
|
||||
client := &http.Client{
|
||||
Timeout: maxTimeout,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
},
|
||||
}
|
||||
|
||||
if len(c) == 0 {
|
||||
return client
|
||||
}
|
||||
|
||||
config := c[0]
|
||||
if config.ProxyType == globals.NoneProxyType {
|
||||
return client
|
||||
}
|
||||
|
||||
if config.ProxyType == globals.HttpProxyType || config.ProxyType == globals.HttpsProxyType {
|
||||
proxyUrl, err := url.Parse(config.Proxy)
|
||||
if err != nil {
|
||||
globals.Warn(fmt.Sprintf("failed to parse proxy url: %s", err))
|
||||
return client
|
||||
}
|
||||
client.Transport = &http.Transport{
|
||||
Proxy: http.ProxyURL(proxyUrl),
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
} else if config.ProxyType == globals.Socks5ProxyType {
|
||||
dialer, err := proxy.SOCKS5("tcp", config.Proxy, nil, proxy.Direct)
|
||||
if err != nil {
|
||||
globals.Warn(fmt.Sprintf("failed to create socks5 proxy: %s", err))
|
||||
return client
|
||||
}
|
||||
|
||||
dialContext := func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return dialer.Dial(network, addr)
|
||||
}
|
||||
|
||||
client.Transport = &http.Transport{
|
||||
DialContext: dialContext,
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
}
|
||||
|
||||
globals.Debug(fmt.Sprintf("[proxy] configured proxy: %s", config.Proxy))
|
||||
return client
|
||||
}
|
||||
|
||||
func fillHeaders(req *http.Request, headers map[string]string) {
|
||||
@ -30,14 +73,14 @@ func fillHeaders(req *http.Request, headers map[string]string) {
|
||||
}
|
||||
}
|
||||
|
||||
func Http(uri string, method string, ptr interface{}, headers map[string]string, body io.Reader) (err error) {
|
||||
func Http(uri string, method string, ptr interface{}, headers map[string]string, body io.Reader, config []globals.ProxyConfig) (err error) {
|
||||
req, err := http.NewRequest(method, uri, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fillHeaders(req, headers)
|
||||
|
||||
client := newClient()
|
||||
client := newClient(config)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -51,14 +94,14 @@ func Http(uri string, method string, ptr interface{}, headers map[string]string,
|
||||
return nil
|
||||
}
|
||||
|
||||
func HttpRaw(uri string, method string, headers map[string]string, body io.Reader) (data []byte, err error) {
|
||||
func HttpRaw(uri string, method string, headers map[string]string, body io.Reader, config []globals.ProxyConfig) (data []byte, err error) {
|
||||
req, err := http.NewRequest(method, uri, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fillHeaders(req, headers)
|
||||
|
||||
client := newClient()
|
||||
client := newClient(config)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -72,26 +115,26 @@ func HttpRaw(uri string, method string, headers map[string]string, body io.Reade
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func Get(uri string, headers map[string]string) (data interface{}, err error) {
|
||||
err = Http(uri, http.MethodGet, &data, headers, nil)
|
||||
func Get(uri string, headers map[string]string, config ...globals.ProxyConfig) (data interface{}, err error) {
|
||||
err = Http(uri, http.MethodGet, &data, headers, nil, config)
|
||||
return data, err
|
||||
}
|
||||
|
||||
func GetRaw(uri string, headers map[string]string) (data string, err error) {
|
||||
buffer, err := HttpRaw(uri, http.MethodGet, headers, nil)
|
||||
func GetRaw(uri string, headers map[string]string, config ...globals.ProxyConfig) (data string, err error) {
|
||||
buffer, err := HttpRaw(uri, http.MethodGet, headers, nil, config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(buffer), nil
|
||||
}
|
||||
|
||||
func Post(uri string, headers map[string]string, body interface{}) (data interface{}, err error) {
|
||||
err = Http(uri, http.MethodPost, &data, headers, ConvertBody(body))
|
||||
func Post(uri string, headers map[string]string, body interface{}, config ...globals.ProxyConfig) (data interface{}, err error) {
|
||||
err = Http(uri, http.MethodPost, &data, headers, ConvertBody(body), config)
|
||||
return data, err
|
||||
}
|
||||
|
||||
func PostRaw(uri string, headers map[string]string, body interface{}) (data string, err error) {
|
||||
buffer, err := HttpRaw(uri, http.MethodPost, headers, ConvertBody(body))
|
||||
func PostRaw(uri string, headers map[string]string, body interface{}, config ...globals.ProxyConfig) (data string, err error) {
|
||||
buffer, err := HttpRaw(uri, http.MethodPost, headers, ConvertBody(body), config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -105,7 +148,7 @@ func ConvertBody(body interface{}) (form io.Reader) {
|
||||
return form
|
||||
}
|
||||
|
||||
func EventSource(method string, uri string, headers map[string]string, body interface{}, callback func(string) error) error {
|
||||
func EventSource(method string, uri string, headers map[string]string, body interface{}, callback func(string) error, config ...globals.ProxyConfig) error {
|
||||
// panic recovery
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
@ -116,7 +159,7 @@ func EventSource(method string, uri string, headers map[string]string, body inte
|
||||
|
||||
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
|
||||
client := newClient()
|
||||
client := newClient(config)
|
||||
req, err := http.NewRequest(method, uri, ConvertBody(body))
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -36,7 +36,7 @@ func getErrorBody(resp *http.Response) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func EventScanner(props *EventScannerProps) *EventScannerError {
|
||||
func EventScanner(props *EventScannerProps, config ...globals.ProxyConfig) *EventScannerError {
|
||||
// panic recovery
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@ -45,7 +45,7 @@ func EventScanner(props *EventScannerProps) *EventScannerError {
|
||||
}
|
||||
}()
|
||||
|
||||
client := newClient()
|
||||
client := newClient(config)
|
||||
req, err := http.NewRequest(props.Method, props.Uri, ConvertBody(props.Body))
|
||||
if err != nil {
|
||||
return &EventScannerError{Error: err}
|
||||
|
Loading…
Reference in New Issue
Block a user