mirror of
https://github.com/zhengkai/orca.git
synced 2026-02-04 16:52:26 +08:00
up
This commit is contained in:
16
proto/rsp.proto
Normal file
16
proto/rsp.proto
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
option go_package = "/pb";
|
||||||
|
package pb;
|
||||||
|
|
||||||
|
message Rsp {
|
||||||
|
uint32 id = 1;
|
||||||
|
string object = 2;
|
||||||
|
fixed32 created = 3;
|
||||||
|
string model = 4;
|
||||||
|
RspUsage usage = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RspUsage {
|
||||||
|
uint32 prompt_tokens = 1;
|
||||||
|
uint32 total_tokens = 2;
|
||||||
|
}
|
||||||
1
server/build/.gitignore
vendored
1
server/build/.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
/.git-hash
|
/.git-hash
|
||||||
|
/env.sh
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
DIR=$(readlink -f "$0") && DIR=$(dirname "$DIR") && cd "$DIR" || exit 1
|
DIR=$(readlink -f "$0") && DIR=$(dirname "$DIR") && cd "$DIR" || exit 1
|
||||||
|
|
||||||
|
if [ -e ./env.sh ]; then
|
||||||
|
. ./env.sh
|
||||||
|
fi
|
||||||
. ./common.sh
|
. ./common.sh
|
||||||
LOG="log file: $LOG_FILE"
|
LOG="log file: $LOG_FILE"
|
||||||
|
|
||||||
|
|||||||
@@ -8,4 +8,8 @@ var (
|
|||||||
StaticDir = `/www/orca/static`
|
StaticDir = `/www/orca/static`
|
||||||
|
|
||||||
WebAddr = `localhost:22035`
|
WebAddr = `localhost:22035`
|
||||||
|
|
||||||
|
RemoteAPI = `https://api.openai.com`
|
||||||
|
|
||||||
|
OpenAIKey = ``
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ func init() {
|
|||||||
Dir, _ = filepath.Abs(filepath.Dir(os.Args[0]))
|
Dir, _ = filepath.Abs(filepath.Dir(os.Args[0]))
|
||||||
|
|
||||||
list := map[string]*string{
|
list := map[string]*string{
|
||||||
`STATIC_DIR`: &StaticDir,
|
`OPENAI_API_KEY`: &OpenAIKey,
|
||||||
`WEB_ADDR`: &WebAddr,
|
`STATIC_DIR`: &StaticDir,
|
||||||
|
`WEB_ADDR`: &WebAddr,
|
||||||
}
|
}
|
||||||
for k, v := range list {
|
for k, v := range list {
|
||||||
s := os.Getenv(k)
|
s := os.Getenv(k)
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"project/pb"
|
"project/pb"
|
||||||
"project/zj"
|
"project/zj"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Core) add(hash [16]byte, req *pb.Req) (pr *row) {
|
func (c *Core) add(req *pb.Req, hr *http.Request) (pr *row) {
|
||||||
|
|
||||||
|
hash := req.Hash()
|
||||||
|
|
||||||
c.mux.Lock()
|
c.mux.Lock()
|
||||||
pr, ok := c.pool[hash]
|
pr, ok := c.pool[hash]
|
||||||
if ok {
|
if ok {
|
||||||
@@ -16,6 +20,7 @@ func (c *Core) add(hash [16]byte, req *pb.Req) (pr *row) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pr = &row{
|
pr = &row{
|
||||||
|
hr: hr,
|
||||||
hash: hash,
|
hash: hash,
|
||||||
req: req,
|
req: req,
|
||||||
t: time.Now(),
|
t: time.Now(),
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"project/pb"
|
"project/pb"
|
||||||
"project/util"
|
"project/util"
|
||||||
|
"project/zj"
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func req(w http.ResponseWriter, r *http.Request) (p *pb.Req, hash [16]byte, err error) {
|
func req(w http.ResponseWriter, r *http.Request) (p *pb.Req, err error) {
|
||||||
|
|
||||||
path := r.URL.Path
|
path := r.URL.Path
|
||||||
method := r.Method
|
method := r.Method
|
||||||
|
|
||||||
ab, err := ioutil.ReadAll(http.MaxBytesReader(w, r.Body, 1024*1024))
|
zj.J(method, path)
|
||||||
|
|
||||||
|
ab, err := io.ReadAll(http.MaxBytesReader(w, r.Body, 1024*1024))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -27,14 +27,7 @@ func req(w http.ResponseWriter, r *http.Request) (p *pb.Req, hash [16]byte, err
|
|||||||
Method: method,
|
Method: method,
|
||||||
Body: ab,
|
Body: ab,
|
||||||
}
|
}
|
||||||
|
zj.F(`%x %s %s`, p.Hash(), method, path)
|
||||||
pab, err := proto.Marshal(p)
|
|
||||||
if err != nil {
|
|
||||||
err500(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
hash = md5.Sum(pab)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package core
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"project/pb"
|
"project/pb"
|
||||||
|
"project/util"
|
||||||
"project/zj"
|
"project/zj"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -10,6 +12,7 @@ import (
|
|||||||
|
|
||||||
type row struct {
|
type row struct {
|
||||||
hash [16]byte
|
hash [16]byte
|
||||||
|
hr *http.Request
|
||||||
req *pb.Req
|
req *pb.Req
|
||||||
rsp []byte
|
rsp []byte
|
||||||
err error
|
err error
|
||||||
@@ -25,7 +28,10 @@ func (pr *row) run() {
|
|||||||
s := fmt.Sprintf(`%x, %s`, pr.hash, pr.t.Format(`2006-01-02 15:04:05`))
|
s := fmt.Sprintf(`%x, %s`, pr.hash, pr.t.Format(`2006-01-02 15:04:05`))
|
||||||
zj.J(`new`, s)
|
zj.J(`new`, s)
|
||||||
|
|
||||||
pr.rsp = []byte(s)
|
pr.rsp, pr.err = fetchRemote(pr.req)
|
||||||
|
go pr.saveFile()
|
||||||
|
go pr.metrics()
|
||||||
|
|
||||||
pr.done = true
|
pr.done = true
|
||||||
pr.mux.Unlock()
|
pr.mux.Unlock()
|
||||||
}
|
}
|
||||||
@@ -36,3 +42,14 @@ func (pr *row) wait() {
|
|||||||
pr.mux.RUnlock()
|
pr.mux.RUnlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pr *row) saveFile() {
|
||||||
|
rspFile := util.CacheName(pr.req.Hash()) + `-rsp.json`
|
||||||
|
if !util.FileExists(rspFile) {
|
||||||
|
util.WriteFile(rspFile, pr.rsp)
|
||||||
|
zj.J(rspFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr *row) metrics() {
|
||||||
|
}
|
||||||
|
|||||||
42
server/src/core/rsp.go
Normal file
42
server/src/core/rsp.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"project/config"
|
||||||
|
"project/pb"
|
||||||
|
"project/zj"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func fetchRemote(r *pb.Req) (ab []byte, err error) {
|
||||||
|
|
||||||
|
u, err := url.Parse(config.RemoteAPI)
|
||||||
|
if err != nil {
|
||||||
|
zj.W(`url fail`, config.RemoteAPI, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u.Path = r.Path
|
||||||
|
|
||||||
|
req, err := http.NewRequest(r.Method, u.String(), bytes.NewReader(r.Body))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set(`Content-Type`, `application/json`)
|
||||||
|
req.Header.Set(`Authorization`, `Bearer `+config.OpenAIKey)
|
||||||
|
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
rsp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rsp.Body.Close()
|
||||||
|
|
||||||
|
return io.ReadAll(rsp.Body)
|
||||||
|
}
|
||||||
@@ -3,19 +3,27 @@ package core
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"project/metrics"
|
"project/metrics"
|
||||||
|
"project/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WebHandle ...
|
// WebHandle ...
|
||||||
func (c *Core) WebHandle(w http.ResponseWriter, r *http.Request) {
|
func (c *Core) WebHandle(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
p, hash, err := req(w, r)
|
p, err := req(w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
metrics.ReqFailCount()
|
metrics.ReqFailCount()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
metrics.ReqBytes(len(p.Body))
|
metrics.ReqBytes(len(p.Body))
|
||||||
|
|
||||||
pr := c.add(hash, p)
|
pr := c.add(p, r)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
reqFile := util.CacheName(p.Hash()) + `-req.json`
|
||||||
|
if !util.FileExists(reqFile) {
|
||||||
|
util.WriteFile(reqFile, p.Body)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
pr.wait()
|
pr.wait()
|
||||||
|
|
||||||
|
|||||||
22
server/src/pb/req.go
Normal file
22
server/src/pb/req.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package pb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Hash ...
|
||||||
|
func (x *Req) Hash() [16]byte {
|
||||||
|
|
||||||
|
m := md5.New()
|
||||||
|
|
||||||
|
io.WriteString(m, x.Method)
|
||||||
|
m.Write([]byte{0x00})
|
||||||
|
io.WriteString(m, x.Method)
|
||||||
|
m.Write([]byte{0x00})
|
||||||
|
m.Write(x.Body)
|
||||||
|
|
||||||
|
var h [16]byte
|
||||||
|
copy(h[:], m.Sum(nil)[:])
|
||||||
|
return h
|
||||||
|
}
|
||||||
@@ -4,16 +4,31 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"project/config"
|
"project/config"
|
||||||
"project/zj"
|
"project/zj"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/zhengkai/zu"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DownloadFunc ...
|
// DownloadFunc ...
|
||||||
type DownloadFunc func(url string) (ab []byte, err error)
|
type DownloadFunc func(url string) (ab []byte, err error)
|
||||||
|
|
||||||
|
// CacheName ...
|
||||||
|
func CacheName(hash [16]byte) string {
|
||||||
|
s := fmt.Sprintf(`cache/%x/%x/%x`, hash[0:2], hash[2:4], hash[4:])
|
||||||
|
os.MkdirAll(StaticFile(filepath.Dir(s)), 0755)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileExists ...
|
||||||
|
func FileExists(filename string) bool {
|
||||||
|
filename = fmt.Sprintf(`%s/%s`, config.StaticDir, filename)
|
||||||
|
return zu.FileExists(filename)
|
||||||
|
}
|
||||||
|
|
||||||
// IsURL ...
|
// IsURL ...
|
||||||
func IsURL(s string) bool {
|
func IsURL(s string) bool {
|
||||||
return strings.HasPrefix(s, `https://`) || strings.HasPrefix(s, `http://`)
|
return strings.HasPrefix(s, `https://`) || strings.HasPrefix(s, `http://`)
|
||||||
|
|||||||
@@ -8,16 +8,16 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetUint32IP ...
|
// GetIP ...
|
||||||
func GetUint32IP(r *http.Request) (uint32, error) {
|
func GetIP(r *http.Request) (net.IP, error) {
|
||||||
var ipStr string
|
var ipStr string
|
||||||
|
|
||||||
if realIP := r.Header.Get("X-Real-IP"); realIP != "" {
|
if realIP := r.Header.Get(`X-Real-IP`); realIP != `` {
|
||||||
ipStr = realIP
|
ipStr = realIP
|
||||||
} else {
|
} else {
|
||||||
ip, _, err := net.SplitHostPort(r.RemoteAddr)
|
ip, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ipStr = ip
|
ipStr = ip
|
||||||
}
|
}
|
||||||
@@ -25,22 +25,21 @@ func GetUint32IP(r *http.Request) (uint32, error) {
|
|||||||
|
|
||||||
parsedIP := net.ParseIP(ipStr)
|
parsedIP := net.ParseIP(ipStr)
|
||||||
if parsedIP == nil {
|
if parsedIP == nil {
|
||||||
return 0, errors.New("Invalid IP address")
|
return nil, errors.New(`Invalid IP address`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否是IPv4
|
// 检查是否是IPv4
|
||||||
parsedIPv4 := parsedIP.To4()
|
parsedIPv4 := parsedIP.To4()
|
||||||
if parsedIPv4 == nil {
|
if parsedIPv4 == nil {
|
||||||
return 0, errors.New("IP address not IPv4")
|
return nil, errors.New(`IP address not IPv4`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否为局域网IP
|
// 检查是否为局域网IP
|
||||||
if !parsedIP.IsPrivate() {
|
if !parsedIP.IsPrivate() {
|
||||||
return 0, errors.New("Public IP address not allowed")
|
return nil, errors.New(`Public IP address not allowed`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ip to uint32
|
return parsedIPv4, nil
|
||||||
return binary.BigEndian.Uint32(parsedIPv4), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IPString ...
|
// IPString ...
|
||||||
|
|||||||
Reference in New Issue
Block a user