MCPcopy Create free account
hub / github.com/coder/aibridge / ProcessRequest

Method ProcessRequest

intercept/messages/streaming.go:93–535  ·  view source on GitHub ↗

ProcessRequest handles a request to /v1/messages. This API has a state-machine behind it, which is described in https://docs.claude.com/en/docs/build-with-claude/streaming#event-types. Each stream uses the following event flow: - `message_start`: contains a Message object with empty content. - A se

(w http.ResponseWriter, r *http.Request)

Source from the content-addressed store, hash-verified

91// results relayed to the SERVER. The response from the server will be handled synchronously, and this loop
92// can continue until all injected tool invocations are completed and the response is relayed to the client.
93func (i *StreamingInterception) ProcessRequest(w http.ResponseWriter, r *http.Request) (outErr error) {
94 if len(i.reqPayload) == 0 {
95 return xerrors.New("developer error: request payload is empty")
96 }
97
98 ctx, span := i.tracer.Start(r.Context(), "Intercept.ProcessRequest", trace.WithAttributes(tracing.InterceptionAttributesFromContext(r.Context())...))
99 defer tracing.EndSpanErr(span, &outErr)
100
101 // Allow us to interrupt watch via cancel.
102 ctx, cancel := context.WithCancel(ctx)
103 defer cancel()
104 r = r.WithContext(ctx) // Rewire context for SSE cancellation.
105
106 logger := i.logger.With(slog.F("model", i.Model()))
107
108 var (
109 prompt string
110 promptFound bool
111 err error
112 )
113
114 prompt, promptFound, err = i.reqPayload.lastUserPrompt()
115 if err != nil {
116 logger.Warn(ctx, "failed to determine last user prompt", slog.Error(err))
117 }
118
119 // Claude Code uses a "small/fast model" for certain tasks.
120 if !i.isSmallFastModel() {
121 // Only inject tools into "actual" request.
122 i.injectTools()
123 }
124
125 streamCtx, streamCancel := context.WithCancelCause(ctx)
126 defer streamCancel(xerrors.New("deferred"))
127
128 // TODO(ssncferreira): inject actor headers directly in the client-header
129 // middleware instead of using SDK options.
130 var opts []option.RequestOption
131 if actor := aibcontext.ActorFromContext(ctx); actor != nil && i.cfg.SendActorHeaders {
132 opts = append(opts, intercept.ActorHeadersAsAnthropicOpts(actor)...)
133 }
134
135 svc, err := i.newMessagesService(streamCtx, opts...)
136 if err != nil {
137 err = xerrors.Errorf("create anthropic client: %w", err)
138 http.Error(w, err.Error(), http.StatusInternalServerError)
139 return err
140 }
141
142 // events will either terminate when shutdown after interaction with upstream completes, or when streamCtx is done.
143 events := eventstream.NewEventStream(streamCtx, logger.Named("sse-sender"), i.pingPayload())
144 go events.Start(w, r)
145 defer func() {
146 _ = events.Shutdown(streamCtx) // Catch-all in case it doesn't get shutdown after stream completes.
147 }()
148
149 // Accumulate usage across the entire streaming interaction (including tool reinvocations).
150 var cumulativeUsage anthropic.Usage

Callers

nothing calls this directly

Calls 15

StartMethod · 0.95
pingPayloadMethod · 0.95
ShutdownMethod · 0.95
newStreamMethod · 0.95
marshalEventMethod · 0.95
SendMethod · 0.95
IsStreamingMethod · 0.95
marshalMethod · 0.95
EndSpanErrFunction · 0.92
NewEventStreamFunction · 0.92

Tested by

no test coverage detected