Skip to content

Instantly share code, notes, and snippets.

@crasm
Last active March 30, 2024 18:26
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save crasm/41b5b11111d2f2419b31da159fa77447 to your computer and use it in GitHub Desktop.
Save crasm/41b5b11111d2f2419b31da159fa77447 to your computer and use it in GitHub Desktop.
Shell script for merging TheBloke's .gguf-split model files
#!/bin/sh
log() {
format="$1"; shift
# shellcheck disable=SC2059
>&2 printf "$format\n" "$@"
}
usage() {
>&2 cat <<EOF
usage: $(basename "$0") [-d] <model-prefix>
options:
-d (delete split files during merge, saves space)
example:
$ $(basename "$0") airoboros-l2-70b-2.1-creative.Q8_0.gguf-split-
Merging:
airoboros-l2-70b-2.1-creative.Q8_0.gguf-split-a
airoboros-l2-70b-2.1-creative.Q8_0.gguf-split-b
Into:
airoboros-l2-70b-2.1-creative.Q8_0.gguf
...
EOF
exit 1
}
# This is called by an interrupt
# shellcheck disable=SC2317
cleanup() {
if [ -n "$1" ]; then
log "$@"
else
log 'Interrupted...'
fi
if [ -w "$split_a" ] && [ -n "$split_a_size" ]; then
log 'Truncating "%s" to original size: %s bytes\n' "$split_a" "$split_a_size"
truncate -c -s "$split_a_size" "$split_a"
fi
exit 1
}
opt_delete=
while getopts 'd' opt; do
case $opt in
'd')
opt_delete='true'
shift
;;
'?')
log 'Invalid option: %s' "-$OPTARG"
usage
esac
done
if [ "$#" -ne 1 ]; then
usage
fi
model="$1"
# Load file glob into arg array
set -- "${model}"*
# Check if the glob failed to expand
if [ "$1" = "${model}*" ]; then
log 'No files found.'
exit 1
fi
split_a="$1"
split_a_size="$(stat -c '%s' "$split_a")"
target="$(echo "$split_a" | sed 's/-split-a$//')"
log 'Merging:'
log '\t%s' "$@"
log 'Into:\n\t%s\n' "$target"
>&2 printf 'Continue? [Y/n] '
read -r response
case "$response" in
[Yy]|"") ;;
*)
log 'Exiting...'
exit 0
;;
esac
# Enable cleanup hook now, since we will begin operating on the files
trap cleanup INT
# Remove $1 (...-split-a) from arg array, which is the target for the appends
shift
for i in "$@"; do
log "Appending $i"
dd if="$i" of="$split_a" bs=32M oflag=append conv=notrunc status=progress || cleanup 'dd exited with error: %s' "$?"
if [ "$opt_delete" = 'true' ]; then
rm -vf "$i" || log 'failed to rm %s' "$i"
fi
done
mv -vf "$split_a" "$target"
@F474M0R64N4
Copy link

F474M0R64N4 commented Nov 3, 2023

import glob
import os
import shutil
from argparse import ArgumentParser

def log(format, *args):
    print(format % args, file=sys.stderr)

def usage():
    print(f"""usage: {os.path.basename(sys.argv[0])} [-d] <model-prefix>
options:
    -d      (delete split files during merge, saves space)
example:
    {os.path.basename(sys.argv[0])} airoboros-l2-70b-2.1-creative.Q8_0.gguf-split-
    Merging:
        airoboros-l2-70b-2.1-creative.Q8_0.gguf-split-a
        airoboros-l2-70b-2.1-creative.Q8_0.gguf-split-b
    Into:
        airoboros-l2-70b-2.1-creative.Q8_0.gguf
    ...
    """, file=sys.stderr)
    sys.exit(1)

def main():
    parser = ArgumentParser()
    parser.add_argument('-d', '--delete', action='store_true', help='delete split files during merge, saves space')
    parser.add_argument('model', help='model prefix')
    args = parser.parse_args()

    files = glob.glob(f'{args.model}*')
    if not files:
        log('No files found.')
        sys.exit(1)

    files.sort()
    split_a = files[0]
    target = split_a.replace('-split-a', '')

    log('Merging:')
    for file in files:
        log('\t%s', file)
    log('Into:\n\t%s\n', target)

    response = input('Continue? [Y/n] ')
    if response.lower() not in ['', 'y']:
        log('Exiting...')
        sys.exit(0)

    for file in files[1:]:
        log('Appending %s', file)
        with open(split_a, 'ab') as out_file, open(file, 'rb') as in_file:
            shutil.copyfileobj(in_file, out_file)
        if args.delete:
            try:
                os.remove(file)
                log('Removed %s', file)
            except OSError as e:
                log('Failed to remove %s: %s', file, e)

    try:
        os.rename(split_a, target)
        log('Moved %s to %s', split_a, target)
    except OSError as e:
        log('Failed to move %s to %s: %s', split_a, target, e)

if __name__ == "__main__":
    main()```

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment