fix: validate LM Studio model identity in review workflow (#2746)

* fix: validate LM Studio model identity in review workflow

Capture the full API response before extracting content, then compare
the top-level `.model` field against the configured LM_STUDIO_MODEL.
Emits a warning to stderr if LM Studio served a different model than
requested, while still proceeding with the review response.

Closes #2721

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(review): skip LM Studio review file when content is empty instead of writing error text

Also applies the same fix to llama.cpp which had the identical pattern of writing
a literal error string into the review temp file when content was empty/null.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-04-26 13:33:28 -04:00
committed by GitHub
parent b8bbc74192
commit 7255539ff9

View File

@@ -270,15 +270,20 @@ LM_STUDIO_MODEL=$(gsd-sdk query config-get review.models.lm_studio 2>/dev/null |
if [ -z "$LM_STUDIO_MODEL" ] || [ "$LM_STUDIO_MODEL" = "null" ]; then
LM_STUDIO_MODEL=$(curl -s --max-time 2 "${LM_STUDIO_HOST}/v1/models" 2>/dev/null | jq -r '.data[0].id // "local-model"' 2>/dev/null || echo "local-model")
fi
jq -n --rawfile content /tmp/gsd-review-prompt-{phase}.md \
LM_STUDIO_RESPONSE=$(jq -n --rawfile content /tmp/gsd-review-prompt-{phase}.md \
--arg model "$LM_STUDIO_MODEL" \
'{model: $model, messages: [{role: "user", content: $content}]}' | \
curl -s --max-time 120 -X POST "${LM_STUDIO_HOST}/v1/chat/completions" \
-H "Content-Type: application/json" -d @- 2>/dev/null | \
jq -r '.choices[0].message.content // "LM Studio review failed or returned empty output."' \
> /tmp/gsd-review-lm_studio-{phase}.md
if [ ! -s /tmp/gsd-review-lm_studio-{phase}.md ]; then
echo "LM Studio review failed or returned empty output." > /tmp/gsd-review-lm_studio-{phase}.md
-H "Content-Type: application/json" -d @- 2>/dev/null)
LM_STUDIO_ACTUAL_MODEL=$(echo "$LM_STUDIO_RESPONSE" | jq -r '.model // ""' 2>/dev/null || echo "")
if [ -n "$LM_STUDIO_ACTUAL_MODEL" ] && [ "$LM_STUDIO_ACTUAL_MODEL" != "null" ] && [ "$LM_STUDIO_ACTUAL_MODEL" != "$LM_STUDIO_MODEL" ]; then
echo "Warning: LM Studio served model '$LM_STUDIO_ACTUAL_MODEL' but '$LM_STUDIO_MODEL' was requested. Review may be from a different model." >&2
fi
LM_STUDIO_CONTENT=$(echo "$LM_STUDIO_RESPONSE" | jq -r '.choices[0].message.content // ""' 2>/dev/null || echo "")
if [ -n "$LM_STUDIO_CONTENT" ]; then
echo "$LM_STUDIO_CONTENT" > /tmp/gsd-review-lm_studio-{phase}.md
else
echo "Warning: LM Studio returned empty content — skipping review." >&2
fi
```
@@ -290,15 +295,16 @@ LLAMA_CPP_MODEL=$(gsd-sdk query config-get review.models.llama_cpp 2>/dev/null |
if [ -z "$LLAMA_CPP_MODEL" ] || [ "$LLAMA_CPP_MODEL" = "null" ]; then
LLAMA_CPP_MODEL=$(curl -s --max-time 2 "${LLAMA_CPP_HOST}/v1/models" 2>/dev/null | jq -r '.data[0].id // "local-model"' 2>/dev/null || echo "local-model")
fi
jq -n --rawfile content /tmp/gsd-review-prompt-{phase}.md \
LLAMA_CPP_CONTENT=$(jq -n --rawfile content /tmp/gsd-review-prompt-{phase}.md \
--arg model "$LLAMA_CPP_MODEL" \
'{model: $model, messages: [{role: "user", content: $content}]}' | \
curl -s --max-time 120 -X POST "${LLAMA_CPP_HOST}/v1/chat/completions" \
-H "Content-Type: application/json" -d @- 2>/dev/null | \
jq -r '.choices[0].message.content // "llama.cpp review failed or returned empty output."' \
> /tmp/gsd-review-llama_cpp-{phase}.md
if [ ! -s /tmp/gsd-review-llama_cpp-{phase}.md ]; then
echo "llama.cpp review failed or returned empty output." > /tmp/gsd-review-llama_cpp-{phase}.md
jq -r '.choices[0].message.content // ""' 2>/dev/null || echo "")
if [ -n "$LLAMA_CPP_CONTENT" ]; then
echo "$LLAMA_CPP_CONTENT" > /tmp/gsd-review-llama_cpp-{phase}.md
else
echo "Warning: llama.cpp returned empty content — skipping review." >&2
fi
```