fix(ai): request final responses from Qwen3.5
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
# Qwen3.5 compatibility preparation
|
||||
|
||||
The owner requested `qwen3.5:9b-q4_K_M` after live deployment. The model is downloaded, but the active sidecar still uses the previous model. This change prepares the existing adapter; it does not activate a model or qualify the withheld P5-13 capability.
|
||||
|
||||
The existing adapter consumes Ollama's final `response`. A synthetic local probe found an empty response with the Qwen3.5 thinking default; a20October probe with `think:false` returned the requested synthetic JSON and no thinking text. The adapter now sets that request field only when the configured base family is exactly `qwen3.5`. Existing temperature, JSON mode, timeouts, response parsing, other model payloads and external-provider consent/fallback remain unchanged. Custom namespace aliases are outside this exact family match.
|
||||
|
||||
Seven new offline cases verify both text/JSON for the requested tag and bare family, and unchanged payloads for qwen2.5, qwen3 and a lookalike family. Four Qwen3.5 cases failed ordinarily before the two-line change; three compatibility cases passed. The full sidecar suite then passed **56/56**, zero failures/errors/skips,8.45seconds,20October13:32UTC. The prior49-case baseline grows by seven; no model calls are part of these tests. Raw RED/GREEN logs and JUnit are retained under `.artifacts/release-20261020/model-compat/`.
|
||||
|
||||
Live activation, deployed adapter identity, GPU residency and final-response smoke remain pending behind the release. P5-13's deployed-identity/366-call/independent-human-review/UI requirements remain unmet and its review feature stays disabled. Production readiness remains NO-GO independently of the owner's release authorization.
|
||||
@@ -646,6 +646,8 @@ def _ollama_generate(prompt: str, *, json_mode: bool, temperature: float, timeou
|
||||
"stream": False,
|
||||
"options": {"temperature": temperature},
|
||||
}
|
||||
if OLLAMA_MODEL.split(":", 1)[0] == "qwen3.5":
|
||||
payload["think"] = False
|
||||
if json_mode:
|
||||
payload["format"] = "json"
|
||||
body = _http_post_json(f"{OLLAMA_BASE_URL}/api/generate", payload, {}, timeout)
|
||||
|
||||
@@ -8,6 +8,7 @@ import textwrap
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from docx import Document
|
||||
from fastapi.testclient import TestClient
|
||||
from PIL import Image
|
||||
@@ -253,6 +254,55 @@ def test_provider_defaults_to_ollama_and_is_unchanged(monkeypatch):
|
||||
assert captured["body"]["options"]["temperature"] == 0.1
|
||||
|
||||
|
||||
@pytest.mark.parametrize("ollama_model", ["qwen3.5:9b-q4_K_M", "qwen3.5"])
|
||||
@pytest.mark.parametrize("json_mode", [False, True])
|
||||
def test_qwen35_requests_nonthinking_output_without_consuming_thinking(monkeypatch, ollama_model, json_mode):
|
||||
module = load_app_module(monkeypatch, ollama_model=ollama_model)
|
||||
captured = {}
|
||||
output = '{"score": 7}' if json_mode else "Rewritten CV text"
|
||||
|
||||
def fake_urlopen(req, timeout=None):
|
||||
captured["body"] = json.loads(req.data.decode("utf-8"))
|
||||
captured["timeout"] = timeout
|
||||
captured["url"] = req.full_url
|
||||
return _FakeResponse({
|
||||
"response": output if captured["body"].get("think") is False else "",
|
||||
"thinking": "Private reasoning must never become candidate text.",
|
||||
})
|
||||
|
||||
monkeypatch.setattr(module.urllib_request, "urlopen", fake_urlopen)
|
||||
result = module._ollama_generate_json("synthetic prompt") if json_mode else module._ollama_generate_text("synthetic prompt")
|
||||
|
||||
assert result == ({"score": 7} if json_mode else output)
|
||||
expected = {
|
||||
"model": ollama_model,
|
||||
"prompt": "synthetic prompt",
|
||||
"stream": False,
|
||||
"options": {"temperature": 0.1 if json_mode else 0.2},
|
||||
"think": False,
|
||||
}
|
||||
if json_mode:
|
||||
expected["format"] = "json"
|
||||
assert captured["body"] == expected
|
||||
assert captured["url"] == f"{module.OLLAMA_BASE_URL}/api/generate"
|
||||
assert captured["timeout"] == (120 if json_mode else 180)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("ollama_model", ["qwen2.5:7b", "qwen3:8b", "qwen3.5-plus:9b"])
|
||||
def test_other_ollama_model_payloads_remain_unchanged(monkeypatch, ollama_model):
|
||||
module = load_app_module(monkeypatch, ollama_model=ollama_model)
|
||||
captured = {}
|
||||
_install_fake_urlopen(monkeypatch, module, {"response": " Candidate text ", "thinking": "Reasoning"}, captured)
|
||||
|
||||
assert module._ollama_generate_text("synthetic prompt") == "Candidate text"
|
||||
assert captured["body"] == {
|
||||
"model": ollama_model,
|
||||
"prompt": "synthetic prompt",
|
||||
"stream": False,
|
||||
"options": {"temperature": 0.2},
|
||||
}
|
||||
|
||||
|
||||
def test_local_success_wins_even_when_external_fallback_is_permitted(monkeypatch):
|
||||
monkeypatch.setenv("AI_PROVIDER", "gemini")
|
||||
monkeypatch.setenv("GEMINI_API_KEY", "test-key")
|
||||
|
||||
Reference in New Issue
Block a user