| 1 | package provider |
| 2 | |
| 3 | import ( |
| 4 | "fmt" |
| 5 | "strings" |
| 6 | ) |
| 7 | |
| 8 | // Descriptor is the non-sensitive provider/model metadata shared across |
| 9 | // process boundaries. It intentionally contains no endpoint, credential, |
| 10 | // header, proxy, or environment-variable information. |
| 11 | type Descriptor struct { |
| 12 | Ref string `json:"ref"` |
| 13 | DisplayName string `json:"displayName,omitempty"` |
| 14 | Model string `json:"model,omitempty"` |
| 15 | ContextWindow int `json:"contextWindow,omitempty"` |
| 16 | PricingCurrency string `json:"pricingCurrency,omitempty"` |
| 17 | CacheHitPerMillion float64 `json:"cacheHitPerMillion,omitempty"` |
| 18 | InputPerMillion float64 `json:"inputPerMillion,omitempty"` |
| 19 | OutputPerMillion float64 `json:"outputPerMillion,omitempty"` |
| 20 | Vision bool `json:"vision,omitempty"` |
| 21 | Tools bool `json:"tools,omitempty"` |
| 22 | Reasoning bool `json:"reasoning,omitempty"` |
| 23 | Efforts []string `json:"efforts,omitempty"` |
| 24 | DefaultEffort string `json:"defaultEffort,omitempty"` |
| 25 | ToolCallReasoning bool `json:"toolCallReasoning,omitempty"` |
| 26 | ReasoningRoundTrip bool `json:"reasoningRoundTrip,omitempty"` |
| 27 | WarnOnMissingToolCallReasoning bool `json:"warnOnMissingToolCallReasoning,omitempty"` |
| 28 | } |
| 29 | |
| 30 | // Selection identifies a catalog provider and an optional session-local |
| 31 | // effort override. |
| 32 | type Selection struct { |
| 33 | Ref string `json:"ref"` |
| 34 | Effort *string `json:"effort,omitempty"` |
| 35 | } |
| 36 | |
| 37 | // Resolver creates providers without exposing credential material to callers. |
| 38 | // Remote runtimes use a Broker-backed resolver; ordinary boots keep using the |
| 39 | // local config-backed resolver. |
| 40 | type Resolver interface { |
| 41 | Catalog() []Descriptor |
| 42 | Resolve(Selection) (Provider, error) |
| 43 | } |
| 44 | |
| 45 | // StaticResolver is a small deterministic test double. |
| 46 | type StaticResolver struct { |
| 47 | Descriptors []Descriptor |
| 48 | Providers map[string]Provider |
| 49 | } |
| 50 | |
| 51 | func (r *StaticResolver) Catalog() []Descriptor { |
| 52 | if r == nil { |
| 53 | return nil |
| 54 | } |
| 55 | out := make([]Descriptor, len(r.Descriptors)) |
| 56 | copy(out, r.Descriptors) |
| 57 | return out |
| 58 | } |
| 59 | |
| 60 | func (r *StaticResolver) Resolve(selection Selection) (Provider, error) { |
| 61 | if r == nil { |
| 62 | return nil, fmt.Errorf("provider resolver is nil") |
| 63 | } |
| 64 | ref := strings.TrimSpace(selection.Ref) |
| 65 | if ref == "" { |
| 66 | return nil, fmt.Errorf("provider selection ref is required") |
| 67 | } |
| 68 | if p, ok := r.Providers[ref]; ok { |
| 69 | return p, nil |
| 70 | } |
| 71 | for key, p := range r.Providers { |
| 72 | if strings.HasPrefix(key, ref+"/") || strings.HasSuffix(key, "/"+ref) { |
| 73 | return p, nil |
| 74 | } |
| 75 | } |
| 76 | return nil, fmt.Errorf("unknown provider ref %q", ref) |
| 77 | } |
| 78 |