MCPcopy Index your code
hub / github.com/coder/coder / ProcessRequest

Method ProcessRequest

aibridge/intercept/messages/streaming.go:94–603  ·  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

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

Callers

nothing calls this directly

Calls 15

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

Tested by

no test coverage detected