Log actual Ollama token counts and add user guide
Add a second usage log line after each proxy response with actual ↑prompt ↓completion token counts from Ollama (prompt_eval_count/eval_count for native endpoints, usage object for OpenAI endpoint). Also adds KURZANLEITUNG.md for students and colleagues covering API access, model selection, Python examples, opencode setup, and quota/admin information.
This commit is contained in:
parent
256bafe30d
commit
cced65693c
168
KURZANLEITUNG.md
Normal file
168
KURZANLEITUNG.md
Normal file
@ -0,0 +1,168 @@
|
||||
# LLM-Dienst – Kurzanleitung
|
||||
|
||||
## Worum geht es?
|
||||
|
||||
Der Dienst stellt **große Sprachmodelle (LLMs)** über eine einfache HTTP-API bereit, die direkt aus Python-Skripten, Jupyter-Notebooks oder eigenen Anwendungen angesprochen werden kann. Die Modelle laufen lokal auf einem GPU-Server im Intranet – ohne Datenübertragung nach außen und ohne Cloud-Kosten.
|
||||
|
||||
Typische Anwendungsfälle:
|
||||
|
||||
- Texte zusammenfassen, übersetzen oder umformulieren
|
||||
- KI-gestütztes Coding (z.B. mit **[opencode](https://opencode.ai)**)
|
||||
- Experimente mit Prompt-Engineering und LLM-Integration in eigene Projekte
|
||||
|
||||
---
|
||||
|
||||
## Zugang
|
||||
|
||||
Der Dienst ist **nur im Intranet** erreichbar.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **API-Endpunkt** | `http://141.75.33.244:8000` |
|
||||
| **Authentifizierung** | API-Key erforderlich (per E-Mail beim Admin anfragen) |
|
||||
|
||||
---
|
||||
|
||||
## Verfügbare Modelle
|
||||
|
||||
| Modell | Größe | Hinweis |
|
||||
|---|---|---|
|
||||
| `gemma4:31b` | 19 GB | kompakt, schnell |
|
||||
| `gpt-oss:20b` | 13 GB | kompakt, schnell |
|
||||
| `gpt-oss:120b` | 65 GB | sehr leistungsfähig |
|
||||
| `qwen3.5:122b` | 81 GB | sehr leistungsfähig |
|
||||
| `qwen3-coder-next:q8_0` | 84 GB | speziell für Code |
|
||||
|
||||
> **Wichtig:** Es kann immer nur **ein Modell gleichzeitig** im GPU-Speicher geladen sein.
|
||||
> Wechselt jemand das Modell, muss das vorherige entladen und das neue geladen werden –
|
||||
> das kann **mehrere Minuten** dauern. Der erste Prompt nach einem Modellwechsel ist
|
||||
> deshalb deutlich langsamer. Danach bleibt das Modell einige Zeit geladen.
|
||||
|
||||
---
|
||||
|
||||
## Python-Beispiel – Einfacher Prompt
|
||||
|
||||
Das API folgt dem **OpenAI-Standard**, d.h. die `openai`-Bibliothek kann direkt verwendet werden.
|
||||
|
||||
```bash
|
||||
pip install openai
|
||||
```
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
|
||||
API_KEY = "sk-..." # euren API-Key eintragen
|
||||
BASE_URL = "http://141.75.33.244:8000/v1"
|
||||
MODEL = "gemma4:31b" # Modell nach Bedarf wählen
|
||||
|
||||
client = OpenAI(api_key=API_KEY, base_url=BASE_URL)
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model=MODEL,
|
||||
messages=[
|
||||
{"role": "user", "content": "Erkläre den Unterschied zwischen L1- und L2-Regularisierung."}
|
||||
]
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Python-Beispiel – Modell wählen und auflisten
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
|
||||
API_KEY = "sk-..."
|
||||
BASE_URL = "http://141.75.33.244:8000/v1"
|
||||
|
||||
client = OpenAI(api_key=API_KEY, base_url=BASE_URL)
|
||||
|
||||
# Verfügbare Modelle abrufen
|
||||
models = client.models.list()
|
||||
for m in models.data:
|
||||
print(m.id)
|
||||
|
||||
# Prompt mit einem bestimmten Modell
|
||||
response = client.chat.completions.create(
|
||||
model="qwen3-coder-next:q8_0",
|
||||
messages=[
|
||||
{"role": "system", "content": "Du bist ein hilfreicher Coding-Assistent."},
|
||||
{"role": "user", "content": "Schreibe eine Python-Funktion zum Berechnen der Fibonacci-Folge."}
|
||||
]
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Empfehlungen zur Nutzung
|
||||
|
||||
- **Kleines Modell zuerst** (`gemma4:31b` oder `gpt-oss:20b`) – viel schneller, für viele Aufgaben ausreichend.
|
||||
- **Großes Modell** nur bei komplexen Aufgaben (`qwen3.5:122b`, `gpt-oss:120b`).
|
||||
- **Code-Aufgaben**: `qwen3-coder-next:q8_0` ist speziell dafür optimiert.
|
||||
- Wenn möglich, **dasselbe Modell wie andere Nutzer** verwenden, um häufige Modellwechsel zu vermeiden.
|
||||
|
||||
---
|
||||
|
||||
## Quotas
|
||||
|
||||
Je nach API-Key können folgende Limits konfiguriert sein:
|
||||
|
||||
- Maximale **Anfragen pro Tag / Monat**
|
||||
- Maximale **Tokens pro Tag / Monat**
|
||||
|
||||
Bei Überschreitung gibt die API den Statuscode `429 Too Many Requests` zurück.
|
||||
|
||||
---
|
||||
|
||||
## Coding-Assistent: opencode
|
||||
|
||||
[opencode](https://opencode.ai) ist ein terminal-basierter KI-Coding-Agent (ähnlich Claude Code), der OpenAI-kompatible APIs unterstützt und damit direkt auf den Intranet-Dienst zeigen kann.
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
npm install -g opencode-ai
|
||||
# oder
|
||||
curl -fsSL https://opencode.ai/install | bash
|
||||
```
|
||||
|
||||
### Konfiguration
|
||||
|
||||
Konfigurationsdatei anlegen unter `~/.config/opencode/config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"providers": {
|
||||
"openai": {
|
||||
"apiKey": "sk-...",
|
||||
"baseURL": "http://141.75.33.244:8000/v1"
|
||||
}
|
||||
},
|
||||
"model": "openai/qwen3-coder-next:q8_0"
|
||||
}
|
||||
```
|
||||
|
||||
Für Code-Aufgaben empfiehlt sich `qwen3-coder-next:q8_0`, für allgemeine Aufgaben `gemma4:31b` oder `gpt-oss:20b`.
|
||||
|
||||
### Starten
|
||||
|
||||
```bash
|
||||
opencode
|
||||
```
|
||||
|
||||
opencode öffnet eine interaktive TUI im Terminal und kann dann im Projektverzeichnis eingesetzt werden – Dateien lesen, Code generieren, Refactoring vorschlagen usw.
|
||||
|
||||
---
|
||||
|
||||
## Administration (nur für Admins)
|
||||
|
||||
Das Web-Interface zur Verwaltung von API-Keys und Quotas ist erreichbar unter:
|
||||
|
||||
**`http://141.75.33.244:8001`**
|
||||
|
||||
Dort können API-Keys angelegt, deaktiviert und mit Quotas versehen werden.
|
||||
@ -101,7 +101,11 @@ async def generate(request: Request, db: Session = Depends(get_db)):
|
||||
request.state.api_key_name, body.get("model", "?"), prompt_tokens, prompt_preview)
|
||||
try:
|
||||
response = await proxy_request(f"{ollama_url}/api/generate", method="POST", json_data=body)
|
||||
return JSONResponse(content=response.json(), status_code=response.status_code)
|
||||
resp_json = response.json()
|
||||
usage_log.info('%s | /api/generate | %s | actual ↑%d ↓%d tokens',
|
||||
request.state.api_key_name, body.get("model", "?"),
|
||||
resp_json.get("prompt_eval_count", 0), resp_json.get("eval_count", 0))
|
||||
return JSONResponse(content=resp_json, status_code=response.status_code)
|
||||
except Exception as exc:
|
||||
error_log.error("Proxy error | %s | /api/generate | %s | %s: %s",
|
||||
request.state.api_key_name, body.get("model", "?"), type(exc).__name__, exc, exc_info=exc)
|
||||
@ -121,7 +125,11 @@ async def chat(request: Request, db: Session = Depends(get_db)):
|
||||
request.state.api_key_name, body.get("model", "?"), prompt_tokens, _last_user_msg(messages))
|
||||
try:
|
||||
response = await proxy_request(f"{ollama_url}/api/chat", method="POST", json_data=body)
|
||||
return JSONResponse(content=response.json(), status_code=response.status_code)
|
||||
resp_json = response.json()
|
||||
usage_log.info('%s | /api/chat | %s | actual ↑%d ↓%d tokens',
|
||||
request.state.api_key_name, body.get("model", "?"),
|
||||
resp_json.get("prompt_eval_count", 0), resp_json.get("eval_count", 0))
|
||||
return JSONResponse(content=resp_json, status_code=response.status_code)
|
||||
except Exception as exc:
|
||||
error_log.error("Proxy error | %s | /api/chat | %s | %s: %s",
|
||||
request.state.api_key_name, body.get("model", "?"), type(exc).__name__, exc, exc_info=exc)
|
||||
@ -185,7 +193,12 @@ async def openai_chat_completions(request: Request, db: Session = Depends(get_db
|
||||
|
||||
try:
|
||||
response = await proxy_request(target, method="POST", json_data=body)
|
||||
return JSONResponse(content=response.json(), status_code=response.status_code)
|
||||
resp_json = response.json()
|
||||
usage = resp_json.get("usage", {})
|
||||
usage_log.info('%s | /v1/chat/completions | %s | actual ↑%d ↓%d tokens',
|
||||
request.state.api_key_name, model_name,
|
||||
usage.get("prompt_tokens", 0), usage.get("completion_tokens", 0))
|
||||
return JSONResponse(content=resp_json, status_code=response.status_code)
|
||||
except Exception as exc:
|
||||
error_log.error("Proxy error | %s | /v1/chat/completions | %s | %s: %s",
|
||||
request.state.api_key_name, model_name, type(exc).__name__, exc, exc_info=exc)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user