fix(svc-ai-litellm): bound what a local backend may generate

The gateway registered ollama and lmstudio with an rpm cap and nothing
else, so a single request could generate without an output limit and hold
a worker for as long as the backend kept producing tokens. Requests per
minute bound how often a caller may ask, not how much one answer costs.

max_output_tokens caps the answer itself. upstream_timeout is the backstop
for a wedged backend, not a latency budget, so it sits above every
consumer's own budget: the longest wait on the AI path is 360s
(discourse-ai.spec.js), with 300s and 240s behind it, and TIMEOUT_FACTOR
defaults to 1. At 420s the gateway can only fire once every caller has
already given up, which keeps a slow generation from being reported as a
gateway error instead of the slow answer it is.

openrouter/auto stays unbounded here. It never carried an rpm cap either,
and its limits are the provider's rather than ours, so giving it one is a
separate decision from closing the local-backend hole.
This commit is contained in:
kevinveenbirkenbach committed 2026-09-08 16:19:12 +02:00
1 parent 0f0d804a67
commit 13f5ca6d95
3 files changed
+8

No files matched your search

+2
View File
@@ -13,6 +13,8 @@ litellm:
image: ghcr.io/berriai/litellm
version: main-v1.77.3.dynamic_rates
name: litellm
max_output_tokens: 512
upstream_timeout: 420
min_storage: 1022MB
cpus: "2.0"
mem_reservation: 1g
@@ -7,6 +7,8 @@ model_list:{{ ' []' if not has_backend else '' }}
model: "ollama/{{ _model }}"
api_base: {{ OLLAMA_BASE_LOCAL_URL }}
rpm: 1000
max_tokens: {{ LITELLM_MAX_OUTPUT_TOKENS }}
timeout: {{ LITELLM_UPSTREAM_TIMEOUT }}
{% endfor %}
{% if 'svc-ai-lmstudio' in group_names %}
- model_name: lmstudio/default
@@ -15,6 +17,8 @@ model_list:{{ ' []' if not has_backend else '' }}
api_base: {{ LMSTUDIO_BASE_LOCAL_URL }}/v1
api_key: lm-studio
rpm: 1000
max_tokens: {{ LITELLM_MAX_OUTPUT_TOKENS }}
timeout: {{ LITELLM_UPSTREAM_TIMEOUT }}
{% endif %}
{% if LITELLM_OPENROUTER_API_KEY %}
- model_name: openrouter/auto
+2
View File
@@ -5,6 +5,8 @@ entity_name: "{{ application_id | get_entity_name }}"
LITELLM_CONTAINER: "{{ lookup('config', application_id, 'services.litellm.name') }}"
LITELLM_PORT: "{{ lookup('config', application_id, 'services.litellm.ports.internal.http') }}"
LITELLM_MASTER_KEY: "{{ lookup('config', application_id, 'secrets.credentials.master_key') }}"
LITELLM_MAX_OUTPUT_TOKENS: "{{ lookup('config', application_id, 'services.litellm.max_output_tokens') }}"
LITELLM_UPSTREAM_TIMEOUT: "{{ lookup('config', application_id, 'services.litellm.upstream_timeout') }}"
LITELLM_CONFIG_PATH_HOST: "{{ [ lookup('container', application_id, 'directories.config'), 'config.yaml' ] | path_join }}"
LITELLM_CONFIG_PATH_DOCKER: "{{ lookup('volume', application_id, 'litellm_config').target }}"
LITELLM_OPENROUTER_API_KEY: "{{ OPENROUTER_API_KEY }}"