Files
kde-ollamacloud-dashboard/contents/scripts/usage.sh
T
Matteo Benedetto 374a54d46a feat: dashboard KDE Plasma 6 per residui Ollama Cloud con countdown reset
Widget desktop 224x224 che mostra:
- residuo sessione (5h) e settimanale (7d) con barre colorate
- conteggio richieste, costo 4 settimane, modello top
- timer di reset stimati (calibrati al primo reset osservato)
- backend bash che chiama https://ollama.com/api/usage
- legge la chiave API da ~/.pi/agent/auth.json (provider ollama-cloud)
2026-08-20 18:34:38 +02:00

98 lines
4.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# Backend del plasmoide Ollama Cloud Usage.
# L'API /api/usage non fornisce reset_at: manteniamo una stima locale e la
# ricalibriamo quando usage/conteggi scendono (reset effettivamente osservato).
set -u
AUTH_JSON="${OLLAMA_AUTH_JSON:-$HOME/.pi/agent/auth.json}"
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/ollama-cloud-usage"
STATE_FILE="$STATE_DIR/reset-state.json"
KEY=""
if [ -f "$AUTH_JSON" ]; then
KEY="$(jq -r '.["ollama-cloud"].key // empty' "$AUTH_JSON" 2>/dev/null)"
fi
[ -n "$KEY" ] || KEY="${OLLAMA_API_KEY:-}"
if [ -z "$KEY" ]; then
printf '{"ok":false,"error":"no_api_key","detail":"nessuna chiave Ollama Cloud configurata"}\n'
exit 0
fi
RESP="$(curl -sS --max-time 15 https://ollama.com/api/usage -H "Authorization: Bearer $KEY" 2>/dev/null)"
CURL_RC=$?
if [ "$CURL_RC" -ne 0 ] || [ -z "$RESP" ]; then
printf '{"ok":false,"error":"fetch_failed","detail":"curl exit %s"}\n' "$CURL_RC"
exit 0
fi
if ! printf '%s' "$RESP" | jq -e '(.limits.session.usage | type == "number") and (.limits.weekly.usage | type == "number")' >/dev/null 2>&1; then
printf '{"ok":false,"error":"bad_json","detail":"risposta non valida da /api/usage"}\n'
exit 0
fi
NOW="$(date +%s)"
SESSION_USAGE="$(printf '%s' "$RESP" | jq -c '.limits.session.usage')"
WEEKLY_USAGE="$(printf '%s' "$RESP" | jq -c '.limits.weekly.usage')"
SESSION_REQ="$(printf '%s' "$RESP" | jq -c '[.limits.session.models[]?.request_count] | add // 0')"
WEEKLY_REQ="$(printf '%s' "$RESP" | jq -c '[.limits.weekly.models[]?.request_count] | add // 0')"
mkdir -p "$STATE_DIR" 2>/dev/null || true
STATE_JSON='{}'
[ -f "$STATE_FILE" ] && STATE_JSON="$(<"$STATE_FILE")"
# $1 nome finestra, $2 durata sec, $3 usage, $4 richieste.
# Esporta RESET_AT e CALIBRATED; calibrated=true solo dopo reset osservato.
calculate_reset() {
local name="$1" duration="$2" current_usage="$3" current_req="$4"
local previous_usage previous_req previous_reset previous_calibrated detected periods
previous_usage="$(printf '%s' "$STATE_JSON" | jq -c --arg n "$name" '.[$n].last_usage // null')"
previous_req="$(printf '%s' "$STATE_JSON" | jq -c --arg n "$name" '.[$n].last_requests // null')"
previous_reset="$(printf '%s' "$STATE_JSON" | jq -r --arg n "$name" '.[$n].reset_at // 0')"
previous_calibrated="$(printf '%s' "$STATE_JSON" | jq -r --arg n "$name" '.[$n].calibrated // false')"
detected="$(jq -n --argjson old "$previous_usage" --argjson new "$current_usage" --argjson oldreq "$previous_req" --argjson newreq "$current_req" '($old != null and (($new < ($old - 0.02)) or ($newreq < $oldreq)))')"
if [ "$previous_reset" -le 0 ]; then
RESET_AT=$((NOW + duration))
CALIBRATED=false
elif [ "$detected" = true ]; then
RESET_AT=$((NOW + duration))
CALIBRATED=true
else
RESET_AT="$previous_reset"
CALIBRATED="$previous_calibrated"
# Una stima scaduta viene avanzata; resta STIMA finché non osserviamo il reset.
if [ "$NOW" -ge "$RESET_AT" ]; then
periods=$(((NOW - RESET_AT) / duration + 1))
RESET_AT=$((RESET_AT + periods * duration))
fi
fi
}
calculate_reset session 18000 "$SESSION_USAGE" "$SESSION_REQ"
SESSION_RESET="$RESET_AT"
SESSION_CALIBRATED="$CALIBRATED"
calculate_reset weekly 604800 "$WEEKLY_USAGE" "$WEEKLY_REQ"
WEEKLY_RESET="$RESET_AT"
WEEKLY_CALIBRATED="$CALIBRATED"
# Stato 0600: non contiene segreti, ma resta privato al profilo utente.
umask 077
TMP_STATE="$(mktemp "$STATE_DIR/.reset-state.XXXXXX" 2>/dev/null || true)"
if [ -n "$TMP_STATE" ]; then
jq -n \
--argjson now "$NOW" \
--argjson su "$SESSION_USAGE" --argjson sr "$SESSION_REQ" --argjson sat "$SESSION_RESET" --argjson sc "$SESSION_CALIBRATED" \
--argjson wu "$WEEKLY_USAGE" --argjson wr "$WEEKLY_REQ" --argjson wat "$WEEKLY_RESET" --argjson wc "$WEEKLY_CALIBRATED" \
'{updated_at:$now,session:{last_usage:$su,last_requests:$sr,reset_at:$sat,calibrated:$sc},weekly:{last_usage:$wu,last_requests:$wr,reset_at:$wat,calibrated:$wc}}' >"$TMP_STATE" && mv "$TMP_STATE" "$STATE_FILE"
fi
printf '%s' "$RESP" | jq -c \
--argjson fetched "$NOW" \
--argjson session_at "$SESSION_RESET" --argjson session_cal "$SESSION_CALIBRATED" \
--argjson weekly_at "$WEEKLY_RESET" --argjson weekly_cal "$WEEKLY_CALIBRATED" \
'{ok:true,session:{remaining:(1-.limits.session.usage),used:.limits.session.usage,models:.limits.session.models},weekly:{remaining:(1-.limits.weekly.usage),used:.limits.weekly.usage,models:.limits.weekly.models},activity:.activity,fetched_at:$fetched,resets:{session:{at:$session_at,estimated:($session_cal|not)},weekly:{at:$weekly_at,estimated:($weekly_cal|not)}}}' \
2>/dev/null || printf '{"ok":false,"error":"bad_json","detail":"impossibile elaborare la risposta"}\n'
exit 0