19func acquireBudget(ctx context.Context, slots chan struct{}) (func(), error) { 20 select { 21 case slots <- struct{}{}: 22 return func() { <-slots }, nil 23 case <-ctx.Done(): 24 return nil, ctx.Err() 25 } 26} 27func (t *Thinker) acquireLLMBudget(ctx context.Context) (func(), error) { 28 if t.bus == nil { 29 return func() {}, nil 30 } 31 b := t.bus.limits() 32 slots := b.llm 33 if t.threadID == "main" { 34 slots = b.mainLLM 35 } 36 return acquireBudget(ctx, slots) 37} 38func (t *Thinker) acquireExecutionBudget(ctx context.Context) (func(), error) { 39 if t.bus == nil { 40 return func() {}, nil 41 } 42 b := t.bus.limits() 43 slots := b.tools 44 if t.threadID == "main" { 45 slots = b.mainTools 46 } 47 return acquireBudget(ctx, slots) 48} 49 50func permanentProviderError(err error) bool { 51 if err == nil { 52 return false 53 } 54 var invalid *toolNameError 55 if errors.As(err, &invalid) { 56 return true 57 } 58 msg := strings.ToLower(err.Error()) 59 for _, code := range []string{"api error 400:", "api error 401:", "api error 403:", "api error 404:", "api error 422:", "invalid_api_key", "invalid api key"} { 60 if strings.Contains(msg, code) { 61 return true 62 } 63 } 64 return false 65} 66