Andhs commited on
Commit
fddaa41
·
verified ·
1 Parent(s): edbd3b6

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +545 -467
app.py CHANGED
@@ -1,467 +1,545 @@
1
- import sys
2
- import types
3
- import os
4
- import math
5
- import copy
6
- import json
7
- import torch
8
- import torch.nn.functional as F
9
- from flask import Flask, request, jsonify, Response
10
- from transformers import AutoTokenizer, AutoModelForMaskedLM, AutoModelForCausalLM
11
-
12
- # 1. Environment Parsing & Architecture Strategy Mapping
13
- MODEL_NAME = os.getenv("MODEL_NAME", "dllm-hub/Qwen3-0.6B-diffusion-bd3lm-v0.1")
14
- IS_DIFFUSION = "diffusion" in MODEL_NAME.lower()
15
-
16
- # Dynamic initialization layer targeting Diffusion Language Models
17
- if IS_DIFFUSION:
18
- try:
19
- import dllm.utils
20
- import dllm.pipelines
21
- import dllm.data
22
- import dllm.core
23
- except ImportError:
24
- pass
25
- if 'dllm' not in sys.modules:
26
- dllm_mock = types.ModuleType('dllm')
27
- dllm_mock.core = sys.modules.get('dllm.core')
28
- dllm_mock.data = sys.modules.get('dllm.data')
29
- dllm_mock.pipelines = sys.modules.get('dllm.pipelines')
30
- dllm_mock.utils = sys.modules.get('dllm.utils')
31
- sys.modules['dllm'] = dllm_mock
32
-
33
- app = Flask(__name__)
34
- model = None
35
- tokenizer = None
36
- device = None
37
-
38
- # ==========================================================
39
- # SYSTEM WORKSPACE PIPELINES: CORE DIFFUSION SAMPLING LOOPS
40
- # ==========================================================
41
- def add_gumbel_noise(logits, temperature):
42
- if temperature == 0:
43
- return logits
44
- logits = logits.to(torch.float64)
45
- noise = torch.rand_like(logits, dtype=torch.float64)
46
- g = (-torch.log(noise)) ** temperature
47
- return logits.exp() / g
48
-
49
- def get_num_transfer_tokens(mask_index, steps):
50
- mask_num = mask_index.sum(dim=1, keepdim=True)
51
- base = mask_num // steps
52
- rem = mask_num % steps
53
- out = torch.zeros(mask_num.size(0), steps, device=mask_index.device, dtype=torch.long) + base
54
- for i in range(mask_num.size(0)):
55
- out[i, : rem[i]] += 1
56
- return out
57
-
58
- def build_staircase_attention_mask(x, block_size, pad_id):
59
- B, T = x.shape
60
- device = x.device
61
- valid = x != pad_id
62
- pos_raw = torch.cumsum(valid.long(), dim=-1)
63
- position_ids = torch.where(valid, pos_raw - 1, torch.zeros_like(pos_raw)).long()
64
- col = torch.arange(T, device=device)
65
- block_ids = (col // block_size).view(1, T).expand(B, T)
66
- block_ids = torch.where(valid, block_ids, torch.full_like(block_ids, -1))
67
- q = block_ids.view(B, 1, T, 1)
68
- k = block_ids.view(B, 1, 1, T)
69
- attn = (k <= q) & (q >= 0) & (k >= 0)
70
- return attn, position_ids
71
-
72
- def diffusion_step_block(logits, x_block, mask_block, num_transfer, temperature, remasking):
73
- B, L, _ = logits.shape
74
- if not mask_block.any():
75
- return x_block
76
- noisy = add_gumbel_noise(logits, temperature)
77
- x0 = noisy.argmax(dim=-1)
78
- if remasking == "low_confidence":
79
- p = F.softmax(logits, dim=-1)
80
- conf = p.gather(-1, x0.unsqueeze(-1)).squeeze(-1)
81
- elif remasking == "random":
82
- conf = torch.rand((B, L), device=logits.device)
83
- else:
84
- raise ValueError(remasking)
85
- x0 = torch.where(mask_block, x0, x_block)
86
- neg_inf = torch.full_like(conf, -float("inf"))
87
- conf = torch.where(mask_block, conf, neg_inf)
88
- commit = torch.zeros_like(x_block, dtype=torch.bool)
89
- for i in range(B):
90
- k = int(num_transfer[i].item())
91
- if k > 0:
92
- valid = (conf[i] > -float("inf")).sum().item()
93
- k = min(k, valid)
94
- _, idx = torch.topk(conf[i], k)
95
- commit[i, idx] = True
96
- out = x_block.clone()
97
- out[commit] = x0[commit]
98
- return out
99
-
100
- @torch.no_grad()
101
- def generate(model, tokenizer, prompt, steps=128, max_new_tokens=128, block_size=32, temperature=0.0, cfg_scale=0.0, remasking="low_confidence", capture_interval=0):
102
- device = model.device
103
- mask_id = tokenizer.mask_token_id
104
- pad_id = tokenizer.pad_token_id
105
- if pad_id is None:
106
- pad_id = tokenizer.eos_token_id if tokenizer.eos_token_id is not None else tokenizer.mask_token_id
107
- if isinstance(prompt, torch.Tensor):
108
- x = prompt.to(device).long()
109
- else:
110
- if isinstance(prompt[0], (list, tuple)):
111
- max_len = max(len(p) for p in prompt)
112
- x = torch.full((len(prompt), max_len), pad_id, device=device, dtype=torch.long)
113
- for i, p in enumerate(prompt):
114
- x[i, : len(p)] = torch.tensor(p, device=device)
115
- else:
116
- x = torch.tensor(prompt, device=device).long()
117
- if x.dim() == 1:
118
- x = x.unsqueeze(0)
119
- B = x.size(0)
120
- finished = torch.zeros(B, dtype=torch.bool, device=device)
121
- num_blocks = math.ceil(max_new_tokens / block_size)
122
- steps_per_block = math.ceil(steps / num_blocks)
123
- generated = 0
124
- intermediates = []
125
- total_step = 0
126
- while generated < max_new_tokens:
127
- if finished.all():
128
- break
129
- T_prefix = x.size(1)
130
- offset = T_prefix % block_size
131
- room = block_size if offset == 0 else block_size - offset
132
- cur_len = min(room, max_new_tokens - generated)
133
- if cur_len <= 0:
134
- break
135
- attn_pfx, pos_pfx = build_staircase_attention_mask(x, block_size, pad_id)
136
- out = model(x, attention_mask=attn_pfx, position_ids=pos_pfx, use_cache=True)
137
- cond_past = out.past_key_values
138
- if cfg_scale > 0:
139
- un_x = x.clone()
140
- un_x[:] = mask_id
141
- out_un = model(un_x, attention_mask=attn_pfx, position_ids=pos_pfx, use_cache=True)
142
- uncond_past = out_un.past_key_values
143
- else:
144
- uncond_past = None
145
- block = torch.full((B, cur_len), mask_id, device=device, dtype=torch.long)
146
- block[finished] = pad_id
147
- x = torch.cat([x, block], dim=1)
148
- T_total = x.size(1)
149
- block_mask = x[:, -cur_len:] == mask_id
150
- num_transfer = get_num_transfer_tokens(block_mask, steps_per_block)
151
- eff_steps = num_transfer.size(1)
152
- full_attn, full_pos = build_staircase_attention_mask(x, block_size, pad_id)
153
- attn_blk = full_attn[:, :, T_prefix:T_total, :]
154
- pos_blk = full_pos[:, T_prefix:T_total]
155
- for t in range(eff_steps):
156
- x_blk = x[:, T_prefix:T_total]
157
- m_blk = x_blk == mask_id
158
- cond_logits = model(x_blk, attention_mask=attn_blk, position_ids=pos_blk, past_key_values=copy.deepcopy(cond_past), use_cache=False).logits
159
- logits = cond_logits
160
- if cfg_scale > 0:
161
- un_logits = model(x_blk, attention_mask=attn_blk, position_ids=pos_blk, past_key_values=copy.deepcopy(uncond_past), use_cache=False).logits
162
- logits = un_logits + (cfg_scale + 1.0) * (cond_logits - un_logits)
163
- x_blk_new = diffusion_step_block(logits, x_blk, m_blk, num_transfer[:, t], temperature, remasking)
164
- x[:, T_prefix:T_total] = x_blk_new
165
- if capture_interval > 0 and total_step % capture_interval == 0:
166
- intermediates.append(x.clone())
167
- total_step += 1
168
- if tokenizer.eos_token_id is not None:
169
- finished |= (x_blk_new == tokenizer.eos_token_id).any(dim=1)
170
- generated += cur_len
171
- if finished.all():
172
- break
173
-
174
- if capture_interval > 0:
175
- return x, intermediates
176
- return x
177
-
178
- @torch.no_grad()
179
- def generate_stream(model, tokenizer, prompt, steps=128, max_new_tokens=128, block_size=32, temperature=0.0, cfg_scale=0.0, remasking="low_confidence", capture_interval=10):
180
- device = model.device
181
- mask_id = tokenizer.mask_token_id
182
- pad_id = tokenizer.pad_token_id
183
- if pad_id is None:
184
- pad_id = tokenizer.eos_token_id if tokenizer.eos_token_id is not None else tokenizer.mask_token_id
185
- if isinstance(prompt, torch.Tensor):
186
- x = prompt.to(device).long()
187
- else:
188
- if isinstance(prompt[0], (list, tuple)):
189
- max_len = max(len(p) for p in prompt)
190
- x = torch.full((len(prompt), max_len), pad_id, device=device, dtype=torch.long)
191
- for i, p in enumerate(prompt):
192
- x[i, : len(p)] = torch.tensor(p, device=device)
193
- else:
194
- x = torch.tensor(prompt, device=device).long()
195
- if x.dim() == 1:
196
- x = x.unsqueeze(0)
197
- B = x.size(0)
198
- finished = torch.zeros(B, dtype=torch.bool, device=device)
199
- num_blocks = math.ceil(max_new_tokens / block_size)
200
- steps_per_block = math.ceil(steps / num_blocks)
201
- generated = 0
202
- total_step = 0
203
- prompt_len = x.size(1)
204
- while generated < max_new_tokens:
205
- if finished.all():
206
- break
207
- T_prefix = x.size(1)
208
- offset = T_prefix % block_size
209
- room = block_size if offset == 0 else block_size - offset
210
- cur_len = min(room, max_new_tokens - generated)
211
- if cur_len <= 0:
212
- break
213
- attn_pfx, pos_pfx = build_staircase_attention_mask(x, block_size, pad_id)
214
- out = model(x, attention_mask=attn_pfx, position_ids=pos_pfx, use_cache=True)
215
- cond_past = out.past_key_values
216
- if cfg_scale > 0:
217
- un_x = x.clone()
218
- un_x[:] = mask_id
219
- out_un = model(un_x, attention_mask=attn_pfx, position_ids=pos_pfx, use_cache=True)
220
- uncond_past = out_un.past_key_values
221
- else:
222
- uncond_past = None
223
- block = torch.full((B, cur_len), mask_id, device=device, dtype=torch.long)
224
- block[finished] = pad_id
225
- x = torch.cat([x, block], dim=1)
226
- T_total = x.size(1)
227
- block_mask = x[:, -cur_len:] == mask_id
228
- num_transfer = get_num_transfer_tokens(block_mask, steps_per_block)
229
- eff_steps = num_transfer.size(1)
230
- full_attn, full_pos = build_staircase_attention_mask(x, block_size, pad_id)
231
- attn_blk = full_attn[:, :, T_prefix:T_total, :]
232
- pos_blk = full_pos[:, T_prefix:T_total]
233
- for t in range(eff_steps):
234
- x_blk = x[:, T_prefix:T_total]
235
- m_blk = x_blk == mask_id
236
- cond_logits = model(x_blk, attention_mask=attn_blk, position_ids=pos_blk, past_key_values=copy.deepcopy(cond_past), use_cache=False).logits
237
- logits = cond_logits
238
- if cfg_scale > 0:
239
- un_logits = model(
240
- x_blk, attention_mask=attn_blk, position_ids=pos_blk,
241
- past_key_values=copy.deepcopy(uncond_past), use_cache=False
242
- ).logits
243
- logits = un_logits + (cfg_scale + 1.0) * (cond_logits - un_logits)
244
- x_blk_new = diffusion_step_block(
245
- logits, x_blk, m_blk, num_transfer[:, t], temperature, remasking
246
- )
247
- x[:, T_prefix:T_total] = x_blk_new
248
-
249
- if total_step % capture_interval == 0:
250
- new_tokens = x[0, prompt_len:prompt_len + max_new_tokens].tolist()
251
- text = tokenizer.decode(new_tokens, skip_special_tokens=True)
252
- yield {
253
- "type": "intermediate",
254
- "step": total_step,
255
- "text": text,
256
- "total_steps": steps
257
- }
258
-
259
- total_step += 1
260
-
261
- if tokenizer.eos_token_id is not None:
262
- finished |= (x_blk_new == tokenizer.eos_token_id).any(dim=1)
263
- if finished.all():
264
- break
265
- generated += cur_len
266
-
267
- if finished.all():
268
- break
269
-
270
- new_tokens = x[0, prompt_len:prompt_len + max_new_tokens].tolist()
271
- final_text = tokenizer.decode(new_tokens, skip_special_tokens=True)
272
- yield {
273
- "type": "final",
274
- "text": final_text,
275
- "total_steps": total_step
276
- }
277
-
278
-
279
- # ==========================================================
280
- # ARCHITECTURE ROUTING LAYERS & TRANSLATION ENGINE CODES
281
- # ==========================================================
282
- def load_model():
283
- global model, tokenizer, device
284
- device = "cuda" if torch.cuda.is_available() else "cpu"
285
-
286
- print(f"Initializing {MODEL_NAME} on {device}... (Diffusion Strategy Flag = {IS_DIFFUSION})")
287
-
288
- if IS_DIFFUSION:
289
- model = AutoModelForMaskedLM.from_pretrained(
290
- MODEL_NAME,
291
- dtype=torch.bfloat16,
292
- trust_remote_code=True
293
- ).to(device).eval()
294
- else:
295
- model = AutoModelForCausalLM.from_pretrained(
296
- MODEL_NAME,
297
- torch_dtype=torch.bfloat16,
298
- trust_remote_code=False
299
- ).to(device).eval()
300
-
301
- tokenizer = AutoTokenizer.from_pretrained(
302
- MODEL_NAME,
303
- trust_remote_code=IS_DIFFUSION
304
- )
305
- print("Model compilation completed and loaded into memory workspace.")
306
-
307
- @app.route('/health', methods=['GET'])
308
- def health():
309
- return jsonify({"status": "healthy", "model_loaded": model is not None, "is_diffusion": IS_DIFFUSION})
310
-
311
- @app.route('/generate', methods=['POST'])
312
- def generate_text():
313
- if model is None or tokenizer is None:
314
- return jsonify({"error": "Model initialization missing"}), 503
315
-
316
- data = request.get_json() or {}
317
- if 'prompt' not in data:
318
- return jsonify({"error": "Missing 'prompt' operational field"}), 400
319
-
320
- prompt = data['prompt']
321
- max_new_tokens = data.get('max_new_tokens', 256)
322
- temperature = data.get('temperature', 0.0)
323
- system_prompt = data.get('system_prompt', 'You are an expert assistant.')
324
-
325
- messages = [
326
- {"role": "system", "content": system_prompt},
327
- {"role": "user", "content": prompt}
328
- ]
329
-
330
- encoded = tokenizer.apply_chat_template(
331
- messages,
332
- add_generation_prompt=True,
333
- tokenize=True,
334
- enable_thinking=False if IS_DIFFUSION else None
335
- )
336
-
337
- if IS_DIFFUSION:
338
- input_ids = torch.tensor([encoded], dtype=torch.long, device=device)
339
- steps = data.get('steps', 256)
340
- block_size = data.get('block_size', 32)
341
- cfg_scale = data.get('cfg_scale', 0.0)
342
- remasking = data.get('remasking', 'low_confidence')
343
-
344
- output = generate(
345
- model, tokenizer, input_ids,
346
- steps=steps, max_new_tokens=max_new_tokens, block_size=block_size,
347
- temperature=temperature, cfg_scale=cfg_scale, remasking=remasking,
348
- )
349
- prompt_len = len(encoded)
350
- new_tokens = output[0, prompt_len:prompt_len + max_new_tokens].tolist()
351
- generated_text = tokenizer.decode(new_tokens, skip_special_tokens=True)
352
- else:
353
- # High-Speed Autoregressive Optimization Matrix
354
- input_ids = torch.tensor([encoded], dtype=torch.long, device=device)
355
- with torch.no_grad():
356
- output_ids = model.generate(
357
- input_ids,
358
- max_new_tokens=max_new_tokens,
359
- temperature=temperature,
360
- do_sample=True if temperature > 0 else False,
361
- pad_token_id=tokenizer.eos_token_id
362
- )
363
- generated_ids = output_ids[0, input_ids.shape[-1]:]
364
- generated_text = tokenizer.decode(generated_ids, skip_special_tokens=True)
365
-
366
- return jsonify({"prompt": prompt, "generated_text": generated_text})
367
-
368
- @app.route('/generate_stream', methods=['POST'])
369
- def generate_text_stream():
370
- if model is None or tokenizer is None:
371
- return jsonify({"error": "Model workspace offline"}), 503
372
-
373
- data = request.get_json() or {}
374
- if not data or 'prompt' not in data:
375
- return jsonify({"error": "Missing 'prompt' operational field"}), 400
376
-
377
- prompt = data['prompt']
378
- max_new_tokens = data.get('max_new_tokens', 256)
379
- temperature = data.get('temperature', 0.0)
380
- system_prompt = data.get('system_prompt', 'You are an expert ssistant.')
381
-
382
- messages = [
383
- {"role": "system", "content": system_prompt},
384
- {"role": "user", "content": prompt}
385
- ]
386
- encoded = tokenizer.apply_chat_template(messages, add_generation_prompt=True, tokenize=True, enable_thinking=False if IS_DIFFUSION else None)
387
-
388
- if IS_DIFFUSION:
389
- input_ids = torch.tensor([encoded], dtype=torch.long, device=device)
390
- steps = data.get('steps', 256)
391
- block_size = data.get('block_size', 32)
392
- cfg_scale = data.get('cfg_scale', 0.0)
393
- remasking = data.get('remasking', 'low_confidence')
394
- capture_interval = data.get('capture_interval', 10)
395
-
396
- output, intermediates = generate(
397
- model, tokenizer, input_ids,
398
- steps=steps, max_new_tokens=max_new_tokens, block_size=block_size,
399
- temperature=temperature, cfg_scale=cfg_scale, remasking=remasking,
400
- capture_interval=capture_interval,
401
- )
402
- prompt_len = len(encoded)
403
- intermediate_states = []
404
- for i, intermediate in enumerate(intermediates):
405
- new_tokens = intermediate[0, prompt_len:prompt_len + max_new_tokens].tolist()
406
- text = tokenizer.decode(new_tokens, skip_special_tokens=True)
407
- intermediate_states.append({"step": i * capture_interval, "text": text})
408
-
409
- new_tokens = output[0, prompt_len:prompt_len + max_new_tokens].tolist()
410
- generated_text = tokenizer.decode(new_tokens, skip_special_tokens=True)
411
- return jsonify({"prompt": prompt, "generated_text": generated_text, "intermediate_states": intermediate_states})
412
- else:
413
- input_ids = torch.tensor([encoded], dtype=torch.long, device=device)
414
- with torch.no_grad():
415
- output_ids = model.generate(input_ids, max_new_tokens=max_new_tokens, temperature=temperature, do_sample=True if temperature > 0 else False, pad_token_id=tokenizer.eos_token_id)
416
- generated_ids = output_ids[0, input_ids.shape[-1]:]
417
- generated_text = tokenizer.decode(generated_ids, skip_special_tokens=True)
418
- return jsonify({"prompt": prompt, "generated_text": generated_text, "intermediate_states": []})
419
-
420
- @app.route('/generate_sse', methods=['POST'])
421
- def generate_text_sse():
422
- if model is None or tokenizer is None:
423
- return jsonify({"error": "Model workspace offline"}), 503
424
-
425
- data = request.get_json() or {}
426
- if not data or 'prompt' not in data:
427
- return jsonify({"error": "Missing 'prompt' operational field"}), 400
428
-
429
- prompt = data['prompt']
430
- max_new_tokens = data.get('max_new_tokens', 256)
431
- temperature = data.get('temperature', 0.0)
432
- system_prompt = data.get('system_prompt', 'You are an expert assistant.')
433
-
434
- messages = [
435
- {"role": "system", "content": system_prompt},
436
- {"role": "user", "content": prompt}
437
- ]
438
- encoded = tokenizer.apply_chat_template(messages, add_generation_prompt=True, tokenize=True, enable_thinking=False if IS_DIFFUSION else None)
439
- input_ids = torch.tensor([encoded], dtype=torch.long, device=device)
440
-
441
- def stream():
442
- if IS_DIFFUSION:
443
- steps = data.get('steps', 256)
444
- block_size = data.get('block_size', 32)
445
- cfg_scale = data.get('cfg_scale', 0.0)
446
- remasking = data.get('remasking', 'low_confidence')
447
- capture_interval = data.get('capture_interval', 10)
448
- for state in generate_stream(model, tokenizer, input_ids, steps=steps, max_new_tokens=max_new_tokens, block_size=block_size, temperature=temperature, cfg_scale=cfg_scale, remasking=remasking, capture_interval=capture_interval):
449
- yield f"data: {json.dumps(state)}\n\n"
450
- else:
451
- with torch.no_grad():
452
- output_ids = model.generate(input_ids, max_new_tokens=max_new_tokens, temperature=temperature, do_sample=True if temperature > 0 else False, pad_token_id=tokenizer.eos_token_id)
453
- generated_ids = output_ids[0, input_ids.shape[-1]:]
454
- final_text = tokenizer.decode(generated_ids, skip_special_tokens=True)
455
- yield f"data: {json.dumps({'type': 'final', 'text': final_text, 'total_steps': 1})}\n\n"
456
-
457
- return Response(stream(), mimetype='text/event-stream', headers={'Cache-Control': 'no-cache', 'X-Accel-Buffering': 'no'})
458
-
459
- @app.route('/')
460
- def index():
461
- return {
462
- "status": "healthy",
463
- "message": f"Multi-architecture API Router up and running. Target: {'Diffusion Framework' if IS_DIFFUSION else 'Causal Baseline Model'}", "model_loaded": MODEL_NAME}, 200
464
-
465
- if __name__ == '__main__':
466
- load_model()
467
- app.run(host='0.0.0.0', port=int(os.getenv('PORT', 7860)))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import types
3
+ import os
4
+ import math
5
+ import json
6
+ import copy
7
+ import torch
8
+ import torch.nn.functional as F
9
+ from flask import Flask, request, jsonify, Response
10
+ from transformers import AutoTokenizer, AutoModelForMaskedLM, AutoModelForCausalLM, TextIteratorStreamer
11
+ from threading import Thread
12
+
13
+ # 1. Environment Parsing & Architecture Strategy Mapping
14
+ MODEL_NAME = os.getenv("MODEL_NAME", "dllm-hub/Qwen3-0.6B-diffusion-bd3lm-v0.1")
15
+ IS_DIFFUSION = "diffusion" in MODEL_NAME.lower()
16
+
17
+ # Dynamic initialization layer targeting Diffusion Language Models
18
+ if IS_DIFFUSION:
19
+ try:
20
+ import dllm.utils
21
+ import dllm.pipelines
22
+ import dllm.data
23
+ import dllm.core
24
+ except ImportError:
25
+ pass
26
+ if 'dllm' not in sys.modules:
27
+ dllm_mock = types.ModuleType('dllm')
28
+ dllm_mock.core = sys.modules.get('dllm.core')
29
+ dllm_mock.data = sys.modules.get('dllm.data')
30
+ dllm_mock.pipelines = sys.modules.get('dllm.pipelines')
31
+ dllm_mock.utils = sys.modules.get('dllm.utils')
32
+ sys.modules['dllm'] = dllm_mock
33
+
34
+ app = Flask(__name__)
35
+ model = None
36
+ tokenizer = None
37
+ device = None
38
+
39
+ # ==========================================================
40
+ # SYSTEM WORKSPACE PIPELINES: CORE DIFFUSION SAMPLING LOOPS
41
+ # ==========================================================
42
+
43
+ def add_gumbel_noise(logits, temperature):
44
+ """Add Gumbel noise using float32 (faster than float64 on most GPUs)."""
45
+ if temperature == 0:
46
+ return logits
47
+ logits = logits.float()
48
+ noise = torch.rand_like(logits)
49
+ g = (-torch.log(noise)) ** temperature
50
+ return logits.exp() / g
51
+
52
+
53
+ def get_num_transfer_tokens(mask_index, steps):
54
+ mask_num = mask_index.sum(dim=1, keepdim=True)
55
+ base = mask_num // steps
56
+ rem = mask_num % steps
57
+ out = torch.zeros(mask_num.size(0), steps, device=mask_index.device, dtype=torch.long) + base
58
+ for i in range(mask_num.size(0)):
59
+ out[i, : rem[i]] += 1
60
+ return out
61
+
62
+
63
+ def build_staircase_attention_mask(x, block_size, pad_id):
64
+ B, T = x.shape
65
+ device = x.device
66
+ valid = x != pad_id
67
+ pos_raw = torch.cumsum(valid.long(), dim=-1)
68
+ position_ids = torch.where(valid, pos_raw - 1, torch.zeros_like(pos_raw)).long()
69
+ col = torch.arange(T, device=device)
70
+ block_ids = (col // block_size).view(1, T).expand(B, T)
71
+ block_ids = torch.where(valid, block_ids, torch.full_like(block_ids, -1))
72
+ q = block_ids.view(B, 1, T, 1)
73
+ k = block_ids.view(B, 1, 1, T)
74
+ attn = (k <= q) & (q >= 0) & (k >= 0)
75
+ return attn, position_ids
76
+
77
+
78
+ def clone_past_key_values(pkv):
79
+ """Clone KV-cache. Fast path for tuples and Cache objects; falls back to deepcopy."""
80
+ if pkv is None:
81
+ return None
82
+ # Fast path: legacy tuple format
83
+ if isinstance(pkv, tuple):
84
+ return tuple(
85
+ (k.clone() if k is not None else None, v.clone() if v is not None else None)
86
+ for k, v in pkv
87
+ )
88
+ # Fast path: transformers Cache objects (DynamicCache, etc.)
89
+ if hasattr(pkv, 'key_cache') and hasattr(pkv, 'value_cache'):
90
+ try:
91
+ new_cache = pkv.__class__()
92
+ new_cache.key_cache = [k.clone() for k in pkv.key_cache]
93
+ new_cache.value_cache = [v.clone() for v in pkv.value_cache]
94
+ for attr in ('_seen_tokens', 'seen_tokens'):
95
+ if hasattr(pkv, attr):
96
+ setattr(new_cache, attr, getattr(pkv, attr))
97
+ return new_cache
98
+ except Exception:
99
+ pass
100
+ # Fallback
101
+ return copy.deepcopy(pkv)
102
+
103
+
104
+ def diffusion_step_block(logits, x_block, mask_block, num_transfer, temperature, remasking):
105
+ """Vectorized diffusion step — no per-sample Python loops."""
106
+ B, L, _ = logits.shape
107
+ if not mask_block.any():
108
+ return x_block
109
+ noisy = add_gumbel_noise(logits, temperature)
110
+ x0 = noisy.argmax(dim=-1)
111
+ if remasking == "low_confidence":
112
+ p = F.softmax(logits, dim=-1)
113
+ conf = p.gather(-1, x0.unsqueeze(-1)).squeeze(-1)
114
+ elif remasking == "random":
115
+ conf = torch.rand((B, L), device=logits.device)
116
+ else:
117
+ raise ValueError(remasking)
118
+ x0 = torch.where(mask_block, x0, x_block)
119
+ conf = conf.masked_fill(~mask_block, float("-inf"))
120
+ k_max = int(num_transfer.max().item())
121
+ if k_max > 0:
122
+ k = min(k_max, L)
123
+ topk_vals, topk_idx = torch.topk(conf, k=k, dim=-1)
124
+ commit = torch.zeros_like(x_block, dtype=torch.bool)
125
+ valid_mask = torch.arange(k, device=x_block.device).view(1, k) < num_transfer.view(B, 1)
126
+ commit.scatter_(1, topk_idx, valid_mask)
127
+ x_block = torch.where(commit, x0, x_block)
128
+ return x_block
129
+
130
+
131
+ @torch.inference_mode()
132
+ def generate(model, tokenizer, prompt, steps=128, max_new_tokens=128, block_size=32,
133
+ temperature=0.0, cfg_scale=0.0, remasking="low_confidence", capture_interval=0):
134
+ device = model.device
135
+ mask_id = tokenizer.mask_token_id
136
+ pad_id = tokenizer.pad_token_id
137
+ if pad_id is None:
138
+ pad_id = tokenizer.eos_token_id if tokenizer.eos_token_id is not None else tokenizer.mask_token_id
139
+ if isinstance(prompt, torch.Tensor):
140
+ x = prompt.to(device).long()
141
+ else:
142
+ if isinstance(prompt[0], (list, tuple)):
143
+ max_len = max(len(p) for p in prompt)
144
+ x = torch.full((len(prompt), max_len), pad_id, device=device, dtype=torch.long)
145
+ for i, p in enumerate(prompt):
146
+ x[i, : len(p)] = torch.tensor(p, device=device)
147
+ else:
148
+ x = torch.tensor(prompt, device=device).long()
149
+ if x.dim() == 1:
150
+ x = x.unsqueeze(0)
151
+ B = x.size(0)
152
+ finished = torch.zeros(B, dtype=torch.bool, device=device)
153
+ num_blocks = math.ceil(max_new_tokens / block_size)
154
+ steps_per_block = math.ceil(steps / num_blocks)
155
+ generated = 0
156
+ intermediates = []
157
+ total_step = 0
158
+ while generated < max_new_tokens:
159
+ if finished.all():
160
+ break
161
+ T_prefix = x.size(1)
162
+ offset = T_prefix % block_size
163
+ room = block_size if offset == 0 else block_size - offset
164
+ cur_len = min(room, max_new_tokens - generated)
165
+ if cur_len <= 0:
166
+ break
167
+ attn_pfx, pos_pfx = build_staircase_attention_mask(x, block_size, pad_id)
168
+ out = model(x, attention_mask=attn_pfx, position_ids=pos_pfx, use_cache=True)
169
+ cond_past = out.past_key_values
170
+ if cfg_scale > 0:
171
+ un_x = x.clone()
172
+ un_x[:] = mask_id
173
+ out_un = model(un_x, attention_mask=attn_pfx, position_ids=pos_pfx, use_cache=True)
174
+ uncond_past = out_un.past_key_values
175
+ else:
176
+ uncond_past = None
177
+ block = torch.full((B, cur_len), mask_id, device=device, dtype=torch.long)
178
+ block[finished] = pad_id
179
+ x = torch.cat([x, block], dim=1)
180
+ T_total = x.size(1)
181
+ block_mask = x[:, -cur_len:] == mask_id
182
+ num_transfer = get_num_transfer_tokens(block_mask, steps_per_block)
183
+ eff_steps = num_transfer.size(1)
184
+ full_attn, full_pos = build_staircase_attention_mask(x, block_size, pad_id)
185
+ attn_blk = full_attn[:, :, T_prefix:T_total, :]
186
+ pos_blk = full_pos[:, T_prefix:T_total]
187
+ for t in range(eff_steps):
188
+ x_blk = x[:, T_prefix:T_total]
189
+ m_blk = x_blk == mask_id
190
+ cond_logits = model(
191
+ x_blk, attention_mask=attn_blk, position_ids=pos_blk,
192
+ past_key_values=clone_past_key_values(cond_past), use_cache=False
193
+ ).logits
194
+ logits = cond_logits
195
+ if cfg_scale > 0:
196
+ un_logits = model(
197
+ x_blk, attention_mask=attn_blk, position_ids=pos_blk,
198
+ past_key_values=clone_past_key_values(uncond_past), use_cache=False
199
+ ).logits
200
+ logits = un_logits + (cfg_scale + 1.0) * (cond_logits - un_logits)
201
+ x_blk_new = diffusion_step_block(
202
+ logits, x_blk, m_blk, num_transfer[:, t], temperature, remasking
203
+ )
204
+ x[:, T_prefix:T_total] = x_blk_new
205
+ if capture_interval > 0 and total_step % capture_interval == 0:
206
+ intermediates.append(x.clone())
207
+ total_step += 1
208
+ if tokenizer.eos_token_id is not None:
209
+ finished |= (x_blk_new == tokenizer.eos_token_id).any(dim=1)
210
+ generated += cur_len
211
+ if finished.all():
212
+ break
213
+ if capture_interval > 0:
214
+ return x, intermediates
215
+ return x
216
+
217
+
218
+ @torch.inference_mode()
219
+ def generate_stream(model, tokenizer, prompt, steps=128, max_new_tokens=128, block_size=32,
220
+ temperature=0.0, cfg_scale=0.0, remasking="low_confidence", capture_interval=10):
221
+ device = model.device
222
+ mask_id = tokenizer.mask_token_id
223
+ pad_id = tokenizer.pad_token_id
224
+ if pad_id is None:
225
+ pad_id = tokenizer.eos_token_id if tokenizer.eos_token_id is not None else tokenizer.mask_token_id
226
+ if isinstance(prompt, torch.Tensor):
227
+ x = prompt.to(device).long()
228
+ else:
229
+ if isinstance(prompt[0], (list, tuple)):
230
+ max_len = max(len(p) for p in prompt)
231
+ x = torch.full((len(prompt), max_len), pad_id, device=device, dtype=torch.long)
232
+ for i, p in enumerate(prompt):
233
+ x[i, : len(p)] = torch.tensor(p, device=device)
234
+ else:
235
+ x = torch.tensor(prompt, device=device).long()
236
+ if x.dim() == 1:
237
+ x = x.unsqueeze(0)
238
+ B = x.size(0)
239
+ finished = torch.zeros(B, dtype=torch.bool, device=device)
240
+ num_blocks = math.ceil(max_new_tokens / block_size)
241
+ steps_per_block = math.ceil(steps / num_blocks)
242
+ generated = 0
243
+ total_step = 0
244
+ prompt_len = x.size(1)
245
+ while generated < max_new_tokens:
246
+ if finished.all():
247
+ break
248
+ T_prefix = x.size(1)
249
+ offset = T_prefix % block_size
250
+ room = block_size if offset == 0 else block_size - offset
251
+ cur_len = min(room, max_new_tokens - generated)
252
+ if cur_len <= 0:
253
+ break
254
+ attn_pfx, pos_pfx = build_staircase_attention_mask(x, block_size, pad_id)
255
+ out = model(x, attention_mask=attn_pfx, position_ids=pos_pfx, use_cache=True)
256
+ cond_past = out.past_key_values
257
+ if cfg_scale > 0:
258
+ un_x = x.clone()
259
+ un_x[:] = mask_id
260
+ out_un = model(un_x, attention_mask=attn_pfx, position_ids=pos_pfx, use_cache=True)
261
+ uncond_past = out_un.past_key_values
262
+ else:
263
+ uncond_past = None
264
+ block = torch.full((B, cur_len), mask_id, device=device, dtype=torch.long)
265
+ block[finished] = pad_id
266
+ x = torch.cat([x, block], dim=1)
267
+ T_total = x.size(1)
268
+ block_mask = x[:, -cur_len:] == mask_id
269
+ num_transfer = get_num_transfer_tokens(block_mask, steps_per_block)
270
+ eff_steps = num_transfer.size(1)
271
+ full_attn, full_pos = build_staircase_attention_mask(x, block_size, pad_id)
272
+ attn_blk = full_attn[:, :, T_prefix:T_total, :]
273
+ pos_blk = full_pos[:, T_prefix:T_total]
274
+ for t in range(eff_steps):
275
+ x_blk = x[:, T_prefix:T_total]
276
+ m_blk = x_blk == mask_id
277
+ cond_logits = model(
278
+ x_blk, attention_mask=attn_blk, position_ids=pos_blk,
279
+ past_key_values=clone_past_key_values(cond_past), use_cache=False
280
+ ).logits
281
+ logits = cond_logits
282
+ if cfg_scale > 0:
283
+ un_logits = model(
284
+ x_blk, attention_mask=attn_blk, position_ids=pos_blk,
285
+ past_key_values=clone_past_key_values(uncond_past), use_cache=False
286
+ ).logits
287
+ logits = un_logits + (cfg_scale + 1.0) * (cond_logits - un_logits)
288
+ x_blk_new = diffusion_step_block(
289
+ logits, x_blk, m_blk, num_transfer[:, t], temperature, remasking
290
+ )
291
+ x[:, T_prefix:T_total] = x_blk_new
292
+ if total_step % capture_interval == 0:
293
+ new_tokens = x[0, prompt_len:prompt_len + max_new_tokens].tolist()
294
+ text = tokenizer.decode(new_tokens, skip_special_tokens=True)
295
+ yield {
296
+ "type": "intermediate",
297
+ "step": total_step,
298
+ "text": text,
299
+ "total_steps": steps
300
+ }
301
+ total_step += 1
302
+ if tokenizer.eos_token_id is not None:
303
+ finished |= (x_blk_new == tokenizer.eos_token_id).any(dim=1)
304
+ if finished.all():
305
+ break
306
+ generated += cur_len
307
+ if finished.all():
308
+ break
309
+ new_tokens = x[0, prompt_len:prompt_len + max_new_tokens].tolist()
310
+ final_text = tokenizer.decode(new_tokens, skip_special_tokens=True)
311
+ yield {
312
+ "type": "final",
313
+ "text": final_text,
314
+ "total_steps": total_step
315
+ }
316
+
317
+
318
+ # ==========================================================
319
+ # ARCHITECTURE ROUTING LAYERS & TRANSLATION ENGINE CODES
320
+ # ==========================================================
321
+
322
+ def load_model():
323
+ global model, tokenizer, device
324
+ device = "cuda" if torch.cuda.is_available() else "cpu"
325
+ print(f"Initializing {MODEL_NAME} on {device}... (Diffusion Strategy Flag = {IS_DIFFUSION})")
326
+ if IS_DIFFUSION:
327
+ model = AutoModelForMaskedLM.from_pretrained(
328
+ MODEL_NAME,
329
+ torch_dtype=torch.bfloat16,
330
+ trust_remote_code=True
331
+ ).to(device).eval()
332
+ else:
333
+ model = AutoModelForCausalLM.from_pretrained(
334
+ MODEL_NAME,
335
+ torch_dtype=torch.bfloat16,
336
+ trust_remote_code=False
337
+ ).to(device).eval()
338
+ # Compile model for faster inference — ONLY for standard causal models
339
+ if not IS_DIFFUSION:
340
+ try:
341
+ model = torch.compile(model, mode="reduce-overhead", fullgraph=False)
342
+ print("Model compiled with torch.compile.")
343
+ except Exception as e:
344
+ print(f"torch.compile skipped: {e}")
345
+ else:
346
+ print("Diffusion model loaded without torch.compile (custom FX code incompatible with Dynamo).")
347
+ tokenizer = AutoTokenizer.from_pretrained(
348
+ MODEL_NAME,
349
+ trust_remote_code=IS_DIFFUSION
350
+ )
351
+ print("Model compilation completed and loaded into memory workspace.")
352
+
353
+
354
+ @app.route('/health', methods=['GET'])
355
+ def health():
356
+ return jsonify({"status": "healthy", "model_loaded": model is not None, "is_diffusion": IS_DIFFUSION})
357
+
358
+
359
+ @app.route('/generate', methods=['POST'])
360
+ def generate_text():
361
+ if model is None or tokenizer is None:
362
+ return jsonify({"error": "Model initialization missing"}), 503
363
+ data = request.get_json() or {}
364
+ if 'prompt' not in data:
365
+ return jsonify({"error": "Missing 'prompt' operational field"}), 400
366
+ prompt = data['prompt']
367
+ max_new_tokens = data.get('max_new_tokens', 256)
368
+ temperature = data.get('temperature', 0.0)
369
+ system_prompt = data.get('system_prompt', 'You are an expert real-time translation assistant.')
370
+ messages = [
371
+ {"role": "system", "content": system_prompt},
372
+ {"role": "user", "content": prompt}
373
+ ]
374
+ # enable_thinking=False for ALL routes to prevent Qwen3 from leaking internal monologue
375
+ encoded = tokenizer.apply_chat_template(
376
+ messages,
377
+ add_generation_prompt=True,
378
+ tokenize=True,
379
+ enable_thinking=False
380
+ )
381
+ if IS_DIFFUSION:
382
+ input_ids = torch.tensor([encoded], dtype=torch.long, device=device)
383
+ steps = data.get('steps', 256)
384
+ block_size = data.get('block_size', 32)
385
+ cfg_scale = data.get('cfg_scale', 0.0)
386
+ remasking = data.get('remasking', 'low_confidence')
387
+ output = generate(
388
+ model, tokenizer, input_ids,
389
+ steps=steps, max_new_tokens=max_new_tokens, block_size=block_size,
390
+ temperature=temperature, cfg_scale=cfg_scale, remasking=remasking,
391
+ )
392
+ prompt_len = len(encoded)
393
+ new_tokens = output[0, prompt_len:prompt_len + max_new_tokens].tolist()
394
+ generated_text = tokenizer.decode(new_tokens, skip_special_tokens=True)
395
+ else:
396
+ input_ids = torch.tensor([encoded], dtype=torch.long, device=device)
397
+ output_ids = model.generate(
398
+ input_ids,
399
+ max_new_tokens=max_new_tokens,
400
+ temperature=temperature,
401
+ do_sample=True if temperature > 0 else False,
402
+ pad_token_id=tokenizer.eos_token_id
403
+ )
404
+ generated_ids = output_ids[0, input_ids.shape[-1]:]
405
+ generated_text = tokenizer.decode(generated_ids, skip_special_tokens=True)
406
+ return jsonify({"prompt": prompt, "generated_text": generated_text})
407
+
408
+
409
+ @app.route('/generate_stream', methods=['POST'])
410
+ def generate_text_stream():
411
+ if model is None or tokenizer is None:
412
+ return jsonify({"error": "Model workspace offline"}), 503
413
+ data = request.get_json() or {}
414
+ if not data or 'prompt' not in data:
415
+ return jsonify({"error": "Missing 'prompt' operational field"}), 400
416
+ prompt = data['prompt']
417
+ max_new_tokens = data.get('max_new_tokens', 256)
418
+ temperature = data.get('temperature', 0.0)
419
+ system_prompt = data.get('system_prompt', 'You are an expert real-time translation assistant.')
420
+ messages = [
421
+ {"role": "system", "content": system_prompt},
422
+ {"role": "user", "content": prompt}
423
+ ]
424
+ encoded = tokenizer.apply_chat_template(
425
+ messages, add_generation_prompt=True, tokenize=True,
426
+ enable_thinking=False
427
+ )
428
+ if IS_DIFFUSION:
429
+ input_ids = torch.tensor([encoded], dtype=torch.long, device=device)
430
+ steps = data.get('steps', 256)
431
+ block_size = data.get('block_size', 32)
432
+ cfg_scale = data.get('cfg_scale', 0.0)
433
+ remasking = data.get('remasking', 'low_confidence')
434
+ capture_interval = data.get('capture_interval', 10)
435
+ output, intermediates = generate(
436
+ model, tokenizer, input_ids,
437
+ steps=steps, max_new_tokens=max_new_tokens, block_size=block_size,
438
+ temperature=temperature, cfg_scale=cfg_scale, remasking=remasking,
439
+ capture_interval=capture_interval,
440
+ )
441
+ prompt_len = len(encoded)
442
+ intermediate_states = []
443
+ for i, intermediate in enumerate(intermediates):
444
+ new_tokens = intermediate[0, prompt_len:prompt_len + max_new_tokens].tolist()
445
+ text = tokenizer.decode(new_tokens, skip_special_tokens=True)
446
+ intermediate_states.append({"step": i * capture_interval, "text": text})
447
+ new_tokens = output[0, prompt_len:prompt_len + max_new_tokens].tolist()
448
+ generated_text = tokenizer.decode(new_tokens, skip_special_tokens=True)
449
+ return jsonify({"prompt": prompt, "generated_text": generated_text, "intermediate_states": intermediate_states})
450
+ else:
451
+ input_ids = torch.tensor([encoded], dtype=torch.long, device=device)
452
+ output_ids = model.generate(
453
+ input_ids, max_new_tokens=max_new_tokens, temperature=temperature,
454
+ do_sample=True if temperature > 0 else False,
455
+ pad_token_id=tokenizer.eos_token_id
456
+ )
457
+ generated_ids = output_ids[0, input_ids.shape[-1]:]
458
+ generated_text = tokenizer.decode(generated_ids, skip_special_tokens=True)
459
+ return jsonify({"prompt": prompt, "generated_text": generated_text, "intermediate_states": []})
460
+
461
+
462
+ @app.route('/generate_sse', methods=['POST'])
463
+ def generate_text_sse():
464
+ if model is None or tokenizer is None:
465
+ return jsonify({"error": "Model workspace offline"}), 503
466
+ data = request.get_json() or {}
467
+ if not data or 'prompt' not in data:
468
+ return jsonify({"error": "Missing 'prompt' operational field"}), 400
469
+ prompt = data['prompt']
470
+ max_new_tokens = data.get('max_new_tokens', 256)
471
+ temperature = data.get('temperature', 0.0)
472
+ system_prompt = data.get('system_prompt', 'You are an expert real-time translation assistant.')
473
+ messages = [
474
+ {"role": "system", "content": system_prompt},
475
+ {"role": "user", "content": prompt}
476
+ ]
477
+ encoded = tokenizer.apply_chat_template(
478
+ messages, add_generation_prompt=True, tokenize=True,
479
+ enable_thinking=False
480
+ )
481
+ input_ids = torch.tensor([encoded], dtype=torch.long, device=device)
482
+
483
+ def stream():
484
+ if IS_DIFFUSION:
485
+ steps = data.get('steps', 256)
486
+ block_size = data.get('block_size', 32)
487
+ cfg_scale = data.get('cfg_scale', 0.0)
488
+ remasking = data.get('remasking', 'low_confidence')
489
+ capture_interval = data.get('capture_interval', 10)
490
+ for state in generate_stream(
491
+ model, tokenizer, input_ids,
492
+ steps=steps, max_new_tokens=max_new_tokens, block_size=block_size,
493
+ temperature=temperature, cfg_scale=cfg_scale, remasking=remasking,
494
+ capture_interval=capture_interval
495
+ ):
496
+ yield f"data: {json.dumps(state)}\n\n"
497
+ else:
498
+ streamer = TextIteratorStreamer(
499
+ tokenizer, skip_prompt=True, skip_special_tokens=True
500
+ )
501
+ generation_kwargs = dict(
502
+ input_ids=input_ids,
503
+ streamer=streamer,
504
+ max_new_tokens=max_new_tokens,
505
+ temperature=temperature,
506
+ do_sample=True if temperature > 0 else False,
507
+ pad_token_id=tokenizer.eos_token_id,
508
+ )
509
+
510
+ def _generate():
511
+ with torch.inference_mode():
512
+ model.generate(**generation_kwargs)
513
+
514
+ thread = Thread(target=_generate)
515
+ thread.start()
516
+
517
+ accumulated = []
518
+ for text in streamer:
519
+ if not text: # skip empty chunks
520
+ continue
521
+ accumulated.append(text)
522
+ current = "".join(accumulated)
523
+ yield f"data: {json.dumps({'type': 'intermediate', 'text': current})}\n\n"
524
+
525
+ full_text = "".join(accumulated)
526
+ yield f"data: {json.dumps({'type': 'final', 'text': full_text, 'total_steps': 1})}\n\n"
527
+
528
+ return Response(
529
+ stream(), mimetype='text/event-stream',
530
+ headers={'Cache-Control': 'no-cache', 'X-Accel-Buffering': 'no'}
531
+ )
532
+
533
+
534
+ @app.route('/')
535
+ def index():
536
+ return {
537
+ "status": "healthy",
538
+ "message": f"Multi-architecture API Router up and running. Target: {'Diffusion Framework' if IS_DIFFUSION else 'Causal Baseline Model'}",
539
+ "model_loaded": MODEL_NAME
540
+ }, 200
541
+
542
+
543
+ if __name__ == '__main__':
544
+ load_model()
545
+ app.run(host='0.0.0.0', port=int(os.getenv('PORT', 7860)))