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.
- {dragon_cli-0.2.0/src/dragon_cli.egg-info → dragon_cli-0.2.1}/PKG-INFO +49 -20
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/README.md +47 -18
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/pyproject.toml +5 -2
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/__init__.py +1 -1
- dragon_cli-0.2.1/src/dragon_cli/static/dragon-icon.png +0 -0
- dragon_cli-0.2.1/src/dragon_cli/static/style.css +352 -0
- dragon_cli-0.2.1/src/dragon_cli/templates/chat.html +160 -0
- dragon_cli-0.2.1/src/dragon_cli/templates/index.html +82 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1/src/dragon_cli.egg-info}/PKG-INFO +49 -20
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli.egg-info/SOURCES.txt +5 -1
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli.egg-info/requires.txt +1 -1
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/LICENSE +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/setup.cfg +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/__main__.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/_cuda.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/app.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/banner.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/chat/__init__.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/chat/commands.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/chat/session.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/chat/streaming.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/cli.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/config.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/hardware.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/mcp/__init__.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/mcp/manager.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/mcp/tool_call.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/menu.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/model_hub.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/models/__init__.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/models/catalog.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/models/loader.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/recommendation.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/runtime.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/server.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/setup_llm.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/storage/__init__.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/storage/conversations.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/storage/database.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli/web.py +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli.egg-info/dependency_links.txt +0 -0
- {dragon_cli-0.2.0 → dragon_cli-0.2.1}/src/dragon_cli.egg-info/entry_points.txt +0 -0
- {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.
|
|
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:
|
|
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
|
-

|
|
27
|
+

|
|
28
28
|
|
|
29
29
|
[](LICENSE)
|
|
30
30
|
[](https://www.python.org/)
|
|
@@ -32,38 +32,61 @@ Dynamic: license-file
|
|
|
32
32
|
[](https://github.com/JPEDROPS092/dragon-cli-ai-local/stargazers)
|
|
33
33
|
[](https://github.com/JPEDROPS092/dragon-cli-ai-local/issues)
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
[](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 -
|
|
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
|
-
|
|
47
|
-
|
|
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
|
-
`
|
|
53
|
-
|
|
54
|
-
|
|
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
|
|
60
|
-
|
|
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
|
-
|
|
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
|
-

|
|
3
|
+

|
|
4
4
|
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://www.python.org/)
|
|
@@ -8,38 +8,61 @@
|
|
|
8
8
|
[](https://github.com/JPEDROPS092/dragon-cli-ai-local/stargazers)
|
|
9
9
|
[](https://github.com/JPEDROPS092/dragon-cli-ai-local/issues)
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
[](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 -
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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 (
|
|
29
|
-
|
|
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
|
|
36
|
-
`pip install -e ".[llm]"`.
|
|
48
|
+
source yourself: `pip install "dragon-cli[llm]"`.
|
|
37
49
|
|
|
38
|
-
|
|
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.
|
|
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
|
-
"
|
|
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/*"]
|
|
Binary file
|
|
@@ -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 →</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 /v1/models</code> — list the loaded model</li>
|
|
27
|
+
<li><code>POST /v1/chat/completions</code> — chat completion (stream or not)</li>
|
|
28
|
+
<li><code>GET /docs</code> — interactive docs (Swagger UI)</li>
|
|
29
|
+
<li><code>GET /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>·</span>
|
|
76
|
+
<a href="{{ base_url }}/docs">Swagger UI</a>
|
|
77
|
+
<span>·</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.
|
|
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:
|
|
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
|
-

|
|
27
|
+

|
|
28
28
|
|
|
29
29
|
[](LICENSE)
|
|
30
30
|
[](https://www.python.org/)
|
|
@@ -32,38 +32,61 @@ Dynamic: license-file
|
|
|
32
32
|
[](https://github.com/JPEDROPS092/dragon-cli-ai-local/stargazers)
|
|
33
33
|
[](https://github.com/JPEDROPS092/dragon-cli-ai-local/issues)
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
[](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 -
|
|
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
|
-
|
|
47
|
-
|
|
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
|
-
`
|
|
53
|
-
|
|
54
|
-
|
|
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
|
|
60
|
-
|
|
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
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|