Coverage for /home/admin/Documents/AI/applications/lexigram-dev/lexigram/experimental/ai/lexigram-ai-llm/src/lexigram/ai/llm/thinking/patterns.py: 100%
10 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 07:19 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 07:19 +0800
1"""Thinking block pattern definitions for LLM output extraction."""
3from __future__ import annotations
5from dataclasses import dataclass
8@dataclass(frozen=True)
9class ThinkingPattern:
10 """Pattern for detecting and extracting thinking blocks from LLM output.
12 Attributes:
13 name: Human-readable name of the pattern.
14 start_marker: Opening delimiter. May be empty string for bare-closing variant.
15 end_marker: Closing delimiter.
16 strip_end_marker: Whether the end marker itself should be removed from content.
17 """
19 name: str
20 start_marker: str
21 end_marker: str
22 strip_end_marker: bool
25# Ordered list of patterns (most specific first)
26# Ordered list of patterns (most specific first)
27THINKING_PATTERNS: list[ThinkingPattern] = [
28 # Gemma-4 channel format — handled by _extract_gemma_channel() in normalizer.py.
29 # Official format: <|channel>thought\n...thinking...\n<channel|>\n...response...
30 # The start_marker here is unused by the special-case handler but kept for
31 # documentation. The normalizer identifies this entry by name="gemma_channel".
32 ThinkingPattern(
33 name="gemma_channel",
34 start_marker="<|channel>thought",
35 end_marker="<channel|>",
36 strip_end_marker=True,
37 ),
38 # Qwen3 pipe-delimited format: <|think|>...</|think|>
39 ThinkingPattern(
40 name="qwen3_pipe",
41 start_marker="<|think|>",
42 end_marker="</|think|>",
43 strip_end_marker=True,
44 ),
45 # XML think tags (DeepSeek-R1, Qwen3): <think>...</think>
46 ThinkingPattern(
47 name="xml_think",
48 start_marker="<think>",
49 end_marker="</think>",
50 strip_end_marker=True,
51 ),
52 # Markdown fence: ```thinking\n...\n```
53 ThinkingPattern(
54 name="markdown_fence",
55 start_marker="```thinking",
56 end_marker="```",
57 strip_end_marker=True,
58 ),
59 # Bare closing tag (no opening): ...thinking text...</think>
60 ThinkingPattern(
61 name="bare_closing_tag",
62 start_marker="",
63 end_marker="</think>",
64 strip_end_marker=True,
65 ),
66]
68__all__ = ["THINKING_PATTERNS", "ThinkingPattern"]