1+ use crate :: engine:: universal:: { ShimmyUniversalEngine , UniversalModelSpec } ;
2+ use crate :: model_registry:: Registry ;
3+
4+ #[ cfg( test) ]
5+ mod issue_139_tests {
6+ use super :: * ;
7+
8+ #[ tokio:: test]
9+ async fn test_unicode_stop_token_handling ( ) {
10+ // Test that stop tokens are handled correctly with Unicode characters
11+ // This reproduces the issue where stop token truncation could split
12+ // multi-byte UTF-8 characters, causing FromUtf8Error
13+
14+ let registry = Registry :: with_discovery ( ) ;
15+ let engine = ShimmyUniversalEngine :: new ( ) ;
16+
17+ // Find a model to test with
18+ let models = registry. list_loaded ( ) ;
19+ if models. is_empty ( ) {
20+ println ! ( "No models loaded, skipping Unicode stop token test" ) ;
21+ return ;
22+ }
23+
24+ let model_name = & models[ 0 ] . name ;
25+ let spec = registry. load ( model_name) . expect ( "Failed to load model" ) ;
26+
27+ let model = engine. load ( & spec. into ( ) ) . await
28+ . expect ( "Failed to load model for Unicode test" ) ;
29+
30+ // Test prompt that should generate content that might include Unicode
31+ let prompt = "Write a short poem about emojis" ;
32+
33+ // Use a stop token that could appear near Unicode characters
34+ let mut opts = crate :: engine:: GenOptions :: default ( ) ;
35+ opts. stop_tokens = vec ! [ "." . to_string( ) ] ; // Stop at period
36+
37+ let result = model. generate ( prompt, opts, Some ( Box :: new ( |_token| {
38+ // Just receiving tokens should not cause a panic
39+ // The issue was that stop token truncation could create invalid UTF-8
40+ } ) ) ) . await ;
41+
42+ // The test should not panic with FromUtf8Error
43+ // If it completes successfully, we've fixed the Unicode issue
44+ match result {
45+ Ok ( generated_text) => {
46+ println ! ( "Unicode stop token test passed! Generated: {}" , generated_text) ;
47+ // Verify the result is valid UTF-8
48+ assert ! ( std:: str :: from_utf8( generated_text. as_bytes( ) ) . is_ok( ) ,
49+ "Generated text is not valid UTF-8" ) ;
50+ }
51+ Err ( e) => {
52+ // Check if this is the specific Unicode error we're trying to fix
53+ if e. to_string ( ) . contains ( "FromUtf8Error" ) ||
54+ e. to_string ( ) . contains ( "incomplete utf-8 byte sequence" ) {
55+ panic ! ( "Unicode stop token issue not fixed: {}" , e) ;
56+ } else {
57+ // Some other error, might be model-specific, don't fail the test
58+ println ! ( "Test skipped due to non-Unicode error: {}" , e) ;
59+ }
60+ }
61+ }
62+ }
63+
64+ #[ tokio:: test]
65+ async fn test_unicode_generation_with_callback ( ) {
66+ // Test that Unicode characters work correctly with token streaming callback
67+ let registry = Registry :: with_discovery ( ) ;
68+ let engine = ShimmyUniversalEngine :: new ( ) ;
69+
70+ let models = registry. list_loaded ( ) ;
71+ if models. is_empty ( ) {
72+ println ! ( "No models loaded, skipping Unicode callback test" ) ;
73+ return ;
74+ }
75+
76+ let model_name = & models[ 0 ] . name ;
77+ let spec = registry. load ( model_name) . expect ( "Failed to load model" ) ;
78+
79+ let model = engine. load ( & spec. into ( ) ) . await
80+ . expect ( "Failed to load model for callback test" ) ;
81+
82+ // Simple prompt that might generate Unicode
83+ let prompt = "Hello world 🌍" ;
84+
85+ let result = model. generate ( prompt, Default :: default ( ) , Some ( Box :: new ( |token| {
86+ // Verify each token received is valid UTF-8
87+ assert ! ( std:: str :: from_utf8( token. as_bytes( ) ) . is_ok( ) ,
88+ "Token received via callback is not valid UTF-8: {:?}" , token. as_bytes( ) ) ;
89+ } ) ) ) . await ;
90+
91+ match result {
92+ Ok ( _) => println ! ( "Unicode callback test passed!" ) ,
93+ Err ( e) => {
94+ if e. to_string ( ) . contains ( "FromUtf8Error" ) ||
95+ e. to_string ( ) . contains ( "incomplete utf-8 byte sequence" ) {
96+ panic ! ( "Unicode callback still causes panic: {}" , e) ;
97+ }
98+ // Other errors are acceptable for this test
99+ println ! ( "Callback test completed with non-Unicode error: {}" , e) ;
100+ }
101+ }
102+ }
103+ }
0 commit comments