@@ -4,8 +4,11 @@ import (
44 "context"
55 "encoding/json"
66 "fmt"
7+ "html"
78 "io"
89 "net/http"
10+ "net/url"
11+ "regexp"
912 "strconv"
1013 "strings"
1114 "time"
@@ -62,7 +65,11 @@ type ConfigurableProvider interface {
6265
6366// --- OpenRouter /models fetcher ---
6467
65- var openRouterHTTPClient = & http.Client {Timeout : 20 * time .Second }
68+ var (
69+ openRouterHTTPClient = & http.Client {Timeout : 20 * time .Second }
70+ openRouterPageBaseURL = "https://openrouter.ai"
71+ openRouterDescriptionPayload = regexp .MustCompile (`\\"description\\":\\"((?:\\\\.|[^\\"])*)\\"` )
72+ )
6673
6774// FetchOpenRouterModels pulls the full catalog from OpenRouter and returns a
6875// map keyed by model id. Prices are normalised to USD per 1M tokens (OpenRouter
@@ -133,6 +140,85 @@ func parseOpenRouterModels(body []byte) (map[string]Capabilities, error) {
133140 return out , nil
134141}
135142
143+ // OpenRouterDescriptionLooksTruncated reports descriptions that already arrive
144+ // from OpenRouter with an ellipsis. Those are upstream-shortened strings, not a
145+ // UI clamp, so callers may optionally enrich them from the model page payload.
146+ func OpenRouterDescriptionLooksTruncated (desc string ) bool {
147+ desc = strings .TrimSpace (desc )
148+ return strings .HasSuffix (desc , "..." ) || strings .HasSuffix (desc , "…" )
149+ }
150+
151+ // FetchOpenRouterModelDescription fetches a model page and extracts the longest
152+ // matching description from its SSR payload. OpenRouter's public /models API can
153+ // return shortened descriptions ending in "...", while the model page payload
154+ // usually carries the full text.
155+ func FetchOpenRouterModelDescription (ctx context.Context , modelID , currentDescription string ) (string , error ) {
156+ req , err := http .NewRequestWithContext (ctx , http .MethodGet , openRouterModelPageURL (modelID ), nil )
157+ if err != nil {
158+ return "" , err
159+ }
160+ resp , err := openRouterHTTPClient .Do (req )
161+ if err != nil {
162+ return "" , fmt .Errorf ("openrouter model page: %w" , err )
163+ }
164+ defer resp .Body .Close ()
165+
166+ body , err := io .ReadAll (io .LimitReader (resp .Body , 4 * 1024 * 1024 ))
167+ if err != nil {
168+ return "" , fmt .Errorf ("openrouter model page: read: %w" , err )
169+ }
170+ if resp .StatusCode != http .StatusOK {
171+ return "" , fmt .Errorf ("openrouter model page: HTTP %d: %s" , resp .StatusCode , string (body ))
172+ }
173+ full := extractOpenRouterPageDescription (body , currentDescription )
174+ if full == "" {
175+ return "" , fmt .Errorf ("openrouter model page: full description not found" )
176+ }
177+ return full , nil
178+ }
179+
180+ func openRouterModelPageURL (modelID string ) string {
181+ parts := strings .Split (modelID , "/" )
182+ for i := range parts {
183+ parts [i ] = url .PathEscape (parts [i ])
184+ }
185+ return strings .TrimRight (openRouterPageBaseURL , "/" ) + "/" + strings .Join (parts , "/" )
186+ }
187+
188+ func extractOpenRouterPageDescription (body []byte , currentDescription string ) string {
189+ current := normalizeDescriptionText (currentDescription )
190+ prefix := strings .TrimSpace (strings .TrimSuffix (strings .TrimSuffix (current , "..." ), "…" ))
191+ if len (prefix ) > 120 {
192+ prefix = prefix [:120 ]
193+ }
194+ var best string
195+ for _ , m := range openRouterDescriptionPayload .FindAllSubmatch (body , - 1 ) {
196+ if len (m ) != 2 {
197+ continue
198+ }
199+ raw := string (m [1 ])
200+ decoded , err := strconv .Unquote (`"` + raw + `"` )
201+ if err != nil {
202+ continue
203+ }
204+ decoded = normalizeDescriptionText (html .UnescapeString (decoded ))
205+ if decoded == "" || len (decoded ) <= len (current ) || OpenRouterDescriptionLooksTruncated (decoded ) {
206+ continue
207+ }
208+ if prefix != "" && ! strings .HasPrefix (decoded , prefix ) {
209+ continue
210+ }
211+ if len (decoded ) > len (best ) {
212+ best = decoded
213+ }
214+ }
215+ return best
216+ }
217+
218+ func normalizeDescriptionText (s string ) string {
219+ return strings .Join (strings .Fields (strings .TrimSpace (s )), " " )
220+ }
221+
136222func containsStr (s []string , target string ) bool {
137223 for _ , v := range s {
138224 if strings .EqualFold (v , target ) {
0 commit comments