Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
65dbb72
feat: update header content
jakehwll Dec 11, 2025
f708a74
fix: resolve table layout
jakehwll Dec 11, 2025
0b2ab86
feat: add model content
jakehwll Dec 11, 2025
476030a
feat: implement badges for input/output
jakehwll Dec 11, 2025
c69c5dd
feat: add customised date (ew)
jakehwll Dec 11, 2025
375be8b
chore: remove hover effect
jakehwll Dec 11, 2025
7d1fdc7
fix: truncate content correctly in `<RequestLogsRow />`
jakehwll Dec 11, 2025
0798036
feat: add provider iconography
jakehwll Dec 11, 2025
d3ce38a
feat: enforce truncation of usernames
jakehwll Dec 11, 2025
fd91c72
feat: extend icons into `<ProviderFilter />`
jakehwll Dec 11, 2025
10328b6
feat: restore tooltips
jakehwll Dec 12, 2025
1ad186f
fix: resolve location of `X of X`
jakehwll Dec 12, 2025
6413524
fix: rename `user` to `initiator`
jakehwll Dec 12, 2025
306cef3
fix: resolve padding on tablecell
jakehwll Dec 12, 2025
939a343
fix: resolve date rendering on `interception`
jakehwll Dec 12, 2025
dabd9db
feat: implement interception model truncation
jakehwll Dec 12, 2025
9c79649
fix: resolve initiator truncation
jakehwll Dec 12, 2025
8295422
fix: resolve content leading and size
jakehwll Dec 12, 2025
af9767d
fix: prefer `name` instead of `username`
jakehwll Dec 12, 2025
742a0ec
fix: resolve pagination amount position
jakehwll Dec 12, 2025
0e5e705
Merge branch 'main' into jakehwll/ai-bridge-request-logs-improvements
jakehwll Dec 12, 2025
8cfd5af
chore: lint
jakehwll Dec 12, 2025
1561217
fix: revert `<PageHeaderTitle/>` and `<PageHeaderSubtitle />`
jakehwll Dec 13, 2025
c539f6a
feat: rename `OpenAiIcon` to `OpenAIIcon`
jakehwll Dec 13, 2025
3d415c8
fix: rename `PaginationHeader` to `PaginationAmount`
jakehwll Dec 13, 2025
2ba10ca
feat: replicate input/output tokens into one field
jakehwll Dec 13, 2025
9dd3abe
feat: return all the models from the db
jakehwll Dec 13, 2025
020daf6
feat: implement filtering by `model`
jakehwll Dec 13, 2025
7caae7a
feat: implement `q`, `limit` and `offset`
jakehwll Dec 13, 2025
d98267e
feat: add missing docs
jakehwll Dec 13, 2025
0d13fe9
fix: resolve `dbmock.go`
jakehwll Dec 13, 2025
d6ead77
chore: regenerate `dbmock.go`
jakehwll Dec 13, 2025
4fd7a36
chore: resolve `swagger.json` diff
jakehwll Dec 13, 2025
64e5b41
fix: remove `dbmock.go` from root
jakehwll Dec 13, 2025
0188989
chore: commit bump (ci)
jakehwll Dec 13, 2025
37cfc69
fix: resolve `ended_at` actually being `started_at`
jakehwll Dec 14, 2025
f0cb35b
Merge branch 'jakehwll/ai-bridge-request-logs-improvements' into jake…
jakehwll Dec 15, 2025
3bf8111
Merge branch 'main' into jakehwll/ai-bridge-request-logs-improvements
jakehwll Dec 15, 2025
3392877
Merge branch 'jakehwll/ai-bridge-request-logs-improvements' into jake…
jakehwll Dec 15, 2025
0612534
Merge branch 'main' into jakehwll/ai-bridge-request-logs-improvements
jakehwll Dec 15, 2025
9979902
fix: remove unused `db`
jakehwll Dec 15, 2025
9278746
feat: add `dbauthz_test.go`
jakehwll Dec 15, 2025
205a235
fix: change `ListAuthorizedAIBridgeModels` to `ListAIBridgeModels` in…
jakehwll Dec 15, 2025
cddf851
chore: remove `.db` to remove panic error
jakehwll Dec 15, 2025
c5f5d89
chore: rename `prepared` to `_` as its unused
jakehwll Dec 15, 2025
b7ff2a5
Merge branch 'jakehwll/ai-bridge-request-logs-improvements' into jake…
jakehwll Dec 15, 2025
43e6fde
chore: commit bump (ci)
jakehwll Dec 15, 2025
5e15a0e
fix: resolve missing key in story
jakehwll Dec 15, 2025
e2c0a29
chore: commit bump (ci)
jakehwll Dec 15, 2025
325f70b
fix: use `ILIKE` instead of `=` on search
jakehwll Dec 15, 2025
1e1e92f
chore: commit bump (ci)
jakehwll Dec 15, 2025
837a49c
Merge branch 'jakehwll/ai-bridge-request-logs-improvements' into jake…
jakehwll Dec 15, 2025
8d0fd1d
fix: add comment describing `@authorize_filter` usecase
jakehwll Dec 15, 2025
f933ea4
fix: describe the default in `AIBridgeModels()`
jakehwll Dec 15, 2025
6078455
fix: add missing comment in `queries.sql.go`
jakehwll Dec 15, 2025
ac6af6a
Update site/src/pages/AIBridgePage/RequestLogsPage/RequestLogsRow/Req…
jakehwll Dec 16, 2025
ee2f961
fix: remove emotion styles
jakehwll Dec 16, 2025
19bdefd
fix: ensure we show date in `customisedDateLocale`
jakehwll Dec 16, 2025
f4eba46
fix: update comment to describe mobile better
jakehwll Dec 16, 2025
f076c5d
fix: resolve icons
jakehwll Dec 16, 2025
4941f85
fix: add `anthropic.svg` to `icons.json`
jakehwll Dec 16, 2025
7392a1f
Merge branch 'main' into jakehwll/ai-bridge-request-logs-improvements
jakehwll Dec 17, 2025
0de9cb9
fix: rename `anthropic.svg` to `claude-device.svg`
jakehwll Dec 17, 2025
89a5b8f
fix: refactor to `<AIBridgeProviderIcon />`
jakehwll Dec 17, 2025
3ce0d11
fix: reorder `"claude-device.svg"` and `"claude.svg"`
jakehwll Dec 17, 2025
37cda01
fix: add missing icon size class
jakehwll Dec 17, 2025
a1979d2
fix: dont enforce `hour12` time
jakehwll Dec 17, 2025
1f9aac6
feat: add `hover` effect
jakehwll Dec 17, 2025
dceecec
chore!: promote AIBridge from `ExperimentalHandler` (#21278)
jakehwll Dec 17, 2025
48f5403
Merge branch 'jakehwll/ai-bridge-request-logs-improvements' into jake…
jakehwll Dec 18, 2025
7aa5496
feat: implement `<RequestLogsPrompt />`
jakehwll Dec 18, 2025
a5e7aba
feat: increase `line-clamp`, inline with figma
jakehwll Dec 18, 2025
fb62d10
fix: ensure `max-w-` doesn't exceed `800px`
jakehwll Dec 18, 2025
ea4506a
chore: commit bump
jakehwll Dec 23, 2025
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
28 changes: 28 additions & 0 deletions coderd/apidoc/docs.go

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

24 changes: 24 additions & 0 deletions coderd/apidoc/swagger.json

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

15 changes: 15 additions & 0 deletions coderd/database/dbauthz/dbauthz.go
Original file line number Diff line number Diff line change
Expand Up @@ -4647,6 +4647,14 @@ func (q *querier) ListAIBridgeInterceptionsTelemetrySummaries(ctx context.Contex
return q.db.ListAIBridgeInterceptionsTelemetrySummaries(ctx, arg)
}

func (q *querier) ListAIBridgeModels(ctx context.Context, arg database.ListAIBridgeModelsParams) ([]string, error) {
prep, err := prepareSQLFilter(ctx, q.auth, policy.ActionRead, rbac.ResourceAibridgeInterception.Type)
if err != nil {
return nil, xerrors.Errorf("(dev error) prepare sql filter: %w", err)
}
return q.db.ListAuthorizedAIBridgeModels(ctx, arg, prep)
}

func (q *querier) ListAIBridgeTokenUsagesByInterceptionIDs(ctx context.Context, interceptionIDs []uuid.UUID) ([]database.AIBridgeTokenUsage, error) {
// This function is a system function until we implement a join for aibridge interceptions.
// Matches the behavior of the workspaces listing endpoint.
Expand Down Expand Up @@ -6156,3 +6164,10 @@ func (q *querier) CountAuthorizedAIBridgeInterceptions(ctx context.Context, arg
// database.Store interface, so dbauthz needs to implement it.
return q.CountAIBridgeInterceptions(ctx, arg)
}

func (q *querier) ListAuthorizedAIBridgeModels(ctx context.Context, arg database.ListAIBridgeModelsParams, _ rbac.PreparedAuthorized) ([]string, error) {
// TODO: Delete this function, all ListAIBridgeModels should be authorized. For now just call ListAIBridgeModels on the authz querier.
// This cannot be deleted for now because it's included in the
// database.Store interface, so dbauthz needs to implement it.
return q.ListAIBridgeModels(ctx, arg)
}
14 changes: 14 additions & 0 deletions coderd/database/dbauthz/dbauthz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4662,6 +4662,20 @@ func (s *MethodTestSuite) TestAIBridge() {
check.Args(params, emptyPreparedAuthorized{}).Asserts()
}))

s.Run("ListAIBridgeModels", s.Mocked(func(db *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
params := database.ListAIBridgeModelsParams{}
db.EXPECT().ListAuthorizedAIBridgeModels(gomock.Any(), params, gomock.Any()).Return([]string{}, nil).AnyTimes()
// No asserts here because SQLFilter.
check.Args(params).Asserts()
}))

s.Run("ListAuthorizedAIBridgeModels", s.Mocked(func(db *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
params := database.ListAIBridgeModelsParams{}
db.EXPECT().ListAuthorizedAIBridgeModels(gomock.Any(), params, gomock.Any()).Return([]string{}, nil).AnyTimes()
// No asserts here because SQLFilter.
check.Args(params, emptyPreparedAuthorized{}).Asserts()
}))

s.Run("CountAIBridgeInterceptions", s.Mocked(func(db *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
params := database.CountAIBridgeInterceptionsParams{}
db.EXPECT().CountAuthorizedAIBridgeInterceptions(gomock.Any(), params, gomock.Any()).Return(int64(0), nil).AnyTimes()
Expand Down
14 changes: 14 additions & 0 deletions coderd/database/dbmetrics/querymetrics.go

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

30 changes: 30 additions & 0 deletions coderd/database/dbmock/dbmock.go

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

30 changes: 30 additions & 0 deletions coderd/database/modelqueries.go
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,7 @@ func (q *sqlQuerier) CountAuthorizedConnectionLogs(ctx context.Context, arg Coun
type aibridgeQuerier interface {
ListAuthorizedAIBridgeInterceptions(ctx context.Context, arg ListAIBridgeInterceptionsParams, prepared rbac.PreparedAuthorized) ([]ListAIBridgeInterceptionsRow, error)
CountAuthorizedAIBridgeInterceptions(ctx context.Context, arg CountAIBridgeInterceptionsParams, prepared rbac.PreparedAuthorized) (int64, error)
ListAuthorizedAIBridgeModels(ctx context.Context, arg ListAIBridgeModelsParams, prepared rbac.PreparedAuthorized) ([]string, error)
}

func (q *sqlQuerier) ListAuthorizedAIBridgeInterceptions(ctx context.Context, arg ListAIBridgeInterceptionsParams, prepared rbac.PreparedAuthorized) ([]ListAIBridgeInterceptionsRow, error) {
Expand Down Expand Up @@ -866,6 +867,35 @@ func (q *sqlQuerier) CountAuthorizedAIBridgeInterceptions(ctx context.Context, a
return count, nil
}

func (q *sqlQuerier) ListAuthorizedAIBridgeModels(ctx context.Context, arg ListAIBridgeModelsParams, prepared rbac.PreparedAuthorized) ([]string, error) {
authorizedFilter, err := prepared.CompileToSQL(ctx, regosql.ConvertConfig{
VariableConverter: regosql.AIBridgeInterceptionConverter(),
})
if err != nil {
return nil, xerrors.Errorf("compile authorized filter: %w", err)
}
filtered, err := insertAuthorizedFilter(listAIBridgeModels, fmt.Sprintf(" AND %s", authorizedFilter))
if err != nil {
return nil, xerrors.Errorf("insert authorized filter: %w", err)
}

query := fmt.Sprintf("-- name: ListAIBridgeModels :many\n%s", filtered)
rows, err := q.db.QueryContext(ctx, query, arg.Model, arg.Offset, arg.Limit)
if err != nil {
return nil, err
}
defer rows.Close()
var items []string
for rows.Next() {
var model string
if err := rows.Scan(&model); err != nil {
return nil, err
}
items = append(items, model)
}
return items, nil
}

func insertAuthorizedFilter(query string, replaceWith string) (string, error) {
if !strings.Contains(query, authorizedQueryPlaceholder) {
return "", xerrors.Errorf("query does not contain authorized replace string, this is not an authorized query")
Expand Down
1 change: 1 addition & 0 deletions coderd/database/querier.go

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

51 changes: 51 additions & 0 deletions coderd/database/queries.sql.go

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

22 changes: 22 additions & 0 deletions coderd/database/queries/aibridge.sql
Original file line number Diff line number Diff line change
Expand Up @@ -366,3 +366,25 @@ SELECT (
(SELECT COUNT(*) FROM user_prompts) +
(SELECT COUNT(*) FROM interceptions)
)::bigint as total_deleted;

-- name: ListAIBridgeModels :many
SELECT
model
FROM
aibridge_interceptions
WHERE
ended_at IS NOT NULL
-- Filter model
AND CASE
WHEN @model::text != '' THEN aibridge_interceptions.model ILIKE '%' || @model::text || '%'
ELSE true
END
-- We use an `@authorize_filter` as we are attempting to list models that are relevant
-- to the user and what they are allowed to see.
-- Authorize Filter clause will be injected below in ListAIBridgeModelsAuthorized
-- @authorize_filter
GROUP BY
model
LIMIT COALESCE(NULLIF(@limit_::integer, 0), 100)
OFFSET @offset_
;
29 changes: 29 additions & 0 deletions coderd/searchquery/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,35 @@ func AIBridgeInterceptions(ctx context.Context, db database.Store, query string,
return filter, parser.Errors
}

func AIBridgeModels(query string, page codersdk.Pagination) (database.ListAIBridgeModelsParams, []codersdk.ValidationError) {
// nolint:exhaustruct // Empty values just means "don't filter by that field".
filter := database.ListAIBridgeModelsParams{
// #nosec G115 - Safe conversion for pagination offset which is expected to be within int32 range
Offset: int32(page.Offset),
// #nosec G115 - Safe conversion for pagination limit which is expected to be within int32 range
Limit: int32(page.Limit),
}

if query == "" {
return filter, nil
}

values, errors := searchTerms(query, func(term string, values url.Values) error {
// Defaults to the `model` if no `key:value` pair is provided.
values.Add("model", term)
return nil
})
if len(errors) > 0 {
return filter, errors
}

parser := httpapi.NewQueryParamParser()
filter.Model = parser.String(values, "", "model")

parser.ErrorExcessParams(values)
return filter, parser.Errors
}

// Tasks parses a search query for tasks.
//
// Supported query parameters:
Expand Down
33 changes: 33 additions & 0 deletions docs/reference/api/aibridge.md

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

Loading