mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat(coderd/database): add error columns to aibridge interception records (#26960)
Adds a nullable `aibridge_interception_error_type` enum and an `error_message` column to `aibridge_interceptions`, so a failed interception's terminal upstream error can be persisted. Schema only: the write path and API exposure land in the stacked backend PR. *This PR was produced by opencode (agent) using the `anthropic/claude-opus-4-8` model, under human direction and review.*
This commit is contained in:
Generated
+17
-1
@@ -27,6 +27,16 @@ CREATE TYPE ai_seat_usage_reason AS ENUM (
|
||||
'task'
|
||||
);
|
||||
|
||||
CREATE TYPE aibridge_interception_error_type AS ENUM (
|
||||
'bad_request',
|
||||
'unauthorized',
|
||||
'rate_limited',
|
||||
'overloaded',
|
||||
'server_error',
|
||||
'timeout',
|
||||
'unknown'
|
||||
);
|
||||
|
||||
CREATE TYPE api_key_scope AS ENUM (
|
||||
'coder:all',
|
||||
'coder:application_connect',
|
||||
@@ -1559,7 +1569,9 @@ CREATE TABLE aibridge_interceptions (
|
||||
credential_kind credential_kind DEFAULT 'centralized'::credential_kind NOT NULL,
|
||||
credential_hint character varying(15) DEFAULT ''::character varying NOT NULL,
|
||||
agent_firewall_session_id uuid,
|
||||
agent_firewall_sequence_number integer
|
||||
agent_firewall_sequence_number integer,
|
||||
error_type aibridge_interception_error_type,
|
||||
error_message character varying(1024)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE aibridge_interceptions IS 'Audit log of requests intercepted by AI Bridge';
|
||||
@@ -1584,6 +1596,10 @@ COMMENT ON COLUMN aibridge_interceptions.agent_firewall_session_id IS 'The Agent
|
||||
|
||||
COMMENT ON COLUMN aibridge_interceptions.agent_firewall_sequence_number IS 'The Agent Firewall sequence number from the request header. Used to determine exact ordering of network requests relative to Agent Firewall audit events. NULL when the request did not pass through Agent Firewall.';
|
||||
|
||||
COMMENT ON COLUMN aibridge_interceptions.error_type IS 'Categorised terminal upstream error for a failed interception; NULL when the interception succeeded.';
|
||||
|
||||
COMMENT ON COLUMN aibridge_interceptions.error_message IS 'Raw terminal upstream error message for a failed interception; NULL when the interception succeeded.';
|
||||
|
||||
CREATE TABLE aibridge_model_thoughts (
|
||||
interception_id uuid NOT NULL,
|
||||
content text NOT NULL,
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
ALTER TABLE aibridge_interceptions
|
||||
DROP COLUMN error_type,
|
||||
DROP COLUMN error_message;
|
||||
|
||||
DROP TYPE aibridge_interception_error_type;
|
||||
@@ -0,0 +1,19 @@
|
||||
CREATE TYPE aibridge_interception_error_type AS ENUM (
|
||||
'bad_request',
|
||||
'unauthorized',
|
||||
'rate_limited',
|
||||
'overloaded',
|
||||
'server_error',
|
||||
'timeout',
|
||||
'unknown'
|
||||
);
|
||||
|
||||
-- Records the terminal upstream error observed when an interception failed.
|
||||
-- Both columns are NULL for interceptions that completed successfully.
|
||||
-- error_message is capped at 1024 characters as a hard schema-level bound.
|
||||
ALTER TABLE aibridge_interceptions
|
||||
ADD COLUMN error_type aibridge_interception_error_type,
|
||||
ADD COLUMN error_message varchar(1024);
|
||||
|
||||
COMMENT ON COLUMN aibridge_interceptions.error_type IS 'Categorised terminal upstream error for a failed interception; NULL when the interception succeeded.';
|
||||
COMMENT ON COLUMN aibridge_interceptions.error_message IS 'Raw terminal upstream error message for a failed interception; NULL when the interception succeeded.';
|
||||
@@ -1151,6 +1151,8 @@ func (q *sqlQuerier) ListAuthorizedAIBridgeSessionThreads(ctx context.Context, a
|
||||
&i.AIBridgeInterception.CredentialHint,
|
||||
&i.AIBridgeInterception.AgentFirewallSessionID,
|
||||
&i.AIBridgeInterception.AgentFirewallSequenceNumber,
|
||||
&i.AIBridgeInterception.ErrorType,
|
||||
&i.AIBridgeInterception.ErrorMessage,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Generated
+77
@@ -16,6 +16,79 @@ import (
|
||||
"github.com/sqlc-dev/pqtype"
|
||||
)
|
||||
|
||||
type AIBridgeInterceptionErrorType string
|
||||
|
||||
const (
|
||||
AibridgeInterceptionErrorTypeBadRequest AIBridgeInterceptionErrorType = "bad_request"
|
||||
AibridgeInterceptionErrorTypeUnauthorized AIBridgeInterceptionErrorType = "unauthorized"
|
||||
AibridgeInterceptionErrorTypeRateLimited AIBridgeInterceptionErrorType = "rate_limited"
|
||||
AibridgeInterceptionErrorTypeOverloaded AIBridgeInterceptionErrorType = "overloaded"
|
||||
AibridgeInterceptionErrorTypeServerError AIBridgeInterceptionErrorType = "server_error"
|
||||
AibridgeInterceptionErrorTypeTimeout AIBridgeInterceptionErrorType = "timeout"
|
||||
AibridgeInterceptionErrorTypeUnknown AIBridgeInterceptionErrorType = "unknown"
|
||||
)
|
||||
|
||||
func (e *AIBridgeInterceptionErrorType) Scan(src interface{}) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = AIBridgeInterceptionErrorType(s)
|
||||
case string:
|
||||
*e = AIBridgeInterceptionErrorType(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for AIBridgeInterceptionErrorType: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullAIBridgeInterceptionErrorType struct {
|
||||
AIBridgeInterceptionErrorType AIBridgeInterceptionErrorType `json:"aibridge_interception_error_type"`
|
||||
Valid bool `json:"valid"` // Valid is true if AIBridgeInterceptionErrorType is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullAIBridgeInterceptionErrorType) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
ns.AIBridgeInterceptionErrorType, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.AIBridgeInterceptionErrorType.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullAIBridgeInterceptionErrorType) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.AIBridgeInterceptionErrorType), nil
|
||||
}
|
||||
|
||||
func (e AIBridgeInterceptionErrorType) Valid() bool {
|
||||
switch e {
|
||||
case AibridgeInterceptionErrorTypeBadRequest,
|
||||
AibridgeInterceptionErrorTypeUnauthorized,
|
||||
AibridgeInterceptionErrorTypeRateLimited,
|
||||
AibridgeInterceptionErrorTypeOverloaded,
|
||||
AibridgeInterceptionErrorTypeServerError,
|
||||
AibridgeInterceptionErrorTypeTimeout,
|
||||
AibridgeInterceptionErrorTypeUnknown:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AllAIBridgeInterceptionErrorTypeValues() []AIBridgeInterceptionErrorType {
|
||||
return []AIBridgeInterceptionErrorType{
|
||||
AibridgeInterceptionErrorTypeBadRequest,
|
||||
AibridgeInterceptionErrorTypeUnauthorized,
|
||||
AibridgeInterceptionErrorTypeRateLimited,
|
||||
AibridgeInterceptionErrorTypeOverloaded,
|
||||
AibridgeInterceptionErrorTypeServerError,
|
||||
AibridgeInterceptionErrorTypeTimeout,
|
||||
AibridgeInterceptionErrorTypeUnknown,
|
||||
}
|
||||
}
|
||||
|
||||
type AIProviderType string
|
||||
|
||||
const (
|
||||
@@ -4570,6 +4643,10 @@ type AIBridgeInterception struct {
|
||||
AgentFirewallSessionID uuid.NullUUID `db:"agent_firewall_session_id" json:"agent_firewall_session_id"`
|
||||
// The Agent Firewall sequence number from the request header. Used to determine exact ordering of network requests relative to Agent Firewall audit events. NULL when the request did not pass through Agent Firewall.
|
||||
AgentFirewallSequenceNumber sql.NullInt32 `db:"agent_firewall_sequence_number" json:"agent_firewall_sequence_number"`
|
||||
// Categorised terminal upstream error for a failed interception; NULL when the interception succeeded.
|
||||
ErrorType NullAIBridgeInterceptionErrorType `db:"error_type" json:"error_type"`
|
||||
// Raw terminal upstream error message for a failed interception; NULL when the interception succeeded.
|
||||
ErrorMessage sql.NullString `db:"error_message" json:"error_message"`
|
||||
}
|
||||
|
||||
// Audit log of model thinking in intercepted requests in AI Bridge
|
||||
|
||||
Generated
+15
-5
@@ -1158,7 +1158,7 @@ func (q *sqlQuerier) DeleteOldAIBridgeRecords(ctx context.Context, beforeTime ti
|
||||
|
||||
const getAIBridgeInterceptionByID = `-- name: GetAIBridgeInterceptionByID :one
|
||||
SELECT
|
||||
id, initiator_id, provider, model, started_at, metadata, ended_at, api_key_id, client, thread_parent_id, thread_root_id, client_session_id, session_id, provider_name, credential_kind, credential_hint, agent_firewall_session_id, agent_firewall_sequence_number
|
||||
id, initiator_id, provider, model, started_at, metadata, ended_at, api_key_id, client, thread_parent_id, thread_root_id, client_session_id, session_id, provider_name, credential_kind, credential_hint, agent_firewall_session_id, agent_firewall_sequence_number, error_type, error_message
|
||||
FROM
|
||||
aibridge_interceptions
|
||||
WHERE
|
||||
@@ -1187,6 +1187,8 @@ func (q *sqlQuerier) GetAIBridgeInterceptionByID(ctx context.Context, id uuid.UU
|
||||
&i.CredentialHint,
|
||||
&i.AgentFirewallSessionID,
|
||||
&i.AgentFirewallSequenceNumber,
|
||||
&i.ErrorType,
|
||||
&i.ErrorMessage,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -1221,7 +1223,7 @@ func (q *sqlQuerier) GetAIBridgeInterceptionLineageByToolCallID(ctx context.Cont
|
||||
|
||||
const getAIBridgeInterceptions = `-- name: GetAIBridgeInterceptions :many
|
||||
SELECT
|
||||
id, initiator_id, provider, model, started_at, metadata, ended_at, api_key_id, client, thread_parent_id, thread_root_id, client_session_id, session_id, provider_name, credential_kind, credential_hint, agent_firewall_session_id, agent_firewall_sequence_number
|
||||
id, initiator_id, provider, model, started_at, metadata, ended_at, api_key_id, client, thread_parent_id, thread_root_id, client_session_id, session_id, provider_name, credential_kind, credential_hint, agent_firewall_session_id, agent_firewall_sequence_number, error_type, error_message
|
||||
FROM
|
||||
aibridge_interceptions
|
||||
`
|
||||
@@ -1254,6 +1256,8 @@ func (q *sqlQuerier) GetAIBridgeInterceptions(ctx context.Context) ([]AIBridgeIn
|
||||
&i.CredentialHint,
|
||||
&i.AgentFirewallSessionID,
|
||||
&i.AgentFirewallSequenceNumber,
|
||||
&i.ErrorType,
|
||||
&i.ErrorMessage,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1413,7 +1417,7 @@ INSERT INTO aibridge_interceptions (
|
||||
) VALUES (
|
||||
$1, $2, $3, $4, $5, $6, COALESCE($7::jsonb, '{}'::jsonb), $8, $9, $10, $11::uuid, $12::uuid, $13, $14, $15::uuid, $16
|
||||
)
|
||||
RETURNING id, initiator_id, provider, model, started_at, metadata, ended_at, api_key_id, client, thread_parent_id, thread_root_id, client_session_id, session_id, provider_name, credential_kind, credential_hint, agent_firewall_session_id, agent_firewall_sequence_number
|
||||
RETURNING id, initiator_id, provider, model, started_at, metadata, ended_at, api_key_id, client, thread_parent_id, thread_root_id, client_session_id, session_id, provider_name, credential_kind, credential_hint, agent_firewall_session_id, agent_firewall_sequence_number, error_type, error_message
|
||||
`
|
||||
|
||||
type InsertAIBridgeInterceptionParams struct {
|
||||
@@ -1474,6 +1478,8 @@ func (q *sqlQuerier) InsertAIBridgeInterception(ctx context.Context, arg InsertA
|
||||
&i.CredentialHint,
|
||||
&i.AgentFirewallSessionID,
|
||||
&i.AgentFirewallSequenceNumber,
|
||||
&i.ErrorType,
|
||||
&i.ErrorMessage,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -1904,7 +1910,7 @@ WITH paginated_threads AS (
|
||||
)
|
||||
SELECT
|
||||
COALESCE(aibridge_interceptions.thread_root_id, aibridge_interceptions.id) AS thread_id,
|
||||
aibridge_interceptions.id, aibridge_interceptions.initiator_id, aibridge_interceptions.provider, aibridge_interceptions.model, aibridge_interceptions.started_at, aibridge_interceptions.metadata, aibridge_interceptions.ended_at, aibridge_interceptions.api_key_id, aibridge_interceptions.client, aibridge_interceptions.thread_parent_id, aibridge_interceptions.thread_root_id, aibridge_interceptions.client_session_id, aibridge_interceptions.session_id, aibridge_interceptions.provider_name, aibridge_interceptions.credential_kind, aibridge_interceptions.credential_hint, aibridge_interceptions.agent_firewall_session_id, aibridge_interceptions.agent_firewall_sequence_number
|
||||
aibridge_interceptions.id, aibridge_interceptions.initiator_id, aibridge_interceptions.provider, aibridge_interceptions.model, aibridge_interceptions.started_at, aibridge_interceptions.metadata, aibridge_interceptions.ended_at, aibridge_interceptions.api_key_id, aibridge_interceptions.client, aibridge_interceptions.thread_parent_id, aibridge_interceptions.thread_root_id, aibridge_interceptions.client_session_id, aibridge_interceptions.session_id, aibridge_interceptions.provider_name, aibridge_interceptions.credential_kind, aibridge_interceptions.credential_hint, aibridge_interceptions.agent_firewall_session_id, aibridge_interceptions.agent_firewall_sequence_number, aibridge_interceptions.error_type, aibridge_interceptions.error_message
|
||||
FROM
|
||||
aibridge_interceptions
|
||||
JOIN
|
||||
@@ -1970,6 +1976,8 @@ func (q *sqlQuerier) ListAIBridgeSessionThreads(ctx context.Context, arg ListAIB
|
||||
&i.AIBridgeInterception.CredentialHint,
|
||||
&i.AIBridgeInterception.AgentFirewallSessionID,
|
||||
&i.AIBridgeInterception.AgentFirewallSequenceNumber,
|
||||
&i.AIBridgeInterception.ErrorType,
|
||||
&i.AIBridgeInterception.ErrorMessage,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -2398,7 +2406,7 @@ UPDATE aibridge_interceptions
|
||||
WHERE
|
||||
id = $3::uuid
|
||||
AND ended_at IS NULL
|
||||
RETURNING id, initiator_id, provider, model, started_at, metadata, ended_at, api_key_id, client, thread_parent_id, thread_root_id, client_session_id, session_id, provider_name, credential_kind, credential_hint, agent_firewall_session_id, agent_firewall_sequence_number
|
||||
RETURNING id, initiator_id, provider, model, started_at, metadata, ended_at, api_key_id, client, thread_parent_id, thread_root_id, client_session_id, session_id, provider_name, credential_kind, credential_hint, agent_firewall_session_id, agent_firewall_sequence_number, error_type, error_message
|
||||
`
|
||||
|
||||
type UpdateAIBridgeInterceptionEndedParams struct {
|
||||
@@ -2429,6 +2437,8 @@ func (q *sqlQuerier) UpdateAIBridgeInterceptionEnded(ctx context.Context, arg Up
|
||||
&i.CredentialHint,
|
||||
&i.AgentFirewallSessionID,
|
||||
&i.AgentFirewallSequenceNumber,
|
||||
&i.ErrorType,
|
||||
&i.ErrorMessage,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
@@ -261,6 +261,7 @@ sql:
|
||||
latest_build_has_ai_task: LatestBuildHasAITask
|
||||
cors_behavior: CorsBehavior
|
||||
aibridge_interception: AIBridgeInterception
|
||||
aibridge_interception_error_type: AIBridgeInterceptionErrorType
|
||||
aibridge_tool_usage: AIBridgeToolUsage
|
||||
aibridge_token_usage: AIBridgeTokenUsage
|
||||
aibridge_user_prompt: AIBridgeUserPrompt
|
||||
|
||||
Reference in New Issue
Block a user