fix: live-API corrections — music result shapes, mesh format honesty, mesh conversion
Gadfly review (reusable) / review (pull_request) Successful in 15s
Adversarial Review (Gadfly) / review (pull_request) Successful in 15s
CI / Tidy (pull_request) Successful in 9m26s
CI / Build & Test (pull_request) Successful in 10m0s

Round 2 from live smokes on netherstorm (2026-07-14):

- ACE-Step result blob is an ARRAY of objects and carries RAW control
  characters inside string values (literal newlines) — strict JSON
  rejected it. parseMusicResult sanitizes control chars (only legal
  inside string values in the double-encoded blob) and accepts array or
  object shapes. Regression test uses the live payload shape.
- Hunyuan3D GenerationRequest has NO output-format field (the documented
  type param is fiction) — it always returns GLB. Results are now
  labelled by sniffed magic bytes, never by the requested format.
- NEW meshgen.Converter/ConverterProvider optional surface + llamaswap
  impl over the mediautils shim POST /v1/convert_mesh — the STL hop for
  the printer pipeline.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
2026-07-14 12:22:06 -04:00
co-authored by Claude Fable 5
parent 89a0b2bdd3
commit fd8d56c3c1
5 changed files with 212 additions and 6 deletions
+23 -2
View File
@@ -42,7 +42,11 @@ type meshModel struct {
// hunyuanGenerateRequest is the Hunyuan3D api_server /generate shape.
// Optional fields are pointers/omitempty so unset values fall back to the
// server's defaults (mirrors the sd-server wire structs).
// server's defaults (mirrors the sd-server wire structs). Type is sent for
// forward-compat but the LIVE server's GenerationRequest has no such field
// and always returns GLB (verified 2026-07-14) — the response format is
// therefore SNIFFED, and non-GLB output is the caller's conversion problem
// (meshgen.Converter / the mediautils shim).
type hunyuanGenerateRequest struct {
Image string `json:"image"`
Type string `json:"type,omitempty"`
@@ -123,7 +127,24 @@ func (m *meshModel) Generate(ctx context.Context, req meshgen.Request, opts ...m
return nil, &llm.APIError{Provider: m.p.name, Model: m.id,
Message: "mesh response is JSON, not mesh bytes: " + truncateForError(raw)}
}
return &meshgen.Result{Mesh: meshgen.Mesh{Data: raw, Format: format, MIME: mimeType}}, nil
// Label the result by what the bytes ARE, not what was requested: the
// live server ignores the format field entirely.
actualFormat, actualMIME := sniffMeshFormat(raw, format, mimeType)
return &meshgen.Result{Mesh: meshgen.Mesh{Data: raw, Format: actualFormat, MIME: actualMIME}}, nil
}
// sniffMeshFormat identifies the mesh container from magic bytes, falling
// back to the requested format only when the bytes are ambiguous (binary
// STL has no magic).
func sniffMeshFormat(raw []byte, requested, requestedMIME string) (string, string) {
switch {
case len(raw) >= 4 && string(raw[:4]) == "glTF":
return "glb", meshFormats["glb"]
case len(raw) >= 6 && strings.EqualFold(string(raw[:6]), "solid "):
return "stl", meshFormats["stl"]
default:
return requested, requestedMIME
}
}
// truncateForError bounds a payload quoted into an error message.