feat: session list API (#23202)

<!--

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.

-->

_Disclaimer:_ _initially_ _produced_ _by_ _Claude_ _Opus_ _4\.6,_ _heavily_ _modified_ _and_ _reviewed_ _by_ _me._

Closes https://github.com/coder/internal/issues/1360

Adds a new `/api/v2/aibridge/sessions` API which returns "sessions".

Sessions, as defined in the [RFC](https://www.notion.so/coderhq/AI-Bridge-Sessions-Threads-2ccd579be59280f28021d3baf7472fbe?source=copy_link), are a set of interceptions logically grouped by a session key issued by the client.  
The API design for this endpoint was done in [this doc](https://github.com/coder/internal/issues/1360).

If the client has not provided a session ID, we will revert to the thread root ID, and if that's not present we use the interception's own ID (i.e. a session of a single interception - which is effectively what we show currently in our `/api/v2/aibridge/interceptions` API).

The SQL query looks gnarly but it's relatively simple, and seems to perform well (~200ms) even when I import dogfood's `aibridge_*` tables into my workspace. If we need to improve performance on this later we can investigate materialized views, perhaps, but for now I don't think it's warranted.

---

_The PR looks large but it's got a lot of generated code; the actual changes aren't huge._
This commit is contained in:
Danny Kopping
2026-03-24 08:58:47 +02:00
committed by GitHub
parent 3d5d58ec2b
commit 43a1af3cd6
23 changed files with 2118 additions and 8 deletions
+2 -2
View File
@@ -16,10 +16,10 @@ AI Bridge interception data can be exported for external analysis, compliance re
### REST API
You can retrieve AI Bridge interceptions via the Coder API with filtering and pagination support.
You can retrieve AI Bridge sessions via the Coder API, with filtering and pagination support.
```sh
curl -X GET "https://coder.example.com/api/v2/aibridge/interceptions?q=initiator:me" \
curl -X GET "https://coder.example.com/api/v2/aibridge/sessions" \
-H "Coder-Session-Token: $CODER_SESSION_TOKEN"
```
+70
View File
@@ -137,3 +137,73 @@ curl -X GET http://coder-server:8080/api/v2/aibridge/models \
<h3 id="list-ai-bridge-models-responseschema">Response Schema</h3>
To perform this operation, you must be authenticated. [Learn more](authentication.md).
## List AI Bridge sessions
### Code samples
```shell
# Example request using curl
curl -X GET http://coder-server:8080/api/v2/aibridge/sessions \
-H 'Accept: application/json' \
-H 'Coder-Session-Token: API_KEY'
```
`GET /aibridge/sessions`
### Parameters
| Name | In | Type | Required | Description |
|--------------------|-------|---------|----------|--------------------------------------------------------------------------------------------------------------------------------------------|
| `q` | query | string | false | Search query in the format `key:value`. Available keys are: initiator, provider, model, client, session_id, started_after, started_before. |
| `limit` | query | integer | false | Page limit |
| `after_session_id` | query | string | false | Cursor pagination after session ID (cannot be used with offset) |
| `offset` | query | integer | false | Offset pagination (cannot be used with after_session_id) |
### Example responses
> 200 Response
```json
{
"count": 0,
"sessions": [
{
"client": "string",
"ended_at": "2019-08-24T14:15:22Z",
"id": "string",
"initiator": {
"avatar_url": "http://example.com",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"username": "string"
},
"last_prompt": "string",
"metadata": {
"property1": null,
"property2": null
},
"models": [
"string"
],
"providers": [
"string"
],
"started_at": "2019-08-24T14:15:22Z",
"threads": 0,
"token_usage_summary": {
"input_tokens": 0,
"output_tokens": 0
}
}
]
}
```
### Responses
| Status | Meaning | Description | Schema |
|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------------------------------|
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.AIBridgeListSessionsResponse](schemas.md#codersdkaibridgelistsessionsresponse) |
To perform this operation, you must be authenticated. [Learn more](authentication.md).
+111
View File
@@ -598,6 +598,51 @@
| `count` | integer | false | | |
| `results` | array of [codersdk.AIBridgeInterception](#codersdkaibridgeinterception) | false | | |
## codersdk.AIBridgeListSessionsResponse
```json
{
"count": 0,
"sessions": [
{
"client": "string",
"ended_at": "2019-08-24T14:15:22Z",
"id": "string",
"initiator": {
"avatar_url": "http://example.com",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"username": "string"
},
"last_prompt": "string",
"metadata": {
"property1": null,
"property2": null
},
"models": [
"string"
],
"providers": [
"string"
],
"started_at": "2019-08-24T14:15:22Z",
"threads": 0,
"token_usage_summary": {
"input_tokens": 0,
"output_tokens": 0
}
}
]
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
|------------|---------------------------------------------------------------|----------|--------------|-------------|
| `count` | integer | false | | |
| `sessions` | array of [codersdk.AIBridgeSession](#codersdkaibridgesession) | false | | |
## codersdk.AIBridgeOpenAIConfig
```json
@@ -650,6 +695,72 @@
| `upstream_proxy` | string | false | | |
| `upstream_proxy_ca` | string | false | | |
## codersdk.AIBridgeSession
```json
{
"client": "string",
"ended_at": "2019-08-24T14:15:22Z",
"id": "string",
"initiator": {
"avatar_url": "http://example.com",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"username": "string"
},
"last_prompt": "string",
"metadata": {
"property1": null,
"property2": null
},
"models": [
"string"
],
"providers": [
"string"
],
"started_at": "2019-08-24T14:15:22Z",
"threads": 0,
"token_usage_summary": {
"input_tokens": 0,
"output_tokens": 0
}
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
|-----------------------|----------------------------------------------------------------------------------------|----------|--------------|-------------|
| `client` | string | false | | |
| `ended_at` | string | false | | |
| `id` | string | false | | |
| `initiator` | [codersdk.MinimalUser](#codersdkminimaluser) | false | | |
| `last_prompt` | string | false | | |
| `metadata` | object | false | | |
| » `[any property]` | any | false | | |
| `models` | array of string | false | | |
| `providers` | array of string | false | | |
| `started_at` | string | false | | |
| `threads` | integer | false | | |
| `token_usage_summary` | [codersdk.AIBridgeSessionTokenUsageSummary](#codersdkaibridgesessiontokenusagesummary) | false | | |
## codersdk.AIBridgeSessionTokenUsageSummary
```json
{
"input_tokens": 0,
"output_tokens": 0
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
|-----------------|---------|----------|--------------|-------------|
| `input_tokens` | integer | false | | |
| `output_tokens` | integer | false | | |
## codersdk.AIBridgeTokenUsage
```json