DEV: patchbot: add the AI-based bot to pre-select candidate patches to backport

This is a set of scripts, prompts and howtos to have an LLM read commit
messages and determine with great accuracy whether the patch's author
intended for the patch to be backported ASAP, backported after some time,
not backported, or unknown state. It provides all this in an interactive
interface making it easy to adjust choices and proceed with what was
selected. This has been improving over the last 9 months, as helped to
spot patches for a handful of backport sessions, and was only limited by
usability issues (UI). Now that these issues are solved, let's commit the
tool in its current working state. It currently runs every hour in a
crontab for me and started to prove useful since the last update, so it
should be considered in a usable state now, especially since this latest
update reaches close to 100% accuracy compared to a human choice, so it
saves precious development time and may allow stable releases to be
emitted more regularly.

There's detailed readme, please read it before complaining about the
ugliness of the UI :-)
This commit is contained in:
Willy Tarreau
2023-12-18 20:41:41 +01:00
parent 57c5ae10f6
commit 693da29ab7
13 changed files with 1357 additions and 0 deletions

372
dev/patchbot/scripts/post-ai.sh Executable file
View File

@@ -0,0 +1,372 @@
#!/bin/bash
####
#### Todo:
#### - change line color based on the selected radio button
#### - support collapsing lines per color/category (show/hide for each)
#### - add category "next" and see if the prompt can handle that (eg: d3e379b3)
#### - produce multiple lists on output (per category) allowing to save batches
####
die() {
[ "$#" -eq 0 ] || echo "$*" >&2
exit 1
}
err() {
echo "$*" >&2
}
quit() {
[ "$#" -eq 0 ] || echo "$*"
exit 0
}
#### Main
USAGE="Usage: ${0##*/} [ -h ] [ -b 'bkp_list' ] patch..."
MYSELF="$0"
GITURL="http://git.haproxy.org/?p=haproxy.git;a=commitdiff;h="
ISSUES="https://github.com/haproxy/haproxy/issues/"
BKP=""
while [ -n "$1" -a -z "${1##-*}" ]; do
case "$1" in
-h|--help) quit "$USAGE" ;;
-b) BKP="$2"; shift 2 ;;
*) die "$USAGE" ;;
esac
done
PATCHES=( "$@" )
if [ ${#PATCHES[@]} = 0 ]; then
die "$USAGE"
fi
# BKP is a space-delimited list of 8-char commit IDs, we'll
# assign them to the local bkp[] associative array.
declare -A bkp
for cid in $BKP; do
bkp[$cid]=1
done
# some colors
BG_BKP="#e0e0e0"
BT_N="gray"; BG_N="white"
BT_U="#00e000"; BG_U="#e0ffe0"
BT_W="#0060ff"; BG_W="#e0e0ff"
BT_Y="red"; BG_Y="#ffe0e0"
echo "<HTML>"
cat <<- EOF
<HEAD><style>
input.n[type="radio"] {
appearance: none;
width: 1.25em;
height: 1.25em;
border-radius: 50%;
border: 3px solid $BT_N;
background-color: transparent;
}
input.n[type="radio"]:checked {
appearance: none;
width: 1.25em;
height: 1.25em;
border-radius: 50%;
border: 2px solid black;
background-color: $BT_N;
}
input.u[type="radio"] {
appearance: none;
width: 1.25em;
height: 1.25em;
border-radius: 50%;
border: 3px solid $BT_U;
background-color: transparent;
}
input.u[type="radio"]:checked {
appearance: none;
width: 1.25em;
height: 1.25em;
border-radius: 50%;
border: 2px solid black;
background-color: $BT_U;
}
input.w[type="radio"] {
appearance: none;
width: 1.25em;
height: 1.25em;
border-radius: 50%;
border: 3px solid $BT_W;
background-color: transparent;
}
input.w[type="radio"]:checked {
appearance: none;
width: 1.25em;
height: 1.25em;
border-radius: 50%;
border: 2px solid black;
background-color: $BT_W;
}
input.y[type="radio"] {
appearance: none;
width: 1.25em;
height: 1.25em;
border-radius: 50%;
border: 3px solid $BT_Y;
background-color: transparent;
}
input.y[type="radio"]:checked {
appearance: none;
width: 1.25em;
height: 1.25em;
border-radius: 50%;
border: 2px solid black;
background-color: $BT_Y;
}
</style>
<script type="text/javascript"><!--
// statuses are "y", "w", "u", "n"
var statuses = [];
var cid = [];
// first line to review
var review = 0;
// show/hide table lines and update their color
function updt_table(line) {
var n = document.getElementById("sh_n").checked;
var u = document.getElementById("sh_u").checked;
var w = document.getElementById("sh_w").checked;
var y = document.getElementById("sh_y").checked;
var tn = 0, tu = 0, tw = 0, ty = 0;
var i, el;
for (i = 1; i < statuses.length; i++) {
if (statuses[i] == "n") {
tn++;
if (line && i != line)
continue;
el = document.getElementById("tr_" + i);
el.style.backgroundColor = "$BG_N";
el.style.display = n && i >= review ? "" : "none";
}
else if (statuses[i] == "u") {
tu++;
if (line && i != line)
continue;
el = document.getElementById("tr_" + i);
el.style.backgroundColor = "$BG_U";
el.style.display = u && i >= review ? "" : "none";
}
else if (statuses[i] == "w") {
tw++;
if (line && i != line)
continue;
el = document.getElementById("tr_" + i);
el.style.backgroundColor = "$BG_W";
el.style.display = w && i >= review ? "" : "none";
}
else if (statuses[i] == "y") {
ty++;
if (line && i != line)
continue;
el = document.getElementById("tr_" + i);
el.style.backgroundColor = "$BG_Y";
el.style.display = y && i >= review ? "" : "none";
}
else {
// bug
if (line && i != line)
continue;
el = document.getElementById("tr_" + i);
el.style.backgroundColor = "red";
el.style.display = "";
}
}
document.getElementById("cnt_n").innerText = tn;
document.getElementById("cnt_u").innerText = tu;
document.getElementById("cnt_w").innerText = tw;
document.getElementById("cnt_y").innerText = ty;
}
function updt_output() {
var i, y = "", w = "", u = "", n = "";
for (i = 1; i < statuses.length; i++) {
if (i < review)
continue;
if (statuses[i] == "y")
y = y + " " + cid[i];
else if (statuses[i] == "w")
w = w + " " + cid[i];
else if (statuses[i] == "u")
u = u + " " + cid[i];
else if (statuses[i] == "n")
n = n + " " + cid[i];
}
// update the textarea
document.getElementById("output").value =
"cid_y=(" + y + " )\n" +
"cid_w=(" + w + " )\n" +
"cid_u=(" + u + " )\n" +
"cid_n=(" + n + " )\n";
}
function updt(line,value) {
if (value != "r") {
statuses[line] = value;
} else {
review = line;
line = 0; // redraw everything
}
updt_table(line);
updt_output();
}
// -->
</script>
</HEAD>
EOF
echo "<BODY>"
echo -n "<big><big>Show:"
echo -n " <span style='background-color:$BG_N'><input type='checkbox' onclick='updt_table(0);' id='sh_n' checked />N (<span id='cnt_n'>0</span>)</span> "
echo -n " <span style='background-color:$BG_U'><input type='checkbox' onclick='updt_table(0);' id='sh_u' checked />U (<span id='cnt_u'>0</span>)</span> "
echo -n " <span style='background-color:$BG_W'><input type='checkbox' onclick='updt_table(0);' id='sh_w' checked />W (<span id='cnt_w'>0</span>)</span> "
echo -n " <span style='background-color:$BG_Y'><input type='checkbox' onclick='updt_table(0);' id='sh_y' checked />Y (<span id='cnt_y'>0</span>)</span> "
echo -n "</big/></big> (N=no/drop, U=uncertain, W=wait/next, Y=yes/pick"
if [ -n "$BKP" ]; then
echo -n ", <span style='background-color:$BG_BKP'>&nbsp;backported&nbsp;</span>"
fi
echo ")<P/>"
echo "<TABLE COLS=5 BORDER=1 CELLSPACING=0 CELLPADDING=3>"
echo "<TR><TH>All<br/><input type='radio' name='review' onclick='updt(0,\"r\");' checked title='Start review here'/></TH><TH>CID</TH><TH>Subject</TH><TH>Verdict<BR>N U W Y</BR></TH><TH>Reason</TH></TR>"
seq_num=1; do_check=1; review=0;
for patch in "${PATCHES[@]}"; do
# try to retrieve the patch's numbering (0001-9999)
pnum="${patch##*/}"
pnum="${pnum%%[^0-9]*}"
id=$(sed -ne 's/^#id: \(.*\)/\1/p' "$patch")
resp=$(grep -v ^llama "$patch" | sed -ne '/^Explanation:/,$p' | sed -z 's/\n[\n]*/\n/g' | sed -z 's/\([^. ]\)\n\([A-Z]\)/\1.\n\2/' | tr '\012' ' ')
resp="${resp#Explanation:}";
while [ -n "$resp" -a -z "${resp##[ .]*}" ]; do
resp="${resp#[ .]}"
done
respl=$(echo -- "$resp" | tr 'A-Z' 'a-z')
if [[ "${respl}" =~ (conclusion|verdict)[:\ ][^.]*yes ]]; then
verdict=yes
elif [[ "${respl}" =~ (conclusion|verdict)[:\ ][^.]*wait ]]; then
verdict=wait
elif [[ "${respl}" =~ (conclusion|verdict)[:\ ][^.]*no ]]; then
verdict=no
elif [[ "${respl}" =~ (conclusion|verdict)[:\ ][^.]*uncertain ]]; then
verdict=uncertain
elif [[ "${respl}" =~ (\"wait\"|\"yes\"|\"no\"|\"uncertain\")[^\"]*$ ]]; then
# last word under quotes in the response, sometimes happens as
# in 'thus I would conclude "no"'.
verdict=${BASH_REMATCH[1]}
else
verdict=uncertain
fi
verdict="${verdict//[\"\',;:. ]}"
verdict=$(echo -n "$verdict" | tr '[A-Z]' '[a-z]')
# There are two formats for the ID line:
# - old: #id: cid subject
# - new: #id: cid author date subject
# We can detect the 2nd one as the date starts with a series of digits
# followed by "-" then an upper case letter (eg: "18-Dec23").
set -- $id
cid="$1"
author=""
date=""
if [ -n "$3" ] && [ -z "${3##[1-9]-[A-Z]*}" -o -z "${3##[0-3][0-9]-[A-Z]*}" ]; then
author="$2"
date="$3"
subj="${id#$cid $author $date }"
else
subj="${id#$cid }"
fi
if [ -z "$cid" ]; then
echo "ERROR: commit ID not found in patch $pnum: $patch" >&2
continue
fi
echo "<script type='text/javascript'>cid[$seq_num]='$cid';statuses[$seq_num]='$verdict'[0];</script>"
echo -n "<TR id='tr_$seq_num' name='$cid'"
# highlight unqualified docs and bugs
if [ "$verdict" != "no" ]; then
: # no special treatment for accepted/uncertain elements
elif [ -z "${subj##BUG*}" ] && ! [[ "${respl}" =~ (explicitly|specifically|clearly|also|commit\ message|does)[\ ]*(state|mention|say|request) ]]; then
# bold for BUG marked "no" with no "explicitly states that ..."
echo -n " style='font-weight:bold'"
elif [ -z "${subj##DOC*}" ]; then # && ! [[ "${respl}" =~ (explicitly|specifically|clearly|also|commit\ message|does)[\ ]*(state|mention|say|request) ]]; then
# gray for DOC marked "no"
echo -n " style='font-weight:bold'"
#echo -n " bgcolor=#E0E0E0" #"$BG_U"
fi
echo -n ">"
# HTMLify subject and summary
subj="${subj//&/&amp;}"; subj="${subj//</&lt;}"; subj="${subj//>/&gt;}";
resp="${resp//&/&amp;}"; resp="${resp//</&lt;}"; resp="${resp//>/&gt;}";
# turn "#XXXX" to a link to an issue
resp=$(echo "$resp" | sed -e "s|#\([0-9]\{1,5\}\)|<a href='${ISSUES}\1'>#\1</a>|g")
# put links to commit IDs
resp=$(echo "$resp" | sed -e "s|\([0-9a-f]\{8,40\}\)|<a href='${GITURL}\1'>\1</a>|g")
echo -n "<TD nowrap align=center ${bkp[$cid]:+style='background-color:${BG_BKP}'}>$seq_num<BR/>"
echo -n "<input type='radio' name='review' onclick='updt($seq_num,\"r\");' ${do_check:+checked} title='Start review here'/></TD>"
echo -n "<TD nowrap ${bkp[$cid]:+style='background-color:${BG_BKP}'}><tt><a href='${GITURL}${cid}'>$cid</a></tt>${date:+<br/><small style='font-weight:normal'>$date</small>}</TD>"
echo -n "<TD nowrap><a href='${GITURL}${cid}'>${pnum:+$pnum }$subj</a>${author:+<br/><div align=right><small style='font-weight:normal'>$author</small></div>}</TD>"
echo -n "<TD nowrap align=center>"
echo -n "<input type='radio' onclick='updt($seq_num,\"n\");' id='bt_${seq_num}_n' class='n' name='$cid' value='n' title='Drop' $( [ "$verdict" != no ] || echo -n checked) />"
echo -n "<input type='radio' onclick='updt($seq_num,\"u\");' id='bt_${seq_num}_u' class='u' name='$cid' value='u' title='Uncertain' $( [ "$verdict" != uncertain ] || echo -n checked) />"
echo -n "<input type='radio' onclick='updt($seq_num,\"w\");' id='bt_${seq_num}_w' class='w' name='$cid' value='w' title='wait in -next' $([ "$verdict" != wait ] || echo -n checked) />"
echo -n "<input type='radio' onclick='updt($seq_num,\"y\");' id='bt_${seq_num}_y' class='y' name='$cid' value='y' title='Pick' $( [ "$verdict" != yes ] || echo -n checked) />"
echo -n "</TD>"
echo -n "<TD>$resp</TD>"
echo "</TR>"
echo
((seq_num++))
# if this patch was already backported, make the review start on the next
if [ -n "${bkp[$cid]}" ]; then
review=$seq_num
do_check=1
else
do_check=
fi
done
echo "<TR><TH>New<br/><input type='radio' name='review' onclick='updt($seq_num,\"r\");' ${do_check:+checked} title='Nothing to backport'/></TH><TH>CID</TH><TH>Subject</TH><TH>Verdict<BR>N U W Y</BR></TH><TH>Reason</TH></TR>"
echo "</TABLE>"
echo "<P/>"
echo "<H3>Output:</H3>"
echo "<textarea cols=120 rows=10 id='output'></textarea>"
echo "<P/>"
echo "<script type='text/javascript'>review=$review; updt_table(0); updt_output();</script>"
echo "</BODY></HTML>"

View File

@@ -0,0 +1,56 @@
#!/bin/bash
# the patch itself
F="$1"
shift
# if non-empty, force to redo the patch
FORCE="${FORCE:-}"
CPU="${CPU:-$(nproc)}"
MODEL="${MODEL:-../models/airoboros-l2-13b-gpt4-1.4.1.Q5_K_M.gguf}"
PROMPT_PFX="${PROMPT_PFX:-prompt14-airo14-pfx.txt}"
PROMPT_SFX="${PROMPT_SFX:-prompt14-airo14-sfx.txt}"
CACHE="${CACHE:-prompt-airo14.cache}"
CACHE_RO="${CACHE_RO- --prompt-cache-ro}"
EXT="${EXT:-airo14.txt}"
OUTPUT="${OUTPUT:-$(set -- "$F"."$EXT"; echo $1)}"
MAINPROG="${MAINPROG:-./main}"
# switch to interactive mode with this reverse-prompt at the end if set.
# Typically: INTERACTIVE="Developer".
INTERACTIVE=${INTERACTIVE:-""}
# Compute the full prompt
#
# Input format for "$F": git-format-patch with lines in this order:
# 1: From cid ...
# 2: From: author user@...
# 3: Date:
# 4: Subject:
# ...
# n: ^---$
# It will emit a preliminary line with the commit ID, the author, the date,
# the subject, then the whole commit message indented. The output can be
# searched using grep '^\(Bot:\|#id:\)'
PROMPT="$(cat "$PROMPT_PFX"; cat "$F" | sed -e '/^---/,$d' -e '/^Signed-off-by:/d' -e '/^Cc:/d' -e '/^Reported-by:/d' -e '/^Acked-by:/d' -e '1s/From \([0-9a-f]\{8\}\)\([0-9a-f]\{32\}\).*/\1/' -e '2s/^From: .*<\([^<@>]*\)@\([^<.>]*\).*/\1@\2/' -e '3s/^Date:[^,]*, \([^ ]*\) \([^ ]*\) 20\([^ ]*\).*/\1-\2\3/' | sed -ne '1h;1d;2x;2G;2h;2d;3x;3G;3h;3d;4x;4G;4s/^\([^\n]*\)\n\([^\n]*\)\n\([^\n]*\)\nSubject: \(.*\)/#id: \1 \2 \3 \4\n\nSubject: \4/;p' | sed -e '3,$s/^/ \0/'; echo; cat "$PROMPT_SFX")"
# already done: don't do it again. Note that /dev/null is OK
if [ -z "$FORCE" -a -s "$OUTPUT" ]; then
exit 0
fi
# In order to rebuild the prompt cache:
# OUTPUT=blah CACHE_RO= ./$0 /dev/null
#
# Note: airoboros is able to carefully isolate an entire context, tests show
# that it's possible to ask it to repeat the entire commit message and it does
# so correctly. However its logic is sometimes bizarre
if [ -z "$INTERACTIVE" ]; then
LANG=C "$MAINPROG" --log-disable --model "$MODEL" --threads "$CPU" --ctx_size 4096 --temp 0.36 --top_k 12 --top_p 1 --repeat_last_n 256 --batch_size 16384 --repeat_penalty 1.1 --n_predict 200 --multiline-input --prompt "$PROMPT" --prompt-cache "$CACHE" $CACHE_RO "$@" 2>&1 | grep -v ^llama_model_loader | grep -v ^llm_load_ > "${OUTPUT}"
else
LANG=C "$MAINPROG" --log-disable --model "$MODEL" --threads "$CPU" --ctx_size 4096 --temp 0.36 --repeat_penalty 1.1 --n_predict 200 --multiline-input --prompt "$PROMPT" --prompt-cache "$CACHE" $CACHE_RO -n -1 -i --color --in-prefix ' ' --reverse-prompt "$INTERACTIVE:" "$@"
fi

View File

@@ -0,0 +1,79 @@
#!/bin/bash
# note: the program may re-execute itself: when it has more than one patch to
# process, it will call itself with one patch only in argument. When called
# with a single patch in argument, it will always start the analysis directly.
# The program uses several environment variables:
# - EXT file name extension for the response
# - MODEL path to the model file (GGUF format)
# - FORCE force to re-process existing patches
# - PROGRAM path to the script to be called
# - CACHE path to the prompt cache (optional)
# - CACHE_RO force cache to remain read-only
# - PROMPT_PFX path to the prompt prefix (before the patch)
# - PROMPT_SFX path to the prompt suffix (after the patch)
# - TOT_CPUS total number of usable CPUs (def: nproc or 1)
# - SLOT_CPUS if defined, it's an array of CPU sets for each running slot
# - CPU_SLOT passed by the first level to the second one to allow binding
# to a specific CPU set based on the slot number from 0 to N-1.
die() {
[ "$#" -eq 0 ] || echo "$*" >&2
exit 1
}
err() {
echo "$*" >&2
}
quit() {
[ "$#" -eq 0 ] || echo "$*"
exit 0
}
#### Main
# detect if running under -x, pass it down to sub-processes
#opt=; set -o | grep xtrace | grep -q on && opt=-x
USAGE="Usage: ${0##*/} [ -s slots ] patch..."
MYSELF="$0"
TOT_CPUS=${TOT_CPUS:-$(nproc)}
TOT_CPUS=${TOT_CPUS:-1}
SLOTS=1
while [ -n "$1" -a -z "${1##-*}" ]; do
case "$1" in
-s) SLOTS="$2" ; shift 2 ;;
-h|--help) quit "$USAGE" ;;
*) die "$USAGE" ;;
esac
done
[ -n "$EXT" ] || die "Missing extension name (EXT)"
[ -n "$MODEL" ] || die "Missing model name (MODEL)"
[ -n "$PROGRAM" ] || die "Missing program name (PROGRAM)"
[ -n "$PROMPT_PFX" ] || die "Missing prompt prefix (PROMPT_PFX)"
[ -n "$PROMPT_SFX" ] || die "Missing prompt suffix (PROMPT_SFX)"
PATCHES=( "$@" )
if [ ${#PATCHES[@]} = 0 ]; then
die "$USAGE"
elif [ ${#PATCHES[@]} = 1 ]; then
# really execute
taskset_cmd=""
if [ -n "$CPU_SLOT" ] && [ -n "${SLOT_CPUS[$CPU_SLOT]}" ]; then
taskset_cmd="taskset -c ${SLOT_CPUS[$CPU_SLOT]}"
fi
export CPU=$TOT_CPUS
${taskset_cmd} ${PROGRAM} "${PATCHES[0]}"
else
# divide CPUs by number of slots
export TOT_CPUS=$(( (TOT_CPUS + SLOTS - 1) / SLOTS ))
# reexecute ourselves in parallel with a single patch each
xargs -n 1 -P "${SLOTS}" --process-slot-var=CPU_SLOT "${MYSELF}" -s 1 <<< "${PATCHES[@]}"
fi

View File

@@ -0,0 +1,66 @@
#!/bin/bash
SCRIPTS_DIR="$HOME/prog/scripts"
HAPROXY_DIR="$HOME/data/in/haproxy"
PATCHES_PFX="$HOME/data/in/patches"
VERDICT_DIR="$HOME/data/out"
PROMPTS_DIR="$HOME/data/prompts"
MODELS_DIR="$HOME/data/models"
MAINPROG="$HOME/prog/bin/main"
PARALLEL_RUNS=2
BRANCH=$(cd "$HAPROXY_DIR" && git describe --tags HEAD|cut -f1 -d-|cut -f2- -dv)
if [ -z "$BRANCH" ]; then
echo "Couldn't guess current branch, aborting."
exit 1
fi
# eg: for v3.0-dev0^ we should get v2.9.0 hence "2.9"
STABLE=$(cd "$HAPROXY_DIR" && git describe --tags "v${BRANCH}-dev0^" |cut -f1,2 -d.|cut -f2- -dv)
PATCHES_DIR="$PATCHES_PFX"-"$BRANCH"
(cd "$HAPROXY_DIR"
git pull
last_file=$(ls -1 "$PATCHES_DIR"/*.patch 2>/dev/null | tail -n1)
if [ -n "$last_file" ]; then
restart=$(head -n1 "$last_file" | cut -f2 -d' ')
else
restart="v${BRANCH}-dev0"
fi
"$SCRIPTS_DIR"/mk-patch-list.sh -o "$PATCHES_DIR" -b v${BRANCH}-dev0 $(git log $restart.. --oneline | cut -f1 -d' ')
)
# List backported fixes (possibly none)
BKP=(
$(
cd "$HAPROXY_DIR"
if ! git remote update "$STABLE"; then
git remote add "$STABLE" "http://git.haproxy.org/git/haproxy-${STABLE}.git/"
git remote update "$STABLE"
fi >&2
git log --no-decorate --reverse "v${STABLE}.0..${STABLE}/master" |
sed -ne 's,(cherry picked from commit \(.\{8\}\).*,\1,p'
)
)
# by far the best model for now with little uncertain and few wait
echo "${BRANCH}: mistral-7b-v0.2"
if [ ! -e "${PROMPTS_DIR}/prompt-${BRANCH}-m7bv02.cache" -o "${PROMPTS_DIR}/prompt15-${BRANCH}-mist7bv2-pfx.txt" -nt "${PROMPTS_DIR}/prompt-${BRANCH}-m7bv02.cache" ]; then
echo "Regenerating the prompt cache, may take 1-2 min"
rm -f "${PROMPTS_DIR}/prompt-${BRANCH}-m7bv02.cache"
rm -f empty
touch empty
time EXT=m7bv02.txt MODEL=${MODELS_DIR}/mistral-7b-instruct-v0.2.Q5_K_M.gguf CACHE=${PROMPTS_DIR}/prompt-${BRANCH}-m7bv02.cache CACHE_RO= PROMPT_PFX=${PROMPTS_DIR}/prompt15-${BRANCH}-mist7bv2-pfx.txt PROMPT_SFX=${PROMPTS_DIR}/prompt15-${BRANCH}-mist7bv2-sfx.txt MAINPROG=$MAINPROG PROGRAM="$SCRIPTS_DIR"/process-patch-v15.sh "$SCRIPTS_DIR"/submit-ai.sh empty
rm -f empty empty.m7bv02.txt
echo "Done!"
fi
# Now process the patches, may take 1-2 hours
time EXT=m7bv02.txt MODEL=${MODELS_DIR}/mistral-7b-instruct-v0.2.Q5_K_M.gguf CACHE=${PROMPTS_DIR}/prompt-${BRANCH}-m7bv02.cache PROMPT_PFX=${PROMPTS_DIR}/prompt15-${BRANCH}-mist7bv2-pfx.txt PROMPT_SFX=${PROMPTS_DIR}/prompt15-${BRANCH}-mist7bv2-sfx.txt MAINPROG=$MAINPROG PROGRAM="$SCRIPTS_DIR"/process-patch-v15.sh "$SCRIPTS_DIR"/submit-ai.sh -s ${PARALLEL_RUNS} ${PATCHES_DIR}/*.patch
# generate the output, takes 3-5 seconds
"$SCRIPTS_DIR"/post-ai.sh -b "${BKP[*]}" ${PATCHES_DIR}/*.m7bv02.txt > ${VERDICT_DIR}/verdict-${BRANCH}-m7bv02.html