| 125 | } |
| 126 | |
| 127 | func (s *Server) RecordInterception(ctx context.Context, in *proto.RecordInterceptionRequest) (*proto.RecordInterceptionResponse, error) { |
| 128 | //nolint:gocritic // AIBridged has specific authz rules. |
| 129 | ctx = dbauthz.AsAIBridged(ctx) |
| 130 | |
| 131 | intcID, err := uuid.Parse(in.GetId()) |
| 132 | if err != nil { |
| 133 | return nil, xerrors.Errorf("invalid interception ID %q: %w", in.GetId(), err) |
| 134 | } |
| 135 | initID, err := uuid.Parse(in.GetInitiatorId()) |
| 136 | if err != nil { |
| 137 | return nil, xerrors.Errorf("invalid initiator ID %q: %w", in.GetInitiatorId(), err) |
| 138 | } |
| 139 | if in.ApiKeyId == "" { |
| 140 | return nil, xerrors.Errorf("empty API key ID") |
| 141 | } |
| 142 | |
| 143 | metadata := metadataToMap(in.GetMetadata()) |
| 144 | |
| 145 | if in.UserAgent != "" { |
| 146 | if _, ok := metadata[MetadataUserAgentKey]; ok { |
| 147 | s.logger.Warn(ctx, "interception metadata contains user agent key, will be overwritten") |
| 148 | } |
| 149 | metadata[MetadataUserAgentKey] = in.UserAgent |
| 150 | } |
| 151 | |
| 152 | // Look up the interception lineage using the correlating tool call ID. |
| 153 | parentID, rootID := s.findInterceptionLineage(ctx, in.GetCorrelatingToolCallId()) |
| 154 | |
| 155 | if s.structuredLogging { |
| 156 | s.logger.Info(ctx, InterceptionLogMarker, |
| 157 | slog.F("record_type", "interception_start"), |
| 158 | slog.F("interception_id", intcID.String()), |
| 159 | slog.F("initiator_id", initID.String()), |
| 160 | slog.F("api_key_id", in.ApiKeyId), |
| 161 | slog.F("provider", in.Provider), |
| 162 | slog.F("model", in.Model), |
| 163 | slog.F("client", in.Client), |
| 164 | slog.F("client_session_id", in.GetClientSessionId()), |
| 165 | slog.F("started_at", in.StartedAt.AsTime()), |
| 166 | slog.F("metadata", metadata), |
| 167 | slog.F("correlating_tool_call_id", in.GetCorrelatingToolCallId()), |
| 168 | slog.F("thread_parent_id", parentID), |
| 169 | slog.F("thread_root_id", rootID), |
| 170 | ) |
| 171 | } |
| 172 | |
| 173 | out, err := json.Marshal(metadata) |
| 174 | if err != nil { |
| 175 | s.logger.Warn(ctx, "failed to marshal aibridge metadata from proto to JSON", slog.F("metadata", in), slog.Error(err)) |
| 176 | } |
| 177 | |
| 178 | providerName := strings.TrimSpace(in.ProviderName) |
| 179 | if providerName == "" { |
| 180 | providerName = in.Provider |
| 181 | } |
| 182 | |
| 183 | _, err = s.store.InsertAIBridgeInterception(ctx, database.InsertAIBridgeInterceptionParams{ |
| 184 | ID: intcID, |