@modular-prompt/driver 0.6.3 → 0.8.1

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 (40) hide show
  1. package/README.md +37 -540
  2. package/dist/driver-registry/registry.d.ts +1 -2
  3. package/dist/driver-registry/registry.d.ts.map +1 -1
  4. package/dist/driver-registry/registry.js +1 -2
  5. package/dist/driver-registry/registry.js.map +1 -1
  6. package/dist/formatter/formatter.d.ts.map +1 -1
  7. package/dist/formatter/formatter.js +32 -14
  8. package/dist/formatter/formatter.js.map +1 -1
  9. package/dist/mlx-ml/mlx-driver.d.ts +4 -0
  10. package/dist/mlx-ml/mlx-driver.d.ts.map +1 -1
  11. package/dist/mlx-ml/mlx-driver.js +67 -8
  12. package/dist/mlx-ml/mlx-driver.js.map +1 -1
  13. package/dist/mlx-ml/process/index.d.ts +3 -3
  14. package/dist/mlx-ml/process/index.d.ts.map +1 -1
  15. package/dist/mlx-ml/process/index.js +2 -2
  16. package/dist/mlx-ml/process/index.js.map +1 -1
  17. package/dist/mlx-ml/process/parameter-mapper.d.ts.map +1 -1
  18. package/dist/mlx-ml/process/parameter-mapper.js +4 -2
  19. package/dist/mlx-ml/process/parameter-mapper.js.map +1 -1
  20. package/dist/mlx-ml/process/process-communication.d.ts.map +1 -1
  21. package/dist/mlx-ml/process/process-communication.js +2 -7
  22. package/dist/mlx-ml/process/process-communication.js.map +1 -1
  23. package/dist/mlx-ml/process/queue.d.ts +2 -2
  24. package/dist/mlx-ml/process/queue.d.ts.map +1 -1
  25. package/dist/mlx-ml/process/queue.js +2 -1
  26. package/dist/mlx-ml/process/queue.js.map +1 -1
  27. package/dist/mlx-ml/process/types.d.ts +18 -0
  28. package/dist/mlx-ml/process/types.d.ts.map +1 -1
  29. package/dist/mlx-ml/tool-call-parser.d.ts +29 -0
  30. package/dist/mlx-ml/tool-call-parser.d.ts.map +1 -0
  31. package/dist/mlx-ml/tool-call-parser.js +163 -0
  32. package/dist/mlx-ml/tool-call-parser.js.map +1 -0
  33. package/dist/mlx-ml/types.d.ts +11 -0
  34. package/dist/mlx-ml/types.d.ts.map +1 -1
  35. package/package.json +6 -4
  36. package/skills/driver-usage/SKILL.md +432 -0
  37. package/src/mlx-ml/python/__main__.py +53 -38
  38. package/src/mlx-ml/python/pyproject.toml +1 -1
  39. package/src/mlx-ml/python/token_utils.py +72 -1
  40. package/src/mlx-ml/python/uv.lock +4 -4
@@ -34,59 +34,69 @@ def read():
34
34
  def supports_chat_template():
35
35
  """
36
36
  チャットテンプレートがサポートされているかを判定
37
-
37
+
38
38
  apply_chat_templateメソッドの存在と、tokenizer.chat_templateの両方を確認する。
39
39
  tokenizer.chat_templateが設定されていない場合、apply_chat_templateを呼んでも
40
40
  エラーになるため、両方の条件をチェックする必要がある。
41
-
41
+
42
42
  Returns:
43
43
  bool: チャットテンプレートがサポートされている場合True
44
44
  """
45
- return (hasattr(tokenizer, 'apply_chat_template') and
46
- hasattr(tokenizer, 'chat_template') and
45
+ return (hasattr(tokenizer, 'apply_chat_template') and
46
+ hasattr(tokenizer, 'chat_template') and
47
47
  tokenizer.chat_template is not None)
48
48
 
49
+
49
50
  def handle_capabilities():
50
51
  """capabilities API の処理"""
51
52
  print(json.dumps(capabilities), end='\0', flush=True)
52
53
 
53
54
 
54
- def handle_format_test(messages, options=None):
55
+ def handle_format_test(messages, options=None, tools=None):
55
56
  """フォーマットテスト API の処理(実際に生成せずフォーマットのみ)"""
56
57
  if options is None:
57
58
  options = {}
58
-
59
+
59
60
  result = {
60
61
  "formatted_prompt": None,
61
62
  "template_applied": False,
62
63
  "model_specific_processing": None,
63
64
  "error": None
64
65
  }
65
-
66
+
66
67
  try:
67
68
  # チャットテンプレートが利用可能かチェック
68
69
  if supports_chat_template():
69
70
  # messagesはTypeScript側で既にモデル固有処理済み
70
71
  result["model_specific_processing"] = messages
71
-
72
+
72
73
  # プロンプト生成(フォーマットのみ)
73
74
  primer = options.get('primer')
74
75
  add_generation_prompt = True
75
76
  tokenize = False # 常にテキストで返す
76
-
77
+
77
78
  if primer is not None:
78
79
  messages.append({'role': 'assistant', 'content': primer})
79
80
  add_generation_prompt = False
80
81
 
81
- formatted_prompt = tokenizer.apply_chat_template(
82
- messages,
83
- add_generation_prompt=add_generation_prompt,
84
- tokenize=tokenize,
85
- )
82
+ # tools対応を試みる(テンプレートが対応していなければtools無しで実行)
83
+ try:
84
+ formatted_prompt = tokenizer.apply_chat_template(
85
+ messages,
86
+ tools=tools,
87
+ add_generation_prompt=add_generation_prompt,
88
+ tokenize=tokenize,
89
+ )
90
+ except TypeError:
91
+ formatted_prompt = tokenizer.apply_chat_template(
92
+ messages,
93
+ add_generation_prompt=add_generation_prompt,
94
+ tokenize=tokenize,
95
+ )
86
96
 
87
97
  if primer is not None:
88
98
  formatted_prompt = primer.join(formatted_prompt.split(primer)[0:-1]) + primer
89
-
99
+
90
100
  result["formatted_prompt"] = formatted_prompt
91
101
  result["template_applied"] = True
92
102
  else:
@@ -95,49 +105,52 @@ def handle_format_test(messages, options=None):
95
105
  primer = options.get('primer')
96
106
  if primer is not None:
97
107
  formatted_prompt += primer
98
-
108
+
99
109
  result["formatted_prompt"] = formatted_prompt
100
110
  result["template_applied"] = False
101
-
111
+
102
112
  except Exception as e:
103
113
  result["error"] = str(e)
104
-
114
+
105
115
  print(json.dumps(result), end='\0', flush=True)
106
116
 
107
- def handle_chat(messages, primer=None, options=None):
117
+ def handle_chat(messages, primer=None, options=None, tools=None):
108
118
  """chat API の処理"""
109
119
  if options is None:
110
120
  options = {}
111
-
121
+
112
122
  # チャットテンプレートが利用可能かチェック
113
123
  if not supports_chat_template():
114
124
  # チャットテンプレートがない場合はcompletionフォーマットに変換
115
- # 注意: TypeScript側でAPIを決定するため、ここに来る場合は
116
- # TypeScript側でchatが選択されたが、実際にはテンプレートがないケース
117
125
  prompt = generate_merged_prompt(messages)
118
- # primerはTypeScript側で既に追加されている場合があるので追加しない
119
- # (TypeScript側でcompletion APIへの変換時に追加済み)
120
126
  if primer is not None:
121
127
  print(primer, end='', flush=True)
122
128
  generate_text(prompt, options)
123
129
  return
124
-
125
- # messagesはTypeScript側で既にモデル固有処理済み
126
-
130
+
127
131
  # プロンプト生成
128
132
  add_generation_prompt = True
129
133
  tokenize = False
130
-
134
+
131
135
  if primer is not None:
132
136
  messages.append({'role': 'assistant', 'content': primer})
133
137
  add_generation_prompt = False
134
138
  tokenize = False
135
139
 
136
- prompt = tokenizer.apply_chat_template(
137
- messages,
138
- add_generation_prompt=add_generation_prompt,
139
- tokenize=tokenize,
140
- )
140
+ # tools対応を試みる(テンプレートが対応していなければtools無しで実行)
141
+ try:
142
+ prompt = tokenizer.apply_chat_template(
143
+ messages,
144
+ tools=tools,
145
+ add_generation_prompt=add_generation_prompt,
146
+ tokenize=tokenize,
147
+ )
148
+ except TypeError:
149
+ prompt = tokenizer.apply_chat_template(
150
+ messages,
151
+ add_generation_prompt=add_generation_prompt,
152
+ tokenize=tokenize,
153
+ )
141
154
 
142
155
  if primer is not None:
143
156
  prompt = primer.join(prompt.split(primer)[0:-1]) + primer
@@ -274,20 +287,22 @@ def main():
274
287
  sys.stderr.write("Error: 'messages' field is required for format_test method\n")
275
288
  print('\n', end='\0', flush=True)
276
289
  continue
277
-
290
+
278
291
  options = req.get('options', {})
279
- handle_format_test(messages, options)
280
-
292
+ tools = req.get('tools')
293
+ handle_format_test(messages, options, tools)
294
+
281
295
  elif method == 'chat':
282
296
  messages = req.get('messages')
283
297
  if not messages:
284
298
  sys.stderr.write("Error: 'messages' field is required for chat method\n")
285
299
  print('\n', end='\0', flush=True)
286
300
  continue
287
-
301
+
288
302
  primer = req.get('primer')
289
303
  options = req.get('options', {})
290
- handle_chat(messages, primer, options)
304
+ tools = req.get('tools')
305
+ handle_chat(messages, primer, options, tools)
291
306
 
292
307
  elif method == 'completion':
293
308
  prompt = req.get('prompt')
@@ -7,7 +7,7 @@ dependencies = [
7
7
  "flex>=6.14.1",
8
8
  "hf-xet>=1.1.8",
9
9
  "jinja2>=3.1.6",
10
- "mlx-lm>=0.30.4",
10
+ "mlx-lm>=0.30.7",
11
11
  "torch>=2.9.0",
12
12
  ]
13
13
 
@@ -169,17 +169,88 @@ def get_special_tokens(tokenizer):
169
169
  return special_tokens
170
170
 
171
171
 
172
+ def detect_tool_call_format(tokenizer):
173
+ """tokenizer設定からtool call/resultのデリミタを検出する
174
+
175
+ tokenizer_config.json / chat_template からtool call関連の情報を抽出:
176
+ - tool_parser_type: パーサー種別("json_tools" 等)
177
+ - chat_template テキスト内の <tool_call></tool_call> 等のタグパターン
178
+
179
+ Returns:
180
+ dict or None: {
181
+ "tool_parser_type": str, # tokenizer_configのtool_parser_type
182
+ "call_start": str, # tool callの開始デリミタ
183
+ "call_end": str, # tool callの終了デリミタ
184
+ "response_start": str, # tool responseの開始デリミタ(検出時)
185
+ "response_end": str, # tool responseの終了デリミタ(検出時)
186
+ } or None
187
+ """
188
+ import re
189
+
190
+ # tool_parser_type を取得
191
+ tool_parser_type = None
192
+ if hasattr(tokenizer, 'init_kwargs'):
193
+ tool_parser_type = tokenizer.init_kwargs.get('tool_parser_type')
194
+
195
+ # chat_template テキストを取得
196
+ template = getattr(tokenizer, 'chat_template', None)
197
+ if not template and hasattr(tokenizer, 'init_kwargs'):
198
+ template = tokenizer.init_kwargs.get('chat_template', '')
199
+
200
+ # tool_parser_type もテンプレートもなければ非対応
201
+ if not tool_parser_type and not template:
202
+ return None
203
+
204
+ result = {}
205
+ if tool_parser_type:
206
+ result["tool_parser_type"] = tool_parser_type
207
+
208
+ # テンプレートテキストからデリミタを抽出
209
+ if template:
210
+ # tool_call タグの検出(<tool_call>, <|tool_call|> 等)
211
+ call_match = re.search(r'(<\|?tool_call\|?>)\s*\\n.*?(<\/?\|?tool_call\|?>|<\|?/tool_call\|?>)', template)
212
+ if call_match:
213
+ result["call_start"] = call_match.group(1)
214
+ result["call_end"] = call_match.group(2)
215
+ else:
216
+ # フォールバック: tool_call を含む開閉タグペアを探す
217
+ tags = re.findall(r'<[|/]?tool_call[|]?>', template)
218
+ if len(tags) >= 2:
219
+ # 開タグと閉タグを分離
220
+ open_tags = [t for t in tags if '/' not in t]
221
+ close_tags = [t for t in tags if '/' in t]
222
+ if open_tags and close_tags:
223
+ result["call_start"] = open_tags[0]
224
+ result["call_end"] = close_tags[0]
225
+
226
+ # tool_response タグの検出
227
+ resp_tags = re.findall(r'<[|/]?tool_response[|]?>', template)
228
+ if len(resp_tags) >= 2:
229
+ open_tags = [t for t in resp_tags if '/' not in t]
230
+ close_tags = [t for t in resp_tags if '/' in t]
231
+ if open_tags and close_tags:
232
+ result["response_start"] = open_tags[0]
233
+ result["response_end"] = close_tags[0]
234
+
235
+ return result if result else None
236
+
237
+
172
238
  def get_chat_template_info(tokenizer):
173
239
  """チャットテンプレートの詳細情報を取得"""
174
240
  if not hasattr(tokenizer, 'apply_chat_template'):
175
241
  return None
176
-
242
+
177
243
  template_info = {
178
244
  "template_string": getattr(tokenizer, 'chat_template', None),
179
245
  "supported_roles": [],
180
246
  "preview": None,
181
247
  "constraints": {}
182
248
  }
249
+
250
+ # tool callフォーマット検出
251
+ tool_format = detect_tool_call_format(tokenizer)
252
+ if tool_format:
253
+ template_info["tool_call_format"] = tool_format
183
254
 
184
255
  # サポートされるroleを検査
185
256
  test_roles = ["system", "user", "assistant", "tool", "function"]
@@ -382,13 +382,13 @@ requires-dist = [
382
382
  { name = "flex", specifier = ">=6.14.1" },
383
383
  { name = "hf-xet", specifier = ">=1.1.8" },
384
384
  { name = "jinja2", specifier = ">=3.1.6" },
385
- { name = "mlx-lm", specifier = ">=0.30.4" },
385
+ { name = "mlx-lm", specifier = ">=0.30.7" },
386
386
  { name = "torch", specifier = ">=2.9.0" },
387
387
  ]
388
388
 
389
389
  [[package]]
390
390
  name = "mlx-lm"
391
- version = "0.30.6"
391
+ version = "0.30.7"
392
392
  source = { registry = "https://pypi.org/simple" }
393
393
  dependencies = [
394
394
  { name = "jinja2" },
@@ -400,9 +400,9 @@ dependencies = [
400
400
  { name = "sentencepiece" },
401
401
  { name = "transformers" },
402
402
  ]
403
- sdist = { url = "https://files.pythonhosted.org/packages/76/cb/815deddc8699b1f694d7e1f9cbed52934c03a8b49432c8add72932bb2f0b/mlx_lm-0.30.6.tar.gz", hash = "sha256:807e042d7040268f1b19190b7eaefd8b2efbff5590a65460974ad4225b91dda1", size = 271733, upload-time = "2026-02-04T21:27:45.741Z" }
403
+ sdist = { url = "https://files.pythonhosted.org/packages/66/0d/56542e2ae13ec6f542d3977d7cff89a205d4f6c5122e0ce23f33265f61c9/mlx_lm-0.30.7.tar.gz", hash = "sha256:e5f31ac58d9f2381f28e1ba639ff903e64f7cff1bdc245c0bc97f72264be329c", size = 275764, upload-time = "2026-02-12T18:41:11.86Z" }
404
404
  wheels = [
405
- { url = "https://files.pythonhosted.org/packages/20/5f/01d281f1fa8a1521d5936659beb4f5ab1f32b463d059263cf9d4cef969d9/mlx_lm-0.30.6-py3-none-any.whl", hash = "sha256:a7405bd581eacc4bf8209d7a6b7f23629585a0d7c6740c2a97e51fee35b3b0e1", size = 379451, upload-time = "2026-02-04T21:27:43.222Z" },
405
+ { url = "https://files.pythonhosted.org/packages/1e/17/a41c798a3d9cbdc47f39c6db5bba4c2cd199203ead26bf911cb03b644070/mlx_lm-0.30.7-py3-none-any.whl", hash = "sha256:17442a4bf01c4c2d3bca1e647712fe44f19890c3f1eadc8589d389e57b44b9bf", size = 386591, upload-time = "2026-02-12T18:41:10.236Z" },
406
406
  ]
407
407
 
408
408
  [[package]]