Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitff532d9

Browse files
authored
chore: handle deprecatedaibridge experimental routes (#20565)
In v2.28 we're [removing the aibridgeexperiment](#20544).We need to handle `/api/experimental/aibridge/*` until Beta (nextrelease).Signed-off-by: Danny Kopping <danny@coder.com>
1 parent54497f4 commitff532d9

File tree

3 files changed

+107
-19
lines changed

3 files changed

+107
-19
lines changed

‎enterprise/coderd/aibridge.go‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import (
44
"context"
55
"fmt"
66
"net/http"
7+
"strings"
78

9+
"github.com/go-chi/chi/v5"
810
"github.com/google/uuid"
911
"golang.org/x/xerrors"
1012

@@ -23,6 +25,38 @@ const (
2325
defaultListInterceptionsLimit=100
2426
)
2527

28+
// aibridgeHandler handles all aibridged-related endpoints.
29+
funcaibridgeHandler(api*API,middlewares...func(http.Handler) http.Handler)func(r chi.Router) {
30+
returnfunc(r chi.Router) {
31+
r.Use(api.RequireFeatureMW(codersdk.FeatureAIBridge))
32+
r.Group(func(r chi.Router) {
33+
r.Use(middlewares...)
34+
r.Get("/interceptions",api.aiBridgeListInterceptions)
35+
})
36+
37+
// This is a bit funky but since aibridge only exposes a HTTP
38+
// handler, this is how it has to be.
39+
r.HandleFunc("/*",func(rw http.ResponseWriter,r*http.Request) {
40+
ifapi.aibridgedHandler==nil {
41+
httpapi.Write(r.Context(),rw,http.StatusNotFound, codersdk.Response{
42+
Message:"aibridged handler not mounted",
43+
})
44+
return
45+
}
46+
47+
// Strip either the experimental or stable prefix.
48+
// TODO: experimental route is deprecated and must be removed with Beta.
49+
prefixes:= []string{"/api/experimental/aibridge","/api/v2/aibridge"}
50+
for_,prefix:=rangeprefixes {
51+
ifstrings.Contains(r.URL.String(),prefix) {
52+
http.StripPrefix(prefix,api.aibridgedHandler).ServeHTTP(rw,r)
53+
break
54+
}
55+
}
56+
})
57+
}
58+
}
59+
2660
// aiBridgeListInterceptions returns all AIBridge interceptions a user can read.
2761
// Optional filters with query params
2862
//

‎enterprise/coderd/aibridge_test.go‎

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package coderd_test
22

33
import (
4+
"io"
45
"net/http"
56
"testing"
67
"time"
@@ -592,3 +593,68 @@ func TestAIBridgeListInterceptions(t *testing.T) {
592593
}
593594
})
594595
}
596+
597+
funcTestAIBridgeRouting(t*testing.T) {
598+
t.Parallel()
599+
600+
dv:=coderdtest.DeploymentValues(t)
601+
client,closer,api,_:=coderdenttest.NewWithAPI(t,&coderdenttest.Options{
602+
Options:&coderdtest.Options{
603+
DeploymentValues:dv,
604+
},
605+
LicenseOptions:&coderdenttest.LicenseOptions{
606+
Features: license.Features{
607+
codersdk.FeatureAIBridge:1,
608+
},
609+
},
610+
})
611+
t.Cleanup(func() {
612+
_=closer.Close()
613+
})
614+
615+
// Register a simple test handler that echoes back the request path.
616+
testHandler:=http.HandlerFunc(func(rw http.ResponseWriter,r*http.Request) {
617+
rw.WriteHeader(http.StatusOK)
618+
_,_=rw.Write([]byte(r.URL.Path))
619+
})
620+
api.RegisterInMemoryAIBridgedHTTPHandler(testHandler)
621+
622+
cases:= []struct {
623+
namestring
624+
pathstring
625+
expectedPathstring
626+
}{
627+
{
628+
name:"StablePrefix",
629+
path:"/api/v2/aibridge/openai/v1/chat/completions",
630+
expectedPath:"/openai/v1/chat/completions",
631+
},
632+
{
633+
name:"ExperimentalPrefix",
634+
path:"/api/experimental/aibridge/openai/v1/chat/completions",
635+
expectedPath:"/openai/v1/chat/completions",
636+
},
637+
}
638+
639+
for_,tc:=rangecases {
640+
t.Run(tc.name,func(t*testing.T) {
641+
t.Parallel()
642+
643+
ctx:=testutil.Context(t,testutil.WaitLong)
644+
req,err:=http.NewRequestWithContext(ctx,http.MethodPost,client.URL.String()+tc.path,nil)
645+
require.NoError(t,err)
646+
req.Header.Set(codersdk.SessionTokenHeader,client.SessionToken())
647+
648+
httpClient:=&http.Client{}
649+
resp,err:=httpClient.Do(req)
650+
require.NoError(t,err)
651+
deferresp.Body.Close()
652+
require.Equal(t,http.StatusOK,resp.StatusCode)
653+
654+
// Verify that the prefix was stripped correctly and the path was forwarded.
655+
body,err:=io.ReadAll(resp.Body)
656+
require.NoError(t,err)
657+
require.Equal(t,tc.expectedPath,string(body))
658+
})
659+
}
660+
}

‎enterprise/coderd/coderd.go‎

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -226,26 +226,14 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
226226
returnapi.refreshEntitlements(ctx)
227227
}
228228

229-
api.AGPL.APIHandler.Group(func(r chi.Router) {
230-
r.Route("/aibridge",func(r chi.Router) {
231-
r.Use(api.RequireFeatureMW(codersdk.FeatureAIBridge))
232-
r.Group(func(r chi.Router) {
233-
r.Use(apiKeyMiddleware)
234-
r.Get("/interceptions",api.aiBridgeListInterceptions)
235-
})
229+
api.AGPL.ExperimentalHandler.Group(func(r chi.Router) {
230+
// Deprecated.
231+
// TODO: remove with Beta release.
232+
r.Route("/aibridge",aibridgeHandler(api,apiKeyMiddleware))
233+
})
236234

237-
// This is a bit funky but since aibridge only exposes a HTTP
238-
// handler, this is how it has to be.
239-
r.HandleFunc("/*",func(rw http.ResponseWriter,r*http.Request) {
240-
ifapi.aibridgedHandler==nil {
241-
httpapi.Write(r.Context(),rw,http.StatusNotFound, codersdk.Response{
242-
Message:"aibridged handler not mounted",
243-
})
244-
return
245-
}
246-
http.StripPrefix("/api/v2/aibridge",api.aibridgedHandler).ServeHTTP(rw,r)
247-
})
248-
})
235+
api.AGPL.APIHandler.Group(func(r chi.Router) {
236+
r.Route("/aibridge",aibridgeHandler(api,apiKeyMiddleware))
249237
})
250238

251239
api.AGPL.APIHandler.Group(func(r chi.Router) {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp