返回 DeepSeek-Reasonix
service.go
根目录 / internal / imageinput / service.go
1 package imageinput
2
3 import (
4 "context"
5 "strings"
6 "sync"
7
8 "reasonix/internal/event"
9 "reasonix/internal/provider"
10 )
11
12 // Config is frozen by boot and shared by independent per-session services.
13 type Config struct {
14 Model string
15 Resolve func(string) (provider.Provider, error)
16 Select func(string, string) (string, bool)
17 }
18 type Service struct {
19 config Config
20 once sync.Once
21 queue chan struct{}
22 cached map[string]*provider.VisionSummary
23 }
24
25 func New(config Config) *Service {
26 config.Model = strings.TrimSpace(config.Model)
27 return &Service{config: config}
28 }
29
30 // Understand serializes a session's image prepasses without holding session locks.
31 func (s *Service) Understand(ctx context.Context, current string, images []string, history func() []provider.Message, sink event.Sink) (*provider.VisionSummary, error) {
32 target, err := s.selectModel(current, images)
33 if err != nil {
34 return nil, err
35 }
36 return s.UnderstandSelected(ctx, target, images, history, sink)
37 }
38
39 // SelectModel fixes the destination before request images are encoded/uploaded.
40 func (s *Service) SelectModel(current string, images []string) (string, error) {
41 return s.selectModel(current, images)
42 }
43
44 func (s *Service) UnderstandSelected(ctx context.Context, target string, images []string, history func() []provider.Message, sink event.Sink) (*provider.VisionSummary, error) {
45 if sink == nil {
46 sink = event.Discard
47 }
48 s.once.Do(func() { s.queue = make(chan struct{}, 1) })
49 select {
50 case <-ctx.Done():
51 return nil, ctx.Err()
52 case s.queue <- struct{}{}:
53 }
54 defer func() { <-s.queue }()
55 if err := ctx.Err(); err != nil {
56 return nil, err
57 }
58 digests, key, cacheable, cached := s.lookup(target, images, history)
59 if cached != nil {
60 return cached, nil
61 }
62 sink.Emit(event.Event{Kind: event.Notice, Level: event.LevelInfo, Text: "正在分析图片…"})
63 summary, err := s.summarizeImages(ctx, target, images, digests, sink)
64 if err != nil {
65 return nil, err
66 }
67 if err := ctx.Err(); err != nil {
68 return nil, err
69 }
70 if cacheable {
71 if s.cached == nil || len(s.cached) >= 32 {
72 s.cached = make(map[string]*provider.VisionSummary)
73 }
74 s.cached[key] = clone(summary)
75 }
76 return summary, nil
77 }
78
78 lines GO