jnjj commited on
Commit
03d415c
·
verified ·
1 Parent(s): 307efb1

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -1570
app.py DELETED
@@ -1,1570 +0,0 @@
1
- import os
2
- import gc
3
- import json
4
- import random
5
- import torch
6
- import asyncio
7
- import logging
8
- import time
9
- from typing import List, Dict, Any, Optional, Union, AsyncGenerator, Tuple
10
- from fastapi import FastAPI, HTTPException, Query, Request, Depends, status
11
- from fastapi.responses import StreamingResponse, PlainTextResponse, HTMLResponse, JSONResponse
12
- from fastapi.security import APIKeyHeader
13
- from pydantic import BaseModel, Field, ValidationError, validator
14
- from transformers import (
15
- AutoConfig, AutoModelForCausalLM, AutoTokenizer,
16
- GenerationConfig, LogitsProcessorList,
17
- MinLengthLogitsProcessor, MaxLengthCriteria,
18
- StoppingCriteriaList, StoppingCriteria
19
- )
20
- import uvicorn
21
- from concurrent.futures import ThreadPoolExecutor
22
- import math
23
- import torch.nn.functional as F
24
- import copy
25
-
26
-
27
- class StopSequenceCriteria(StoppingCriteria):
28
- def __init__(self, stop_sequences: List[str], tokenizer: AutoTokenizer):
29
- self.tokenizer = tokenizer
30
- self.stop_sequences_text = []
31
- self.stop_sequence_ids = []
32
- for seq in stop_sequences:
33
- if seq:
34
- encoded_ids = tokenizer.encode(seq, add_special_tokens=False)
35
- decoded_text = tokenizer.decode(encoded_ids, skip_special_tokens=True)
36
- if decoded_text:
37
- self.stop_sequences_text.append(decoded_text)
38
- self.stop_sequence_ids.append(encoded_ids)
39
-
40
-
41
- def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor, **kwargs) -> bool:
42
- if not self.stop_sequence_ids:
43
- return False
44
-
45
- input_ids_list = input_ids[0].tolist()
46
-
47
- for stop_seq_ids in self.stop_sequence_ids:
48
- stop_len = len(stop_seq_ids)
49
- if len(input_ids_list) >= stop_len:
50
- if input_ids_list[-stop_len:] == stop_seq_ids:
51
- return True
52
-
53
- check_tail_len = max(len(self.tokenizer.decode(seq, add_special_tokens=False)) for seq in self.stop_sequence_ids) + 5 if self.stop_sequence_ids else 50
54
- tail_ids = input_ids_list[-min(check_tail_len, len(input_ids_list)):]
55
- tail_text = self.tokenizer.decode(tail_ids, skip_special_tokens=True)
56
-
57
- for stop_seq_text in self.stop_sequences_text:
58
- if stop_seq_text and stop_seq_text in tail_text:
59
- return True
60
-
61
- return False
62
-
63
-
64
- logging.getLogger("uvicorn").handlers.clear()
65
- logging.getLogger("uvicorn.error").handlers.clear()
66
- logging.getLogger("uvicorn.access").handlers.clear()
67
- logging.getLogger("uvicorn").propagate = False
68
- logging.getLogger("uvicorn.error").propagate = False
69
- logging.getLogger("uvicorn.access").propagate = False
70
- logging.getLogger("uvicorn").setLevel(logging.CRITICAL)
71
- logging.getLogger("uvicorn.error").setLevel(logging.CRITICAL)
72
- logging.getLogger("uvicorn.access").setLevel(logging.CRITICAL)
73
- logging.getLogger("fastapi").setLevel(logging.CRITICAL)
74
- logging.getLogger("transformers").setLevel(logging.CRITICAL)
75
- logging.getLogger().handlers.clear()
76
- logging.getLogger().addHandler(logging.NullHandler())
77
-
78
-
79
- MODEL_NAME = os.environ.get("MODEL_NAME", "jnjj/multi-dataset-model")
80
- SYSTEM_PROMPT = os.environ.get("SYSTEM_PROMPT", "Eres un asistente profesional y servicial.")
81
- MAX_CONTEXT_TOKENS = int(os.environ.get("MAX_CONTEXT_TOKENS", 1024))
82
- MAX_GENERATION_TOKENS = int(os.environ.get("MAX_GENERATION_TOKENS", 512))
83
- MAX_CONCURRENT_GENERATIONS = int(os.environ.get("MAX_CONCURRENT_GENERATIONS", 4))
84
- TRUST_REMOTE_CODE = os.environ.get("TRUST_REMOTE_CODE", "false").lower() == "true"
85
- ENABLE_FLASH_ATTENTION_2 = os.environ.get("ENABLE_FLASH_ATTENTION_2", "false").lower() == "true"
86
- TORCH_DTYPE_STR = os.environ.get("TORCH_DTYPE", "float16")
87
- TORCH_DTYPE = getattr(torch, TORCH_DTYPE_STR.lower(), torch.float16)
88
- if TORCH_DTYPE_STR.lower() == "bfloat16" and (not torch.cuda.is_available() or (torch.cuda.is_available() and not torch.cuda.get_device_properties(0).has_bfloat16)):
89
- TORCH_DTYPE = torch.float16
90
- elif not isinstance(TORCH_DTYPE, torch.dtype):
91
- TORCH_DTYPE = torch.float16
92
-
93
- API_KEY = os.environ.get("API_KEY")
94
-
95
- global_model = None
96
- global_tokenizer = None
97
- global_tokens: Dict[str, Optional[int]] = {}
98
- executor = ThreadPoolExecutor(max_workers=MAX_CONCURRENT_GENERATIONS)
99
- generation_semaphore = asyncio.Semaphore(MAX_CONCURRENT_GENERATIONS)
100
-
101
- api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False)
102
-
103
- async def get_api_key(api_key: str = Depends(api_key_header)):
104
- if API_KEY is None:
105
- return
106
- if api_key is None or api_key != API_KEY:
107
- raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid or missing API Key")
108
- return api_key
109
-
110
-
111
- class GenerateRequest(BaseModel):
112
- input_text: str = Field(..., description="The input text from the user.")
113
- history: Optional[List[Dict[str, str]]] = Field(None, description="Conversation history in the format [{\"role\": \"system\", \"content\": \"...\"}, {\"role\": \"user\", \"content\": \"...\"}, {\"role\": \"assistant\", \"content\": \"...\"}, ...]. The last message in history should be the user's most recent previous turn if provided.")
114
- stream: bool = Field(True, description="Whether to stream the response.")
115
- temperature: float = Field(1.0, ge=0.0, le=2.0, description="Controls the randomness of the output. Higher values mean more random output.")
116
- top_k: int = Field(50, ge=0, description="The number of highest probability vocabulary tokens to keep for top-k-filtering.")
117
- top_p: float = Field(1.0, ge=0.0, le=1.0, description="If set to float < 1, only the most probable tokens with probabilities that add up to top_p or higher are kept for generation.")
118
- repetition_penalty: float = Field(1.0, ge=0.0, description="The parameter for repetition penalty. 1.0 means no penalty.")
119
- frequency_penalty: float = Field(0.0, ge=0.0, description="Penalizes tokens based on their frequency in the generation.")
120
- presence_penalty: float = Field(0.0, ge=0.0, description="Penalizes tokens based on whether they appear in the generation.")
121
- num_beams: int = Field(1, ge=1, description="Number of beams for beam search. 1 means no beam search.")
122
- length_penalty: float = Field(1.0, ge=0.0, description="Exponential penalty to the length. 1.0 means no penalty. Set to values greater than 1.0 in order to encourage longer sequences, and to values less than 1.0 in order to encourage shorter sequences.")
123
- no_repeat_ngram_size: int = Field(0, ge=0, description="If set to int > 0, all ngrams of that size can only occur once.")
124
- early_stopping: bool = Field(False, description="Whether to stop the beam search when at least one beam reaches a stopping criteria (e.g. EOS token).")
125
- do_sample: bool = Field(True, description="Whether to use sampling.")
126
- use_mirostat: bool = Field(False, description="Whether to use Mirostat sampling.")
127
- mirostat_tau: float = Field(5.0, ge=0.0, description="Mirostat tau parameter.")
128
- mirostat_eta: float = Field(0.1, ge=0.0, description="Mirostat eta parameter.")
129
- max_new_tokens: int = Field(MAX_GENERATION_TOKENS, ge=1, description="The maximum number of tokens to generate.")
130
- system_prompt: Optional[str] = Field(None, description="Override the default system prompt for this request.")
131
- seed: Optional[int] = Field(None, description="Random seed for generation.")
132
- stop_sequences: Optional[List[str]] = Field(None, description="List of strings that will stop generation.")
133
- tokenize_only: bool = Field(False, description="If true, only tokenize the input and return token count, not generate.")
134
- strip_trailing_whitespace: bool = Field(False, description="Whether to strip trailing whitespace from the final generated text.")
135
- remove_incomplete_sentences: bool = Field(False, description="Whether to remove the last sentence if it appears incomplete.")
136
- num_return_sequences: int = Field(1, ge=1, le=5, description="The number of sequences to return for non-streaming generation (ignored for streaming).")
137
- bad_words_ids: Optional[List[List[int]]] = Field(None, description="List of token ids that are not allowed to be generated.")
138
- forced_bos_token_id: Optional[int] = Field(None, description="The id of the token to force as the first generated token.")
139
- forced_eos_token_id: Optional[int] = Field(None, description="The id of the token to force as the last generated token.")
140
- renormalize_logits: Optional[bool] = Field(None, description="Whether to renormalize the logits before applying penalties and sampling.")
141
- suppress_tokens: Optional[List[int]] = Field(None, description="List of tokens that will be suppressed at generation.")
142
- begin_suppress_tokens: Optional[List[int]] = Field(None, description="List of tokens that will be suppressed at the beginning of the generation.")
143
- end_suppress_tokens: Optional[List[int]] = Field(None, description="List of tokens that will be suppressed at the end of the generation.")
144
- encoder_no_repeat_ngram_size: int = Field(0, ge=0, description="If set to int > 0, all ngrams of that size can only occur once in the encoder_output.")
145
- min_length: int = Field(0, ge=0, description="The minimum length of the sequence to be generated.")
146
- max_length: Optional[int] = Field(None, description="The maximum total length (input + generated) of the sequence to be generated.")
147
- exponential_decay_length_penalty: Optional[Tuple[float, int, float]] = Field(None, description="Exponential decay length penalty parameters (decay_factor, start_index, threshold).")
148
- use_cache: bool = Field(True, description="Whether or not the model should use the past key/values attentions to speed up decoding.")
149
- top_k_dynamic: Optional[int] = Field(None, description="Dynamic top_k parameter.")
150
- top_p_dynamic: Optional[float] = Field(None, description="Dynamic top_p parameter.")
151
- typical_p: float = Field(1.0, ge=0.0, le=1.0, description="Typical P sampling parameter.")
152
- epsilon_cutoff: float = Field(0.0, ge=0.0, description="Epsilon cutoff parameter for Locally Typical Sampling.")
153
- eta_cutoff: float = Field(0.0, ge=0.0, description="Eta cutoff parameter for Locally Typical Sampling.")
154
- temperature_cutoff: Optional[float] = Field(None, ge=0.0, description="Temperature cutoff parameter.")
155
- encoder_repetition_penalty: float = Field(1.0, ge=0.0, description="The parameter for repetition penalty on encoder_outputs.")
156
- max_time: Optional[float] = Field(None, ge=0.0, description="The maximum amount of time in seconds the generation process can take.")
157
- output_watermark: bool = Field(False, description="Whether or not to output watermark information (if supported by the model).")
158
- remove_input_from_output: bool = Field(False, description="Whether to remove the input tokens from the output.")
159
- eos_token_id_override: Optional[int] = Field(None, description="Override the EOS token id for generation.")
160
- pad_token_id_override: Optional[int] = Field(None, description="Override the PAD token id for generation.")
161
- bos_token_id_override: Optional[int] = Field(None, description="Override the BOS token id for generation.")
162
- repetition_penalty_range: Optional[int] = Field(None, ge=0, description="The number of tokens to consider for repetition penalty.")
163
- diversity_penalty: float = Field(0.0, ge=0.0, description="The diversity penalty parameter for diverse beam search.")
164
- num_beam_groups: int = Field(1, ge=1, description="Number of groups to divide beams into in diverse beam search.")
165
-
166
- return_dict_in_generate: bool = Field(False, description="Whether or not to return a dictionary from model.generate().")
167
- output_attentions: bool = Field(False, description="Whether or not to return attentions from model.generate().")
168
- output_hidden_states: bool = Field(False, description="Whether or not to return hidden states from model.generate().")
169
- output_scores: bool = Field(False, description="Whether or not to return scores from model.generate().")
170
- return_token_logprobs: bool = Field(False, description="Whether to return token logprobs in the streaming response (only for stream=True).")
171
- return_text_from_sequence: bool = Field(True, description="Whether to decode the sequence back into text in the response.")
172
- length_normalization_factor: Optional[float] = Field(None, description="The length normalization factor for beam search.")
173
- min_new_tokens: int = Field(0, ge=0, description="The minimum number of new tokens to generate.")
174
- do_normalize_logits: bool = Field(False, description="Whether to normalize the logits before applying sampling/greedy decoding.")
175
-
176
- return_generation_inputs: bool = Field(False, description="Whether or not to return generation inputs from model.generate().")
177
- return_unused_generate_parameters: bool = Field(False, description="Whether or not to return unused generate parameters from model.generate().")
178
- use_fast_tokenizer: bool = Field(True, description="Whether to use a fast tokenizer if available.")
179
- model_kwargs: Optional[Dict[str, Any]] = Field(None, description="Additional model keyword arguments for generate().")
180
- tokenizer_kwargs: Optional[Dict[str, Any]] = Field(None, description="Additional tokenizer keyword arguments for encode().")
181
-
182
- return_only_text: bool = Field(False, description="If true, only return the generated text (plain text for streaming, JSON list of strings for non-streaming num_return_sequences > 1). Other metadata will be omitted.")
183
-
184
-
185
- @validator('stop_sequences')
186
- def validate_stop_sequences(cls, v):
187
- if v is not None:
188
- if not all(isinstance(seq, str) for seq in v):
189
- raise ValueError('Each stop sequence must be a string')
190
- return v
191
-
192
- @validator('bad_words_ids')
193
- def validate_bad_words_ids(cls, v):
194
- if v is not None:
195
- if not all(isinstance(word_id_list, list) and all(isinstance(token_id, int) for token_id in word_id_list) for word_id_list in v):
196
- raise ValueError('bad_words_ids must be a list of lists of integers')
197
- return v
198
-
199
- @validator('exponential_decay_length_penalty')
200
- def validate_exponential_decay_length_penalty(cls, v):
201
- if v is not None:
202
- if not (isinstance(v, (list, tuple)) and len(v) == 3 and
203
- isinstance(v[0], (int, float)) and v[0] > 0 and
204
- isinstance(v[1], int) and v[1] >= 0 and
205
- isinstance(v[2], (int, float))):
206
- raise ValueError('exponential_decay_length_penalty must be a tuple/list of 3 numbers (decay_factor, start_index, threshold)')
207
- return v
208
-
209
-
210
- class TokenizeRequest(BaseModel):
211
- text: Union[str, List[str]] = Field(..., description="Text or list of texts to tokenize.")
212
- add_special_tokens: bool = Field(True, description="Whether to add special tokens.")
213
- is_split_into_words: bool = Field(False, description="Whether the input text is pre-tokenized.")
214
- return_token_type_ids: bool = Field(False, description="Whether to return token type IDs.")
215
- padding: Union[bool, str] = Field(False, description="Enable padding. Can be 'longest', 'max_length', 'do_not_pad' or True/False.")
216
- truncation: Union[bool, str] = Field(False, description="Enable truncation. Can be 'only_first', 'only_second', 'longest_first', 'do_not_truncate' or True/False.")
217
- max_length: Optional[int] = Field(None, ge=1, description="Maximum length for padding and truncation.")
218
- return_tensors: Optional[str] = Field(None, description="The type of tensors to return ('pt' for PyTorch, 'tf' for TensorFlow, 'np' for NumPy).")
219
- return_attention_mask: Optional[bool] = Field(None, description="Whether to return the attention mask.")
220
- return_offsets_mapping: Optional[bool] = Field(None, description="Whether to return offsets mapping.")
221
- return_length: Optional[bool] = Field(None, description="Whether to return the length of the encoded sequences.")
222
- verbose: bool = Field(False, description="Whether to print verbose information.")
223
- tokenizer_kwargs: Optional[Dict[str, Any]] = Field(None, description="Additional tokenizer keyword arguments.")
224
-
225
-
226
- class DecodeRequest(BaseModel):
227
- token_ids: List[int] = Field(..., description="List of token IDs to decode.")
228
- skip_special_tokens: bool = Field(True, description="Whether to skip special tokens during decoding.")
229
- clean_up_tokenization_spaces: bool = Field(True, description="Whether to remove leading/trailing whitespace and double spaces.")
230
- decode_kwargs: Optional[Dict[str, Any]] = Field(None, description="Additional decode keyword arguments.")
231
-
232
-
233
- class SystemPromptUpdateRequest(BaseModel):
234
- system_prompt: str = Field(..., description="The new global system prompt.")
235
-
236
- class ModelReloadRequest(BaseModel):
237
- model_name: Optional[str] = Field(None, description="New model name to load. If None, reloads the current model.")
238
- trust_remote_code: Optional[bool] = Field(None, description="Override trust_remote_code setting.")
239
- enable_flash_attention_2: Optional[bool] = Field(None, description="Override enable_flash_attention_2 setting.")
240
- torch_dtype: Optional[str] = Field(None, description="Override torch_dtype setting (e.g., 'float16', 'bfloat16').")
241
- model_kwargs: Optional[Dict[str, Any]] = Field(None, description="Additional model keyword arguments for from_pretrained().")
242
- tokenizer_kwargs: Optional[Dict[str, Any]] = Field(None, description="Additional tokenizer keyword arguments for from_pretrained().")
243
-
244
- def format_conversation(input_text: str, history: Optional[List[Dict[str, str]]], system_prompt: Optional[str]) -> str:
245
- full_history: List[Dict[str, str]] = []
246
- used_system_prompt = system_prompt if system_prompt is not None else SYSTEM_PROMPT
247
- if not history or history[0].get("role") != "system" or history[0].get("content") != used_system_prompt:
248
- full_history.append({"role": "system", "content": used_system_prompt})
249
-
250
- if history:
251
- full_history.extend(history)
252
-
253
- if not full_history or full_history[-1].get("role") != "user" or full_history[-1].get("content") != input_text:
254
- full_history.append({"role": "user", "content": input_text})
255
-
256
- if global_tokenizer and hasattr(global_tokenizer, 'apply_chat_template') and global_tokenizer.chat_template:
257
- try:
258
- return global_tokenizer.apply_chat_template(full_history, tokenize=False, add_generation_prompt=True)
259
- except Exception as e:
260
- logging.error(f"Failed to apply chat template: {e}. Falling back to manual formatting.")
261
- pass
262
-
263
- formatted_text = ""
264
- for i, message in enumerate(full_history):
265
- if i == 0 and message["role"] == "system" and len(full_history) > 1 and full_history[1].get("role") == "system":
266
- continue
267
-
268
- if message["role"] == "system":
269
- formatted_text += f"{message['content'].strip()}\n\n"
270
- elif message["role"] == "user":
271
- formatted_text += f"Usuario: {message['content'].strip()}\n"
272
- elif message["role"] == "assistant":
273
- formatted_text += f"Bot: {message['content'].strip()}\n"
274
-
275
- if not formatted_text.endswith("Bot:"):
276
- formatted_text += "Bot:"
277
-
278
- return formatted_text.strip()
279
-
280
- def truncate_encoded_ids(input_ids: torch.Tensor, max_length: int) -> torch.Tensor:
281
- if input_ids.shape[-1] > max_length:
282
- return input_ids[:, -max_length:]
283
- return input_ids
284
-
285
- def apply_seed(seed: Optional[int]):
286
- if seed is not None:
287
- torch.manual_seed(seed)
288
- if torch.cuda.is_available():
289
- torch.cuda.manual_seed_all(seed)
290
- random.seed(seed)
291
-
292
- def get_stopping_criteria(req: GenerateRequest, initial_ids: torch.Tensor, tokenizer: AutoTokenizer) -> StoppingCriteriaList:
293
- criteria = StoppingCriteriaList()
294
- max_len_from_req = None
295
- if req.max_length is not None and req.max_length > 0:
296
- max_len_from_req = req.max_length
297
- elif req.max_new_tokens is not None and req.max_new_tokens > 0:
298
- max_len_from_req = initial_ids.shape[-1] + req.max_new_tokens
299
- else:
300
- max_len_from_req = initial_ids.shape[-1] + MAX_GENERATION_TOKENS
301
-
302
-
303
- if max_len_from_req is not None and max_len_from_req > 0:
304
- criteria.append(MaxLengthCriteria(max_len_from_req))
305
-
306
-
307
- if req.min_length is not None and req.min_length > 0:
308
- eos_token_id = req.eos_token_id_override if req.eos_token_id_override is not None else global_tokens.get("eos_token_id", -1)
309
- criteria.append(MinLengthLogitsProcessor(initial_ids.shape[-1] + req.min_length, eos_token_id))
310
-
311
- if req.stop_sequences:
312
- criteria.append(StopSequenceCriteria(req.stop_sequences, tokenizer))
313
-
314
-
315
- return criteria
316
-
317
-
318
- def generate_next_token_sync(
319
- input_ids,
320
- past_key_values,
321
- gen_cfg: GenerationConfig,
322
- device: str
323
- ) -> Tuple[torch.Tensor, Any, Optional[float], Optional[torch.Tensor], Any, Any]:
324
- with torch.no_grad():
325
- outputs = global_model(
326
- input_ids, past_key_values=past_key_values,
327
- use_cache=gen_cfg.use_cache, return_dict=True,
328
- output_attentions=gen_cfg.output_attentions,
329
- output_hidden_states=gen_cfg.output_hidden_states,
330
- output_scores=gen_cfg.output_scores,
331
- )
332
- logits = outputs.logits[:, -1, :]
333
- past = outputs.past_key_values
334
-
335
- scores = outputs.scores if gen_cfg.output_scores else None
336
- attentions = outputs.attentions if gen_cfg.output_attentions else None
337
- hidden_states = outputs.hidden_states if gen_cfg.output_hidden_states else None
338
-
339
- step_logits_for_criteria = logits.clone()
340
-
341
- if gen_cfg.do_normalize_logits:
342
- logits = F.log_softmax(logits, dim=-1)
343
-
344
- if gen_cfg.do_sample:
345
- if gen_cfg.use_mirostat_mode == 1 and hasattr(global_model, 'mirostat_sample_logits'):
346
- token = global_model.mirostat_sample_logits(
347
- logits=logits,
348
- temperature=gen_cfg.temperature,
349
- mirostat_tau=gen_cfg.mirostat_tau,
350
- mirostat_eta=gen_cfg.mirostat_eta
351
- ).unsqueeze(0).to(device)
352
- else:
353
- logits = logits / gen_cfg.temperature
354
-
355
- if gen_cfg.temperature_cutoff is not None and gen_cfg.temperature_cutoff > 0:
356
- logits = torch.where(logits < gen_cfg.temperature_cutoff, torch.tensor(-float('Inf')).to(logits.device), logits)
357
-
358
- if gen_cfg.top_k:
359
- topk_values, topk_indices = torch.topk(logits, gen_cfg.top_k)
360
- logits[logits < topk_values[:, -1]] = -float('Inf')
361
-
362
- if gen_cfg.top_p < 1.0:
363
- sorted_logits, sorted_indices = torch.sort(logits, dim=-1, descending=True)
364
- cumulative_probs = torch.cumsum(torch.softmax(sorted_logits, dim=-1), dim=-1)
365
- sorted_indices_to_remove = cumulative_probs > gen_cfg.top_p
366
- sorted_indices_to_remove[..., 1:] = sorted_indices_to_remove[..., :-1].clone()
367
- sorted_indices_to_remove[..., 0] = False
368
- indices_to_remove = sorted_indices[sorted_indices_to_remove]
369
- logits[:, indices_to_remove] = -float('Inf')
370
-
371
- if gen_cfg.typical_p < 1.0:
372
- probs = torch.softmax(logits, dim=-1)
373
- entropy = torch.distributions.Categorical(probs).entropy()
374
- probs_sorted, indices_sorted = torch.sort(probs, dim=-1, descending=True)
375
- cumsum_probs_sorted = torch.cumsum(probs_sorted, dim=-1)
376
- mask = cumsum_probs_sorted < gen_cfg.typical_p * entropy.exp()
377
- indices_to_remove = indices_sorted[~mask]
378
- logits[:, indices_to_remove] = -float('Inf')
379
-
380
-
381
- if gen_cfg.epsilon_cutoff is not None and gen_cfg.epsilon_cutoff > 0:
382
- probs = torch.softmax(logits, dim=-1)
383
- mask = probs < gen_cfg.epsilon_cutoff
384
- logits[:, mask] = -float('Inf')
385
-
386
- if gen_cfg.eta_cutoff is not None and gen_cfg.eta_cutoff > 0:
387
- probs = torch.softmax(logits, dim=-1)
388
- mask = probs > gen_cfg.eta_cutoff
389
- logits[:, ~mask] = -float('Inf')
390
-
391
- probs = torch.softmax(logits, dim=-1)
392
- token = torch.multinomial(probs, 1)
393
- else:
394
- token = torch.argmax(logits, dim=-1, keepdim=True)
395
-
396
- token_logprob = None
397
- if gen_cfg.output_scores:
398
- log_probs = F.log_softmax(step_logits_for_criteria, dim=-1)
399
- if 0 <= token.squeeze().item() < log_probs.shape[-1]:
400
- token_logprob = float(log_probs[:, token.squeeze()].item())
401
- else:
402
- token_logprob = None
403
-
404
-
405
- return token, past, token_logprob, step_logits_for_criteria, attentions, hidden_states
406
-
407
- def post_process_text(text: str, strip_trailing_whitespace: bool, remove_incomplete_sentences: bool) -> str:
408
- if strip_trailing_whitespace:
409
- text = text.rstrip()
410
- if remove_incomplete_sentences:
411
- for terminator in ['.', '!', '?', '\n']:
412
- last_terminator = text.rfind(terminator)
413
- if last_terminator != -1:
414
- text = text[:last_terminator + 1]
415
- break
416
- return text
417
-
418
- async def stream_generation_logic(req: GenerateRequest, initial_ids: torch.Tensor, gen_cfg: GenerationConfig, device: str) -> AsyncGenerator[str, None]:
419
- past = None
420
- generated_tokens_count = 0
421
- eos_token_id = req.eos_token_id_override if req.eos_token_id_override is not None else global_tokens.get("eos_token_id")
422
- pad_token_id = req.pad_token_id_override if req.pad_token_id_override is not None else global_tokens.get("pad_token_id", eos_token_id)
423
- stop_token_ids = {eos_token_id} if eos_token_id is not None else set()
424
- if pad_token_id is not None and pad_token_id != eos_token_id:
425
- stop_token_ids.add(pad_token_id)
426
-
427
- current_ids = initial_ids
428
- start_time = time.time()
429
- total_ids_list = initial_ids.tolist()[0]
430
- finish_reason = "unknown"
431
-
432
- stopping_criteria = get_stopping_criteria(req, initial_ids, global_tokenizer)
433
-
434
- last_step_logits = None
435
- accumulated_text_for_processing = ""
436
-
437
- try:
438
- while True:
439
- if generated_tokens_count >= req.max_new_tokens:
440
- finish_reason = "max_new_tokens"
441
- break
442
- if req.max_time is not None and (time.time() - start_time) > req.max_time:
443
- finish_reason = "time"
444
- break
445
-
446
- input_ids_sync = current_ids if past is None else token
447
-
448
- token, past, token_logprob, step_logits, attentions, hidden_states = await asyncio.to_thread(
449
- generate_next_token_sync,
450
- input_ids_sync,
451
- past,
452
- gen_cfg,
453
- device
454
- )
455
- last_step_logits = step_logits
456
-
457
- generated_token_id = token[0].item()
458
- total_ids_list.append(generated_token_id)
459
-
460
- text = global_tokenizer.decode([generated_token_id], skip_special_tokens=True)
461
- accumulated_text_for_processing += text
462
-
463
- chunk_payload: Dict[str, Any] = {
464
- "type": "token",
465
- "text": text,
466
- "token_id": generated_token_id,
467
- "generated_tokens_count": generated_tokens_count + 1,
468
- }
469
- if req.return_token_logprobs and token_logprob is not None:
470
- chunk_payload["logprob"] = token_logprob
471
-
472
- yield json.dumps(chunk_payload) + "\n"
473
-
474
- if generated_token_id in stop_token_ids:
475
- finish_reason = "eos_token"
476
- break
477
-
478
- current_full_ids_tensor = torch.tensor([total_ids_list], device=device)
479
- if stopping_criteria(current_full_ids_tensor, step_logits):
480
- finish_reason = "stopping_criteria"
481
- current_len = len(total_ids_list)
482
- initial_len = initial_ids.shape[-1]
483
-
484
- max_len_crit_met = any(isinstance(c, MaxLengthCriteria) for c in stopping_criteria) and \
485
- ( (req.max_new_tokens is not None and current_len >= (initial_len + req.max_new_tokens)) or
486
- (req.max_length is not None and current_len >= req.max_length) )
487
- stop_seq_crit_met = any(isinstance(c, StopSequenceCriteria) for c in stopping_criteria) and req.stop_sequences and \
488
- any(seq in global_tokenizer.decode(total_ids_list[initial_len:], skip_special_tokens=True) for seq in req.stop_sequences)
489
-
490
- if max_len_crit_met:
491
- if req.max_new_tokens is not None and current_len >= (initial_len + req.max_new_tokens):
492
- finish_reason = "max_new_tokens"
493
- elif req.max_length is not None and current_len >= req.max_length:
494
- finish_reason = "max_length"
495
-
496
- if stop_seq_crit_met:
497
- finish_reason = "stop_sequence"
498
-
499
-
500
- break
501
-
502
-
503
- current_ids = token
504
- generated_tokens_count += 1
505
-
506
- final_text_raw = global_tokenizer.decode(total_ids_list[initial_ids.shape[-1]:], skip_special_tokens=True)
507
- if req.stop_sequences and finish_reason == "stop_sequence":
508
- for stop_seq in req.stop_sequences:
509
- if stop_seq and stop_seq in final_text_raw:
510
- final_text_raw = final_text_raw.split(stop_seq, 1)[0]
511
- break
512
-
513
- final_text_processed = post_process_text(final_text_raw, req.strip_trailing_whitespace, req.remove_incomplete_sentences)
514
-
515
-
516
- final_payload: Dict[str, Any] = {
517
- "type": "done",
518
- "total_prompt_tokens": initial_ids.shape[-1],
519
- "total_generated_tokens": generated_tokens_count,
520
- "total_sequence_tokens": len(total_ids_list),
521
- "final_text": final_text_processed,
522
- "finish_reason": finish_reason
523
- }
524
- yield json.dumps(final_payload) + "\n"
525
-
526
-
527
- except Exception as e:
528
- logging.exception("Streaming generation error:")
529
- error_payload = {"type": "error", "message": str(e)}
530
- yield json.dumps(error_payload) + "\n"
531
-
532
- finally:
533
- await cleanup(device)
534
-
535
- async def plain_text_stream_processor(json_stream_generator: AsyncGenerator[str, None], req: GenerateRequest) -> AsyncGenerator[str, None]:
536
- accumulated_text = ""
537
- try:
538
- async for chunk in json_stream_generator:
539
- try:
540
- data = json.loads(chunk)
541
- if data.get("type") == "token":
542
- yield data.get("text", "")
543
- accumulated_text += data.get("text", "")
544
- elif data.get("type") == "done":
545
- final_text_raw = data.get("final_text", "")
546
- return
547
- elif data.get("type") == "error":
548
- raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=data.get("message", "An unknown error occurred during streaming."))
549
- except json.JSONDecodeError:
550
- logging.warning(f"Received non-JSON chunk in stream: {chunk.strip()}")
551
- except HTTPException as e:
552
- raise e
553
- except Exception as e:
554
- logging.exception("Error processing stream chunk for plain text:")
555
- raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Error processing stream chunk: {e}")
556
- except Exception as e:
557
- raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Streaming error: {e}")
558
-
559
-
560
- async def non_stream_generation_logic(req: GenerateRequest, initial_ids: torch.Tensor, gen_cfg: GenerationConfig, device: str) -> Dict[str, Any]:
561
- try:
562
- logits_processor_list = LogitsProcessorList()
563
-
564
- stopping_criteria_list = get_stopping_criteria(req, initial_ids, global_tokenizer)
565
-
566
-
567
- with torch.no_grad():
568
- out = global_model.generate(
569
- input_ids=initial_ids,
570
- generation_config=gen_cfg,
571
- return_dict_in_generate=True,
572
- output_scores=req.output_scores,
573
- output_attentions=req.output_attentions,
574
- output_hidden_states=req.output_hidden_states,
575
- num_return_sequences=req.num_return_sequences,
576
- bad_words_ids=req.bad_words_ids,
577
- suppress_tokens=req.suppress_tokens,
578
- begin_suppress_tokens=req.begin_suppress_tokens,
579
- end_suppress_tokens=req.end_suppress_tokens,
580
- logits_processor=logits_processor_list if logits_processor_list else None,
581
- stopping_criteria=stopping_criteria_list if stopping_criteria_list else None,
582
- )
583
-
584
- generated_data = []
585
- for i in range(req.num_return_sequences):
586
- if i >= len(out.sequences):
587
- break
588
-
589
- sequence = out.sequences[i]
590
- start_index = initial_ids.shape[-1]
591
- generated_ids_tensor = sequence[start_index:]
592
- full_sequence_ids = sequence.tolist()
593
-
594
- text = global_tokenizer.decode(generated_ids_tensor, skip_special_tokens=True)
595
-
596
- if req.stop_sequences:
597
- for stop_seq in req.stop_sequences:
598
- if stop_seq and stop_seq in text:
599
- text = text.split(stop_seq, 1)[0]
600
- break
601
-
602
- text = post_process_text(text, req.strip_trailing_whitespace, req.remove_incomplete_sentences)
603
-
604
- finish_reason = "length"
605
- eos_token_id = req.eos_token_id_override if req.eos_token_id_override is not None else global_tokens.get("eos_token_id")
606
- if len(generated_ids_tensor) > 0 and generated_ids_tensor[-1] == eos_token_id:
607
- finish_reason = "eos_token"
608
- elif len(generated_ids_tensor) >= gen_cfg.max_new_tokens:
609
- finish_reason = "max_new_tokens"
610
- elif req.max_length is not None and len(full_sequence_ids) >= req.max_length:
611
- finish_reason = "max_length"
612
- elif hasattr(out, 'max_time_exceeded') and out.max_time_exceeded:
613
- finish_reason = "time"
614
-
615
- if req.stop_sequences and finish_reason == "length":
616
- decoded_full_output = global_tokenizer.decode(full_sequence_ids, skip_special_tokens=True)
617
- if any(seq in decoded_full_output for seq in req.stop_sequences):
618
- finish_reason = "stop_sequence"
619
-
620
-
621
- item_data: Dict[str, Any] = {
622
- "text": text if req.return_text_from_sequence else None,
623
- "token_ids": generated_ids_tensor.tolist(),
624
- "generated_tokens_count": len(generated_ids_tensor),
625
- "finish_reason": finish_reason
626
- }
627
- if not req.remove_input_from_output:
628
- item_data["full_sequence_token_ids"] = full_sequence_ids
629
-
630
- if req.output_scores and hasattr(out, 'scores') and out.scores is not None:
631
- item_data["scores"] = "Scores output needs custom handling (complex structure)."
632
-
633
- if req.return_token_logprobs:
634
- item_data["token_logprobs"] = "Token logprobs require parsing scores output which is complex for batched/beamed generation."
635
-
636
- if req.output_attentions and hasattr(out, 'attentions') and out.attentions is not None:
637
- item_data["attentions"] = "Attentions output needs custom handling (too large)."
638
- if req.output_hidden_states and hasattr(out, 'hidden_states') and out.hidden_states is not None:
639
- item_data["hidden_states"] = "Hidden states output needs custom handling (too large)."
640
- if req.output_watermark and hasattr(out, 'watermark') and out.watermark is not None:
641
- item_data["watermark"] = out.watermark[i] if isinstance(out.watermark, list) and len(out.watermark) > i else out.watermark
642
-
643
-
644
- generated_data.append(item_data)
645
-
646
-
647
- response_payload: Dict[str, Any] = {
648
- "prompt_tokens": initial_ids.shape[-1],
649
- "generated_sequences": generated_data,
650
- }
651
- if req.num_return_sequences == 1 and generated_data:
652
- response_payload["total_tokens"] = response_payload["prompt_tokens"] + generated_data[0]["generated_tokens_count"]
653
-
654
- if req.return_dict_in_generate:
655
- raw_out_dict = {}
656
- for key in out.keys():
657
- if key not in ['sequences', 'scores', 'attentions', 'hidden_states', 'past_key_values', 'watermark', 'sequences_scores']:
658
- value = out[key]
659
- if isinstance(value, torch.Tensor):
660
- raw_out_dict[key] = value.tolist()
661
- else:
662
- raw_out_dict[key] = value
663
-
664
- response_payload["raw_generate_output"] = raw_out_dict
665
-
666
- return response_payload
667
-
668
- except Exception as e:
669
- logging.exception("Non-streaming generation error:")
670
- raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Generation error: {e}")
671
-
672
- async def cleanup(device: str):
673
- if device == "cuda":
674
- torch.cuda.empty_cache()
675
- gc.collect()
676
-
677
- app = FastAPI(title="Chatbot Profesional API", version="1.0.0")
678
-
679
- @app.on_event("startup")
680
- async def load_model():
681
- global global_model, global_tokenizer, global_tokens, MODEL_NAME, TRUST_REMOTE_CODE, ENABLE_FLASH_ATTENTION_2, TORCH_DTYPE, TORCH_DTYPE_STR
682
-
683
- try:
684
- TORCH_DTYPE = getattr(torch, TORCH_DTYPE_STR.lower(), torch.float16)
685
- if TORCH_DTYPE_STR.lower() == "bfloat16" and (not torch.cuda.is_available() or (torch.cuda.is_available() and not torch.cuda.get_device_properties(0).has_bfloat16)):
686
- logging.warning(f"Requested dtype {TORCH_DTYPE_STR} but not supported or CUDA not available. Falling back to float16.")
687
- TORCH_DTYPE = torch.float16
688
- elif not isinstance(TORCH_DTYPE, torch.dtype):
689
- logging.warning(f"Invalid TORCH_DTYPE specified: {TORCH_DTYPE_STR}. Falling back to float16.")
690
- TORCH_DTYPE = torch.float16
691
- except AttributeError:
692
- logging.warning(f"Invalid TORCH_DTYPE specified: {TORCH_DTYPE_STR}. Falling back to float16.")
693
- TORCH_DTYPE = torch.float16
694
-
695
- try:
696
- logging.info(f"Loading config for model: {MODEL_NAME}")
697
- config = AutoConfig.from_pretrained(MODEL_NAME, trust_remote_code=TRUST_REMOTE_CODE)
698
- logging.info("Config loaded.")
699
-
700
- logging.info(f"Loading tokenizer for model: {MODEL_NAME}")
701
- tokenizer_kwargs = {"config": config, "trust_remote_code": TRUST_REMOTE_CODE}
702
- global_tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, **tokenizer_kwargs)
703
- logging.info("Tokenizer loaded.")
704
-
705
- logging.info(f"Loading model: {MODEL_NAME} with dtype {TORCH_DTYPE} (CUDA: {torch.cuda.is_available()})")
706
- device = "cuda" if torch.cuda.is_available() else "cpu"
707
-
708
- model_kwargs = {"config": config, "torch_dtype": TORCH_DTYPE, "trust_remote_code": TRUST_REMOTE_CODE}
709
- if ENABLE_FLASH_ATTENTION_2 and device == "cuda":
710
- try:
711
- if hasattr(config, '_supports_flash_attn_2') and config._supports_flash_attn_2:
712
- model_kwargs['use_flash_attention_2'] = True
713
- logging.info("Flash Attention 2 enabled.")
714
- elif hasattr(AutoModelForCausalLM, '_supports_flash_attn_2') and AutoModelForCausalLM._supports_flash_attn_2(config):
715
- model_kwargs['use_flash_attention_2'] = True
716
- logging.info("Flash Attention 2 enabled via model check.")
717
- else:
718
- logging.warning("Flash Attention 2 requested but not supported by model config.")
719
- except Exception as e:
720
- logging.warning(f"Error checking for Flash Attention 2 support: {e}")
721
- pass
722
-
723
-
724
- global_model = AutoModelForCausalLM.from_pretrained(MODEL_NAME, **model_kwargs)
725
- global_model.to(device)
726
- global_model.eval()
727
- logging.info("Model loaded successfully.")
728
-
729
- global_tokens["eos_token_id"] = global_tokenizer.eos_token_id
730
- global_tokens["pad_token_id"] = global_tokenizer.pad_token_id
731
- if global_tokens["pad_token_id"] is None and global_tokens["eos_token_id"] is not None:
732
- global_tokens["pad_token_id"] = global_tokens["eos_token_id"]
733
- if global_model.config.pad_token_id is None:
734
- global_model.config.pad_token_id = global_tokens["pad_token_id"]
735
- elif global_tokens["pad_token_id"] is None and global_tokens["eos_token_id"] is None:
736
- logging.warning("Neither EOS nor PAD token is defined for this tokenizer/model.")
737
-
738
- except Exception as e:
739
- logging.exception("Failed to load model or tokenizer:")
740
- global_model = None
741
- global_tokenizer = None
742
- global_tokens = {}
743
-
744
- html_code = """
745
- <!DOCTYPE html>
746
- <html lang="es">
747
- <head>
748
- <meta charset="UTF-8" />
749
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
750
- <title>Chatbot Profesional</title>
751
- <style>
752
- body { font-family: Arial, sans-serif; margin: 20px; }
753
- #chatbox { width: 100%; height: 400px; border: 1px solid #ccc; padding: 10px; overflow-y: scroll; margin-bottom: 10px; }
754
- #user-input { width: calc(100% - 100px); padding: 8px; box-sizing: border-box;}
755
- #send-btn { width: 90px; padding: 8px 0;}
756
- #input-area { display: flex;}
757
- </style>
758
- </head>
759
- <body>
760
- <h1>Chatbot Profesional (POST API)</h1>
761
- <div id="chatbox"></div>
762
- <div id="input-area">
763
- <input type="text" id="user-input" placeholder="Escribe tu mensaje aquí..." autocomplete="off"/>
764
- <button id="send-btn">Enviar</button>
765
- </div>
766
- <script>
767
- const chatbox = document.getElementById('chatbox');
768
- const userInput = document.getElementById('user-input');
769
- const sendBtn = document.getElementById('send-btn');
770
-
771
- let conversationHistory = [];
772
- const DEFAULT_SYSTEM_PROMPT = "Eres un asistente profesional y servicial.";
773
- let currentSystemPrompt = DEFAULT_SYSTEM_PROMPT;
774
- let botMessageElement = null;
775
-
776
- function appendMessage(sender, text, isStreaming = false) {
777
- let msg;
778
- if (isStreaming && botMessageElement) {
779
- botMessageElement.textContent += text;
780
- } else {
781
- msg = document.createElement('p');
782
- msg.innerHTML = `<strong>${sender}:</strong> `;
783
- const textNode = document.createTextNode(text);
784
- msg.appendChild(textNode);
785
- chatbox.appendChild(msg);
786
- if (sender === 'Bot' && isStreaming) {
787
- botMessageElement = textNode;
788
- } else {
789
- botMessageElement = null;
790
- }
791
- }
792
- chatbox.scrollTop = chatbox.scrollHeight;
793
- }
794
-
795
- function updateHistory(role, content) {
796
- conversationHistory.push({ "role": role, "content": content });
797
- const maxHistorySize = 10;
798
- if (conversationHistory.length > maxHistorySize * 2) {
799
- conversationHistory = conversationHistory.slice(-(maxHistorySize * 2));
800
- }
801
- }
802
-
803
- async function sendMessage() {
804
- const text = userInput.value;
805
- if (!text) {
806
- return;
807
- }
808
- appendMessage('Usuario', text);
809
- updateHistory("user", text);
810
- userInput.value = '';
811
- botMessageElement = null;
812
-
813
- const messagePayload = {
814
- input_text: text,
815
- history: conversationHistory,
816
- system_prompt: currentSystemPrompt,
817
- stream: true,
818
- temperature: 1.0,
819
- top_k: 50,
820
- top_p: 1.0,
821
- repetition_penalty: 1.0,
822
- frequency_penalty: 0.0,
823
- presence_penalty: 0.0,
824
- num_beams: 1,
825
- length_penalty: 1.0,
826
- no_repeat_ngram_size: 0,
827
- early_stopping: false,
828
- do_sample: true,
829
- use_mirostat: false,
830
- mirostat_tau: 5.0,
831
- mirostat_eta: 0.1,
832
- max_new_tokens: 512,
833
- num_return_sequences: 1,
834
- return_token_logprobs: true
835
- };
836
-
837
- try {
838
- const response = await fetch('/generate', {
839
- method: 'POST',
840
- headers: {
841
- 'Content-Type': 'application/json',
842
- // Add API Key header if needed
843
- // 'X-API-Key': 'YOUR_API_KEY_HERE'
844
- },
845
- body: JSON.stringify(messagePayload),
846
- });
847
-
848
- if (!response.ok) {
849
- const errorData = await response.json();
850
- throw new Error(`API Error: ${response.status} ${response.statusText} - ${errorData.detail || errorData.error}`);
851
- }
852
-
853
- const reader = response.body.getReader();
854
- const decoder = new TextDecoder();
855
- let buffer = '';
856
- let currentBotResponse = "";
857
-
858
- while (true) {
859
- const { value, done } = await reader.read();
860
- if (done) break;
861
-
862
- buffer += decoder.decode(value, { stream: true });
863
-
864
- const lines = buffer.split('\n');
865
- buffer = lines.pop();
866
-
867
- for (const line of lines) {
868
- if (line.trim() === '') continue;
869
- try {
870
- const data = JSON.parse(line);
871
- if (data.type === 'token') {
872
- currentBotResponse += data.text;
873
- appendMessage('Bot', data.text, true);
874
- console.log('Token:', data.token_id, 'Text:', data.text, 'Logprob:', data.logprob);
875
- } else if (data.type === 'done') {
876
- console.log('Generation done', data);
877
- if (data.total_tokens !== undefined) {
878
- appendMessage('System', `Generated ${data.total_tokens} tokens. Finish reason: ${data.finish_reason}`);
879
- }
880
- if (data.final_text !== undefined) {
881
- updateHistory("assistant", data.final_text);
882
- } else if (currentBotResponse) {
883
- updateHistory("assistant", currentBotResponse);
884
- }
885
-
886
- } else if (data.type === 'error') {
887
- appendMessage('Error', data.message);
888
- currentBotResponse = "";
889
- }
890
- } catch (e) {
891
- console.error('Failed to parse stream chunk:', e, line);
892
- appendMessage('Error', 'Failed to process stream.');
893
- currentBotResponse = "";
894
- reader.cancel();
895
- return;
896
- }
897
- }
898
- }
899
-
900
- if (buffer.trim() !== '') {
901
- try {
902
- const data = JSON.parse(buffer);
903
- if (data.type === 'token') {
904
- currentBotResponse += data.text;
905
- appendMessage('Bot', data.text, true);
906
- console.log('Token:', data.token_id, 'Text:', data.text, 'Logprob:', data.logprob);
907
- } else if (data.type === 'done') {
908
- console.log('Generation done', data);
909
- if (data.total_tokens !== undefined) {
910
- appendMessage('System', `Generated ${data.total_tokens} tokens. Finish reason: ${data.finish_reason}`);
911
- }
912
- if (data.final_text !== undefined) {
913
- updateHistory("assistant", data.final_text);
914
- } else if (currentBotResponse) {
915
- updateHistory("assistant", currentBotResponse);
916
- }
917
- } else if (data.type === 'error') {
918
- appendMessage('Error', data.message);
919
- currentBotResponse = "";
920
- }
921
- } catch (e) {
922
- console.error('Failed to parse remaining buffer:', e, buffer);
923
- appendMessage('Error', 'Failed to process remaining stream data.');
924
- currentBotResponse = "";
925
- }
926
- }
927
-
928
-
929
- if (currentBotResponse && !botMessageElement) {
930
- updateHistory("assistant", currentBotResponse);
931
- }
932
- botMessageElement = null;
933
- currentBotResponse = "";
934
-
935
-
936
- } catch (error) {
937
- console.error('Send message error:', error);
938
- appendMessage('Error', error.message || 'An unknown error occurred.');
939
- botMessageElement = null;
940
- currentBotResponse = "";
941
- }
942
- }
943
-
944
- sendBtn.onclick = sendMessage;
945
-
946
- userInput.addEventListener('keypress', function(event) {
947
- if (event.key === 'Enter') {
948
- event.preventDefault();
949
- sendMessage();
950
- }
951
- });
952
-
953
-
954
- </script>
955
- </body>
956
- </html>
957
- """
958
-
959
- @app.get("/", response_class=HTMLResponse, summary="Interactive HTML interface")
960
- async def root():
961
- return HTMLResponse(content=html_code)
962
-
963
- @app.get("/health", summary="Check API health and model status")
964
- async def health():
965
- if global_model is None or global_tokenizer is None:
966
- status_detail = "loading model"
967
- if global_tokenizer is None and global_model is not None:
968
- status_detail = "tokenizer not loaded"
969
- elif global_model is None and global_tokenizer is not None:
970
- status_detail = "model not loaded"
971
- elif global_model is None and global_tokenizer is None:
972
- status_detail = "model and tokenizer not loaded"
973
-
974
- return JSONResponse(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, content={"status": status_detail, "model_loaded": global_model is not None, "tokenizer_loaded": global_tokenizer is not None})
975
- return {"status": "ok", "model_loaded": True, "tokenizer_loaded": True}
976
-
977
- @app.get("/config", summary="Get API configuration")
978
- async def get_config():
979
- torch_dtype_str_out = str(TORCH_DTYPE).split('.')[-1] if isinstance(TORCH_DTYPE, torch.dtype) else str(TORCH_DTYPE)
980
-
981
- return JSONResponse({
982
- "model_name": MODEL_NAME,
983
- "system_prompt_default": SYSTEM_PROMPT,
984
- "max_context_tokens": MAX_CONTEXT_TOKENS,
985
- "max_generation_tokens": MAX_GENERATION_TOKENS,
986
- "cuda_available": torch.cuda.is_available(),
987
- "model_loaded": global_model is not None,
988
- "tokenizer_loaded": global_tokenizer is not None,
989
- "max_concurrent_generations": MAX_CONCURRENT_GENERATIONS,
990
- "trust_remote_code": TRUST_REMOTE_CODE,
991
- "enable_flash_attention_2": ENABLE_FLASH_ATTENTION_2,
992
- "torch_dtype": torch_dtype_str_out,
993
- "eos_token_id": global_tokens.get("eos_token_id"),
994
- "pad_token_id": global_tokens.get("pad_token_id"),
995
- "bos_token_id": global_tokenizer.bos_token_id if global_tokenizer else None,
996
- "api_key_required": API_KEY is not None
997
- })
998
-
999
- @app.get("/model/info", summary="Get detailed model information", dependencies=[Depends(get_api_key)])
1000
- async def get_model_info():
1001
- if global_model is None:
1002
- raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Model is not loaded.")
1003
- try:
1004
- config_dict = global_model.config.to_dict()
1005
- # Remove potentially large/complex entries if necessary
1006
- keys_to_remove = ['torch_dtype', '_attn_implementation', 'architectures', 'id2label', 'label2id', 'torch_dtype']
1007
- for key in keys_to_remove:
1008
- config_dict.pop(key, None)
1009
-
1010
- return JSONResponse({
1011
- "model_name": MODEL_NAME,
1012
- "is_loaded": True,
1013
- "device": str(global_model.device),
1014
- "torch_dtype": str(global_model.dtype),
1015
- "config": config_dict
1016
- })
1017
- except Exception as e:
1018
- raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Error getting model info: {e}")
1019
-
1020
-
1021
- @app.post("/tokenize", summary="Tokenize text", dependencies=[Depends(get_api_key)])
1022
- async def tokenize_endpoint(req: TokenizeRequest):
1023
- if global_tokenizer is None:
1024
- raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Tokenizer is not loaded.")
1025
-
1026
- try:
1027
- tokenizer_kwargs_final = req.tokenizer_kwargs or {}
1028
-
1029
- return_tensors = req.return_tensors if req.return_tensors is not None else None
1030
- if return_tensors is None and (req.return_attention_mask or req.return_offsets_mapping or req.return_length):
1031
- return_tensors = "pt"
1032
-
1033
- encoded = global_tokenizer(
1034
- req.text,
1035
- add_special_tokens=req.add_special_tokens,
1036
- return_token_type_ids=req.return_token_type_ids,
1037
- padding=req.padding,
1038
- truncation=req.truncation,
1039
- max_length=req.max_length,
1040
- is_split_into_words=req.is_split_into_words,
1041
- return_tensors=return_tensors,
1042
- return_attention_mask=req.return_attention_mask,
1043
- return_offsets_mapping=req.return_offsets_mapping,
1044
- return_length=req.return_length,
1045
- verbose=req.verbose,
1046
- **tokenizer_kwargs_final
1047
- )
1048
- if return_tensors:
1049
- return JSONResponse({k: v.tolist() if isinstance(v, torch.Tensor) else v for k, v in encoded.items()})
1050
- else:
1051
- if isinstance(req.text, str):
1052
- response_data = {
1053
- "tokens": encoded["input_ids"],
1054
- "token_count": len(encoded["input_ids"]),
1055
- "input_text": req.text
1056
- }
1057
- if "token_type_ids" in encoded: response_data["token_type_ids"] = encoded["token_type_ids"]
1058
- if "attention_mask" in encoded: response_data["attention_mask"] = encoded["attention_mask"]
1059
- if "offset_mapping" in encoded: response_data["offset_mapping"] = encoded["offset_mapping"]
1060
- if "length" in encoded: response_data["length"] = encoded["length"]
1061
- return JSONResponse(response_data)
1062
- elif isinstance(req.text, list):
1063
- results = []
1064
- for i, item_text in enumerate(req.text):
1065
- item_result: Dict[str, Any] = {"text": item_text}
1066
- if i < len(encoded["input_ids"]):
1067
- item_result["tokens"] = encoded["input_ids"][i]
1068
- item_result["token_count"] = len(encoded["input_ids"][i])
1069
- if "token_type_ids" in encoded and i < len(encoded["token_type_ids"]):
1070
- item_result["token_type_ids"] = encoded["token_type_ids"][i]
1071
- if "attention_mask" in encoded and i < len(encoded["attention_mask"]):
1072
- item_result["attention_mask"] = encoded["attention_mask"][i]
1073
- if "offset_mapping" in encoded and i < len(encoded["offset_mapping"]):
1074
- item_result["offset_mapping"] = encoded["offset_mapping"][i]
1075
- if "length" in encoded and i < len(encoded["length"]):
1076
- item_result["length"] = encoded["length"][i]
1077
- else:
1078
- item_result["tokens"] = []
1079
- item_result["token_count"] = 0
1080
- results.append(item_result)
1081
- return JSONResponse(results)
1082
-
1083
-
1084
- except Exception as e:
1085
- logging.exception("Tokenization error:")
1086
- raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Tokenization error: {e}")
1087
-
1088
- @app.post("/decode", summary="Decode token IDs to text", dependencies=[Depends(get_api_key)])
1089
- async def decode_endpoint(req: DecodeRequest):
1090
- if global_tokenizer is None:
1091
- raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Tokenizer is not loaded.")
1092
- try:
1093
- decode_kwargs = req.decode_kwargs or {}
1094
- text = global_tokenizer.decode(
1095
- req.token_ids,
1096
- skip_special_tokens=req.skip_special_tokens,
1097
- clean_up_tokenization_spaces=req.clean_up_tokenization_spaces,
1098
- **decode_kwargs
1099
- )
1100
- return JSONResponse({"text": text})
1101
- except Exception as e:
1102
- logging.exception("Decoding error:")
1103
- raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Decoding error: {e}")
1104
-
1105
- @app.post("/system_prompt", summary="Update global system prompt", dependencies=[Depends(get_api_key)])
1106
- async def update_system_prompt(req: SystemPromptUpdateRequest):
1107
- global SYSTEM_PROMPT
1108
- if req.system_prompt is not None:
1109
- SYSTEM_PROMPT = req.system_prompt.strip()
1110
- return JSONResponse({"status": "success", "message": "Global system prompt updated"})
1111
- else:
1112
- raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="System prompt cannot be null")
1113
-
1114
- @app.get("/system_prompt", summary="Get global system prompt", dependencies=[Depends(get_api_key)])
1115
- async def get_system_prompt():
1116
- return JSONResponse({"system_prompt": SYSTEM_PROMPT})
1117
-
1118
- @app.post("/model/reload", summary="Reload the model", dependencies=[Depends(get_api_key)])
1119
- async def reload_model(req: ModelReloadRequest):
1120
- global global_model, global_tokenizer, global_tokens, MODEL_NAME, TRUST_REMOTE_CODE, ENABLE_FLASH_ATTENTION_2, TORCH_DTYPE, TORCH_DTYPE_STR
1121
-
1122
- new_model_name = req.model_name if req.model_name else MODEL_NAME
1123
- new_trust_remote_code = req.trust_remote_code if req.trust_remote_code is not None else TRUST_REMOTE_CODE
1124
- new_enable_flash_attention_2 = req.enable_flash_attention_2 if req.enable_flash_attention_2 is not None else ENABLE_FLASH_ATTENTION_2
1125
- new_torch_dtype_str_req = req.torch_dtype if req.torch_dtype else TORCH_DTYPE_STR
1126
-
1127
- try:
1128
- new_torch_dtype = getattr(torch, new_torch_dtype_str_req.lower())
1129
- if new_torch_dtype_str_req.lower() == "bfloat16" and (not torch.cuda.is_available() or (torch.cuda.is_available() and not torch.cuda.get_device_properties(0).has_bfloat16)):
1130
- logging.warning(f"Requested dtype {new_torch_dtype_str_req} but not supported or CUDA not available. Falling back to float16.")
1131
- new_torch_dtype = torch.float16
1132
- elif not isinstance(new_torch_dtype, torch.dtype):
1133
- raise AttributeError
1134
- new_torch_dtype_str = str(new_torch_dtype).split('.')[-1]
1135
- except AttributeError:
1136
- raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"Invalid or unsupported torch_dtype: {new_torch_dtype_str_req}")
1137
-
1138
-
1139
- device = "cuda" if torch.cuda.is_available() else "cpu"
1140
-
1141
- async def _reload_model():
1142
- global global_model, global_tokenizer, global_tokens, MODEL_NAME, TRUST_REMOTE_CODE, ENABLE_FLASH_ATTENTION_2, TORCH_DTYPE, TORCH_DTYPE_STR
1143
- logging.info(f"Attempting to load model: {new_model_name}")
1144
- try:
1145
- logging.info("Unloading current model...")
1146
- await cleanup(device)
1147
- if global_model is not None:
1148
- del global_model
1149
- global_model = None
1150
- if global_tokenizer is not None:
1151
- del global_tokenizer
1152
- global_tokenizer = None
1153
- global_tokens = {}
1154
- torch.cuda.empty_cache()
1155
- gc.collect()
1156
- logging.info("Current model unloaded.")
1157
-
1158
- logging.info(f"Loading config for model: {new_model_name}")
1159
- config = AutoConfig.from_pretrained(new_model_name, trust_remote_code=new_trust_remote_code)
1160
- logging.info("Config loaded.")
1161
-
1162
- logging.info(f"Loading tokenizer for model: {new_model_name}")
1163
- tokenizer_kwargs = {"config": config, "trust_remote_code": new_trust_remote_code}
1164
- if req.tokenizer_kwargs:
1165
- tokenizer_kwargs.update(req.tokenizer_kwargs)
1166
- tokenizer = AutoTokenizer.from_pretrained(new_model_name, **tokenizer_kwargs)
1167
- logging.info("Tokenizer loaded.")
1168
-
1169
- logging.info(f"Loading model: {new_model_name} with dtype {new_torch_dtype_str} (CUDA: {torch.cuda.is_available()})")
1170
- model_kwargs = {"config": config, "torch_dtype": new_torch_dtype, "trust_remote_code": new_trust_remote_code}
1171
- if new_enable_flash_attention_2 and device == "cuda":
1172
- try:
1173
- if hasattr(config, '_supports_flash_attn_2') and config._supports_flash_attn_2:
1174
- model_kwargs['use_flash_attention_2'] = True
1175
- logging.info("Flash Attention 2 enabled for new model.")
1176
- elif hasattr(AutoModelForCausalLM, '_supports_flash_attn_2') and AutoModelForCausalLM._supports_flash_attn_2(config):
1177
- model_kwargs['use_flash_attention_2'] = True
1178
- logging.info("Flash Attention 2 enabled via model check for new model.")
1179
- else:
1180
- logging.warning("Flash Attention 2 requested but not supported by new model config.")
1181
- except Exception as e:
1182
- logging.warning(f"Error checking for Flash Attention 2 support for new model: {e}")
1183
- pass
1184
-
1185
- if req.model_kwargs:
1186
- model_kwargs.update(req.model_kwargs)
1187
-
1188
- model = AutoModelForCausalLM.from_pretrained(new_model_name, **model_kwargs)
1189
- model.to(device)
1190
- model.eval()
1191
- logging.info("New model loaded successfully.")
1192
-
1193
- global_model = model
1194
- global_tokenizer = tokenizer
1195
- global_tokens["eos_token_id"] = global_tokenizer.eos_token_id
1196
- global_tokens["pad_token_id"] = global_tokenizer.pad_token_id
1197
- if global_tokens["pad_token_id"] is None and global_tokens["eos_token_id"] is not None:
1198
- global_tokens["pad_token_id"] = global_tokens["eos_token_id"]
1199
- if global_model.config.pad_token_id is None:
1200
- global_model.config.pad_token_id = global_tokens["pad_token_id"]
1201
- elif global_tokens["pad_token_id"] is None and global_tokens["eos_token_id"] is None:
1202
- logging.warning("Neither EOS nor PAD token defined for new model.")
1203
-
1204
-
1205
- MODEL_NAME = new_model_name
1206
- TRUST_REMOTE_CODE = new_trust_remote_code
1207
- ENABLE_FLASH_ATTENTION_2 = new_enable_flash_attention_2
1208
- TORCH_DTYPE = new_torch_dtype
1209
- TORCH_DTYPE_STR = new_torch_dtype_str
1210
- if hasattr(global_tokenizer, 'use_fast'):
1211
- req.use_fast_tokenizer = global_tokenizer.use_fast
1212
-
1213
-
1214
- logging.info(f"Model successfully reloaded to: {MODEL_NAME}")
1215
- logging.info({"status": "success", "message": f"Model {new_model_name} loaded successfully."})
1216
-
1217
- except Exception as e:
1218
- logging.exception(f"Failed to load model {new_model_name}:")
1219
- global_model = None
1220
- global_tokenizer = None
1221
- global_tokens = {}
1222
- logging.error({"status": "error", "message": f"Failed to load model {new_model_name}: {e}. Model is now unloaded."})
1223
-
1224
-
1225
- asyncio.create_task(_reload_model())
1226
- return JSONResponse({"status": "info", "message": f"Attempting to load model {new_model_name} in background. Check logs for status."})
1227
-
1228
- @app.post("/model/unload", summary="Unload the model", dependencies=[Depends(get_api_key)])
1229
- async def unload_model():
1230
- global global_model, global_tokenizer, global_tokens
1231
- device = "cuda" if torch.cuda.is_available() else "cpu"
1232
- logging.info("Attempting to unload model.")
1233
- try:
1234
- await cleanup(device)
1235
- if global_model is not None:
1236
- del global_model
1237
- global_model = None
1238
- if global_tokenizer is not None:
1239
- del global_tokenizer
1240
- global_tokenizer = None
1241
- global_tokens = {}
1242
- torch.cuda.empty_cache()
1243
- gc.collect()
1244
- logging.info("Model unloaded successfully.")
1245
- return JSONResponse({"status": "success", "message": "Model unloaded successfully."})
1246
- except Exception as e:
1247
- logging.exception("Failed to unload model:")
1248
- raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to unload model: {e}")
1249
-
1250
- @app.get("/queue/status", summary="Get current generation queue status", dependencies=[Depends(get_api_key)])
1251
- async def queue_status():
1252
- try:
1253
- current_running = MAX_CONCURRENT_GENERATIONS - generation_semaphore._value
1254
- is_waiting = generation_semaphore.locked()
1255
-
1256
- return JSONResponse({
1257
- "max_concurrent": MAX_CONCURRENT_GENERATIONS,
1258
- "currently_running": current_running,
1259
- "available_slots": generation_semaphore._value
1260
- })
1261
- except Exception as e:
1262
- logging.exception("Error getting queue status:")
1263
- raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Error getting queue status: {e}")
1264
-
1265
- @app.get("/tokens/special", summary="Get special token IDs", dependencies=[Depends(get_api_key)])
1266
- async def get_special_tokens():
1267
- if global_tokenizer is None:
1268
- raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Tokenizer is not loaded.")
1269
- try:
1270
- return JSONResponse({
1271
- "eos_token": global_tokenizer.eos_token,
1272
- "eos_token_id": global_tokenizer.eos_token_id,
1273
- "pad_token": global_tokenizer.pad_token,
1274
- "pad_token_id": global_tokenizer.pad_token_id,
1275
- "bos_token": global_tokenizer.bos_token,
1276
- "bos_token_id": global_tokenizer.bos_token_id,
1277
- "unk_token": global_tokenizer.unk_token,
1278
- "unk_token_id": global_tokenizer.unk_token_id,
1279
- })
1280
- except Exception as e:
1281
- logging.exception("Error getting special tokens:")
1282
- raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Error getting special tokens: {e}")
1283
-
1284
- @app.post("/generate", summary="Generate text", dependencies=[Depends(get_api_key)])
1285
- async def generate_endpoint(req: GenerateRequest):
1286
- if global_model is None or global_tokenizer is None:
1287
- raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Model is not loaded. It may still be loading or failed to load.")
1288
-
1289
- device = "cuda" if torch.cuda.is_available() else "cpu"
1290
-
1291
- apply_seed(req.seed)
1292
-
1293
- try:
1294
- initial_prompt_text = format_conversation(req.input_text, req.history, req.system_prompt)
1295
- except Exception as e:
1296
- logging.exception("Error formatting conversation:")
1297
- raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"Error formatting conversation: {e}")
1298
-
1299
-
1300
- try:
1301
- tokenizer_encoding_kwargs = req.tokenizer_kwargs or {}
1302
-
1303
- encoded = global_tokenizer(initial_prompt_text, return_tensors="pt", add_special_tokens=False, **tokenizer_encoding_kwargs).to(device)
1304
- initial_ids_before_trunc = encoded.input_ids
1305
- initial_prompt_tokens_count_before_trunc = initial_ids_before_trunc.shape[-1]
1306
- ids = truncate_encoded_ids(initial_ids_before_trunc, MAX_CONTEXT_TOKENS)
1307
- current_prompt_tokens_count = ids.shape[-1]
1308
-
1309
-
1310
- except Exception as e:
1311
- logging.exception("Tokenizer error during encoding:")
1312
- await cleanup(device)
1313
- raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Tokenizer encoding error: {e}")
1314
-
1315
- if req.tokenize_only:
1316
- await cleanup(device)
1317
- return JSONResponse({
1318
- "prompt_tokens_count": initial_prompt_tokens_count_before_trunc,
1319
- "max_context_tokens": MAX_CONTEXT_TOKENS,
1320
- "truncated": initial_prompt_tokens_count_before_trunc > MAX_CONTEXT_TOKENS,
1321
- "input_text_processed": initial_prompt_text,
1322
- "input_ids_truncated": ids.tolist()[0]
1323
- })
1324
-
1325
- total_capacity = MAX_CONTEXT_TOKENS + MAX_GENERATION_TOKENS
1326
- total_requested_seq_len = current_prompt_tokens_count + req.max_new_tokens
1327
-
1328
-
1329
- async with generation_semaphore:
1330
- if req.stream or total_requested_seq_len <= total_capacity:
1331
-
1332
- gen_cfg = GenerationConfig(
1333
- temperature=req.temperature,
1334
- top_k=req.top_k,
1335
- top_p=req.top_p,
1336
- repetition_penalty=req.repetition_penalty,
1337
- frequency_penalty=req.frequency_penalty,
1338
- presence_penalty=req.presence_penalty,
1339
- num_beams=req.num_beams if not req.stream else 1,
1340
- length_penalty=req.length_penalty,
1341
- no_repeat_ngram_size=req.no_repeat_ngram_size,
1342
- early_stopping=req.early_stopping,
1343
- do_sample=req.do_sample,
1344
- use_mirostat_mode=1 if req.use_mirostat else 0,
1345
- mirostat_tau=req.mirostat_tau,
1346
- mirostat_eta=req.mirostat_eta,
1347
- max_new_tokens=req.max_new_tokens,
1348
- eos_token_id=req.eos_token_id_override if req.eos_token_id_override is not None else global_tokens.get("eos_token_id"),
1349
- pad_token_id=req.pad_token_id_override if req.pad_token_id_override is not None else global_tokens.get("pad_token_id"),
1350
- bos_token_id=req.bos_token_id_override if req.bos_token_id_override is not None else global_tokenizer.bos_token_id,
1351
- num_return_sequences=req.num_return_sequences if not req.stream else 1,
1352
- bad_words_ids=req.bad_words_ids,
1353
- forced_bos_token_id=req.forced_bos_token_id,
1354
- forced_eos_token_id=req.forced_eos_token_id,
1355
- renormalize_logits=req.renormalize_logits,
1356
- suppress_tokens=req.suppress_tokens,
1357
- begin_suppress_tokens=req.begin_suppress_tokens,
1358
- end_suppress_tokens=req.end_suppress_tokens,
1359
- encoder_no_repeat_ngram_size=req.encoder_no_repeat_ngram_size,
1360
- min_length=req.min_length,
1361
- max_length=req.max_length,
1362
- exponential_decay_length_penalty=req.exponential_decay_length_penalty,
1363
- use_cache=req.use_cache,
1364
- typical_p=req.typical_p,
1365
- epsilon_cutoff=req.epsilon_cutoff,
1366
- eta_cutoff=req.eta_cutoff,
1367
- temperature_cutoff=req.temperature_cutoff,
1368
- encoder_repetition_penalty=req.encoder_repetition_penalty,
1369
- max_time=req.max_time,
1370
- output_watermark=req.output_watermark,
1371
- diversity_penalty=req.diversity_penalty,
1372
- num_beam_groups=req.num_beam_groups if not req.stream else 1,
1373
- length_normalization_factor=req.length_normalization_factor,
1374
- min_new_tokens=req.min_new_tokens,
1375
- do_normalize_logits=req.do_normalize_logits,
1376
- output_scores=req.output_scores,
1377
- output_attentions=req.output_attentions,
1378
- output_hidden_states=req.output_hidden_states,
1379
- )
1380
-
1381
- if req.stream:
1382
- gen_cfg.use_cache = True
1383
- gen_cfg.num_beams = 1
1384
- gen_cfg.num_return_sequences = 1
1385
- gen_cfg.num_beam_groups = 1
1386
-
1387
- try:
1388
- json_stream = stream_generation_logic(req, ids, gen_cfg, device)
1389
- if req.return_only_text:
1390
- return StreamingResponse(plain_text_stream_processor(json_stream, req), media_type="text/plain")
1391
- else:
1392
- return StreamingResponse(json_stream, media_type="application/json")
1393
- except Exception as e:
1394
- logging.exception("Error setting up streaming generation:")
1395
- await cleanup(device)
1396
- raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Error setting up streaming generation: {e}")
1397
-
1398
- else:
1399
- try:
1400
- if total_requested_seq_len <= total_capacity:
1401
- response_payload = await non_stream_generation_logic(req, ids, gen_cfg, device)
1402
- else:
1403
- total_generated_ids = []
1404
- original_history = req.history or []
1405
- remaining_max_new_tokens = req.max_new_tokens
1406
- finish_reason = "max_new_tokens"
1407
-
1408
- base_gen_cfg = GenerationConfig(
1409
- temperature=req.temperature,
1410
- top_k=req.top_k,
1411
- top_p=req.top_p,
1412
- repetition_penalty=req.repetition_penalty,
1413
- frequency_penalty=req.frequency_penalty,
1414
- presence_penalty=req.presence_penalty,
1415
- num_beams=1,
1416
- length_penalty=req.length_penalty,
1417
- no_repeat_ngram_size=req.no_repeat_ngram_size,
1418
- early_stopping=req.early_stopping,
1419
- do_sample=req.do_sample,
1420
- use_mirostat_mode=1 if req.use_mirostat else 0,
1421
- mirostat_tau=req.mirostat_tau,
1422
- mirostat_eta=req.mirostat_eta,
1423
- eos_token_id=req.eos_token_id_override if req.eos_token_id_override is not None else global_tokens.get("eos_token_id"),
1424
- pad_token_id=req.pad_token_id_override if req.pad_token_id_override is not None else global_tokens.get("pad_token_id"),
1425
- bos_token_id=req.bos_token_id_override if req.bos_token_id_override is not None else global_tokenizer.bos_token_id,
1426
- num_return_sequences=1,
1427
- bad_words_ids=req.bad_words_ids,
1428
- forced_bos_token_id=req.forced_bos_token_id,
1429
- forced_eos_token_id=req.forced_eos_token_id,
1430
- renormalize_logits=req.renormalize_logits,
1431
- suppress_tokens=req.suppress_tokens,
1432
- begin_suppress_tokens=req.begin_suppress_tokens,
1433
- end_suppress_tokens=req.end_suppress_tokens,
1434
- encoder_no_repeat_ngram_size=req.encoder_no_repeat_ngram_size,
1435
- min_length=0,
1436
- max_length=None,
1437
- exponential_decay_length_penalty=req.exponential_decay_length_penalty,
1438
- use_cache=True,
1439
- typical_p=req.typical_p,
1440
- epsilon_cutoff=req.epsilon_cutoff,
1441
- eta_cutoff=req.eta_cutoff,
1442
- temperature_cutoff=req.temperature_cutoff,
1443
- encoder_repetition_penalty=req.encoder_repetition_penalty,
1444
- max_time=req.max_time,
1445
- output_watermark=False,
1446
- diversity_penalty=req.diversity_penalty,
1447
- num_beam_groups=1,
1448
- length_normalization_factor=req.length_normalization_factor,
1449
- min_new_tokens=0,
1450
- do_normalize_logits=req.do_normalize_logits,
1451
- output_scores=False,
1452
- output_attentions=False,
1453
- output_hidden_states=False,
1454
- )
1455
-
1456
- while remaining_max_new_tokens > 0:
1457
- history_for_this_chunk = copy.deepcopy(original_history)
1458
- history_for_this_chunk.append({"role": "user", "content": req.input_text})
1459
- if total_generated_ids:
1460
- accumulated_text = global_tokenizer.decode(total_generated_ids, skip_special_tokens=True)
1461
- history_for_this_chunk.append({"role": "assistant", "content": accumulated_text})
1462
-
1463
- current_prompt_text = format_conversation(req.input_text if not total_generated_ids else "", history_for_this_chunk, req.system_prompt)
1464
-
1465
- encoded_current_prompt = global_tokenizer(current_prompt_text, return_tensors="pt", add_special_tokens=False, **(req.tokenizer_kwargs or {})).to(device)
1466
- current_ids = truncate_encoded_ids(encoded_current_prompt.input_ids, MAX_CONTEXT_TOKENS)
1467
- current_prompt_tokens_count = current_ids.shape[-1]
1468
-
1469
- chunk_max_new_tokens = min(remaining_max_new_tokens, MAX_GENERATION_TOKENS, total_capacity - current_prompt_tokens_count)
1470
-
1471
- if chunk_max_new_tokens <= 0:
1472
- finish_reason = "length_limit_reached_no_space"
1473
- break
1474
-
1475
- chunk_gen_cfg = copy.deepcopy(base_gen_cfg)
1476
- chunk_gen_cfg.max_new_tokens = max(1, chunk_max_new_tokens)
1477
-
1478
- stopping_criteria_list = get_stopping_criteria(req, current_ids, global_tokenizer)
1479
-
1480
- with torch.no_grad():
1481
- chunk_out = global_model.generate(
1482
- input_ids=current_ids,
1483
- generation_config=chunk_gen_cfg,
1484
- return_dict_in_generate=True,
1485
- output_scores=False,
1486
- output_attentions=False,
1487
- output_hidden_states=False,
1488
- num_return_sequences=1,
1489
- bad_words_ids=req.bad_words_ids,
1490
- suppress_tokens=req.suppress_tokens,
1491
- begin_suppress_tokens=req.begin_suppress_tokens,
1492
- end_suppress_tokens=req.end_suppress_tokens,
1493
- stopping_criteria=stopping_criteria_list if stopping_criteria_list else None,
1494
- )
1495
-
1496
- chunk_sequences = chunk_out.sequences
1497
- if not chunk_sequences:
1498
- finish_reason = "no_tokens_generated"
1499
- break
1500
-
1501
- this_chunk_generated_ids = chunk_sequences[0][current_ids.shape[-1]:].tolist()
1502
- total_generated_ids.extend(this_chunk_generated_ids)
1503
- remaining_max_new_tokens -= len(this_chunk_generated_ids)
1504
-
1505
- eos_token_id = req.eos_token_id_override if req.eos_token_id_override is not None else global_tokens.get("eos_token_id")
1506
- if eos_token_id is not None and eos_token_id in this_chunk_generated_ids:
1507
- finish_reason = "eos_token"
1508
- break
1509
-
1510
- decoded_this_chunk = global_tokenizer.decode(this_chunk_generated_ids, skip_special_tokens=True)
1511
- if req.stop_sequences and any(seq in decoded_this_chunk for seq in req.stop_sequences):
1512
- finish_reason = "stop_sequence"
1513
- break
1514
-
1515
- if not this_chunk_generated_ids and remaining_max_new_tokens > 0:
1516
- finish_reason = "stopped_early_no_generation"
1517
- break
1518
-
1519
- final_text_raw = global_tokenizer.decode(total_generated_ids, skip_special_tokens=True)
1520
-
1521
- if finish_reason == "stop_sequence" and req.stop_sequences:
1522
- for stop_seq in req.stop_sequences:
1523
- if stop_seq and stop_seq in final_text_raw:
1524
- final_text_raw = final_text_raw.split(stop_seq, 1)[0]
1525
- break
1526
-
1527
- final_text_processed = post_process_text(final_text_raw, req.strip_trailing_whitespace, req.remove_incomplete_sentences)
1528
-
1529
- response_payload: Dict[str, Any] = {
1530
- "prompt_tokens": initial_prompt_tokens_count_before_trunc,
1531
- "generated_sequences": [{
1532
- "text": final_text_processed if req.return_text_from_sequence else None,
1533
- "token_ids": total_generated_ids,
1534
- "generated_tokens_count": len(total_generated_ids),
1535
- "finish_reason": finish_reason,
1536
- "full_sequence_token_ids": (initial_ids_before_trunc.tolist()[0] if initial_ids_before_trunc is not None else []) + total_generated_ids if not req.remove_input_from_output else None,
1537
- }],
1538
- "total_tokens": initial_prompt_tokens_count_before_trunc + len(total_generated_ids),
1539
- }
1540
-
1541
-
1542
- if req.return_only_text:
1543
- texts = [seq["text"] for seq in response_payload.get("generated_sequences", []) if seq.get("text") is not None]
1544
- if req.num_return_sequences == 1 and texts:
1545
- return PlainTextResponse(texts[0])
1546
- else:
1547
- return JSONResponse(texts)
1548
- else:
1549
- return JSONResponse(response_payload)
1550
-
1551
- except HTTPException:
1552
- raise
1553
- except Exception as e:
1554
- logging.exception("Error during non-streaming generation:")
1555
- raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Non-streaming generation error: {e}")
1556
- else:
1557
-
1558
- await cleanup(device)
1559
- raise HTTPException(
1560
- status_code=status.HTTP_400_BAD_REQUEST,
1561
- detail=f"Requested sequence length ({total_requested_seq_len} tokens = {current_prompt_tokens_count} prompt + {req.max_new_tokens} new) exceeds model capacity ({total_capacity} tokens) and chunking failed or is not applicable/implemented for this configuration."
1562
- )
1563
-
1564
-
1565
- if __name__ == "__main__":
1566
- uvicorn.run(
1567
- app, host="0.0.0.0", port=7860,
1568
- log_level="critical",
1569
- access_log=False
1570
- )