jnjj commited on
Commit
fc7ccf7
·
verified ·
1 Parent(s): 1ca223a

Delete app.py

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