chore: graduate aibridge API out of experimental (#20523)

<!--

If you have used AI to produce some or all of this PR, please ensure you have read our [AI Contribution guidelines](https://coder.com/docs/about/contributing/AI_CONTRIBUTING) before submitting.

-->
This commit is contained in:
Danny Kopping
2025-10-29 07:18:54 -06:00
committed by GitHub
parent 2294c55bd9
commit b20fd6f2c1
8 changed files with 22 additions and 28 deletions
+1 -1
View File
@@ -85,7 +85,7 @@ const docTemplate = `{
}
}
},
"/api/experimental/aibridge/interceptions": {
"/aibridge/interceptions": {
"get": {
"security": [
{
+1 -1
View File
@@ -65,7 +65,7 @@
}
}
},
"/api/experimental/aibridge/interceptions": {
"/aibridge/interceptions": {
"get": {
"security": [
{
+2 -2
View File
@@ -113,8 +113,8 @@ func (f AIBridgeListInterceptionsFilter) asRequestOption() RequestOption {
// AIBridgeListInterceptions returns AIBridge interceptions with the given
// filter.
func (c *ExperimentalClient) AIBridgeListInterceptions(ctx context.Context, filter AIBridgeListInterceptionsFilter) (AIBridgeListInterceptionsResponse, error) {
res, err := c.Request(ctx, http.MethodGet, "/api/experimental/aibridge/interceptions", nil, filter.asRequestOption(), filter.Pagination.asRequestOption(), filter.Pagination.asRequestOption())
func (c *Client) AIBridgeListInterceptions(ctx context.Context, filter AIBridgeListInterceptionsFilter) (AIBridgeListInterceptionsResponse, error) {
res, err := c.Request(ctx, http.MethodGet, "/api/v2/aibridge/interceptions", nil, filter.asRequestOption(), filter.Pagination.asRequestOption(), filter.Pagination.asRequestOption())
if err != nil {
return AIBridgeListInterceptionsResponse{}, err
}
+2 -2
View File
@@ -6,12 +6,12 @@
```shell
# Example request using curl
curl -X GET http://coder-server:8080/api/v2/api/experimental/aibridge/interceptions \
curl -X GET http://coder-server:8080/api/v2/aibridge/interceptions \
-H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY'
```
`GET /api/experimental/aibridge/interceptions`
`GET /aibridge/interceptions`
### Parameters
+1 -1
View File
@@ -19,7 +19,7 @@ var _ io.Closer = &Server{}
// Server provides the AI Bridge functionality.
// It is responsible for:
// - receiving requests on /api/experimental/aibridged/* // TODO: update endpoint once out of experimental
// - receiving requests on /api/v2/aibridged/*
// - manipulating the requests
// - relaying requests to upstream AI services and relaying responses to caller
//
+1 -1
View File
@@ -36,7 +36,7 @@ const (
// @Param after_id query string false "Cursor pagination after ID (cannot be used with offset)"
// @Param offset query int false "Offset pagination (cannot be used with after_id)"
// @Success 200 {object} codersdk.AIBridgeListInterceptionsResponse
// @Router /api/experimental/aibridge/interceptions [get]
// @Router /aibridge/interceptions [get]
func (api *API) aiBridgeListInterceptions(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
apiKey := httpmw.APIKey(r)
+12 -18
View File
@@ -36,10 +36,10 @@ func TestAIBridgeListInterceptions(t *testing.T) {
Features: license.Features{},
},
})
experimentalClient := codersdk.NewExperimentalClient(client)
ctx := testutil.Context(t, testutil.WaitLong)
_, err := experimentalClient.AIBridgeListInterceptions(ctx, codersdk.AIBridgeListInterceptionsFilter{})
//nolint:gocritic // Owner role is irrelevant here.
_, err := client.AIBridgeListInterceptions(ctx, codersdk.AIBridgeListInterceptionsFilter{})
var sdkErr *codersdk.Error
require.ErrorAs(t, err, &sdkErr)
require.Equal(t, http.StatusForbidden, sdkErr.StatusCode())
@@ -59,9 +59,9 @@ func TestAIBridgeListInterceptions(t *testing.T) {
},
},
})
experimentalClient := codersdk.NewExperimentalClient(client)
ctx := testutil.Context(t, testutil.WaitLong)
res, err := experimentalClient.AIBridgeListInterceptions(ctx, codersdk.AIBridgeListInterceptionsFilter{})
//nolint:gocritic // Owner role is irrelevant here.
res, err := client.AIBridgeListInterceptions(ctx, codersdk.AIBridgeListInterceptionsFilter{})
require.NoError(t, err)
require.Empty(t, res.Results)
})
@@ -79,7 +79,6 @@ func TestAIBridgeListInterceptions(t *testing.T) {
},
},
})
experimentalClient := codersdk.NewExperimentalClient(client)
ctx := testutil.Context(t, testutil.WaitLong)
user1, err := client.User(ctx, codersdk.Me)
@@ -140,7 +139,7 @@ func TestAIBridgeListInterceptions(t *testing.T) {
i1SDK := db2sdk.AIBridgeInterception(i1, user1Visible, []database.AIBridgeTokenUsage{i1tok2, i1tok1}, []database.AIBridgeUserPrompt{i1up2, i1up1}, []database.AIBridgeToolUsage{i1tool2, i1tool1})
i2SDK := db2sdk.AIBridgeInterception(i2, user2Visible, nil, nil, nil)
res, err := experimentalClient.AIBridgeListInterceptions(ctx, codersdk.AIBridgeListInterceptionsFilter{})
res, err := client.AIBridgeListInterceptions(ctx, codersdk.AIBridgeListInterceptionsFilter{})
require.NoError(t, err)
require.Len(t, res.Results, 2)
require.Equal(t, i2SDK.ID, res.Results[0].ID)
@@ -190,7 +189,6 @@ func TestAIBridgeListInterceptions(t *testing.T) {
},
},
})
experimentalClient := codersdk.NewExperimentalClient(client)
ctx := testutil.Context(t, testutil.WaitLong)
allInterceptionIDs := make([]uuid.UUID, 0, 20)
@@ -221,7 +219,7 @@ func TestAIBridgeListInterceptions(t *testing.T) {
}
// Try to fetch with an invalid limit.
res, err := experimentalClient.AIBridgeListInterceptions(ctx, codersdk.AIBridgeListInterceptionsFilter{
res, err := client.AIBridgeListInterceptions(ctx, codersdk.AIBridgeListInterceptionsFilter{
Pagination: codersdk.Pagination{
Limit: 1001,
},
@@ -232,7 +230,7 @@ func TestAIBridgeListInterceptions(t *testing.T) {
require.Empty(t, res.Results)
// Try to fetch with both after_id and offset pagination.
res, err = experimentalClient.AIBridgeListInterceptions(ctx, codersdk.AIBridgeListInterceptionsFilter{
res, err = client.AIBridgeListInterceptions(ctx, codersdk.AIBridgeListInterceptionsFilter{
Pagination: codersdk.Pagination{
AfterID: allInterceptionIDs[0],
Offset: 1,
@@ -265,7 +263,7 @@ func TestAIBridgeListInterceptions(t *testing.T) {
} else {
pagination.Offset = len(interceptionIDs)
}
res, err := experimentalClient.AIBridgeListInterceptions(ctx, codersdk.AIBridgeListInterceptionsFilter{
res, err := client.AIBridgeListInterceptions(ctx, codersdk.AIBridgeListInterceptionsFilter{
Pagination: pagination,
})
require.NoError(t, err)
@@ -305,11 +303,9 @@ func TestAIBridgeListInterceptions(t *testing.T) {
},
},
})
adminExperimentalClient := codersdk.NewExperimentalClient(adminClient)
ctx := testutil.Context(t, testutil.WaitLong)
secondUserClient, secondUser := coderdtest.CreateAnotherUser(t, adminClient, firstUser.OrganizationID)
secondUserExperimentalClient := codersdk.NewExperimentalClient(secondUserClient)
now := dbtime.Now()
i1 := dbgen.AIBridgeInterception(t, db, database.InsertAIBridgeInterceptionParams{
@@ -322,7 +318,7 @@ func TestAIBridgeListInterceptions(t *testing.T) {
}, &now)
// Admin can see all interceptions.
res, err := adminExperimentalClient.AIBridgeListInterceptions(ctx, codersdk.AIBridgeListInterceptionsFilter{})
res, err := adminClient.AIBridgeListInterceptions(ctx, codersdk.AIBridgeListInterceptionsFilter{})
require.NoError(t, err)
require.EqualValues(t, 2, res.Count)
require.Len(t, res.Results, 2)
@@ -330,7 +326,7 @@ func TestAIBridgeListInterceptions(t *testing.T) {
require.Equal(t, i2.ID, res.Results[1].ID)
// Second user can only see their own interceptions.
res, err = secondUserExperimentalClient.AIBridgeListInterceptions(ctx, codersdk.AIBridgeListInterceptionsFilter{})
res, err = secondUserClient.AIBridgeListInterceptions(ctx, codersdk.AIBridgeListInterceptionsFilter{})
require.NoError(t, err)
require.EqualValues(t, 1, res.Count)
require.Len(t, res.Results, 1)
@@ -350,7 +346,6 @@ func TestAIBridgeListInterceptions(t *testing.T) {
},
},
})
experimentalClient := codersdk.NewExperimentalClient(client)
ctx := testutil.Context(t, testutil.WaitLong)
user1, err := client.User(ctx, codersdk.Me)
@@ -500,7 +495,7 @@ func TestAIBridgeListInterceptions(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitLong)
res, err := experimentalClient.AIBridgeListInterceptions(ctx, tc.filter)
res, err := client.AIBridgeListInterceptions(ctx, tc.filter)
require.NoError(t, err)
require.EqualValues(t, len(tc.want), res.Count)
// We just compare UUID strings for the sake of this test.
@@ -530,7 +525,6 @@ func TestAIBridgeListInterceptions(t *testing.T) {
},
},
})
experimentalClient := codersdk.NewExperimentalClient(client)
// No need to insert any test data, we're just testing the filter
// errors.
@@ -587,7 +581,7 @@ func TestAIBridgeListInterceptions(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitLong)
res, err := experimentalClient.AIBridgeListInterceptions(ctx, codersdk.AIBridgeListInterceptionsFilter{
res, err := client.AIBridgeListInterceptions(ctx, codersdk.AIBridgeListInterceptionsFilter{
FilterQuery: tc.q,
})
var sdkErr *codersdk.Error
+2 -2
View File
@@ -226,7 +226,7 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
return api.refreshEntitlements(ctx)
}
api.AGPL.ExperimentalHandler.Group(func(r chi.Router) {
api.AGPL.APIHandler.Group(func(r chi.Router) {
r.Route("/aibridge", func(r chi.Router) {
r.Use(api.RequireFeatureMW(codersdk.FeatureAIBridge))
r.Group(func(r chi.Router) {
@@ -243,7 +243,7 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
})
return
}
http.StripPrefix("/api/experimental/aibridge", api.aibridgedHandler).ServeHTTP(rw, r)
http.StripPrefix("/api/v2/aibridge", api.aibridgedHandler).ServeHTTP(rw, r)
})
})
})