Heute freuen wir uns, die „Powerful“, „Diverse“ und „Practical“ Qwen2.5-Coder- Serien (früher bekannt als CodeQwen1.5) als Open Source zu veröffentlichen, die sich der kontinuierlichen Förderung der Entwicklung von Open CodeLLMs widmen.
Leistungsstark: Qwen2.5-Coder-32B-Instruct ist zum aktuellen SOTA-Open-Source-Codemodell geworden und entspricht den Codierungsfunktionen von GPT-4o. Er verfügt nicht nur über starke und umfassende Programmierfähigkeiten, sondern verfügt auch über gute allgemeine und mathematische Fähigkeiten;
Vielfältig: Aufbauend auf den zuvor offen verfügbaren zwei Größen 1,5B/7B bringt diese Version vier Modellgrößen, darunter 0,5B/3B/14B/32B. Derzeit deckt Qwen2.5-Coder sechs gängige Modellgrößen ab, um den Anforderungen verschiedener Entwickler gerecht zu werden.
? Praktisch: Wir untersuchen die Praktikabilität von Qwen2.5-Coder in zwei Szenarien, einschließlich Code-Assistenten und Artefakten, wobei einige Beispiele die möglichen Anwendungen von Qwen2.5-Coder in realen Szenarien veranschaulichen;
Unterstützung des langen Kontextverständnisses und der Generierung mit der Kontextlänge von 128.000 Token;
Unterstützt 92 Codierungssprachen;
['ada', 'agda', 'alloy', 'antlr', 'applescript', 'assembly', 'augeas', 'awk', 'batchfile', 'bluespec', 'c', 'c#', 'c++', 'clojure', 'cmake', 'coffeescript', 'common-lisp', 'css', 'cuda', 'dart', 'dockerfile', 'elixir', 'elm', 'emacs-lisp', 'erlang', 'f#', 'fortran', 'glsl', 'go', 'groovy', 'haskell', 'html', 'idris', 'isabelle', 'java', 'java-server-pages', 'javascript', 'json', 'julia', 'jupyter-notebook', 'kotlin', 'lean', 'literate-agda', 'literate-coffeescript', 'literate-haskell', 'lua', 'makefile', 'maple', 'markdown', 'mathematica', 'matlab', 'objectc++', 'ocaml', 'pascal', 'perl', 'php', 'powershell', 'prolog', 'protocol-buffer', 'python', 'r', 'racket', 'restructuredtext', 'rmarkdown', 'ruby', 'rust', 'sas', 'scala', 'scheme', 'shell', 'smalltalk', 'solidity', 'sparql', 'sql', 'stan', 'standard-ml', 'stata', 'swift', 'systemverilog', 'tcl', 'tcsh', 'tex', 'thrift', 'typescript', 'verilog', 'vhdl', 'visual-basic', 'vue', 'xslt', 'yacc', 'yaml', 'zig']
Behalten Sie die Stärken in Mathematik und allgemeinen Fähigkeiten des Basismodells bei
Wichtig
Wir aktualisieren sowohl die speziellen Token als auch ihre entsprechenden Token-IDs, um die Konsistenz mit Qwen2.5 aufrechtzuerhalten. Die neuen Sondermarken lauten wie folgt:
{ "<|fim_prefix|>": 151659, "<|fim_middle|>": 151660, "<|fim_suffix|>": 151661, "<|fim_pad|>": 151662, "<|repo_name|>": 151663, "<|file_sep|>": 151664, "<|im_start|>": 151644, "<|im_end|>": 151645}
Modellname | Typ | Länge | Herunterladen |
---|---|---|---|
Qwen2.5-Coder-0.5B | Base | 32k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-1.5B | Base | 32k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-3B | Base | 32k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-7B | Base | 128k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-14B | Base | 128k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-32B | Base | 128k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-0.5B-Anweisung | anweisen | 32k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-1.5B-Anweisung | anweisen | 32k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-3B-Anweisung | anweisen | 32k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-7B-Anweisung | anweisen | 128k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-14B-Anweisung | anweisen | 128k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-32B-Anweisung | anweisen | 128k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-0.5B-Instruct-AWQ | anweisen | 32k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-0.5B-Instruct-GGUF | anweisen | 32k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-0.5B-Instruct-GPTQ-Int4 | anweisen | 32k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-0.5B-Instruct-GPTQ-Int8 | anweisen | 32k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-1.5B-Instruct-AWQ | anweisen | 32k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-1.5B-Instruct-GGUF | anweisen | 32k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-1.5B-Instruct-GPTQ-Int4 | anweisen | 32k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-1.5B-Instruct-GPTQ-Int8 | anweisen | 32k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-3B-Instruct-AWQ | anweisen | 32k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-3B-Instruct-GGUF | anweisen | 32k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-3B-Instruct-GPTQ-Int4 | anweisen | 32k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-3B-Instruct-GPTQ-Int8 | anweisen | 32k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-7B-Instruct-AWQ | anweisen | 128k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-7B-Instruct-GGUF | anweisen | 128k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-7B-Instruct-GPTQ-Int4 | anweisen | 128k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-7B-Instruct-GPTQ-Int8 | anweisen | 128k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-14B-Instruct-AWQ | anweisen | 128k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-14B-Instruct-GGUF | anweisen | 128k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-14B-Instruct-GPTQ-Int4 | anweisen | 128k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-14B-Instruct-GPTQ-Int8 | anweisen | 128k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-32B-Instruct-AWQ | anweisen | 128k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-32B-Instruct-GGUF | anweisen | 128k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-32B-Instruct-GPTQ-Int4 | anweisen | 128k | Umarmendes Gesicht • ModelScope |
Qwen2.5-Coder-32B-Instruct-GPTQ-Int8 | anweisen | 128k | Umarmendes Gesicht • ModelScope |
Detaillierte Leistung und Einführung finden Sie hier. Blog.
python>=3.9
transformers>4.37.0
für Qwen2.5-Dense-Modelle.
Warnung
Dies ist ein Muss, da „Transformers“ seit „4.37.0“ Qwen2-Codes integriert haben.
Sie können die benötigten Pakete mit dem folgenden Befehl installieren:
pip install -r Anforderungen.txt
Wichtig
Qwen2.5-Coder-[0.5-32]B-Instrcut sind Anweisungsmodelle zum Chatten;
Qwen2.5-Coder-[0.5-32]B ist ein Basismodell, das typischerweise zur Vervollständigung verwendet wird und als besserer Ausgangspunkt für die Feinabstimmung dient.
Sie können einfach mehrere Zeilen Code mit transformers
schreiben, um mit Qwen2.5-Coder-32B-Instruct zu chatten. Im Wesentlichen erstellen wir den Tokenizer und das Modell mit der Methode „ from_pretrained
und verwenden die Methode „generate“, um mithilfe der vom Tokenizer bereitgestellten Chat-Vorlage zu chatten. Nachfolgend finden Sie ein Beispiel für den Chat mit Qwen2.5-Coder-32B-Instruct:
aus Transformatoren importieren AutoModelForCausalLM, AutoTokenizermodel_name = "Qwen/Qwen2.5-Coder-32B-Instruct"model = AutoModelForCausalLM.from_pretrained(model_name,torch_dtype="auto",device_map="auto")tokenizer = AutoTokenizer.from_pretrained(model_name)prompt = „Schreiben Sie einen schnellen Sortieralgorithmus.“messages = [ {"role": "system", "content": "Sie sind Qwen, erstellt von Alibaba Cloud. Sie sind ein hilfreicher Assistent."}, {"role": "user", "content": prompt} ]text = tokenizer.apply_chat_template(messages,tokenize=False,add_generation_prompt=True)model_inputs = tokenizer([text], return_tensors="pt").to(model.device)generated_ids = model.generate(**model_inputs,max_new_tokens= 512)generated_ids = [output_ids[len(input_ids):] für input_ids, Output_ids in zip(model_inputs.input_ids, generic_ids) ]response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
Die Funktion apply_chat_template()
wird verwendet, um die Nachrichten in ein Format zu konvertieren, das das Modell verstehen kann. Das Argument add_generation_prompt
wird verwendet, um eine Generierungsaufforderung hinzuzufügen, die sich auf <|im_start|>assistantn
zur Eingabe bezieht. Insbesondere verwenden wir die ChatML-Vorlage für Chat-Modelle entsprechend unserer bisherigen Praxis. Das Argument max_new_tokens
wird verwendet, um die maximale Länge der Antwort festzulegen. Die Funktion tokenizer.batch_decode()
wird zum Dekodieren der Antwort verwendet. In Bezug auf die Eingabe sind die obigen Meldungen ein Beispiel, das zeigt, wie Sie Ihren Dialogverlauf und Ihre Systemeingabeaufforderung formatieren. Sie können die andere Größe des Instruct-Modells auf die gleiche Weise verwenden.
Das Modell vervollständigt die Codeausschnitte gemäß den gegebenen Eingabeaufforderungen, ohne zusätzliche Formatierung, was in den Codegenerierungsaufgaben normalerweise als code completion
bezeichnet wird.
Im Wesentlichen erstellen wir den Tokenizer und das Modell mit der Methode from_pretrained
und verwenden die Methode „generate“, um die Codevervollständigung durchzuführen. Nachfolgend finden Sie ein Beispiel für den Chat mit Qwen2.5-Coder-32B:
from Transformers import AutoTokenizer, AutoModelForCausalLMdevice = "cuda" # das Gerät, auf das das Modell geladen werden soll# Jetzt müssen Sie nicht mehr "trust_remote_code=True"TOKENIZER = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-Coder-32B")MODEL hinzufügen = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-Coder-32B", device_map="auto").eval()# tokenisieren Sie die Eingabe in Tokensinput_text = "#Schreiben Sie einen schnellen Sortieralgorithmus"model_inputs = TOKENIZER([input_text], return_tensors ="pt").to(device)# Verwenden Sie „max_new_tokens“, um die maximale Ausgabe zu steuern length.generated_ids = MODEL.generate(model_inputs.input_ids, max_new_tokens=512, do_sample=False)[0]# Die generierten_ids enthalten prompt_ids, daher müssen wir die Token erst nach prompt_ids.output_text = TOKENIZER.decode(generated_ids[len( model_inputs.input_ids[0]):], skip_special_tokens=True)print(f"Prompt: {input_text}nnGenerierter Text: {output_text}")
Das Argument max_new_tokens
wird verwendet, um die maximale Länge der Antwort festzulegen. Der input_text
könnte ein beliebiger Text sein, mit dem das Modell fortfahren soll.
Die aktuelle config.json
ist auf eine Kontextlänge von bis zu 32.768 Token eingestellt. Um umfangreiche Eingaben mit mehr als 32.768 Token zu verarbeiten, verwenden wir YaRN, eine Technik zur Verbesserung der Modelllängenextrapolation, die eine optimale Leistung bei langen Texten gewährleistet.
Für unterstützte Frameworks können Sie Folgendes zu config.json
hinzufügen, um YaRN zu aktivieren:
{ ..., „rope_scaling“: {“factor“: 4.0, „original_max_position_embeddings“: 32768, „type“: „yarn“ } }
Die Code-Einfügungsaufgabe, auch „Fill-in-the-Middle“-Herausforderung genannt, erfordert das Einfügen von Codesegmenten auf eine Weise, die die Lücken innerhalb eines bestimmten Codekontexts schließt. Für einen an Best Practices ausgerichteten Ansatz empfehlen wir die Einhaltung der Formatierungsrichtlinien, die im Dokument „Efficient Training of Language Models to Fill in the Middle“[arxiv] beschrieben sind. Dies beinhaltet die Verwendung von drei speziellen Tokens <fim_prefix>
, <fim_suffix>
und <fim_middle>
zur Bezeichnung der jeweiligen Segmente der Codestruktur. Die Eingabeaufforderung sollte wie folgt aufgebaut sein:
prompt = '<|fim_prefix|>' + prefix_code + '<|fim_suffix|>' + suffix_code + '<|fim_middle|>'
Folgt man dem genannten Ansatz, würde ein Beispiel folgendermaßen aufgebaut sein:
from Transformers Import AutoTokenizer, AutoModelForCausalLM# load modeldevice = "cuda" # das Gerät, auf das das Modell geladen werden sollTOKENIZER = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-Coder-32B")MODEL = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5 -Coder-32B", device_map="auto").eval()input_text = """<|fim_prefix|>def quicksort(arr): if len(arr) <= 1: return arr Pivot = arr[len(arr) // 2] <|fim_suffix|> middle = [x für x in arr wenn x == Pivot] rechts = [x für x in arr wenn x > Pivot] return Quicksort(left) + Middle + Quicksort(right)<|fim_middle|>"""model_inputs = TOKENIZER([input_text], return_tensors="pt").to(device)# Verwenden Sie „max_new_tokens“, um die maximale Ausgabelänge zu steuern.generated_ids = MODEL.generate(model_inputs.input_ids, max_new_tokens=512, do_sample=False)[0] # Die generierten_IDs enthalten prompt_ids, wir müssen die Token danach nur noch dekodieren prompt_ids.output_text = TOKENIZER.decode(generated_ids[len(model_inputs.input_ids[0]):], skip_special_tokens=True)print(f"Prompt: {input_text}nnGenerated text: {output_text}")
Bei der Aufgabe zur Codevervollständigung auf Repository-Ebene wird dem Modell der Inhalt mehrerer Dateien aus demselben Repository zugeführt. Dadurch kann das Modell die Zusammenhänge zwischen verschiedenen Aufrufen innerhalb dieser Dateien verstehen und so die Vervollständigung des Codeinhalts erleichtern. Wir empfehlen die Verwendung der beiden speziellen Token <|repo_name|>
und <|file_sep|>
zur Angabe der Repository-Struktur. Angenommen, der Repository-Name ist in repo_name
gespeichert und enthält Dateien mit ihren jeweiligen Pfaden und Inhalten, die als [( file_path1
, file_content1
), ( file_path2
, file_content2
)] aufgeführt sind, dann würde das Format der endgültigen Eingabeaufforderung wie folgt aussehen:
input_text = f'''<|repo_name|>{repo_name}<|file_sep|>{file_path1} {file_content1}<|file_sep|>{file_path2} {file_content2}''
from Transformers import AutoTokenizer, AutoModelForCausalLMdevice = "cuda" # das Gerät, auf das das Modell geladen werden soll# Jetzt müssen Sie nicht mehr "trust_remote_code=True"TOKENIZER = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-Coder-32B")MODEL hinzufügen = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-Coder-32B", device_map="auto").eval()# tokenisieren Sie die Eingabe in tokensinput_text = """<|repo_name|>library-system<|file_sep|>library .pyclass Buch: def __init__(selbst, Titel, Autor, isbn, Kopien): self.title = Titel self.author = Autor self.isbn = isbn self.copies = Kopien def __str__(self): return f"Titel: {self.title}, Autor: {self.author}, ISBN: {self.isbn}, Kopien: {self.copies} "Klassenbibliothek: def __init__(self): self.books = [] def add_book(self, title, author, isbn, copy): book = Book(title, author, isbn, copy) self.books.append(book) def find_book(self, isbn): für Buch in self.books: if book.isbn == isbn: return book return None def list_books(self): return self.books<|file_sep|> student.pyclass Student: def __init__(self, name, id): self.name = name self.id = id self.borrowed_books = [] def Borrow_book(self, book, Library): wenn book und book.copies > 0: self.borrowed_books.append(book) book.copies -= 1 return True return False def return_book(self, book, Library): wenn book in self .borrowed_books: self.borrowed_books.remove(book) book.copies += 1 return True return False<|file_sep|>main.pyfrom Library import Libraryfrom student import Studentdef main(): # Richten Sie die Bibliothek mit einigen Büchern ein. Library = Library() Library.add_book("The Great Gatsby", "F. Scott Fitzgerald", "1234567890", 3) Library.add_book("To Kill a Mockingbird", "Harper Lee", "1234567891", 2) # Einen Schüler einrichten student = Student("Alice", "S1") # Der Student leiht sich ein Buch aus""model_inputs = TOKENIZER([input_text], return_tensors="pt").to(device)# Benutzen `max_new_tokens` zur Steuerung der maximalen Ausgabelänge.generated_ids = MODEL.generate(model_inputs.input_ids, max_new_tokens=1024, do_sample=False)[0]# Die generierten_ids enthalten prompt_ids, daher müssen wir die Token erst nach prompt_ids.output_text = dekodieren TOKENIZER.decode(generated_ids[len(model_inputs.input_ids[0]):], skip_special_tokens=True)print(f"Prompt: n{input_text}nnGenerated text: n{output_text}")
Die erwartete Ausgabe lautet wie folgt:
Generierter Text:book = Library.find_book("1234567890")if student.borrow_book(book, Library):print(f"{student.name} Borrowed {book.title}")else:print(f"{student.name } konnte {book.title} nicht ausleihen") # Student gibt ein Buch zurück, wenn student.return_book(book,library):print(f"{student.name} zurückgegeben wird {book.title}")else:print(f"{student.name} konnte {book.title} nicht zurückgeben") # Alle Bücher in der Bibliothek auflistenprint("Alle Bücher in der Bibliothek:")für Buch in der Bibliothek. list_books():print(book)if __name__ == "__main__":main()
Als Familienmitglied von Qwen2.5 werden Qwen2.5-Coder von vLLM unterstützt. Das ausführliche Tutorial finden Sie im Qwen-Tutorial. Hier geben wir Ihnen ein einfaches Beispiel für Offline-Batch-Inferenz in vLLM.
from transformers import AutoTokenizerfrom vllm import LLM, SamplingParams# Initialisieren Sie den Tokenizertokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-Coder-32B")# Übergeben Sie die Standard-Dekodierungs-Hyperparameter von Qwen1.5-32B-Chat# max_tokens ist für das Maximum Länge für generation.sampling_params = SamplingParams(temperature=0.7, top_p=0.8, repetition_penalty=1.05, max_tokens=1024)# Geben Sie den Modellnamen oder Pfad ein. Kann GPTQ- oder AWQ-Modelle sein.llm = LLM(model="Qwen/Qwen2.5-Coder-32B")# Bereiten Sie Ihre Eingabeaufforderungen vorprompt = "#Schreiben Sie einen Schnellsortierungsalgorithmus.ndef quick_sort("# generic outputsoutputs = llm.generate( [prompt], sampling_params)# Drucken Sie die Ausgabe von „outputs.for“ in „outputs:prompt = output.promptgenerated_text =“. Output.outputs[0].textprint(f"Prompt: {prompt!r}, Generierter Text: {generated_text!r}")
Um Ihren Bereitstellungsdurchsatz zu steigern, hilft Ihnen die verteilte Bereitstellung durch die Nutzung von mehr GPU-Geräten. Bei der Verwendung extrem langer Sequenzen zur Inferenz kann es zu unzureichendem GPU-Speicher kommen. Hier zeigen wir, wie man Qwen2.5-Coder-32B mit Tensorparallelität ausführt, indem man einfach das Argument tensor_parallel_size
übergibt.
llm = LLM(model="Qwen/Qwen2.5-Coder-32B", tensor_parallel_size=8)
Für ein besseres Erlebnis bieten wir auch eine Gradio-Schnittstelle an, die einfach ausgeführt wird von:
cd demo/chatbot/# Für Linux- und Windows-Benutzer (und macOS mit Intel??)python app.py # Für macOS mit Apple Silicon-Benutzern, Intel wird nicht unterstützt, dies ist möglicherweise 20x langsamer als RTX 4090PYTORCH_ENABLE_MPS_FALLBACK=1 python app.py
Wir bieten auch eine Gradio-Schnittstelle für den Artefaktmodus an:
CD-Demo/Artefakte/ Python app.py
Sie können die Argumente --server_port
, --share
, --server_name
entsprechend Ihren Anforderungen angeben!
Oder probieren Sie es ganz einfach auf HuggingFace aus: „Chatbot-Demo“? 「Artefakte-Demo」
Weitere Informationen finden Sie im technischen Bericht zu Qwen2.5-Coder.
Wenn Sie unsere Arbeit hilfreich finden, können Sie uns gerne zitieren.
@article{hui2024qwen2, title={Qwen2. 5-Coder Technical Report}, Autor={Hui, Binyuan und Yang, Jian und Cui, Zeyu und Yang, Jiaxi und Liu, Dayiheng und Zhang, Lei und Liu, Tianyu und Zhang, Jiajun und Yu, Bowen und Dang, Kai und andere}, journal={arXiv preprint arXiv:2409.12186}, year={2024}}@article{qwen2,title={Qwen2 Technical Report},author={An Yang und Baosong Yang und Binyuan Hui und Bo Zheng und Bowen Yu und Chang Zhou und Chengpeng Li und Chengyuan Li und Dayiheng Liu und Fei Huang und Guanting Dong und Haoran Wei und Huan Lin und Jialong Tang und Jialin Wang und Jian Yang und Jianhong Tu und Jianwei Zhang und Jianxin Ma und Jin Xu und Jingren Zhou und Jinze Bai und Jinzheng He und Junyang Lin und Kai Dang und Keming Lu und Keqin Chen und Kexin Yang und Mei Li und Mingfeng Xue und Na Ni und Pei Zhang und Peng Wang und Ru Peng und Rui Men und Ruize Gao und Runji Lin und Shijie Wang und Shuai Bai und Sinan Tan und Tianhang Zhu und Tianhao Li und Tianyu Liu und Wenbin Ge und Xiaodong Deng und Xiaohuan Zhou und Xingzhang Ren und Xinyu Zhang und Xipin Wei und Xuancheng Ren und Yang Fan und Yang Yao und Yichang Zhang und Yu Wan und Yunfei Chu und Yuqiong Liu und Zeyu Cui und Zhenru Zhang und Zhihao Fan}, Zeitschrift ={arXiv preprint arXiv:2407.10671},year={2024}}
Wenn Sie daran interessiert sind, unserem Forschungs- oder Produktteam eine Nachricht zu hinterlassen, treten Sie unseren Discord- oder WeChat-Gruppen bei!
↑ Zurück nach oben ↑