llmstack-cli 0.1.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/.github/ISSUE_TEMPLATE/bug_report.yml +38 -0
- llmstack_cli-0.3.0/.github/ISSUE_TEMPLATE/feature_request.yml +23 -0
- llmstack_cli-0.3.0/.github/PULL_REQUEST_TEMPLATE.md +17 -0
- llmstack_cli-0.3.0/.github/SECURITY.md +17 -0
- llmstack_cli-0.3.0/CHANGELOG.md +63 -0
- llmstack_cli-0.3.0/PKG-INFO +424 -0
- llmstack_cli-0.3.0/README.md +386 -0
- llmstack_cli-0.3.0/assets/social-preview.png +0 -0
- llmstack_cli-0.3.0/demo.gif +0 -0
- llmstack_cli-0.3.0/demo.tape +51 -0
- llmstack_cli-0.3.0/examples/output.txt +86 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/pyproject.toml +6 -3
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/__init__.py +1 -1
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/cli/app.py +18 -0
- llmstack_cli-0.3.0/src/llmstack/cli/commands/chat.py +104 -0
- llmstack_cli-0.3.0/src/llmstack/cli/commands/export.py +190 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/core/health.py +1 -1
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/core/stack.py +12 -1
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/docker/manager.py +23 -7
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/gateway/Dockerfile +6 -2
- 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.1.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.1.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.1.0 → llmstack_cli-0.3.0}/src/llmstack/services/gateway/service.py +16 -1
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/services/observe/prometheus.py +76 -23
- 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_export.py +65 -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.1.0 → llmstack_cli-0.3.0}/tests/unit/test_services.py +21 -0
- llmstack_cli-0.1.0/CHANGELOG.md +0 -16
- llmstack_cli-0.1.0/PKG-INFO +0 -252
- llmstack_cli-0.1.0/README.md +0 -217
- llmstack_cli-0.1.0/src/llmstack/gateway/proxy.py +0 -58
- llmstack_cli-0.1.0/src/llmstack/gateway/routes/chat.py +0 -27
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/.github/workflows/ci.yml +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/.github/workflows/release.yml +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/.gitignore +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/CONTRIBUTING.md +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/LICENSE +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/Makefile +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/__main__.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/cli/__init__.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/cli/commands/__init__.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/cli/commands/doctor.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/cli/commands/down.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/cli/commands/init.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/cli/commands/logs.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/cli/commands/status.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/cli/commands/up.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/cli/console.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/config/__init__.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/config/loader.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/config/presets/__init__.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/config/presets/agent.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/config/presets/chat.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/config/presets/rag.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/config/schema.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/core/__init__.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/core/hardware.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/core/resolver.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/docker/__init__.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/gateway/__init__.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/gateway/middleware/__init__.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/gateway/middleware/auth.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/gateway/middleware/metrics.py +0 -0
- {llmstack_cli-0.1.0/src/llmstack/gateway/routes → llmstack_cli-0.3.0/src/llmstack/gateway/rag}/__init__.py +0 -0
- {llmstack_cli-0.1.0/src/llmstack/plugins → llmstack_cli-0.3.0/src/llmstack/gateway/routes}/__init__.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/gateway/routes/embeddings.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/gateway/routes/models.py +0 -0
- {llmstack_cli-0.1.0/src/llmstack/services → llmstack_cli-0.3.0/src/llmstack/plugins}/__init__.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/plugins/loader.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/plugins/spec.py +0 -0
- {llmstack_cli-0.1.0/src/llmstack/services/cache → llmstack_cli-0.3.0/src/llmstack/services}/__init__.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/services/base.py +0 -0
- {llmstack_cli-0.1.0/src/llmstack/services/embeddings → llmstack_cli-0.3.0/src/llmstack/services/cache}/__init__.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/services/cache/redis.py +0 -0
- {llmstack_cli-0.1.0/src/llmstack/services/gateway → llmstack_cli-0.3.0/src/llmstack/services/embeddings}/__init__.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/services/embeddings/tei.py +0 -0
- {llmstack_cli-0.1.0/src/llmstack/services/inference → llmstack_cli-0.3.0/src/llmstack/services/gateway}/__init__.py +0 -0
- {llmstack_cli-0.1.0/src/llmstack/services/observe → llmstack_cli-0.3.0/src/llmstack/services/inference}/__init__.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/services/inference/ollama.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/services/inference/vllm.py +0 -0
- {llmstack_cli-0.1.0/src/llmstack/services/vectordb → llmstack_cli-0.3.0/src/llmstack/services/observe}/__init__.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/services/registry.py +0 -0
- {llmstack_cli-0.1.0/tests → llmstack_cli-0.3.0/src/llmstack/services/vectordb}/__init__.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/src/llmstack/services/vectordb/qdrant.py +0 -0
- {llmstack_cli-0.1.0/tests/unit → llmstack_cli-0.3.0/tests}/__init__.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/tests/unit/test_config.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/tests/unit/test_gateway.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/tests/unit/test_hardware.py +0 -0
- {llmstack_cli-0.1.0 → llmstack_cli-0.3.0}/tests/unit/test_resolver.py +0 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: Bug Report
|
|
2
|
+
description: Report a bug or unexpected behavior
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: description
|
|
7
|
+
attributes:
|
|
8
|
+
label: What happened?
|
|
9
|
+
description: A clear description of the bug.
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: reproduce
|
|
14
|
+
attributes:
|
|
15
|
+
label: Steps to reproduce
|
|
16
|
+
description: Minimal steps to reproduce the issue.
|
|
17
|
+
placeholder: |
|
|
18
|
+
1. llmstack init --preset rag
|
|
19
|
+
2. llmstack up
|
|
20
|
+
3. ...
|
|
21
|
+
validations:
|
|
22
|
+
required: true
|
|
23
|
+
- type: textarea
|
|
24
|
+
id: expected
|
|
25
|
+
attributes:
|
|
26
|
+
label: Expected behavior
|
|
27
|
+
description: What did you expect to happen?
|
|
28
|
+
- type: textarea
|
|
29
|
+
id: environment
|
|
30
|
+
attributes:
|
|
31
|
+
label: Environment
|
|
32
|
+
description: Paste the output of `llmstack doctor`
|
|
33
|
+
render: shell
|
|
34
|
+
- type: input
|
|
35
|
+
id: version
|
|
36
|
+
attributes:
|
|
37
|
+
label: llmstack version
|
|
38
|
+
placeholder: "0.1.0"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: Feature Request
|
|
2
|
+
description: Suggest a new feature or improvement
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: Problem
|
|
9
|
+
description: What problem does this solve? What's your use case?
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: solution
|
|
14
|
+
attributes:
|
|
15
|
+
label: Proposed solution
|
|
16
|
+
description: How would you like this to work?
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
19
|
+
- type: textarea
|
|
20
|
+
id: alternatives
|
|
21
|
+
attributes:
|
|
22
|
+
label: Alternatives considered
|
|
23
|
+
description: Any other approaches you've considered?
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting a Vulnerability
|
|
4
|
+
|
|
5
|
+
If you discover a security vulnerability, please report it responsibly:
|
|
6
|
+
|
|
7
|
+
1. **Do not** open a public issue
|
|
8
|
+
2. Email: [open a private security advisory](https://github.com/mara-werils/llmstack/security/advisories/new)
|
|
9
|
+
3. Include steps to reproduce and potential impact
|
|
10
|
+
|
|
11
|
+
We will respond within 48 hours and aim to release a fix within 7 days.
|
|
12
|
+
|
|
13
|
+
## Supported Versions
|
|
14
|
+
|
|
15
|
+
| Version | Supported |
|
|
16
|
+
|---------|-----------|
|
|
17
|
+
| 0.1.x | Yes |
|
|
@@ -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
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: llmstack-cli
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: One command. Full LLM stack. Zero config.
|
|
5
|
+
Author: mara-werils
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: ai,cli,docker,inference,llm,openai,rag
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Requires-Dist: docker>=7.0
|
|
18
|
+
Requires-Dist: httpx>=0.27
|
|
19
|
+
Requires-Dist: psutil>=5.9
|
|
20
|
+
Requires-Dist: pydantic>=2.0
|
|
21
|
+
Requires-Dist: pyyaml>=6.0
|
|
22
|
+
Requires-Dist: rich>=13.0
|
|
23
|
+
Requires-Dist: typer>=0.12
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: fakeredis>=2.21; extra == 'dev'
|
|
26
|
+
Requires-Dist: fastapi>=0.115; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: redis>=5.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
31
|
+
Requires-Dist: starlette>=0.40; extra == 'dev'
|
|
32
|
+
Provides-Extra: gateway
|
|
33
|
+
Requires-Dist: fastapi>=0.115; extra == 'gateway'
|
|
34
|
+
Requires-Dist: redis>=5.0; extra == 'gateway'
|
|
35
|
+
Requires-Dist: starlette>=0.40; extra == 'gateway'
|
|
36
|
+
Requires-Dist: uvicorn[standard]>=0.30; extra == 'gateway'
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
|
|
39
|
+
<p align="center">
|
|
40
|
+
<h1 align="center">llmstack</h1>
|
|
41
|
+
<p align="center"><strong>One command. Full LLM stack. Zero config.</strong></p>
|
|
42
|
+
<p align="center">Stop wiring Docker containers. Start building AI apps.</p>
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
<p align="center">
|
|
46
|
+
<a href="https://pypi.org/project/llmstack-cli/"><img src="https://img.shields.io/pypi/v/llmstack-cli?color=blue" alt="PyPI"></a>
|
|
47
|
+
<a href="https://github.com/mara-werils/llmstack/actions/workflows/ci.yml"><img src="https://github.com/mara-werils/llmstack/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
48
|
+
<a href="https://github.com/mara-werils/llmstack/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-green" alt="License"></a>
|
|
49
|
+
<a href="https://www.python.org/"><img src="https://img.shields.io/badge/python-3.11+-blue" alt="Python"></a>
|
|
50
|
+
<a href="https://github.com/mara-werils/llmstack/stargazers"><img src="https://img.shields.io/github/stars/mara-werils/llmstack?style=social" alt="Stars"></a>
|
|
51
|
+
</p>
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
<p align="center">
|
|
56
|
+
<img src="demo.gif" alt="llmstack demo" width="800">
|
|
57
|
+
</p>
|
|
58
|
+
|
|
59
|
+
## Quick Start
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install llmstack-cli
|
|
63
|
+
llmstack init --preset rag
|
|
64
|
+
llmstack up
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
That's it. You now have **7 services** running: inference, embeddings, vector DB, cache, API gateway, Prometheus, and Grafana.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Chat completion (OpenAI-compatible)
|
|
71
|
+
curl http://localhost:8000/v1/chat/completions \
|
|
72
|
+
-H "Authorization: Bearer YOUR_KEY" \
|
|
73
|
+
-H "Content-Type: application/json" \
|
|
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?"}'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Works with **any OpenAI-compatible client**: LangChain, LlamaIndex, Vercel AI SDK, openai-python.
|
|
90
|
+
|
|
91
|
+
## Who is this for?
|
|
92
|
+
|
|
93
|
+
- **AI app developers** who want local inference + RAG without Docker boilerplate
|
|
94
|
+
- **Teams** who need an OpenAI-compatible API backed by local models
|
|
95
|
+
- **Hobbyists** running LLMs locally who want vector search, caching, and monitoring out of the box
|
|
96
|
+
- **Anyone** tired of writing 200+ lines of docker-compose.yml every time
|
|
97
|
+
|
|
98
|
+
## Architecture
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
llmstack up
|
|
102
|
+
|
|
|
103
|
+
+---------v----------+
|
|
104
|
+
| Hardware Detect |
|
|
105
|
+
| NVIDIA / Apple / CPU|
|
|
106
|
+
+---------+----------+
|
|
107
|
+
|
|
|
108
|
+
+-------+-------+-------+-------+
|
|
109
|
+
| | | | |
|
|
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
|
|
124
|
+
```
|
|
125
|
+
|
|
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
|
|
189
|
+
|
|
190
|
+
## How it works
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
llmstack init # Detects hardware, generates llmstack.yaml
|
|
194
|
+
# Picks optimal backend: vLLM for NVIDIA 16GB+, Ollama otherwise
|
|
195
|
+
|
|
196
|
+
llmstack up # Boots services in order with health checks:
|
|
197
|
+
# Qdrant -> Redis -> Inference -> Embeddings -> Gateway -> Metrics
|
|
198
|
+
|
|
199
|
+
llmstack status # Shows health of all running services
|
|
200
|
+
llmstack chat # Interactive terminal chat with streaming
|
|
201
|
+
llmstack logs ollama # Stream inference logs
|
|
202
|
+
llmstack down # Stops everything
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Auto hardware detection
|
|
206
|
+
|
|
207
|
+
| Your hardware | Backend | Why |
|
|
208
|
+
|---|---|---|
|
|
209
|
+
| NVIDIA GPU 16GB+ VRAM | vLLM | Max throughput, PagedAttention |
|
|
210
|
+
| NVIDIA GPU <16GB | Ollama | Lower memory overhead |
|
|
211
|
+
| Apple Silicon (M1-M4) | Ollama | Metal acceleration |
|
|
212
|
+
| CPU only | Ollama | GGUF quantized models |
|
|
213
|
+
|
|
214
|
+
## Presets
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
llmstack init --preset chat # Minimal: inference + cache + gateway
|
|
218
|
+
llmstack init --preset rag # + Qdrant + embeddings for RAG apps
|
|
219
|
+
llmstack init --preset agent # 70B model + 16K context + longer timeouts
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Configuration
|
|
223
|
+
|
|
224
|
+
One file: `llmstack.yaml`
|
|
225
|
+
|
|
226
|
+
```yaml
|
|
227
|
+
version: "1"
|
|
228
|
+
|
|
229
|
+
models:
|
|
230
|
+
chat:
|
|
231
|
+
name: llama3.2
|
|
232
|
+
backend: auto # auto | ollama | vllm
|
|
233
|
+
context_length: 8192
|
|
234
|
+
embeddings:
|
|
235
|
+
name: bge-m3
|
|
236
|
+
|
|
237
|
+
services:
|
|
238
|
+
vectors:
|
|
239
|
+
provider: qdrant
|
|
240
|
+
port: 6333
|
|
241
|
+
cache:
|
|
242
|
+
provider: redis
|
|
243
|
+
max_memory: 256mb
|
|
244
|
+
|
|
245
|
+
gateway:
|
|
246
|
+
port: 8000
|
|
247
|
+
auth: api_key
|
|
248
|
+
rate_limit: 100/min
|
|
249
|
+
cors: ["*"]
|
|
250
|
+
|
|
251
|
+
observe:
|
|
252
|
+
metrics: true
|
|
253
|
+
dashboard_port: 8080
|
|
254
|
+
```
|
|
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
|
+
|
|
282
|
+
## Interactive Chat
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
llmstack chat
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
LLMStack Chat — model: llama3.2
|
|
290
|
+
Type 'exit' or Ctrl+C to quit. '/clear' to reset conversation.
|
|
291
|
+
|
|
292
|
+
You: What is quantum computing?
|
|
293
|
+
Assistant: Quantum computing uses quantum mechanical phenomena like
|
|
294
|
+
superposition and entanglement to process information...
|
|
295
|
+
|
|
296
|
+
You: /clear
|
|
297
|
+
Conversation cleared.
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Export to Docker Compose
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
llmstack export
|
|
304
|
+
# Exported 7 services to docker-compose.yml
|
|
305
|
+
# Run with: docker compose up -d
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Share the generated file with your team — no llmstack dependency required.
|
|
309
|
+
|
|
310
|
+
## Use the API
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
from openai import OpenAI
|
|
314
|
+
|
|
315
|
+
client = OpenAI(base_url="http://localhost:8000/v1", api_key="YOUR_KEY")
|
|
316
|
+
|
|
317
|
+
# Chat completion
|
|
318
|
+
response = client.chat.completions.create(
|
|
319
|
+
model="llama3.2",
|
|
320
|
+
messages=[{"role": "user", "content": "Explain quantum computing"}]
|
|
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"])
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## CLI
|
|
350
|
+
|
|
351
|
+
| Command | Description |
|
|
352
|
+
|---------|-------------|
|
|
353
|
+
| `llmstack init [--preset]` | Create config with smart defaults |
|
|
354
|
+
| `llmstack up [--attach]` | Start all services |
|
|
355
|
+
| `llmstack down [--volumes]` | Stop and clean up |
|
|
356
|
+
| `llmstack status` | Health check all services |
|
|
357
|
+
| `llmstack chat [--model]` | Interactive terminal chat |
|
|
358
|
+
| `llmstack export [--output]` | Generate docker-compose.yml |
|
|
359
|
+
| `llmstack logs <service>` | Stream service logs |
|
|
360
|
+
| `llmstack doctor` | Diagnose system issues |
|
|
361
|
+
|
|
362
|
+
## Observability
|
|
363
|
+
|
|
364
|
+
When `observe.metrics: true`, llmstack boots Prometheus + Grafana with a pre-built dashboard:
|
|
365
|
+
|
|
366
|
+
- **Request rate** per endpoint
|
|
367
|
+
- **Latency** p50 / p99 histograms
|
|
368
|
+
- **Token throughput** (input + output)
|
|
369
|
+
- **Error rate** (4xx / 5xx)
|
|
370
|
+
- **Cache hit rate**
|
|
371
|
+
- **Circuit breaker state**
|
|
372
|
+
- **Rate limit rejections**
|
|
373
|
+
|
|
374
|
+
Access at `http://localhost:8080` (login: admin / llmstack)
|
|
375
|
+
|
|
376
|
+
## Comparison
|
|
377
|
+
|
|
378
|
+
| | llmstack | Ollama | LocalAI | AnythingLLM | LiteLLM |
|
|
379
|
+
|---|---|---|---|---|---|
|
|
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 |
|
|
385
|
+
| Auto hardware detection | **Yes** | No | No | No | No |
|
|
386
|
+
| OpenAI-compatible API | **Yes** | Yes | Yes | No | Yes |
|
|
387
|
+
| Built-in vector DB | **Yes** | No | No | Bundled | No |
|
|
388
|
+
| Observability dashboard | **Yes** | No | Partial | No | Partial |
|
|
389
|
+
| Plugin ecosystem | **Yes** | No | No | No | No |
|
|
390
|
+
|
|
391
|
+
## Plugins
|
|
392
|
+
|
|
393
|
+
Extend llmstack with new backends via pip:
|
|
394
|
+
|
|
395
|
+
```bash
|
|
396
|
+
pip install llmstack-cli-plugin-chromadb
|
|
397
|
+
# Now: vectors.provider: chromadb in llmstack.yaml
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Create your own: implement `ServiceBase`, register via entry_points. See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
401
|
+
|
|
402
|
+
## Tech stack
|
|
403
|
+
|
|
404
|
+
- **CLI**: [Typer](https://typer.tiangolo.com/) + [Rich](https://rich.readthedocs.io/)
|
|
405
|
+
- **Config**: [Pydantic v2](https://docs.pydantic.dev/)
|
|
406
|
+
- **Gateway**: [FastAPI](https://fastapi.tiangolo.com/) + Redis + Qdrant
|
|
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
|
|
411
|
+
- **Metrics**: Prometheus + Grafana
|
|
412
|
+
|
|
413
|
+
## Requirements
|
|
414
|
+
|
|
415
|
+
- Python 3.11+
|
|
416
|
+
- Docker
|
|
417
|
+
|
|
418
|
+
## Contributing
|
|
419
|
+
|
|
420
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
|
|
421
|
+
|
|
422
|
+
## License
|
|
423
|
+
|
|
424
|
+
Apache-2.0
|