返回 DeepSeek-Reasonix
request_gate.go
根目录 / internal / provider / request_gate.go
1 package provider
2
3 import (
4 "context"
5 "errors"
6 "strings"
7 )
8
9 // RequestGate belongs to one runtime's frozen connection snapshot. Keeping it
10 // in the request context preserves the concrete Provider and optional interfaces.
11 type RequestGate interface {
12 BeforeModelRequest(string) error
13 ModelRequestFailed(string, error)
14 }
15
16 type requestGateKey struct{}
17
18 func WithRequestGate(ctx context.Context, gate RequestGate) context.Context {
19 return context.WithValue(ctx, requestGateKey{}, gate)
20 }
21
22 func Stream(ctx context.Context, p Provider, req Request) (<-chan Chunk, error) {
23 return StreamForModel(ctx, p, req, "")
24 }
25
26 func requestModelRef(p Provider) string {
27 ref := p.Name() + "/"
28 if metadata, ok := p.(ModelInfoProvider); ok {
29 if model := metadata.ModelInfo().ID; model != "" {
30 ref += model
31 }
32 }
33 return ref
34 }
35
36 // StreamForModel admits and observes each actual request, including stream
37 // errors, before a caller can retry. It never changes the wire request.
38 func StreamForModel(ctx context.Context, p Provider, req Request, ref string) (<-chan Chunk, error) {
39 gate, _ := ctx.Value(requestGateKey{}).(RequestGate)
40 if gate == nil {
41 return p.Stream(ctx, req)
42 }
43 if strings.TrimSpace(ref) == "" {
44 ref = requestModelRef(p)
45 }
46 if err := gate.BeforeModelRequest(ref); err != nil {
47 return nil, err
48 }
49 record := func(err error) error {
50 var auth *AuthError
51 if errors.As(err, &auth) && auth != nil {
52 copy := *auth
53 if copy.ModelRef == "" {
54 copy.ModelRef = strings.TrimSpace(ref)
55 }
56 gate.ModelRequestFailed(copy.ModelRef, &copy)
57 return &copy
58 }
59 return err
60 }
61 ch, err := p.Stream(ctx, req)
62 if err != nil {
63 return nil, record(err)
64 }
65 out := make(chan Chunk)
66 go func() {
67 defer close(out)
68 for {
69 select {
70 case <-ctx.Done():
71 return
72 case chunk, ok := <-ch:
73 if !ok {
74 return
75 }
76 chunk.Err = record(chunk.Err)
77 select {
78 case out <- chunk:
79 case <-ctx.Done():
80 return
81 }
82 }
83 }
84 }()
85 return out, nil
86 }
87
87 lines GO