@modular-prompt/driver 0.13.5 → 0.15.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 (213) hide show
  1. package/README.md +117 -4
  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 +16 -0
  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 +21 -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 +8 -2
  21. package/dist/driver-registry/types.d.ts.map +1 -1
  22. package/dist/index.d.ts +11 -2
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +11 -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 +40 -47
  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} +89 -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-support.d.ts +23 -0
  71. package/dist/mlx-ml/mlx-cache-support.d.ts.map +1 -0
  72. package/dist/mlx-ml/mlx-cache-support.js +45 -0
  73. package/dist/mlx-ml/mlx-cache-support.js.map +1 -0
  74. package/dist/mlx-ml/mlx-driver.d.ts +20 -59
  75. package/dist/mlx-ml/mlx-driver.d.ts.map +1 -1
  76. package/dist/mlx-ml/mlx-driver.js +86 -415
  77. package/dist/mlx-ml/mlx-driver.js.map +1 -1
  78. package/dist/mlx-ml/mlx-local-inference-adapters.d.ts +3 -0
  79. package/dist/mlx-ml/mlx-local-inference-adapters.d.ts.map +1 -0
  80. package/dist/mlx-ml/mlx-local-inference-adapters.js +19 -0
  81. package/dist/mlx-ml/mlx-local-inference-adapters.js.map +1 -0
  82. package/dist/mlx-ml/mlx-options.d.ts +19 -0
  83. package/dist/mlx-ml/mlx-options.d.ts.map +1 -0
  84. package/dist/mlx-ml/mlx-options.js +30 -0
  85. package/dist/mlx-ml/mlx-options.js.map +1 -0
  86. package/dist/mlx-ml/process/index.d.ts +11 -8
  87. package/dist/mlx-ml/process/index.d.ts.map +1 -1
  88. package/dist/mlx-ml/process/index.js +75 -52
  89. package/dist/mlx-ml/process/index.js.map +1 -1
  90. package/dist/mlx-ml/process/model-specific.d.ts +2 -1
  91. package/dist/mlx-ml/process/model-specific.d.ts.map +1 -1
  92. package/dist/mlx-ml/process/model-specific.js.map +1 -1
  93. package/dist/mlx-ml/process/prompt-builder.d.ts +11 -0
  94. package/dist/mlx-ml/process/prompt-builder.d.ts.map +1 -0
  95. package/dist/mlx-ml/process/prompt-builder.js +51 -0
  96. package/dist/mlx-ml/process/prompt-builder.js.map +1 -0
  97. package/dist/mlx-ml/process/types.d.ts +15 -183
  98. package/dist/mlx-ml/process/types.d.ts.map +1 -1
  99. package/dist/mlx-ml/tool-call-parser/tool-formatter.js +2 -2
  100. package/dist/mlx-ml/tool-call-parser/tool-formatter.js.map +1 -1
  101. package/dist/mlx-ml/types.d.ts +2 -45
  102. package/dist/mlx-ml/types.d.ts.map +1 -1
  103. package/dist/models-config/index.d.ts +8 -0
  104. package/dist/models-config/index.d.ts.map +1 -0
  105. package/dist/models-config/index.js +7 -0
  106. package/dist/models-config/index.js.map +1 -0
  107. package/dist/models-config/loader.d.ts +20 -0
  108. package/dist/models-config/loader.d.ts.map +1 -0
  109. package/dist/models-config/loader.js +85 -0
  110. package/dist/models-config/loader.js.map +1 -0
  111. package/dist/models-config/paths.d.ts +7 -0
  112. package/dist/models-config/paths.d.ts.map +1 -0
  113. package/dist/models-config/paths.js +11 -0
  114. package/dist/models-config/paths.js.map +1 -0
  115. package/dist/models-config/resolve.d.ts +57 -0
  116. package/dist/models-config/resolve.d.ts.map +1 -0
  117. package/dist/models-config/resolve.js +187 -0
  118. package/dist/models-config/resolve.js.map +1 -0
  119. package/dist/models-config/types.d.ts +60 -0
  120. package/dist/models-config/types.d.ts.map +1 -0
  121. package/dist/models-config/types.js +5 -0
  122. package/dist/models-config/types.js.map +1 -0
  123. package/dist/pytorch/process/index.d.ts +35 -0
  124. package/dist/pytorch/process/index.d.ts.map +1 -0
  125. package/dist/pytorch/process/index.js +69 -0
  126. package/dist/pytorch/process/index.js.map +1 -0
  127. package/dist/pytorch/pytorch-driver.d.ts +35 -0
  128. package/dist/pytorch/pytorch-driver.d.ts.map +1 -0
  129. package/dist/pytorch/pytorch-driver.js +48 -0
  130. package/dist/pytorch/pytorch-driver.js.map +1 -0
  131. package/dist/pytorch/pytorch-local-inference-adapters.d.ts +3 -0
  132. package/dist/pytorch/pytorch-local-inference-adapters.d.ts.map +1 -0
  133. package/dist/pytorch/pytorch-local-inference-adapters.js +19 -0
  134. package/dist/pytorch/pytorch-local-inference-adapters.js.map +1 -0
  135. package/dist/pytorch/pytorch-options.d.ts +8 -0
  136. package/dist/pytorch/pytorch-options.d.ts.map +1 -0
  137. package/dist/pytorch/pytorch-options.js +21 -0
  138. package/dist/pytorch/pytorch-options.js.map +1 -0
  139. package/dist/query-logger.js +1 -1
  140. package/dist/query-logger.js.map +1 -1
  141. package/dist/query-utils.d.ts +29 -0
  142. package/dist/query-utils.d.ts.map +1 -0
  143. package/dist/query-utils.js +61 -0
  144. package/dist/query-utils.js.map +1 -0
  145. package/dist/runtime/check.d.ts +10 -0
  146. package/dist/runtime/check.d.ts.map +1 -0
  147. package/dist/runtime/check.js +24 -0
  148. package/dist/runtime/check.js.map +1 -0
  149. package/dist/runtime/index.d.ts +4 -0
  150. package/dist/runtime/index.d.ts.map +1 -0
  151. package/dist/runtime/index.js +4 -0
  152. package/dist/runtime/index.js.map +1 -0
  153. package/dist/runtime/manifest-core.d.mts +27 -0
  154. package/dist/runtime/manifest-core.d.mts.map +1 -0
  155. package/dist/runtime/manifest-core.mjs +68 -0
  156. package/dist/runtime/manifest-core.mjs.map +1 -0
  157. package/dist/runtime/manifest.d.ts +18 -0
  158. package/dist/runtime/manifest.d.ts.map +1 -0
  159. package/dist/runtime/manifest.js +9 -0
  160. package/dist/runtime/manifest.js.map +1 -0
  161. package/dist/runtime/paths-core.d.mts +17 -0
  162. package/dist/runtime/paths-core.d.mts.map +1 -0
  163. package/dist/runtime/paths-core.mjs +67 -0
  164. package/dist/runtime/paths-core.mjs.map +1 -0
  165. package/dist/runtime/paths.d.ts +12 -0
  166. package/dist/runtime/paths.d.ts.map +1 -0
  167. package/dist/runtime/paths.js +17 -0
  168. package/dist/runtime/paths.js.map +1 -0
  169. package/dist/types.d.ts +20 -1
  170. package/dist/types.d.ts.map +1 -1
  171. package/dist/types.js.map +1 -1
  172. package/package.json +8 -5
  173. package/scripts/download-model.js +25 -9
  174. package/scripts/runtime-cli.js +315 -0
  175. package/skills/driver-usage/SKILL.md +56 -1
  176. package/src/mlx-ml/python/__main__.py +43 -4
  177. package/src/mlx-ml/python/handlers/__init__.py +2 -1
  178. package/src/mlx-ml/python/handlers/cancel.py +53 -0
  179. package/src/mlx-ml/python/handlers/completion.py +3 -24
  180. package/src/mlx-ml/python/handlers/{chat.py → generate.py} +36 -106
  181. package/src/mlx-ml/python/handlers/render.py +40 -0
  182. package/src/mlx-ml/python/pyproject.toml +3 -2
  183. package/src/mlx-ml/python/server.py +35 -8
  184. package/src/mlx-ml/python/utils/template_render.py +80 -0
  185. package/src/mlx-ml/python/utils/token_utils.py +2 -2
  186. package/src/mlx-ml/python/uv.lock +549 -454
  187. package/src/pytorch/python/__main__.py +19 -0
  188. package/src/pytorch/python/backends/__init__.py +3 -0
  189. package/src/pytorch/python/backends/base.py +84 -0
  190. package/src/pytorch/python/backends/transformers_lm.py +127 -0
  191. package/src/pytorch/python/handlers/__init__.py +6 -0
  192. package/src/pytorch/python/handlers/cancel.py +53 -0
  193. package/src/pytorch/python/handlers/capabilities.py +6 -0
  194. package/src/pytorch/python/handlers/completion.py +15 -0
  195. package/src/pytorch/python/handlers/format_test.py +70 -0
  196. package/src/pytorch/python/handlers/generate.py +68 -0
  197. package/src/pytorch/python/handlers/render.py +40 -0
  198. package/src/pytorch/python/handlers/tokenize.py +63 -0
  199. package/src/pytorch/python/pyproject.toml +36 -0
  200. package/src/pytorch/python/server.py +140 -0
  201. package/src/pytorch/python/utils/__init__.py +0 -0
  202. package/src/pytorch/python/utils/chat_template_constraints.py +164 -0
  203. package/src/pytorch/python/utils/prompt_builder.py +54 -0
  204. package/src/pytorch/python/utils/template_render.py +80 -0
  205. package/src/pytorch/python/utils/token_utils.py +376 -0
  206. package/src/pytorch/python/uv.lock +694 -0
  207. package/dist/mlx-ml/process/process-communication.d.ts +0 -37
  208. package/dist/mlx-ml/process/process-communication.d.ts.map +0 -1
  209. package/dist/mlx-ml/process/process-communication.js.map +0 -1
  210. package/dist/mlx-ml/process/queue.d.ts +0 -33
  211. package/dist/mlx-ml/process/queue.d.ts.map +0 -1
  212. package/dist/mlx-ml/process/queue.js.map +0 -1
  213. package/scripts/setup-mlx.js +0 -53
@@ -0,0 +1,376 @@
1
+ """
2
+ トークン関連のユーティリティ関数
3
+ """
4
+ from utils.chat_template_constraints import detect_chat_restrictions
5
+
6
+
7
+ def is_eod_token(response, tokenizer):
8
+ """
9
+ レスポンスがEODトークンかどうかを判定する
10
+
11
+ Args:
12
+ response: stream_generateからのレスポンス
13
+ tokenizer: tokenizerオブジェクト(必須)
14
+
15
+ Returns:
16
+ bool: EODトークンの場合True
17
+ """
18
+ # 1. finish_reasonによる終了判定(MLX-LMの標準的な方法)
19
+ if hasattr(response, 'finish_reason') and response.finish_reason == 'stop':
20
+ return True
21
+
22
+ # 2. response.tokenによる終了トークン判定
23
+ if hasattr(response, 'token'):
24
+ token = response.token
25
+
26
+ # special_tokens_mapとadded_tokens_encoderから終了トークンを取得
27
+ end_token_ids = []
28
+
29
+ # special_tokens_mapから標準的な終了トークンを取得
30
+ if hasattr(tokenizer, 'special_tokens_map') and hasattr(tokenizer, 'added_tokens_encoder'):
31
+ special_map = tokenizer.special_tokens_map
32
+ added_encoder = tokenizer.added_tokens_encoder
33
+
34
+ # EOSトークン
35
+ eos_token_str = special_map.get('eos_token')
36
+ if eos_token_str and eos_token_str in added_encoder:
37
+ end_token_ids.append(added_encoder[eos_token_str])
38
+
39
+ # その他の終了関連トークン
40
+ end_related_keys = ['eoi_token'] # end_of_image
41
+ for key in end_related_keys:
42
+ token_str = special_map.get(key)
43
+ if token_str and token_str in added_encoder:
44
+ end_token_ids.append(added_encoder[token_str])
45
+
46
+ # added_tokens_encoderから直接取得(会話終了トークンなど)
47
+ if hasattr(tokenizer, 'added_tokens_encoder'):
48
+ added_encoder = tokenizer.added_tokens_encoder
49
+ conversation_end_tokens = ['<end_of_turn>']
50
+ for token_str in conversation_end_tokens:
51
+ token_id = added_encoder.get(token_str)
52
+ if token_id is not None:
53
+ end_token_ids.append(token_id)
54
+
55
+ # 重複を除去してチェック
56
+ if token in set(end_token_ids):
57
+ return True
58
+
59
+ return False
60
+
61
+
62
+ def get_special_tokens(tokenizer):
63
+ """
64
+ tokenizerから特殊トークンを取得する
65
+
66
+ Returns:
67
+ dict: special_tokens情報
68
+ """
69
+ special_tokens = {}
70
+
71
+ # 標準的なspecial tokens(tokenizerに定義されているもの)
72
+ # VLM processorではこれらの属性がない場合があるためgetattr使用
73
+ standard_tokens = {
74
+ "eod": getattr(tokenizer, "eos_token", None), # End of Document/Sequence
75
+ "bos": getattr(tokenizer, "bos_token", None), # Beginning of Sequence
76
+ "unk": getattr(tokenizer, "unk_token", None), # Unknown token
77
+ "pad": getattr(tokenizer, "pad_token", None), # Padding token
78
+ }
79
+
80
+ for name, token in standard_tokens.items():
81
+ if token is not None:
82
+ token_id = getattr(tokenizer, f"{name}_token_id", None)
83
+ if token_id is not None:
84
+ special_tokens[name] = {"text": token, "id": token_id}
85
+
86
+ # ペアトークン(存在する場合のみ)
87
+ pair_tokens = {
88
+ # ChatML基本形式
89
+ "system": ("<|system|>", "<|/system|>"),
90
+ "user": ("<|user|>", "<|/user|>"),
91
+ "assistant": ("<|assistant|>", "<|/assistant|>"),
92
+
93
+ # フォーマット・構造化
94
+ "code": ("<|code_start|>", "<|code_end|>"),
95
+ "python": ("<|python|>", "<|/python|>"),
96
+ "javascript": ("<|javascript|>", "<|/javascript|>"),
97
+ "bash": ("<|bash|>", "<|/bash|>"),
98
+ "quote": ("<|quote|>", "<|/quote|>"),
99
+ "ref": ("<|ref|>", "<|/ref|>"),
100
+ "citation": ("<|citation|>", "<|/citation|>"),
101
+ "table": ("<|table|>", "<|/table|>"),
102
+ "heading": ("<|heading|>", "<|/heading|>"),
103
+
104
+ # メディア・リッチコンテンツ
105
+ "image": ("<|image|>", "<|/image|>"),
106
+ "audio": ("<|audio|>", "<|/audio|>"),
107
+ "video": ("<|video|>", "<|/video|>"),
108
+
109
+ # 機能・制御
110
+ "tool_call": ("<|tool_call|>", "<|/tool_call|>"),
111
+ "function": ("<|function|>", "<|/function|>"),
112
+ "api": ("<|api|>", "<|/api|>"),
113
+ "search": ("<|search|>", "<|/search|>"),
114
+ "knowledge": ("<|knowledge|>", "<|/knowledge|>"),
115
+ "context": ("<|context|>", "<|/context|>"),
116
+
117
+ # 思考・推論
118
+ "thinking": ("<|thinking|>", "</thinking>"),
119
+ "reasoning": ("<|reasoning|>", "<|/reasoning|>"),
120
+ "scratchpad": ("<|scratchpad|>", "<|/scratchpad|>"),
121
+ "analysis": ("<|analysis|>", "<|/analysis|>"),
122
+ "summary": ("<|summary|>", "<|/summary|>"),
123
+ "explanation": ("<|explanation|>", "<|/explanation|>"),
124
+
125
+ # tool_call バリエーション(追加)
126
+ "tool_call_explicit": ("<|tool_call_start|>", "<|tool_call_end|>"),
127
+ "tool_call_xml": ("<tool_call>", "</tool_call>"),
128
+ "tool_calls_section": ("<|tool_calls_section_begin|>", "<|tool_calls_section_end|>"),
129
+ "function_call_tags": ("<start_function_call>", "<end_function_call>"),
130
+ "longcat_tool_call": ("<longcat_tool_call>", "</longcat_tool_call>"),
131
+ "minimax_tool_call": ("<minimax:tool_call>", "</minimax:tool_call>"),
132
+ }
133
+
134
+ # 単体トークン(存在する場合のみ)
135
+ single_tokens = {
136
+ # Fill-in-the-Middle
137
+ "fim_prefix": "<|fim_prefix|>",
138
+ "fim_middle": "<|fim_middle|>",
139
+ "fim_suffix": "<|fim_suffix|>",
140
+
141
+ # リスト・構造
142
+ "list_item": "<|list_item|>",
143
+
144
+ # メディア単体
145
+ "vision": "<|vision|>",
146
+
147
+ # 一般的なマークダウン風
148
+ "code_inline": "`",
149
+ "code_block_start": "```",
150
+ "code_block_end": "```",
151
+
152
+ # ツール関連の単体トークン(追加)
153
+ "tool_calls_marker": "[TOOL_CALLS]",
154
+ # Harmony形式のcallトークン(tool_call_endとは異なる用途)
155
+ "harmony_call": "<|call|>",
156
+ }
157
+
158
+ # VLM processorではconvert_tokens_to_idsがない場合がある
159
+ if not hasattr(tokenizer, 'convert_tokens_to_ids'):
160
+ return special_tokens
161
+
162
+ unk_token_id = getattr(tokenizer, "unk_token_id", None)
163
+
164
+ # ペアトークンの処理
165
+ for name, (start_token, end_token) in pair_tokens.items():
166
+ start_id = tokenizer.convert_tokens_to_ids(start_token)
167
+ end_id = tokenizer.convert_tokens_to_ids(end_token)
168
+
169
+ # unk_tokenでない場合のみ追加
170
+ if start_id != unk_token_id and end_id != unk_token_id:
171
+ special_tokens[name] = {
172
+ "start": {"text": start_token, "id": start_id},
173
+ "end": {"text": end_token, "id": end_id}
174
+ }
175
+
176
+ # 単体トークンの処理
177
+ for name, token_text in single_tokens.items():
178
+ token_id = tokenizer.convert_tokens_to_ids(token_text)
179
+
180
+ # unk_tokenでない場合のみ追加
181
+ if token_id != unk_token_id:
182
+ special_tokens[name] = {"text": token_text, "id": token_id}
183
+
184
+ return special_tokens
185
+
186
+
187
+ def detect_tool_call_format(tokenizer):
188
+ """tokenizer設定からtool call/resultのデリミタを検出する
189
+
190
+ tokenizerアクセスが必要な情報のみを抽出する:
191
+ - tool_parser_type: tokenizer_config由来のパーサー種別文字列
192
+ - chat_templateテキストからのデリミタパターン検出
193
+
194
+ パーサー種別→デリミタのマッピングはTS側(detector.ts)に一元化。
195
+ Python側はtokenizerから生の情報を抽出して渡す役割に専念する。
196
+
197
+ Returns:
198
+ dict or None: {
199
+ "tool_parser_type": str, # tokenizer_configのtool_parser_type
200
+ "call_start": str, # chat_templateから検出した開始デリミタ
201
+ "call_end": str, # chat_templateから検出した終了デリミタ
202
+ "response_start": str, # tool responseの開始デリミタ(検出時)
203
+ "response_end": str, # tool responseの終了デリミタ(検出時)
204
+ } or None
205
+ """
206
+ import re
207
+
208
+ tool_parser_type = None
209
+ if hasattr(tokenizer, 'init_kwargs'):
210
+ tool_parser_type = tokenizer.init_kwargs.get('tool_parser_type')
211
+
212
+ template = getattr(tokenizer, 'chat_template', None)
213
+ if not template and hasattr(tokenizer, 'init_kwargs'):
214
+ template = tokenizer.init_kwargs.get('chat_template', '')
215
+
216
+ if not tool_parser_type and not template:
217
+ return None
218
+
219
+ result = {}
220
+ if tool_parser_type:
221
+ result["tool_parser_type"] = tool_parser_type
222
+
223
+ if template:
224
+ tool_call_patterns = [
225
+ (r'<\|?tool_call\|?>', r'</tool_call>|<\|/tool_call\|>|<tool_call\|>'),
226
+ (r'<\|tool_call_start\|>', r'<\|tool_call_end\|>'),
227
+ (r'<start_function_call>', r'<end_function_call>'),
228
+ (r'<\|tool_calls_section_begin\|>', r'<\|tool_calls_section_end\|>'),
229
+ (r'<longcat_tool_call>', r'</longcat_tool_call>'),
230
+ (r'<minimax:tool_call>', r'</minimax:tool_call>'),
231
+ ]
232
+
233
+ for start_pattern, end_pattern in tool_call_patterns:
234
+ start_match = re.search(start_pattern, template)
235
+ end_match = re.search(end_pattern, template)
236
+ if start_match and end_match:
237
+ result["call_start"] = start_match.group(0)
238
+ result["call_end"] = end_match.group(0)
239
+ break
240
+
241
+ if "call_start" not in result:
242
+ has_functions = re.search(r'"functions\."', template)
243
+ has_call = re.search(r'<\|call\|>', template)
244
+ if has_functions and has_call:
245
+ result["tool_parser_type"] = "harmony"
246
+ result["call_start"] = "to=functions."
247
+ result["call_end"] = "<|call|>"
248
+
249
+ if "call_start" not in result:
250
+ mistral_match = re.search(r'\[TOOL_CALLS\]', template)
251
+ if mistral_match:
252
+ result["call_start"] = "[TOOL_CALLS]"
253
+ result["call_end"] = ""
254
+
255
+ resp_tags = re.findall(r'<[|/]?tool_response[|]?>', template)
256
+ if len(resp_tags) >= 2:
257
+ open_tags = [t for t in resp_tags if '/' not in t]
258
+ close_tags = [t for t in resp_tags if '/' in t]
259
+ if open_tags and close_tags:
260
+ result["response_start"] = open_tags[0]
261
+ result["response_end"] = close_tags[0]
262
+
263
+ return result if result else None
264
+
265
+
266
+ def get_chat_template_info(tokenizer):
267
+ """チャットテンプレートの詳細情報を取得"""
268
+ if not hasattr(tokenizer, 'apply_chat_template'):
269
+ return None
270
+
271
+ template_info = {
272
+ "supported_roles": [],
273
+ "preview": None,
274
+ "constraints": {}
275
+ }
276
+
277
+ # tool callフォーマット検出
278
+ tool_format = detect_tool_call_format(tokenizer)
279
+ if tool_format:
280
+ template_info["tool_call_format"] = tool_format
281
+
282
+ # サポートされるroleを検査
283
+ test_roles = ["system", "user", "assistant", "tool", "function"]
284
+ for role in test_roles:
285
+ test_msg = [{"role": role, "content": "test"}]
286
+ try:
287
+ tokenizer.apply_chat_template(test_msg, tokenize=False, add_generation_prompt=False)
288
+ template_info["supported_roles"].append(role)
289
+ except:
290
+ continue
291
+
292
+ # プレビュー生成
293
+ if template_info["supported_roles"]:
294
+ sample_messages = []
295
+ if "system" in template_info["supported_roles"]:
296
+ sample_messages.append({"role": "system", "content": "You are a helpful assistant."})
297
+ if "user" in template_info["supported_roles"]:
298
+ sample_messages.append({"role": "user", "content": "Hello!"})
299
+ if "assistant" in template_info["supported_roles"]:
300
+ sample_messages.append({"role": "assistant", "content": "Hi there!"})
301
+
302
+ try:
303
+ template_info["preview"] = tokenizer.apply_chat_template(
304
+ sample_messages,
305
+ tokenize=False,
306
+ add_generation_prompt=False
307
+ )
308
+ except Exception as e:
309
+ template_info["preview"] = f"Preview error: {e}"
310
+
311
+ return template_info
312
+
313
+
314
+ def get_tokenizer_features(tokenizer):
315
+ """
316
+ tokenizerの機能情報を取得する
317
+
318
+ Returns:
319
+ dict: features情報
320
+ """
321
+ # apply_chat_templateメソッドの存在だけでなく、テンプレートが実際に設定されているか確認
322
+ has_chat_template = hasattr(tokenizer, 'apply_chat_template') and bool(getattr(tokenizer, 'chat_template', None))
323
+ features = {
324
+ "apply_chat_template": has_chat_template,
325
+ "vocab_size": getattr(tokenizer, 'vocab_size', None),
326
+ "model_max_length": getattr(tokenizer, 'model_max_length', None)
327
+ }
328
+
329
+ # チャットテンプレート情報を追加
330
+ chat_template_info = get_chat_template_info(tokenizer)
331
+ if chat_template_info:
332
+ features["chat_template"] = chat_template_info
333
+
334
+ return features
335
+
336
+
337
+ def get_capabilities(tokenizer):
338
+ """
339
+ tokenizerの全機能情報を取得する(capabilities API用)
340
+
341
+ Returns:
342
+ dict: capabilities情報
343
+ """
344
+ # 基本メソッド
345
+ methods = ["capabilities", "completion", "generate", "format_test"]
346
+
347
+ # apply_chat_templateがある場合はchatメソッドを追加
348
+ if hasattr(tokenizer, 'apply_chat_template'):
349
+ methods.append("render")
350
+
351
+ capabilities = {
352
+ "methods": methods,
353
+ "special_tokens": get_special_tokens(tokenizer),
354
+ "features": get_tokenizer_features(tokenizer)
355
+ }
356
+
357
+ # tool_call_formatの情報をspecial_tokensに反映(補完)
358
+ features = capabilities.get("features", {})
359
+ chat_template = features.get("chat_template")
360
+ if chat_template:
361
+ tcf = chat_template.get("tool_call_format")
362
+ if tcf and tcf.get("call_start") and "tool_call" not in capabilities["special_tokens"]:
363
+ call_start = tcf["call_start"]
364
+ call_end = tcf.get("call_end", "")
365
+ if call_end: # ペアがある場合のみ
366
+ capabilities["special_tokens"]["tool_call"] = {
367
+ "start": {"text": call_start, "id": -1},
368
+ "end": {"text": call_end, "id": -1}
369
+ }
370
+
371
+ # チャット制約を検出して追加
372
+ chat_restrictions = detect_chat_restrictions(tokenizer)
373
+ if chat_restrictions:
374
+ capabilities["chat_restrictions"] = chat_restrictions
375
+
376
+ return capabilities