Browse Source

feat: add support of OpenRouter (#619)

HEAD
Kelvin Chiu Yang Luo 2 years ago
parent
commit
5fda8b936b
5 changed files with 161 additions and 0 deletions
  1. +1
    -0
      go.mod
  2. +2
    -0
      go.sum
  3. +130
    -0
      model/openrouter.go
  4. +2
    -0
      model/provider.go
  5. +26
    -0
      web/src/Setting.js

+ 1
- 0
go.mod View File

@@ -3,6 +3,7 @@ module github.com/casbin/casibase
go 1.18

require (
github.com/Lok-Lu/go-openrouter v0.0.0-20230807015935-ab5cee433ad3
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1585
github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible
github.com/anhao/go-ernie v1.0.4


+ 2
- 0
go.sum View File

@@ -38,6 +38,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Lok-Lu/go-openrouter v0.0.0-20230807015935-ab5cee433ad3 h1:E15Zr1fu3QEIleqINqXpl4SlYuWkwWPJ4cx6UVaJITE=
github.com/Lok-Lu/go-openrouter v0.0.0-20230807015935-ab5cee433ad3/go.mod h1:nM0kITDAJEkwn9x2DQPdfJynThaLe0vGNpPcqJ8vjAI=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=


+ 130
- 0
model/openrouter.go View File

@@ -0,0 +1,130 @@
// Copyright 2023 The casbin Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package model

import (
"context"
"fmt"
"io"
"net/http"
"strings"

"github.com/Lok-Lu/go-openrouter"
"github.com/casbin/casibase/proxy"
)

type OpenRouterModelProvider struct {
subType string
secretKey string
siteName string
siteUrl string
}

func NewOpenRouterModelProvider(subType string, secretKey string) (*OpenRouterModelProvider, error) {
p := &OpenRouterModelProvider{
subType: subType,
secretKey: secretKey,
siteName: "Casibase",
siteUrl: "https://casibase.org",
}
return p, nil
}

func (p *OpenRouterModelProvider) getProxyClientFromToken() *openrouter.Client {
config, err := openrouter.DefaultConfig(p.secretKey, p.siteName, p.siteUrl)
if err != nil {
panic(err)
}

config.HTTPClient = proxy.ProxyHttpClient

c := openrouter.NewClientWithConfig(config)
return c
}

func (p *OpenRouterModelProvider) QueryText(question string, writer io.Writer, builder *strings.Builder) error {
client := p.getProxyClientFromToken()

ctx := context.Background()
flusher, ok := writer.(http.Flusher)
if !ok {
return fmt.Errorf("writer does not implement http.Flusher")
}

model := p.subType
if model == "" {
model = openrouter.Gpt35Turbo
}

promptTokens, err := GetTokenSize(model, question)
if err != nil {
return err
}

maxTokens := 4097 - promptTokens

respStream, err := client.CreateChatCompletionStream(
ctx,
&openrouter.ChatCompletionRequest{
Model: p.subType,
Messages: []openrouter.ChatCompletionMessage{
{
Role: openrouter.ChatMessageRoleSystem,
Content: "You are a helpful assistant.",
},
{
Role: openrouter.ChatMessageRoleUser,
Content: question,
},
},
Stream: false,
Temperature: nil,
TopP: nil,
MaxTokens: maxTokens,
},
)
if err != nil {
return err
}
defer respStream.Close()

isLeadingReturn := true
for {
completion, streamErr := respStream.Recv()
if streamErr != nil {
if streamErr == io.EOF {
break
}
return streamErr
}

data := completion.Choices[0].Message.Content
if isLeadingReturn && len(data) != 0 {
if strings.Count(data, "\n") == len(data) {
continue
} else {
isLeadingReturn = false
}
}

if _, err = fmt.Fprintf(writer, "event: message\ndata: %s\n\n", data); err != nil {
return err
}
flusher.Flush()
builder.WriteString(data)
}

return nil
}

+ 2
- 0
model/provider.go View File

@@ -30,6 +30,8 @@ func GetModelProvider(typ string, subType string, clientId string, clientSecret
p, err = NewOpenAiModelProvider(subType, clientSecret)
} else if typ == "Hugging Face" {
p, err = NewHuggingFaceModelProvider(subType, clientSecret)
} else if typ == "OpenRouter" {
p, err = NewOpenRouterModelProvider(subType, clientSecret)
} else if typ == "Ernie" {
p, err = NewErnieModelProvider(subType, clientId, clientSecret)
}


+ 26
- 0
web/src/Setting.js View File

@@ -659,6 +659,7 @@ export function getProviderTypeOptions(category) {
[
{id: "OpenAI", name: "OpenAI"},
{id: "Hugging Face", name: "Hugging Face"},
{id: "OpenRouter", name: "OpenRouter"},
{id: "Ernie", name: "Ernie"},
]
);
@@ -739,6 +740,31 @@ export function getProviderSubTypeOptions(category, type) {
{id: "THUDM/chatglm2-6b", name: "THUDM/chatglm2-6b"},
]
);
} else if (type === "OpenRouter") {
return (
[
{id: "google/palm-2-codechat-bison", name: "google/palm-2-codechat-bison"},
{id: "google/palm-2-chat-bison", name: "google/palm-2-chat-bison"},
{id: "openai/gpt-3.5-turbo", name: "openai/gpt-3.5-turbo"},
{id: "openai/gpt-3.5-turbo-16k", name: "openai/gpt-3.5-turbo-16k"},
{id: "openai/gpt-4", name: "openai/gpt-4"},
{id: "openai/gpt-4-32k", name: "openai/gpt-4-32k"},
{id: "anthropic/claude-2", name: "anthropic/claude-2"},
{id: "anthropic/claude-instant-v1", name: "anthropic/claude-instant-v1"},
{id: "meta-llama/llama-2-13b-chat", name: "meta-llama/llama-2-13b-chat"},
{id: "meta-llama/llama-2-70b-chat", name: "meta-llama/llama-2-70b-chat"},
{id: "palm-2-codechat-bison", name: "palm-2-codechat-bison"},
{id: "palm-2-chat-bison", name: "palm-2-chat-bison"},
{id: "gpt-3.5-turbo", name: "gpt-3.5-turbo"},
{id: "gpt-3.5-turbo-16k", name: "gpt-3.5-turbo-16k"},
{id: "gpt-4", name: "gpt-4"},
{id: "gpt-4-32k", name: "gpt-4-32k"},
{id: "claude-2", name: "claude-2"},
{id: "claude-instant-v1", name: "claude-instant-v1"},
{id: "llama-2-13b-chat", name: "llama-2-13b-chat"},
{id: "llama-2-70b-chat", name: "llama-2-70b-chat"},
]
);
} else if (type === "Ernie") {
return (
[


Loading…
Cancel
Save