test_lmstudio_wrapper_generator.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import importlib.util
  2. import json
  3. import tempfile
  4. import unittest
  5. from pathlib import Path
  6. MODULE_PATH = Path(__file__).resolve().parent.parent / "tools" / "lmstudio" / "wrapper_generator.py"
  7. if MODULE_PATH.exists():
  8. spec = importlib.util.spec_from_file_location("wrapper_generator", MODULE_PATH)
  9. module = importlib.util.module_from_spec(spec)
  10. assert spec is not None and spec.loader is not None
  11. spec.loader.exec_module(module)
  12. gen = module
  13. else: # pragma: no cover
  14. gen = None
  15. class WrapperGeneratorTests(unittest.TestCase):
  16. def setUp(self):
  17. if gen is None:
  18. self.fail("tools.lmstudio.wrapper_generator is not available")
  19. self.official_model_yaml = """\
  20. model: qwen/qwen3.6-35b-a3b
  21. base:
  22. - key: lmstudio-community/qwen3.6-35b-a3b-gguf
  23. metadataOverrides:
  24. domain: llm
  25. architectures:
  26. - qwen35moe
  27. compatibilityTypes:
  28. - gguf
  29. reasoning: true
  30. config:
  31. operation:
  32. fields:
  33. - key: llm.prediction.promptTemplate
  34. value:
  35. type: jinja
  36. jinjaPromptTemplate:
  37. template: |
  38. {%- if add_generation_prompt %}
  39. {%- if enable_thinking is defined and enable_thinking is false %}
  40. {{- '<think>\\n\\n</think>\\n\\n' }}
  41. {%- else %}
  42. {{- '<think>\\n' }}
  43. {%- endif %}
  44. {%- endif %}
  45. customFields:
  46. - key: enableThinking
  47. effects:
  48. - type: setJinjaVariable
  49. variable: enable_thinking
  50. - key: preserveThinking
  51. effects:
  52. - type: setJinjaVariable
  53. variable: preserve_thinking
  54. """
  55. self.official_manifest = {
  56. "type": "model",
  57. "owner": "qwen",
  58. "name": "qwen3.6-35b-a3b",
  59. "dependencies": [
  60. {
  61. "type": "model",
  62. "purpose": "baseModel",
  63. "modelKeys": ["lmstudio-community/qwen3.6-35b-a3b-gguf"],
  64. }
  65. ],
  66. "revision": 1,
  67. }
  68. def test_build_wrapper_files_contains_reasoning_and_target_key(self):
  69. result = gen.build_wrapper_files(
  70. target_model_key="qwen3.6-35b-a3b-uncensored-hauhaucs-aggressive",
  71. wrapper_owner="local",
  72. wrapper_name="qwen3.6-35b-a3b-hauhaucs-aggressive",
  73. official_model_yaml_text=self.official_model_yaml,
  74. official_manifest=self.official_manifest,
  75. )
  76. self.assertIn("reasoning: true", result["model.yaml"])
  77. self.assertIn("key: enableThinking", result["model.yaml"])
  78. self.assertIn("key: preserveThinking", result["model.yaml"])
  79. self.assertIn(
  80. "qwen3.6-35b-a3b-uncensored-hauhaucs-aggressive",
  81. result["model.yaml"],
  82. )
  83. manifest = json.loads(result["manifest.json"])
  84. self.assertEqual(manifest["owner"], "local")
  85. self.assertEqual(manifest["name"], "qwen3.6-35b-a3b-hauhaucs-aggressive")
  86. self.assertEqual(
  87. manifest["dependencies"][0]["modelKeys"],
  88. ["qwen3.6-35b-a3b-uncensored-hauhaucs-aggressive"],
  89. )
  90. def test_dry_run_does_not_write_output(self):
  91. with tempfile.TemporaryDirectory() as tmpdir:
  92. output_dir = Path(tmpdir) / "wrapper"
  93. summary = gen.generate_wrapper(
  94. output_dir=output_dir,
  95. target_model_key="qwen3.6-35b-a3b-uncensored-hauhaucs-aggressive",
  96. wrapper_owner="local",
  97. wrapper_name="qwen3.6-35b-a3b-hauhaucs-aggressive",
  98. official_model_yaml_text=self.official_model_yaml,
  99. official_manifest=self.official_manifest,
  100. dry_run=True,
  101. )
  102. self.assertFalse(output_dir.exists())
  103. self.assertTrue(summary["dry_run"])
  104. self.assertIn("model.yaml", summary["files"])
  105. self.assertIn("manifest.json", summary["files"])
  106. self.assertIn("README.md", summary["files"])
  107. if __name__ == "__main__":
  108. unittest.main()