Skip to content

Commit 04f17a1

Browse files
danmrichardsclaude
andcommitted
fix(model): drop non-string tags from modelSearch response
Numeric IDs appear in the tags array when a server-side join fails. These are internal artefacts — silently skip them rather than failing to unmarshal the response. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
1 parent c5e63a1 commit 04f17a1

1 file changed

Lines changed: 42 additions & 22 deletions

File tree

internal/api/types.go

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,26 @@ type ModelSearchRequest struct {
284284
Offset int `json:"offset,omitempty"`
285285
}
286286

287+
// ModelTags decodes a JSON array of strings. Non-string elements (e.g. internal
288+
// numeric IDs returned when a server-side join fails) are silently dropped.
289+
type ModelTags []string
290+
291+
func (s *ModelTags) UnmarshalJSON(data []byte) error {
292+
var raw []json.RawMessage
293+
if err := json.Unmarshal(data, &raw); err != nil {
294+
return err
295+
}
296+
result := make([]string, 0, len(raw))
297+
for _, r := range raw {
298+
var str string
299+
if err := json.Unmarshal(r, &str); err == nil {
300+
result = append(result, str)
301+
}
302+
}
303+
*s = result
304+
return nil
305+
}
306+
287307
// ModelSearchResponse is the response wrapper from modelSearch.
288308
type ModelSearchResponse struct {
289309
TaskType TaskType `json:"taskType"`
@@ -294,28 +314,28 @@ type ModelSearchResponse struct {
294314

295315
// ModelResult is a single model from the search results.
296316
type ModelResult struct {
297-
Name string `json:"name"`
298-
AIR string `json:"air"`
299-
Tags []string `json:"tags"`
300-
ImageURL string `json:"imageURL"`
301-
ThumbnailURL string `json:"thumbnailURL"`
302-
Category string `json:"category"`
303-
Private bool `json:"private"`
304-
BaseModel string `json:"baseModel,omitempty"`
305-
Version string `json:"version"`
306-
Architecture string `json:"architecture"`
307-
NSFWLevel int `json:"nsfwLevel"`
308-
Type string `json:"type,omitempty"`
309-
DefaultWeight float64 `json:"defaultWeight,omitempty"`
310-
DefaultWidth int `json:"defaultWidth,omitempty"`
311-
DefaultHeight int `json:"defaultHeight,omitempty"`
312-
DefaultSteps int `json:"defaultSteps,omitempty"`
313-
DefaultScheduler string `json:"defaultScheduler,omitempty"`
314-
DefaultCFG float64 `json:"defaultCFG,omitempty"`
315-
DefaultStrength float64 `json:"defaultStrength,omitempty"`
316-
PositiveTriggerWords string `json:"positiveTriggerWords,omitempty"`
317-
NegativeTriggerWords string `json:"negativeTriggerWords,omitempty"`
318-
Primary bool `json:"primary,omitempty"`
317+
Name string `json:"name"`
318+
AIR string `json:"air"`
319+
Tags ModelTags `json:"tags"`
320+
ImageURL string `json:"imageURL"`
321+
ThumbnailURL string `json:"thumbnailURL"`
322+
Category string `json:"category"`
323+
Private bool `json:"private"`
324+
BaseModel string `json:"baseModel,omitempty"`
325+
Version string `json:"version"`
326+
Architecture string `json:"architecture"`
327+
NSFWLevel int `json:"nsfwLevel"`
328+
Type string `json:"type,omitempty"`
329+
DefaultWeight float64 `json:"defaultWeight,omitempty"`
330+
DefaultWidth int `json:"defaultWidth,omitempty"`
331+
DefaultHeight int `json:"defaultHeight,omitempty"`
332+
DefaultSteps int `json:"defaultSteps,omitempty"`
333+
DefaultScheduler string `json:"defaultScheduler,omitempty"`
334+
DefaultCFG float64 `json:"defaultCFG,omitempty"`
335+
DefaultStrength float64 `json:"defaultStrength,omitempty"`
336+
PositiveTriggerWords string `json:"positiveTriggerWords,omitempty"`
337+
NegativeTriggerWords string `json:"negativeTriggerWords,omitempty"`
338+
Primary bool `json:"primary,omitempty"`
319339
}
320340

321341
// ModelUploadRequest contains fields for the modelUpload task type.

0 commit comments

Comments
 (0)