Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
change
sglang
Commits
94959237
Unverified
Commit
94959237
authored
Aug 19, 2025
by
Simo Lin
Committed by
GitHub
Aug 19, 2025
Browse files
[router] add dsr1, kimi, and qwen reasoning parser (#9353)
parent
f4fafacc
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
463 additions
and
58 deletions
+463
-58
sgl-router/src/reasoning_parser/factory.rs
sgl-router/src/reasoning_parser/factory.rs
+12
-46
sgl-router/src/reasoning_parser/mod.rs
sgl-router/src/reasoning_parser/mod.rs
+4
-2
sgl-router/src/reasoning_parser/parsers/base.rs
sgl-router/src/reasoning_parser/parsers/base.rs
+9
-5
sgl-router/src/reasoning_parser/parsers/deepseek_r1.rs
sgl-router/src/reasoning_parser/parsers/deepseek_r1.rs
+112
-0
sgl-router/src/reasoning_parser/parsers/kimi.rs
sgl-router/src/reasoning_parser/parsers/kimi.rs
+137
-0
sgl-router/src/reasoning_parser/parsers/mod.rs
sgl-router/src/reasoning_parser/parsers/mod.rs
+6
-0
sgl-router/src/reasoning_parser/parsers/qwen3.rs
sgl-router/src/reasoning_parser/parsers/qwen3.rs
+178
-0
sgl-router/src/reasoning_parser/traits.rs
sgl-router/src/reasoning_parser/traits.rs
+5
-5
No files found.
sgl-router/src/reasoning_parser/factory.rs
View file @
94959237
...
@@ -3,7 +3,9 @@
...
@@ -3,7 +3,9 @@
use
std
::
collections
::
HashMap
;
use
std
::
collections
::
HashMap
;
use
std
::
sync
::{
Arc
,
RwLock
};
use
std
::
sync
::{
Arc
,
RwLock
};
use
crate
::
reasoning_parser
::
parsers
::
BaseReasoningParser
;
use
crate
::
reasoning_parser
::
parsers
::{
BaseReasoningParser
,
DeepSeekR1Parser
,
KimiParser
,
Qwen3Parser
,
QwenThinkingParser
,
};
use
crate
::
reasoning_parser
::
traits
::{
ParseError
,
ParserConfig
,
ReasoningParser
};
use
crate
::
reasoning_parser
::
traits
::{
ParseError
,
ParserConfig
,
ReasoningParser
};
/// Type alias for parser creator functions.
/// Type alias for parser creator functions.
...
@@ -82,53 +84,17 @@ impl ParserFactory {
...
@@ -82,53 +84,17 @@ impl ParserFactory {
Box
::
new
(
BaseReasoningParser
::
new
(
ParserConfig
::
default
()))
Box
::
new
(
BaseReasoningParser
::
new
(
ParserConfig
::
default
()))
});
});
// Register DeepSeek-R1 parser
// Register DeepSeek-R1 parser (starts with in_reasoning=true)
registry
.register_parser
(
"deepseek_r1"
,
||
{
registry
.register_parser
(
"deepseek_r1"
,
||
Box
::
new
(
DeepSeekR1Parser
::
new
()));
let
config
=
ParserConfig
{
think_start_token
:
"<think>"
.to_string
(),
think_end_token
:
"</think>"
.to_string
(),
force_reasoning
:
true
,
stream_reasoning
:
true
,
max_buffer_size
:
65536
,
};
Box
::
new
(
BaseReasoningParser
::
new
(
config
)
.with_model_type
(
"deepseek_r1"
.to_string
()))
});
// Register Qwen3 parser
// Register Qwen3 parser (starts with in_reasoning=false)
registry
.register_parser
(
"qwen3"
,
||
{
registry
.register_parser
(
"qwen3"
,
||
Box
::
new
(
Qwen3Parser
::
new
()));
let
config
=
ParserConfig
{
think_start_token
:
"<think>"
.to_string
(),
think_end_token
:
"</think>"
.to_string
(),
force_reasoning
:
false
,
stream_reasoning
:
true
,
max_buffer_size
:
65536
,
};
Box
::
new
(
BaseReasoningParser
::
new
(
config
)
.with_model_type
(
"qwen3"
.to_string
()))
});
// Register Qwen3-thinking parser (forced reasoning)
// Register Qwen3-thinking parser (starts with in_reasoning=true)
registry
.register_parser
(
"qwen3_thinking"
,
||
{
registry
.register_parser
(
"qwen3_thinking"
,
||
Box
::
new
(
QwenThinkingParser
::
new
()));
let
config
=
ParserConfig
{
think_start_token
:
"<think>"
.to_string
(),
think_end_token
:
"</think>"
.to_string
(),
force_reasoning
:
true
,
stream_reasoning
:
true
,
max_buffer_size
:
65536
,
};
Box
::
new
(
BaseReasoningParser
::
new
(
config
)
.with_model_type
(
"qwen3_thinking"
.to_string
()))
});
// Register Kimi parser with Unicode tokens
// Register Kimi parser with Unicode tokens (starts with in_reasoning=false)
registry
.register_parser
(
"kimi"
,
||
{
registry
.register_parser
(
"kimi"
,
||
Box
::
new
(
KimiParser
::
new
()));
let
config
=
ParserConfig
{
think_start_token
:
"◁think▷"
.to_string
(),
think_end_token
:
"◁/think▷"
.to_string
(),
force_reasoning
:
false
,
stream_reasoning
:
true
,
max_buffer_size
:
65536
,
};
Box
::
new
(
BaseReasoningParser
::
new
(
config
)
.with_model_type
(
"kimi"
.to_string
()))
});
// Register model patterns
// Register model patterns
registry
.register_pattern
(
"deepseek-r1"
,
"deepseek_r1"
);
registry
.register_pattern
(
"deepseek-r1"
,
"deepseek_r1"
);
...
@@ -155,9 +121,9 @@ impl ParserFactory {
...
@@ -155,9 +121,9 @@ impl ParserFactory {
let
config
=
ParserConfig
{
let
config
=
ParserConfig
{
think_start_token
:
""
.to_string
(),
think_start_token
:
""
.to_string
(),
think_end_token
:
""
.to_string
(),
think_end_token
:
""
.to_string
(),
force_reasoning
:
false
,
stream_reasoning
:
true
,
stream_reasoning
:
true
,
max_buffer_size
:
65536
,
max_buffer_size
:
65536
,
initial_in_reasoning
:
false
,
};
};
Ok
(
Box
::
new
(
Ok
(
Box
::
new
(
BaseReasoningParser
::
new
(
config
)
.with_model_type
(
"passthrough"
.to_string
()),
BaseReasoningParser
::
new
(
config
)
.with_model_type
(
"passthrough"
.to_string
()),
...
...
sgl-router/src/reasoning_parser/mod.rs
View file @
94959237
...
@@ -3,5 +3,7 @@ pub mod parsers;
...
@@ -3,5 +3,7 @@ pub mod parsers;
pub
mod
traits
;
pub
mod
traits
;
pub
use
factory
::{
ParserFactory
,
ParserRegistry
};
pub
use
factory
::{
ParserFactory
,
ParserRegistry
};
pub
use
parsers
::
BaseReasoningParser
;
pub
use
parsers
::{
pub
use
traits
::{
ParseError
,
ParserResult
,
ReasoningParser
};
BaseReasoningParser
,
DeepSeekR1Parser
,
KimiParser
,
Qwen3Parser
,
QwenThinkingParser
,
};
pub
use
traits
::{
ParseError
,
ParserConfig
,
ParserResult
,
ReasoningParser
};
sgl-router/src/reasoning_parser/parsers/base.rs
View file @
94959237
...
@@ -20,7 +20,7 @@ pub struct BaseReasoningParser {
...
@@ -20,7 +20,7 @@ pub struct BaseReasoningParser {
impl
BaseReasoningParser
{
impl
BaseReasoningParser
{
/// Create a new BaseReasoningParser with the given configuration.
/// Create a new BaseReasoningParser with the given configuration.
pub
fn
new
(
config
:
ParserConfig
)
->
Self
{
pub
fn
new
(
config
:
ParserConfig
)
->
Self
{
let
in_reasoning
=
config
.
force
_reasoning
;
let
in_reasoning
=
config
.
initial_in
_reasoning
;
Self
{
Self
{
config
,
config
,
in_reasoning
,
in_reasoning
,
...
@@ -179,7 +179,7 @@ impl ReasoningParser for BaseReasoningParser {
...
@@ -179,7 +179,7 @@ impl ReasoningParser for BaseReasoningParser {
}
}
fn
reset
(
&
mut
self
)
{
fn
reset
(
&
mut
self
)
{
self
.in_reasoning
=
self
.config.
force
_reasoning
;
self
.in_reasoning
=
self
.config.
initial_in
_reasoning
;
self
.buffer
.clear
();
self
.buffer
.clear
();
self
.stripped_think_start
=
false
;
self
.stripped_think_start
=
false
;
}
}
...
@@ -193,13 +193,16 @@ impl ReasoningParser for BaseReasoningParser {
...
@@ -193,13 +193,16 @@ impl ReasoningParser for BaseReasoningParser {
mod
tests
{
mod
tests
{
use
super
::
*
;
use
super
::
*
;
fn
create_test_parser
(
force_reasoning
:
bool
,
stream_reasoning
:
bool
)
->
BaseReasoningParser
{
fn
create_test_parser
(
initial_in_reasoning
:
bool
,
stream_reasoning
:
bool
,
)
->
BaseReasoningParser
{
let
config
=
ParserConfig
{
let
config
=
ParserConfig
{
think_start_token
:
"<think>"
.to_string
(),
think_start_token
:
"<think>"
.to_string
(),
think_end_token
:
"</think>"
.to_string
(),
think_end_token
:
"</think>"
.to_string
(),
force_reasoning
,
stream_reasoning
,
stream_reasoning
,
max_buffer_size
:
65536
,
max_buffer_size
:
65536
,
initial_in_reasoning
,
};
};
BaseReasoningParser
::
new
(
config
)
BaseReasoningParser
::
new
(
config
)
}
}
...
@@ -265,7 +268,8 @@ mod tests {
...
@@ -265,7 +268,8 @@ mod tests {
}
}
#[test]
#[test]
fn
test_force_reasoning_mode
()
{
fn
test_initial_in_reasoning_true
()
{
// Parser starts with in_reasoning=true (like DeepSeek-R1)
let
mut
parser
=
create_test_parser
(
true
,
true
);
let
mut
parser
=
create_test_parser
(
true
,
true
);
let
result
=
parser
let
result
=
parser
.detect_and_parse_reasoning
(
"no think tags here"
)
.detect_and_parse_reasoning
(
"no think tags here"
)
...
...
sgl-router/src/reasoning_parser/parsers/deepseek_r1.rs
0 → 100644
View file @
94959237
// DeepSeek-R1 specific reasoning parser.
// This parser starts with in_reasoning=true, assuming all text is reasoning
// until an end token is encountered.
use
crate
::
reasoning_parser
::
parsers
::
BaseReasoningParser
;
use
crate
::
reasoning_parser
::
traits
::{
ParseError
,
ParserConfig
,
ParserResult
,
ReasoningParser
};
/// DeepSeek-R1 reasoning parser.
///
/// This parser assumes reasoning from the start of text (in_reasoning=true)
/// and uses <think> and </think> tokens.
pub
struct
DeepSeekR1Parser
{
base
:
BaseReasoningParser
,
}
impl
DeepSeekR1Parser
{
/// Create a new DeepSeek-R1 parser.
pub
fn
new
()
->
Self
{
let
config
=
ParserConfig
{
think_start_token
:
"<think>"
.to_string
(),
think_end_token
:
"</think>"
.to_string
(),
stream_reasoning
:
true
,
max_buffer_size
:
65536
,
initial_in_reasoning
:
true
,
// Always starts with reasoning
};
Self
{
base
:
BaseReasoningParser
::
new
(
config
)
.with_model_type
(
"deepseek_r1"
.to_string
()),
}
}
}
impl
Default
for
DeepSeekR1Parser
{
fn
default
()
->
Self
{
Self
::
new
()
}
}
impl
ReasoningParser
for
DeepSeekR1Parser
{
fn
detect_and_parse_reasoning
(
&
mut
self
,
text
:
&
str
)
->
Result
<
ParserResult
,
ParseError
>
{
self
.base
.detect_and_parse_reasoning
(
text
)
}
fn
parse_reasoning_streaming_incremental
(
&
mut
self
,
text
:
&
str
,
)
->
Result
<
ParserResult
,
ParseError
>
{
self
.base
.parse_reasoning_streaming_incremental
(
text
)
}
fn
reset
(
&
mut
self
)
{
self
.base
.reset
()
}
fn
model_type
(
&
self
)
->
&
str
{
self
.base
.model_type
()
}
}
#[cfg(test)]
mod
tests
{
use
super
::
*
;
#[test]
fn
test_deepseek_r1_initial_state
()
{
let
mut
parser
=
DeepSeekR1Parser
::
new
();
// Should treat text as reasoning even without start token
let
result
=
parser
.detect_and_parse_reasoning
(
"This is reasoning content"
)
.unwrap
();
assert_eq!
(
result
.normal_text
,
""
);
assert_eq!
(
result
.reasoning_text
,
"This is reasoning content"
);
}
#[test]
fn
test_deepseek_r1_with_end_token
()
{
let
mut
parser
=
DeepSeekR1Parser
::
new
();
// Should extract reasoning until end token
let
result
=
parser
.detect_and_parse_reasoning
(
"reasoning content</think>normal content"
)
.unwrap
();
assert_eq!
(
result
.normal_text
,
"normal content"
);
assert_eq!
(
result
.reasoning_text
,
"reasoning content"
);
}
#[test]
fn
test_deepseek_r1_streaming
()
{
let
mut
parser
=
DeepSeekR1Parser
::
new
();
// First chunk - all reasoning
let
result1
=
parser
.parse_reasoning_streaming_incremental
(
"thinking about"
)
.unwrap
();
assert_eq!
(
result1
.reasoning_text
,
"thinking about"
);
assert_eq!
(
result1
.normal_text
,
""
);
// Second chunk - ends reasoning
let
result2
=
parser
.parse_reasoning_streaming_incremental
(
" the problem</think>answer"
)
.unwrap
();
assert_eq!
(
result2
.reasoning_text
,
"the problem"
);
// Text is trimmed
assert_eq!
(
result2
.normal_text
,
"answer"
);
}
#[test]
fn
test_model_type
()
{
let
parser
=
DeepSeekR1Parser
::
new
();
assert_eq!
(
parser
.model_type
(),
"deepseek_r1"
);
}
}
sgl-router/src/reasoning_parser/parsers/kimi.rs
0 → 100644
View file @
94959237
// Kimi specific reasoning parser.
// This parser uses Unicode tokens and starts with in_reasoning=false.
use
crate
::
reasoning_parser
::
parsers
::
BaseReasoningParser
;
use
crate
::
reasoning_parser
::
traits
::{
ParseError
,
ParserConfig
,
ParserResult
,
ReasoningParser
};
/// Kimi reasoning parser.
///
/// This parser uses Unicode tokens (◁think▷ and ◁/think▷) and requires
/// explicit start tokens to enter reasoning mode.
pub
struct
KimiParser
{
base
:
BaseReasoningParser
,
}
impl
KimiParser
{
/// Create a new Kimi parser.
pub
fn
new
()
->
Self
{
let
config
=
ParserConfig
{
think_start_token
:
"◁think▷"
.to_string
(),
think_end_token
:
"◁/think▷"
.to_string
(),
stream_reasoning
:
true
,
max_buffer_size
:
65536
,
initial_in_reasoning
:
false
,
// Requires explicit start token
};
Self
{
base
:
BaseReasoningParser
::
new
(
config
)
.with_model_type
(
"kimi"
.to_string
()),
}
}
}
impl
Default
for
KimiParser
{
fn
default
()
->
Self
{
Self
::
new
()
}
}
impl
ReasoningParser
for
KimiParser
{
fn
detect_and_parse_reasoning
(
&
mut
self
,
text
:
&
str
)
->
Result
<
ParserResult
,
ParseError
>
{
self
.base
.detect_and_parse_reasoning
(
text
)
}
fn
parse_reasoning_streaming_incremental
(
&
mut
self
,
text
:
&
str
,
)
->
Result
<
ParserResult
,
ParseError
>
{
self
.base
.parse_reasoning_streaming_incremental
(
text
)
}
fn
reset
(
&
mut
self
)
{
self
.base
.reset
()
}
fn
model_type
(
&
self
)
->
&
str
{
self
.base
.model_type
()
}
}
#[cfg(test)]
mod
tests
{
use
super
::
*
;
#[test]
fn
test_kimi_initial_state
()
{
let
mut
parser
=
KimiParser
::
new
();
// Should NOT treat text as reasoning without start token
let
result
=
parser
.detect_and_parse_reasoning
(
"This is normal content"
)
.unwrap
();
assert_eq!
(
result
.normal_text
,
"This is normal content"
);
assert_eq!
(
result
.reasoning_text
,
""
);
}
#[test]
fn
test_kimi_with_unicode_tokens
()
{
let
mut
parser
=
KimiParser
::
new
();
// Should extract reasoning with Unicode tokens
let
result
=
parser
.detect_and_parse_reasoning
(
"◁think▷reasoning content◁/think▷answer"
)
.unwrap
();
assert_eq!
(
result
.normal_text
,
"answer"
);
assert_eq!
(
result
.reasoning_text
,
"reasoning content"
);
}
#[test]
fn
test_kimi_partial_unicode
()
{
let
mut
parser
=
KimiParser
::
new
();
// Test partial Unicode token buffering
let
result1
=
parser
.parse_reasoning_streaming_incremental
(
"◁thi"
)
.unwrap
();
assert_eq!
(
result1
.normal_text
,
""
);
assert_eq!
(
result1
.reasoning_text
,
""
);
// Complete the token
let
result2
=
parser
.parse_reasoning_streaming_incremental
(
"nk▷reasoning"
)
.unwrap
();
assert_eq!
(
result2
.normal_text
,
""
);
assert_eq!
(
result2
.reasoning_text
,
"reasoning"
);
}
#[test]
fn
test_kimi_streaming
()
{
let
mut
parser
=
KimiParser
::
new
();
// Normal text first
let
result1
=
parser
.parse_reasoning_streaming_incremental
(
"normal "
)
.unwrap
();
assert_eq!
(
result1
.normal_text
,
"normal "
);
assert_eq!
(
result1
.reasoning_text
,
""
);
// Enter reasoning with Unicode token
let
result2
=
parser
.parse_reasoning_streaming_incremental
(
"◁think▷thinking"
)
.unwrap
();
assert_eq!
(
result2
.normal_text
,
""
);
assert_eq!
(
result2
.reasoning_text
,
"thinking"
);
// Exit reasoning
let
result3
=
parser
.parse_reasoning_streaming_incremental
(
"◁/think▷answer"
)
.unwrap
();
assert_eq!
(
result3
.normal_text
,
"answer"
);
assert_eq!
(
result3
.reasoning_text
,
""
);
// Already returned in stream mode
}
#[test]
fn
test_model_type
()
{
let
parser
=
KimiParser
::
new
();
assert_eq!
(
parser
.model_type
(),
"kimi"
);
}
}
sgl-router/src/reasoning_parser/parsers/mod.rs
View file @
94959237
pub
mod
base
;
pub
mod
base
;
pub
mod
deepseek_r1
;
pub
mod
kimi
;
pub
mod
qwen3
;
pub
use
base
::
BaseReasoningParser
;
pub
use
base
::
BaseReasoningParser
;
pub
use
deepseek_r1
::
DeepSeekR1Parser
;
pub
use
kimi
::
KimiParser
;
pub
use
qwen3
::{
Qwen3Parser
,
QwenThinkingParser
};
sgl-router/src/reasoning_parser/parsers/qwen3.rs
0 → 100644
View file @
94959237
// Qwen3 specific reasoning parser.
// This parser starts with in_reasoning=false, requiring an explicit
// start token to enter reasoning mode.
use
crate
::
reasoning_parser
::
parsers
::
BaseReasoningParser
;
use
crate
::
reasoning_parser
::
traits
::{
ParseError
,
ParserConfig
,
ParserResult
,
ReasoningParser
};
/// Qwen3 reasoning parser.
///
/// This parser requires explicit <think> tokens to enter reasoning mode
/// (in_reasoning=false initially).
pub
struct
Qwen3Parser
{
base
:
BaseReasoningParser
,
}
impl
Qwen3Parser
{
/// Create a new Qwen3 parser.
pub
fn
new
()
->
Self
{
let
config
=
ParserConfig
{
think_start_token
:
"<think>"
.to_string
(),
think_end_token
:
"</think>"
.to_string
(),
stream_reasoning
:
true
,
max_buffer_size
:
65536
,
initial_in_reasoning
:
false
,
// Requires explicit start token
};
Self
{
base
:
BaseReasoningParser
::
new
(
config
)
.with_model_type
(
"qwen3"
.to_string
()),
}
}
}
impl
Default
for
Qwen3Parser
{
fn
default
()
->
Self
{
Self
::
new
()
}
}
impl
ReasoningParser
for
Qwen3Parser
{
fn
detect_and_parse_reasoning
(
&
mut
self
,
text
:
&
str
)
->
Result
<
ParserResult
,
ParseError
>
{
self
.base
.detect_and_parse_reasoning
(
text
)
}
fn
parse_reasoning_streaming_incremental
(
&
mut
self
,
text
:
&
str
,
)
->
Result
<
ParserResult
,
ParseError
>
{
self
.base
.parse_reasoning_streaming_incremental
(
text
)
}
fn
reset
(
&
mut
self
)
{
self
.base
.reset
()
}
fn
model_type
(
&
self
)
->
&
str
{
self
.base
.model_type
()
}
}
/// QwenThinking parser - variant that assumes reasoning from start.
///
/// This is for qwen*thinking models that behave like DeepSeek-R1.
pub
struct
QwenThinkingParser
{
base
:
BaseReasoningParser
,
}
impl
QwenThinkingParser
{
/// Create a new QwenThinking parser.
pub
fn
new
()
->
Self
{
let
config
=
ParserConfig
{
think_start_token
:
"<think>"
.to_string
(),
think_end_token
:
"</think>"
.to_string
(),
stream_reasoning
:
true
,
max_buffer_size
:
65536
,
initial_in_reasoning
:
true
,
// Assumes reasoning from start
};
Self
{
base
:
BaseReasoningParser
::
new
(
config
)
.with_model_type
(
"qwen_thinking"
.to_string
()),
}
}
}
impl
Default
for
QwenThinkingParser
{
fn
default
()
->
Self
{
Self
::
new
()
}
}
impl
ReasoningParser
for
QwenThinkingParser
{
fn
detect_and_parse_reasoning
(
&
mut
self
,
text
:
&
str
)
->
Result
<
ParserResult
,
ParseError
>
{
self
.base
.detect_and_parse_reasoning
(
text
)
}
fn
parse_reasoning_streaming_incremental
(
&
mut
self
,
text
:
&
str
,
)
->
Result
<
ParserResult
,
ParseError
>
{
self
.base
.parse_reasoning_streaming_incremental
(
text
)
}
fn
reset
(
&
mut
self
)
{
self
.base
.reset
()
}
fn
model_type
(
&
self
)
->
&
str
{
self
.base
.model_type
()
}
}
#[cfg(test)]
mod
tests
{
use
super
::
*
;
#[test]
fn
test_qwen3_initial_state
()
{
let
mut
parser
=
Qwen3Parser
::
new
();
// Should NOT treat text as reasoning without start token
let
result
=
parser
.detect_and_parse_reasoning
(
"This is normal content"
)
.unwrap
();
assert_eq!
(
result
.normal_text
,
"This is normal content"
);
assert_eq!
(
result
.reasoning_text
,
""
);
}
#[test]
fn
test_qwen3_with_tokens
()
{
let
mut
parser
=
Qwen3Parser
::
new
();
// Should extract reasoning with proper tokens
let
result
=
parser
.detect_and_parse_reasoning
(
"<think>reasoning</think>answer"
)
.unwrap
();
assert_eq!
(
result
.normal_text
,
"answer"
);
assert_eq!
(
result
.reasoning_text
,
"reasoning"
);
}
#[test]
fn
test_qwen_thinking_initial_state
()
{
let
mut
parser
=
QwenThinkingParser
::
new
();
// Should treat text as reasoning even without start token
let
result
=
parser
.detect_and_parse_reasoning
(
"This is reasoning content"
)
.unwrap
();
assert_eq!
(
result
.normal_text
,
""
);
assert_eq!
(
result
.reasoning_text
,
"This is reasoning content"
);
}
#[test]
fn
test_qwen3_streaming
()
{
let
mut
parser
=
Qwen3Parser
::
new
();
// First chunk - normal text (no start token yet)
let
result1
=
parser
.parse_reasoning_streaming_incremental
(
"normal text "
)
.unwrap
();
assert_eq!
(
result1
.normal_text
,
"normal text "
);
assert_eq!
(
result1
.reasoning_text
,
""
);
// Second chunk - enters reasoning
let
result2
=
parser
.parse_reasoning_streaming_incremental
(
"<think>reasoning"
)
.unwrap
();
assert_eq!
(
result2
.normal_text
,
""
);
assert_eq!
(
result2
.reasoning_text
,
"reasoning"
);
}
#[test]
fn
test_model_types
()
{
let
qwen3
=
Qwen3Parser
::
new
();
assert_eq!
(
qwen3
.model_type
(),
"qwen3"
);
let
qwen_thinking
=
QwenThinkingParser
::
new
();
assert_eq!
(
qwen_thinking
.model_type
(),
"qwen_thinking"
);
}
}
sgl-router/src/reasoning_parser/traits.rs
View file @
94959237
...
@@ -96,14 +96,14 @@ pub struct ParserConfig {
...
@@ -96,14 +96,14 @@ pub struct ParserConfig {
/// The token that marks the end of reasoning content.
/// The token that marks the end of reasoning content.
pub
think_end_token
:
String
,
pub
think_end_token
:
String
,
/// Whether to force all text to be treated as reasoning.
pub
force_reasoning
:
bool
,
/// Whether to stream reasoning content as it arrives.
/// Whether to stream reasoning content as it arrives.
pub
stream_reasoning
:
bool
,
pub
stream_reasoning
:
bool
,
/// Maximum buffer size in bytes.
/// Maximum buffer size in bytes.
pub
max_buffer_size
:
usize
,
pub
max_buffer_size
:
usize
,
/// Initial state for in_reasoning flag (fixed per parser type).
pub
initial_in_reasoning
:
bool
,
}
}
impl
Default
for
ParserConfig
{
impl
Default
for
ParserConfig
{
...
@@ -111,9 +111,9 @@ impl Default for ParserConfig {
...
@@ -111,9 +111,9 @@ impl Default for ParserConfig {
Self
{
Self
{
think_start_token
:
"<think>"
.to_string
(),
think_start_token
:
"<think>"
.to_string
(),
think_end_token
:
"</think>"
.to_string
(),
think_end_token
:
"</think>"
.to_string
(),
force_reasoning
:
false
,
stream_reasoning
:
true
,
stream_reasoning
:
true
,
max_buffer_size
:
65536
,
// 64KB default
max_buffer_size
:
65536
,
// 64KB default
initial_in_reasoning
:
false
,
// Default to false (explicit reasoning)
}
}
}
}
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment