llmstack-cli 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- llmstack_cli-0.3.0/CHANGELOG.md +63 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/PKG-INFO +162 -48
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/README.md +158 -47
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/pyproject.toml +3 -3
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/__init__.py +1 -1
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/core/health.py +1 -1
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/gateway/Dockerfile +3 -1
- llmstack_cli-0.3.0/src/llmstack/gateway/cache.py +187 -0
- llmstack_cli-0.3.0/src/llmstack/gateway/circuit_breaker.py +177 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/gateway/main.py +28 -5
- llmstack_cli-0.3.0/src/llmstack/gateway/middleware/logging.py +114 -0
- llmstack_cli-0.3.0/src/llmstack/gateway/middleware/rate_limit.py +204 -0
- llmstack_cli-0.3.0/src/llmstack/gateway/proxy.py +110 -0
- llmstack_cli-0.3.0/src/llmstack/gateway/rag/pipeline.py +188 -0
- llmstack_cli-0.3.0/src/llmstack/gateway/rag/store.py +242 -0
- llmstack_cli-0.3.0/src/llmstack/gateway/routes/chat.py +51 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/gateway/routes/health.py +31 -5
- llmstack_cli-0.3.0/src/llmstack/gateway/routes/rag.py +137 -0
- llmstack_cli-0.3.0/tests/unit/__init__.py +0 -0
- llmstack_cli-0.3.0/tests/unit/test_cache.py +83 -0
- llmstack_cli-0.3.0/tests/unit/test_circuit_breaker.py +136 -0
- llmstack_cli-0.3.0/tests/unit/test_rag_pipeline.py +50 -0
- llmstack_cli-0.3.0/tests/unit/test_rag_store.py +79 -0
- llmstack_cli-0.3.0/tests/unit/test_rate_limit.py +72 -0
- llmstack_cli-0.2.0/CHANGELOG.md +0 -29
- llmstack_cli-0.2.0/src/llmstack/gateway/proxy.py +0 -58
- llmstack_cli-0.2.0/src/llmstack/gateway/routes/chat.py +0 -27
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/.github/SECURITY.md +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/.github/workflows/ci.yml +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/.github/workflows/release.yml +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/.gitignore +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/CONTRIBUTING.md +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/LICENSE +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/Makefile +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/assets/social-preview.png +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/demo.gif +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/demo.tape +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/examples/output.txt +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/__main__.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/cli/__init__.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/cli/app.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/cli/commands/__init__.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/cli/commands/chat.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/cli/commands/doctor.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/cli/commands/down.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/cli/commands/export.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/cli/commands/init.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/cli/commands/logs.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/cli/commands/status.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/cli/commands/up.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/cli/console.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/config/__init__.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/config/loader.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/config/presets/__init__.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/config/presets/agent.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/config/presets/chat.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/config/presets/rag.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/config/schema.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/core/__init__.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/core/hardware.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/core/resolver.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/core/stack.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/docker/__init__.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/docker/manager.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/gateway/__init__.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/gateway/middleware/__init__.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/gateway/middleware/auth.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/gateway/middleware/metrics.py +0 -0
- {llmstack_cli-0.2.0/src/llmstack/gateway/routes → llmstack_cli-0.3.0/src/llmstack/gateway/rag}/__init__.py +0 -0
- {llmstack_cli-0.2.0/src/llmstack/plugins → llmstack_cli-0.3.0/src/llmstack/gateway/routes}/__init__.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/gateway/routes/embeddings.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/gateway/routes/models.py +0 -0
- {llmstack_cli-0.2.0/src/llmstack/services → llmstack_cli-0.3.0/src/llmstack/plugins}/__init__.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/plugins/loader.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/plugins/spec.py +0 -0
- {llmstack_cli-0.2.0/src/llmstack/services/cache → llmstack_cli-0.3.0/src/llmstack/services}/__init__.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/services/base.py +0 -0
- {llmstack_cli-0.2.0/src/llmstack/services/embeddings → llmstack_cli-0.3.0/src/llmstack/services/cache}/__init__.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/services/cache/redis.py +0 -0
- {llmstack_cli-0.2.0/src/llmstack/services/gateway → llmstack_cli-0.3.0/src/llmstack/services/embeddings}/__init__.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/services/embeddings/tei.py +0 -0
- {llmstack_cli-0.2.0/src/llmstack/services/inference → llmstack_cli-0.3.0/src/llmstack/services/gateway}/__init__.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/services/gateway/service.py +0 -0
- {llmstack_cli-0.2.0/src/llmstack/services/observe → llmstack_cli-0.3.0/src/llmstack/services/inference}/__init__.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/services/inference/ollama.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/services/inference/vllm.py +0 -0
- {llmstack_cli-0.2.0/src/llmstack/services/vectordb → llmstack_cli-0.3.0/src/llmstack/services/observe}/__init__.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/services/observe/prometheus.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/services/registry.py +0 -0
- {llmstack_cli-0.2.0/tests → llmstack_cli-0.3.0/src/llmstack/services/vectordb}/__init__.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/src/llmstack/services/vectordb/qdrant.py +0 -0
- {llmstack_cli-0.2.0/tests/unit → llmstack_cli-0.3.0/tests}/__init__.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/tests/unit/test_config.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/tests/unit/test_export.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/tests/unit/test_gateway.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/tests/unit/test_hardware.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/tests/unit/test_resolver.py +0 -0
- {llmstack_cli-0.2.0 → llmstack_cli-0.3.0}/tests/unit/test_services.py +0 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.3.0] - 2026-05-07
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **RAG Pipeline** — real retrieval-augmented generation with Qdrant
|
|
7
|
+
- `POST /v1/rag/ingest` — chunk, embed, and store documents in Qdrant
|
|
8
|
+
- `POST /v1/rag/query` — semantic search + LLM generation with source citations
|
|
9
|
+
- `DELETE /v1/rag/documents/{source}` — delete documents by source
|
|
10
|
+
- `GET /v1/rag/status` — collection statistics
|
|
11
|
+
- Streaming support for RAG queries via SSE
|
|
12
|
+
- **Semantic Response Cache** — Redis-backed LLM response caching
|
|
13
|
+
- SHA-256 hash of (model + messages) for deterministic cache keys
|
|
14
|
+
- Only caches low-temperature requests (≤0.1) for correctness
|
|
15
|
+
- TTL-based expiration with configurable duration
|
|
16
|
+
- Cache hit/miss metrics exposed in `/healthz`
|
|
17
|
+
- `X-Cache: HIT/MISS` response headers
|
|
18
|
+
- **Token Bucket Rate Limiter** — Redis-backed with Lua script for atomicity
|
|
19
|
+
- Configurable via `rate_limit` in llmstack.yaml (e.g., `100/min`, `10/sec`)
|
|
20
|
+
- Per-API-key rate limiting with IP fallback
|
|
21
|
+
- In-memory fallback when Redis is unavailable
|
|
22
|
+
- Standard `X-RateLimit-*` and `Retry-After` headers
|
|
23
|
+
- Atomic Lua script prevents race conditions in distributed setup
|
|
24
|
+
- **Circuit Breaker** — resilience pattern for inference backend
|
|
25
|
+
- Three-state machine: CLOSED → OPEN → HALF_OPEN → CLOSED
|
|
26
|
+
- Exponential backoff on recovery timeout (capped)
|
|
27
|
+
- Fail-fast with `503 Service Unavailable` when circuit is open
|
|
28
|
+
- Metrics exposed in `/healthz` (state, failure count, rejections)
|
|
29
|
+
- **Structured Logging** — JSON request logs with correlation IDs
|
|
30
|
+
- `X-Request-ID` header propagation
|
|
31
|
+
- Per-request structured JSON with method, path, status, duration, client IP
|
|
32
|
+
- Configurable log level and format (JSON / text)
|
|
33
|
+
- Token usage extraction from inference responses into Prometheus metrics
|
|
34
|
+
- Redis health check in `/healthz` endpoint
|
|
35
|
+
- 50 new unit tests (95 total) covering cache, circuit breaker, rate limiter, RAG
|
|
36
|
+
|
|
37
|
+
## [0.2.0] - 2026-05-07
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
- `llmstack chat` — interactive terminal chat with streaming responses
|
|
41
|
+
- `llmstack export` — generate standalone docker-compose.yml from llmstack.yaml
|
|
42
|
+
- GitHub issue templates, PR template, security policy
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
- Gateway Docker image now builds locally (no longer requires ghcr.io)
|
|
46
|
+
- Prometheus and Grafana configs are written to disk before container start
|
|
47
|
+
- Generated API keys persist to llmstack.yaml across restarts
|
|
48
|
+
- Clear error messages for port conflicts
|
|
49
|
+
|
|
50
|
+
## [0.1.0] - 2026-05-07
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
- CLI with `init`, `up`, `down`, `status`, `logs`, `doctor` commands
|
|
54
|
+
- Auto hardware detection (NVIDIA, Apple Silicon, CPU)
|
|
55
|
+
- Smart backend resolver (auto-picks Ollama or vLLM)
|
|
56
|
+
- Services: Ollama, vLLM, Qdrant, Redis, TEI (Text Embeddings Inference)
|
|
57
|
+
- API Gateway: OpenAI-compatible proxy with auth, rate limiting, SSE streaming
|
|
58
|
+
- Prometheus + Grafana observability with pre-provisioned dashboard
|
|
59
|
+
- Plugin system via Python entry_points
|
|
60
|
+
- Presets: `chat`, `rag`, `agent`
|
|
61
|
+
- Pydantic v2 config schema (`llmstack.yaml`)
|
|
62
|
+
- Docker SDK orchestration (no docker-compose dependency)
|
|
63
|
+
- CI/CD: GitHub Actions for lint/test and PyPI release
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: llmstack-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: One command. Full LLM stack. Zero config.
|
|
5
5
|
Author: mara-werils
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -22,13 +22,16 @@ Requires-Dist: pyyaml>=6.0
|
|
|
22
22
|
Requires-Dist: rich>=13.0
|
|
23
23
|
Requires-Dist: typer>=0.12
|
|
24
24
|
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: fakeredis>=2.21; extra == 'dev'
|
|
25
26
|
Requires-Dist: fastapi>=0.115; extra == 'dev'
|
|
26
27
|
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
27
28
|
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: redis>=5.0; extra == 'dev'
|
|
28
30
|
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
29
31
|
Requires-Dist: starlette>=0.40; extra == 'dev'
|
|
30
32
|
Provides-Extra: gateway
|
|
31
33
|
Requires-Dist: fastapi>=0.115; extra == 'gateway'
|
|
34
|
+
Requires-Dist: redis>=5.0; extra == 'gateway'
|
|
32
35
|
Requires-Dist: starlette>=0.40; extra == 'gateway'
|
|
33
36
|
Requires-Dist: uvicorn[standard]>=0.30; extra == 'gateway'
|
|
34
37
|
Description-Content-Type: text/markdown
|
|
@@ -64,23 +67,35 @@ llmstack up
|
|
|
64
67
|
That's it. You now have **7 services** running: inference, embeddings, vector DB, cache, API gateway, Prometheus, and Grafana.
|
|
65
68
|
|
|
66
69
|
```bash
|
|
67
|
-
#
|
|
70
|
+
# Chat completion (OpenAI-compatible)
|
|
68
71
|
curl http://localhost:8000/v1/chat/completions \
|
|
69
72
|
-H "Authorization: Bearer YOUR_KEY" \
|
|
70
73
|
-H "Content-Type: application/json" \
|
|
71
74
|
-d '{"model":"llama3.2","messages":[{"role":"user","content":"Hello!"}]}'
|
|
75
|
+
|
|
76
|
+
# Ingest a document for RAG
|
|
77
|
+
curl http://localhost:8000/v1/rag/ingest \
|
|
78
|
+
-H "Authorization: Bearer YOUR_KEY" \
|
|
79
|
+
-H "Content-Type: application/json" \
|
|
80
|
+
-d '{"text":"LLMStack is an open-source tool for...","source":"docs.txt"}'
|
|
81
|
+
|
|
82
|
+
# Query with RAG
|
|
83
|
+
curl http://localhost:8000/v1/rag/query \
|
|
84
|
+
-H "Authorization: Bearer YOUR_KEY" \
|
|
85
|
+
-H "Content-Type: application/json" \
|
|
86
|
+
-d '{"question":"What is LLMStack?"}'
|
|
72
87
|
```
|
|
73
88
|
|
|
74
89
|
Works with **any OpenAI-compatible client**: LangChain, LlamaIndex, Vercel AI SDK, openai-python.
|
|
75
90
|
|
|
76
91
|
## Who is this for?
|
|
77
92
|
|
|
78
|
-
- **AI app developers** who want local inference without Docker boilerplate
|
|
93
|
+
- **AI app developers** who want local inference + RAG without Docker boilerplate
|
|
79
94
|
- **Teams** who need an OpenAI-compatible API backed by local models
|
|
80
95
|
- **Hobbyists** running LLMs locally who want vector search, caching, and monitoring out of the box
|
|
81
96
|
- **Anyone** tired of writing 200+ lines of docker-compose.yml every time
|
|
82
97
|
|
|
83
|
-
##
|
|
98
|
+
## Architecture
|
|
84
99
|
|
|
85
100
|
```
|
|
86
101
|
llmstack up
|
|
@@ -92,28 +107,85 @@ Works with **any OpenAI-compatible client**: LangChain, LlamaIndex, Vercel AI SD
|
|
|
92
107
|
|
|
|
93
108
|
+-------+-------+-------+-------+
|
|
94
109
|
| | | | |
|
|
95
|
-
+----v--+ +--v---+ +v-----+ +v----+ +v
|
|
96
|
-
|Qdrant | |Redis | |Ollama| | TEI | |
|
|
97
|
-
|Vector | |Cache | | or | |Embed| |
|
|
98
|
-
| DB | |
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
110
|
+
+----v--+ +--v---+ +v-----+ +v----+ +v-----------+
|
|
111
|
+
|Qdrant | |Redis | |Ollama| | TEI | | Gateway |
|
|
112
|
+
|Vector | |Cache | | or | |Embed| | FastAPI |
|
|
113
|
+
| DB | |+ Rate| | vLLM | | | | + RAG |
|
|
114
|
+
| | | Limit| | | | | | + Cache |
|
|
115
|
+
+-------+ +------+ +------+ +-----+ | + Breaker |
|
|
116
|
+
:6333 :6379 :11434 :8002 | + Metrics |
|
|
117
|
+
+-----+------+
|
|
118
|
+
|:8000
|
|
119
|
+
+-----v------+
|
|
120
|
+
| Prometheus |
|
|
121
|
+
| + Grafana |
|
|
122
|
+
+------------+
|
|
123
|
+
:8080
|
|
107
124
|
```
|
|
108
125
|
|
|
109
|
-
| Layer | Service |
|
|
110
|
-
|
|
111
|
-
| Inference | Ollama / vLLM (auto) |
|
|
112
|
-
| Embeddings | TEI / Ollama (auto) |
|
|
113
|
-
| Vector DB | Qdrant |
|
|
114
|
-
| Cache | Redis |
|
|
115
|
-
| API Gateway | FastAPI
|
|
116
|
-
| Dashboard | Grafana + Prometheus |
|
|
126
|
+
| Layer | Service | What it does | Port |
|
|
127
|
+
|-------|---------|-------------|------|
|
|
128
|
+
| Inference | Ollama / vLLM (auto) | LLM chat completions | 11434 |
|
|
129
|
+
| Embeddings | TEI / Ollama (auto) | Text embeddings for RAG | 8002 |
|
|
130
|
+
| Vector DB | Qdrant | Document storage + semantic search | 6333 |
|
|
131
|
+
| Cache | Redis | Response cache + rate limit state | 6379 |
|
|
132
|
+
| API Gateway | FastAPI | Routing, auth, caching, RAG, circuit breaker | 8000 |
|
|
133
|
+
| Dashboard | Grafana + Prometheus | Request rate, latency, tokens, errors | 8080 |
|
|
134
|
+
|
|
135
|
+
## Gateway Features
|
|
136
|
+
|
|
137
|
+
The gateway is not a simple proxy — it's a production-grade API layer:
|
|
138
|
+
|
|
139
|
+
### Semantic Response Cache (Redis)
|
|
140
|
+
```
|
|
141
|
+
Request → SHA-256(model + messages) → Redis lookup
|
|
142
|
+
HIT → Return cached response (< 1ms)
|
|
143
|
+
MISS → Forward to inference → Cache result → Return
|
|
144
|
+
```
|
|
145
|
+
- Only caches deterministic requests (temperature <= 0.1)
|
|
146
|
+
- TTL-based expiration (default: 1 hour)
|
|
147
|
+
- `X-Cache: HIT/MISS` response headers
|
|
148
|
+
- Cache stats in `/healthz`
|
|
149
|
+
|
|
150
|
+
### Token Bucket Rate Limiter (Redis + Lua)
|
|
151
|
+
```
|
|
152
|
+
Request → Extract API key/IP → Redis EVALSHA (atomic Lua) → Allow/Reject
|
|
153
|
+
```
|
|
154
|
+
- Configurable: `100/min`, `10/sec`, `3600/hour`
|
|
155
|
+
- Per-API-key rate limiting with IP fallback
|
|
156
|
+
- Atomic Lua script prevents race conditions
|
|
157
|
+
- In-memory fallback if Redis is unavailable
|
|
158
|
+
- Standard `X-RateLimit-*` and `Retry-After` headers
|
|
159
|
+
|
|
160
|
+
### Circuit Breaker (Inference Resilience)
|
|
161
|
+
```
|
|
162
|
+
CLOSED ──[5 failures]──> OPEN ──[timeout]──> HALF_OPEN ──[success]──> CLOSED
|
|
163
|
+
| |
|
|
164
|
+
└──[reject fast] └──[failure]──> OPEN (backoff x2)
|
|
165
|
+
```
|
|
166
|
+
- Prevents cascading failures when inference is down
|
|
167
|
+
- Exponential backoff on recovery timeout
|
|
168
|
+
- Fail-fast with `503 Service Unavailable`
|
|
169
|
+
- Metrics: state, failure count, rejections, time in state
|
|
170
|
+
|
|
171
|
+
### RAG Pipeline (Qdrant + Embeddings)
|
|
172
|
+
```
|
|
173
|
+
Ingest: Document → Chunk (512 words, 64 overlap) → Embed → Qdrant
|
|
174
|
+
Query: Question → Embed → Qdrant search → Build context → LLM generate
|
|
175
|
+
```
|
|
176
|
+
- `POST /v1/rag/ingest` — chunk, embed, and store documents
|
|
177
|
+
- `POST /v1/rag/query` — semantic search + augmented generation
|
|
178
|
+
- Source citations in responses
|
|
179
|
+
- Streaming support via SSE
|
|
180
|
+
- Deterministic chunk IDs (deduplication)
|
|
181
|
+
|
|
182
|
+
### Structured Logging
|
|
183
|
+
```json
|
|
184
|
+
{"ts":"2026-05-07T14:23:01","level":"INFO","msg":"POST /v1/chat/completions 200 1234.5ms","request_id":"a1b2c3d4","method":"POST","path":"/v1/chat/completions","status":200,"duration_ms":1234.5,"client_ip":"10.0.0.1"}
|
|
185
|
+
```
|
|
186
|
+
- `X-Request-ID` correlation headers
|
|
187
|
+
- JSON structured output for log aggregation
|
|
188
|
+
- Configurable level and format
|
|
117
189
|
|
|
118
190
|
## How it works
|
|
119
191
|
|
|
@@ -125,6 +197,7 @@ llmstack up # Boots services in order with health checks:
|
|
|
125
197
|
# Qdrant -> Redis -> Inference -> Embeddings -> Gateway -> Metrics
|
|
126
198
|
|
|
127
199
|
llmstack status # Shows health of all running services
|
|
200
|
+
llmstack chat # Interactive terminal chat with streaming
|
|
128
201
|
llmstack logs ollama # Stream inference logs
|
|
129
202
|
llmstack down # Stops everything
|
|
130
203
|
```
|
|
@@ -180,6 +253,32 @@ observe:
|
|
|
180
253
|
dashboard_port: 8080
|
|
181
254
|
```
|
|
182
255
|
|
|
256
|
+
## API Reference
|
|
257
|
+
|
|
258
|
+
### OpenAI-compatible endpoints
|
|
259
|
+
|
|
260
|
+
| Endpoint | Method | Description |
|
|
261
|
+
|----------|--------|-------------|
|
|
262
|
+
| `/v1/chat/completions` | POST | Chat completion (streaming + non-streaming) |
|
|
263
|
+
| `/v1/embeddings` | POST | Text embeddings |
|
|
264
|
+
| `/v1/models` | GET | List available models |
|
|
265
|
+
|
|
266
|
+
### RAG endpoints
|
|
267
|
+
|
|
268
|
+
| Endpoint | Method | Description |
|
|
269
|
+
|----------|--------|-------------|
|
|
270
|
+
| `/v1/rag/ingest` | POST | Ingest a document (chunk + embed + store) |
|
|
271
|
+
| `/v1/rag/query` | POST | Query with retrieval-augmented generation |
|
|
272
|
+
| `/v1/rag/documents/{source}` | DELETE | Delete documents by source |
|
|
273
|
+
| `/v1/rag/status` | GET | Collection statistics |
|
|
274
|
+
|
|
275
|
+
### System endpoints
|
|
276
|
+
|
|
277
|
+
| Endpoint | Method | Description |
|
|
278
|
+
|----------|--------|-------------|
|
|
279
|
+
| `/healthz` | GET | Health check with circuit breaker + cache stats |
|
|
280
|
+
| `/metrics` | GET | Prometheus metrics |
|
|
281
|
+
|
|
183
282
|
## Interactive Chat
|
|
184
283
|
|
|
185
284
|
```bash
|
|
@@ -198,12 +297,8 @@ You: /clear
|
|
|
198
297
|
Conversation cleared.
|
|
199
298
|
```
|
|
200
299
|
|
|
201
|
-
Streaming responses, conversation history, works with any model in your stack.
|
|
202
|
-
|
|
203
300
|
## Export to Docker Compose
|
|
204
301
|
|
|
205
|
-
Don't want to install llmstack? Generate a standalone `docker-compose.yml`:
|
|
206
|
-
|
|
207
302
|
```bash
|
|
208
303
|
llmstack export
|
|
209
304
|
# Exported 7 services to docker-compose.yml
|
|
@@ -218,10 +313,37 @@ Share the generated file with your team — no llmstack dependency required.
|
|
|
218
313
|
from openai import OpenAI
|
|
219
314
|
|
|
220
315
|
client = OpenAI(base_url="http://localhost:8000/v1", api_key="YOUR_KEY")
|
|
316
|
+
|
|
317
|
+
# Chat completion
|
|
221
318
|
response = client.chat.completions.create(
|
|
222
319
|
model="llama3.2",
|
|
223
320
|
messages=[{"role": "user", "content": "Explain quantum computing"}]
|
|
224
321
|
)
|
|
322
|
+
|
|
323
|
+
# Embeddings
|
|
324
|
+
embeddings = client.embeddings.create(
|
|
325
|
+
model="bge-m3",
|
|
326
|
+
input=["Hello world"]
|
|
327
|
+
)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
import httpx
|
|
332
|
+
|
|
333
|
+
# RAG: Ingest documents
|
|
334
|
+
httpx.post("http://localhost:8000/v1/rag/ingest", json={
|
|
335
|
+
"text": open("whitepaper.txt").read(),
|
|
336
|
+
"source": "whitepaper.txt",
|
|
337
|
+
}, headers={"Authorization": "Bearer YOUR_KEY"})
|
|
338
|
+
|
|
339
|
+
# RAG: Query
|
|
340
|
+
response = httpx.post("http://localhost:8000/v1/rag/query", json={
|
|
341
|
+
"question": "What are the key findings?",
|
|
342
|
+
"top_k": 5,
|
|
343
|
+
}, headers={"Authorization": "Bearer YOUR_KEY"})
|
|
344
|
+
|
|
345
|
+
print(response.json()["answer"])
|
|
346
|
+
print(response.json()["sources"])
|
|
225
347
|
```
|
|
226
348
|
|
|
227
349
|
## CLI
|
|
@@ -245,35 +367,24 @@ When `observe.metrics: true`, llmstack boots Prometheus + Grafana with a pre-bui
|
|
|
245
367
|
- **Latency** p50 / p99 histograms
|
|
246
368
|
- **Token throughput** (input + output)
|
|
247
369
|
- **Error rate** (4xx / 5xx)
|
|
248
|
-
- **
|
|
370
|
+
- **Cache hit rate**
|
|
371
|
+
- **Circuit breaker state**
|
|
372
|
+
- **Rate limit rejections**
|
|
249
373
|
|
|
250
374
|
Access at `http://localhost:8080` (login: admin / llmstack)
|
|
251
375
|
|
|
252
|
-
## Why not just Docker Compose?
|
|
253
|
-
|
|
254
|
-
Here's what llmstack replaces:
|
|
255
|
-
|
|
256
|
-
```yaml
|
|
257
|
-
# Without llmstack: ~200 lines of docker-compose.yml
|
|
258
|
-
# You have to configure each service, write health checks,
|
|
259
|
-
# set up networking, manage GPU passthrough, create Prometheus
|
|
260
|
-
# scrape configs, provision Grafana dashboards...
|
|
261
|
-
|
|
262
|
-
# With llmstack:
|
|
263
|
-
llmstack init && llmstack up
|
|
264
|
-
```
|
|
265
|
-
|
|
266
376
|
## Comparison
|
|
267
377
|
|
|
268
378
|
| | llmstack | Ollama | LocalAI | AnythingLLM | LiteLLM |
|
|
269
379
|
|---|---|---|---|---|---|
|
|
270
|
-
| One-command full stack | **Yes** | No
|
|
380
|
+
| One-command full stack | **Yes** | No | No | Partial | No |
|
|
381
|
+
| Built-in RAG pipeline | **Yes** | No | No | Bundled | No |
|
|
382
|
+
| Response caching | **Yes** | No | No | No | No |
|
|
383
|
+
| Circuit breaker | **Yes** | No | No | No | No |
|
|
384
|
+
| Rate limiting (Redis) | **Yes** | No | No | Yes | Yes |
|
|
271
385
|
| Auto hardware detection | **Yes** | No | No | No | No |
|
|
272
386
|
| OpenAI-compatible API | **Yes** | Yes | Yes | No | Yes |
|
|
273
387
|
| Built-in vector DB | **Yes** | No | No | Bundled | No |
|
|
274
|
-
| Built-in embeddings | **Yes** | No | No | Bundled | No |
|
|
275
|
-
| Caching (Redis) | **Yes** | No | No | No | No |
|
|
276
|
-
| Auth + rate limiting | **Yes** | No | No | Yes | Yes |
|
|
277
388
|
| Observability dashboard | **Yes** | No | Partial | No | Partial |
|
|
278
389
|
| Plugin ecosystem | **Yes** | No | No | No | No |
|
|
279
390
|
|
|
@@ -292,8 +403,11 @@ Create your own: implement `ServiceBase`, register via entry_points. See [CONTRI
|
|
|
292
403
|
|
|
293
404
|
- **CLI**: [Typer](https://typer.tiangolo.com/) + [Rich](https://rich.readthedocs.io/)
|
|
294
405
|
- **Config**: [Pydantic v2](https://docs.pydantic.dev/)
|
|
295
|
-
- **Gateway**: [FastAPI](https://fastapi.tiangolo.com/)
|
|
406
|
+
- **Gateway**: [FastAPI](https://fastapi.tiangolo.com/) + Redis + Qdrant
|
|
296
407
|
- **Containers**: [Docker SDK for Python](https://docker-py.readthedocs.io/)
|
|
408
|
+
- **Cache**: Redis with semantic hashing
|
|
409
|
+
- **Rate Limiting**: Token bucket with Redis Lua scripts
|
|
410
|
+
- **Resilience**: Circuit breaker with exponential backoff
|
|
297
411
|
- **Metrics**: Prometheus + Grafana
|
|
298
412
|
|
|
299
413
|
## Requirements
|
|
@@ -29,23 +29,35 @@ llmstack up
|
|
|
29
29
|
That's it. You now have **7 services** running: inference, embeddings, vector DB, cache, API gateway, Prometheus, and Grafana.
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
#
|
|
32
|
+
# Chat completion (OpenAI-compatible)
|
|
33
33
|
curl http://localhost:8000/v1/chat/completions \
|
|
34
34
|
-H "Authorization: Bearer YOUR_KEY" \
|
|
35
35
|
-H "Content-Type: application/json" \
|
|
36
36
|
-d '{"model":"llama3.2","messages":[{"role":"user","content":"Hello!"}]}'
|
|
37
|
+
|
|
38
|
+
# Ingest a document for RAG
|
|
39
|
+
curl http://localhost:8000/v1/rag/ingest \
|
|
40
|
+
-H "Authorization: Bearer YOUR_KEY" \
|
|
41
|
+
-H "Content-Type: application/json" \
|
|
42
|
+
-d '{"text":"LLMStack is an open-source tool for...","source":"docs.txt"}'
|
|
43
|
+
|
|
44
|
+
# Query with RAG
|
|
45
|
+
curl http://localhost:8000/v1/rag/query \
|
|
46
|
+
-H "Authorization: Bearer YOUR_KEY" \
|
|
47
|
+
-H "Content-Type: application/json" \
|
|
48
|
+
-d '{"question":"What is LLMStack?"}'
|
|
37
49
|
```
|
|
38
50
|
|
|
39
51
|
Works with **any OpenAI-compatible client**: LangChain, LlamaIndex, Vercel AI SDK, openai-python.
|
|
40
52
|
|
|
41
53
|
## Who is this for?
|
|
42
54
|
|
|
43
|
-
- **AI app developers** who want local inference without Docker boilerplate
|
|
55
|
+
- **AI app developers** who want local inference + RAG without Docker boilerplate
|
|
44
56
|
- **Teams** who need an OpenAI-compatible API backed by local models
|
|
45
57
|
- **Hobbyists** running LLMs locally who want vector search, caching, and monitoring out of the box
|
|
46
58
|
- **Anyone** tired of writing 200+ lines of docker-compose.yml every time
|
|
47
59
|
|
|
48
|
-
##
|
|
60
|
+
## Architecture
|
|
49
61
|
|
|
50
62
|
```
|
|
51
63
|
llmstack up
|
|
@@ -57,28 +69,85 @@ Works with **any OpenAI-compatible client**: LangChain, LlamaIndex, Vercel AI SD
|
|
|
57
69
|
|
|
|
58
70
|
+-------+-------+-------+-------+
|
|
59
71
|
| | | | |
|
|
60
|
-
+----v--+ +--v---+ +v-----+ +v----+ +v
|
|
61
|
-
|Qdrant | |Redis | |Ollama| | TEI | |
|
|
62
|
-
|Vector | |Cache | | or | |Embed| |
|
|
63
|
-
| DB | |
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
+----v--+ +--v---+ +v-----+ +v----+ +v-----------+
|
|
73
|
+
|Qdrant | |Redis | |Ollama| | TEI | | Gateway |
|
|
74
|
+
|Vector | |Cache | | or | |Embed| | FastAPI |
|
|
75
|
+
| DB | |+ Rate| | vLLM | | | | + RAG |
|
|
76
|
+
| | | Limit| | | | | | + Cache |
|
|
77
|
+
+-------+ +------+ +------+ +-----+ | + Breaker |
|
|
78
|
+
:6333 :6379 :11434 :8002 | + Metrics |
|
|
79
|
+
+-----+------+
|
|
80
|
+
|:8000
|
|
81
|
+
+-----v------+
|
|
82
|
+
| Prometheus |
|
|
83
|
+
| + Grafana |
|
|
84
|
+
+------------+
|
|
85
|
+
:8080
|
|
72
86
|
```
|
|
73
87
|
|
|
74
|
-
| Layer | Service |
|
|
75
|
-
|
|
76
|
-
| Inference | Ollama / vLLM (auto) |
|
|
77
|
-
| Embeddings | TEI / Ollama (auto) |
|
|
78
|
-
| Vector DB | Qdrant |
|
|
79
|
-
| Cache | Redis |
|
|
80
|
-
| API Gateway | FastAPI
|
|
81
|
-
| Dashboard | Grafana + Prometheus |
|
|
88
|
+
| Layer | Service | What it does | Port |
|
|
89
|
+
|-------|---------|-------------|------|
|
|
90
|
+
| Inference | Ollama / vLLM (auto) | LLM chat completions | 11434 |
|
|
91
|
+
| Embeddings | TEI / Ollama (auto) | Text embeddings for RAG | 8002 |
|
|
92
|
+
| Vector DB | Qdrant | Document storage + semantic search | 6333 |
|
|
93
|
+
| Cache | Redis | Response cache + rate limit state | 6379 |
|
|
94
|
+
| API Gateway | FastAPI | Routing, auth, caching, RAG, circuit breaker | 8000 |
|
|
95
|
+
| Dashboard | Grafana + Prometheus | Request rate, latency, tokens, errors | 8080 |
|
|
96
|
+
|
|
97
|
+
## Gateway Features
|
|
98
|
+
|
|
99
|
+
The gateway is not a simple proxy — it's a production-grade API layer:
|
|
100
|
+
|
|
101
|
+
### Semantic Response Cache (Redis)
|
|
102
|
+
```
|
|
103
|
+
Request → SHA-256(model + messages) → Redis lookup
|
|
104
|
+
HIT → Return cached response (< 1ms)
|
|
105
|
+
MISS → Forward to inference → Cache result → Return
|
|
106
|
+
```
|
|
107
|
+
- Only caches deterministic requests (temperature <= 0.1)
|
|
108
|
+
- TTL-based expiration (default: 1 hour)
|
|
109
|
+
- `X-Cache: HIT/MISS` response headers
|
|
110
|
+
- Cache stats in `/healthz`
|
|
111
|
+
|
|
112
|
+
### Token Bucket Rate Limiter (Redis + Lua)
|
|
113
|
+
```
|
|
114
|
+
Request → Extract API key/IP → Redis EVALSHA (atomic Lua) → Allow/Reject
|
|
115
|
+
```
|
|
116
|
+
- Configurable: `100/min`, `10/sec`, `3600/hour`
|
|
117
|
+
- Per-API-key rate limiting with IP fallback
|
|
118
|
+
- Atomic Lua script prevents race conditions
|
|
119
|
+
- In-memory fallback if Redis is unavailable
|
|
120
|
+
- Standard `X-RateLimit-*` and `Retry-After` headers
|
|
121
|
+
|
|
122
|
+
### Circuit Breaker (Inference Resilience)
|
|
123
|
+
```
|
|
124
|
+
CLOSED ──[5 failures]──> OPEN ──[timeout]──> HALF_OPEN ──[success]──> CLOSED
|
|
125
|
+
| |
|
|
126
|
+
└──[reject fast] └──[failure]──> OPEN (backoff x2)
|
|
127
|
+
```
|
|
128
|
+
- Prevents cascading failures when inference is down
|
|
129
|
+
- Exponential backoff on recovery timeout
|
|
130
|
+
- Fail-fast with `503 Service Unavailable`
|
|
131
|
+
- Metrics: state, failure count, rejections, time in state
|
|
132
|
+
|
|
133
|
+
### RAG Pipeline (Qdrant + Embeddings)
|
|
134
|
+
```
|
|
135
|
+
Ingest: Document → Chunk (512 words, 64 overlap) → Embed → Qdrant
|
|
136
|
+
Query: Question → Embed → Qdrant search → Build context → LLM generate
|
|
137
|
+
```
|
|
138
|
+
- `POST /v1/rag/ingest` — chunk, embed, and store documents
|
|
139
|
+
- `POST /v1/rag/query` — semantic search + augmented generation
|
|
140
|
+
- Source citations in responses
|
|
141
|
+
- Streaming support via SSE
|
|
142
|
+
- Deterministic chunk IDs (deduplication)
|
|
143
|
+
|
|
144
|
+
### Structured Logging
|
|
145
|
+
```json
|
|
146
|
+
{"ts":"2026-05-07T14:23:01","level":"INFO","msg":"POST /v1/chat/completions 200 1234.5ms","request_id":"a1b2c3d4","method":"POST","path":"/v1/chat/completions","status":200,"duration_ms":1234.5,"client_ip":"10.0.0.1"}
|
|
147
|
+
```
|
|
148
|
+
- `X-Request-ID` correlation headers
|
|
149
|
+
- JSON structured output for log aggregation
|
|
150
|
+
- Configurable level and format
|
|
82
151
|
|
|
83
152
|
## How it works
|
|
84
153
|
|
|
@@ -90,6 +159,7 @@ llmstack up # Boots services in order with health checks:
|
|
|
90
159
|
# Qdrant -> Redis -> Inference -> Embeddings -> Gateway -> Metrics
|
|
91
160
|
|
|
92
161
|
llmstack status # Shows health of all running services
|
|
162
|
+
llmstack chat # Interactive terminal chat with streaming
|
|
93
163
|
llmstack logs ollama # Stream inference logs
|
|
94
164
|
llmstack down # Stops everything
|
|
95
165
|
```
|
|
@@ -145,6 +215,32 @@ observe:
|
|
|
145
215
|
dashboard_port: 8080
|
|
146
216
|
```
|
|
147
217
|
|
|
218
|
+
## API Reference
|
|
219
|
+
|
|
220
|
+
### OpenAI-compatible endpoints
|
|
221
|
+
|
|
222
|
+
| Endpoint | Method | Description |
|
|
223
|
+
|----------|--------|-------------|
|
|
224
|
+
| `/v1/chat/completions` | POST | Chat completion (streaming + non-streaming) |
|
|
225
|
+
| `/v1/embeddings` | POST | Text embeddings |
|
|
226
|
+
| `/v1/models` | GET | List available models |
|
|
227
|
+
|
|
228
|
+
### RAG endpoints
|
|
229
|
+
|
|
230
|
+
| Endpoint | Method | Description |
|
|
231
|
+
|----------|--------|-------------|
|
|
232
|
+
| `/v1/rag/ingest` | POST | Ingest a document (chunk + embed + store) |
|
|
233
|
+
| `/v1/rag/query` | POST | Query with retrieval-augmented generation |
|
|
234
|
+
| `/v1/rag/documents/{source}` | DELETE | Delete documents by source |
|
|
235
|
+
| `/v1/rag/status` | GET | Collection statistics |
|
|
236
|
+
|
|
237
|
+
### System endpoints
|
|
238
|
+
|
|
239
|
+
| Endpoint | Method | Description |
|
|
240
|
+
|----------|--------|-------------|
|
|
241
|
+
| `/healthz` | GET | Health check with circuit breaker + cache stats |
|
|
242
|
+
| `/metrics` | GET | Prometheus metrics |
|
|
243
|
+
|
|
148
244
|
## Interactive Chat
|
|
149
245
|
|
|
150
246
|
```bash
|
|
@@ -163,12 +259,8 @@ You: /clear
|
|
|
163
259
|
Conversation cleared.
|
|
164
260
|
```
|
|
165
261
|
|
|
166
|
-
Streaming responses, conversation history, works with any model in your stack.
|
|
167
|
-
|
|
168
262
|
## Export to Docker Compose
|
|
169
263
|
|
|
170
|
-
Don't want to install llmstack? Generate a standalone `docker-compose.yml`:
|
|
171
|
-
|
|
172
264
|
```bash
|
|
173
265
|
llmstack export
|
|
174
266
|
# Exported 7 services to docker-compose.yml
|
|
@@ -183,10 +275,37 @@ Share the generated file with your team — no llmstack dependency required.
|
|
|
183
275
|
from openai import OpenAI
|
|
184
276
|
|
|
185
277
|
client = OpenAI(base_url="http://localhost:8000/v1", api_key="YOUR_KEY")
|
|
278
|
+
|
|
279
|
+
# Chat completion
|
|
186
280
|
response = client.chat.completions.create(
|
|
187
281
|
model="llama3.2",
|
|
188
282
|
messages=[{"role": "user", "content": "Explain quantum computing"}]
|
|
189
283
|
)
|
|
284
|
+
|
|
285
|
+
# Embeddings
|
|
286
|
+
embeddings = client.embeddings.create(
|
|
287
|
+
model="bge-m3",
|
|
288
|
+
input=["Hello world"]
|
|
289
|
+
)
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
```python
|
|
293
|
+
import httpx
|
|
294
|
+
|
|
295
|
+
# RAG: Ingest documents
|
|
296
|
+
httpx.post("http://localhost:8000/v1/rag/ingest", json={
|
|
297
|
+
"text": open("whitepaper.txt").read(),
|
|
298
|
+
"source": "whitepaper.txt",
|
|
299
|
+
}, headers={"Authorization": "Bearer YOUR_KEY"})
|
|
300
|
+
|
|
301
|
+
# RAG: Query
|
|
302
|
+
response = httpx.post("http://localhost:8000/v1/rag/query", json={
|
|
303
|
+
"question": "What are the key findings?",
|
|
304
|
+
"top_k": 5,
|
|
305
|
+
}, headers={"Authorization": "Bearer YOUR_KEY"})
|
|
306
|
+
|
|
307
|
+
print(response.json()["answer"])
|
|
308
|
+
print(response.json()["sources"])
|
|
190
309
|
```
|
|
191
310
|
|
|
192
311
|
## CLI
|
|
@@ -210,35 +329,24 @@ When `observe.metrics: true`, llmstack boots Prometheus + Grafana with a pre-bui
|
|
|
210
329
|
- **Latency** p50 / p99 histograms
|
|
211
330
|
- **Token throughput** (input + output)
|
|
212
331
|
- **Error rate** (4xx / 5xx)
|
|
213
|
-
- **
|
|
332
|
+
- **Cache hit rate**
|
|
333
|
+
- **Circuit breaker state**
|
|
334
|
+
- **Rate limit rejections**
|
|
214
335
|
|
|
215
336
|
Access at `http://localhost:8080` (login: admin / llmstack)
|
|
216
337
|
|
|
217
|
-
## Why not just Docker Compose?
|
|
218
|
-
|
|
219
|
-
Here's what llmstack replaces:
|
|
220
|
-
|
|
221
|
-
```yaml
|
|
222
|
-
# Without llmstack: ~200 lines of docker-compose.yml
|
|
223
|
-
# You have to configure each service, write health checks,
|
|
224
|
-
# set up networking, manage GPU passthrough, create Prometheus
|
|
225
|
-
# scrape configs, provision Grafana dashboards...
|
|
226
|
-
|
|
227
|
-
# With llmstack:
|
|
228
|
-
llmstack init && llmstack up
|
|
229
|
-
```
|
|
230
|
-
|
|
231
338
|
## Comparison
|
|
232
339
|
|
|
233
340
|
| | llmstack | Ollama | LocalAI | AnythingLLM | LiteLLM |
|
|
234
341
|
|---|---|---|---|---|---|
|
|
235
|
-
| One-command full stack | **Yes** | No
|
|
342
|
+
| One-command full stack | **Yes** | No | No | Partial | No |
|
|
343
|
+
| Built-in RAG pipeline | **Yes** | No | No | Bundled | No |
|
|
344
|
+
| Response caching | **Yes** | No | No | No | No |
|
|
345
|
+
| Circuit breaker | **Yes** | No | No | No | No |
|
|
346
|
+
| Rate limiting (Redis) | **Yes** | No | No | Yes | Yes |
|
|
236
347
|
| Auto hardware detection | **Yes** | No | No | No | No |
|
|
237
348
|
| OpenAI-compatible API | **Yes** | Yes | Yes | No | Yes |
|
|
238
349
|
| Built-in vector DB | **Yes** | No | No | Bundled | No |
|
|
239
|
-
| Built-in embeddings | **Yes** | No | No | Bundled | No |
|
|
240
|
-
| Caching (Redis) | **Yes** | No | No | No | No |
|
|
241
|
-
| Auth + rate limiting | **Yes** | No | No | Yes | Yes |
|
|
242
350
|
| Observability dashboard | **Yes** | No | Partial | No | Partial |
|
|
243
351
|
| Plugin ecosystem | **Yes** | No | No | No | No |
|
|
244
352
|
|
|
@@ -257,8 +365,11 @@ Create your own: implement `ServiceBase`, register via entry_points. See [CONTRI
|
|
|
257
365
|
|
|
258
366
|
- **CLI**: [Typer](https://typer.tiangolo.com/) + [Rich](https://rich.readthedocs.io/)
|
|
259
367
|
- **Config**: [Pydantic v2](https://docs.pydantic.dev/)
|
|
260
|
-
- **Gateway**: [FastAPI](https://fastapi.tiangolo.com/)
|
|
368
|
+
- **Gateway**: [FastAPI](https://fastapi.tiangolo.com/) + Redis + Qdrant
|
|
261
369
|
- **Containers**: [Docker SDK for Python](https://docker-py.readthedocs.io/)
|
|
370
|
+
- **Cache**: Redis with semantic hashing
|
|
371
|
+
- **Rate Limiting**: Token bucket with Redis Lua scripts
|
|
372
|
+
- **Resilience**: Circuit breaker with exponential backoff
|
|
262
373
|
- **Metrics**: Prometheus + Grafana
|
|
263
374
|
|
|
264
375
|
## Requirements
|