Files
Linux_post_install/lib/ai-providers/openrouter.sh
T
Your Name 01aa7f3e8f
gates / consistency-and-conventions (push) Successful in 32s
fix: OpenRouter 402 — send max_tokens cost cap; make session window configurable
User hit 'API error 402: ... You requested up to 131072 tokens, but can
only afford 4511' on the assist alias: no provider ever sent max_tokens,
so OpenRouter's credit pre-check billed the routed model's full
worst-case output; user also asked to bound session history to the last
5 requests/responses.

Architect decisions:
- AI_MAX_TOKENS (num, default 2048): sent as max_tokens on OpenRouter
  and generationConfig.maxOutputTokens on Gemini — a real per-request
  cost ceiling. llamacpp unchanged (local/free, no pre-check).
- AI_SESSION_TURNS (num, default 40 kept back-compat; messages, 2 per
  exchange — 10 = last 5 conversations): resolved lazily in session_push
  because config loads after the hardcoded line-25 default.
- Both registered in the bin/pos-ai POS_CONFIG @General section, so they
  appear in 'pos config ai' with num: validation.

Reviewer hardening (CHANGES_REQUIRED -> fixed): unguarded env input could
reach jq tonumber (0/-5/010/abc all savable via config-ui's ^-?[0-9]+$)
and abort the CLI; both providers and session_push now guard with
^[1-9][0-9]*$ and fall back to the default.

Verified: fake-curl shim smoke (16 provider-body + 12 session-window
checks incl. the 010-regression proof), make gen idempotent, make check
OK, make lint 0 FAIL/0 WARN, make test 17 files / 299 checks / 0 fail
(~49s), bash -n clean, git diff --check clean. Reviewer ACCEPT (twice).

Tester regression round (permanent provider-body + session-pruning
coverage) intentionally not run this cycle — user's call; remains a
documented follow-up.
2026-09-07 07:25:38 -04:00

66 lines
2.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# OpenRouter provider adapter for pos-ai
# Provider-specific: API call, auth, response parsing, models list
# Part of the R8 provider-agnostic architecture (lib/ai-providers/).
# Provider-specific config variables (auto-discovered by pos config ai):
# PROVIDER_CONFIG: OPENROUTER_API_KEY=secret:OpenRouter API key from openrouter.ai
# PROVIDER_CONFIG: OPENROUTER_MODEL=:OpenRouter model id (default: openrouter/auto)
provider_name() { printf 'OpenRouter'; }
provider_default_model() { printf 'openrouter/auto'; }
# $1=model $2=messages JSON ({"messages":[{role,content}]}) $3=optional system prompt
provider_generate() {
local model="$1" messages="$2" system="${3:-}" body resp code body_out errmsg
if [ -n "$system" ]; then
body="$(printf '%s' "$messages" | jq -c --arg s "$system" \
'[{role:"system",content:$s}] + .messages')"
else
body="$(printf '%s' "$messages" | jq -c '.messages')"
fi
local mt="${AI_MAX_TOKENS:-2048}"
[[ "$mt" =~ ^[1-9][0-9]*$ ]] || mt=2048
body="$(printf '%s' "$body" | jq -nc --arg m "$model" --argjson msgs "$body" --arg mt "$mt" \
'{model:$m, messages:$msgs, max_tokens:($mt|tonumber)}')"
resp="$(curl -sS -m 60 -X POST "https://openrouter.ai/api/v1/chat/completions" \
-H "Authorization: Bearer ${AI_API_KEY}" \
-H "Content-Type: application/json" \
-H "HTTP-Referer: https://github.com/admin/Linux_post_install" \
--write-out $'\n%{http_code}' \
--data "$body")" || { echo "request failed (curl exit $?)" >&2; return 1; }
code="${resp##*$'\n'}"
body_out="${resp%$'\n'*}"
if [ "$code" != "200" ]; then
errmsg="$(printf '%s' "$body_out" | jq -r '.error.message // empty' 2>/dev/null || true)"
echo "API error $code${errmsg:+: $errmsg}" >&2
return 1
fi
printf '%s' "$body_out" | jq -r '.choices[0].message.content // ""'
}
# $1=current default model → stdout=formatted model list
provider_models_list() {
local model="$1" resp code body m
resp="$(curl -sS -m 30 "https://openrouter.ai/api/v1/models" \
-H "Authorization: Bearer ${AI_API_KEY}" \
--write-out $'\n%{http_code}')" || err "request failed (curl exit $?)"
code="${resp##*$'\n'}"
body="${resp%$'\n'*}"
[ "$code" = "200" ] || err "API error $code: $(printf '%s' "$body" | jq -r '.error.message // empty')"
local list
list="$(printf '%s' "$body" | jq -r '.data[]?.id' | sort)"
echo "OpenRouter models:"
while IFS= read -r m; do
[ -n "$m" ] || continue
if [ "$m" = "$model" ]; then
printf ' %-48s <- default\n' "$m"
else
printf ' %-48s\n' "$m"
fi
done <<< "$list"
if ! grep -qxF "$model" <<< "$list" 2>/dev/null; then
warn "configured default '$model' is not in the list — set AI_MODEL or OPENROUTER_MODEL"
fi
}