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

chore: add API key ID to interceptions#20594

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Draft
pawbana wants to merge9 commits intomain
base:main
Choose a base branch
Loading
frompb/aibridge-intc-api-key-graphite
Draft
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletionscoderd/apidoc/docs.go
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

3 changes: 3 additions & 0 deletionscoderd/apidoc/swagger.json
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

3 changes: 3 additions & 0 deletionscoderd/database/db2sdk/db2sdk.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -974,6 +974,9 @@ func AIBridgeInterception(interception database.AIBridgeInterception, initiator
UserPrompts: sdkUserPrompts,
ToolUsages: sdkToolUsages,
}
if interception.APIKeyID.Valid {
intc.APIKeyID = &interception.APIKeyID.String
}
if interception.EndedAt.Valid {
intc.EndedAt = &interception.EndedAt.Time
}
Expand Down
1 change: 1 addition & 0 deletionscoderd/database/dbgen/dbgen.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -1498,6 +1498,7 @@ func ClaimPrebuild(
func AIBridgeInterception(t testing.TB, db database.Store, seed database.InsertAIBridgeInterceptionParams, endedAt *time.Time) database.AIBridgeInterception {
interception, err := db.InsertAIBridgeInterception(genCtx, database.InsertAIBridgeInterceptionParams{
ID: takeFirst(seed.ID, uuid.New()),
APIKeyID: seed.APIKeyID,
InitiatorID: takeFirst(seed.InitiatorID, uuid.New()),
Provider: takeFirst(seed.Provider, "provider"),
Model: takeFirst(seed.Model, "model"),
Expand Down
3 changes: 2 additions & 1 deletioncoderd/database/dump.sql
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
ALTER TABLE aibridge_interceptions DROP COLUMN api_key_id;
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
-- column is nullable to not break interceptions recorded before this column was added
ALTER TABLE aibridge_interceptions ADD COLUMN api_key_id text;
1 change: 1 addition & 0 deletionscoderd/database/modelqueries.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -805,6 +805,7 @@ func (q *sqlQuerier) ListAuthorizedAIBridgeInterceptions(ctx context.Context, ar
&i.AIBridgeInterception.StartedAt,
&i.AIBridgeInterception.Metadata,
&i.AIBridgeInterception.EndedAt,
&i.AIBridgeInterception.APIKeyID,
&i.VisibleUser.ID,
&i.VisibleUser.Username,
&i.VisibleUser.Name,
Expand Down
1 change: 1 addition & 0 deletionscoderd/database/models.go
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

21 changes: 14 additions & 7 deletionscoderd/database/queries.sql.go
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

4 changes: 2 additions & 2 deletionscoderd/database/queries/aibridge.sql
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
-- name: InsertAIBridgeInterception :one
INSERT INTO aibridge_interceptions (
id, initiator_id, provider, model, metadata, started_at
id,api_key_id,initiator_id, provider, model, metadata, started_at
)VALUES (
@id, @initiator_id, @provider, @model, COALESCE(@metadata::jsonb,'{}'::jsonb), @started_at
@id, @api_key_id, @initiator_id, @provider, @model, COALESCE(@metadata::jsonb,'{}'::jsonb), @started_at
)
RETURNING*;

Expand Down
1 change: 1 addition & 0 deletionscodersdk/aibridge.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@ import (

type AIBridgeInterception struct {
ID uuid.UUID `json:"id" format:"uuid"`
APIKeyID *string `json:"api_key_id"`
Initiator MinimalUser `json:"initiator"`
Provider string `json:"provider"`
Model string `json:"model"`
Expand Down
1 change: 1 addition & 0 deletionsdocs/reference/api/aibridge.md
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

3 changes: 3 additions & 0 deletionsdocs/reference/api/schemas.md
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

13 changes: 13 additions & 0 deletionsenterprise/aibridged/aibridged_integration_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,6 +18,7 @@ import (
"github.com/coder/coder/v2/coderd/database/dbtestutil"
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/coderd/externalauth"
"github.com/coder/coder/v2/coderd/httpmw"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/enterprise/aibridged"
"github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
Expand DownExpand Up@@ -221,6 +222,18 @@ func TestIntegration(t *testing.T) {
require.NoError(t, err)
require.Len(t, interceptions, 1)

intc0 := interceptions[0]
keyID, _, err := httpmw.SplitAPIToken(apiKey.Key)
require.NoError(t, err)
require.Equal(t, user.ID, intc0.InitiatorID)
require.True(t, intc0.APIKeyID.Valid)
require.Equal(t, keyID, intc0.APIKeyID.String)
require.Equal(t, "openai", intc0.Provider)
require.Equal(t, "gpt-4.1", intc0.Model)
require.True(t, intc0.EndedAt.Valid)
require.True(t, intc0.StartedAt.Before(intc0.EndedAt.Time))
require.Less(t, intc0.EndedAt.Time.Sub(intc0.StartedAt), 5*time.Second)

prompts, err := db.GetAIBridgeUserPromptsByInterceptionID(ctx, interceptions[0].ID)
require.NoError(t, err)
require.Len(t, prompts, 1)
Expand Down
1 change: 1 addition & 0 deletionsenterprise/aibridged/http.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -68,6 +68,7 @@ func (s *Server) ServeHTTP(rw http.ResponseWriter, r *http.Request) {

handler, err := s.GetRequestHandler(ctx, Request{
SessionKey: key,
APIKeyID: resp.ApiKeyId,
InitiatorID: id,
})
if err != nil {
Expand Down
23 changes: 12 additions & 11 deletionsenterprise/aibridged/pool.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -111,17 +111,9 @@ func (p *CachedBridgePool) Acquire(ctx context.Context, req Request, clientFn Cl
// may visit the slow path unnecessarily.
defer p.cache.Wait()

recorder := aibridge.NewRecorder(p.logger.Named("recorder"), func() (aibridge.Recorder, error) {
client, err := clientFn()
if err != nil {
return nil, xerrors.Errorf("acquire client: %w", err)
}

return &recorderTranslation{client: client}, nil
})

// Fast path.
bridge, ok := p.cache.Get(req.InitiatorID.String())
cacheKey := req.InitiatorID.String() + "|" + req.APIKeyID
bridge, ok := p.cache.Get(cacheKey)
if ok && bridge != nil {
// TODO: future improvement:
// Once we can detect token expiry against an MCP server, we no longer need to let these instances
Expand All@@ -131,6 +123,15 @@ func (p *CachedBridgePool) Acquire(ctx context.Context, req Request, clientFn Cl
return bridge, nil
}

recorder := aibridge.NewRecorder(p.logger.Named("recorder"), func() (aibridge.Recorder, error) {
client, err := clientFn()
if err != nil {
return nil, xerrors.Errorf("acquire client: %w", err)
}

return &recorderTranslation{apiKeyID: req.APIKeyID, client: client}, nil
})

// Slow path.
// Creating an *aibridge.RequestBridge may take some time, so gate all subsequent callers behind the initial request and return the resulting value.
// TODO: track startup time since it adds latency to first request (histogram count will also help us see how often this occurs).
Expand DownExpand Up@@ -158,7 +159,7 @@ func (p *CachedBridgePool) Acquire(ctx context.Context, req Request, clientFn Cl
return nil, xerrors.Errorf("create new request bridge: %w", err)
}

p.cache.SetWithTTL(req.InitiatorID.String(), bridge, cacheCost, p.options.TTL)
p.cache.SetWithTTL(cacheKey, bridge, cacheCost, p.options.TTL)

return bridge, nil
})
Expand Down
23 changes: 22 additions & 1 deletionenterprise/aibridged/pool_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,7 +34,7 @@ func TestPool(t *testing.T) {
require.NoError(t, err)
t.Cleanup(func() { pool.Shutdown(context.Background()) })

id, id2 := uuid.New(), uuid.New()
id, id2, apiKeyID1, apiKeyID2 := uuid.New(), uuid.New(), uuid.New(), uuid.New()
clientFn := func() (aibridged.DRPCClient, error) {
return client, nil
}
Expand All@@ -50,13 +50,15 @@ func TestPool(t *testing.T) {
inst, err := pool.Acquire(t.Context(), aibridged.Request{
SessionKey: "key",
InitiatorID: id,
APIKeyID: apiKeyID1.String(),
}, clientFn, newMockMCPFactory(mcpProxy))
require.NoError(t, err, "acquire pool instance")

// ...and it will return it when acquired again.
instB, err := pool.Acquire(t.Context(), aibridged.Request{
SessionKey: "key",
InitiatorID: id,
APIKeyID: apiKeyID1.String(),
}, clientFn, newMockMCPFactory(mcpProxy))
require.NoError(t, err, "acquire pool instance")
require.Same(t, inst, instB)
Expand All@@ -74,6 +76,7 @@ func TestPool(t *testing.T) {
inst2, err := pool.Acquire(t.Context(), aibridged.Request{
SessionKey: "key",
InitiatorID: id2,
APIKeyID: apiKeyID1.String(),
}, clientFn, newMockMCPFactory(mcpProxy))
require.NoError(t, err, "acquire pool instance")
require.NotSame(t, inst, inst2)
Expand All@@ -84,6 +87,24 @@ func TestPool(t *testing.T) {
require.EqualValues(t, 1, metrics.Hits())
require.EqualValues(t, 2, metrics.Misses())

// This will get called again because a new instance will be created.
mcpProxy.EXPECT().Init(gomock.Any()).Times(1).Return(nil)

// New instance is created for different api key id
inst2B, err := pool.Acquire(t.Context(), aibridged.Request{
SessionKey: "key",
InitiatorID: id2,
APIKeyID: apiKeyID2.String(),
}, clientFn, newMockMCPFactory(mcpProxy))
require.NoError(t, err, "acquire pool instance 2B")
require.NotSame(t, inst2, inst2B)

metrics = pool.Metrics()
require.EqualValues(t, 3, metrics.KeysAdded())
require.EqualValues(t, 2, metrics.KeysEvicted())
require.EqualValues(t, 1, metrics.Hits())
require.EqualValues(t, 3, metrics.Misses())

// TODO: add test for expiry.
// This requires Go 1.25's [synctest](https://pkg.go.dev/testing/synctest) since the
// internal cache lib cannot be tested using coder/quartz.
Expand Down
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp