dragon-cli 0.2.0__tar.gz → 0.2.1__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.
Files changed (43) hide show
  1. {dragon_cli-0.2.0/src/dragon_cli.egg-info → dragon_cli-0.2.1}/PKG-INFO +49 -20
  2. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/README.md +47 -18
  3. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/pyproject.toml +5 -2
  4. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/__init__.py +1 -1
  5. dragon_cli-0.2.1/src/dragon_cli/static/dragon-icon.png +0 -0
  6. dragon_cli-0.2.1/src/dragon_cli/static/style.css +352 -0
  7. dragon_cli-0.2.1/src/dragon_cli/templates/chat.html +160 -0
  8. dragon_cli-0.2.1/src/dragon_cli/templates/index.html +82 -0
  9. {dragon_cli-0.2.0 → dragon_cli-0.2.1/src/dragon_cli.egg-info}/PKG-INFO +49 -20
  10. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli.egg-info/SOURCES.txt +5 -1
  11. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli.egg-info/requires.txt +1 -1
  12. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/LICENSE +0 -0
  13. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/setup.cfg +0 -0
  14. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/__main__.py +0 -0
  15. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/_cuda.py +0 -0
  16. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/app.py +0 -0
  17. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/banner.py +0 -0
  18. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/chat/__init__.py +0 -0
  19. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/chat/commands.py +0 -0
  20. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/chat/session.py +0 -0
  21. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/chat/streaming.py +0 -0
  22. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/cli.py +0 -0
  23. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/config.py +0 -0
  24. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/hardware.py +0 -0
  25. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/mcp/__init__.py +0 -0
  26. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/mcp/manager.py +0 -0
  27. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/mcp/tool_call.py +0 -0
  28. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/menu.py +0 -0
  29. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/model_hub.py +0 -0
  30. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/models/__init__.py +0 -0
  31. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/models/catalog.py +0 -0
  32. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/models/loader.py +0 -0
  33. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/recommendation.py +0 -0
  34. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/runtime.py +0 -0
  35. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/server.py +0 -0
  36. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/setup_llm.py +0 -0
  37. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/storage/__init__.py +0 -0
  38. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/storage/conversations.py +0 -0
  39. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/storage/database.py +0 -0
  40. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/web.py +0 -0
  41. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli.egg-info/dependency_links.txt +0 -0
  42. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli.egg-info/entry_points.txt +0 -0
  43. {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dragon-cli
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: CLI to chat with local GGUF LLMs, with MCP tool-calling and SQLite conversation history.
5
5
  Author: JPEDROPS092
6
6
  License-Expression: MIT
@@ -17,14 +17,14 @@ Requires-Dist: mcp
17
17
  Requires-Dist: fastapi
18
18
  Requires-Dist: starlette
19
19
  Requires-Dist: uvicorn
20
- Requires-Dist: fastapi
20
+ Requires-Dist: jinja2
21
21
  Provides-Extra: llm
22
22
  Requires-Dist: llama-cpp-python; extra == "llm"
23
23
  Dynamic: license-file
24
24
 
25
25
  # Dragon CLI
26
26
 
27
- ![1785346956957](image/README/1785346956957.png)
27
+ ![1785346956957](https://raw.githubusercontent.com/JPEDROPS092/dragon-cli-ai-local/main/image/README/1785346956957.png)
28
28
 
29
29
  [![License: MIT](https://img.shields.io/github/license/JPEDROPS092/dragon-cli-ai-local)](LICENSE)
30
30
  [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
@@ -32,38 +32,61 @@ Dynamic: license-file
32
32
  [![GitHub stars](https://img.shields.io/github/stars/JPEDROPS092/dragon-cli-ai-local?style=social)](https://github.com/JPEDROPS092/dragon-cli-ai-local/stargazers)
33
33
  [![GitHub issues](https://img.shields.io/github/issues/JPEDROPS092/dragon-cli-ai-local)](https://github.com/JPEDROPS092/dragon-cli-ai-local/issues)
34
34
 
35
- Chat with local GGUF LLMs (100% CPU, via `llama-cpp-python`), with optional
36
- MCP (Model Context Protocol) tool-calling and a SQLite-backed conversation
37
- history.
35
+ [![PyPI](https://img.shields.io/pypi/v/dragon-cli)](https://pypi.org/project/dragon-cli/)
36
+
37
+ Chat with local GGUF LLMs on CPU or GPU (via `llama-cpp-python`), with
38
+ MCP (Model Context Protocol) tool-calling, a SQLite-backed conversation
39
+ history, and an OpenAI-compatible HTTP server you can plug into agent
40
+ frameworks (OpenAI SDK, OpenAI Agents SDK, LangChain, LlamaIndex, CrewAI,
41
+ Continue, Aider, Open WebUI...).
42
+
43
+ 📖 **Full docs, guides and agent-framework examples:**
44
+ https://jpedrops092.github.io/dragon-cli-ai-local/
38
45
 
39
46
  ## Install
40
47
 
48
+ Requires Python 3.10+ on Windows, Linux or macOS.
49
+
41
50
  ```bash
42
- pip install -e .
43
- dragon-cli-install-llm
51
+ pip install dragon-cli
52
+ dragon-cli-install-llm # installs the right llama-cpp-python build for your CPU/GPU
53
+ dragon-cli # interactive menu
44
54
  ```
45
55
 
46
- This exposes a `dragon-cli` command. `llama-cpp-python` is intentionally kept
47
- out of the core dependencies, since it ships as C++ source on PyPI for many
48
- platforms — a plain `pip install` would try to compile it locally and fail
49
- without a C/C++ toolchain (Visual Studio Build Tools on Windows, gcc on
50
- Linux, Xcode command line tools on macOS).
56
+ Upgrade with `pip install -U dragon-cli`. A virtual environment is
57
+ recommended (`python -m venv .venv` and activate it first).
51
58
 
52
- `dragon-cli-install-llm` detects your OS/architecture (Windows, Linux, or
53
- macOS) and installs a prebuilt CPU wheel instead, so a compiler usually isn't
54
- needed at all. If no prebuilt wheel is available for your exact
59
+ `llama-cpp-python` is intentionally kept out of the core dependencies, since
60
+ it ships as C++ source on PyPI for many platforms, so a plain `pip install`
61
+ would try to compile it locally and fail without a C/C++ toolchain (Visual
62
+ Studio Build Tools on Windows, gcc on Linux, Xcode command line tools on
63
+ macOS).
64
+
65
+ `dragon-cli-install-llm` detects your OS/architecture and GPU (NVIDIA CUDA,
66
+ AMD ROCm, Apple Metal) and installs a prebuilt wheel instead, so a compiler
67
+ usually isn't needed at all. If no prebuilt wheel is available for your exact
55
68
  platform/Python combination, it prints the toolchain to install and the
56
69
  fallback command to run.
57
70
 
58
71
  If you already have a working C/C++ toolchain and would rather build from
59
- source yourself, skip the installer and use the extra instead:
60
- `pip install -e ".[llm]"`.
72
+ source yourself: `pip install "dragon-cli[llm]"`.
73
+
74
+ Models (`./models`), `mcp_servers.json` and `dragon_cli.db` are created in
75
+ the folder you run `dragon-cli` from.
61
76
 
62
- You can also run it without installing:
77
+ ### Other ways to install
63
78
 
64
79
  ```bash
80
+ # latest development version from GitHub
81
+ pip install git+https://github.com/JPEDROPS092/dragon-cli-ai-local
82
+
83
+ # from a local clone (editable)
84
+ git clone https://github.com/JPEDROPS092/dragon-cli-ai-local
85
+ cd dragon-cli-ai-local
86
+ pip install -e .
87
+
88
+ # or run without installing
65
89
  python dragon_cli.py
66
- # or
67
90
  python -m dragon_cli
68
91
  ```
69
92
 
@@ -80,6 +103,12 @@ dragon-cli --db path/to/other.db # use a different conversation database
80
103
  dragon-cli --serve # serve the model over HTTP instead of chatting
81
104
  dragon-cli --serve --port 8080 # custom port (default: 8000)
82
105
  dragon-cli --serve --host 0.0.0.0 # expose beyond localhost (e.g. to your LAN) — opt-in only
106
+ dragon-cli --profile # show detected CPU/RAM/GPU and exit
107
+ dragon-cli --recommend # rank catalog models for your hardware
108
+ dragon-cli --auto # pick the best model for your hardware and chat
109
+ dragon-cli --priority speed # speed | quality | balanced | memory (for --auto/--recommend)
110
+ dragon-cli --gpu / --cpu # force full GPU offload, or CPU only
111
+ dragon-cli --ngl 20 # offload exactly 20 layers to the GPU
83
112
  ```
84
113
 
85
114
  ## HTTP server (`--serve`)
@@ -1,6 +1,6 @@
1
1
  # Dragon CLI
2
2
 
3
- ![1785346956957](image/README/1785346956957.png)
3
+ ![1785346956957](https://raw.githubusercontent.com/JPEDROPS092/dragon-cli-ai-local/main/image/README/1785346956957.png)
4
4
 
5
5
  [![License: MIT](https://img.shields.io/github/license/JPEDROPS092/dragon-cli-ai-local)](LICENSE)
6
6
  [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
@@ -8,38 +8,61 @@
8
8
  [![GitHub stars](https://img.shields.io/github/stars/JPEDROPS092/dragon-cli-ai-local?style=social)](https://github.com/JPEDROPS092/dragon-cli-ai-local/stargazers)
9
9
  [![GitHub issues](https://img.shields.io/github/issues/JPEDROPS092/dragon-cli-ai-local)](https://github.com/JPEDROPS092/dragon-cli-ai-local/issues)
10
10
 
11
- Chat with local GGUF LLMs (100% CPU, via `llama-cpp-python`), with optional
12
- MCP (Model Context Protocol) tool-calling and a SQLite-backed conversation
13
- history.
11
+ [![PyPI](https://img.shields.io/pypi/v/dragon-cli)](https://pypi.org/project/dragon-cli/)
12
+
13
+ Chat with local GGUF LLMs on CPU or GPU (via `llama-cpp-python`), with
14
+ MCP (Model Context Protocol) tool-calling, a SQLite-backed conversation
15
+ history, and an OpenAI-compatible HTTP server you can plug into agent
16
+ frameworks (OpenAI SDK, OpenAI Agents SDK, LangChain, LlamaIndex, CrewAI,
17
+ Continue, Aider, Open WebUI...).
18
+
19
+ 📖 **Full docs, guides and agent-framework examples:**
20
+ https://jpedrops092.github.io/dragon-cli-ai-local/
14
21
 
15
22
  ## Install
16
23
 
24
+ Requires Python 3.10+ on Windows, Linux or macOS.
25
+
17
26
  ```bash
18
- pip install -e .
19
- dragon-cli-install-llm
27
+ pip install dragon-cli
28
+ dragon-cli-install-llm # installs the right llama-cpp-python build for your CPU/GPU
29
+ dragon-cli # interactive menu
20
30
  ```
21
31
 
22
- This exposes a `dragon-cli` command. `llama-cpp-python` is intentionally kept
23
- out of the core dependencies, since it ships as C++ source on PyPI for many
24
- platforms — a plain `pip install` would try to compile it locally and fail
25
- without a C/C++ toolchain (Visual Studio Build Tools on Windows, gcc on
26
- Linux, Xcode command line tools on macOS).
32
+ Upgrade with `pip install -U dragon-cli`. A virtual environment is
33
+ recommended (`python -m venv .venv` and activate it first).
34
+
35
+ `llama-cpp-python` is intentionally kept out of the core dependencies, since
36
+ it ships as C++ source on PyPI for many platforms, so a plain `pip install`
37
+ would try to compile it locally and fail without a C/C++ toolchain (Visual
38
+ Studio Build Tools on Windows, gcc on Linux, Xcode command line tools on
39
+ macOS).
27
40
 
28
- `dragon-cli-install-llm` detects your OS/architecture (Windows, Linux, or
29
- macOS) and installs a prebuilt CPU wheel instead, so a compiler usually isn't
30
- needed at all. If no prebuilt wheel is available for your exact
41
+ `dragon-cli-install-llm` detects your OS/architecture and GPU (NVIDIA CUDA,
42
+ AMD ROCm, Apple Metal) and installs a prebuilt wheel instead, so a compiler
43
+ usually isn't needed at all. If no prebuilt wheel is available for your exact
31
44
  platform/Python combination, it prints the toolchain to install and the
32
45
  fallback command to run.
33
46
 
34
47
  If you already have a working C/C++ toolchain and would rather build from
35
- source yourself, skip the installer and use the extra instead:
36
- `pip install -e ".[llm]"`.
48
+ source yourself: `pip install "dragon-cli[llm]"`.
37
49
 
38
- You can also run it without installing:
50
+ Models (`./models`), `mcp_servers.json` and `dragon_cli.db` are created in
51
+ the folder you run `dragon-cli` from.
52
+
53
+ ### Other ways to install
39
54
 
40
55
  ```bash
56
+ # latest development version from GitHub
57
+ pip install git+https://github.com/JPEDROPS092/dragon-cli-ai-local
58
+
59
+ # from a local clone (editable)
60
+ git clone https://github.com/JPEDROPS092/dragon-cli-ai-local
61
+ cd dragon-cli-ai-local
62
+ pip install -e .
63
+
64
+ # or run without installing
41
65
  python dragon_cli.py
42
- # or
43
66
  python -m dragon_cli
44
67
  ```
45
68
 
@@ -56,6 +79,12 @@ dragon-cli --db path/to/other.db # use a different conversation database
56
79
  dragon-cli --serve # serve the model over HTTP instead of chatting
57
80
  dragon-cli --serve --port 8080 # custom port (default: 8000)
58
81
  dragon-cli --serve --host 0.0.0.0 # expose beyond localhost (e.g. to your LAN) — opt-in only
82
+ dragon-cli --profile # show detected CPU/RAM/GPU and exit
83
+ dragon-cli --recommend # rank catalog models for your hardware
84
+ dragon-cli --auto # pick the best model for your hardware and chat
85
+ dragon-cli --priority speed # speed | quality | balanced | memory (for --auto/--recommend)
86
+ dragon-cli --gpu / --cpu # force full GPU offload, or CPU only
87
+ dragon-cli --ngl 20 # offload exactly 20 layers to the GPU
59
88
  ```
60
89
 
61
90
  ## HTTP server (`--serve`)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dragon-cli"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "CLI to chat with local GGUF LLMs, with MCP tool-calling and SQLite conversation history."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -18,7 +18,7 @@ dependencies = [
18
18
  "fastapi",
19
19
  "starlette",
20
20
  "uvicorn",
21
- "fastapi"
21
+ "jinja2",
22
22
  ]
23
23
 
24
24
  [project.optional-dependencies]
@@ -40,3 +40,6 @@ dragon-cli-install-llm = "dragon_cli.setup_llm:main"
40
40
 
41
41
  [tool.setuptools.packages.find]
42
42
  where = ["src"]
43
+
44
+ [tool.setuptools.package-data]
45
+ dragon_cli = ["templates/*", "static/*"]
@@ -1,4 +1,4 @@
1
- __version__ = "0.2.0"
1
+ __version__ = "0.2.1"
2
2
 
3
3
  from ._cuda import preload_cuda_runtime
4
4
 
@@ -0,0 +1,352 @@
1
+ :root {
2
+ color-scheme: dark;
3
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
4
+ background: #09101a;
5
+ color: #e7edf4;
6
+ }
7
+
8
+ * {
9
+ box-sizing: border-box;
10
+ }
11
+
12
+ body {
13
+ margin: 0;
14
+ min-height: 100vh;
15
+ padding: 0;
16
+ background: radial-gradient(circle at top, rgba(93, 173, 249, 0.15), transparent 35%),
17
+ linear-gradient(180deg, #08111c 0%, #0b141e 100%);
18
+ }
19
+
20
+ button,
21
+ input,
22
+ textarea {
23
+ font: inherit;
24
+ }
25
+
26
+ .app-shell {
27
+ display: grid;
28
+ grid-template-columns: 280px 1fr;
29
+ min-height: 100vh;
30
+ gap: 1rem;
31
+ padding: 1.25rem;
32
+ }
33
+
34
+ .sidebar {
35
+ background: rgba(14, 24, 38, 0.95);
36
+ border: 1px solid rgba(255, 255, 255, 0.06);
37
+ border-radius: 24px;
38
+ padding: 1.5rem;
39
+ display: flex;
40
+ flex-direction: column;
41
+ gap: 1.5rem;
42
+ }
43
+
44
+ .brand {
45
+ display: flex;
46
+ align-items: center;
47
+ gap: 1rem;
48
+ }
49
+
50
+ .brand-icon {
51
+ width: 54px;
52
+ height: 54px;
53
+ object-fit: contain;
54
+ border-radius: 18px;
55
+ background: linear-gradient(135deg, #2c5a7b, #0f2334);
56
+ border: 1px solid rgba(255, 255, 255, 0.08);
57
+ padding: 0.7rem;
58
+ }
59
+
60
+ .brand h1 {
61
+ margin: 0;
62
+ font-size: 1.3rem;
63
+ }
64
+
65
+ .brand p {
66
+ margin: 0.25rem 0 0;
67
+ color: #8fabc6;
68
+ }
69
+
70
+ .model-card,
71
+ .instructions {
72
+ background: rgba(255, 255, 255, 0.03);
73
+ border: 1px solid rgba(255, 255, 255, 0.08);
74
+ border-radius: 18px;
75
+ padding: 1rem;
76
+ }
77
+
78
+ .model-card strong {
79
+ display: block;
80
+ margin-bottom: 0.5rem;
81
+ color: #90d7ff;
82
+ }
83
+
84
+ .chat-panel {
85
+ display: flex;
86
+ flex-direction: column;
87
+ background: rgba(6, 14, 24, 0.95);
88
+ border: 1px solid rgba(255, 255, 255, 0.06);
89
+ border-radius: 28px;
90
+ padding: 1.25rem;
91
+ min-height: calc(100vh - 2.5rem);
92
+ }
93
+
94
+ .chat-header {
95
+ display: flex;
96
+ justify-content: space-between;
97
+ align-items: flex-start;
98
+ gap: 1rem;
99
+ margin-bottom: 1rem;
100
+ }
101
+
102
+ .chat-header h2 {
103
+ margin: 0;
104
+ font-size: 1.35rem;
105
+ }
106
+
107
+ .chat-header p {
108
+ margin: 0.35rem 0 0;
109
+ color: #8da3c4;
110
+ }
111
+
112
+ .messages {
113
+ flex: 1;
114
+ display: grid;
115
+ gap: 0.85rem;
116
+ overflow-y: auto;
117
+ padding-right: 0.25rem;
118
+ }
119
+
120
+ .message {
121
+ padding: 1rem 1.1rem;
122
+ border-radius: 18px;
123
+ line-height: 1.6;
124
+ white-space: pre-wrap;
125
+ word-break: break-word;
126
+ }
127
+
128
+ .message-user {
129
+ background: rgba(68, 119, 218, 0.16);
130
+ border: 1px solid rgba(68, 119, 218, 0.25);
131
+ align-self: end;
132
+ }
133
+
134
+ .message-assistant {
135
+ background: rgba(98, 234, 194, 0.08);
136
+ border: 1px solid rgba(98, 234, 194, 0.14);
137
+ }
138
+
139
+ .message-role {
140
+ margin-bottom: 0.5rem;
141
+ font-size: 0.82rem;
142
+ letter-spacing: 0.04em;
143
+ text-transform: uppercase;
144
+ color: #72d6c9;
145
+ }
146
+
147
+ .composer {
148
+ margin-top: 1rem;
149
+ display: grid;
150
+ gap: 0.9rem;
151
+ }
152
+
153
+ textarea {
154
+ width: 100%;
155
+ min-height: 120px;
156
+ resize: vertical;
157
+ border-radius: 20px;
158
+ border: 1px solid rgba(255, 255, 255, 0.08);
159
+ background: rgba(10, 19, 32, 0.92);
160
+ color: #eef4fb;
161
+ padding: 1rem 1rem;
162
+ }
163
+
164
+ textarea:focus {
165
+ outline: none;
166
+ border-color: #5dd9f9;
167
+ box-shadow: 0 0 0 3px rgba(93, 217, 255, 0.12);
168
+ }
169
+
170
+ .composer-actions {
171
+ display: flex;
172
+ justify-content: space-between;
173
+ align-items: center;
174
+ gap: 1rem;
175
+ flex-wrap: wrap;
176
+ }
177
+
178
+ button[type="submit"] {
179
+ border: none;
180
+ cursor: pointer;
181
+ border-radius: 999px;
182
+ padding: 0.95rem 1.55rem;
183
+ background: linear-gradient(135deg, #2b9bff, #4fc3ff);
184
+ color: #081116;
185
+ font-weight: 700;
186
+ }
187
+
188
+ button[type="submit"]:hover {
189
+ opacity: 0.95;
190
+ }
191
+
192
+ label {
193
+ display: inline-flex;
194
+ align-items: center;
195
+ gap: 0.5rem;
196
+ color: #b7c8de;
197
+ font-size: 0.95rem;
198
+ }
199
+
200
+ input[type="checkbox"] {
201
+ width: 16px;
202
+ height: 16px;
203
+ }
204
+
205
+ @media (max-width: 980px) {
206
+ .app-shell {
207
+ grid-template-columns: 1fr;
208
+ }
209
+ .chat-panel {
210
+ min-height: auto;
211
+ }
212
+ }
213
+
214
+ /* ---------- Welcome page ---------- */
215
+ .welcome-shell {
216
+ max-width: 920px;
217
+ margin: 0 auto;
218
+ padding: 3rem 1.25rem 2rem;
219
+ display: flex;
220
+ flex-direction: column;
221
+ gap: 2rem;
222
+ }
223
+
224
+ .welcome-hero {
225
+ text-align: center;
226
+ background: rgba(14, 24, 38, 0.95);
227
+ border: 1px solid rgba(255, 255, 255, 0.06);
228
+ border-radius: 28px;
229
+ padding: 2.5rem 2rem;
230
+ }
231
+
232
+ .welcome-icon {
233
+ width: 80px;
234
+ height: 80px;
235
+ object-fit: contain;
236
+ border-radius: 22px;
237
+ background: linear-gradient(135deg, #2c5a7b, #0f2334);
238
+ border: 1px solid rgba(255, 255, 255, 0.08);
239
+ padding: 0.9rem;
240
+ }
241
+
242
+ .welcome-hero h1 {
243
+ margin: 1.25rem 0 0.5rem;
244
+ font-size: 2rem;
245
+ }
246
+
247
+ .welcome-sub {
248
+ margin: 0 auto 1.5rem;
249
+ max-width: 560px;
250
+ color: #8fabc6;
251
+ line-height: 1.6;
252
+ }
253
+
254
+ .cta {
255
+ display: inline-block;
256
+ text-decoration: none;
257
+ border-radius: 999px;
258
+ padding: 0.85rem 1.6rem;
259
+ background: linear-gradient(135deg, #2b9bff, #4fc3ff);
260
+ color: #081116;
261
+ font-weight: 700;
262
+ }
263
+
264
+ .cta:hover {
265
+ opacity: 0.95;
266
+ }
267
+
268
+ .welcome-grid {
269
+ display: grid;
270
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
271
+ gap: 1.25rem;
272
+ }
273
+
274
+ .card {
275
+ background: rgba(255, 255, 255, 0.03);
276
+ border: 1px solid rgba(255, 255, 255, 0.08);
277
+ border-radius: 20px;
278
+ padding: 1.5rem;
279
+ }
280
+
281
+ .card h2 {
282
+ margin: 0 0 0.85rem;
283
+ font-size: 1.1rem;
284
+ color: #90d7ff;
285
+ }
286
+
287
+ .card p {
288
+ margin: 0 0 0.75rem;
289
+ line-height: 1.6;
290
+ color: #b7c8de;
291
+ }
292
+
293
+ .endpoint-list {
294
+ margin: 0;
295
+ padding: 0;
296
+ list-style: none;
297
+ display: grid;
298
+ gap: 0.6rem;
299
+ }
300
+
301
+ .endpoint-list li {
302
+ color: #b7c8de;
303
+ line-height: 1.5;
304
+ }
305
+
306
+ .code-block {
307
+ margin: 0.25rem 0 0;
308
+ padding: 1rem;
309
+ background: rgba(6, 14, 24, 0.95);
310
+ border: 1px solid rgba(255, 255, 255, 0.06);
311
+ border-radius: 14px;
312
+ color: #d6e7f5;
313
+ font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
314
+ font-size: 0.85rem;
315
+ line-height: 1.5;
316
+ overflow-x: auto;
317
+ white-space: pre;
318
+ }
319
+
320
+ .welcome-footer {
321
+ display: flex;
322
+ flex-wrap: wrap;
323
+ gap: 0.6rem;
324
+ justify-content: center;
325
+ align-items: center;
326
+ color: #8fabc6;
327
+ font-size: 0.9rem;
328
+ padding-top: 0.5rem;
329
+ }
330
+
331
+ .welcome-footer a {
332
+ color: #90d7ff;
333
+ text-decoration: none;
334
+ }
335
+
336
+ .welcome-footer a:hover {
337
+ text-decoration: underline;
338
+ }
339
+
340
+ .instructions a,
341
+ .welcome-footer a {
342
+ color: #90d7ff;
343
+ }
344
+
345
+ code {
346
+ background: rgba(255, 255, 255, 0.06);
347
+ border-radius: 6px;
348
+ padding: 0.1rem 0.35rem;
349
+ font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
350
+ font-size: 0.85em;
351
+ }
352
+
@@ -0,0 +1,160 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Dragon CLI Chat UI</title>
7
+ <link rel="icon" type="image/svg+xml" href="/favicon.ico">
8
+ <link rel="stylesheet" href="/static/style.css">
9
+ </head>
10
+ <body>
11
+ <div class="app-shell">
12
+ <aside class="sidebar">
13
+ <div class="brand">
14
+ <img src="/static/dragon-icon.png" alt="Dragon CLI" class="brand-icon">
15
+ <div>
16
+ <h1>Dragon CLI</h1>
17
+ <p>Chat with your local model</p>
18
+ </div>
19
+ </div>
20
+ <div class="model-card">
21
+ <strong>Model:</strong>
22
+ <span>{{ model_name }}</span>
23
+ </div>
24
+ <div class="instructions">
25
+ <p>Type your message below and send it to chat with the local model.</p>
26
+ <p><a href="{{ base_url }}/">Back to home</a></p>
27
+ </div>
28
+ </aside>
29
+
30
+ <main class="chat-panel">
31
+ <header class="chat-header">
32
+ <div>
33
+ <h2>Dragon CLI Chat</h2>
34
+ <p>Servidor: {{ base_url }}</p>
35
+ </div>
36
+ </header>
37
+
38
+ <section id="messages" class="messages" aria-live="polite"></section>
39
+
40
+ <form id="chat-form" class="composer">
41
+ <textarea id="prompt" placeholder="Type your question here..." rows="3" required></textarea>
42
+ <div class="composer-actions">
43
+ <button type="submit">Send</button>
44
+ <label>
45
+ <input type="checkbox" id="stream" checked>
46
+ Streaming
47
+ </label>
48
+ </div>
49
+ </form>
50
+ </main>
51
+ </div>
52
+
53
+ <script>
54
+ const baseUrl = "{{ base_url }}";
55
+ const modelName = "{{ model_name }}";
56
+
57
+ const messagesEl = document.getElementById("messages");
58
+ const form = document.getElementById("chat-form");
59
+ const promptInput = document.getElementById("prompt");
60
+ const streamCheckbox = document.getElementById("stream");
61
+
62
+ function addMessage(role, text) {
63
+ const messageElement = document.createElement("div");
64
+ messageElement.className = `message message-${role}`;
65
+ messageElement.innerHTML = `<div class="message-role">${role}</div><div class="message-text"></div>`;
66
+ messageElement.querySelector(".message-text").textContent = text;
67
+ messagesEl.appendChild(messageElement);
68
+ messagesEl.scrollTop = messagesEl.scrollHeight;
69
+ return messageElement;
70
+ }
71
+
72
+ function setAssistantText(el, text) {
73
+ el.querySelector(".message-text").textContent = text;
74
+ messagesEl.scrollTop = messagesEl.scrollHeight;
75
+ }
76
+
77
+ form.addEventListener("submit", async (event) => {
78
+ event.preventDefault();
79
+ const prompt = promptInput.value.trim();
80
+ if (!prompt) return;
81
+
82
+ const userMessage = addMessage("user", prompt);
83
+ promptInput.value = "";
84
+ promptInput.focus();
85
+
86
+ const assistantMessage = addMessage("assistant", "Waiting for response...");
87
+ const stream = streamCheckbox.checked;
88
+
89
+ try {
90
+ if (stream) {
91
+ const res = await fetch(`${baseUrl}/v1/chat/completions`, {
92
+ method: "POST",
93
+ headers: { "Content-Type": "application/json" },
94
+ body: JSON.stringify({
95
+ model: modelName,
96
+ messages: [
97
+ { role: "user", content: prompt }
98
+ ],
99
+ stream: true,
100
+ }),
101
+ });
102
+
103
+ if (!res.ok) {
104
+ throw new Error(`Error ${res.status}`);
105
+ }
106
+
107
+ const reader = res.body.getReader();
108
+ const decoder = new TextDecoder("utf-8");
109
+ let assistantText = "";
110
+
111
+ while (true) {
112
+ const { done, value } = await reader.read();
113
+ if (done) break;
114
+ const chunk = decoder.decode(value, { stream: true });
115
+ for (const line of chunk.split("\n")) {
116
+ if (!line.trim().startsWith("data:")) continue;
117
+ const payload = line.replace(/^data:\s*/, "");
118
+ if (payload === "[DONE]") {
119
+ return;
120
+ }
121
+ try {
122
+ const event = JSON.parse(payload);
123
+ if (event.choices?.[0]?.delta?.content) {
124
+ assistantText += event.choices[0].delta.content;
125
+ setAssistantText(assistantMessage, assistantText);
126
+ }
127
+ } catch (error) {
128
+ console.warn("Failed to parse SSE chunk", error);
129
+ }
130
+ }
131
+ }
132
+ } else {
133
+ const res = await fetch(`${baseUrl}/v1/chat/completions`, {
134
+ method: "POST",
135
+ headers: { "Content-Type": "application/json" },
136
+ body: JSON.stringify({
137
+ model: modelName,
138
+ messages: [
139
+ { role: "user", content: prompt }
140
+ ],
141
+ stream: false,
142
+ }),
143
+ });
144
+
145
+ if (!res.ok) {
146
+ const details = await res.text();
147
+ throw new Error(details || `Error ${res.status}`);
148
+ }
149
+
150
+ const payload = await res.json();
151
+ const content = payload.choices?.[0]?.message?.content || "No response.";
152
+ setAssistantText(assistantMessage, content);
153
+ }
154
+ } catch (error) {
155
+ setAssistantText(assistantMessage, `Error: ${error.message}`);
156
+ }
157
+ });
158
+ </script>
159
+ </body>
160
+ </html>
@@ -0,0 +1,82 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Dragon CLI — Local Model Server</title>
7
+ <link rel="icon" type="image/svg+xml" href="/favicon.ico">
8
+ <link rel="stylesheet" href="/static/style.css">
9
+ </head>
10
+ <body>
11
+ <div class="welcome-shell">
12
+ <header class="welcome-hero">
13
+ <img src="/static/dragon-icon.png" alt="Dragon CLI" class="welcome-icon">
14
+ <h1>Welcome to Dragon CLI</h1>
15
+ <p class="welcome-sub">
16
+ Local OpenAI-compatible server running the
17
+ <strong>{{ model_name }}</strong> model.
18
+ </p>
19
+ <a class="cta" href="{{ base_url }}/chat">Open chat &rarr;</a>
20
+ </header>
21
+
22
+ <section class="welcome-grid">
23
+ <article class="card">
24
+ <h2>Main endpoints</h2>
25
+ <ul class="endpoint-list">
26
+ <li><code>GET&nbsp;&nbsp;/v1/models</code> — list the loaded model</li>
27
+ <li><code>POST&nbsp;/v1/chat/completions</code> — chat completion (stream or not)</li>
28
+ <li><code>GET&nbsp;&nbsp;/docs</code> — interactive docs (Swagger UI)</li>
29
+ <li><code>GET&nbsp;&nbsp;/chat</code> — web chat interface</li>
30
+ </ul>
31
+ </article>
32
+
33
+ <article class="card">
34
+ <h2>How to use</h2>
35
+ <p>
36
+ Point any OpenAI client at
37
+ <code>{{ base_url }}/v1</code> using <em>any key</em> (authentication is ignored).
38
+ The full base URL is:
39
+ </p>
40
+ <pre class="code-block">{{ base_url }}/v1</pre>
41
+ </article>
42
+
43
+ <article class="card">
44
+ <h2>cURL example</h2>
45
+ <pre class="code-block">curl -X POST {{ base_url }}/v1/chat/completions \
46
+ -H "Content-Type: application/json" \
47
+ -d '{
48
+ "model": "{{ model_name }}",
49
+ "messages": [
50
+ {"role": "user", "content": "Hello!"}
51
+ ],
52
+ "stream": false
53
+ }'</pre>
54
+ </article>
55
+
56
+ <article class="card">
57
+ <h2>Python example</h2>
58
+ <pre class="code-block">from openai import OpenAI
59
+
60
+ client = OpenAI(
61
+ base_url="{{ base_url }}/v1",
62
+ api_key="ignored",
63
+ )
64
+
65
+ resp = client.chat.completions.create(
66
+ model="{{ model_name }}",
67
+ messages=[{"role": "user", "content": "Hello!"}],
68
+ )
69
+ print(resp.choices[0].message.content)</pre>
70
+ </article>
71
+ </section>
72
+
73
+ <footer class="welcome-footer">
74
+ <span>Active model: <strong>{{ model_name }}</strong></span>
75
+ <span>&middot;</span>
76
+ <a href="{{ base_url }}/docs">Swagger UI</a>
77
+ <span>&middot;</span>
78
+ <a href="{{ base_url }}/chat">Chat</a>
79
+ </footer>
80
+ </div>
81
+ </body>
82
+ </html>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dragon-cli
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: CLI to chat with local GGUF LLMs, with MCP tool-calling and SQLite conversation history.
5
5
  Author: JPEDROPS092
6
6
  License-Expression: MIT
@@ -17,14 +17,14 @@ Requires-Dist: mcp
17
17
  Requires-Dist: fastapi
18
18
  Requires-Dist: starlette
19
19
  Requires-Dist: uvicorn
20
- Requires-Dist: fastapi
20
+ Requires-Dist: jinja2
21
21
  Provides-Extra: llm
22
22
  Requires-Dist: llama-cpp-python; extra == "llm"
23
23
  Dynamic: license-file
24
24
 
25
25
  # Dragon CLI
26
26
 
27
- ![1785346956957](image/README/1785346956957.png)
27
+ ![1785346956957](https://raw.githubusercontent.com/JPEDROPS092/dragon-cli-ai-local/main/image/README/1785346956957.png)
28
28
 
29
29
  [![License: MIT](https://img.shields.io/github/license/JPEDROPS092/dragon-cli-ai-local)](LICENSE)
30
30
  [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
@@ -32,38 +32,61 @@ Dynamic: license-file
32
32
  [![GitHub stars](https://img.shields.io/github/stars/JPEDROPS092/dragon-cli-ai-local?style=social)](https://github.com/JPEDROPS092/dragon-cli-ai-local/stargazers)
33
33
  [![GitHub issues](https://img.shields.io/github/issues/JPEDROPS092/dragon-cli-ai-local)](https://github.com/JPEDROPS092/dragon-cli-ai-local/issues)
34
34
 
35
- Chat with local GGUF LLMs (100% CPU, via `llama-cpp-python`), with optional
36
- MCP (Model Context Protocol) tool-calling and a SQLite-backed conversation
37
- history.
35
+ [![PyPI](https://img.shields.io/pypi/v/dragon-cli)](https://pypi.org/project/dragon-cli/)
36
+
37
+ Chat with local GGUF LLMs on CPU or GPU (via `llama-cpp-python`), with
38
+ MCP (Model Context Protocol) tool-calling, a SQLite-backed conversation
39
+ history, and an OpenAI-compatible HTTP server you can plug into agent
40
+ frameworks (OpenAI SDK, OpenAI Agents SDK, LangChain, LlamaIndex, CrewAI,
41
+ Continue, Aider, Open WebUI...).
42
+
43
+ 📖 **Full docs, guides and agent-framework examples:**
44
+ https://jpedrops092.github.io/dragon-cli-ai-local/
38
45
 
39
46
  ## Install
40
47
 
48
+ Requires Python 3.10+ on Windows, Linux or macOS.
49
+
41
50
  ```bash
42
- pip install -e .
43
- dragon-cli-install-llm
51
+ pip install dragon-cli
52
+ dragon-cli-install-llm # installs the right llama-cpp-python build for your CPU/GPU
53
+ dragon-cli # interactive menu
44
54
  ```
45
55
 
46
- This exposes a `dragon-cli` command. `llama-cpp-python` is intentionally kept
47
- out of the core dependencies, since it ships as C++ source on PyPI for many
48
- platforms — a plain `pip install` would try to compile it locally and fail
49
- without a C/C++ toolchain (Visual Studio Build Tools on Windows, gcc on
50
- Linux, Xcode command line tools on macOS).
56
+ Upgrade with `pip install -U dragon-cli`. A virtual environment is
57
+ recommended (`python -m venv .venv` and activate it first).
51
58
 
52
- `dragon-cli-install-llm` detects your OS/architecture (Windows, Linux, or
53
- macOS) and installs a prebuilt CPU wheel instead, so a compiler usually isn't
54
- needed at all. If no prebuilt wheel is available for your exact
59
+ `llama-cpp-python` is intentionally kept out of the core dependencies, since
60
+ it ships as C++ source on PyPI for many platforms, so a plain `pip install`
61
+ would try to compile it locally and fail without a C/C++ toolchain (Visual
62
+ Studio Build Tools on Windows, gcc on Linux, Xcode command line tools on
63
+ macOS).
64
+
65
+ `dragon-cli-install-llm` detects your OS/architecture and GPU (NVIDIA CUDA,
66
+ AMD ROCm, Apple Metal) and installs a prebuilt wheel instead, so a compiler
67
+ usually isn't needed at all. If no prebuilt wheel is available for your exact
55
68
  platform/Python combination, it prints the toolchain to install and the
56
69
  fallback command to run.
57
70
 
58
71
  If you already have a working C/C++ toolchain and would rather build from
59
- source yourself, skip the installer and use the extra instead:
60
- `pip install -e ".[llm]"`.
72
+ source yourself: `pip install "dragon-cli[llm]"`.
73
+
74
+ Models (`./models`), `mcp_servers.json` and `dragon_cli.db` are created in
75
+ the folder you run `dragon-cli` from.
61
76
 
62
- You can also run it without installing:
77
+ ### Other ways to install
63
78
 
64
79
  ```bash
80
+ # latest development version from GitHub
81
+ pip install git+https://github.com/JPEDROPS092/dragon-cli-ai-local
82
+
83
+ # from a local clone (editable)
84
+ git clone https://github.com/JPEDROPS092/dragon-cli-ai-local
85
+ cd dragon-cli-ai-local
86
+ pip install -e .
87
+
88
+ # or run without installing
65
89
  python dragon_cli.py
66
- # or
67
90
  python -m dragon_cli
68
91
  ```
69
92
 
@@ -80,6 +103,12 @@ dragon-cli --db path/to/other.db # use a different conversation database
80
103
  dragon-cli --serve # serve the model over HTTP instead of chatting
81
104
  dragon-cli --serve --port 8080 # custom port (default: 8000)
82
105
  dragon-cli --serve --host 0.0.0.0 # expose beyond localhost (e.g. to your LAN) — opt-in only
106
+ dragon-cli --profile # show detected CPU/RAM/GPU and exit
107
+ dragon-cli --recommend # rank catalog models for your hardware
108
+ dragon-cli --auto # pick the best model for your hardware and chat
109
+ dragon-cli --priority speed # speed | quality | balanced | memory (for --auto/--recommend)
110
+ dragon-cli --gpu / --cpu # force full GPU offload, or CPU only
111
+ dragon-cli --ngl 20 # offload exactly 20 layers to the GPU
83
112
  ```
84
113
 
85
114
  ## HTTP server (`--serve`)
@@ -32,6 +32,10 @@ src/dragon_cli/mcp/tool_call.py
32
32
  src/dragon_cli/models/__init__.py
33
33
  src/dragon_cli/models/catalog.py
34
34
  src/dragon_cli/models/loader.py
35
+ src/dragon_cli/static/dragon-icon.png
36
+ src/dragon_cli/static/style.css
35
37
  src/dragon_cli/storage/__init__.py
36
38
  src/dragon_cli/storage/conversations.py
37
- src/dragon_cli/storage/database.py
39
+ src/dragon_cli/storage/database.py
40
+ src/dragon_cli/templates/chat.html
41
+ src/dragon_cli/templates/index.html
@@ -5,7 +5,7 @@ mcp
5
5
  fastapi
6
6
  starlette
7
7
  uvicorn
8
- fastapi
8
+ jinja2
9
9
 
10
10
  [llm]
11
11
  llama-cpp-python
File without changes
File without changes