@llmops/cli 1.0.0-beta.19 → 1.0.0-beta.22
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/dist/index.mjs +141 -10
- package/package.json +11 -20
- package/dist/index.d.mts +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
import { boolean, command, run, string } from "@drizzle-team/brocli";
|
|
3
4
|
import { logger } from "@llmops/core";
|
|
4
|
-
import { existsSync } from "node:fs";
|
|
5
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync } from "node:fs";
|
|
5
6
|
import yoctoSpinner from "yocto-spinner";
|
|
6
7
|
import prompts from "prompts";
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
8
|
+
import path, { basename, join, resolve } from "node:path";
|
|
9
|
+
import { pathToFileURL } from "node:url";
|
|
10
|
+
import { execSync } from "node:child_process";
|
|
11
|
+
import chalk from "chalk";
|
|
9
12
|
|
|
10
13
|
//#region src/lib/get-config.ts
|
|
11
14
|
const getConfig = async ({ cwd, configPath }) => {
|
|
12
|
-
if (configPath)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
if (!configPath) return void 0;
|
|
16
|
+
const resolvedPath = existsSync(configPath) ? path.resolve(configPath) : path.resolve(cwd, configPath);
|
|
17
|
+
if (!existsSync(resolvedPath)) throw new Error(`Config file not found: ${resolvedPath}`);
|
|
18
|
+
const mod = await import(pathToFileURL(resolvedPath).href);
|
|
19
|
+
if (mod.config) return mod.config;
|
|
20
|
+
if (mod.default) {
|
|
21
|
+
if (typeof mod.default === "object" && mod.default !== null && "config" in mod.default && mod.default.config) return mod.default.config;
|
|
22
|
+
return mod.default;
|
|
17
23
|
}
|
|
18
24
|
};
|
|
19
25
|
|
|
@@ -79,9 +85,134 @@ const migrateCommand = command({
|
|
|
79
85
|
}
|
|
80
86
|
});
|
|
81
87
|
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/commands/eval.ts
|
|
90
|
+
/**
|
|
91
|
+
* Find eval files from a path (file or directory).
|
|
92
|
+
* Default pattern: *.eval.ts, *.eval.js
|
|
93
|
+
*/
|
|
94
|
+
function findEvalFiles(target) {
|
|
95
|
+
const resolved = resolve(target);
|
|
96
|
+
if (!existsSync(resolved)) {
|
|
97
|
+
console.error(chalk.red(`Path not found: ${target}`));
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
const stat = statSync(resolved);
|
|
101
|
+
if (stat.isFile()) return [resolved];
|
|
102
|
+
if (stat.isDirectory()) return collectEvalFiles(resolved);
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
function collectEvalFiles(dir) {
|
|
106
|
+
const files = [];
|
|
107
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
108
|
+
for (const entry of entries) {
|
|
109
|
+
const fullPath = join(dir, entry.name);
|
|
110
|
+
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") files.push(...collectEvalFiles(fullPath));
|
|
111
|
+
else if (entry.isFile() && (entry.name.endsWith(".eval.ts") || entry.name.endsWith(".eval.js"))) files.push(fullPath);
|
|
112
|
+
}
|
|
113
|
+
return files.sort();
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Bundle and run an eval file using esbuild.
|
|
117
|
+
* Bundles to a temp file, executes with node, then cleans up.
|
|
118
|
+
*/
|
|
119
|
+
async function bundleAndRun(file, env) {
|
|
120
|
+
const esbuild = await import("esbuild");
|
|
121
|
+
const tmpDir = join(process.cwd(), ".llmops-eval-tmp");
|
|
122
|
+
mkdirSync(tmpDir, { recursive: true });
|
|
123
|
+
const outFile = join(tmpDir, `${basename(file, ".ts").replace(".eval", "")}_eval.mjs`);
|
|
124
|
+
try {
|
|
125
|
+
await esbuild.build({
|
|
126
|
+
entryPoints: [file],
|
|
127
|
+
bundle: true,
|
|
128
|
+
platform: "node",
|
|
129
|
+
format: "esm",
|
|
130
|
+
outfile: outFile,
|
|
131
|
+
external: [
|
|
132
|
+
"@llmops/sdk",
|
|
133
|
+
"@llmops/sdk/*",
|
|
134
|
+
"@llmops/core",
|
|
135
|
+
"@llmops/core/*",
|
|
136
|
+
"openai",
|
|
137
|
+
"esbuild",
|
|
138
|
+
"fsevents"
|
|
139
|
+
],
|
|
140
|
+
treeShaking: true,
|
|
141
|
+
sourcemap: false,
|
|
142
|
+
banner: { js: "" }
|
|
143
|
+
});
|
|
144
|
+
execSync(`node ${outFile}`, {
|
|
145
|
+
stdio: "inherit",
|
|
146
|
+
env,
|
|
147
|
+
cwd: process.cwd()
|
|
148
|
+
});
|
|
149
|
+
} finally {
|
|
150
|
+
try {
|
|
151
|
+
rmSync(tmpDir, {
|
|
152
|
+
recursive: true,
|
|
153
|
+
force: true
|
|
154
|
+
});
|
|
155
|
+
} catch {}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const evalCommand = command({
|
|
159
|
+
name: "eval",
|
|
160
|
+
desc: "Run evaluation files",
|
|
161
|
+
options: {
|
|
162
|
+
target: string().default("./evals").desc("File or directory to run. Default: ./evals"),
|
|
163
|
+
outputDir: string().default("./llmops-evals").desc("Output directory for results").alias("o"),
|
|
164
|
+
json: boolean().desc("Output results as JSON to stdout").alias("j")
|
|
165
|
+
},
|
|
166
|
+
handler: async (opts) => {
|
|
167
|
+
const target = opts.target;
|
|
168
|
+
const files = findEvalFiles(target);
|
|
169
|
+
if (files.length === 0) {
|
|
170
|
+
console.error(chalk.yellow(`No eval files found. Create files matching *.eval.ts or *.eval.js in ${target}`));
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
console.log(chalk.dim(`Found ${files.length} eval file${files.length > 1 ? "s" : ""}`));
|
|
174
|
+
const env = {
|
|
175
|
+
...process.env,
|
|
176
|
+
LLMOPS_EVAL_OUTPUT_DIR: resolve(opts.outputDir),
|
|
177
|
+
...opts.json ? { LLMOPS_EVAL_OUTPUT: "json" } : {}
|
|
178
|
+
};
|
|
179
|
+
let hasErrors = false;
|
|
180
|
+
for (const file of files) {
|
|
181
|
+
const name = basename(file);
|
|
182
|
+
console.log(chalk.dim(`\nRunning ${name}...`));
|
|
183
|
+
try {
|
|
184
|
+
await bundleAndRun(file, env);
|
|
185
|
+
} catch (err) {
|
|
186
|
+
hasErrors = true;
|
|
187
|
+
console.error(chalk.red(`\n✗ ${name} failed`));
|
|
188
|
+
if (err instanceof Error && !("status" in err)) console.error(err.message);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const outputDir = resolve(opts.outputDir);
|
|
192
|
+
if (existsSync(outputDir) && !opts.json) {
|
|
193
|
+
const evalDirs = readdirSync(outputDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
194
|
+
if (evalDirs.length > 0) {
|
|
195
|
+
console.log(chalk.dim("\n─────────────────────────────────"));
|
|
196
|
+
console.log(chalk.dim(`Results saved to ${opts.outputDir}/`));
|
|
197
|
+
for (const evalDir of evalDirs) {
|
|
198
|
+
const resultFiles = readdirSync(join(outputDir, evalDir)).filter((f) => f.endsWith(".json")).sort().reverse();
|
|
199
|
+
if (resultFiles.length > 0) try {
|
|
200
|
+
const scores = JSON.parse(readFileSync(join(outputDir, evalDir, resultFiles[0]), "utf-8")).scores;
|
|
201
|
+
const scoreStr = Object.entries(scores).map(([k, v]) => `${k}=${v.mean.toFixed(2)}`).join(" ");
|
|
202
|
+
console.log(` ${chalk.white(evalDir)} ${chalk.cyan(scoreStr)}`);
|
|
203
|
+
} catch {
|
|
204
|
+
console.log(` ${chalk.white(evalDir)} ${chalk.dim(resultFiles[0])}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (hasErrors) process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
82
213
|
//#endregion
|
|
83
214
|
//#region src/index.ts
|
|
84
|
-
run([migrateCommand], {
|
|
215
|
+
run([migrateCommand, evalCommand], {
|
|
85
216
|
name: "llmops",
|
|
86
217
|
description: "LLMOps CLI - A pluggable LLMOps toolkit for TypeScript teams",
|
|
87
218
|
version: "0.0.1"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llmops/cli",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.22",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "LLMOps CLI - A pluggable LLMOps toolkit for TypeScript teams",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -14,23 +14,13 @@
|
|
|
14
14
|
"directory": "packages/cli"
|
|
15
15
|
},
|
|
16
16
|
"author": "chtushar",
|
|
17
|
+
"bin": {
|
|
18
|
+
"llmops": "./dist/index.mjs"
|
|
19
|
+
},
|
|
17
20
|
"files": [
|
|
18
21
|
"dist"
|
|
19
22
|
],
|
|
20
23
|
"main": "./dist/index.mjs",
|
|
21
|
-
"module": "./dist/index.mjs",
|
|
22
|
-
"types": "./dist/index.d.mts",
|
|
23
|
-
"exports": "./dist/index.mjs",
|
|
24
|
-
"bin": {
|
|
25
|
-
"cli": "./dist/index.mjs",
|
|
26
|
-
"llmops": "./dist/index.mjs"
|
|
27
|
-
},
|
|
28
|
-
"publishConfig": {
|
|
29
|
-
"access": "public",
|
|
30
|
-
"executableFiles": [
|
|
31
|
-
"./dist/index.mjs"
|
|
32
|
-
]
|
|
33
|
-
},
|
|
34
24
|
"keywords": [
|
|
35
25
|
"llmops",
|
|
36
26
|
"cli",
|
|
@@ -38,15 +28,17 @@
|
|
|
38
28
|
"llm",
|
|
39
29
|
"typescript"
|
|
40
30
|
],
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
41
34
|
"dependencies": {
|
|
42
35
|
"@drizzle-team/brocli": "^0.11.0",
|
|
43
|
-
"c12": "^3.3.2",
|
|
44
36
|
"chalk": "^5.6.2",
|
|
45
|
-
"
|
|
37
|
+
"esbuild": "^0.25.0",
|
|
46
38
|
"prompts": "^2.4.2",
|
|
47
39
|
"yocto-spinner": "^1.0.0",
|
|
48
|
-
"@llmops/core": "^1.0.0-beta.
|
|
49
|
-
"@llmops/sdk": "^1.0.0-beta.
|
|
40
|
+
"@llmops/core": "^1.0.0-beta.22",
|
|
41
|
+
"@llmops/sdk": "^1.0.0-beta.22"
|
|
50
42
|
},
|
|
51
43
|
"devDependencies": {
|
|
52
44
|
"@types/pg": "^8.15.6",
|
|
@@ -55,9 +47,8 @@
|
|
|
55
47
|
},
|
|
56
48
|
"scripts": {
|
|
57
49
|
"build": "tsdown",
|
|
58
|
-
"start": "node ./dist/index.mjs",
|
|
59
50
|
"clean": "rm -rf dist",
|
|
60
|
-
"dev": "
|
|
51
|
+
"dev": "tsx src/index.ts",
|
|
61
52
|
"test": "vitest",
|
|
62
53
|
"typecheck": "tsc --noEmit"
|
|
63
54
|
}
|
package/dist/index.d.mts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { };
|