abstractcore 2.9.1__py3-none-any.whl → 2.11.2__py3-none-any.whl
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.
- abstractcore/__init__.py +7 -27
- abstractcore/apps/extractor.py +33 -100
- abstractcore/apps/intent.py +19 -0
- abstractcore/apps/judge.py +20 -1
- abstractcore/apps/summarizer.py +20 -1
- abstractcore/architectures/detection.py +34 -1
- abstractcore/architectures/response_postprocessing.py +313 -0
- abstractcore/assets/architecture_formats.json +38 -8
- abstractcore/assets/model_capabilities.json +781 -160
- abstractcore/compression/__init__.py +1 -2
- abstractcore/compression/glyph_processor.py +6 -4
- abstractcore/config/main.py +31 -19
- abstractcore/config/manager.py +389 -11
- abstractcore/config/vision_config.py +5 -5
- abstractcore/core/interface.py +151 -3
- abstractcore/core/session.py +16 -10
- abstractcore/download.py +1 -1
- abstractcore/embeddings/manager.py +20 -6
- abstractcore/endpoint/__init__.py +2 -0
- abstractcore/endpoint/app.py +458 -0
- abstractcore/mcp/client.py +3 -1
- abstractcore/media/__init__.py +52 -17
- abstractcore/media/auto_handler.py +42 -22
- abstractcore/media/base.py +44 -1
- abstractcore/media/capabilities.py +12 -33
- abstractcore/media/enrichment.py +105 -0
- abstractcore/media/handlers/anthropic_handler.py +19 -28
- abstractcore/media/handlers/local_handler.py +124 -70
- abstractcore/media/handlers/openai_handler.py +19 -31
- abstractcore/media/processors/__init__.py +4 -2
- abstractcore/media/processors/audio_processor.py +57 -0
- abstractcore/media/processors/office_processor.py +8 -3
- abstractcore/media/processors/pdf_processor.py +46 -3
- abstractcore/media/processors/text_processor.py +22 -24
- abstractcore/media/processors/video_processor.py +58 -0
- abstractcore/media/types.py +97 -4
- abstractcore/media/utils/image_scaler.py +20 -2
- abstractcore/media/utils/video_frames.py +219 -0
- abstractcore/media/vision_fallback.py +136 -22
- abstractcore/processing/__init__.py +32 -3
- abstractcore/processing/basic_deepsearch.py +15 -10
- abstractcore/processing/basic_intent.py +3 -2
- abstractcore/processing/basic_judge.py +3 -2
- abstractcore/processing/basic_summarizer.py +1 -1
- abstractcore/providers/__init__.py +3 -1
- abstractcore/providers/anthropic_provider.py +95 -8
- abstractcore/providers/base.py +1516 -81
- abstractcore/providers/huggingface_provider.py +546 -69
- abstractcore/providers/lmstudio_provider.py +35 -923
- abstractcore/providers/mlx_provider.py +382 -35
- abstractcore/providers/model_capabilities.py +5 -1
- abstractcore/providers/ollama_provider.py +99 -15
- abstractcore/providers/openai_compatible_provider.py +406 -180
- abstractcore/providers/openai_provider.py +188 -44
- abstractcore/providers/openrouter_provider.py +76 -0
- abstractcore/providers/registry.py +61 -5
- abstractcore/providers/streaming.py +138 -33
- abstractcore/providers/vllm_provider.py +92 -817
- abstractcore/server/app.py +461 -13
- abstractcore/server/audio_endpoints.py +139 -0
- abstractcore/server/vision_endpoints.py +1319 -0
- abstractcore/structured/handler.py +316 -41
- abstractcore/tools/common_tools.py +5501 -2012
- abstractcore/tools/comms_tools.py +1641 -0
- abstractcore/tools/core.py +37 -7
- abstractcore/tools/handler.py +4 -9
- abstractcore/tools/parser.py +49 -2
- abstractcore/tools/tag_rewriter.py +2 -1
- abstractcore/tools/telegram_tdlib.py +407 -0
- abstractcore/tools/telegram_tools.py +261 -0
- abstractcore/utils/cli.py +1085 -72
- abstractcore/utils/token_utils.py +2 -0
- abstractcore/utils/truncation.py +29 -0
- abstractcore/utils/version.py +3 -4
- abstractcore/utils/vlm_token_calculator.py +12 -2
- abstractcore-2.11.2.dist-info/METADATA +562 -0
- abstractcore-2.11.2.dist-info/RECORD +133 -0
- {abstractcore-2.9.1.dist-info → abstractcore-2.11.2.dist-info}/WHEEL +1 -1
- {abstractcore-2.9.1.dist-info → abstractcore-2.11.2.dist-info}/entry_points.txt +1 -0
- abstractcore-2.9.1.dist-info/METADATA +0 -1190
- abstractcore-2.9.1.dist-info/RECORD +0 -119
- {abstractcore-2.9.1.dist-info → abstractcore-2.11.2.dist-info}/licenses/LICENSE +0 -0
- {abstractcore-2.9.1.dist-info → abstractcore-2.11.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Response post-processing helpers driven by architecture formats and model capabilities.
|
|
3
|
+
|
|
4
|
+
These utilities normalize model output across providers (local runtimes, OpenAI-compatible
|
|
5
|
+
servers, etc.) based on `assets/architecture_formats.json` and `assets/model_capabilities.json`.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
from typing import Any, Mapping, Optional, Tuple
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _coerce_str(value: Any) -> Optional[str]:
|
|
15
|
+
if not isinstance(value, str):
|
|
16
|
+
return None
|
|
17
|
+
s = value.strip()
|
|
18
|
+
return s or None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def strip_output_wrappers(
|
|
22
|
+
text: str,
|
|
23
|
+
*,
|
|
24
|
+
architecture_format: Optional[Mapping[str, Any]] = None,
|
|
25
|
+
model_capabilities: Optional[Mapping[str, Any]] = None,
|
|
26
|
+
) -> str:
|
|
27
|
+
"""Strip known model-specific wrapper tokens around assistant output.
|
|
28
|
+
|
|
29
|
+
Some model/server combinations emit wrapper tokens like:
|
|
30
|
+
<|begin_of_box|> ... <|end_of_box|>
|
|
31
|
+
We remove these only when they appear as leading/trailing wrappers (not when
|
|
32
|
+
embedded mid-text).
|
|
33
|
+
"""
|
|
34
|
+
if not isinstance(text, str) or not text:
|
|
35
|
+
return text
|
|
36
|
+
|
|
37
|
+
# Architecture defaults first, model-specific overrides last.
|
|
38
|
+
start_token: Optional[str] = None
|
|
39
|
+
end_token: Optional[str] = None
|
|
40
|
+
for src in (architecture_format, model_capabilities):
|
|
41
|
+
if not isinstance(src, Mapping):
|
|
42
|
+
continue
|
|
43
|
+
wrappers = src.get("output_wrappers")
|
|
44
|
+
if not isinstance(wrappers, Mapping):
|
|
45
|
+
continue
|
|
46
|
+
start = _coerce_str(wrappers.get("start"))
|
|
47
|
+
end = _coerce_str(wrappers.get("end"))
|
|
48
|
+
if start is not None:
|
|
49
|
+
start_token = start
|
|
50
|
+
if end is not None:
|
|
51
|
+
end_token = end
|
|
52
|
+
|
|
53
|
+
if start_token is None and end_token is None:
|
|
54
|
+
return text
|
|
55
|
+
|
|
56
|
+
out = text
|
|
57
|
+
if start_token:
|
|
58
|
+
out = re.sub(r"^\s*" + re.escape(start_token) + r"\s*", "", out, count=1)
|
|
59
|
+
if end_token:
|
|
60
|
+
out = re.sub(r"\s*" + re.escape(end_token) + r"\s*$", "", out, count=1)
|
|
61
|
+
|
|
62
|
+
return out
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _get_thinking_tags(
|
|
66
|
+
*,
|
|
67
|
+
architecture_format: Optional[Mapping[str, Any]] = None,
|
|
68
|
+
model_capabilities: Optional[Mapping[str, Any]] = None,
|
|
69
|
+
) -> Optional[Tuple[str, str]]:
|
|
70
|
+
"""Return (start_tag, end_tag) for inline thinking tags when configured."""
|
|
71
|
+
tags: Any = None
|
|
72
|
+
for src in (architecture_format, model_capabilities):
|
|
73
|
+
if not isinstance(src, Mapping):
|
|
74
|
+
continue
|
|
75
|
+
value = src.get("thinking_tags")
|
|
76
|
+
if value is not None:
|
|
77
|
+
tags = value
|
|
78
|
+
if not isinstance(tags, (list, tuple)) or len(tags) != 2:
|
|
79
|
+
return None
|
|
80
|
+
start = _coerce_str(tags[0])
|
|
81
|
+
end = _coerce_str(tags[1])
|
|
82
|
+
if start is None or end is None:
|
|
83
|
+
return None
|
|
84
|
+
return start, end
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def strip_thinking_tags(
|
|
88
|
+
text: str,
|
|
89
|
+
*,
|
|
90
|
+
architecture_format: Optional[Mapping[str, Any]] = None,
|
|
91
|
+
model_capabilities: Optional[Mapping[str, Any]] = None,
|
|
92
|
+
) -> Tuple[str, Optional[str]]:
|
|
93
|
+
"""Strip inline thinking tags and return (clean_text, reasoning).
|
|
94
|
+
|
|
95
|
+
Some models emit reasoning as tagged blocks inside the assistant content, e.g.:
|
|
96
|
+
<think> ... </think>
|
|
97
|
+
When configured via assets, we extract the tagged reasoning and remove it from
|
|
98
|
+
the visible content. This keeps downstream transcripts clean while preserving
|
|
99
|
+
reasoning in metadata.
|
|
100
|
+
"""
|
|
101
|
+
if not isinstance(text, str) or not text:
|
|
102
|
+
return text, None
|
|
103
|
+
|
|
104
|
+
tags = _get_thinking_tags(
|
|
105
|
+
architecture_format=architecture_format,
|
|
106
|
+
model_capabilities=model_capabilities,
|
|
107
|
+
)
|
|
108
|
+
if tags is None:
|
|
109
|
+
return text, None
|
|
110
|
+
|
|
111
|
+
start_tag, end_tag = tags
|
|
112
|
+
# Non-greedy across newlines; allow multiple blocks.
|
|
113
|
+
pattern = re.compile(re.escape(start_tag) + r"(.*?)" + re.escape(end_tag), re.DOTALL)
|
|
114
|
+
matches = list(pattern.finditer(text))
|
|
115
|
+
if not matches:
|
|
116
|
+
# Some models (notably Qwen3 Thinking variants) may emit ONLY the closing tag `</think>`
|
|
117
|
+
# with the opening tag provided by the chat template (i.e., not present in decoded text).
|
|
118
|
+
# In that case, treat everything before the first end tag as reasoning.
|
|
119
|
+
if end_tag in text and start_tag not in text:
|
|
120
|
+
before, after = text.split(end_tag, 1)
|
|
121
|
+
reasoning_only = before.strip() or None
|
|
122
|
+
cleaned = after
|
|
123
|
+
cleaned = re.sub(r"\n{3,}", "\n\n", cleaned).strip()
|
|
124
|
+
return cleaned, reasoning_only
|
|
125
|
+
return text, None
|
|
126
|
+
|
|
127
|
+
extracted: list[str] = []
|
|
128
|
+
for m in matches:
|
|
129
|
+
chunk = (m.group(1) or "").strip()
|
|
130
|
+
if chunk:
|
|
131
|
+
extracted.append(chunk)
|
|
132
|
+
|
|
133
|
+
cleaned = pattern.sub("", text)
|
|
134
|
+
# Tidy up: collapse multiple blank lines created by removal.
|
|
135
|
+
cleaned = re.sub(r"\n{3,}", "\n\n", cleaned).strip()
|
|
136
|
+
|
|
137
|
+
reasoning = "\n\n".join(extracted).strip() if extracted else None
|
|
138
|
+
return cleaned, reasoning or None
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def split_harmony_response_text(text: str) -> Tuple[Optional[str], Optional[str]]:
|
|
142
|
+
"""Best-effort split of OpenAI Harmony-style transcripts into (final, reasoning).
|
|
143
|
+
|
|
144
|
+
Expected shape (common in GPT-OSS):
|
|
145
|
+
<|channel|>analysis<|message|>...<|end|><|start|>assistant<|channel|>final<|message|>...<|end|>
|
|
146
|
+
"""
|
|
147
|
+
if not isinstance(text, str) or not text:
|
|
148
|
+
return None, None
|
|
149
|
+
|
|
150
|
+
final_marker = "<|channel|>final"
|
|
151
|
+
msg_marker = "<|message|>"
|
|
152
|
+
end_marker = "<|end|>"
|
|
153
|
+
start_marker = "<|start|>"
|
|
154
|
+
|
|
155
|
+
idx_final = text.rfind(final_marker)
|
|
156
|
+
|
|
157
|
+
# Extract analysis reasoning if present (even if final is truncated/missing).
|
|
158
|
+
reasoning_text: Optional[str] = None
|
|
159
|
+
idx_analysis = text.find("<|channel|>analysis")
|
|
160
|
+
if idx_analysis != -1:
|
|
161
|
+
idx_analysis_msg = text.find(msg_marker, idx_analysis)
|
|
162
|
+
if idx_analysis_msg != -1:
|
|
163
|
+
a_start = idx_analysis_msg + len(msg_marker)
|
|
164
|
+
# Prefer explicit end marker; otherwise stop at final marker if present; otherwise consume remainder.
|
|
165
|
+
a_end = text.find(end_marker, a_start)
|
|
166
|
+
if a_end == -1 and idx_final != -1:
|
|
167
|
+
a_end = idx_final
|
|
168
|
+
if a_end == -1:
|
|
169
|
+
a_end = len(text)
|
|
170
|
+
reasoning_raw = text[a_start:a_end]
|
|
171
|
+
reasoning_text = reasoning_raw.strip() if reasoning_raw.strip() else None
|
|
172
|
+
|
|
173
|
+
if idx_final == -1:
|
|
174
|
+
return None, reasoning_text
|
|
175
|
+
|
|
176
|
+
idx_msg = text.find(msg_marker, idx_final)
|
|
177
|
+
start = (idx_msg + len(msg_marker)) if idx_msg != -1 else (idx_final + len(final_marker))
|
|
178
|
+
final_raw = text[start:]
|
|
179
|
+
|
|
180
|
+
# Cut off any trailing transcript tokens.
|
|
181
|
+
cut_points = []
|
|
182
|
+
for marker in (end_marker, start_marker):
|
|
183
|
+
pos = final_raw.find(marker)
|
|
184
|
+
if pos != -1:
|
|
185
|
+
cut_points.append(pos)
|
|
186
|
+
if cut_points:
|
|
187
|
+
final_raw = final_raw[: min(cut_points)]
|
|
188
|
+
final_text = final_raw.strip()
|
|
189
|
+
|
|
190
|
+
return final_text, reasoning_text
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def should_extract_harmony_final(
|
|
194
|
+
*,
|
|
195
|
+
architecture_format: Optional[Mapping[str, Any]] = None,
|
|
196
|
+
model_capabilities: Optional[Mapping[str, Any]] = None,
|
|
197
|
+
) -> bool:
|
|
198
|
+
"""Return True when this model is expected to emit Harmony transcripts."""
|
|
199
|
+
msg_fmt = ""
|
|
200
|
+
resp_fmt = ""
|
|
201
|
+
try:
|
|
202
|
+
msg_fmt = str((architecture_format or {}).get("message_format") or "").strip().lower()
|
|
203
|
+
except Exception:
|
|
204
|
+
msg_fmt = ""
|
|
205
|
+
try:
|
|
206
|
+
resp_fmt = str((model_capabilities or {}).get("response_format") or "").strip().lower()
|
|
207
|
+
except Exception:
|
|
208
|
+
resp_fmt = ""
|
|
209
|
+
return msg_fmt == "harmony" or resp_fmt == "harmony"
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def maybe_extract_harmony_final_text(
|
|
213
|
+
text: str,
|
|
214
|
+
*,
|
|
215
|
+
architecture_format: Optional[Mapping[str, Any]] = None,
|
|
216
|
+
model_capabilities: Optional[Mapping[str, Any]] = None,
|
|
217
|
+
) -> Tuple[str, Optional[str]]:
|
|
218
|
+
"""If the model emits Harmony transcripts, return (clean_text, reasoning)."""
|
|
219
|
+
if not isinstance(text, str) or not text:
|
|
220
|
+
return text, None
|
|
221
|
+
|
|
222
|
+
if not should_extract_harmony_final(
|
|
223
|
+
architecture_format=architecture_format,
|
|
224
|
+
model_capabilities=model_capabilities,
|
|
225
|
+
):
|
|
226
|
+
return text, None
|
|
227
|
+
|
|
228
|
+
final_text, reasoning = split_harmony_response_text(text)
|
|
229
|
+
|
|
230
|
+
if final_text is None:
|
|
231
|
+
# If we only got analysis (e.g., truncated before final), strip the wrapper tokens
|
|
232
|
+
# so the caller doesn't see raw Harmony markup.
|
|
233
|
+
if isinstance(reasoning, str) and reasoning.strip() and text.lstrip().startswith("<|channel|>analysis"):
|
|
234
|
+
return reasoning.strip(), reasoning.strip()
|
|
235
|
+
return text, reasoning.strip() if isinstance(reasoning, str) and reasoning.strip() else None
|
|
236
|
+
|
|
237
|
+
return final_text, reasoning.strip() if isinstance(reasoning, str) and reasoning.strip() else None
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def extract_reasoning_from_message(
|
|
241
|
+
message: Mapping[str, Any],
|
|
242
|
+
*,
|
|
243
|
+
architecture_format: Optional[Mapping[str, Any]] = None,
|
|
244
|
+
model_capabilities: Optional[Mapping[str, Any]] = None,
|
|
245
|
+
) -> Optional[str]:
|
|
246
|
+
"""Extract reasoning from a provider message dict when present.
|
|
247
|
+
|
|
248
|
+
Supported keys:
|
|
249
|
+
- `reasoning` (OpenAI-compatible reasoning outputs)
|
|
250
|
+
- `thinking` (Ollama thinking outputs)
|
|
251
|
+
- `thinking_output_field` from assets (e.g., `reasoning_content` for some GLM models)
|
|
252
|
+
"""
|
|
253
|
+
if not isinstance(message, Mapping):
|
|
254
|
+
return None
|
|
255
|
+
|
|
256
|
+
for key in ("reasoning", "thinking"):
|
|
257
|
+
v = message.get(key)
|
|
258
|
+
if isinstance(v, str) and v.strip():
|
|
259
|
+
return v.strip()
|
|
260
|
+
|
|
261
|
+
thinking_output_field: Optional[str] = None
|
|
262
|
+
for src in (architecture_format, model_capabilities):
|
|
263
|
+
if not isinstance(src, Mapping):
|
|
264
|
+
continue
|
|
265
|
+
field = _coerce_str(src.get("thinking_output_field"))
|
|
266
|
+
if field is not None:
|
|
267
|
+
thinking_output_field = field
|
|
268
|
+
|
|
269
|
+
if thinking_output_field:
|
|
270
|
+
v = message.get(thinking_output_field)
|
|
271
|
+
if isinstance(v, str) and v.strip():
|
|
272
|
+
return v.strip()
|
|
273
|
+
|
|
274
|
+
return None
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def normalize_assistant_text(
|
|
278
|
+
text: str,
|
|
279
|
+
*,
|
|
280
|
+
architecture_format: Optional[Mapping[str, Any]] = None,
|
|
281
|
+
model_capabilities: Optional[Mapping[str, Any]] = None,
|
|
282
|
+
) -> Tuple[str, Optional[str]]:
|
|
283
|
+
"""Normalize provider output into (clean_text, reasoning).
|
|
284
|
+
|
|
285
|
+
Order:
|
|
286
|
+
1) Strip wrapper tokens (e.g., GLM box wrappers)
|
|
287
|
+
2) Extract Harmony final (GPT-OSS) into final text + reasoning
|
|
288
|
+
3) Extract inline <think>...</think> blocks when configured
|
|
289
|
+
"""
|
|
290
|
+
if not isinstance(text, str) or not text:
|
|
291
|
+
return text, None
|
|
292
|
+
|
|
293
|
+
cleaned = strip_output_wrappers(
|
|
294
|
+
text,
|
|
295
|
+
architecture_format=architecture_format,
|
|
296
|
+
model_capabilities=model_capabilities,
|
|
297
|
+
)
|
|
298
|
+
cleaned, reasoning_harmony = maybe_extract_harmony_final_text(
|
|
299
|
+
cleaned,
|
|
300
|
+
architecture_format=architecture_format,
|
|
301
|
+
model_capabilities=model_capabilities,
|
|
302
|
+
)
|
|
303
|
+
cleaned, reasoning_tags = strip_thinking_tags(
|
|
304
|
+
cleaned,
|
|
305
|
+
architecture_format=architecture_format,
|
|
306
|
+
model_capabilities=model_capabilities,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
parts = [r for r in (reasoning_harmony, reasoning_tags) if isinstance(r, str) and r.strip()]
|
|
310
|
+
reasoning: Optional[str] = None
|
|
311
|
+
if parts:
|
|
312
|
+
reasoning = "\n\n".join(parts).strip() or None
|
|
313
|
+
return cleaned, reasoning
|
|
@@ -157,11 +157,39 @@
|
|
|
157
157
|
"user_prefix": "<|im_start|>user\n",
|
|
158
158
|
"user_suffix": "<|im_end|>\n",
|
|
159
159
|
"assistant_prefix": "<|im_start|>assistant\n",
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
160
|
+
"assistant_suffix": "<|im_end|>\n",
|
|
161
|
+
"tool_format": "special_token",
|
|
162
|
+
"tool_prefix": "<|tool_call|>",
|
|
163
|
+
"thinking_tags": ["<think>", "</think>"],
|
|
164
|
+
"patterns": ["qwen3-0.6b", "qwen3-1.7b", "qwen3-4b", "qwen3-8b", "qwen3-14b", "qwen3-32b", "qwen3:", "qwen3-4b-thinking", "qwen3-4b-instruct"]
|
|
164
165
|
},
|
|
166
|
+
"sera": {
|
|
167
|
+
"description": "AllenAI's SERA architecture (Qwen3-based Open Coding Agents series)",
|
|
168
|
+
"message_format": "im_start_end",
|
|
169
|
+
"system_prefix": "<|im_start|>system\n",
|
|
170
|
+
"system_suffix": "<|im_end|>\n",
|
|
171
|
+
"user_prefix": "<|im_start|>user\n",
|
|
172
|
+
"user_suffix": "<|im_end|>\n",
|
|
173
|
+
"assistant_prefix": "<|im_start|>assistant\n",
|
|
174
|
+
"assistant_suffix": "<|im_end|>\n",
|
|
175
|
+
"tool_format": "xml",
|
|
176
|
+
"thinking_tags": ["<think>", "</think>"],
|
|
177
|
+
"patterns": [
|
|
178
|
+
"sera-",
|
|
179
|
+
"sera-32b",
|
|
180
|
+
"sera-32b-ga",
|
|
181
|
+
"sera-8b",
|
|
182
|
+
"sera-8b-ga",
|
|
183
|
+
"sera32b",
|
|
184
|
+
"sera32bga",
|
|
185
|
+
"sera8b",
|
|
186
|
+
"sera8bga",
|
|
187
|
+
"sera_32b",
|
|
188
|
+
"sera_32b_ga",
|
|
189
|
+
"sera_8b",
|
|
190
|
+
"sera_8b_ga"
|
|
191
|
+
]
|
|
192
|
+
},
|
|
165
193
|
"qwen2": {
|
|
166
194
|
"description": "Alibaba's Qwen2 architecture (June 2024)",
|
|
167
195
|
"message_format": "im_start_end",
|
|
@@ -328,8 +356,8 @@
|
|
|
328
356
|
"user_suffix": "\n",
|
|
329
357
|
"assistant_prefix": "Assistant: ",
|
|
330
358
|
"assistant_suffix": "\n",
|
|
331
|
-
"tool_format": "
|
|
332
|
-
"patterns": ["gemma-2b", "gemma-7b"]
|
|
359
|
+
"tool_format": "pythonic",
|
|
360
|
+
"patterns": ["gemma-2b", "gemma-7b", "gemma-2"]
|
|
333
361
|
},
|
|
334
362
|
"glm4v_moe": {
|
|
335
363
|
"description": "Zhipu AI's GLM-4.6V multimodal MoE architecture (May 2025)",
|
|
@@ -362,7 +390,8 @@
|
|
|
362
390
|
"assistant_suffix": "\n",
|
|
363
391
|
"tool_format": "special_token",
|
|
364
392
|
"tool_prefix": "<|tool_call|>",
|
|
365
|
-
"
|
|
393
|
+
"thinking_output_field": "reasoning_content",
|
|
394
|
+
"patterns": ["glm-4.7", "glm-4.7-flash", "glm-4.6", "glm-4.5", "glm-4.5-air"]
|
|
366
395
|
},
|
|
367
396
|
"glm4v": {
|
|
368
397
|
"description": "Zhipu AI's GLM-4V multimodal architecture (June 2024)",
|
|
@@ -531,7 +560,7 @@
|
|
|
531
560
|
"assistant_prefix": "Assistant: ",
|
|
532
561
|
"assistant_suffix": "\n",
|
|
533
562
|
"tool_format": "json",
|
|
534
|
-
"patterns": []
|
|
563
|
+
"patterns": ["__abstractcore_generic_fallback__"]
|
|
535
564
|
}
|
|
536
565
|
},
|
|
537
566
|
"message_formats": {
|
|
@@ -549,6 +578,7 @@
|
|
|
549
578
|
"pythonic": "Python function call syntax: [func(arg=val)]",
|
|
550
579
|
"json": "JSON object: {\"name\": \"func\", \"parameters\": {...}}",
|
|
551
580
|
"xml": "XML wrapped: <tool>...</tool>",
|
|
581
|
+
"glm_xml": "GLM XML-like tool call format (parsed as XML-wrapped tool calls)",
|
|
552
582
|
"special_token": "Special token format: <|tool_call|>{...}",
|
|
553
583
|
"native": "Native API support (OpenAI/Anthropic)",
|
|
554
584
|
"openai_functions": "OpenAI function calling API format",
|