How to Upload Decensored Models to Hugging Face Using Heretic

Heretic provides an interactive CLI workflow in src/heretic/main.py that authenticates via temporary tokens, constructs repository IDs, and pushes either LoRA adapters or fully merged decensored models to the Hugging Face Hub without persisting credentials to disk.

The p-e-w/heretic repository includes a built-in mechanism to upload decensored models to Hugging Face using Heretic's command-line interface. This workflow handles authentication, repository naming, visibility settings, and model card generation automatically, ensuring your decensored weights reach the Hub securely.

Interactive Upload Workflow in Heretic

The upload sequence is implemented in src/heretic/main.py (lines 777–951) and triggers after you select a trial from the interactive menu. The workflow proceeds through eight distinct stages, from token acquisition to model card attachment.

Authentication and Token Handling

Heretic prioritizes security by avoiding on-disk credential storage. The process begins at lines 777–782:

  • First, the library invokes huggingface_hub.get_token() to check for a cached token.
  • If none exists, prompt_password (defined in src/heretic/utils.py) interactively requests your personal access token. This prevents writing sensitive credentials to disk, which is critical when working on shared GPU servers.

Repository Configuration

Once authenticated, Heretic identifies your Hub identity and constructs the target repository:

  • huggingface_hub.whoami(token) retrieves your username and profile details (lines 783–889), confirming the account that will own the model.
  • The default repository ID follows the pattern <username>/<model-name>-heretic, derived from settings.model in src/heretic/config.py (lines 891–896).
  • You may override this default via an interactive prompt.

Visibility and Merge Strategy Selection

Before pushing, you configure visibility and artifact type:

  • Visibility: The prompt at lines 898–904 lets you select Public or Private, mapping directly to the private boolean passed to push_to_hub.
  • Merge Strategy: obtain_merge_strategy(settings) (lines 907–913) determines whether to upload a LoRA adapter (adapter) or a fully merged model (merged). This aligns with the strategy used for local saves.

Pushing to the Hub

The actual upload occurs at lines 910–924, branching based on the merge strategy:

For LoRA adapters:

model.model.push_to_hub(repo_id, private=private, token=token)

For merged models:

merged_model = model.get_merged_model()
merged_model.push_to_hub(repo_id, private=private, token=token)
model.tokenizer.push_to_hub(repo_id, private=private, token=token)

The merged path explicitly pushes both the model weights and the tokenizer to ensure the repository is complete.

Model Card Generation

Finally, Heretic attaches or creates a model card (lines 927–951):

  • If REPOCARD_NAME (typically README.md) exists in the local model directory, it loads via ModelCard.load.
  • Otherwise, it attempts to fetch the card from the original model on the Hub.
  • If no metadata exists, it initializes a default ModelCardData instance to ensure the Hub receives structured metadata.

The card is then pushed to the repository, completing the upload workflow.

Programmatic Upload Script

While the interactive CLI is the primary interface, you can replicate the upload logic programmatically. The following Python script mirrors the workflow in src/heretic/main.py, allowing you to upload decensored models without manual prompting:

from pathlib import Path
import huggingface_hub
from huggingface_hub import ModelCard, ModelCardData

# 1. Get a token (fallback to manual input)

token = huggingface_hub.get_token()
if not token:
    import getpass
    token = getpass.getpass("Hugging Face access token: ")

# 2. Identify the user

user = huggingface_hub.whoami(token)
repo_id = f"{user['name']}/my-model-heretic"  # customize as needed

private = False  # or True for a private repo

# 3. Load your decensored model

# Replace with your actual Heretic model instance

# model = HereticModel.from_pretrained(...)

# 4. Choose merge strategy

strategy = "merged"  # or "adapter"

if strategy == "adapter":
    model.model.push_to_hub(repo_id, private=private, token=token)
else:
    merged = model.get_merged_model()
    merged.push_to_hub(repo_id, private=private, token=token)
    model.tokenizer.push_to_hub(repo_id, private=private, token=token)

# 5. Handle model card

model_path = Path("path/to/local/model")
card_path = model_path / huggingface_hub.constants.REPOCARD_NAME

if card_path.is_file():
    card = ModelCard.load(card_path)
else:
    try:
        card = ModelCard.load(repo_id)
    except Exception:
        card = ModelCard()

if card.data is None:
    card.data = ModelCardData()

card.push_to_hub(repo_id, token=token, private=private)
print(f"✅ Uploaded to https://huggingface.co/{repo_id}")

When run after you have already performed a Heretic trial (i.e., you have a model object with the decensored weights), this script reproduces the same behaviour as the CLI's "Upload the model to Hugging Face" option.

Key Source Files and Implementation Details

The upload functionality is distributed across four core modules in the p-e-w/heretic repository:

File Role Key Components
src/heretic/main.py Interactive CLI and upload orchestration prompt_password, huggingface_hub.whoami, obtain_merge_strategy, push_to_hub calls (lines 777–951)
src/heretic/config.py Configuration and settings Settings model storing original model path (settings.model) used to derive default repository names
src/heretic/model.py Model abstraction and merging get_merged_model(), push_to_hub(), tokenizer handling
src/heretic/utils.py User interaction utilities prompt_text, prompt_password, prompt_select for interactive prompts

These modules collectively implement the secure, credential-free upload pipeline that distinguishes Heretic's approach to sharing decensored models.

Summary

  • Heretic's upload workflow is implemented in src/heretic/main.py (lines 777–951) and provides an interactive, menu-driven interface for pushing decensored models to Hugging Face.
  • Security-first authentication uses huggingface_hub.get_token() with a fallback to prompt_password, ensuring tokens remain in memory only and are never written to disk—critical for shared GPU environments.
  • Flexible artifact selection via obtain_merge_strategy lets you upload either a LoRA adapter or a fully merged model, with the latter including the tokenizer.
  • Automatic model card handling checks for local README.md files, falls back to remote cards, and ensures valid metadata via ModelCardData before pushing.

Frequently Asked Questions

How does Heretic handle Hugging Face authentication without storing tokens on disk?

Heretic prioritizes security by first attempting to retrieve a cached token via huggingface_hub.get_token(). If no token exists, it invokes prompt_password (defined in src/heretic/utils.py) to request your personal access token interactively. This approach ensures the token remains in memory only and is never written to configuration files, which is essential when operating on shared GPU servers where filesystem credentials could be exposed.

Should I upload a LoRA adapter or a merged model to Hugging Face?

The choice depends on your distribution goals. Heretic's obtain_merge_strategy function (lines 907–913 in src/heretic/main.py) determines the upload artifact:

  • LoRA adapter: Uploads only the adapter weights via model.model.push_to_hub(). This creates a smaller repository and allows users to merge it with the base model locally.
  • Merged model: Creates a full merged copy using model.get_merged_model(), then pushes both the merged weights and the tokenizer. This provides an immediately usable model but results in a larger repository.

Can I make my uploaded decensored model private on Hugging Face?

Yes. During the interactive upload workflow (lines 898–904 in src/heretic/main.py), Heretic prompts you to select repository visibility. Your choice maps directly to the private boolean parameter passed to push_to_hub(). Selecting Private creates a repository visible only to you and explicitly invited collaborators, while Public makes the model accessible to the entire Hugging Face community.

What happens if my local model doesn't have a README.md model card?

Heretic handles missing model cards gracefully. In src/heretic/main.py (lines 927–951), the code first checks for a local REPOCARD_NAME (typically README.md). If not found, it attempts to load an existing card from the remote Hub using the original model ID. If neither exists, it initializes a fresh ModelCard with a default ModelCardData instance to ensure the uploaded repository contains valid metadata. This guarantees that your decensored model appears correctly on the Hugging Face Hub regardless of the original card's presence.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →