Skip to content

Commit 0d9b713

Browse files
committed
feat: Add vanguard for enabling seamless transcoding between REST and RPC protocols
1 parent b05d07b commit 0d9b713

10 files changed

Lines changed: 194 additions & 599 deletions

File tree

mobius/go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
connectrpc.com/grpchealth v1.3.0
1010
connectrpc.com/grpcreflect v1.2.0
1111
connectrpc.com/validate v0.1.0
12+
connectrpc.com/vanguard v0.1.0
1213
github.com/MakeNowJust/heredoc v1.0.0
1314
github.com/missingstudio/studio/protos v0.0.0-00010101000000-000000000000
1415
github.com/muesli/termenv v0.15.2
@@ -28,8 +29,9 @@ require (
2829
github.com/bufbuild/protovalidate-go v0.3.0 // indirect
2930
github.com/davecgh/go-spew v1.1.1 // indirect
3031
github.com/google/cel-go v0.17.4 // indirect
31-
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
32+
github.com/google/go-cmp v0.6.0 // indirect
3233
github.com/inconshreveable/mousetrap v1.1.0 // indirect
34+
github.com/kr/pretty v0.3.1 // indirect
3335
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
3436
github.com/mattn/go-isatty v0.0.18 // indirect
3537
github.com/mattn/go-runewidth v0.0.14 // indirect
@@ -43,5 +45,6 @@ require (
4345
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect
4446
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
4547
google.golang.org/protobuf v1.32.0 // indirect
48+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
4649
gopkg.in/yaml.v3 v3.0.1 // indirect
4750
)

mobius/go.sum

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ connectrpc.com/grpcreflect v1.2.0 h1:Q6og1S7HinmtbEuBvARLNwYmTbhEGRpHDhqrPNlmK+U
88
connectrpc.com/grpcreflect v1.2.0/go.mod h1:nwSOKmE8nU5u/CidgHtPYk1PFI3U9ignz7iDMxOYkSY=
99
connectrpc.com/validate v0.1.0 h1:r55jirxMK7HO/xZwVHj3w2XkVFarsUM77ZDy367NtH4=
1010
connectrpc.com/validate v0.1.0/go.mod h1:GU47c9/x/gd+u9wRSPkrQOP46gx2rMN+Wo37EHgI3Ow=
11+
connectrpc.com/vanguard v0.1.0 h1:2fJzlO4o0Bh3b6A7uQdEe27Gj2mzjAOLwawm4cPIJHw=
12+
connectrpc.com/vanguard v0.1.0/go.mod h1:VNtMHNwYYDPOhQRmBzojK8WqqkoX3ul9PB0+M+HXO1Y=
1113
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
1214
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
1315
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 h1:goHVqTbFX3AIo0tzGr14pgfAW2ZfPChKO21Z9MGf/gk=
@@ -17,23 +19,27 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ
1719
github.com/bufbuild/protovalidate-go v0.3.0 h1:t9zKgM//9VtPnP0TvyFqWubLQtSbwLwEUVOxgtX9/os=
1820
github.com/bufbuild/protovalidate-go v0.3.0/go.mod h1:4mZkDYMGJlnHHQ9rPOhVEZ4bA13iOJBRLzywxy8f/lo=
1921
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
22+
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
2023
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2124
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2225
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2326
github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=
2427
github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=
2528
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
29+
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
30+
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
2631
github.com/google/cel-go v0.17.4 h1:9556LOjSyIZlgnT0oaCYGq2uk9BM6fzuTXhzYHskonk=
2732
github.com/google/cel-go v0.17.4/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY=
2833
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
2934
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
3035
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
31-
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
32-
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
3336
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
3437
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
38+
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
3539
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
3640
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
41+
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
42+
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
3743
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
3844
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
3945
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
@@ -44,10 +50,12 @@ github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWV
4450
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
4551
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
4652
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
53+
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
4754
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4855
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
4956
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
5057
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
58+
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
5159
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
5260
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
5361
github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo=
@@ -85,6 +93,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:
8593
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0=
8694
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=
8795
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=
96+
google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
97+
google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
8898
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
8999
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
90100
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package connectrpc
2+
3+
import "net/http"
4+
5+
func Custom404handler() http.Handler {
6+
return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
7+
http.Error(w, "custom 404 error", http.StatusNotFound)
8+
})
9+
}

mobius/internal/connectrpc/mux.go

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import (
55
"fmt"
66
"log"
77
"net/http"
8+
"time"
89

910
"connectrpc.com/connect"
1011
"connectrpc.com/grpchealth"
1112
"connectrpc.com/grpcreflect"
1213
"connectrpc.com/validate"
13-
greetingv1 "github.com/missingstudio/studio/protos/pkg/greeting/v1"
14-
"github.com/missingstudio/studio/protos/pkg/greeting/v1/greetingv1connect"
14+
"connectrpc.com/vanguard"
15+
llmv1 "github.com/missingstudio/studio/protos/pkg/llm"
16+
"github.com/missingstudio/studio/protos/pkg/llm/llmv1connect"
1517
)
1618

1719
type Deps struct{}
@@ -25,34 +27,50 @@ func NewConnectMux(d Deps) (*http.ServeMux, error) {
2527
}
2628

2729
compress1KB := connect.WithCompressMinBytes(1024)
28-
mux.Handle(greetingv1connect.NewGreetServiceHandler(
29-
&GreetServer{},
30-
compress1KB,
31-
connect.WithInterceptors(validateInterceptor),
32-
))
30+
services := []*vanguard.Service{
31+
vanguard.NewService(llmv1connect.NewLLMServiceHandler(
32+
&LLMServer{},
33+
compress1KB,
34+
connect.WithInterceptors(validateInterceptor),
35+
)),
36+
}
37+
transcoderOptions := []vanguard.TranscoderOption{
38+
vanguard.WithUnknownHandler(Custom404handler()),
39+
}
3340

41+
transcoder, err := vanguard.NewTranscoder(services, transcoderOptions...)
42+
if err != nil {
43+
return nil, fmt.Errorf("failed to create transcoder: %w", err)
44+
}
45+
mux.Handle("/", transcoder)
3446
mux.Handle(grpchealth.NewHandler(
35-
grpchealth.NewStaticChecker(greetingv1connect.GreetServiceName),
47+
grpchealth.NewStaticChecker(llmv1connect.LLMServiceName),
3648
compress1KB,
3749
))
3850

39-
reflector := grpcreflect.NewStaticReflector(greetingv1connect.GreetServiceName)
51+
reflector := grpcreflect.NewStaticReflector(llmv1connect.LLMServiceName)
4052
mux.Handle(grpcreflect.NewHandlerV1(reflector, compress1KB))
4153
mux.Handle(grpcreflect.NewHandlerV1Alpha(reflector, compress1KB))
4254

4355
return mux, nil
4456
}
4557

46-
type GreetServer struct{}
58+
type LLMServer struct {
59+
llmv1connect.UnimplementedLLMServiceHandler
60+
}
4761

48-
func (s *GreetServer) Greet(
62+
func (s *LLMServer) ChatCompletions(
4963
ctx context.Context,
50-
req *connect.Request[greetingv1.GreetRequest],
51-
) (*connect.Response[greetingv1.GreetResponse], error) {
64+
req *connect.Request[llmv1.CompletionRequest],
65+
) (*connect.Response[llmv1.CompletionResponse], error) {
5266
log.Println("Request headers: ", req.Header())
5367

54-
res := connect.NewResponse(&greetingv1.GreetResponse{
55-
Greeting: fmt.Sprintf("Hello, %s!", req.Msg.Name),
68+
res := connect.NewResponse(&llmv1.CompletionResponse{
69+
Id: "1",
70+
Object: "chat.compilation",
71+
Created: uint64(time.Now().Unix()),
72+
Model: "random",
73+
Choices: []*llmv1.CompletionChoice{},
5674
})
5775
return res, nil
5876
}

mobius/main_test.go

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import (
99
"connectrpc.com/connect"
1010

1111
"github.com/missingstudio/studio/backend/internal/connectrpc"
12-
greetingv1 "github.com/missingstudio/studio/protos/pkg/greeting/v1"
13-
"github.com/missingstudio/studio/protos/pkg/greeting/v1/greetingv1connect"
12+
llmv1 "github.com/missingstudio/studio/protos/pkg/llm"
13+
"github.com/missingstudio/studio/protos/pkg/llm/llmv1connect"
1414

1515
"github.com/stretchr/testify/require"
1616
"github.com/zeebo/assert"
@@ -19,39 +19,37 @@ import (
1919
func TestMobiusSercer(t *testing.T) {
2020
t.Parallel()
2121
mux := http.NewServeMux()
22-
mux.Handle(greetingv1connect.NewGreetServiceHandler(
23-
&connectrpc.GreetServer{},
22+
mux.Handle(llmv1connect.NewLLMServiceHandler(
23+
&connectrpc.LLMServer{},
2424
))
2525

2626
server := httptest.NewUnstartedServer(mux)
2727
server.EnableHTTP2 = true
2828
server.StartTLS()
2929
defer server.Close()
3030

31-
connectClient := greetingv1connect.NewGreetServiceClient(
31+
connectClient := llmv1connect.NewLLMServiceClient(
3232
server.Client(),
3333
server.URL,
3434
)
3535

36-
grpcClient := greetingv1connect.NewGreetServiceClient(
36+
grpcClient := llmv1connect.NewLLMServiceClient(
3737
server.Client(),
3838
server.URL,
3939
connect.WithGRPC(),
4040
)
4141

42-
clients := []greetingv1connect.GreetServiceClient{
42+
clients := []llmv1connect.LLMServiceClient{
4343
connectClient,
4444
grpcClient,
4545
}
4646

47-
t.Run("greet", func(t *testing.T) {
47+
t.Run("chat completions", func(t *testing.T) {
4848
for _, client := range clients {
49-
result, err := client.Greet(context.Background(), connect.NewRequest(&greetingv1.GreetRequest{
50-
Name: "Dev",
51-
}))
49+
result, err := client.ChatCompletions(context.Background(), connect.NewRequest(&llmv1.CompletionRequest{}))
5250

5351
require.Nil(t, err)
54-
assert.True(t, len(result.Msg.Greeting) > 0)
52+
assert.True(t, len(result.Msg.Object) > 0)
5553
}
5654
})
5755
}

protos/pkg/greeting/v1/greetingv1connect/service.connect.go

Lines changed: 0 additions & 112 deletions
This file was deleted.

0 commit comments

Comments
 (0)