Skip to content

Commit c19afab

Browse files
author
Sebastian BURGIN-FIX (ext)
committed
wip
1 parent 3463877 commit c19afab

28 files changed

Lines changed: 1747 additions & 93 deletions

database/files/news/de_CH/2025-04-06-docuware-7-12-is-here.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ teaser: >-
66
Mehr Automatisierung und Einblick: Das Release verbessert die E-Rechnungsverarbeitung, bringt IDP in die Cloud und öffnet Workflow-Daten für Analytics.
77
published_at: 2025-04-06
88
author: sebastian.buergin@codebar.ch
9-
hero: images/news/placeholders/docuware-7-12.svg
9+
hero: images/news/placeholders/docuware-7-12-de.svg
1010
hero_alt: Platzhaltergrafik zum DocuWare-Release 7.12
1111
tags: [DMS/ECM]
1212
---

database/files/news/de_CH/2025-09-15-docuware-7-13-is-here.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ teaser: >-
66
Workflows entstehen neu im Browser, der Rechnungseingang kommt mit ausländischen E-Rechnungsformaten zurecht, und die Anmeldung schützt ein zweiter Faktor.
77
published_at: 2025-09-15
88
author: sebastian.buergin@codebar.ch
9-
hero: images/news/placeholders/docuware-7-13.svg
9+
hero: images/news/placeholders/docuware-7-13-de.svg
1010
hero_alt: Platzhaltergrafik zum DocuWare-Release 7.13
1111
tags: [DMS/ECM]
1212
---

database/files/news/de_CH/2026-05-12-docuware-7-14-is-here.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ teaser: >-
66
Die letzten Einstellungen wandern in den Browser, eine neu gebaute App bringt Aufgaben aufs Telefon, und Archive ziehen zwischen Cloud-Organisationen um.
77
published_at: 2026-05-12
88
author: sebastian.buergin@codebar.ch
9-
hero: images/news/placeholders/docuware-7-14.svg
9+
hero: images/news/placeholders/docuware-7-14-de.svg
1010
hero_alt: Platzhaltergrafik zum DocuWare-Release 7.14
1111
tags: [DMS/ECM]
1212
---
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
---
2+
key: llm-gateway-open-source
3+
slug: llm-gateway-open-source
4+
title: Warteschlange statt Warteschleife — unser LLM Gateway ist Open Source
5+
teaser: >-
6+
Lokale Modelle lösen die Datenschutzfrage, aber nicht die Kapazitätsfrage. Wir haben ein
7+
Gateway gebaut, das Anfragen an lokale Modelle annimmt, speichert und der Reihe nach
8+
abarbeitet — und stellen es unter MIT-Lizenz zur Verfügung.
9+
published_at: 2026-07-29
10+
published: false
11+
author: sebastian.buergin@codebar.ch
12+
hero: images/templates/cover-template.jpg
13+
hero_alt: Platzhaltergrafik zum LLM Gateway
14+
tags: [Open Source, KI]
15+
featured: false
16+
---
17+
18+
Wir arbeiten täglich mit lokalen Sprachmodellen. Der Grund ist unspektakulär: Sobald ein Prompt
19+
Kundendaten, Verträge oder interne Dokumente enthält, ist die Frage nicht mehr, welches Modell am
20+
besten antwortet, sondern wer die Anfrage zu sehen bekommt. Ein Modell, das auf der eigenen
21+
Hardware läuft, beantwortet diese Frage von selbst — nichts verlässt das Haus, es gibt keine
22+
Verarbeitung im Ausland, keine Vertragsanhänge zur Auftragsverarbeitung, keine Diskussion darüber,
23+
ob Eingaben irgendwann in ein Training fliessen.
24+
25+
Was lokale Modelle nicht mitliefern, ist Kapazität.
26+
27+
## Das eigentliche Problem ist Gleichzeitigkeit
28+
29+
Ein lokales Modell ist günstig im Betrieb und teuer in der *Parallelität*. Eine GPU — oder der
30+
gemeinsame Speicher eines Macs — trägt realistisch ein bis zwei gleichzeitige Anfragen, bevor das
31+
Umschalten zwischen Modellen mehr kostet, als es bringt. Ollamas `OLLAMA_NUM_PARALLEL` existiert
32+
genau deshalb: Die Grenze ist hart, nicht weich.
33+
34+
Darüber hinaus stauen sich Anfragen. Ob man eine Warteschlange gebaut hat oder nicht, ändert daran
35+
nichts — es ändert nur, *wo* sie entsteht. Entweder an einer Stelle, die man sieht, oder in Ollamas
36+
eigenem Scheduler, wo eine Anfrage, die schlicht wartet, exakt gleich aussieht wie eine, die
37+
hängt.
38+
39+
Für einen einzelnen Entwickler am Notebook ist das kein Thema. Für interne Werkzeuge, die Rechnungen
40+
auslesen, Protokolle zusammenfassen oder Dokumente klassifizieren, wird daraus schnell ein
41+
Betriebsproblem: Ein Batchlauf blockiert alle anderen, ein Timeout im aufrufenden System verwirft
42+
eine Antwort, für die das Modell noch zwei Minuten rechnet, und niemand kann sagen, wie lange etwas
43+
dauern wird.
44+
45+
## Was wir stattdessen gebaut haben
46+
47+
Das LLM Gateway ist eine Warteschlange vor den lokalen Modellen — und sonst bewusst nichts.
48+
49+
Der Ablauf ist einfach: Sie senden Ihre Anfrage genau so, wie Sie sie an das Modell senden würden.
50+
Das Gateway nimmt sie an, schreibt sie in die Datenbank und antwortet sofort mit einer ID. Sobald
51+
Kapazität frei ist, nimmt ein Worker den Eintrag, spielt ihn unverändert ans Modell weiter und legt
52+
die Antwort zurück in denselben Eintrag. Abgeholt wird sie später über die zweite Anfrage.
53+
54+
```bash
55+
# absenden — antwortet in Millisekunden mit einer ID
56+
ID=$(curl -s https://gateway.example/v1/responses \
57+
-H "Authorization: Bearer $MY_API_KEY" \
58+
-H "Content-Type: application/json" \
59+
-d '{"model":"qwen3-vl:30b-a3b","input":"Rechnungsnummer und Total als JSON."}' | jq -r .id)
60+
61+
# abholen — sobald der Status auf «completed» steht
62+
curl -s https://gateway.example/v1/responses/$ID \
63+
-H "Authorization: Bearer $MY_API_KEY" | jq -r '.output[0].content[0].text'
64+
```
65+
66+
Die API ist die von OpenAI definierte: `POST /v1/chat/completions` und `POST /v1/responses`
67+
funktionieren so, wie es jedes SDK erwartet. Der Unterschied liegt in der Antwort — statt auf das
68+
Modell zu warten, kommt ein `202` mit einer ID zurück. Auch eine als synchron gedachte
69+
Chat-Completion wird dabei in einen asynchronen Auftrag umgewandelt. Das ist kein Nebeneffekt,
70+
sondern der Zweck: Eine Warteschlange, die man umgehen kann, ist keine.
71+
72+
## Alles geht 1:1 durch
73+
74+
Der Punkt, auf den wir beim Bauen am meisten geachtet haben: Das Gateway interpretiert die Anfrage
75+
nicht.
76+
77+
- **Der Body** geht unverändert nach oben. Unbekannte Felder werden nicht entfernt, ein fehlendes
78+
`model` ist kein Fehler, es gibt keine eigene Validierung, die irgendwann hinter der API des
79+
Modells zurückbleibt.
80+
- **Die Header** werden aus einer festen Positivliste gespeichert und exakt so wieder gesendet, wie
81+
sie ankamen — inklusive `Authorization` und der `X-Litellm-`-Tags, mit denen wir Kosten pro
82+
Anwendungsfall auswerten. Alles andere wird verworfen statt weitergereicht; ein `Cookie` aus einem
83+
Browser hat beim Modell nichts zu suchen.
84+
85+
Daraus folgt der zweite Punkt: **Das Gateway besitzt keine Zugangsdaten.** Es gibt keinen
86+
Master-Key und keine Benutzertabelle. Was den Aufruf beim Modell legitimiert, ist der Schlüssel des
87+
Aufrufenden — so wie vorher auch, als noch direkt gerufen wurde.
88+
89+
Weil der Aufruf nach oben erst stattfindet, wenn die ursprüngliche HTTP-Verbindung längst geschlossen
90+
ist, müssen die Header zwischengelagert werden. Sie liegen verschlüsselt beim Eintrag, werden von der
91+
API nie zurückgegeben, im Dashboard nur mit *Namen* angezeigt und gelöscht, sobald der Eintrag einen
92+
Endzustand erreicht. Zurück bleibt ein SHA-256-Hash des Tokens — genug, um einen Eintrag seinem
93+
Urheber zuzuordnen, zu wenig, um damit etwas anzustellen.
94+
95+
:::callout{type="info" title="Wem ein Eintrag gehört"}
96+
Lesen, abbrechen und löschen darf nur, wer denselben Schlüssel schickt, mit dem der Eintrag erstellt
97+
wurde. Passt er nicht, antwortet die API mit `404` statt mit `403` — sie bestätigt eine ID nicht, die
98+
sie ohnehin nicht ausliefern würde.
99+
:::
100+
101+
## Mehrere Modelle, mehrere Gateways
102+
103+
Wer lokal arbeitet, hat selten genau eine Maschine. Bei uns sind es ein Mac mit Ollama, eine
104+
LiteLLM-Instanz vor mehreren Modellen und, für Aufgaben ohne schützenswerten Inhalt, ein
105+
kommerzieller Anbieter.
106+
107+
Jedes dieser Ziele wird im Dashboard als «Gateway» erfasst und bekommt eine ID, die vor dem `/v1`
108+
in der URL steht:
109+
110+
```bash
111+
curl https://gateway.example/v1/chat/completions # das Standard-Gateway
112+
curl https://gateway.example/9f3c8b52-…/v1/chat/completions # ein bestimmtes
113+
```
114+
115+
Ein SDK, das mit `base_url = https://gateway.example/{id}/v1` konfiguriert ist, spricht damit ohne
116+
weitere Anpassung genau ein Ziel an — absenden und abholen. Drei Protokolle werden unterstützt:
117+
OpenAI-kompatibel (also LiteLLM, vLLM, Ollamas eigener Shim und alles andere in dieser Form),
118+
Anthropic und Ollama nativ. Bei den letzten beiden übersetzt das Gateway hin und zurück; bei
119+
OpenAI-kompatiblen Zielen bleibt es reiner Durchgang.
120+
121+
| Protokoll | Endpunkt beim Ziel | Body |
122+
| --- | --- | --- |
123+
| OpenAI-kompatibel | `{base}/v1/chat/completions` | unverändert durchgereicht |
124+
| Anthropic | `{base}/v1/messages` | übersetzt hin und zurück |
125+
| Ollama | `{base}/api/chat` | übersetzt hin und zurück |
126+
127+
Ein Gateway zu deaktivieren stoppt nur *neue* Anfragen. Bereits eingereihte Einträge sind fest mit
128+
dem Ziel verbunden, das sie bei der Annahme zugewiesen bekommen haben — ein Wechsel des Standards
129+
lenkt also nie eine Antwort um, auf die jemand gerade wartet.
130+
131+
## Warten mit Anzeige
132+
133+
Eine Warteschlange, die nicht sagt, wie lange es dauert, ist auch nur eine Blackbox. Jede Antwort zu
134+
einem wartenden Eintrag enthält deshalb einen eigenen Block:
135+
136+
```json
137+
"queue": {
138+
"position": 3,
139+
"depth": 7,
140+
"workers": 2,
141+
"estimated_seconds": 84
142+
}
143+
```
144+
145+
`position` zählt den Eintrag selbst mit, `depth` ist der gesamte Rückstand, und `estimated_seconds`
146+
rechnet bis zur *eigenen* Antwort, nicht bis zum Beginn der Bearbeitung — das ist die Zahl, die man
147+
einem wartenden Menschen zeigen kann.
148+
149+
Sie ist eine Hochrechnung, keine Zusage: der Median der letzten tatsächlichen Antwortzeiten für
150+
dasselbe Modell, multipliziert mit der Anzahl Runden, die vor einem liegen. Was sie nicht sehen
151+
kann, ist ein Modellwechsel — eine Anfrage, die Ollama zwingt, ein anderes Modell zu laden, dauert
152+
deutlich länger, und kein Median über vergangene Antworten weiss davon im Voraus.
153+
154+
## Was das Gateway bewusst nicht tut
155+
156+
Wir halten die Grenzen für den ehrlicheren Teil einer Ankündigung:
157+
158+
- **Kein Streaming.** Ein Rückstand und ein Token-Strom widersprechen sich. `stream: true` wird
159+
zwar durchgereicht, die Antwort landet dann aber als Rohstrom im Eintrag. Wer Streaming braucht,
160+
ruft das Modell direkt.
161+
- **Abbrechen nur, solange etwas wartet.** Eine laufende Ollama-Anfrage lässt sich nicht sauber
162+
stoppen — ein Abbruch im Zustand `in_progress` wäre ein Versprechen, das die Anwendung nicht
163+
halten kann.
164+
- **Keine Übersetzung von Tool-Calls.** Die Abbildung zwischen OpenAI-`tools` und Anthropic-`tool_use`
165+
ist ein Thema für sich und ist nicht umgesetzt.
166+
- **Bilder fallen auf den nativen Routen weg.** Auf den Wegen zu Anthropic und Ollama werden
167+
multimodale Inhalte auf ihren Text reduziert, weil beide Bilder unterschiedlich genug
168+
transportieren, dass Raten schlechter wäre als eine sichtbare Lücke.
169+
170+
:::callout{type="warning" title="Ein Worker pro paralleler Anfrage"}
171+
Mehr Worker zu starten, als das Modell gleichzeitig beantworten kann, verschiebt den Rückstand
172+
lediglich aus der eigenen Datenbank in Ollamas undurchsichtige Warteschlange — also genau dorthin,
173+
wo man ihn nicht mehr sieht. Bei Ollama entspricht die richtige Zahl `OLLAMA_NUM_PARALLEL`, in der
174+
Regel zwei.
175+
:::
176+
177+
## Verfügbar unter MIT-Lizenz
178+
179+
Das Gateway ist eine Laravel-Anwendung mit PostgreSQL, getestet mit Pest und statisch geprüft auf
180+
PHPStan-Level 10. Es läuft bei uns auf einem Mac unter Herd, hinter einem Cloudflare Tunnel; die
181+
Konfiguration dafür — launchd-Agents und Ingress-Regeln mit Default-Deny — liegt im Repository,
182+
statt in der Shell-History von jemandem zu verschwinden.
183+
184+
Wir veröffentlichen es so, wie wir es einsetzen. Es löst kein grosses Problem, sondern ein
185+
konkretes: Es macht lokale Modelle für interne Werkzeuge planbar, ohne dass jedes dieser Werkzeuge
186+
seine eigene Warteschlange erfinden muss.
187+
188+
- [codebar-ag/llm-gateway.codebar.ai auf GitHub](https://github.com/codebar-ag/llm-gateway.codebar.ai)
189+
190+
Fehlerberichte und Pull Requests sind willkommen. Sicherheitslücken bitte nicht über ein
191+
öffentliches Issue, sondern auf dem in `SECURITY.md` beschriebenen Weg.

database/files/news/en_CH/2025-04-06-docuware-7-12-is-here.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ teaser: >-
66
More automation and insight: this release improves e-invoice processing, brings IDP into the cloud configuration and opens workflow data up to analytics.
77
published_at: 2025-04-06
88
author: sebastian.buergin@codebar.ch
9-
hero: images/news/placeholders/docuware-7-12.svg
9+
hero: images/news/placeholders/docuware-7-12-en.svg
1010
hero_alt: Placeholder graphic for the DocuWare 7.12 release
1111
tags: [DMS/ECM]
1212
---

database/files/news/en_CH/2025-09-15-docuware-7-13-is-here.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ teaser: >-
66
Workflows are now built in the browser, invoice intake copes with foreign e-invoice formats, and logging in can be secured with a second factor.
77
published_at: 2025-09-15
88
author: sebastian.buergin@codebar.ch
9-
hero: images/news/placeholders/docuware-7-13.svg
9+
hero: images/news/placeholders/docuware-7-13-en.svg
1010
hero_alt: Placeholder graphic for the DocuWare 7.13 release
1111
tags: [DMS/ECM]
1212
---

database/files/news/en_CH/2026-05-12-docuware-7-14-is-here.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ teaser: >-
66
The last settings move into the browser, a rebuilt app puts tasks on your phone, and file cabinets can be moved between two cloud organisations.
77
published_at: 2026-05-12
88
author: sebastian.buergin@codebar.ch
9-
hero: images/news/placeholders/docuware-7-14.svg
9+
hero: images/news/placeholders/docuware-7-14-en.svg
1010
hero_alt: Placeholder graphic for the DocuWare 7.14 release
1111
tags: [DMS/ECM]
1212
---

0 commit comments

Comments
 (0)