mirror of
http://git.haproxy.org/git/haproxy.git
synced 2026-02-05 17:43:23 +02:00
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:
372
dev/patchbot/scripts/post-ai.sh
Executable file
372
dev/patchbot/scripts/post-ai.sh
Executable 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'> backported </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//&/&}"; subj="${subj//</<}"; subj="${subj//>/>}";
|
||||
resp="${resp//&/&}"; resp="${resp//</<}"; resp="${resp//>/>}";
|
||||
|
||||
# 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>"
|
||||
56
dev/patchbot/scripts/process-patch-v15.sh
Executable file
56
dev/patchbot/scripts/process-patch-v15.sh
Executable 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
|
||||
79
dev/patchbot/scripts/submit-ai.sh
Executable file
79
dev/patchbot/scripts/submit-ai.sh
Executable 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
|
||||
|
||||
66
dev/patchbot/scripts/update-3.0.sh
Executable file
66
dev/patchbot/scripts/update-3.0.sh
Executable 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
|
||||
Reference in New Issue
Block a user