olmon 0.1.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.
- olmon-0.1.0/LICENSE +21 -0
- olmon-0.1.0/PKG-INFO +177 -0
- olmon-0.1.0/README.md +156 -0
- olmon-0.1.0/olmon/__init__.py +1 -0
- olmon-0.1.0/olmon/__main__.py +4 -0
- olmon-0.1.0/olmon/client.py +55 -0
- olmon-0.1.0/olmon/commands/__init__.py +0 -0
- olmon-0.1.0/olmon/commands/init.py +30 -0
- olmon-0.1.0/olmon/commands/models.py +49 -0
- olmon-0.1.0/olmon/commands/ps.py +18 -0
- olmon-0.1.0/olmon/commands/status.py +26 -0
- olmon-0.1.0/olmon/commands/uninstall.py +76 -0
- olmon-0.1.0/olmon/commands/update.py +110 -0
- olmon-0.1.0/olmon/commands/watch.py +58 -0
- olmon-0.1.0/olmon/config.py +30 -0
- olmon-0.1.0/olmon/display.py +128 -0
- olmon-0.1.0/olmon/main.py +103 -0
- olmon-0.1.0/olmon.egg-info/PKG-INFO +177 -0
- olmon-0.1.0/olmon.egg-info/SOURCES.txt +23 -0
- olmon-0.1.0/olmon.egg-info/dependency_links.txt +1 -0
- olmon-0.1.0/olmon.egg-info/entry_points.txt +2 -0
- olmon-0.1.0/olmon.egg-info/requires.txt +3 -0
- olmon-0.1.0/olmon.egg-info/top_level.txt +1 -0
- olmon-0.1.0/pyproject.toml +47 -0
- olmon-0.1.0/setup.cfg +4 -0
olmon-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vlad
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
olmon-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: olmon
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight CLI to monitor Ollama models and usage
|
|
5
|
+
Author-email: Vlad Digori <digorivlad59@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/glemiu6/olmon
|
|
8
|
+
Project-URL: Repository, https://github.com/glemiu6/olmon
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/glemiu6/olmon/issues
|
|
10
|
+
Keywords: ollama,monitor,cli,llm,devops,developer-tools
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Requires-Python: >=3.13
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: mypy>=2.1.0
|
|
18
|
+
Requires-Dist: platformdirs>=4.10.0
|
|
19
|
+
Requires-Dist: rich>=15.0.0
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# olmon
|
|
23
|
+
|
|
24
|
+
A lightweight CLI to monitor your Ollama models and usage in real time.
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
$ olmon watch
|
|
28
|
+
Watching http://localhost:11434 every 2s โ Ctrl+C to stop
|
|
29
|
+
|
|
30
|
+
๐ข Ollama v0.30.8 โ 14 models installed, 2 running
|
|
31
|
+
|
|
32
|
+
โโโโโโโโโโโโโโโโโโโณโโโโโโโโโณโโโโโโโโโณโโโโโโโโโโโโโโโ
|
|
33
|
+
โ Name โ Size โ VRAM โ Expires At โ
|
|
34
|
+
โกโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฉ
|
|
35
|
+
โ llama3.2:latest โ 2.4 GB โ 2.4 GB โ 2026-06-23 โ
|
|
36
|
+
โ qwen2.5:7b โ 4.4 GB โ 4.4 GB โ 2026-06-23 โ
|
|
37
|
+
โโโโโโโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโโโโโโ
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
- ๐ **Real-time dashboard** โ live auto-refreshing view of running models
|
|
45
|
+
- ๐ **Model listing** โ browse all installed models with size, family, and quantization
|
|
46
|
+
- ๐ **Model inspection** โ full details on any installed model
|
|
47
|
+
- ๐ข **Status indicators** โ green / blue / red at a glance
|
|
48
|
+
- โ๏ธ **Configurable** โ set your API host and refresh interval
|
|
49
|
+
- ๐ชถ **Lightweight** โ minimal dependencies, works over SSH on headless servers
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Requirements
|
|
54
|
+
|
|
55
|
+
- Python 3.13+
|
|
56
|
+
- [Ollama](https://ollama.com) installed and running
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
### pip
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install olmon
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### curl (Linux / macOS)
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
curl -fsSL https://raw.githubusercontent.com/glemiu6/olmon/master/scripts/install.sh | sh
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### From source
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
git clone https://github.com/glemiu6/olmon.git
|
|
78
|
+
cd olmon
|
|
79
|
+
uv pip install -e .
|
|
80
|
+
```
|
|
81
|
+
### Windows
|
|
82
|
+
|
|
83
|
+
Native Windows binary is not currently supported.
|
|
84
|
+
Use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) and follow the Linux installation instructions.
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Usage
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
olmon status # quick health check
|
|
91
|
+
olmon models # list all installed models
|
|
92
|
+
olmon models --sort size # sort by size
|
|
93
|
+
olmon models --filter llama # filter by name or family
|
|
94
|
+
olmon inspect llama3:latest # full details on a model
|
|
95
|
+
olmon ps # show currently running models
|
|
96
|
+
olmon watch # live auto-refreshing dashboard
|
|
97
|
+
olmon watch --interval 5 # refresh every 5 seconds
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Global flags
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
olmon --host http://192.168.1.10:11434 status # connect to remote Ollama
|
|
104
|
+
olmon --version # print version
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Status Indicators
|
|
110
|
+
|
|
111
|
+
| Indicator | Meaning |
|
|
112
|
+
|-----------|---------|
|
|
113
|
+
| ๐ข Green | Models are loaded and running |
|
|
114
|
+
| ๐ต Blue | Ollama is idle, no models loaded |
|
|
115
|
+
| ๐ด Red | Ollama is offline or unreachable |
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Configuration
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
olmon init # create default config file
|
|
123
|
+
olmon config show # view current config
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Config is stored at `~/.config/olmon/config.json`:
|
|
127
|
+
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"host": "http://localhost:11434",
|
|
131
|
+
"interval": 2,
|
|
132
|
+
"no_color": false,
|
|
133
|
+
"default_sort": "name"
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Update & Uninstall
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
olmon update # update to latest version
|
|
143
|
+
olmon uninstall # remove olmon and config
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Why olmon?
|
|
149
|
+
|
|
150
|
+
Most Ollama monitoring tools are GUI or system tray apps. `olmon` is built for:
|
|
151
|
+
|
|
152
|
+
- **Headless Linux servers** โ no GUI required
|
|
153
|
+
- **Remote monitoring** โ works over SSH
|
|
154
|
+
- **Shell scripting** โ pipe-friendly with `--json` flag and exit codes
|
|
155
|
+
- **DevOps workflows** โ integrate into scripts and cron jobs
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Roadmap
|
|
160
|
+
|
|
161
|
+
See [ROADMAP.md](ROADMAP.md) for the full plan.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Contributing
|
|
166
|
+
|
|
167
|
+
Contributions are welcome. Feel free to open an issue or submit a pull request.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## License
|
|
172
|
+
|
|
173
|
+
MIT โ see [LICENSE](LICENSE) for details.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
Made with โค๏ธ by [Vlad Digori](https://github.com/glemiu6)
|
olmon-0.1.0/README.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# olmon
|
|
2
|
+
|
|
3
|
+
A lightweight CLI to monitor your Ollama models and usage in real time.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
$ olmon watch
|
|
7
|
+
Watching http://localhost:11434 every 2s โ Ctrl+C to stop
|
|
8
|
+
|
|
9
|
+
๐ข Ollama v0.30.8 โ 14 models installed, 2 running
|
|
10
|
+
|
|
11
|
+
โโโโโโโโโโโโโโโโโโโณโโโโโโโโโณโโโโโโโโโณโโโโโโโโโโโโโโโ
|
|
12
|
+
โ Name โ Size โ VRAM โ Expires At โ
|
|
13
|
+
โกโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฉ
|
|
14
|
+
โ llama3.2:latest โ 2.4 GB โ 2.4 GB โ 2026-06-23 โ
|
|
15
|
+
โ qwen2.5:7b โ 4.4 GB โ 4.4 GB โ 2026-06-23 โ
|
|
16
|
+
โโโโโโโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโโโโโโ
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
|
|
23
|
+
- ๐ **Real-time dashboard** โ live auto-refreshing view of running models
|
|
24
|
+
- ๐ **Model listing** โ browse all installed models with size, family, and quantization
|
|
25
|
+
- ๐ **Model inspection** โ full details on any installed model
|
|
26
|
+
- ๐ข **Status indicators** โ green / blue / red at a glance
|
|
27
|
+
- โ๏ธ **Configurable** โ set your API host and refresh interval
|
|
28
|
+
- ๐ชถ **Lightweight** โ minimal dependencies, works over SSH on headless servers
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Requirements
|
|
33
|
+
|
|
34
|
+
- Python 3.13+
|
|
35
|
+
- [Ollama](https://ollama.com) installed and running
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
### pip
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install olmon
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### curl (Linux / macOS)
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
curl -fsSL https://raw.githubusercontent.com/glemiu6/olmon/master/scripts/install.sh | sh
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### From source
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/glemiu6/olmon.git
|
|
57
|
+
cd olmon
|
|
58
|
+
uv pip install -e .
|
|
59
|
+
```
|
|
60
|
+
### Windows
|
|
61
|
+
|
|
62
|
+
Native Windows binary is not currently supported.
|
|
63
|
+
Use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) and follow the Linux installation instructions.
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Usage
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
olmon status # quick health check
|
|
70
|
+
olmon models # list all installed models
|
|
71
|
+
olmon models --sort size # sort by size
|
|
72
|
+
olmon models --filter llama # filter by name or family
|
|
73
|
+
olmon inspect llama3:latest # full details on a model
|
|
74
|
+
olmon ps # show currently running models
|
|
75
|
+
olmon watch # live auto-refreshing dashboard
|
|
76
|
+
olmon watch --interval 5 # refresh every 5 seconds
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Global flags
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
olmon --host http://192.168.1.10:11434 status # connect to remote Ollama
|
|
83
|
+
olmon --version # print version
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Status Indicators
|
|
89
|
+
|
|
90
|
+
| Indicator | Meaning |
|
|
91
|
+
|-----------|---------|
|
|
92
|
+
| ๐ข Green | Models are loaded and running |
|
|
93
|
+
| ๐ต Blue | Ollama is idle, no models loaded |
|
|
94
|
+
| ๐ด Red | Ollama is offline or unreachable |
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Configuration
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
olmon init # create default config file
|
|
102
|
+
olmon config show # view current config
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Config is stored at `~/.config/olmon/config.json`:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"host": "http://localhost:11434",
|
|
110
|
+
"interval": 2,
|
|
111
|
+
"no_color": false,
|
|
112
|
+
"default_sort": "name"
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Update & Uninstall
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
olmon update # update to latest version
|
|
122
|
+
olmon uninstall # remove olmon and config
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Why olmon?
|
|
128
|
+
|
|
129
|
+
Most Ollama monitoring tools are GUI or system tray apps. `olmon` is built for:
|
|
130
|
+
|
|
131
|
+
- **Headless Linux servers** โ no GUI required
|
|
132
|
+
- **Remote monitoring** โ works over SSH
|
|
133
|
+
- **Shell scripting** โ pipe-friendly with `--json` flag and exit codes
|
|
134
|
+
- **DevOps workflows** โ integrate into scripts and cron jobs
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Roadmap
|
|
139
|
+
|
|
140
|
+
See [ROADMAP.md](ROADMAP.md) for the full plan.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Contributing
|
|
145
|
+
|
|
146
|
+
Contributions are welcome. Feel free to open an issue or submit a pull request.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## License
|
|
151
|
+
|
|
152
|
+
MIT โ see [LICENSE](LICENSE) for details.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
Made with โค๏ธ by [Vlad Digori](https://github.com/glemiu6)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import urllib.request
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def get_version(host: str) -> dict | None:
|
|
6
|
+
"""
|
|
7
|
+
Fetches the version information of the Ollama API
|
|
8
|
+
"""
|
|
9
|
+
try:
|
|
10
|
+
with urllib.request.urlopen(f"{host}/api/version") as r:
|
|
11
|
+
return json.loads(r.read().decode("utf-8"))
|
|
12
|
+
except Exception:
|
|
13
|
+
return None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_models(host: str) -> dict | None:
|
|
17
|
+
"""
|
|
18
|
+
Fetches the list of the local models available via Ollama API
|
|
19
|
+
"""
|
|
20
|
+
try:
|
|
21
|
+
with urllib.request.urlopen(f"{host}/api/tags") as r:
|
|
22
|
+
return json.loads(r.read().decode("utf-8"))
|
|
23
|
+
except Exception:
|
|
24
|
+
return None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_running(host: str) -> dict | None:
|
|
28
|
+
"""
|
|
29
|
+
Fetches the running models running via Ollama API
|
|
30
|
+
"""
|
|
31
|
+
try:
|
|
32
|
+
with urllib.request.urlopen(f"{host}/api/ps") as r:
|
|
33
|
+
return json.loads(r.read().decode("utf-8"))
|
|
34
|
+
except Exception:
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_model_info(host: str, model_name: str) -> dict | None:
|
|
39
|
+
"""
|
|
40
|
+
Fetches the information of a specific model via Ollama API
|
|
41
|
+
"""
|
|
42
|
+
try:
|
|
43
|
+
data = json.dumps({"model": model_name}).encode("utf-8")
|
|
44
|
+
|
|
45
|
+
req = urllib.request.Request(
|
|
46
|
+
url=f"{host}/api/show",
|
|
47
|
+
data=data,
|
|
48
|
+
headers={"Content-Type": "application/json"},
|
|
49
|
+
method="POST",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
with urllib.request.urlopen(req) as result:
|
|
53
|
+
return json.loads(result.read().decode("utf-8"))
|
|
54
|
+
except Exception:
|
|
55
|
+
return None
|
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from platformdirs import user_config_dir
|
|
4
|
+
|
|
5
|
+
from olmon.config import OlmonConfig
|
|
6
|
+
|
|
7
|
+
APP_NAME = "olmon"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_default_config_path() -> str:
|
|
11
|
+
config_dir = user_config_dir(APP_NAME)
|
|
12
|
+
os.makedirs(config_dir, exist_ok=True)
|
|
13
|
+
return os.path.join(config_dir, "config.json")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def init_config() -> None:
|
|
17
|
+
paths = get_default_config_path()
|
|
18
|
+
if os.path.exists(paths):
|
|
19
|
+
choice = input("Config file already exists. Overwrite? (y/n): ").lower()
|
|
20
|
+
if choice != "y":
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
host = input("Ollama host [http://localhost:11434]: ") or "http://localhost:11434"
|
|
24
|
+
interval = input("Update interval [2]: ") or 2
|
|
25
|
+
no_color = input("Disable color [False]: ") or False
|
|
26
|
+
default_sort = input("Default sort [name]: ") or "name"
|
|
27
|
+
|
|
28
|
+
cfg = OlmonConfig(host, int(interval), bool(no_color), default_sort)
|
|
29
|
+
cfg.save()
|
|
30
|
+
print("Config file created at", paths)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from olmon.config import OlmonConfig
|
|
2
|
+
from olmon.display import print_error, print_offline
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def models_command(host: str | None = None, sort: str | None = None, filters: str | None = None):
|
|
6
|
+
from olmon.client import get_models
|
|
7
|
+
from olmon.display import print_models_table
|
|
8
|
+
|
|
9
|
+
config = OlmonConfig.load()
|
|
10
|
+
resolved_host = host or config.host
|
|
11
|
+
raw = get_models(resolved_host)
|
|
12
|
+
if raw is None:
|
|
13
|
+
print_offline(resolved_host)
|
|
14
|
+
return
|
|
15
|
+
if "models" not in raw:
|
|
16
|
+
print_error("unexpected response from Ollama API")
|
|
17
|
+
models = raw["models"]
|
|
18
|
+
|
|
19
|
+
if filters:
|
|
20
|
+
models = [m for m in models if filters.lower() in m["name"].lower()]
|
|
21
|
+
sort_key = sort or config.default_sort
|
|
22
|
+
if sort_key == "size":
|
|
23
|
+
models = sorted(models, key=lambda x: x["size"], reverse=True)
|
|
24
|
+
elif sort_key == "date":
|
|
25
|
+
models = sorted(models, key=lambda x: x["modified_at"], reverse=True)
|
|
26
|
+
else:
|
|
27
|
+
models = sorted(models, key=lambda x: x["name"])
|
|
28
|
+
|
|
29
|
+
print_models_table(models)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def inspect_command(host: str | None = None, name: str | None = None):
|
|
33
|
+
from olmon.client import get_model_info
|
|
34
|
+
from olmon.display import print_inspect
|
|
35
|
+
|
|
36
|
+
config = OlmonConfig.load()
|
|
37
|
+
resolved_host = host or config.host
|
|
38
|
+
|
|
39
|
+
if name is None: # โ guard against None before passing to client
|
|
40
|
+
print_error("Model name is required")
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
data = get_model_info(resolved_host, name)
|
|
44
|
+
|
|
45
|
+
if data is None:
|
|
46
|
+
print_error(f"Could not fetch info for '{name}'")
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
print_inspect(data)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from olmon.client import get_running
|
|
2
|
+
from olmon.config import OlmonConfig
|
|
3
|
+
from olmon.display import print_error, print_offline, print_ps_table
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def ps_command(host: str | None = None):
|
|
7
|
+
config = OlmonConfig.load()
|
|
8
|
+
resolved_host = host or config.host
|
|
9
|
+
raw = get_running(resolved_host)
|
|
10
|
+
if raw is None:
|
|
11
|
+
print_offline(resolved_host)
|
|
12
|
+
return
|
|
13
|
+
models = raw.get("models", [])
|
|
14
|
+
|
|
15
|
+
if not models:
|
|
16
|
+
print_error("No models are running")
|
|
17
|
+
return
|
|
18
|
+
print_ps_table(models)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from olmon import client, display
|
|
2
|
+
from olmon.config import OlmonConfig
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def status_command(host: str | None = None):
|
|
6
|
+
config = OlmonConfig.load()
|
|
7
|
+
resolved_host = host or config.host
|
|
8
|
+
|
|
9
|
+
version_data = client.get_version(resolved_host)
|
|
10
|
+
|
|
11
|
+
if version_data is None:
|
|
12
|
+
display.print_offline(resolved_host)
|
|
13
|
+
return
|
|
14
|
+
|
|
15
|
+
models_data = client.get_models(resolved_host)
|
|
16
|
+
running_data = client.get_running(resolved_host)
|
|
17
|
+
|
|
18
|
+
total = len(models_data["models"]) if models_data else 0
|
|
19
|
+
running = len(running_data["models"]) if running_data else 0
|
|
20
|
+
|
|
21
|
+
display.print_status(
|
|
22
|
+
version=version_data["version"],
|
|
23
|
+
host=resolved_host,
|
|
24
|
+
total_models=total,
|
|
25
|
+
running_models=running,
|
|
26
|
+
)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# olmon/commands/uninstall.py
|
|
2
|
+
def uninstall() -> None:
|
|
3
|
+
"""Remove olmon completely = pip, binary, and config."""
|
|
4
|
+
import os
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
|
|
11
|
+
console = Console()
|
|
12
|
+
|
|
13
|
+
console.print("[bold red]Uninstalling olmon...[/bold red]")
|
|
14
|
+
method = _detect_install_method()
|
|
15
|
+
|
|
16
|
+
# Remove config file
|
|
17
|
+
config_path = os.path.expanduser("~/.config/olmon")
|
|
18
|
+
if os.path.exists(config_path):
|
|
19
|
+
shutil.rmtree(config_path)
|
|
20
|
+
console.print("[green]Removed ~/.config/olmon[/green]")
|
|
21
|
+
|
|
22
|
+
if sys.platform == "win32":
|
|
23
|
+
windows_path = os.path.join(os.environ.get("LOCALAPPDATA", ""), "komit")
|
|
24
|
+
if os.path.exists(windows_path):
|
|
25
|
+
shutil.rmtree(windows_path)
|
|
26
|
+
console.print(f"[green]Removed {windows_path}[/green]")
|
|
27
|
+
try:
|
|
28
|
+
import winreg
|
|
29
|
+
|
|
30
|
+
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Environment", 0, winreg.KEY_ALL_ACCESS)
|
|
31
|
+
current_path, _ = winreg.QueryValueEx(key, "Path")
|
|
32
|
+
new_path = ";".join(p for p in current_path.split(";") if "komit" not in p)
|
|
33
|
+
winreg.SetValueEx(key, "Path", 0, winreg.REG_EXPAND_SZ, new_path)
|
|
34
|
+
winreg.CloseKey(key)
|
|
35
|
+
console.print("[green]Removed from PATH[/green]")
|
|
36
|
+
except Exception as e:
|
|
37
|
+
console.print(f"[yellow]Could not remove from PATH: {e}[/yellow]")
|
|
38
|
+
# Remove binary if installed via curl
|
|
39
|
+
all_paths = ["~/.local/bin/olmon", "/usr/local/bin/olmon", "/opt/homebrew/bin/olmon"]
|
|
40
|
+
|
|
41
|
+
for p in all_paths:
|
|
42
|
+
binary = os.path.expanduser(p)
|
|
43
|
+
if os.path.exists(binary):
|
|
44
|
+
try:
|
|
45
|
+
subprocess.run(["sudo", "rm", "-f", binary], check=True)
|
|
46
|
+
console.print(f"[green]Removed {binary}[/green]")
|
|
47
|
+
except subprocess.CalledProcessError:
|
|
48
|
+
console.print(f"[yellow]Failed to remove {binary} - try: sudo rm {binary}[/yellow]")
|
|
49
|
+
# remove for pip
|
|
50
|
+
if method == "pip":
|
|
51
|
+
try:
|
|
52
|
+
subprocess.run([sys.executable, "-m", "pip", "uninstall", "olmon", "-y"], check=False)
|
|
53
|
+
console.print("[green]Removed pip package[/green]")
|
|
54
|
+
except subprocess.CalledProcessError:
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
console.print("\n[bold red]komit uninstalled. Goodbye![/bold red]")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _detect_install_method() -> str:
|
|
61
|
+
import importlib.metadata
|
|
62
|
+
import sys
|
|
63
|
+
|
|
64
|
+
# 1. PyInstaller / bundled binary
|
|
65
|
+
if getattr(sys, "frozen", False):
|
|
66
|
+
return "binary"
|
|
67
|
+
|
|
68
|
+
# 2. pip install (most reliable check)
|
|
69
|
+
try:
|
|
70
|
+
importlib.metadata.version("olmon")
|
|
71
|
+
return "pip"
|
|
72
|
+
except importlib.metadata.PackageNotFoundError:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
# 3. fallback
|
|
76
|
+
return "binary"
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# olmon/commands/update.py
|
|
2
|
+
import importlib.metadata
|
|
3
|
+
import json
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
|
|
9
|
+
from olmon import __version__
|
|
10
|
+
|
|
11
|
+
console = Console()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _get_latest_version() -> str | None:
|
|
15
|
+
import urllib.request
|
|
16
|
+
|
|
17
|
+
for url, extract in [
|
|
18
|
+
(
|
|
19
|
+
"https://api.github.com/repos/glemiu6/olmon/releases/latest",
|
|
20
|
+
lambda d: d["tag_name"].lstrip("v"),
|
|
21
|
+
),
|
|
22
|
+
(
|
|
23
|
+
"https://pypi.org/pypi/olmon/json",
|
|
24
|
+
lambda d: d["info"]["version"],
|
|
25
|
+
),
|
|
26
|
+
]:
|
|
27
|
+
try:
|
|
28
|
+
with urllib.request.urlopen(url, timeout=2) as r:
|
|
29
|
+
return extract(json.loads(r.read().decode("utf-8")))
|
|
30
|
+
except Exception:
|
|
31
|
+
continue
|
|
32
|
+
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def check_for_update() -> None:
|
|
37
|
+
try:
|
|
38
|
+
latest_version = _get_latest_version()
|
|
39
|
+
if latest_version is None:
|
|
40
|
+
return
|
|
41
|
+
if latest_version != __version__:
|
|
42
|
+
console.print(
|
|
43
|
+
f"\n[bold yellow]!! New version available: v{latest_version}[/bold yellow] [dim](you have v{__version__})[/dim]" # noqa: E501
|
|
44
|
+
)
|
|
45
|
+
console.print("[dim] Use: olmon update[/dim]\n")
|
|
46
|
+
except Exception:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _detect_install_method() -> str:
|
|
51
|
+
if getattr(sys, "frozen", False):
|
|
52
|
+
return "binary"
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
importlib.metadata.version("olmon")
|
|
56
|
+
return "pip"
|
|
57
|
+
except importlib.metadata.PackageNotFoundError:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
return "binary"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def update():
|
|
64
|
+
try:
|
|
65
|
+
import importlib.util
|
|
66
|
+
|
|
67
|
+
latest = _get_latest_version()
|
|
68
|
+
if latest is None:
|
|
69
|
+
return
|
|
70
|
+
if latest == __version__:
|
|
71
|
+
console.print("[bold green]Already up to date[/bold green]")
|
|
72
|
+
return
|
|
73
|
+
console.print(
|
|
74
|
+
f"\n[bold yellow]!! New version available: v{latest}[/bold yellow] [dim](you have v{__version__})[/dim]" # noqa: E501
|
|
75
|
+
)
|
|
76
|
+
console.print("[dim] Use: olmon update[/dim]\n")
|
|
77
|
+
method = _detect_install_method()
|
|
78
|
+
match method:
|
|
79
|
+
case "pip":
|
|
80
|
+
console.print("Detected pip installation, updating...")
|
|
81
|
+
if importlib.util.find_spec("pip") is None:
|
|
82
|
+
console.print("pip not found, installing...")
|
|
83
|
+
subprocess.run([sys.executable, "-m", "ensurepip", "--upgrade"], check=True)
|
|
84
|
+
subprocess.run(
|
|
85
|
+
[sys.executable, "-m", "pip", "install", "olmon", "--upgrade"], check=True
|
|
86
|
+
)
|
|
87
|
+
case "binary":
|
|
88
|
+
console.print("Detected binary installation, updating...")
|
|
89
|
+
result = subprocess.run(
|
|
90
|
+
[
|
|
91
|
+
"curl",
|
|
92
|
+
"-fsSL",
|
|
93
|
+
"https://raw.githubusercontent.com/glemiu6/olmon/master/scripts/install.sh",
|
|
94
|
+
],
|
|
95
|
+
capture_output=True,
|
|
96
|
+
check=True,
|
|
97
|
+
)
|
|
98
|
+
subprocess.run(["bash"], input=result.stdout, check=True)
|
|
99
|
+
case _:
|
|
100
|
+
console.print("Unknown installation method, please update manually")
|
|
101
|
+
console.print(" [cyan]pip install olmon --upgrade[/cyan]")
|
|
102
|
+
console.print(
|
|
103
|
+
" [cyan]curl -fsSL https://raw.githubusercontent.com/glemiu6/olmon/master/scripts/install.sh | sh[/cyan]" # noqa: E501
|
|
104
|
+
) # noqa: E501
|
|
105
|
+
console.print(
|
|
106
|
+
"[bold green]Update complete! Restart your terminal to use the latest version.[/bold green]" # noqa: E501
|
|
107
|
+
)
|
|
108
|
+
except Exception as e:
|
|
109
|
+
console.print(f"[bold red]Failed to update: {e}[/bold red]")
|
|
110
|
+
sys.exit(1)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
from rich.live import Live
|
|
5
|
+
from rich.table import Table
|
|
6
|
+
|
|
7
|
+
from olmon.client import get_models, get_running, get_version
|
|
8
|
+
from olmon.config import OlmonConfig
|
|
9
|
+
from olmon.display import format_size
|
|
10
|
+
|
|
11
|
+
console = Console()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _build_dashboard(resolved_host: str):
|
|
15
|
+
version_data = get_version(resolved_host)
|
|
16
|
+
models_data = get_models(resolved_host)
|
|
17
|
+
running_data = get_running(resolved_host)
|
|
18
|
+
|
|
19
|
+
if version_data is None:
|
|
20
|
+
table = Table(title="๐ด Ollama Unreachable")
|
|
21
|
+
return table
|
|
22
|
+
running = running_data.get("models", []) if running_data else []
|
|
23
|
+
total = len(models_data.get("models", [])) if models_data else 0
|
|
24
|
+
|
|
25
|
+
indicator = "๐ข" if running else "๐ต"
|
|
26
|
+
table = Table(
|
|
27
|
+
title=f"{indicator} Ollama v{version_data['version']} - {total} models installed, {len(running)} running" # noqa: E501
|
|
28
|
+
)
|
|
29
|
+
table.add_column("Name")
|
|
30
|
+
table.add_column("Size")
|
|
31
|
+
table.add_column("VRAM")
|
|
32
|
+
table.add_column("Expires At")
|
|
33
|
+
|
|
34
|
+
for model in running:
|
|
35
|
+
table.add_row(
|
|
36
|
+
model.get("name", "N/A"),
|
|
37
|
+
format_size(model.get("size", 0)),
|
|
38
|
+
format_size(model.get("size_vram", 0)),
|
|
39
|
+
model.get("expires_at", "N/A"),
|
|
40
|
+
)
|
|
41
|
+
return table
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def watch_command(host: str | None = None, interval: int | None = None):
|
|
45
|
+
config = OlmonConfig.load()
|
|
46
|
+
resolved_host = host or config.host
|
|
47
|
+
resolved_interval = interval or config.interval
|
|
48
|
+
|
|
49
|
+
console.print(
|
|
50
|
+
f"[dim]Watching {resolved_host} every {resolved_interval}s - Ctrl+C to stop[/dim]"
|
|
51
|
+
) # noqa: E501
|
|
52
|
+
try:
|
|
53
|
+
with Live(refresh_per_second=1) as live:
|
|
54
|
+
while True:
|
|
55
|
+
live.update(_build_dashboard(resolved_host))
|
|
56
|
+
time.sleep(resolved_interval)
|
|
57
|
+
except KeyboardInterrupt:
|
|
58
|
+
console.print("\n[dim]Stopping watch...[/dim]")
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# olmon/config.py
|
|
2
|
+
import json
|
|
3
|
+
from dataclasses import asdict, dataclass
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from platformdirs import user_config_dir
|
|
7
|
+
|
|
8
|
+
CONFIG_PATH = Path(user_config_dir("olmon")) / "config.json"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class OlmonConfig:
|
|
13
|
+
host: str = "http://localhost:11434"
|
|
14
|
+
interval: int = 2
|
|
15
|
+
no_color: bool = False
|
|
16
|
+
default_sort: str = "name"
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def load(cls):
|
|
20
|
+
if not CONFIG_PATH.exists():
|
|
21
|
+
return cls()
|
|
22
|
+
with open(CONFIG_PATH, "r") as f:
|
|
23
|
+
data = json.load(f)
|
|
24
|
+
|
|
25
|
+
return cls(**{k: v for k, v in data.items() if k in cls.__dataclass_fields__})
|
|
26
|
+
|
|
27
|
+
def save(self):
|
|
28
|
+
CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
29
|
+
with open(CONFIG_PATH, "w") as f:
|
|
30
|
+
json.dump(asdict(self), f, indent=2)
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from rich.console import Console
|
|
2
|
+
from rich.panel import Panel
|
|
3
|
+
from rich.table import Table
|
|
4
|
+
|
|
5
|
+
console = Console()
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def print_status(version: str, host: str, total_models: int, running_models: int) -> None:
|
|
9
|
+
status_color = "green" if running_models > 0 else "blue"
|
|
10
|
+
indicator = "๐ข" if running_models > 0 else "๐ต"
|
|
11
|
+
|
|
12
|
+
content = (
|
|
13
|
+
f"[bold]Version[/bold] {version}\n"
|
|
14
|
+
f"[bold]Host[/bold] {host}\n"
|
|
15
|
+
f"[bold]Models[/bold] {total_models} installed, {running_models} running\n"
|
|
16
|
+
)
|
|
17
|
+
console.print(Panel(content, title=f"{indicator} Ollama is running", border_style=status_color))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def print_models_table(models) -> None:
|
|
21
|
+
table = Table(title="Models")
|
|
22
|
+
table.add_column("Name")
|
|
23
|
+
table.add_column("Size")
|
|
24
|
+
table.add_column("Params")
|
|
25
|
+
table.add_column("Quantization")
|
|
26
|
+
table.add_column("Format")
|
|
27
|
+
for model in models:
|
|
28
|
+
table.add_row(
|
|
29
|
+
model["name"],
|
|
30
|
+
format_size(model["size"]),
|
|
31
|
+
model["details"].get("parameter_size", "N/A"),
|
|
32
|
+
model["details"].get("quantization_level", "N/A"),
|
|
33
|
+
model["details"].get("format", "N/A"),
|
|
34
|
+
)
|
|
35
|
+
console.print(table)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def print_ps_table(processes) -> None:
|
|
39
|
+
table = Table(title="Running Models")
|
|
40
|
+
table.add_column("Name")
|
|
41
|
+
table.add_column("Size")
|
|
42
|
+
table.add_column("VRAM")
|
|
43
|
+
table.add_column("Params")
|
|
44
|
+
table.add_column("Context Length")
|
|
45
|
+
table.add_column("Family")
|
|
46
|
+
table.add_column("Quantization")
|
|
47
|
+
table.add_column("Expires At")
|
|
48
|
+
|
|
49
|
+
for model in processes:
|
|
50
|
+
table.add_row(
|
|
51
|
+
model.get("name", "N/A"),
|
|
52
|
+
format_size(model.get("size", 0)),
|
|
53
|
+
format_size(model.get("size_vram", 0)),
|
|
54
|
+
model.get("details", {}).get("parameter_size", "N/A"),
|
|
55
|
+
str(model.get("context_length", 0)),
|
|
56
|
+
model.get("details", {}).get("family", "N/A"),
|
|
57
|
+
model.get("details", {}).get("quantization_level", "N/A"),
|
|
58
|
+
model.get("expires_at", "N/A"),
|
|
59
|
+
)
|
|
60
|
+
console.print(table)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def print_error(msg: str) -> None:
|
|
64
|
+
console.print(f"[bold red]๐ด Error:[/bold red] {msg}")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def print_offline(host: str) -> None:
|
|
68
|
+
status_color = "red"
|
|
69
|
+
indicator = "๐ด"
|
|
70
|
+
content = (
|
|
71
|
+
f"[bold]Host[/bold] {host}\n\n"
|
|
72
|
+
f"Make sure Ollama is running:\n"
|
|
73
|
+
f"[bold yellow]ollama serve[/bold yellow]\n"
|
|
74
|
+
)
|
|
75
|
+
console.print(
|
|
76
|
+
Panel(content, title=f"{indicator} Ollama is unreachable", border_style=status_color)
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def print_inspect(data: dict):
|
|
81
|
+
details = data.get("details", {})
|
|
82
|
+
model_info = data.get("model_info", {})
|
|
83
|
+
capabilities = data.get("capabilities", [])
|
|
84
|
+
|
|
85
|
+
# parse parameters string into a dict
|
|
86
|
+
# "temperature 0.7\nnum_ctx 2048" โ {"temperature": "0.7", "num_ctx": "2048"}
|
|
87
|
+
raw_params = data.get("parameters", "")
|
|
88
|
+
params = {}
|
|
89
|
+
for line in raw_params.splitlines():
|
|
90
|
+
parts = line.split(maxsplit=1)
|
|
91
|
+
if len(parts) == 2:
|
|
92
|
+
params[parts[0]] = parts[1]
|
|
93
|
+
|
|
94
|
+
# clean up modified_at
|
|
95
|
+
modified = data.get("modified_at", "โ").split("T")[0]
|
|
96
|
+
|
|
97
|
+
# find context_length from model_info (key ends with .context_length)
|
|
98
|
+
context = next((str(v) for k, v in model_info.items() if k.endswith(".context_length")), "โ")
|
|
99
|
+
embedding = next(
|
|
100
|
+
(str(v) for k, v in model_info.items() if k.endswith(".embedding_length")), "โ"
|
|
101
|
+
)
|
|
102
|
+
blocks = next((str(v) for k, v in model_info.items() if k.endswith(".block_count")), "โ")
|
|
103
|
+
|
|
104
|
+
# build the panel content
|
|
105
|
+
content = (
|
|
106
|
+
f"[bold]Family[/bold] {details.get('family', 'โ')}\n"
|
|
107
|
+
f"[bold]Format[/bold] {details.get('format', 'โ')}\n"
|
|
108
|
+
f"[bold]Parameters[/bold] {details.get('parameter_size', 'โ')}\n"
|
|
109
|
+
f"[bold]Quantization[/bold] {details.get('quantization_level', 'โ')}\n"
|
|
110
|
+
f"[bold]Modified[/bold] {modified}\n"
|
|
111
|
+
f"[bold]Capabilities[/bold] {', '.join(capabilities) if capabilities else 'โ'}\n"
|
|
112
|
+
f"\n"
|
|
113
|
+
f"[bold]Context[/bold] {context} tokens\n"
|
|
114
|
+
f"[bold]Embedding[/bold] {embedding}\n"
|
|
115
|
+
f"[bold]Blocks[/bold] {blocks}\n"
|
|
116
|
+
f"[bold]Temperature[/bold] {params.get('temperature', 'โ')}\n"
|
|
117
|
+
f"[bold]num_ctx[/bold] {params.get('num_ctx', 'โ')}\n"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
console.print(Panel(content, title=f"[bold]{details.get('family', 'Model')}[/bold]"))
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def format_size(bts: int) -> str:
|
|
124
|
+
gb = bts / (1024**3)
|
|
125
|
+
if gb >= 1:
|
|
126
|
+
return f"{gb:.1f} GB"
|
|
127
|
+
mb = bts / (1024**2)
|
|
128
|
+
return f"{mb:.1f} MB"
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# olmon/main.py
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from olmon import __version__
|
|
7
|
+
from olmon.commands.init import init_config
|
|
8
|
+
from olmon.commands.models import inspect_command, models_command
|
|
9
|
+
from olmon.commands.ps import ps_command
|
|
10
|
+
from olmon.commands.status import status_command
|
|
11
|
+
from olmon.commands.uninstall import uninstall
|
|
12
|
+
from olmon.commands.update import check_for_update, update
|
|
13
|
+
from olmon.commands.watch import watch_command
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def parse_args(argv=None):
|
|
17
|
+
parser = argparse.ArgumentParser(
|
|
18
|
+
prog="olmon",
|
|
19
|
+
description="A lightweight CLI to monitor Ollama models and usage",
|
|
20
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
21
|
+
epilog="""
|
|
22
|
+
Examples:
|
|
23
|
+
$ olmon init
|
|
24
|
+
$ olmon uninstall
|
|
25
|
+
$ olmon update
|
|
26
|
+
$ olmon status
|
|
27
|
+
$ olmon models --sort size
|
|
28
|
+
$ olmon ps
|
|
29
|
+
$ olmon watch --interval 3
|
|
30
|
+
$ olmon inspect llama3:latest
|
|
31
|
+
""",
|
|
32
|
+
)
|
|
33
|
+
parser.add_argument("--version", action="version", version=f"{__version__}")
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
"--host", "-H", default=None, metavar="<url>", help="Ollama API URL (overrides config)"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
subparsers = parser.add_subparsers(title="Commands", dest="command", metavar="<command>")
|
|
39
|
+
subparsers.add_parser("init", help="Create default config file")
|
|
40
|
+
subparsers.add_parser("uninstall", help="Uninstall olmon")
|
|
41
|
+
subparsers.add_parser("update", help="Update olmon to the latest version")
|
|
42
|
+
# status
|
|
43
|
+
subparsers.add_parser("status", help="Show Ollama status")
|
|
44
|
+
|
|
45
|
+
# models
|
|
46
|
+
models_parser = subparsers.add_parser("models", help="List installed models")
|
|
47
|
+
models_parser.add_argument(
|
|
48
|
+
"--sort", "-s", default=None, metavar="<field>", help="Sort by: name, size, date"
|
|
49
|
+
)
|
|
50
|
+
models_parser.add_argument(
|
|
51
|
+
"--filter", "-f", default=None, metavar="<query>", help="Filter by name or family"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# inspect
|
|
55
|
+
inspect_parser = subparsers.add_parser("inspect", help="Show details of a model")
|
|
56
|
+
inspect_parser.add_argument("name", metavar="<name>", help="Model name")
|
|
57
|
+
|
|
58
|
+
# ps
|
|
59
|
+
subparsers.add_parser("ps", help="Show running models")
|
|
60
|
+
|
|
61
|
+
# watch
|
|
62
|
+
watch_parser = subparsers.add_parser("watch", help="Live dashboard")
|
|
63
|
+
watch_parser.add_argument(
|
|
64
|
+
"--interval",
|
|
65
|
+
"-i",
|
|
66
|
+
default=None,
|
|
67
|
+
type=int,
|
|
68
|
+
metavar="<seconds>",
|
|
69
|
+
help="Refresh rate in seconds",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
return parser.parse_args(argv)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def app():
|
|
76
|
+
|
|
77
|
+
args = parse_args()
|
|
78
|
+
check_for_update()
|
|
79
|
+
|
|
80
|
+
match args.command:
|
|
81
|
+
case "status":
|
|
82
|
+
status_command(args.host)
|
|
83
|
+
case "models":
|
|
84
|
+
models_command(args.host, args.sort, args.filter)
|
|
85
|
+
case "inspect":
|
|
86
|
+
inspect_command(args.host, args.name)
|
|
87
|
+
case "ps":
|
|
88
|
+
ps_command(args.host)
|
|
89
|
+
case "watch":
|
|
90
|
+
watch_command(args.host, args.interval)
|
|
91
|
+
case "init":
|
|
92
|
+
init_config()
|
|
93
|
+
case "uninstall":
|
|
94
|
+
uninstall()
|
|
95
|
+
case "update":
|
|
96
|
+
update()
|
|
97
|
+
case _:
|
|
98
|
+
parse_args(["--help"])
|
|
99
|
+
sys.exit(0)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
if __name__ == "__main__":
|
|
103
|
+
app()
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: olmon
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight CLI to monitor Ollama models and usage
|
|
5
|
+
Author-email: Vlad Digori <digorivlad59@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/glemiu6/olmon
|
|
8
|
+
Project-URL: Repository, https://github.com/glemiu6/olmon
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/glemiu6/olmon/issues
|
|
10
|
+
Keywords: ollama,monitor,cli,llm,devops,developer-tools
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Requires-Python: >=3.13
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: mypy>=2.1.0
|
|
18
|
+
Requires-Dist: platformdirs>=4.10.0
|
|
19
|
+
Requires-Dist: rich>=15.0.0
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# olmon
|
|
23
|
+
|
|
24
|
+
A lightweight CLI to monitor your Ollama models and usage in real time.
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
$ olmon watch
|
|
28
|
+
Watching http://localhost:11434 every 2s โ Ctrl+C to stop
|
|
29
|
+
|
|
30
|
+
๐ข Ollama v0.30.8 โ 14 models installed, 2 running
|
|
31
|
+
|
|
32
|
+
โโโโโโโโโโโโโโโโโโโณโโโโโโโโโณโโโโโโโโโณโโโโโโโโโโโโโโโ
|
|
33
|
+
โ Name โ Size โ VRAM โ Expires At โ
|
|
34
|
+
โกโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฉ
|
|
35
|
+
โ llama3.2:latest โ 2.4 GB โ 2.4 GB โ 2026-06-23 โ
|
|
36
|
+
โ qwen2.5:7b โ 4.4 GB โ 4.4 GB โ 2026-06-23 โ
|
|
37
|
+
โโโโโโโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโโโโโโ
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
- ๐ **Real-time dashboard** โ live auto-refreshing view of running models
|
|
45
|
+
- ๐ **Model listing** โ browse all installed models with size, family, and quantization
|
|
46
|
+
- ๐ **Model inspection** โ full details on any installed model
|
|
47
|
+
- ๐ข **Status indicators** โ green / blue / red at a glance
|
|
48
|
+
- โ๏ธ **Configurable** โ set your API host and refresh interval
|
|
49
|
+
- ๐ชถ **Lightweight** โ minimal dependencies, works over SSH on headless servers
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Requirements
|
|
54
|
+
|
|
55
|
+
- Python 3.13+
|
|
56
|
+
- [Ollama](https://ollama.com) installed and running
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
### pip
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install olmon
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### curl (Linux / macOS)
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
curl -fsSL https://raw.githubusercontent.com/glemiu6/olmon/master/scripts/install.sh | sh
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### From source
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
git clone https://github.com/glemiu6/olmon.git
|
|
78
|
+
cd olmon
|
|
79
|
+
uv pip install -e .
|
|
80
|
+
```
|
|
81
|
+
### Windows
|
|
82
|
+
|
|
83
|
+
Native Windows binary is not currently supported.
|
|
84
|
+
Use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) and follow the Linux installation instructions.
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Usage
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
olmon status # quick health check
|
|
91
|
+
olmon models # list all installed models
|
|
92
|
+
olmon models --sort size # sort by size
|
|
93
|
+
olmon models --filter llama # filter by name or family
|
|
94
|
+
olmon inspect llama3:latest # full details on a model
|
|
95
|
+
olmon ps # show currently running models
|
|
96
|
+
olmon watch # live auto-refreshing dashboard
|
|
97
|
+
olmon watch --interval 5 # refresh every 5 seconds
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Global flags
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
olmon --host http://192.168.1.10:11434 status # connect to remote Ollama
|
|
104
|
+
olmon --version # print version
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Status Indicators
|
|
110
|
+
|
|
111
|
+
| Indicator | Meaning |
|
|
112
|
+
|-----------|---------|
|
|
113
|
+
| ๐ข Green | Models are loaded and running |
|
|
114
|
+
| ๐ต Blue | Ollama is idle, no models loaded |
|
|
115
|
+
| ๐ด Red | Ollama is offline or unreachable |
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Configuration
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
olmon init # create default config file
|
|
123
|
+
olmon config show # view current config
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Config is stored at `~/.config/olmon/config.json`:
|
|
127
|
+
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"host": "http://localhost:11434",
|
|
131
|
+
"interval": 2,
|
|
132
|
+
"no_color": false,
|
|
133
|
+
"default_sort": "name"
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Update & Uninstall
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
olmon update # update to latest version
|
|
143
|
+
olmon uninstall # remove olmon and config
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Why olmon?
|
|
149
|
+
|
|
150
|
+
Most Ollama monitoring tools are GUI or system tray apps. `olmon` is built for:
|
|
151
|
+
|
|
152
|
+
- **Headless Linux servers** โ no GUI required
|
|
153
|
+
- **Remote monitoring** โ works over SSH
|
|
154
|
+
- **Shell scripting** โ pipe-friendly with `--json` flag and exit codes
|
|
155
|
+
- **DevOps workflows** โ integrate into scripts and cron jobs
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Roadmap
|
|
160
|
+
|
|
161
|
+
See [ROADMAP.md](ROADMAP.md) for the full plan.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Contributing
|
|
166
|
+
|
|
167
|
+
Contributions are welcome. Feel free to open an issue or submit a pull request.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## License
|
|
172
|
+
|
|
173
|
+
MIT โ see [LICENSE](LICENSE) for details.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
Made with โค๏ธ by [Vlad Digori](https://github.com/glemiu6)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
olmon/__init__.py
|
|
5
|
+
olmon/__main__.py
|
|
6
|
+
olmon/client.py
|
|
7
|
+
olmon/config.py
|
|
8
|
+
olmon/display.py
|
|
9
|
+
olmon/main.py
|
|
10
|
+
olmon.egg-info/PKG-INFO
|
|
11
|
+
olmon.egg-info/SOURCES.txt
|
|
12
|
+
olmon.egg-info/dependency_links.txt
|
|
13
|
+
olmon.egg-info/entry_points.txt
|
|
14
|
+
olmon.egg-info/requires.txt
|
|
15
|
+
olmon.egg-info/top_level.txt
|
|
16
|
+
olmon/commands/__init__.py
|
|
17
|
+
olmon/commands/init.py
|
|
18
|
+
olmon/commands/models.py
|
|
19
|
+
olmon/commands/ps.py
|
|
20
|
+
olmon/commands/status.py
|
|
21
|
+
olmon/commands/uninstall.py
|
|
22
|
+
olmon/commands/update.py
|
|
23
|
+
olmon/commands/watch.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
olmon
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "olmon"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A lightweight CLI to monitor Ollama models and usage"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
license = { text = "MIT" }
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Vlad Digori", email = "digorivlad59@gmail.com" }
|
|
10
|
+
]
|
|
11
|
+
keywords = ["ollama", "monitor", "cli", "llm", "devops", "developer-tools"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"License :: OSI Approved :: MIT License",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
]
|
|
17
|
+
dependencies = [
|
|
18
|
+
"mypy>=2.1.0",
|
|
19
|
+
"platformdirs>=4.10.0",
|
|
20
|
+
"rich>=15.0.0",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.scripts]
|
|
24
|
+
olmon = "olmon.main:app"
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://github.com/glemiu6/olmon"
|
|
28
|
+
Repository = "https://github.com/glemiu6/olmon"
|
|
29
|
+
"Bug Tracker" = "https://github.com/glemiu6/olmon/issues"
|
|
30
|
+
|
|
31
|
+
[dependency-groups]
|
|
32
|
+
dev = [
|
|
33
|
+
"pytest>=8.0.0",
|
|
34
|
+
"build>=1.4.4",
|
|
35
|
+
"twine>=6.2.0",
|
|
36
|
+
"pyinstaller>=6.0.0",
|
|
37
|
+
"pytest-cov>=7.1.0",
|
|
38
|
+
"ruff>=0.4.0",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[tool.ruff]
|
|
42
|
+
line-length = 100
|
|
43
|
+
target-version = "py313"
|
|
44
|
+
|
|
45
|
+
[tool.ruff.lint]
|
|
46
|
+
select = ["E", "F", "I"]
|
|
47
|
+
ignore = []
|
olmon-0.1.0/setup.cfg
ADDED