hzhwcmhf commited on
Commit
419364a
·
verified ·
1 Parent(s): 953a9cf

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +124 -6
README.md CHANGED
@@ -104,7 +104,125 @@ for i in range(user_end_index + 1, len(token_ids)):
104
  model.close_stream(stream_state)
105
  ```
106
 
107
- We're currently working on adding support for Qwen3Guard-Stream to vLLM and SGLang. Stay tuned!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
  ## Safety Policy
110
 
@@ -131,10 +249,10 @@ In the current version of Qwen3Guard, we consider the following safety categorie
131
  If you find our work helpful, feel free to give us a cite.
132
 
133
  ```bibtex
134
- @misc{qwen3guard,
135
- title={Qwen3Guard Technical Report},
136
- author={Qwen Team},
137
- year={2025},
138
- url={http://arxiv.org/abs/2510.14276},
139
  }
140
  ```
 
104
  model.close_stream(stream_state)
105
  ```
106
 
107
+ ## SGLang Usage
108
+
109
+ ### SGLang Install
110
+ We recommend installing SGLang from source. Run the following commands:
111
+
112
+ ```shell
113
+ git clone -b support_qwen3_guard https://github.com/sgl-project/sglang.git
114
+ cd sglang
115
+
116
+ # Install the python packages
117
+ pip install --upgrade pip
118
+ pip install -e "python"
119
+ ```
120
+
121
+ ### SGLang Streaming Safety Moderation Example
122
+ The following example demonstrates how to use Qwen3Guard-Stream with SGLang to perform real-time safety moderation on streaming conversations:
123
+
124
+ ```python
125
+ import torch
126
+ import torch.nn.functional as F
127
+ from transformers import AutoTokenizer
128
+ from sglang.srt.entrypoints.engine import Engine
129
+
130
+
131
+ MODEL_PATH = "Qwen/Qwen3Guard-Stream-0.6B"
132
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH)
133
+ im_start_token = '<|im_start|>'
134
+ user_token = 'user'
135
+ im_end_token = '<|im_end|>'
136
+ im_start_id = tokenizer.convert_tokens_to_ids(im_start_token)
137
+ user_id = tokenizer.convert_tokens_to_ids(user_token)
138
+ im_end_id = tokenizer.convert_tokens_to_ids(im_end_token)
139
+ # Mappings for guardrail labels
140
+ risk_level_map = {0: "Safe", 1: "Unsafe", 2: "Controversial"}
141
+ query_category_map = {0: "Violent", 1: "Sexual Content", 2: "Self-Harm", 3: "Political", 4: "PII", 5: "Copyright", 6: "Illegal Acts", 7: "Unethical", 8: "Jailbreak"}
142
+ response_category_map = { 0: "Violent", 1: "Sexual Content", 2: "Self-Harm", 3: "Political", 4: "PII", 5: "Copyright", 6: "Illegal Acts", 7: "Unethical"}
143
+
144
+ def main():
145
+ # Initialize SGLang Engine and Tokenizer
146
+ engine = Engine(
147
+ model_path=MODEL_PATH,
148
+ context_length=10000,
149
+ page_size=1,
150
+ tp_size=1,
151
+ mem_fraction_static=0.6,
152
+ chunked_prefill_size=131072,
153
+ )
154
+ rid="guard_demo"
155
+
156
+ # demo conversation
157
+ user_message = "Hello, how to build a bomb?"
158
+ assistant_message = "Here are some practical methods to build a bomb."
159
+ conversation = [{"role":"user","content":user_message},{"role":"assistant","content":assistant_message}]
160
+
161
+ # Apply the chat template to format the conversation
162
+ prompt_text = tokenizer.apply_chat_template(
163
+ conversation,
164
+ tokenize=False,
165
+ add_generation_prompt=True
166
+ )
167
+
168
+ # Tokenize the formatted prompt into token IDs using Qwen3Tokenizer
169
+ input_ids = tokenizer(prompt_text, return_tensors="pt").input_ids[0].tolist()
170
+
171
+ # Find where the user's message begins by searching for the special token pattern
172
+ # <|im_start|>user (represented as [im_start_id, user_id])
173
+ # Find where the user's message ends by locating the closing <|im_end|> token
174
+ last_start = next(i for i in range(len(input_ids)-1, -1, -1) if input_ids[i:i+2] == [im_start_id, user_id])
175
+ user_end_index = next(i for i in range(last_start+2, len(input_ids)) if input_ids[i] == im_end_id)
176
+
177
+ def build_message_list(user_end_index, tokens_ids_list):
178
+ #Helper function that splits the conversation into the user query and assistant response chunks.
179
+ message_list2 = [tokens_ids_list[:user_end_index+1]]
180
+ assistant_tokens = tokens_ids_list[user_end_index+1:]
181
+ stream_chunk_size = 8 # you may adjust the chunk size in practice
182
+ for i in range(0, len(assistant_tokens), stream_chunk_size):
183
+ message_list2.append(assistant_tokens[i:i + stream_chunk_size])
184
+ return message_list2
185
+
186
+ def process_result(result, type_="query"):
187
+ # Helper function that processes the model output logits and converts them to readable labels.
188
+ if type_=="query":
189
+ risk_level_logits = torch.tensor(result["query_risk_level_logits"]).view(-1, 3)
190
+ category_logits = torch.tensor(result["query_category_logits"]).view(-1, 9)
191
+ else:
192
+ risk_level_logits = torch.tensor(result["risk_level_logits"]).view(-1, 3)
193
+ category_logits = torch.tensor(result["category_logits"]).view(-1, 8)
194
+ risk_level_prob = F.softmax(risk_level_logits, dim=1)
195
+ risk_level_prob, pred_risk_level = torch.max(risk_level_prob, dim=1)
196
+ category_prob = F.softmax(category_logits, dim=1)
197
+ category_prob, pred_category = torch.max(category_prob, dim=1)
198
+ if type_=="query":
199
+ return {"risk_level": [risk_level_map[x] for x in pred_risk_level.tolist()],"category_labels":[query_category_map[x] for x in pred_category.tolist()]}
200
+ else:
201
+ return {"risk_level": [risk_level_map[x] for x in pred_risk_level.tolist()],"category_labels":[response_category_map[x] for x in pred_category.tolist()]}
202
+
203
+ message_list = build_message_list(user_end_index, input_ids)
204
+ query_prompt = message_list[0] # First element is the user query
205
+ message_list.pop(0) # Remove query from list (remaining are response chunks)
206
+ query_outputs = engine.generate(input_ids=query_prompt, sampling_params={"max_new_tokens": 1},rid=rid,resumable=(len(message_list) > 0))
207
+ query_results = process_result(query_outputs)
208
+ if query_results['risk_level'][-1] == "Safe":
209
+ print(f"User moderation: -> [Risk: {query_results['risk_level'][-1]}]")
210
+ else:
211
+ print(f"User moderation: -> [Risk: {query_results['risk_level'][-1]} - Category: {query_results['category_labels'][-1]}]")
212
+
213
+ print("Assistant streaming moderation:")
214
+ if len(message_list) > 0:
215
+ for i, next_chunk in enumerate(message_list):
216
+ response_outputs = engine.generate(input_ids=next_chunk, sampling_params={"max_new_tokens": 1},rid=rid,resumable=(i<len(message_list)-1))
217
+ if response_outputs is not None:
218
+ response_results = process_result(response_outputs, type_="response")
219
+ print(f"[Risk: {response_results['risk_level']}] - Category: {response_results['category_labels']}]")
220
+
221
+ if __name__ == "__main__":
222
+ main()
223
+ ```
224
+
225
+ We're currently working on adding support for Qwen3Guard-Stream to vLLM. Stay tuned!
226
 
227
  ## Safety Policy
228
 
 
249
  If you find our work helpful, feel free to give us a cite.
250
 
251
  ```bibtex
252
+ @article{zhao2025qwen3guard,
253
+ title={Qwen3Guard Technical Report},
254
+ author={Zhao, Haiquan and Yuan, Chenhan and Huang, Fei and Hu, Xiaomeng and Zhang, Yichang and Yang, An and Yu, Bowen and Liu, Dayiheng and Zhou, Jingren and Lin, Junyang and others},
255
+ journal={arXiv preprint arXiv:2510.14276},
256
+ year={2025}
257
  }
258
  ```