๐Ÿš€ Deployment & Scripts

API Server, Web Demo, and Model Conversion

4
Files
3
Deploy Modes
~510
Lines
๐Ÿ
Python
๐Ÿ”ต FastAPI ยท OpenAI API ๐ŸŸข Streamlit ยท WebUI ๐ŸŸก Model Conversion ๐Ÿ”ด Streaming
๐Ÿช Bakery Storefront Analogy

Think of the scripts module as the storefront and packaging department of a bakery. The model (bread) has been baked in the training kitchen โ€” now it needs to be displayed and served. serve_openai_api.py is the counter where customers (clients) place orders via a standard menu (OpenAI API format). web_demo.py is the cozy cafรฉ area with a tasting counter. convert_model.py is the packaging station โ€” slicing, boxing, and labeling products for different distribution channels (HuggingFace, Llama format). And chat_openai_api.py is the mystery shopper testing the service.

Architecture Overview

flowchart LR
    W["Model Weights\n(.pth / .safetensors)"] --> S["serve_openai_api.py\nFastAPI :8998"]
    W --> WD["web_demo.py\nStreamlit UI"]
    W --> C["convert_model.py\nFormat Converter"]

    S -->|"/v1/chat/completions"| CL["chat_openai_api.py\nTest Client"]
    S -->|"OpenAI API"| EXT["External Clients\ncurl / SDKs"]
    WD -->|"Local Mode"| WD
    WD -->|"API Mode"| S

    C --> HF["HuggingFace Format"]
    C --> LL["LlamaForCausalLM"]
    C --> NT["Native .pth (fp16)"]

    style S fill:#1f6feb,stroke:#58a6ff,color:#fff
    style WD fill:#238636,stroke:#3fb950,color:#fff
    style C fill:#9a6700,stroke:#d29922,color:#fff
    style CL fill:#da3633,stroke:#f85149,color:#fff
    

Key Functions & Classes

NameTypeFileDescription
ChatRequestPydantic Modelserve_openai_api.pyRequest schema: model, messages, temperature, top_p, max_tokens, stream, tools
CustomStreamerClassserve_openai_api.pyQueue-based TextStreamer subclass for SSE streaming responses
/v1/chat/completionsEndpointserve_openai_api.pyOpenAI-compatible chat completion with streaming & non-streaming support
process_assistant_contentFunctionweb_demo.pyRenders <think>/<answer> tags as collapsible HTML details elements
TextIteratorStreamerClass (ext)web_demo.pyToken-by-token streamer for real-time chat display in Streamlit
MODEL_PATHSDictweb_demo.pyMaps model names (MiniMind2-R1, Small-R1, MoE, etc.) to weight paths
convert_torch2transformers_minimindFunctionconvert_model.pyNative MiniMind .pth โ†’ HuggingFace Transformers format
convert_torch2transformers_llamaFunctionconvert_model.pyConverts to LlamaForCausalLM for broad ecosystem compatibility
convert_transformers2torchFunctionconvert_model.pyReverse conversion: HuggingFace โ†’ native .pth (half precision)
chat_openai_api.pyScriptchat_openai_api.pyOpenAI SDK client for testing; supports streaming & maintains history

API Request Flow

sequenceDiagram
    participant Client as Client / WebUI
    participant API as FastAPI Server
    participant Streamer as CustomStreamer
    participant Model as MiniMind Model
    participant LoRA as LoRA Weights (opt)

    Client->>API: POST /v1/chat/completions
    API->>API: Parse ChatRequest
    API->>Model: Load base weights
    API-->>LoRA: Merge LoRA (if present)

    alt Streaming Mode
        API->>Streamer: Create queue-based streamer
        API->>Model: model.generate(streamer=...)
        loop Token by token
            Model->>Streamer: put(token)
            Streamer->>API: yield from queue
            API->>Client: SSE data chunk
        end
        API->>Client: SSE [DONE]
    else Non-Streaming
        API->>Model: model.generate()
        Model->>API: full response
        API->>Client: JSON response
    end
    

Interactions & Dependencies

ComponentDepends OnInteraction
serve_openai_api.pyFastAPI, uvicorn, transformersLaunches HTTP server on port 8998 with OpenAI-compatible routes
serve_openai_api.pyMiniMind model, tokenizerLoads model weights and optional LoRA adapter at startup
CustomStreamerTextStreamer (transformers)Overrides put() to enqueue tokens for SSE streaming
web_demo.pyStreamlit, transformersProvides interactive UI; can load model locally or call API endpoint
web_demo.pyserve_openai_api.py (API mode)Proxies chat requests to FastAPI server when in API mode
convert_model.pytorch, transformers, LlamaConfigBidirectional conversion between native and HuggingFace formats
chat_openai_api.pyopenai SDKTests the serve endpoint with streaming/non-streaming requests
All scriptsmodel/ moduleImport model architecture classes for weight loading and inference

Deep-Dive Topics

๐Ÿ”Œ OpenAI API Compatibility

How serve_openai_api.py mirrors the OpenAI /v1/chat/completions schema โ€” request validation, response formatting, tool calling support, and drop-in SDK compatibility.

๐Ÿ“ก Streaming Architecture

The CustomStreamer queue pattern: how tokens flow from model.generate() through a thread-safe queue into SSE chunks, enabling real-time token-by-token responses.

๐Ÿ”„ Model Format Conversion

Three-way conversion between native MiniMind .pth, HuggingFace Transformers, and LlamaForCausalLM formats โ€” weight mapping, precision handling, and config generation.

Directory Map

scripts/
โ”œโ”€โ”€ serve_openai_api.py    # ~178 lines โ€” FastAPI server, OpenAI-compatible API
โ”‚   โ”œโ”€โ”€ ChatRequest          # Pydantic request model
โ”‚   โ”œโ”€โ”€ CustomStreamer        # Queue-based SSE streaming
โ”‚   โ””โ”€โ”€ /v1/chat/completions  # Main endpoint
โ”œโ”€โ”€ web_demo.py           # ~329 lines โ€” Streamlit interactive chat UI
โ”‚   โ”œโ”€โ”€ MODEL_PATHS          # 5 model variants
โ”‚   โ””โ”€โ”€ process_assistant_content  # Think/answer rendering
โ”œโ”€โ”€ convert_model.py      # ~78 lines โ€” Format converters
โ”‚   โ”œโ”€โ”€ torch โ†’ transformers  # MiniMind & Llama formats
โ”‚   โ””โ”€โ”€ transformers โ†’ torch  # Reverse, fp16
โ””โ”€โ”€ chat_openai_api.py    # ~34 lines โ€” API test client

Configuration Reference

ParameterLocationDefaultDescription
portserve_openai_api.py8998HTTP server port for the FastAPI application
temperatureChatRequest0.7Sampling temperature for response generation
top_pChatRequest0.9Nucleus sampling probability threshold
max_tokensChatRequest8192Maximum number of tokens in the generated response
streamChatRequestfalseEnable SSE streaming mode for token-by-token output
toolsChatRequestNoneOptional tool/function definitions for function calling
history_chat_numweb_demo.py5Number of previous conversation turns to include in context
max_new_tokensweb_demo.py1024Maximum generation length in the web UI
MODEL_PATHSweb_demo.py5 variantsMiniMind2-R1, MiniMind2-Small-R1, MiniMind2, MiniMind2-MoE, MiniMind2-Small
use_loraserve_openai_api.pyFalseWhether to load and merge LoRA adapter weights on top of base model

Code Examples

Starting the API Server

Bash โ€” Launch OpenAI-compatible server
# Start the MiniMind API server on default port 8998
python scripts/serve_openai_api.py

# Test with curl
curl -X POST http://localhost:8998/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "MiniMind2",
    "messages": [{"role": "user", "content": "Hello!"}],
    "stream": true,
    "temperature": 0.7
  }'

Model Format Conversion

Python โ€” Convert between model formats
from scripts.convert_model import (
    convert_torch2transformers_minimind,
    convert_torch2transformers_llama,
    convert_transformers2torch,
)

# Native MiniMind โ†’ HuggingFace Transformers
convert_torch2transformers_minimind(
    input_path="weights/minimind2.pth",
    output_dir="weights/hf_minimind2"
)

# Native โ†’ LlamaForCausalLM (ecosystem compatible)
convert_torch2transformers_llama(
    input_path="weights/minimind2.pth",
    output_dir="weights/llama_minimind2"
)

# HuggingFace โ†’ Native .pth (fp16)
convert_transformers2torch(
    input_dir="weights/hf_minimind2",
    output_path="weights/minimind2_fp16.pth"
)

Using the Chat Test Client

Python โ€” Streaming chat client
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8998/v1", api_key="none")

response = client.chat.completions.create(
    model="MiniMind2",
    messages=[{"role": "user", "content": "Explain transformers briefly"}],
    stream=True
)

for chunk in response:
    if chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)