Files
SIA/sia/llm_engine/qwq_llm_engine.py
2025-04-01 10:42:54 +02:00

124 lines
3.8 KiB
Python

from pathlib import Path
from threading import Thread
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline, TextIteratorStreamer, BitsAndBytesConfig
from typing import Callable, Iterator
import torch
from . import LlmEngine
from .. import util
class QwQLlmEngine(LlmEngine):
def __init__(
self,
model_path: Path,
temperature: float,
token_limit: int = None,
):
"""
Initialize the QwQ LLM Engine.
Args:
model_path: Local path to the model
temperature: Sampling temperature
token_limit: Maximum tokens to generate
"""
self._temperature = temperature
self._token_limit = token_limit
quantization_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True
)
model = AutoModelForCausalLM.from_pretrained(
model_path,
return_dict=True,
device_map="auto",
attn_implementation="flash_attention_2",
use_cache=True,
quantization_config=quantization_config,
)
self._tokenizer = AutoTokenizer.from_pretrained(model_path)
self._pipline = pipeline(
"text-generation",
model=model,
tokenizer=self._tokenizer,
return_full_text=False,
)
def infer(self, system_prompt: str, main_context: str, should_stop: Callable[[], bool] = lambda: False) -> Iterator[str]:
"""
Run inference using the system prompt and main context.
Args:
system_prompt: The system prompt string
main_context: The main context string after templating
should_stop: Callback that returns True when inference should stop
Returns:
Iterator[str]: An iterator that yields the generated text.
"""
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": main_context}
]
text = self._tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
)
streamer = TextIteratorStreamer(
self._tokenizer,
skip_prompt=True,
)
generation_thread = Thread(
target=self._pipline,
kwargs=dict(
text_inputs=text,
do_sample=True,
temperature=self._temperature,
max_new_tokens=self._token_limit,
streamer=streamer,
)
)
generation_thread.start()
for text in util.stop_before_value(streamer, '<|eot_id|>'):
yield text
if should_stop():
break
generation_thread.join()
def token_count(self, system_prompt: str, main_context: str) -> int:
"""
Count tokens for the given system prompt and main context.
Args:
system_prompt: The system prompt string
main_context: The main context string
Returns:
int: Total number of tokens
"""
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": main_context}
]
prompt = self._tokenizer.apply_chat_template(
messages, tokenize=False, add_generation_prompt=True
)
return len(self._tokenizer.encode(prompt))
def token_limit(self) -> int:
return self._token_limit