diff --git a/src/dlm/export/ollama/modelfile.py b/src/dlm/export/ollama/modelfile.py index bce4a29a..51ca10dd 100644 --- a/src/dlm/export/ollama/modelfile.py +++ b/src/dlm/export/ollama/modelfile.py @@ -98,7 +98,7 @@ def render_modelfile(ctx: ModelfileContext) -> str: ) from_line = f"FROM ./{ctx.base_gguf_name}" adapter_line = f"ADAPTER ./{ctx.adapter_gguf_name}" if ctx.adapter_gguf_name else None - template_block = _build_template_block(template_row) + template_block = _build_template_block(template_row) if ctx.plan.include_template else None num_ctx = resolve_num_ctx(ctx.training_sequence_len, ctx.spec.context_length) temperature = ( ctx.override_temperature @@ -121,7 +121,10 @@ def render_modelfile(ctx: ModelfileContext) -> str: parts: list[str] = [header, "", from_line] if adapter_line is not None: parts.append(adapter_line) - parts.extend(["", template_block, ""]) + if template_block is not None: + parts.extend(["", template_block, ""]) + else: + parts.append("") parts.extend(param_lines) if system_line is not None: parts.extend(["", system_line]) diff --git a/src/dlm/export/ollama/vl_modelfile.py b/src/dlm/export/ollama/vl_modelfile.py index 0101ee4d..38eb4270 100644 --- a/src/dlm/export/ollama/vl_modelfile.py +++ b/src/dlm/export/ollama/vl_modelfile.py @@ -131,7 +131,7 @@ def render_vl_modelfile(ctx: VlModelfileContext) -> str: ) from_line = f"FROM ./{ctx.base_gguf_name}" adapter_line = f"ADAPTER ./{ctx.adapter_gguf_name}" if ctx.adapter_gguf_name else None - template_block = f'TEMPLATE """{_VL_TEMPLATE_BODY}"""' + template_block = f'TEMPLATE """{_VL_TEMPLATE_BODY}"""' if ctx.plan.include_template else None num_ctx = resolve_num_ctx(ctx.training_sequence_len, ctx.spec.context_length) temperature = ( ctx.override_temperature @@ -152,7 +152,10 @@ def render_vl_modelfile(ctx: VlModelfileContext) -> str: parts: list[str] = [header, "", from_line] if adapter_line is not None: parts.append(adapter_line) - parts.extend(["", template_block, ""]) + if template_block is not None: + parts.extend(["", template_block, ""]) + else: + parts.append("") parts.extend(param_lines) if system_line is not None: parts.extend(["", system_line]) diff --git a/tests/unit/export/ollama/test_modelfile.py b/tests/unit/export/ollama/test_modelfile.py index 3466ea15..734dc578 100644 --- a/tests/unit/export/ollama/test_modelfile.py +++ b/tests/unit/export/ollama/test_modelfile.py @@ -82,6 +82,33 @@ def test_template_block_present(self, tmp_path: Path) -> None: assert 'TEMPLATE """' in text assert "<|im_start|>" in text # chatml dialect + def test_no_template_when_include_template_false(self, tmp_path: Path) -> None: + """Audit 13 M13.1: ``--no-template`` must actually suppress the + TEMPLATE block in the emitted Modelfile, not just the preflight.""" + ctx = _ctx(tmp_path) + # Bypass the frozen-dataclass replace dance: rebuild with a plan + # that has include_template=False. + plan = ExportPlan(quant="Q4_K_M", merged=False, include_template=False) + ctx_no_tmpl = ModelfileContext( + spec=ctx.spec, + plan=plan, + adapter_dir=ctx.adapter_dir, + base_gguf_name=ctx.base_gguf_name, + adapter_gguf_name=ctx.adapter_gguf_name, + dlm_id=ctx.dlm_id, + adapter_version=ctx.adapter_version, + system_prompt=ctx.system_prompt, + training_sequence_len=ctx.training_sequence_len, + override_temperature=ctx.override_temperature, + override_top_p=ctx.override_top_p, + draft_model_ollama_name=ctx.draft_model_ollama_name, + ) + text = render_modelfile(ctx_no_tmpl) + assert "TEMPLATE" not in text + # Other directives still emit normally. + assert "FROM ./base.Q4_K_M.gguf" in text + assert "PARAMETER temperature" in text + def test_params_emitted(self, tmp_path: Path) -> None: text = render_modelfile(_ctx(tmp_path)) assert "PARAMETER temperature" in text diff --git a/tests/unit/export/ollama/test_vl_modelfile.py b/tests/unit/export/ollama/test_vl_modelfile.py index 79aa4243..30ae6600 100644 --- a/tests/unit/export/ollama/test_vl_modelfile.py +++ b/tests/unit/export/ollama/test_vl_modelfile.py @@ -150,6 +150,21 @@ def test_blank_system_prompt_is_omitted(self, tmp_path: Path) -> None: assert "SYSTEM " not in text + def test_no_template_when_include_template_false(self, tmp_path: Path) -> None: + """`--no-template` skips TEMPLATE emission on the VL renderer.""" + ctx = VlModelfileContext( + spec=_QWEN_VL_SPEC, + plan=ExportPlan(quant="Q4_K_M", merged=False, include_template=False), + adapter_dir=_adapter_dir(tmp_path), + base_gguf_name="base.Q4_K_M.gguf", + adapter_gguf_name="adapter.gguf", + dlm_id="01VLTEST", + adapter_version=4, + ) + text = render_vl_modelfile(ctx) + assert "TEMPLATE" not in text + assert "FROM ./base.Q4_K_M.gguf" in text + class TestVlStopsFromAdapter: def test_family_defaults_and_adapter_special_tokens_are_merged(self, tmp_path: Path) -> None: diff --git a/tests/unit/export/test_vl_modelfile.py b/tests/unit/export/test_vl_modelfile.py index aab1edd1..fc83f3ae 100644 --- a/tests/unit/export/test_vl_modelfile.py +++ b/tests/unit/export/test_vl_modelfile.py @@ -48,10 +48,10 @@ def _fake_vl_spec() -> Any: ) -def _fake_plan() -> Any: +def _fake_plan(*, include_template: bool = True) -> Any: from types import SimpleNamespace - return SimpleNamespace(quant="Q4_K_M", merged=False) + return SimpleNamespace(quant="Q4_K_M", merged=False, include_template=include_template) @pytest.fixture @@ -166,6 +166,21 @@ def test_ends_with_newline(self, vl_ctx: VlModelfileContext) -> None: out = render_vl_modelfile(vl_ctx) assert out.endswith("\n") + def test_no_template_when_include_template_false(self, adapter_dir: Path) -> None: + """`--no-template` skips TEMPLATE emission on the VL path too.""" + ctx = VlModelfileContext( + spec=_fake_vl_spec(), + plan=_fake_plan(include_template=False), + adapter_dir=adapter_dir, + base_gguf_name="base.Q4_K_M.gguf", + adapter_gguf_name="adapter.Q4_K_M.gguf", + dlm_id="01JZZZZZZZZZZZZZZZZZZZZZZZ", + adapter_version=1, + ) + out = render_vl_modelfile(ctx) + assert "TEMPLATE" not in out + assert "FROM ./base.Q4_K_M.gguf" in out + class TestStopsFallback: def test_paligemma_uses_gemma_style_stop(self, tmp_path: Path) -> None: