@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.
- package/README.md +37 -540
- package/dist/driver-registry/registry.d.ts +1 -2
- package/dist/driver-registry/registry.d.ts.map +1 -1
- package/dist/driver-registry/registry.js +1 -2
- package/dist/driver-registry/registry.js.map +1 -1
- package/dist/formatter/formatter.d.ts.map +1 -1
- package/dist/formatter/formatter.js +32 -14
- package/dist/formatter/formatter.js.map +1 -1
- package/dist/mlx-ml/mlx-driver.d.ts +4 -0
- package/dist/mlx-ml/mlx-driver.d.ts.map +1 -1
- package/dist/mlx-ml/mlx-driver.js +67 -8
- package/dist/mlx-ml/mlx-driver.js.map +1 -1
- package/dist/mlx-ml/process/index.d.ts +3 -3
- package/dist/mlx-ml/process/index.d.ts.map +1 -1
- package/dist/mlx-ml/process/index.js +2 -2
- package/dist/mlx-ml/process/index.js.map +1 -1
- package/dist/mlx-ml/process/parameter-mapper.d.ts.map +1 -1
- package/dist/mlx-ml/process/parameter-mapper.js +4 -2
- package/dist/mlx-ml/process/parameter-mapper.js.map +1 -1
- package/dist/mlx-ml/process/process-communication.d.ts.map +1 -1
- package/dist/mlx-ml/process/process-communication.js +2 -7
- package/dist/mlx-ml/process/process-communication.js.map +1 -1
- package/dist/mlx-ml/process/queue.d.ts +2 -2
- package/dist/mlx-ml/process/queue.d.ts.map +1 -1
- package/dist/mlx-ml/process/queue.js +2 -1
- package/dist/mlx-ml/process/queue.js.map +1 -1
- package/dist/mlx-ml/process/types.d.ts +18 -0
- package/dist/mlx-ml/process/types.d.ts.map +1 -1
- package/dist/mlx-ml/tool-call-parser.d.ts +29 -0
- package/dist/mlx-ml/tool-call-parser.d.ts.map +1 -0
- package/dist/mlx-ml/tool-call-parser.js +163 -0
- package/dist/mlx-ml/tool-call-parser.js.map +1 -0
- package/dist/mlx-ml/types.d.ts +11 -0
- package/dist/mlx-ml/types.d.ts.map +1 -1
- package/package.json +6 -4
- package/skills/driver-usage/SKILL.md +432 -0
- package/src/mlx-ml/python/__main__.py +53 -38
- package/src/mlx-ml/python/pyproject.toml +1 -1
- package/src/mlx-ml/python/token_utils.py +72 -1
- 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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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')
|
|
@@ -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.
|
|
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.
|
|
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/
|
|
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/
|
|
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]]
|