Text Generation
Transformers
Safetensors
GGUF
llama
chatbot
multilingual
arabic
french
tamazight
english
conversational
text-generation-inference
4-bit precision
bitsandbytes
Instructions to use kaisser/LLM-Maroc with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use kaisser/LLM-Maroc with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="kaisser/LLM-Maroc") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("kaisser/LLM-Maroc") model = AutoModelForCausalLM.from_pretrained("kaisser/LLM-Maroc", device_map="auto") messages = [ {"role": "user", "content": "Who are you?"}, ] inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=40) print(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use kaisser/LLM-Maroc with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf kaisser/LLM-Maroc:BF16 # Run inference directly in the terminal: llama cli -hf kaisser/LLM-Maroc:BF16
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf kaisser/LLM-Maroc:BF16 # Run inference directly in the terminal: llama cli -hf kaisser/LLM-Maroc:BF16
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf kaisser/LLM-Maroc:BF16 # Run inference directly in the terminal: ./llama-cli -hf kaisser/LLM-Maroc:BF16
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf kaisser/LLM-Maroc:BF16 # Run inference directly in the terminal: ./build/bin/llama-cli -hf kaisser/LLM-Maroc:BF16
Use Docker
docker model run hf.co/kaisser/LLM-Maroc:BF16
- LM Studio
- Jan
- vLLM
How to use kaisser/LLM-Maroc with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "kaisser/LLM-Maroc" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "kaisser/LLM-Maroc", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/kaisser/LLM-Maroc:BF16
- SGLang
How to use kaisser/LLM-Maroc with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "kaisser/LLM-Maroc" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "kaisser/LLM-Maroc", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "kaisser/LLM-Maroc" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "kaisser/LLM-Maroc", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Ollama
How to use kaisser/LLM-Maroc with Ollama:
ollama run hf.co/kaisser/LLM-Maroc:BF16
- Unsloth Studio
How to use kaisser/LLM-Maroc with Unsloth Studio:
Install Unsloth Studio (macOS, Linux, WSL)
curl -fsSL https://unsloth.ai/install.sh | sh # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for kaisser/LLM-Maroc to start chatting
Install Unsloth Studio (Windows)
irm https://unsloth.ai/install.ps1 | iex # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for kaisser/LLM-Maroc to start chatting
Using HuggingFace Spaces for Unsloth
# No setup required # Open https://huggingface.co/spaces/unsloth/studio in your browser # Search for kaisser/LLM-Maroc to start chatting
- Docker Model Runner
How to use kaisser/LLM-Maroc with Docker Model Runner:
docker model run hf.co/kaisser/LLM-Maroc:BF16
- Lemonade
How to use kaisser/LLM-Maroc with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull kaisser/LLM-Maroc:BF16
Run and chat with the model
lemonade run user.LLM-Maroc-BF16
List all available models
lemonade list
- Atomic Chat
| import { useState } from 'react'; | |
| import { MessageExtra } from '../utils/types'; | |
| import toast from 'react-hot-toast'; | |
| import { useAppContext } from '../utils/app.context'; | |
| import * as pdfjs from 'pdfjs-dist'; | |
| import pdfjsWorkerSrc from 'pdfjs-dist/build/pdf.worker.min.mjs?url'; | |
| import { TextContent, TextItem } from 'pdfjs-dist/types/src/display/api'; | |
| pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorkerSrc; | |
| // This file handles uploading extra context items (a.k.a files) | |
| // It allows processing these kinds of files: | |
| // - image files (converted to base64) | |
| // - audio files (converted to base64) | |
| // - text files (including code files) | |
| // - pdf (converted to text) | |
| // Interface describing the API returned by the hook | |
| export interface ChatExtraContextApi { | |
| items?: MessageExtra[]; // undefined if empty, similar to Message['extra'] | |
| addItems: (items: MessageExtra[]) => void; | |
| removeItem: (idx: number) => void; | |
| clearItems: () => void; | |
| onFileAdded: (files: File[]) => void; // used by "upload" button | |
| } | |
| export function useChatExtraContext(): ChatExtraContextApi { | |
| const { serverProps, config } = useAppContext(); | |
| const [items, setItems] = useState<MessageExtra[]>([]); | |
| const addItems = (newItems: MessageExtra[]) => { | |
| setItems((prev) => [...prev, ...newItems]); | |
| }; | |
| const removeItem = (idx: number) => { | |
| setItems((prev) => prev.filter((_, i) => i !== idx)); | |
| }; | |
| const clearItems = () => { | |
| setItems([]); | |
| }; | |
| const isSupportVision = serverProps?.modalities?.vision; | |
| const onFileAdded = async (files: File[]) => { | |
| try { | |
| for (const file of files) { | |
| const mimeType = file.type; | |
| // this limit is only to prevent accidental uploads of huge files | |
| // it can potentially crashes the browser because we read the file as base64 | |
| if (file.size > 500 * 1024 * 1024) { | |
| toast.error('File is too large. Maximum size is 500MB.'); | |
| break; | |
| } | |
| if (mimeType.startsWith('image/')) { | |
| if (!isSupportVision) { | |
| toast.error('Multimodal is not supported by this server or model.'); | |
| break; | |
| } | |
| let base64Url = await getFileAsBase64(file); | |
| if (mimeType === 'image/svg+xml') { | |
| // Convert SVG to PNG | |
| base64Url = await svgBase64UrlToPngDataURL(base64Url); | |
| } | |
| addItems([ | |
| { | |
| type: 'imageFile', | |
| name: file.name, | |
| base64Url, | |
| }, | |
| ]); | |
| } else if (mimeType.startsWith('video/')) { | |
| toast.error('Video files are not supported yet.'); | |
| break; | |
| } else if (mimeType.startsWith('audio/')) { | |
| if (!/mpeg|wav/.test(mimeType)) { | |
| toast.error('Only mp3 and wav audio files are supported.'); | |
| break; | |
| } | |
| // plain base64, not a data URL | |
| const base64Data = await getFileAsBase64(file, false); | |
| addItems([ | |
| { | |
| type: 'audioFile', | |
| name: file.name, | |
| mimeType, | |
| base64Data, | |
| }, | |
| ]); | |
| } else if (mimeType.startsWith('application/pdf')) { | |
| if (config.pdfAsImage && !isSupportVision) { | |
| toast( | |
| 'Multimodal is not supported, PDF will be converted to text instead of image.' | |
| ); | |
| break; | |
| } | |
| if (config.pdfAsImage && isSupportVision) { | |
| // Convert PDF to images | |
| const base64Urls = await convertPDFToImage(file); | |
| addItems( | |
| base64Urls.map((base64Url) => ({ | |
| type: 'imageFile', | |
| name: file.name, | |
| base64Url, | |
| })) | |
| ); | |
| } else { | |
| // Convert PDF to text | |
| const content = await convertPDFToText(file); | |
| addItems([ | |
| { | |
| type: 'textFile', | |
| name: file.name, | |
| content, | |
| }, | |
| ]); | |
| if (isSupportVision) { | |
| toast.success( | |
| 'PDF file converted to text. You can also convert it to image, see in Settings.' | |
| ); | |
| } | |
| } | |
| break; | |
| } else { | |
| // Because there can be many text file types (like code file), we will not check the mime type | |
| // and will just check if the file is not binary. | |
| const reader = new FileReader(); | |
| reader.onload = (event) => { | |
| if (event.target?.result) { | |
| const content = event.target.result as string; | |
| if (!isLikelyNotBinary(content)) { | |
| toast.error('File is binary. Please upload a text file.'); | |
| return; | |
| } | |
| addItems([ | |
| { | |
| type: 'textFile', | |
| name: file.name, | |
| content, | |
| }, | |
| ]); | |
| } | |
| }; | |
| reader.readAsText(file); | |
| } | |
| } | |
| } catch (error) { | |
| const message = error instanceof Error ? error.message : String(error); | |
| const errorMessage = `Error processing file: ${message}`; | |
| toast.error(errorMessage); | |
| } | |
| }; | |
| return { | |
| items: items.length > 0 ? items : undefined, | |
| addItems, | |
| removeItem, | |
| clearItems, | |
| onFileAdded, | |
| }; | |
| } | |
| async function getFileAsBase64(file: File, outputUrl = true): Promise<string> { | |
| return new Promise((resolve, reject) => { | |
| const reader = new FileReader(); | |
| reader.onload = (event) => { | |
| if (event.target?.result) { | |
| let result = event.target.result as string; | |
| if (!outputUrl) { | |
| // remove base64 url prefix and correct characters | |
| result = result.substring(result.indexOf(',') + 1); | |
| } | |
| resolve(result); | |
| } else { | |
| reject(new Error('Failed to read file.')); | |
| } | |
| }; | |
| reader.readAsDataURL(file); | |
| }); | |
| } | |
| async function getFileAsBuffer(file: File): Promise<ArrayBuffer> { | |
| return new Promise((resolve, reject) => { | |
| const reader = new FileReader(); | |
| reader.onload = (event) => { | |
| if (event.target?.result) { | |
| resolve(event.target.result as ArrayBuffer); | |
| } else { | |
| reject(new Error('Failed to read file.')); | |
| } | |
| }; | |
| reader.readAsArrayBuffer(file); | |
| }); | |
| } | |
| async function convertPDFToText(file: File): Promise<string> { | |
| const buffer = await getFileAsBuffer(file); | |
| const pdf = await pdfjs.getDocument(buffer).promise; | |
| const numPages = pdf.numPages; | |
| const textContentPromises: Promise<TextContent>[] = []; | |
| for (let i = 1; i <= numPages; i++) { | |
| textContentPromises.push( | |
| pdf.getPage(i).then((page) => page.getTextContent()) | |
| ); | |
| } | |
| const textContents = await Promise.all(textContentPromises); | |
| const textItems = textContents.flatMap((textContent: TextContent) => | |
| textContent.items.map((item) => (item as TextItem).str ?? '') | |
| ); | |
| return textItems.join('\n'); | |
| } | |
| // returns list of base64 images | |
| async function convertPDFToImage(file: File): Promise<string[]> { | |
| const buffer = await getFileAsBuffer(file); | |
| const doc = await pdfjs.getDocument(buffer).promise; | |
| const pages: Promise<string>[] = []; | |
| for (let i = 1; i <= doc.numPages; i++) { | |
| const page = await doc.getPage(i); | |
| const viewport = page.getViewport({ scale: 1.5 }); | |
| const canvas = document.createElement('canvas'); | |
| const ctx = canvas.getContext('2d'); | |
| canvas.width = viewport.width; | |
| canvas.height = viewport.height; | |
| if (!ctx) { | |
| throw new Error('Failed to get 2D context from canvas'); | |
| } | |
| const task = page.render({ canvasContext: ctx, viewport: viewport }); | |
| pages.push( | |
| task.promise.then(() => { | |
| return canvas.toDataURL(); | |
| }) | |
| ); | |
| } | |
| return await Promise.all(pages); | |
| } | |
| // WARN: vibe code below | |
| // This code is a heuristic to determine if a string is likely not binary. | |
| // It is necessary because input file can have various mime types which we don't have time to investigate. | |
| // For example, a python file can be text/plain, application/x-python, etc. | |
| function isLikelyNotBinary(str: string): boolean { | |
| const options = { | |
| prefixLength: 1024 * 10, // Check the first 10KB of the string | |
| suspiciousCharThresholdRatio: 0.15, // Allow up to 15% suspicious chars | |
| maxAbsoluteNullBytes: 2, | |
| }; | |
| if (!str) { | |
| return true; // Empty string is considered "not binary" or trivially text. | |
| } | |
| const sampleLength = Math.min(str.length, options.prefixLength); | |
| if (sampleLength === 0) { | |
| return true; // Effectively an empty string after considering prefixLength. | |
| } | |
| let suspiciousCharCount = 0; | |
| let nullByteCount = 0; | |
| for (let i = 0; i < sampleLength; i++) { | |
| const charCode = str.charCodeAt(i); | |
| // 1. Check for Unicode Replacement Character (U+FFFD) | |
| // This is a strong indicator if the string was created from decoding bytes as UTF-8. | |
| if (charCode === 0xfffd) { | |
| suspiciousCharCount++; | |
| continue; | |
| } | |
| // 2. Check for Null Bytes (U+0000) | |
| if (charCode === 0x0000) { | |
| nullByteCount++; | |
| // We also count nulls towards the general suspicious character count, | |
| // as they are less common in typical text files. | |
| suspiciousCharCount++; | |
| continue; | |
| } | |
| // 3. Check for C0 Control Characters (U+0001 to U+001F) | |
| // Exclude common text control characters: TAB (9), LF (10), CR (13). | |
| // We can also be a bit lenient with BEL (7) and BS (8) which sometimes appear in logs. | |
| if (charCode < 32) { | |
| if ( | |
| charCode !== 9 && // TAB | |
| charCode !== 10 && // LF | |
| charCode !== 13 && // CR | |
| charCode !== 7 && // BEL (Bell) - sometimes in logs | |
| charCode !== 8 // BS (Backspace) - less common, but possible | |
| ) { | |
| suspiciousCharCount++; | |
| } | |
| } | |
| // Characters from 32 (space) up to 126 (~) are printable ASCII. | |
| // Characters 127 (DEL) is a control character. | |
| // Characters >= 128 are extended ASCII / multi-byte Unicode. | |
| // If they resulted in U+FFFD, we caught it. Otherwise, they are valid | |
| // (though perhaps unusual) Unicode characters from JS's perspective. | |
| // The main concern is if those higher characters came from misinterpreting | |
| // a single-byte encoding as UTF-8, which again, U+FFFD would usually flag. | |
| } | |
| // Check absolute null byte count | |
| if (nullByteCount > options.maxAbsoluteNullBytes) { | |
| return false; // Too many null bytes is a strong binary indicator | |
| } | |
| // Check ratio of suspicious characters | |
| const ratio = suspiciousCharCount / sampleLength; | |
| return ratio <= options.suspiciousCharThresholdRatio; | |
| } | |
| // WARN: vibe code below | |
| // Converts a Base64URL encoded SVG string to a PNG Data URL using browser Canvas API. | |
| function svgBase64UrlToPngDataURL(base64UrlSvg: string): Promise<string> { | |
| const backgroundColor = 'white'; // Default background color for PNG | |
| return new Promise((resolve, reject) => { | |
| try { | |
| const img = new Image(); | |
| img.onload = () => { | |
| const canvas = document.createElement('canvas'); | |
| const ctx = canvas.getContext('2d'); | |
| if (!ctx) { | |
| reject(new Error('Failed to get 2D canvas context.')); | |
| return; | |
| } | |
| // Use provided dimensions or SVG's natural dimensions, with fallbacks | |
| // Fallbacks (e.g., 300x300) are for SVGs without explicit width/height | |
| // or when naturalWidth/Height might be 0 before full processing. | |
| const targetWidth = img.naturalWidth || 300; | |
| const targetHeight = img.naturalHeight || 300; | |
| canvas.width = targetWidth; | |
| canvas.height = targetHeight; | |
| if (backgroundColor) { | |
| ctx.fillStyle = backgroundColor; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| } | |
| ctx.drawImage(img, 0, 0, targetWidth, targetHeight); | |
| resolve(canvas.toDataURL('image/png')); | |
| }; | |
| img.onerror = () => { | |
| reject( | |
| new Error('Failed to load SVG image. Ensure the SVG data is valid.') | |
| ); | |
| }; | |
| // Load SVG string into an Image element | |
| img.src = base64UrlSvg; | |
| } catch (error) { | |
| const message = error instanceof Error ? error.message : String(error); | |
| const errorMessage = `Error converting SVG to PNG: ${message}`; | |
| toast.error(errorMessage); | |
| reject(new Error(errorMessage)); | |
| } | |
| }); | |
| } | |