ollama-bench 1.0.0

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 dalist.
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.
package/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # ollama-benchmark
2
+
3
+ A command-line tool to benchmark Ollama models performance.
4
+
5
+ ## Prerequisites
6
+
7
+ - Node.js >= 14.0.0
8
+ - Ollama installed and running on your system
9
+
10
+ ## Installation
11
+
12
+ You can install globally:
13
+ ```bash
14
+ npm install -g ollama-bench
15
+ ```
16
+
17
+ Or run directly with npx:
18
+ ```bash
19
+ npx ollama-bench <model1> [model2] [model3] ...
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```bash
25
+ ollama-bench mistral llama2 codellama
26
+ ```
27
+
28
+ This will:
29
+ 1. Pull the specified models if not already present
30
+ 2. Run a benchmark test on each model
31
+ 3. Compare and display the results
32
+ 4. Show the best performing model
33
+
34
+ ## Output
35
+
36
+ The tool provides real-time feedback with:
37
+ - Loading animations during operations
38
+ - Color-coded status messages
39
+ - Detailed benchmark results
40
+ - Comparison of model performance
41
+
42
+ ## License
43
+
44
+ MIT
package/bun.lockb ADDED
Binary file
package/dist/index.js ADDED
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ import ollama from 'ollama';
3
+ /**
4
+ * Object containing ANSI color codes for text coloring.
5
+ */
6
+ const colors = {
7
+ reset: '\x1b[0m',
8
+ green: '\x1b[32m',
9
+ yellow: '\x1b[33m',
10
+ red: '\x1b[31m',
11
+ cyan: '\x1b[36m',
12
+ magenta: '\x1b[35m',
13
+ blue: '\x1b[34m',
14
+ };
15
+ /**
16
+ * Object containing emoji characters for various status indicators.
17
+ */
18
+ const emojis = {
19
+ rocket: '🚀',
20
+ check: '✅',
21
+ error: '❌',
22
+ hourglass: '⏳',
23
+ star: '⭐',
24
+ trophy: '🏆',
25
+ gear: '⚙️',
26
+ };
27
+ /**
28
+ * Applies color to the given text.
29
+ * @param text - The text to colorize.
30
+ * @param color - The color to apply.
31
+ * @returns The colorized text.
32
+ */
33
+ function colorize(text, color) {
34
+ return `${colors[color]}${text}${colors.reset}`;
35
+ }
36
+ /**
37
+ * Creates a loading animation for the console.
38
+ * @param operation - The operation being performed.
39
+ * @param model - The model being processed.
40
+ * @returns An interval ID for the animation.
41
+ */
42
+ function createLoadingAnimation(operation, model) {
43
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
44
+ let i = 0;
45
+ let dots = 0;
46
+ return setInterval(() => {
47
+ const frame = frames[i];
48
+ const dotString = '.'.repeat(dots);
49
+ const operationText = colorize(`${operation} ${model}${dotString}`, 'blue');
50
+ process.stdout.write(`\r${frame} ${emojis.gear} ${operationText}`.padEnd(50));
51
+ i = (i + 1) % frames.length;
52
+ dots = (dots + 1) % 4;
53
+ }, 100);
54
+ }
55
+ /**
56
+ * Pulls a model from Ollama.
57
+ * @param model - The name of the model to pull.
58
+ */
59
+ async function pullModel(model) {
60
+ console.log(colorize(`${emojis.rocket} Initiating pull for ${model}...`, 'yellow'));
61
+ const loadingAnimation = createLoadingAnimation('Pulling', model);
62
+ try {
63
+ const start = performance.now();
64
+ const response = await ollama.pull({ model, stream: true });
65
+ for await (const part of response) {
66
+ if (part.status === 'success') {
67
+ clearInterval(loadingAnimation);
68
+ const end = performance.now();
69
+ const duration = (end - start) / 1000;
70
+ console.log(`\r${colorize(`${emojis.check} Successfully pulled ${model} in ${duration.toFixed(2)} seconds`, 'green')} `);
71
+ return;
72
+ }
73
+ }
74
+ }
75
+ catch (error) {
76
+ clearInterval(loadingAnimation);
77
+ console.log(`\r${colorize(`${emojis.error} Error pulling ${model}: ${error.message}`, 'red')} `);
78
+ }
79
+ }
80
+ /**
81
+ * Benchmarks a model's performance.
82
+ * @param model - The name of the model to benchmark.
83
+ * @returns A promise that resolves to the benchmark result.
84
+ */
85
+ async function benchmarkModel(model) {
86
+ const prompt = "Explain the theory of relativity in simple terms.";
87
+ console.log(colorize(`${emojis.hourglass} Initiating benchmark for ${model}...`, 'cyan'));
88
+ const loadingAnimation = createLoadingAnimation('Benchmarking', model);
89
+ try {
90
+ const response = await ollama.generate({
91
+ model,
92
+ prompt,
93
+ stream: false,
94
+ });
95
+ clearInterval(loadingAnimation);
96
+ const totalDuration = response.total_duration / 1e9; // Convert nanoseconds to seconds
97
+ const tokensPerSecond = response.eval_count / (response.eval_duration / 1e9);
98
+ console.log(`\r${colorize(`${emojis.star} Benchmark results for ${model}:`, 'cyan')} `);
99
+ console.log(colorize(` Total time: ${totalDuration.toFixed(2)} seconds`, 'yellow'));
100
+ console.log(colorize(` Tokens generated: ${response.eval_count}`, 'yellow'));
101
+ console.log(colorize(` Tokens per second: ${tokensPerSecond.toFixed(2)}`, 'yellow'));
102
+ console.log();
103
+ return { model, tokensPerSecond };
104
+ }
105
+ catch (error) {
106
+ clearInterval(loadingAnimation);
107
+ console.log(`\r${colorize(`${emojis.error} Error benchmarking ${model}: ${error.message}`, 'red')} `);
108
+ return { model, tokensPerSecond: 0 };
109
+ }
110
+ }
111
+ /**
112
+ * The main function that orchestrates the model pulling and benchmarking process.
113
+ */
114
+ export async function main() {
115
+ const models = process.argv.slice(2);
116
+ if (models.length === 0) {
117
+ console.log(colorize(`${emojis.error} Error: No models provided. Please specify at least one model.`, 'red'));
118
+ process.exit(1);
119
+ }
120
+ console.log(colorize(`${emojis.rocket} Ollama Benchmark Script`, 'cyan'));
121
+ console.log(colorize("=======================", 'cyan'));
122
+ // Pull models
123
+ for (const model of models) {
124
+ await pullModel(model);
125
+ }
126
+ console.log();
127
+ // Benchmark models
128
+ const results = [];
129
+ for (const model of models) {
130
+ const result = await benchmarkModel(model);
131
+ results.push(result);
132
+ }
133
+ // Find the best performing model
134
+ const bestModel = results.reduce((best, current) => current.tokensPerSecond > best.tokensPerSecond ? current : best);
135
+ console.log(colorize(`${emojis.trophy} Best performing model:`, 'magenta'));
136
+ console.log(colorize(` ${bestModel.model} with ${bestModel.tokensPerSecond.toFixed(2)} tokens/second`, 'magenta'));
137
+ }
138
+ if (import.meta.url === import.meta.resolve(process.argv[1])) {
139
+ main().catch(error => {
140
+ console.error('Error:', error);
141
+ process.exit(1);
142
+ });
143
+ }
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "ollama-bench",
3
+ "version": "1.0.0",
4
+ "description": "A CLI tool to benchmark Ollama models performance",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "ollama-benchmark": "./dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "start": "node dist/index.js",
13
+ "dev": "tsc && node dist/index.js"
14
+ },
15
+ "keywords": ["ollama", "benchmark", "ai", "models", "cli"],
16
+ "author": "dalist1",
17
+ "license": "MIT",
18
+ "dependencies": {
19
+ "ollama": "latest"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^20.17.5",
23
+ "typescript": "^5.6.3"
24
+ },
25
+ "engines": {
26
+ "node": ">=14.0.0"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ }
31
+ }
package/src/index.ts ADDED
@@ -0,0 +1,181 @@
1
+ #!/usr/bin/env node
2
+
3
+ import ollama from 'ollama';
4
+
5
+ /**
6
+ * Represents the available color codes for text coloring.
7
+ */
8
+ type Color = 'reset' | 'green' | 'yellow' | 'red' | 'cyan' | 'magenta' | 'blue';
9
+
10
+ /**
11
+ * Represents the available emoji keys.
12
+ */
13
+ type Emoji = 'rocket' | 'check' | 'error' | 'hourglass' | 'star' | 'trophy' | 'gear';
14
+
15
+ /**
16
+ * Object containing ANSI color codes for text coloring.
17
+ */
18
+ const colors: Record<Color, string> = {
19
+ reset: '\x1b[0m',
20
+ green: '\x1b[32m',
21
+ yellow: '\x1b[33m',
22
+ red: '\x1b[31m',
23
+ cyan: '\x1b[36m',
24
+ magenta: '\x1b[35m',
25
+ blue: '\x1b[34m',
26
+ };
27
+
28
+ /**
29
+ * Object containing emoji characters for various status indicators.
30
+ */
31
+ const emojis: Record<Emoji, string> = {
32
+ rocket: '🚀',
33
+ check: '✅',
34
+ error: '❌',
35
+ hourglass: '⏳',
36
+ star: '⭐',
37
+ trophy: '🏆',
38
+ gear: '⚙️',
39
+ };
40
+
41
+ /**
42
+ * Applies color to the given text.
43
+ * @param text - The text to colorize.
44
+ * @param color - The color to apply.
45
+ * @returns The colorized text.
46
+ */
47
+ function colorize(text: string, color: Color): string {
48
+ return `${colors[color]}${text}${colors.reset}`;
49
+ }
50
+
51
+ /**
52
+ * Creates a loading animation for the console.
53
+ * @param operation - The operation being performed.
54
+ * @param model - The model being processed.
55
+ * @returns An interval ID for the animation.
56
+ */
57
+ function createLoadingAnimation(operation: string, model: string): NodeJS.Timeout {
58
+ const frames: string[] = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
59
+ let i = 0;
60
+ let dots = 0;
61
+ return setInterval(() => {
62
+ const frame = frames[i];
63
+ const dotString = '.'.repeat(dots);
64
+ const operationText = colorize(`${operation} ${model}${dotString}`, 'blue');
65
+ process.stdout.write(`\r${frame} ${emojis.gear} ${operationText}`.padEnd(50));
66
+ i = (i + 1) % frames.length;
67
+ dots = (dots + 1) % 4;
68
+ }, 100);
69
+ }
70
+
71
+ /**
72
+ * Pulls a model from Ollama.
73
+ * @param model - The name of the model to pull.
74
+ */
75
+ async function pullModel(model: string): Promise<void> {
76
+ console.log(colorize(`${emojis.rocket} Initiating pull for ${model}...`, 'yellow'));
77
+ const loadingAnimation = createLoadingAnimation('Pulling', model);
78
+ try {
79
+ const start = performance.now();
80
+ const response = await ollama.pull({ model, stream: true });
81
+ for await (const part of response) {
82
+ if (part.status === 'success') {
83
+ clearInterval(loadingAnimation);
84
+ const end = performance.now();
85
+ const duration = (end - start) / 1000;
86
+ console.log(`\r${colorize(`${emojis.check} Successfully pulled ${model} in ${duration.toFixed(2)} seconds`, 'green')} `);
87
+ return;
88
+ }
89
+ }
90
+ } catch (error) {
91
+ clearInterval(loadingAnimation);
92
+ console.log(`\r${colorize(`${emojis.error} Error pulling ${model}: ${(error as Error).message}`, 'red')} `);
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Represents the result of a model benchmark.
98
+ */
99
+ interface BenchmarkResult {
100
+ model: string;
101
+ tokensPerSecond: number;
102
+ }
103
+
104
+ /**
105
+ * Benchmarks a model's performance.
106
+ * @param model - The name of the model to benchmark.
107
+ * @returns A promise that resolves to the benchmark result.
108
+ */
109
+ async function benchmarkModel(model: string): Promise<BenchmarkResult> {
110
+ const prompt = "Explain the theory of relativity in simple terms.";
111
+ console.log(colorize(`${emojis.hourglass} Initiating benchmark for ${model}...`, 'cyan'));
112
+ const loadingAnimation = createLoadingAnimation('Benchmarking', model);
113
+
114
+ try {
115
+ const response = await ollama.generate({
116
+ model,
117
+ prompt,
118
+ stream: false,
119
+ });
120
+
121
+ clearInterval(loadingAnimation);
122
+ const totalDuration = response.total_duration / 1e9; // Convert nanoseconds to seconds
123
+ const tokensPerSecond = response.eval_count / (response.eval_duration / 1e9);
124
+
125
+ console.log(`\r${colorize(`${emojis.star} Benchmark results for ${model}:`, 'cyan')} `);
126
+ console.log(colorize(` Total time: ${totalDuration.toFixed(2)} seconds`, 'yellow'));
127
+ console.log(colorize(` Tokens generated: ${response.eval_count}`, 'yellow'));
128
+ console.log(colorize(` Tokens per second: ${tokensPerSecond.toFixed(2)}`, 'yellow'));
129
+ console.log();
130
+
131
+ return { model, tokensPerSecond };
132
+ } catch (error) {
133
+ clearInterval(loadingAnimation);
134
+ console.log(`\r${colorize(`${emojis.error} Error benchmarking ${model}: ${(error as Error).message}`, 'red')} `);
135
+ return { model, tokensPerSecond: 0 };
136
+ }
137
+ }
138
+
139
+ /**
140
+ * The main function that orchestrates the model pulling and benchmarking process.
141
+ */
142
+ export async function main(): Promise<void> {
143
+ const models = process.argv.slice(2);
144
+
145
+ if (models.length === 0) {
146
+ console.log(colorize(`${emojis.error} Error: No models provided. Please specify at least one model.`, 'red'));
147
+ process.exit(1);
148
+ }
149
+
150
+ console.log(colorize(`${emojis.rocket} Ollama Benchmark Script`, 'cyan'));
151
+ console.log(colorize("=======================", 'cyan'));
152
+
153
+ // Pull models
154
+ for (const model of models) {
155
+ await pullModel(model);
156
+ }
157
+
158
+ console.log();
159
+
160
+ // Benchmark models
161
+ const results: BenchmarkResult[] = [];
162
+ for (const model of models) {
163
+ const result = await benchmarkModel(model);
164
+ results.push(result);
165
+ }
166
+
167
+ // Find the best performing model
168
+ const bestModel = results.reduce((best, current) =>
169
+ current.tokensPerSecond > best.tokensPerSecond ? current : best
170
+ );
171
+
172
+ console.log(colorize(`${emojis.trophy} Best performing model:`, 'magenta'));
173
+ console.log(colorize(` ${bestModel.model} with ${bestModel.tokensPerSecond.toFixed(2)} tokens/second`, 'magenta'));
174
+ }
175
+
176
+ if (import.meta.url === import.meta.resolve(process.argv[1])) {
177
+ main().catch(error => {
178
+ console.error('Error:', error);
179
+ process.exit(1);
180
+ });
181
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ES2020",
5
+ "moduleResolution": "node",
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true
12
+ },
13
+ "include": ["src/**/*"],
14
+ "exclude": ["node_modules", "dist"]
15
+ }