@modular-prompt/driver 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/README.md +90 -5
  2. package/dist/driver-registry/ai-service.d.ts +23 -1
  3. package/dist/driver-registry/ai-service.d.ts.map +1 -1
  4. package/dist/driver-registry/ai-service.js +44 -10
  5. package/dist/driver-registry/ai-service.js.map +1 -1
  6. package/dist/driver-registry/config-based-factory.d.ts.map +1 -1
  7. package/dist/driver-registry/config-based-factory.js +20 -3
  8. package/dist/driver-registry/config-based-factory.js.map +1 -1
  9. package/dist/driver-registry/factory-helper.d.ts +2 -0
  10. package/dist/driver-registry/factory-helper.d.ts.map +1 -1
  11. package/dist/driver-registry/factory-helper.js +22 -3
  12. package/dist/driver-registry/factory-helper.js.map +1 -1
  13. package/dist/driver-registry/index.d.ts +2 -2
  14. package/dist/driver-registry/index.d.ts.map +1 -1
  15. package/dist/driver-registry/index.js +1 -1
  16. package/dist/driver-registry/index.js.map +1 -1
  17. package/dist/driver-registry/registry.d.ts.map +1 -1
  18. package/dist/driver-registry/registry.js +3 -1
  19. package/dist/driver-registry/registry.js.map +1 -1
  20. package/dist/driver-registry/types.d.ts +11 -2
  21. package/dist/driver-registry/types.d.ts.map +1 -1
  22. package/dist/index.d.ts +10 -2
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +9 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/local-inference/adapters.d.ts +66 -0
  27. package/dist/local-inference/adapters.d.ts.map +1 -0
  28. package/dist/local-inference/adapters.js +2 -0
  29. package/dist/local-inference/adapters.js.map +1 -0
  30. package/dist/local-inference/driver.d.ts +51 -0
  31. package/dist/local-inference/driver.d.ts.map +1 -0
  32. package/dist/local-inference/driver.js +309 -0
  33. package/dist/local-inference/driver.js.map +1 -0
  34. package/dist/local-inference/index.d.ts +22 -0
  35. package/dist/local-inference/index.d.ts.map +1 -0
  36. package/dist/local-inference/index.js +17 -0
  37. package/dist/local-inference/index.js.map +1 -0
  38. package/dist/local-inference/process-client.d.ts +50 -0
  39. package/dist/local-inference/process-client.d.ts.map +1 -0
  40. package/dist/local-inference/process-client.js +92 -0
  41. package/dist/local-inference/process-client.js.map +1 -0
  42. package/dist/local-inference/process-communication.d.ts +41 -0
  43. package/dist/local-inference/process-communication.d.ts.map +1 -0
  44. package/dist/{mlx-ml/process → local-inference}/process-communication.js +25 -61
  45. package/dist/local-inference/process-communication.js.map +1 -0
  46. package/dist/local-inference/process-port.d.ts +12 -0
  47. package/dist/local-inference/process-port.d.ts.map +1 -0
  48. package/dist/local-inference/process-port.js +2 -0
  49. package/dist/local-inference/process-port.js.map +1 -0
  50. package/dist/local-inference/prompt-utils.d.ts +6 -0
  51. package/dist/local-inference/prompt-utils.d.ts.map +1 -0
  52. package/dist/local-inference/prompt-utils.js +17 -0
  53. package/dist/local-inference/prompt-utils.js.map +1 -0
  54. package/dist/local-inference/protocol.d.ts +192 -0
  55. package/dist/local-inference/protocol.d.ts.map +1 -0
  56. package/dist/local-inference/protocol.js +2 -0
  57. package/dist/local-inference/protocol.js.map +1 -0
  58. package/dist/local-inference/queue-types.d.ts +54 -0
  59. package/dist/local-inference/queue-types.d.ts.map +1 -0
  60. package/dist/local-inference/queue-types.js +2 -0
  61. package/dist/local-inference/queue-types.js.map +1 -0
  62. package/dist/local-inference/request-queue.d.ts +36 -0
  63. package/dist/local-inference/request-queue.d.ts.map +1 -0
  64. package/dist/{mlx-ml/process/queue.js → local-inference/request-queue.js} +83 -56
  65. package/dist/local-inference/request-queue.js.map +1 -0
  66. package/dist/local-inference/stream-utils.d.ts +19 -0
  67. package/dist/local-inference/stream-utils.d.ts.map +1 -0
  68. package/dist/local-inference/stream-utils.js +76 -0
  69. package/dist/local-inference/stream-utils.js.map +1 -0
  70. package/dist/mlx-ml/mlx-cache-controller.d.ts.map +1 -1
  71. package/dist/mlx-ml/mlx-cache-controller.js +2 -1
  72. package/dist/mlx-ml/mlx-cache-controller.js.map +1 -1
  73. package/dist/mlx-ml/mlx-cache-support.d.ts +23 -0
  74. package/dist/mlx-ml/mlx-cache-support.d.ts.map +1 -0
  75. package/dist/mlx-ml/mlx-cache-support.js +45 -0
  76. package/dist/mlx-ml/mlx-cache-support.js.map +1 -0
  77. package/dist/mlx-ml/mlx-driver.d.ts +20 -59
  78. package/dist/mlx-ml/mlx-driver.d.ts.map +1 -1
  79. package/dist/mlx-ml/mlx-driver.js +86 -460
  80. package/dist/mlx-ml/mlx-driver.js.map +1 -1
  81. package/dist/mlx-ml/mlx-local-inference-adapters.d.ts +3 -0
  82. package/dist/mlx-ml/mlx-local-inference-adapters.d.ts.map +1 -0
  83. package/dist/mlx-ml/mlx-local-inference-adapters.js +19 -0
  84. package/dist/mlx-ml/mlx-local-inference-adapters.js.map +1 -0
  85. package/dist/mlx-ml/mlx-options.d.ts +19 -0
  86. package/dist/mlx-ml/mlx-options.d.ts.map +1 -0
  87. package/dist/mlx-ml/mlx-options.js +30 -0
  88. package/dist/mlx-ml/mlx-options.js.map +1 -0
  89. package/dist/mlx-ml/process/index.d.ts +10 -8
  90. package/dist/mlx-ml/process/index.d.ts.map +1 -1
  91. package/dist/mlx-ml/process/index.js +73 -54
  92. package/dist/mlx-ml/process/index.js.map +1 -1
  93. package/dist/mlx-ml/process/model-specific.d.ts +2 -1
  94. package/dist/mlx-ml/process/model-specific.d.ts.map +1 -1
  95. package/dist/mlx-ml/process/model-specific.js.map +1 -1
  96. package/dist/mlx-ml/process/prompt-builder.d.ts +11 -0
  97. package/dist/mlx-ml/process/prompt-builder.d.ts.map +1 -0
  98. package/dist/mlx-ml/process/prompt-builder.js +51 -0
  99. package/dist/mlx-ml/process/prompt-builder.js.map +1 -0
  100. package/dist/mlx-ml/process/types.d.ts +15 -183
  101. package/dist/mlx-ml/process/types.d.ts.map +1 -1
  102. package/dist/mlx-ml/types.d.ts +2 -45
  103. package/dist/mlx-ml/types.d.ts.map +1 -1
  104. package/dist/models-config/index.d.ts +8 -0
  105. package/dist/models-config/index.d.ts.map +1 -0
  106. package/dist/models-config/index.js +7 -0
  107. package/dist/models-config/index.js.map +1 -0
  108. package/dist/models-config/loader.d.ts +20 -0
  109. package/dist/models-config/loader.d.ts.map +1 -0
  110. package/dist/models-config/loader.js +85 -0
  111. package/dist/models-config/loader.js.map +1 -0
  112. package/dist/models-config/paths.d.ts +15 -0
  113. package/dist/models-config/paths.d.ts.map +1 -0
  114. package/dist/models-config/paths.js +26 -0
  115. package/dist/models-config/paths.js.map +1 -0
  116. package/dist/models-config/resolve.d.ts +57 -0
  117. package/dist/models-config/resolve.d.ts.map +1 -0
  118. package/dist/models-config/resolve.js +208 -0
  119. package/dist/models-config/resolve.js.map +1 -0
  120. package/dist/models-config/types.d.ts +62 -0
  121. package/dist/models-config/types.d.ts.map +1 -0
  122. package/dist/models-config/types.js +5 -0
  123. package/dist/models-config/types.js.map +1 -0
  124. package/dist/pytorch/process/index.d.ts +35 -0
  125. package/dist/pytorch/process/index.d.ts.map +1 -0
  126. package/dist/pytorch/process/index.js +69 -0
  127. package/dist/pytorch/process/index.js.map +1 -0
  128. package/dist/pytorch/pytorch-driver.d.ts +35 -0
  129. package/dist/pytorch/pytorch-driver.d.ts.map +1 -0
  130. package/dist/pytorch/pytorch-driver.js +48 -0
  131. package/dist/pytorch/pytorch-driver.js.map +1 -0
  132. package/dist/pytorch/pytorch-local-inference-adapters.d.ts +3 -0
  133. package/dist/pytorch/pytorch-local-inference-adapters.d.ts.map +1 -0
  134. package/dist/pytorch/pytorch-local-inference-adapters.js +19 -0
  135. package/dist/pytorch/pytorch-local-inference-adapters.js.map +1 -0
  136. package/dist/pytorch/pytorch-options.d.ts +8 -0
  137. package/dist/pytorch/pytorch-options.d.ts.map +1 -0
  138. package/dist/pytorch/pytorch-options.js +21 -0
  139. package/dist/pytorch/pytorch-options.js.map +1 -0
  140. package/dist/query-logger.js +1 -1
  141. package/dist/query-logger.js.map +1 -1
  142. package/dist/runtime/check.d.ts +10 -0
  143. package/dist/runtime/check.d.ts.map +1 -0
  144. package/dist/runtime/check.js +25 -0
  145. package/dist/runtime/check.js.map +1 -0
  146. package/dist/runtime/index.d.ts +5 -0
  147. package/dist/runtime/index.d.ts.map +1 -0
  148. package/dist/runtime/index.js +5 -0
  149. package/dist/runtime/index.js.map +1 -0
  150. package/dist/runtime/manifest-core.d.mts +27 -0
  151. package/dist/runtime/manifest-core.d.mts.map +1 -0
  152. package/dist/runtime/manifest-core.mjs +68 -0
  153. package/dist/runtime/manifest-core.mjs.map +1 -0
  154. package/dist/runtime/manifest.d.ts +18 -0
  155. package/dist/runtime/manifest.d.ts.map +1 -0
  156. package/dist/runtime/manifest.js +9 -0
  157. package/dist/runtime/manifest.js.map +1 -0
  158. package/dist/runtime/paths-core.d.mts +17 -0
  159. package/dist/runtime/paths-core.d.mts.map +1 -0
  160. package/dist/runtime/paths-core.mjs +67 -0
  161. package/dist/runtime/paths-core.mjs.map +1 -0
  162. package/dist/runtime/paths.d.ts +12 -0
  163. package/dist/runtime/paths.d.ts.map +1 -0
  164. package/dist/runtime/paths.js +17 -0
  165. package/dist/runtime/paths.js.map +1 -0
  166. package/dist/runtime/setup-commands-core.d.mts +13 -0
  167. package/dist/runtime/setup-commands-core.d.mts.map +1 -0
  168. package/dist/runtime/setup-commands-core.mjs +14 -0
  169. package/dist/runtime/setup-commands-core.mjs.map +1 -0
  170. package/dist/runtime/setup-commands.d.ts +2 -0
  171. package/dist/runtime/setup-commands.d.ts.map +1 -0
  172. package/dist/runtime/setup-commands.js +2 -0
  173. package/dist/runtime/setup-commands.js.map +1 -0
  174. package/dist/types.d.ts +11 -1
  175. package/dist/types.d.ts.map +1 -1
  176. package/dist/types.js.map +1 -1
  177. package/package.json +13 -7
  178. package/scripts/download-model.js +26 -9
  179. package/scripts/runtime-cli.js +320 -0
  180. package/src/mlx-ml/python/__main__.py +43 -4
  181. package/src/mlx-ml/python/backends/cache_archive.py +41 -0
  182. package/src/mlx-ml/python/backends/mlx_lm.py +17 -1
  183. package/src/mlx-ml/python/handlers/__init__.py +2 -1
  184. package/src/mlx-ml/python/handlers/completion.py +3 -27
  185. package/src/mlx-ml/python/handlers/{chat.py → generate.py} +33 -106
  186. package/src/mlx-ml/python/handlers/render.py +40 -0
  187. package/src/mlx-ml/python/pyproject.toml +11 -4
  188. package/src/mlx-ml/python/server.py +28 -8
  189. package/src/mlx-ml/python/utils/template_render.py +80 -0
  190. package/src/mlx-ml/python/utils/token_utils.py +2 -2
  191. package/src/mlx-ml/python/uv.lock +544 -689
  192. package/src/pytorch/python/__main__.py +19 -0
  193. package/src/pytorch/python/backends/__init__.py +3 -0
  194. package/src/pytorch/python/backends/base.py +84 -0
  195. package/src/pytorch/python/backends/transformers_lm.py +127 -0
  196. package/src/pytorch/python/handlers/__init__.py +6 -0
  197. package/src/pytorch/python/handlers/cancel.py +53 -0
  198. package/src/pytorch/python/handlers/capabilities.py +6 -0
  199. package/src/pytorch/python/handlers/completion.py +15 -0
  200. package/src/pytorch/python/handlers/format_test.py +70 -0
  201. package/src/pytorch/python/handlers/generate.py +68 -0
  202. package/src/pytorch/python/handlers/render.py +40 -0
  203. package/src/pytorch/python/handlers/tokenize.py +63 -0
  204. package/src/pytorch/python/pyproject.toml +36 -0
  205. package/src/pytorch/python/server.py +140 -0
  206. package/src/pytorch/python/utils/__init__.py +0 -0
  207. package/src/pytorch/python/utils/chat_template_constraints.py +164 -0
  208. package/src/pytorch/python/utils/prompt_builder.py +54 -0
  209. package/src/pytorch/python/utils/template_render.py +80 -0
  210. package/src/pytorch/python/utils/token_utils.py +376 -0
  211. package/src/pytorch/python/uv.lock +694 -0
  212. package/dist/mlx-ml/process/process-communication.d.ts +0 -45
  213. package/dist/mlx-ml/process/process-communication.d.ts.map +0 -1
  214. package/dist/mlx-ml/process/process-communication.js.map +0 -1
  215. package/dist/mlx-ml/process/queue.d.ts +0 -35
  216. package/dist/mlx-ml/process/queue.d.ts.map +0 -1
  217. package/dist/mlx-ml/process/queue.js.map +0 -1
  218. package/scripts/setup-mlx.js +0 -53
@@ -1,12 +1,12 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import json
4
+ import os
4
5
  import re
5
6
  import sys
6
7
 
7
8
  from backends.base import ModelBackend
8
9
  from mlx_lm.models.cache import trim_prompt_cache
9
- from utils.prompt_builder import generate_merged_prompt, supports_chat_template
10
10
  from handlers.cancel import poll_cancel
11
11
 
12
12
 
@@ -53,65 +53,42 @@ def _stream_to_stdout(
53
53
  print("", end="\0", flush=True)
54
54
 
55
55
 
56
- def handle_chat(
56
+ def handle_generate(
57
57
  backend: ModelBackend,
58
- capabilities: dict,
59
- messages: list,
60
- primer: str | None = None,
58
+ prompt: str | list[int],
61
59
  options: dict | None = None,
62
- tools: list | None = None,
63
60
  images: list | None = None,
64
61
  max_image_size: int = 768,
65
- reasoning_effort: str | None = None,
62
+ primer: str | None = None,
66
63
  cache_path: str | None = None,
67
64
  cache_trim_tokens: int | None = None,
68
65
  ) -> None:
69
- """chat API の処理"""
66
+ """LIP generate: 整形済み prompt のストリーム推論"""
70
67
  if options is None:
71
68
  options = {}
72
69
 
73
- tokenizer = backend.get_tokenizer()
74
-
75
- if backend.supports_vision():
76
- add_generation_prompt = True
77
- fmt_messages = list(messages)
78
- if primer is not None:
79
- fmt_messages.append({"role": "assistant", "content": primer})
80
- add_generation_prompt = False
81
-
82
- try:
83
- prompt = tokenizer.apply_chat_template(
84
- fmt_messages,
85
- tools=tools,
86
- add_generation_prompt=add_generation_prompt,
87
- tokenize=False,
88
- )
89
- except TypeError:
90
- prompt = tokenizer.apply_chat_template(
91
- fmt_messages,
92
- add_generation_prompt=add_generation_prompt,
93
- tokenize=False,
94
- )
95
-
96
- if primer is not None:
97
- prompt = primer.join(prompt.split(primer)[0:-1]) + primer
98
-
99
- display_prompt = re.sub(r'(<\|image_pad\|>)+', '<|image_pad|>...', prompt)
100
- sys.stderr.write(f"--- vlm prompt (images: {len(images) if images else 0}, max_size: {max_image_size})\n{display_prompt}\n")
101
-
102
- final_options = dict(options)
70
+ final_options = dict(options)
71
+ if images:
103
72
  final_options["max_image_size"] = max_image_size
104
- _stream_to_stdout(
105
- backend,
106
- prompt,
107
- final_options,
108
- images=images,
109
- primer=primer,
110
- )
111
- return
73
+ if os.getenv('MLX_DEBUG'):
74
+ if isinstance(prompt, str):
75
+ display_prompt = re.sub(r'(<\|image_pad\|>)+', '<|image_pad|>...', prompt)
76
+ else:
77
+ display_prompt = f"<token ids: len={len(prompt)}>"
78
+ sys.stderr.write(
79
+ f"--- vlm generate (images: {len(images)}, max_size: {max_image_size})\n{display_prompt}\n"
80
+ )
81
+ elif os.getenv('MLX_DEBUG'):
82
+ if isinstance(prompt, list):
83
+ sys.stderr.write(f"--- prompt: len={len(prompt)}\n")
84
+ else:
85
+ sys.stderr.write(f"--- prompt\n{prompt}\n")
112
86
 
113
- prompt_cache = backend.load_cache_from_file(cache_path) if cache_path else None
87
+ # VLM 経路では KV キャッシュを使わない(旧 chat ハンドラと同様)
88
+ prompt_cache = None
114
89
  cache_tokens = 0
90
+ if cache_path and not images:
91
+ prompt_cache = backend.load_cache_from_file(cache_path)
115
92
  if prompt_cache is not None:
116
93
  if cache_trim_tokens is not None:
117
94
  current_offset = backend.get_cache_offset(prompt_cache)
@@ -128,7 +105,6 @@ def handle_chat(
128
105
  if meta_count is not None:
129
106
  cache_tokens = meta_count
130
107
  else:
131
- # Legacy cache without meta file - skip it for safety
132
108
  sys.stderr.write(
133
109
  f"WARNING: Cache file exists but no .meta.json found at {cache_path}. "
134
110
  "Ignoring cache for safety (may be from old implementation).\n"
@@ -142,67 +118,11 @@ def handle_chat(
142
118
  elif cache_path:
143
119
  sys.stderr.write(f"KV cache load FAILED: {cache_path}\n")
144
120
 
145
- if not supports_chat_template(tokenizer):
146
- prompt = generate_merged_prompt(messages, capabilities)
147
- if prompt_cache is not None:
148
- sys.stderr.write("KV cache ignored: model does not support chat template\n")
149
- _stream_to_stdout(backend, prompt, options, primer=primer)
150
- return
151
-
152
- add_generation_prompt = True
153
- fmt_messages = list(messages)
154
- if primer is not None:
155
- fmt_messages.append({"role": "assistant", "content": primer})
156
- add_generation_prompt = False
157
-
158
- extra_kwargs = {}
159
- if tools is not None:
160
- extra_kwargs["tools"] = tools
161
- if reasoning_effort is not None:
162
- extra_kwargs["reasoning_effort"] = reasoning_effort
163
-
164
- trust_remote_code = options.get("trust_remote_code")
165
- if trust_remote_code is not None:
166
- extra_kwargs["trust_remote_code"] = trust_remote_code
167
-
168
- try:
169
- prompt = tokenizer.apply_chat_template(
170
- fmt_messages,
171
- add_generation_prompt=add_generation_prompt,
172
- tokenize=False,
173
- **extra_kwargs,
174
- )
175
- except TypeError:
176
- try:
177
- fallback_kwargs = {}
178
- if tools is not None:
179
- fallback_kwargs["tools"] = tools
180
- prompt = tokenizer.apply_chat_template(
181
- fmt_messages,
182
- add_generation_prompt=add_generation_prompt,
183
- tokenize=False,
184
- **fallback_kwargs,
185
- )
186
- except TypeError:
187
- prompt = tokenizer.apply_chat_template(
188
- fmt_messages,
189
- add_generation_prompt=add_generation_prompt,
190
- tokenize=False,
191
- )
192
-
193
- if primer is not None:
194
- prompt = primer.join(prompt.split(primer)[0:-1]) + primer
195
-
196
- if isinstance(prompt, list):
197
- sys.stderr.write(f"--- prompt: len={len(prompt)}\n")
198
- else:
199
- sys.stderr.write(f"--- prompt\n{prompt}\n")
200
-
201
- final_options = dict(options)
202
121
  final_options.pop("trust_remote_code", None)
203
122
 
204
123
  effective_prompt = prompt
205
124
  if prompt_cache is not None and cache_tokens > 0 and isinstance(prompt, str):
125
+ tokenizer = backend.get_tokenizer()
206
126
  add_special = tokenizer.bos_token is None or not prompt.startswith(
207
127
  tokenizer.bos_token
208
128
  )
@@ -221,4 +141,11 @@ def handle_chat(
221
141
  )
222
142
  prompt_cache = None
223
143
 
224
- _stream_to_stdout(backend, effective_prompt, final_options, primer=primer, prompt_cache=prompt_cache)
144
+ _stream_to_stdout(
145
+ backend,
146
+ effective_prompt,
147
+ final_options,
148
+ images=images,
149
+ primer=primer,
150
+ prompt_cache=prompt_cache,
151
+ )
@@ -0,0 +1,40 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+
5
+ from backends.base import ModelBackend
6
+ from utils.template_render import apply_chat_template_prompt
7
+
8
+
9
+ def handle_render(
10
+ backend: ModelBackend,
11
+ messages: list,
12
+ options: dict | None = None,
13
+ tools: list | None = None,
14
+ reasoning_effort: str | None = None,
15
+ ) -> None:
16
+ """LIP render: apply_chat_template のみ(推論しない)"""
17
+ if options is None:
18
+ options = {}
19
+
20
+ result: dict = {
21
+ "formatted_prompt": None,
22
+ "error": None,
23
+ }
24
+
25
+ try:
26
+ trust_remote_code = options.get("trust_remote_code")
27
+ primer = options.get("primer")
28
+ prompt = apply_chat_template_prompt(
29
+ backend,
30
+ messages,
31
+ primer=primer,
32
+ tools=tools,
33
+ reasoning_effort=reasoning_effort,
34
+ trust_remote_code=trust_remote_code,
35
+ )
36
+ result["formatted_prompt"] = prompt
37
+ except Exception as e:
38
+ result["error"] = str(e)
39
+
40
+ print(json.dumps(result), end="\0", flush=True)
@@ -2,18 +2,25 @@
2
2
  name = "mlx_driver"
3
3
  version = "0.1.0"
4
4
  description = "MLX driver for moduler-prompt"
5
- requires-python = ">=3.10,<3.14"
5
+ requires-python = ">=3.11,<3.14"
6
6
  dependencies = [
7
7
  "flex==6.14.1",
8
8
  "hf-xet==1.2.0",
9
9
  "jinja2==3.1.6",
10
- "mlx>=0.31.2; sys_platform == 'darwin'",
10
+ "mlx>=0.32.2; sys_platform == 'darwin'",
11
11
  "mlx-lm==0.31.3; sys_platform == 'darwin'",
12
- "mlx-vlm==0.6.3; sys_platform == 'darwin'",
12
+ "mlx-optiq>=0.4.4; sys_platform == 'darwin'",
13
+ "mlx-vlm==0.6.17; sys_platform == 'darwin'",
13
14
  "tokenizers==0.22.2",
14
15
  "torch==2.9.1",
15
16
  "torchvision==0.24.1",
16
- "transformers>=5.5.0",
17
+ "transformers>=5.14.0",
18
+ ]
19
+
20
+ [tool.uv]
21
+ # mlx-optiq は transformers<5.13 を要求するが、mlx-vlm>=0.6.5 は 5.14+ が必要
22
+ override-dependencies = [
23
+ "transformers>=5.14.0",
17
24
  ]
18
25
 
19
26
  [dependency-groups]
@@ -3,7 +3,7 @@ import json
3
3
  import sys
4
4
 
5
5
  from backends.base import ModelBackend
6
- from handlers import handle_cache_prefill, handle_capabilities, handle_chat, handle_completion, handle_format_test, handle_tokenize
6
+ from handlers import handle_cache_prefill, handle_capabilities, handle_completion, handle_format_test, handle_generate, handle_render, handle_tokenize
7
7
  from handlers.cancel import request_cancel, reset_cancel
8
8
 
9
9
 
@@ -27,6 +27,15 @@ def read():
27
27
  continue
28
28
 
29
29
 
30
+ def _is_valid_generate_prompt(prompt) -> bool:
31
+ """prompt は非空文字列または非空のトークン ID リスト"""
32
+ if isinstance(prompt, str):
33
+ return bool(prompt)
34
+ if isinstance(prompt, list):
35
+ return len(prompt) > 0
36
+ return False
37
+
38
+
30
39
  class Server:
31
40
  def __init__(self, backend: ModelBackend, capabilities: dict):
32
41
  self.backend = backend
@@ -93,21 +102,32 @@ class Server:
93
102
  reasoning_effort=req.get('reasoning_effort'),
94
103
  )
95
104
 
96
- elif method == 'chat':
105
+ elif method == 'render':
97
106
  messages = req.get('messages')
98
107
  if not messages:
99
- self._error_response("'messages' field is required for chat method")
108
+ self._error_response("'messages' field is required for render method")
100
109
  return
101
- handle_chat(
110
+ handle_render(
102
111
  self.backend,
103
- self.capabilities,
104
112
  messages,
105
- primer=req.get('primer'),
106
113
  options=req.get('options', {}),
107
114
  tools=req.get('tools'),
108
- images=req.get('images', []),
109
- max_image_size=req.get('maxImageSize', 768),
110
115
  reasoning_effort=req.get('reasoning_effort'),
116
+ )
117
+
118
+ elif method == 'generate':
119
+ prompt = req.get('prompt')
120
+ if not _is_valid_generate_prompt(prompt):
121
+ self._error_response("'prompt' field is required for generate method")
122
+ return
123
+ images = req.get('images', [])
124
+ handle_generate(
125
+ self.backend,
126
+ prompt,
127
+ options=req.get('options', {}),
128
+ images=images if images else None,
129
+ max_image_size=req.get('maxImageSize', 768),
130
+ primer=req.get('primer'),
111
131
  cache_path=req.get('cache_path'),
112
132
  cache_trim_tokens=req.get('cache_trim_tokens'),
113
133
  )
@@ -0,0 +1,80 @@
1
+ """apply_chat_template によるプロンプト整形(推論なし)"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from utils.prompt_builder import supports_chat_template
6
+
7
+
8
+ def apply_chat_template_prompt(
9
+ backend,
10
+ messages: list,
11
+ *,
12
+ primer: str | None = None,
13
+ tools: list | None = None,
14
+ reasoning_effort: str | None = None,
15
+ trust_remote_code: bool | None = None,
16
+ ) -> str | list:
17
+ """messages を chat template で整形して prompt を返す"""
18
+ tokenizer = backend.get_tokenizer()
19
+
20
+ if not supports_chat_template(tokenizer):
21
+ raise ValueError("chat_template_not_available")
22
+
23
+ add_generation_prompt = True
24
+ fmt_messages = list(messages)
25
+ if primer is not None:
26
+ fmt_messages.append({"role": "assistant", "content": primer})
27
+ add_generation_prompt = False
28
+
29
+ if backend.supports_vision():
30
+ try:
31
+ prompt = tokenizer.apply_chat_template(
32
+ fmt_messages,
33
+ tools=tools,
34
+ add_generation_prompt=add_generation_prompt,
35
+ tokenize=False,
36
+ )
37
+ except TypeError:
38
+ prompt = tokenizer.apply_chat_template(
39
+ fmt_messages,
40
+ add_generation_prompt=add_generation_prompt,
41
+ tokenize=False,
42
+ )
43
+ else:
44
+ extra_kwargs: dict = {}
45
+ if tools is not None:
46
+ extra_kwargs["tools"] = tools
47
+ if reasoning_effort is not None:
48
+ extra_kwargs["reasoning_effort"] = reasoning_effort
49
+ if trust_remote_code is not None:
50
+ extra_kwargs["trust_remote_code"] = trust_remote_code
51
+
52
+ try:
53
+ prompt = tokenizer.apply_chat_template(
54
+ fmt_messages,
55
+ add_generation_prompt=add_generation_prompt,
56
+ tokenize=False,
57
+ **extra_kwargs,
58
+ )
59
+ except TypeError:
60
+ try:
61
+ fallback_kwargs: dict = {}
62
+ if tools is not None:
63
+ fallback_kwargs["tools"] = tools
64
+ prompt = tokenizer.apply_chat_template(
65
+ fmt_messages,
66
+ add_generation_prompt=add_generation_prompt,
67
+ tokenize=False,
68
+ **fallback_kwargs,
69
+ )
70
+ except TypeError:
71
+ prompt = tokenizer.apply_chat_template(
72
+ fmt_messages,
73
+ add_generation_prompt=add_generation_prompt,
74
+ tokenize=False,
75
+ )
76
+
77
+ if primer is not None and isinstance(prompt, str):
78
+ prompt = primer.join(prompt.split(primer)[0:-1]) + primer
79
+
80
+ return prompt
@@ -342,11 +342,11 @@ def get_capabilities(tokenizer):
342
342
  dict: capabilities情報
343
343
  """
344
344
  # 基本メソッド
345
- methods = ["capabilities", "completion", "format_test"]
345
+ methods = ["capabilities", "completion", "generate", "format_test"]
346
346
 
347
347
  # apply_chat_templateがある場合はchatメソッドを追加
348
348
  if hasattr(tokenizer, 'apply_chat_template'):
349
- methods.append("chat")
349
+ methods.append("render")
350
350
 
351
351
  capabilities = {
352
352
  "methods": methods,