@@ -4,52 +4,105 @@ import (
4
4
"fmt"
5
5
"net"
6
6
"net/http"
7
+ "net/http/httputil"
8
+ "net/url"
9
+ "os"
10
+ "strings"
7
11
8
12
"golang.org/x/xerrors"
9
13
)
10
14
11
15
type Bridge struct {
12
- srv * http.Server
13
- addr string
16
+ httpSrv * http.Server
17
+ addr string
14
18
}
15
19
16
20
func NewBridge (addr string )* Bridge {
17
21
mux := & http.ServeMux {}
18
- mux .HandleFunc ("/v1/chat/completions" ,bridgeOpenAIRequest )
19
- mux .HandleFunc ("/v1/messages" ,bridgeAnthropicRequest )
22
+ mux .HandleFunc ("/v1/chat/completions" ,proxyOpenAIRequest )
23
+ mux .HandleFunc ("/v1/messages" ,proxyAnthropicRequest )
20
24
21
25
srv := & http.Server {
22
26
Addr :addr ,
23
27
Handler :mux ,
24
28
// TODO: other settings.
25
29
}
26
30
27
- return & Bridge {srv :srv }
31
+ return & Bridge {httpSrv :srv }
28
32
}
29
33
30
- func ( b * Bridge ) Serve () error {
31
- list ,err := net . Listen ( "tcp" , b . srv . Addr )
34
+ func proxyOpenAIRequest ( w http. ResponseWriter , r * http. Request ) {
35
+ target ,err := url . Parse ( "https://api.openai.com" )
32
36
if err != nil {
33
- return xerrors .Errorf ("listen: %w" ,err )
37
+ http .Error (w ,"failed to parse OpenAI URL" ,http .StatusInternalServerError )
38
+ return
34
39
}
35
40
36
- b .addr = list .Addr ().String ()
41
+ proxy := httputil .NewSingleHostReverseProxy (target )
42
+ originalDirector := proxy .Director
43
+ proxy .Director = func (req * http.Request ) {
44
+ originalDirector (req )
37
45
38
- return b .srv .Serve (list )// TODO: TLS.
39
- }
46
+ // Add OpenAI-specific headers
47
+ if strings .TrimSpace (req .Header .Get ("Authorization" ))== "" {
48
+ req .Header .Set ("Authorization" ,fmt .Sprintf ("Bearer %s" ,os .Getenv ("OPENAI_API_KEY" )))
49
+ }
40
50
41
- func ( b * Bridge ) Addr () string {
42
- return b . addr
43
- }
51
+ if req . Header . Get ( "Content-Type" ) == "" {
52
+ req . Header . Set ( "Content-Type" , "application/json" )
53
+ }
44
54
45
- func bridgeOpenAIRequest (w http.ResponseWriter ,r * http.Request ) {
46
- fmt .Println ("OpenAI" )
55
+ req .Host = target .Host
56
+ req .URL .Scheme = target .Scheme
57
+ req .URL .Host = target .Host
58
+
59
+ fmt .Printf ("Proxying %s request to: %s\n " ,req .Method ,req .URL .String ())
60
+ }
61
+ proxy .ServeHTTP (w ,r )
47
62
}
48
63
49
- func bridgeAnthropicRequest (w http.ResponseWriter ,r * http.Request ) {
50
- fmt .Println ("Anthropic" )
64
+ func proxyAnthropicRequest (w http.ResponseWriter ,r * http.Request ) {
65
+ target ,err := url .Parse ("https://api.anthropic.com" )
66
+ if err != nil {
67
+ http .Error (w ,"failed to parse Anthropic URL" ,http .StatusInternalServerError )
68
+ return
69
+ }
70
+
71
+ proxy := httputil .NewSingleHostReverseProxy (target )
72
+ originalDirector := proxy .Director
73
+ proxy .Director = func (req * http.Request ) {
74
+ originalDirector (req )
75
+
76
+ // Add Anthropic-specific headers
77
+ if strings .TrimSpace (req .Header .Get ("x-api-key" ))== "" {
78
+ req .Header .Set ("x-api-key" ,os .Getenv ("ANTHROPIC_API_KEY" ))
79
+ }
80
+ req .Header .Set ("anthropic-version" ,"2023-06-01" )
81
+
82
+ if req .Header .Get ("Content-Type" )== "" {
83
+ req .Header .Set ("Content-Type" ,"application/json" )
84
+ }
85
+
86
+ req .Host = target .Host
87
+ req .URL .Scheme = target .Scheme
88
+ req .URL .Host = target .Host
89
+
90
+ fmt .Printf ("Proxying %s request to: %s\n " ,req .Method ,req .URL .String ())
91
+ }
92
+ proxy .ServeHTTP (w ,r )
51
93
}
52
94
53
- func bridgeRequest (w http.ResponseWriter ,r * http.Request ) {
95
+ func (b * Bridge )Serve ()error {
96
+ list ,err := net .Listen ("tcp" ,b .httpSrv .Addr )
97
+ if err != nil {
98
+ return xerrors .Errorf ("listen: %w" ,err )
99
+ }
54
100
101
+ b .addr = list .Addr ().String ()
102
+
103
+ return b .httpSrv .Serve (list )// TODO: TLS.
104
+ }
105
+
106
+ func (b * Bridge )Addr ()string {
107
+ return b .addr
55
108
}