@llmops/cli 0.1.0-beta.8 → 0.1.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +4 -223
  2. package/package.json +3 -3
package/dist/index.mjs CHANGED
@@ -1,14 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { command, run, string } from "@drizzle-team/brocli";
3
3
  import { logger } from "@llmops/core";
4
- import { createDatabaseFromConnection, detectDatabaseType } from "@llmops/core/db";
4
+ import { createDatabaseFromConnection, detectDatabaseType, getMigrations } from "@llmops/core/db";
5
5
  import { existsSync } from "node:fs";
6
6
  import yoctoSpinner from "yocto-spinner";
7
7
  import chalk from "chalk";
8
8
  import prompts from "prompts";
9
9
  import { loadConfig } from "c12";
10
10
  import path from "node:path";
11
- import { sql } from "kysely";
12
11
 
13
12
  //#region src/lib/get-config.ts
14
13
  const getConfig = async ({ cwd, configPath }) => {
@@ -20,225 +19,6 @@ const getConfig = async ({ cwd, configPath }) => {
20
19
  }
21
20
  };
22
21
 
23
- //#endregion
24
- //#region src/lib/get-migration.ts
25
- const typeMap = {
26
- postgres: {
27
- uuid: [
28
- "character varying",
29
- "varchar",
30
- "text",
31
- "uuid"
32
- ],
33
- text: [
34
- "character varying",
35
- "varchar",
36
- "text"
37
- ],
38
- timestamp: [
39
- "timestamptz",
40
- "timestamp",
41
- "date"
42
- ],
43
- jsonb: ["json", "jsonb"]
44
- },
45
- mysql: {
46
- text: ["varchar", "text"],
47
- timestamp: [
48
- "timestamp",
49
- "datetime",
50
- "date"
51
- ],
52
- jsonb: ["json"]
53
- },
54
- sqlite: {
55
- text: ["TEXT"],
56
- date: ["DATE", "INTEGER"],
57
- integer: ["INTEGER"],
58
- boolean: [
59
- "INTEGER",
60
- "BOOLEAN",
61
- "TEXT"
62
- ],
63
- jsonb: ["TEXT"]
64
- },
65
- mssql: {
66
- varchar: [
67
- "varchar",
68
- "nvarchar",
69
- "uniqueidentifier"
70
- ],
71
- datetime2: [
72
- "datetime2",
73
- "date",
74
- "datetime"
75
- ],
76
- jsonb: ["varchar", "nvarchar"]
77
- }
78
- };
79
- function matchType(columnDataType, fieldType, dbType) {
80
- const normalize = (type) => type.toLowerCase().split("(")[0].trim();
81
- const types = typeMap[dbType];
82
- for (const [expectedType, variants] of Object.entries(types)) if (fieldType.toLowerCase().includes(expectedType.toLowerCase())) return variants.some((variant) => variant.toLowerCase() === normalize(columnDataType));
83
- return false;
84
- }
85
- /**
86
- * Get the current PostgreSQL schema (search_path) for the database connection
87
- */
88
- async function getPostgresSchema(db) {
89
- try {
90
- const result = await sql`SHOW search_path`.execute(db);
91
- if (result.rows[0]?.search_path) return result.rows[0].search_path.split(",").map((s) => s.trim()).map((s) => s.replace(/^["']|["']$/g, "")).filter((s) => !s.startsWith("$"))[0] || "public";
92
- } catch {}
93
- return "public";
94
- }
95
- async function getMigrations(db, dbType) {
96
- let currentSchema = "public";
97
- if (dbType === "postgres") {
98
- currentSchema = await getPostgresSchema(db);
99
- logger.debug(`PostgreSQL migration: Using schema '${currentSchema}'`);
100
- }
101
- const allTableMetadata = await db.introspection.getTables();
102
- let tableMetadata = allTableMetadata;
103
- if (dbType === "postgres") try {
104
- const tablesInSchema = await sql`
105
- SELECT table_name
106
- FROM information_schema.tables
107
- WHERE table_schema = ${currentSchema}
108
- AND table_type = 'BASE TABLE'
109
- `.execute(db);
110
- const tableNamesInSchema = new Set(tablesInSchema.rows.map((row) => row.table_name));
111
- tableMetadata = allTableMetadata.filter((table) => table.schema === currentSchema && tableNamesInSchema.has(table.name));
112
- logger.debug(`Found ${tableMetadata.length} table(s) in schema '${currentSchema}'`);
113
- } catch (error) {
114
- logger.warn("Could not filter tables by schema. Using all discovered tables.");
115
- }
116
- const schema = (await import("@llmops/core/db")).SCHEMA_METADATA.tables;
117
- const toBeCreated = [];
118
- const toBeAdded = [];
119
- for (const [tableName, tableConfig] of Object.entries(schema)) {
120
- const existingTable = tableMetadata.find((t) => t.name === tableName);
121
- if (!existingTable) {
122
- toBeCreated.push({
123
- table: tableName,
124
- fields: tableConfig.fields,
125
- order: tableConfig.order
126
- });
127
- continue;
128
- }
129
- const missingFields = {};
130
- for (const [fieldName, fieldConfig] of Object.entries(tableConfig.fields)) {
131
- const existingColumn = existingTable.columns.find((c) => c.name === fieldName);
132
- if (!existingColumn) {
133
- missingFields[fieldName] = fieldConfig;
134
- continue;
135
- }
136
- if (!matchType(existingColumn.dataType, fieldConfig.type, dbType)) logger.warn(`Field ${fieldName} in table ${tableName} has a different type. Expected ${fieldConfig.type} but got ${existingColumn.dataType}.`);
137
- }
138
- if (Object.keys(missingFields).length > 0) toBeAdded.push({
139
- table: tableName,
140
- fields: missingFields,
141
- order: tableConfig.order
142
- });
143
- }
144
- toBeCreated.sort((a, b) => a.order - b.order);
145
- toBeAdded.sort((a, b) => a.order - b.order);
146
- const migrations = [];
147
- function getColumnType(fieldConfig, fieldName) {
148
- const { type } = fieldConfig;
149
- return {
150
- uuid: {
151
- postgres: "uuid",
152
- mysql: "varchar(36)",
153
- sqlite: "text",
154
- mssql: "varchar(36)"
155
- },
156
- text: {
157
- postgres: "text",
158
- mysql: fieldConfig.unique ? "varchar(255)" : "text",
159
- sqlite: "text",
160
- mssql: fieldConfig.unique ? "varchar(255)" : "varchar(8000)"
161
- },
162
- timestamp: {
163
- postgres: "timestamptz",
164
- mysql: "timestamp(3)",
165
- sqlite: "date",
166
- mssql: sql`datetime2(3)`
167
- },
168
- jsonb: {
169
- postgres: "jsonb",
170
- mysql: "json",
171
- sqlite: "text",
172
- mssql: "varchar(8000)"
173
- },
174
- boolean: {
175
- postgres: "boolean",
176
- mysql: "boolean",
177
- sqlite: "integer",
178
- mssql: sql`bit`
179
- },
180
- integer: {
181
- postgres: "integer",
182
- mysql: "integer",
183
- sqlite: "integer",
184
- mssql: "integer"
185
- }
186
- }[type]?.[dbType] || "text";
187
- }
188
- for (const table of toBeCreated) {
189
- let builder = db.schema.createTable(table.table);
190
- for (const [fieldName, fieldConfig] of Object.entries(table.fields)) {
191
- const type = getColumnType(fieldConfig, fieldName);
192
- builder = builder.addColumn(fieldName, type, (col) => {
193
- let c = col;
194
- if (fieldName === "id") if (dbType === "postgres") c = c.primaryKey().defaultTo(sql`gen_random_uuid()`).notNull();
195
- else c = c.primaryKey().notNull();
196
- else if (!fieldConfig.nullable) c = c.notNull();
197
- if (fieldConfig.references && fieldName !== "id") {
198
- const refTable = fieldConfig.references.table;
199
- const refColumn = fieldConfig.references.column;
200
- c = c.references(`${refTable}.${refColumn}`).onDelete("cascade");
201
- }
202
- if (fieldConfig.unique && fieldName !== "id") c = c.unique();
203
- if (fieldConfig.default === "now()" && fieldName !== "id" && dbType !== "sqlite") if (dbType === "mysql") c = c.defaultTo(sql`CURRENT_TIMESTAMP(3)`);
204
- else c = c.defaultTo(sql`CURRENT_TIMESTAMP`);
205
- return c;
206
- });
207
- }
208
- migrations.push(builder);
209
- }
210
- for (const table of toBeAdded) for (const [fieldName, fieldConfig] of Object.entries(table.fields)) {
211
- const type = getColumnType(fieldConfig, fieldName);
212
- const builder = db.schema.alterTable(table.table).addColumn(fieldName, type, (col) => {
213
- let c = col;
214
- if (!fieldConfig.nullable) c = c.notNull();
215
- if (fieldConfig.references) {
216
- const refTable = fieldConfig.references.table;
217
- const refColumn = fieldConfig.references.column;
218
- c = c.references(`${refTable}.${refColumn}`).onDelete("cascade");
219
- }
220
- if (fieldConfig.unique) c = c.unique();
221
- if (fieldConfig.default === "now()" && dbType !== "sqlite") if (dbType === "mysql") c = c.defaultTo(sql`CURRENT_TIMESTAMP(3)`);
222
- else c = c.defaultTo(sql`CURRENT_TIMESTAMP`);
223
- return c;
224
- });
225
- migrations.push(builder);
226
- }
227
- async function runMigrations() {
228
- for (const migration of migrations) await migration.execute();
229
- }
230
- async function compileMigrations() {
231
- return migrations.map((m) => m.compile().sql).join(";\n\n") + ";";
232
- }
233
- return {
234
- toBeCreated,
235
- toBeAdded,
236
- runMigrations,
237
- compileMigrations,
238
- migrations
239
- };
240
- }
241
-
242
22
  //#endregion
243
23
  //#region src/commands/migrate.ts
244
24
  /**
@@ -283,7 +63,8 @@ const migrateCommand = command({
283
63
  logger.error("No database configuration found.");
284
64
  process.exit(1);
285
65
  }
286
- const db = await createDatabaseFromConnection(config.database);
66
+ const schema = config.schema ?? "llmops";
67
+ const db = await createDatabaseFromConnection(config.database, { schema });
287
68
  if (!db) {
288
69
  logger.error("Failed to create database connection.");
289
70
  process.exit(1);
@@ -294,7 +75,7 @@ const migrateCommand = command({
294
75
  process.exit(1);
295
76
  }
296
77
  const spinner = yoctoSpinner({ text: "preparing migration..." }).start();
297
- const { toBeAdded, toBeCreated, runMigrations } = await getMigrations(db, dbType);
78
+ const { toBeAdded, toBeCreated, runMigrations } = await getMigrations(db, dbType, { schema });
298
79
  if (!toBeAdded.length && !toBeCreated.length) {
299
80
  spinner.stop();
300
81
  console.log("🚀 No migrations needed.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llmops/cli",
3
- "version": "0.1.0-beta.8",
3
+ "version": "0.1.1-beta.1",
4
4
  "type": "module",
5
5
  "description": "LLMOps CLI - A pluggable LLMOps toolkit for TypeScript teams",
6
6
  "license": "Apache-2.0",
@@ -45,8 +45,8 @@
45
45
  "kysely": "^0.28.8",
46
46
  "prompts": "^2.4.2",
47
47
  "yocto-spinner": "^1.0.0",
48
- "@llmops/core": "0.1.0-beta.8",
49
- "@llmops/sdk": "0.1.0-beta.8"
48
+ "@llmops/sdk": "^0.1.1-beta.1",
49
+ "@llmops/core": "^0.1.1-beta.1"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@types/pg": "^8.15.6",