feat(auto): AUTO-only priority chain with tiered slots + live source probing; CSV export w/ key names; audit persistence; fix prompt token accounting & deepseek thinking

This commit is contained in:
root
2026-08-10 00:10:19 +08:00
parent 859d310ad3
commit d48b993010
10 changed files with 663 additions and 210 deletions

View File

@ -57,9 +57,14 @@ type Provider struct {
adapter string
client *http.Client
mu sync.Mutex
sem chan struct{}
health health
mu sync.Mutex
sem chan struct{}
health health
lastProbe struct {
ok bool
err string
at int64
}
}
func New(cfg config.Source, vm *lua.VM) *Provider {
@ -175,6 +180,48 @@ func (p *Provider) Available() bool {
return p.health.available()
}
// Probe performs a lightweight reachability + auth check against the source
// using its best chat model (1-token). It records the result for Status().
func (p *Provider) Probe(ctx context.Context) (bool, string) {
ok := false
msg := ""
model := p.bestChatModel()
if pm := p.ModelByID(model); pm != nil && pm.Kind == "image" {
model = ""
}
if model == "" {
if ms := p.Models(); len(ms) > 0 {
model = ms[0]
}
}
if model != "" {
_, err := p.Chat(ctx, &types.ChatRequest{
Model: model,
Messages: []types.ChatMessage{{Role: "user", Content: types.StringContent("hi")}},
MaxTokens: 1,
})
if err == nil {
ok = true
} else {
msg = err.Error()
}
} else {
msg = "no chat model configured"
}
p.mu.Lock()
p.lastProbe.ok = ok
p.lastProbe.err = msg
p.lastProbe.at = time.Now().Unix()
p.mu.Unlock()
return ok, msg
}
func (p *Provider) LastProbe() (bool, string, int64) {
p.mu.Lock()
defer p.mu.Unlock()
return p.lastProbe.ok, p.lastProbe.err, p.lastProbe.at
}
// ReportStatus records an upstream HTTP status for backoff decisions.
func (p *Provider) ReportStatus(code int) {
p.mu.Lock()
@ -331,21 +378,23 @@ func (p *Provider) ChatStream(ctx context.Context, req *types.ChatRequest) (<-ch
}()
ch := make(chan types.UnifiedChunk, 64)
sel := <-rc
if sel.err != nil {
p.reportError()
p.Release()
return nil, sel.err
}
if sel.resp.StatusCode != 200 {
raw, _ := io.ReadAll(sel.resp.Body)
sel.resp.Body.Close()
p.ReportStatus(sel.resp.StatusCode)
p.Release()
return nil, fmt.Errorf("api error %d: %s", sel.resp.StatusCode, truncate(string(raw), 500))
}
go func() {
defer p.Release()
defer close(ch)
sel := <-rc
if sel.err != nil {
p.reportError()
return
}
defer sel.resp.Body.Close()
if sel.resp.StatusCode != 200 {
raw, _ := io.ReadAll(sel.resp.Body)
p.ReportStatus(sel.resp.StatusCode)
_ = raw
return
}
scanner := bufio.NewScanner(sel.resp.Body)
scanner.Buffer(make([]byte, 0, 64*1024), 1024*1024)
for scanner.Scan() {