#!/bin/sh
# Copyright (c) 2026 Jiaxing Hu <gahing@gahingwoo.com>
# SPDX-License-Identifier: GPL-2.0
# `charsiu serve`. Not meant to be run directly; charsiu dispatches to it.
#
# It exists to turn config.ini into the environment the NPU path needs, which is
# the same job the runner does for a conversation. Six variables decide whether
# a decode reaches the NPU at all, and getting one wrong does not fail loudly, it
# quietly runs a third as fast on the CPU. KMAX and W4_GROUP must be EQUAL.
set -eu

CHARSIU_HERE=$(dirname "$0")
for _l in ${CHARSIU_LIB:+"$CHARSIU_LIB/charsiu-lib.sh"} \
	  "$CHARSIU_HERE/charsiu-lib.sh" \
	  "$CHARSIU_HERE/../scripts/charsiu-lib.sh" \
	  "$CHARSIU_HERE/../../opt/charsiu/charsiu-lib.sh" \
	  /opt/charsiu/charsiu-lib.sh; do
	[ -r "$_l" ] && { . "$_l"; break; }
done
command -v ini_get >/dev/null 2>&1 \
	|| { echo "$(basename "$0"): charsiu-lib.sh not found" >&2; exit 1; }

CONF=$(charsiu_conf)

BIN=$(find_bin charsiu_serve)
[ -n "$BIN" ] || { echo "charsiu serve: charsiu_serve is not built" >&2; exit 1; }

MODEL="${CHARSIU_MODEL:-$(ini_get model path)}"
# THE SAME FALLBACK THE RUNNER HAS. It was added there and not here, so a
# config naming a model that is gone worked from `charsiu` and died from
# `charsiu serve`, which is a worse place to find out.
if [ -z "$MODEL" ] || [ ! -f "$MODEL" ]; then
	_n=$(all_models | wc -l)
	if [ "$_n" -eq 1 ]; then
		_found=$(all_models | head -1)
		[ -n "$MODEL" ] && echo "charsiu serve: $MODEL is gone; serving ${_found##*/}" >&2
		MODEL="$_found"
	fi
fi
[ -n "$MODEL" ] || { echo "charsiu serve: no model set. Try: charsiu setup" >&2; exit 1; }
[ -f "$MODEL" ] || { echo "charsiu serve: $MODEL does not exist (charsiu pull)" >&2; exit 1; }

CTX=$(ini_get run context);      : "${CTX:=2048}"
THREADS=$(ini_get run threads);  : "${THREADS:=4}"
CPUS=$(ini_get run cpus)
NPU=$(ini_get npu enable);       : "${NPU:=yes}"
KSLICE=$(ini_get npu kslice);    : "${KSLICE:=1024}"
MAXN=$(ini_get npu maxn);        : "${MAXN:=262144}"
COEF=$(ini_get npu coef_elems);  : "${COEF:=65536}"

E=""
INHERITED=""
# See env_default in charsiu-lib.sh: the config file is the default and an
# exported CHARSIU_* is the override.
case "$NPU" in
yes|1|true|on)
	env_default CHARSIU_NPU 1
	env_default CHARSIU_NPU_QUANT 1
	env_default CHARSIU_NPU_W4V 1
	env_default CHARSIU_NPU_KMAX "$KSLICE"
	env_default CHARSIU_NPU_W4_GROUP "$KSLICE"
	env_default CHARSIU_NPU_MAXN "$MAXN"
	env_default CHARSIU_COEF_ELEMS "$COEF" ;;
esac
[ -n "$CPUS" ] && env_default CHARSIU_CPUS "$CPUS"
POOLCPUS=$(ini_get run pool_cpus)
[ -n "$POOLCPUS" ] && env_default CHARSIU_POOL_CPUS "$POOLCPUS"

# Tell whoever started it where to point a browser. "listening on 0.0.0.0" is
# not an address anybody can type.
#
# AND THE PORT HAS TO BE THE REAL ONE. The first version printed the default
# whatever --port said, which is worse than printing nothing: it hands somebody
# an address that does not answer and sends them looking for a network fault.
PORT=11434
prev=""
for a in "$@"; do
	[ "$prev" = "--port" ] && { PORT="$a"; break; }
	prev="$a"
done
ip=$(ip -4 addr show scope global 2>/dev/null \
     | awk '/inet /{sub("/.*","",$2); print $2; exit}')
if [ -n "$ip" ]; then
	printf '\n  reachable at  http://%s:%s\n' "$ip" "$PORT" >&2
	printf '    OpenAI     %s/v1/chat/completions\n' "http://$ip:$PORT" >&2
	printf '    Anthropic  %s/v1/messages\n' "http://$ip:$PORT" >&2
fi

# shellcheck disable=SC2086
exec env $E "$BIN" "$MODEL" -c "$CTX" -t "$THREADS" "$@"
