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

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +467 -545
app.py CHANGED
@@ -1,545 +1,467 @@
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)))
 
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)))