diff --git a/README.md b/README.md index fd15ac9..4da8377 100644 --- a/README.md +++ b/README.md @@ -157,8 +157,8 @@ auth: ## 🎈 感谢 | Thanks 感谢这些开源项目提供的思路: -- Claude API 转换: [claude-to-chatgpt](https://github.com/jtsang4/claude-to-chatgpt) - ChatGPT 逆向工程: [go-chatgpt-api](https://github.com/linweiyuan/go-chatgpt-api) +- New Bing 逆向工程: [EdgeGPT](https://github.com/acheong08/EdgeGPT) ## 🎃 开发团队 | Team - [@ProgramZmh](https://github.com/zmh-program) (全栈开发) diff --git a/adapter/adapter.go b/adapter/adapter.go index 9671b9b..5d6d7d1 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -2,6 +2,7 @@ package adapter import ( "chat/adapter/chatgpt" + "chat/adapter/claude" "chat/adapter/palm2" "chat/adapter/slack" "chat/adapter/sparkdesk" @@ -18,13 +19,7 @@ type ChatProps struct { } func NewChatRequest(props *ChatProps, hook globals.Hook) error { - if globals.IsClaudeModel(props.Model) { - instance := slack.NewChatInstanceFromConfig() - return instance.CreateStreamChatRequest(&slack.ChatProps{ - Message: props.Message, - }, hook) - - } else if globals.IsChatGPTModel(props.Model) { + if globals.IsChatGPTModel(props.Model) { instance := chatgpt.NewChatInstanceFromModel(&chatgpt.InstanceProps{ Model: props.Model, Reversible: props.Reversible, @@ -39,6 +34,13 @@ func NewChatRequest(props *ChatProps, hook globals.Hook) error { Token: utils.Multi(globals.IsGPT4Model(props.Model) || props.Reversible || props.Infinity, -1, 2000), }, hook) + } else if globals.IsClaudeModel(props.Model) { + return claude.NewChatInstanceFromConfig().CreateStreamChatRequest(&claude.ChatProps{ + Model: props.Model, + Message: props.Message, + Token: 50000, + }, hook) + } else if globals.IsSparkDeskModel(props.Model) { return sparkdesk.NewChatInstance().CreateStreamChatRequest(&sparkdesk.ChatProps{ Message: props.Message, @@ -50,6 +52,10 @@ func NewChatRequest(props *ChatProps, hook globals.Hook) error { Model: props.Model, Message: props.Message, }, hook) + } else if globals.IsSlackModel(props.Model) { + return slack.NewChatInstanceFromConfig().CreateStreamChatRequest(&slack.ChatProps{ + Message: props.Message, + }, hook) } return nil diff --git a/adapter/bing/struct.go b/adapter/bing/struct.go new file mode 100644 index 0000000..2135f88 --- /dev/null +++ b/adapter/bing/struct.go @@ -0,0 +1,14 @@ +package bing + +import ( + "chat/globals" +) + +type ChatInstance struct { + Endpoint string + Cookies map[string]string +} + +type ChatProps struct { + Message []globals.Message +} diff --git a/adapter/claude/chat.go b/adapter/claude/chat.go new file mode 100644 index 0000000..582db7b --- /dev/null +++ b/adapter/claude/chat.go @@ -0,0 +1,95 @@ +package claude + +import ( + "chat/globals" + "chat/utils" + "fmt" + "strings" +) + +type ChatProps struct { + Model string + Message []globals.Message + Token int +} + +func (c *ChatInstance) GetChatEndpoint() string { + return fmt.Sprintf("%s/v1/complete", 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(), + } +} + +func (c *ChatInstance) ConvertMessage(message []globals.Message) string { + mapper := map[string]string{ + "system": "Assistant", + "user": "Human", + "assistant": "Assistant", + } + + var result string + for i, item := range message { + if i == 0 && item.Role == "assistant" { + // skip first assistant message + continue + } + + result += fmt.Sprintf("\n\n%s: %s", mapper[item.Role], item.Content) + } + return fmt.Sprintf("%s\n\nAssistant:", result) +} + +func (c *ChatInstance) GetChatBody(props *ChatProps, stream bool) *ChatBody { + return &ChatBody{ + Prompt: c.ConvertMessage(props.Message), + MaxTokensToSample: props.Token, + Model: props.Model, + Stream: stream, + } +} + +// CreateChatRequest is the request for anthropic claude +func (c *ChatInstance) CreateChatRequest(props *ChatProps) (string, error) { + data, err := utils.Post(c.GetChatEndpoint(), c.GetChatHeaders(), c.GetChatBody(props, false)) + if err != nil { + return "", fmt.Errorf("claude error: %s", err.Error()) + } + + if form := utils.MapToStruct[ChatResponse](data); form != nil { + return form.Completion, nil + } + return "", fmt.Errorf("claude error: invalid response") +} + +// CreateStreamChatRequest is the stream request for anthropic claude +func (c *ChatInstance) CreateStreamChatRequest(props *ChatProps, hook globals.Hook) error { + return utils.EventSource( + "POST", + c.GetChatEndpoint(), + c.GetChatHeaders(), + c.GetChatBody(props, true), + func(data string) error { + // response example: + // + // event:completion + // data:{"completion":"!","stop_reason":null,"model":"claude-2.0","stop":null,"log_id":"f5f659a5807419c94cfac4a9f2f79a66e95733975714ce7f00e30689dd136b02"} + + if !strings.HasPrefix(data, "data:") { + return nil + } else { + data = strings.TrimSpace(strings.TrimPrefix(data, "data:")) + } + + if form := utils.UnmarshalForm[ChatResponse](data); form != nil { + if err := hook(form.Completion); err != nil { + return err + } + } + return nil + }) +} diff --git a/adapter/claude/struct.go b/adapter/claude/struct.go new file mode 100644 index 0000000..56a7ecb --- /dev/null +++ b/adapter/claude/struct.go @@ -0,0 +1,33 @@ +package claude + +import ( + "chat/utils" + "github.com/spf13/viper" +) + +type ChatInstance struct { + Endpoint string + ApiKey string +} + +func NewChatInstance(endpoint, apiKey string) *ChatInstance { + return &ChatInstance{ + Endpoint: endpoint, + ApiKey: apiKey, + } +} + +func NewChatInstanceFromConfig() *ChatInstance { + return NewChatInstance( + viper.GetString("claude.endpoint"), + utils.GetRandomKey(viper.GetString("claude.apikey")), + ) +} + +func (c *ChatInstance) GetEndpoint() string { + return c.Endpoint +} + +func (c *ChatInstance) GetApiKey() string { + return c.ApiKey +} diff --git a/adapter/claude/types.go b/adapter/claude/types.go new file mode 100644 index 0000000..02c3255 --- /dev/null +++ b/adapter/claude/types.go @@ -0,0 +1,15 @@ +package claude + +// 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"` +} + +// ChatResponse is the native http request and stream response for anthropic claude +type ChatResponse struct { + Completion string `json:"completion"` + LogId string `json:"log_id"` +} diff --git a/app/README.md b/app/README.md deleted file mode 100644 index 1ebe379..0000000 --- a/app/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# React + TypeScript + Vite - -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. - -Currently, two official plugins are available: - -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh - -## Expanding the ESLint configuration - -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: - -- Configure the top-level `parserOptions` property like this: - -```js - parserOptions: { - ecmaVersion: 'latest', - sourceType: 'module', - project: ['./tsconfig.json', './tsconfig.node.json'], - tsconfigRootDir: __dirname, - }, -``` - -- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` -- Optionally add `plugin:@typescript-eslint/stylistic-type-checked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list diff --git a/app/src/conf.ts b/app/src/conf.ts index a880e76..e574ef1 100644 --- a/app/src/conf.ts +++ b/app/src/conf.ts @@ -16,6 +16,8 @@ export const supportModels: string[] = [ "GPT-3.5-16k", "GPT-4", "GPT-4-32k", + "Claude-2", + "Claude-2-100k", "SparkDesk 讯飞星火", "Palm2" // "Claude-2", @@ -27,8 +29,8 @@ export const supportModelConvertor: Record = { "GPT-3.5-16k": "gpt-3.5-turbo-16k", "GPT-4": "gpt-4", "GPT-4-32k": "gpt-4-32k", - "Claude-2": "claude-2", - "Claude-2-100k": "claude-2-100k", + "Claude-2": "claude-1", + "Claude-2-100k": "claude-2", // not claude-2-100k "SparkDesk 讯飞星火": "spark-desk", "Palm2": "chat-bison-001" }; diff --git a/globals/variables.go b/globals/variables.go index e557ef4..21125e2 100644 --- a/globals/variables.go +++ b/globals/variables.go @@ -26,8 +26,9 @@ const ( GPT432k0314 = "gpt-4-32k-0314" GPT432k0613 = "gpt-4-32k-0613" Dalle = "dalle" - Claude2 = "claude-2" - Claude2100k = "claude-2-100k" + Claude2 = "claude-1" // claude v1.3 + Claude2100k = "claude-2" + ClaudeSlack = "claude-slack" SparkDesk = "spark-desk" ChatBison001 = "chat-bison-001" ) @@ -105,6 +106,10 @@ func IsDalleModel(model string) bool { return model == Dalle } +func IsSlackModel(model string) bool { + return model == ClaudeSlack +} + func IsSparkDeskModel(model string) bool { return model == SparkDesk } diff --git a/go.mod b/go.mod index 9a9a416..6a68a77 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module chat go 1.20 require ( + github.com/bincooo/claude-api v1.0.2 github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/gin-gonic/gin v1.9.1 github.com/go-redis/redis/v8 v8.11.5 @@ -17,7 +18,6 @@ require ( require ( github.com/andybalholm/brotli v1.0.5 // indirect - github.com/bincooo/claude-api v1.0.2 // indirect github.com/bincooo/requests v0.0.0-20230720064210-7eae5d6c9d1e // indirect github.com/bitly/go-simplejson v0.5.0 // indirect github.com/bytedance/sonic v1.10.1 // indirect @@ -45,7 +45,6 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pavel-one/EdgeGPT-Go v1.3.3 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/refraction-networking/utls v1.3.2 // indirect github.com/sirupsen/logrus v1.9.3 // indirect @@ -57,15 +56,11 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect github.com/wangluozhe/fhttp v0.0.0-20230512135433-5c2ebfb4868a // indirect - go.uber.org/atomic v1.10.0 // indirect - go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.24.0 // indirect golang.org/x/arch v0.5.0 // indirect golang.org/x/crypto v0.13.0 // indirect golang.org/x/sys v0.12.0 // indirect golang.org/x/text v0.13.0 // indirect google.golang.org/protobuf v1.31.0 // indirect - gopkg.in/guregu/null.v4 v4.0.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index e073119..1f211b2 100644 --- a/go.sum +++ b/go.sum @@ -46,6 +46,7 @@ github.com/bincooo/requests v0.0.0-20230720064210-7eae5d6c9d1e h1:38ztKJW0K6qQGi github.com/bincooo/requests v0.0.0-20230720064210-7eae5d6c9d1e/go.mod h1:0WuzYU+4cQL/hVbjoncY5TACMTbD9I+pLCdnPjfItp0= github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc= @@ -211,8 +212,6 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= -github.com/pavel-one/EdgeGPT-Go v1.3.3 h1:eRWN3fhNm048LjO0WD2zxBAOotttnEQ0wCV97uVY8KI= -github.com/pavel-one/EdgeGPT-Go v1.3.3/go.mod h1:voam1mTgtXbNCHq66/b0qLBKQ20Gr2qukALdYoiCslI= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -225,7 +224,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/refraction-networking/utls v1.3.2 h1:o+AkWB57mkcoW36ET7uJ002CpBWHu0KPxi6vzxvPnv8= github.com/refraction-networking/utls v1.3.2/go.mod h1:fmoaOww2bxzzEpIKOebIsnBvjQpqP7L2vcm/9KUfm/E= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -273,12 +271,6 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y= golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= @@ -578,8 +570,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/guregu/null.v4 v4.0.0 h1:1Wm3S1WEA2I26Kq+6vcW+w0gcDo44YKYD7YIEJNHDjg= -gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu/JrI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=