make-mp-data 2.1.6 → 3.0.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 (76) hide show
  1. package/README.md +31 -0
  2. package/dungeons/adspend.js +2 -2
  3. package/dungeons/ai-chat-analytics-ed.js +3 -2
  4. package/dungeons/anon.js +2 -2
  5. package/dungeons/array-of-object-loopup.js +181 -0
  6. package/dungeons/benchmark-heavy.js +241 -0
  7. package/dungeons/benchmark-light.js +141 -0
  8. package/dungeons/big.js +9 -8
  9. package/dungeons/business.js +2 -1
  10. package/dungeons/clinch-agi.js +632 -0
  11. package/dungeons/complex.js +3 -2
  12. package/dungeons/copilot.js +383 -0
  13. package/dungeons/ecommerce-store.js +0 -0
  14. package/dungeons/experiments.js +5 -4
  15. package/dungeons/foobar.js +101 -101
  16. package/dungeons/funnels.js +2 -2
  17. package/dungeons/gaming.js +3 -2
  18. package/dungeons/harness/harness-education.js +988 -0
  19. package/dungeons/harness/harness-fintech.js +976 -0
  20. package/dungeons/harness/harness-food.js +985 -0
  21. package/dungeons/harness/harness-gaming.js +1178 -0
  22. package/dungeons/harness/harness-media.js +961 -0
  23. package/dungeons/harness/harness-sass.js +923 -0
  24. package/dungeons/harness/harness-social.js +928 -0
  25. package/dungeons/kurby.js +211 -0
  26. package/dungeons/media.js +5 -4
  27. package/dungeons/mil.js +4 -3
  28. package/dungeons/mirror.js +2 -2
  29. package/dungeons/money2020-ed.js +8 -7
  30. package/dungeons/sanity.js +3 -2
  31. package/dungeons/scd.js +3 -2
  32. package/dungeons/simple.js +29 -14
  33. package/dungeons/strict-event-test.js +30 -0
  34. package/dungeons/student-teacher.js +3 -2
  35. package/dungeons/text-generation.js +84 -85
  36. package/dungeons/too-big-events.js +166 -0
  37. package/dungeons/uday-schema.json +220 -0
  38. package/dungeons/userAgent.js +4 -3
  39. package/index.js +41 -54
  40. package/lib/core/config-validator.js +122 -7
  41. package/lib/core/context.js +7 -14
  42. package/lib/core/storage.js +60 -30
  43. package/lib/generators/adspend.js +12 -27
  44. package/lib/generators/events.js +6 -7
  45. package/lib/generators/funnels.js +16 -5
  46. package/lib/generators/product-lookup.js +262 -0
  47. package/lib/generators/product-names.js +195 -0
  48. package/lib/generators/profiles.js +3 -3
  49. package/lib/generators/scd.js +13 -3
  50. package/lib/generators/text.js +17 -4
  51. package/lib/orchestrators/mixpanel-sender.js +251 -208
  52. package/lib/orchestrators/user-loop.js +57 -19
  53. package/lib/templates/funnels-instructions.txt +272 -0
  54. package/lib/templates/hook-examples.json +187 -0
  55. package/lib/templates/hooks-instructions.txt +295 -8
  56. package/lib/templates/phrases.js +473 -16
  57. package/lib/templates/refine-instructions.txt +485 -0
  58. package/lib/templates/schema-instructions.txt +239 -109
  59. package/lib/templates/schema.d.ts +173 -0
  60. package/lib/templates/verbose-schema.js +140 -206
  61. package/lib/utils/ai.js +853 -77
  62. package/lib/utils/chart.js +210 -0
  63. package/lib/utils/function-registry.js +285 -0
  64. package/lib/utils/json-evaluator.js +172 -0
  65. package/lib/utils/logger.js +38 -0
  66. package/lib/utils/mixpanel.js +101 -0
  67. package/lib/utils/project.js +3 -2
  68. package/lib/utils/utils.js +41 -4
  69. package/package.json +13 -19
  70. package/types.d.ts +15 -5
  71. package/lib/generators/text-bak-old.js +0 -1121
  72. package/lib/orchestrators/worker-manager.js +0 -203
  73. package/lib/templates/phrases-bak.js +0 -925
  74. package/lib/templates/prompt (old).txt +0 -98
  75. package/lib/templates/scratch-dungeon-template.js +0 -116
  76. package/lib/templates/textQuickTest.js +0 -172
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Server-side Mixpanel wrapper for AI observability
3
+ *
4
+ * Provides a clean interface for tracking AI job events and usage metrics.
5
+ * Non-blocking - tracking failures are logged but don't affect application flow.
6
+ */
7
+
8
+ import Mixpanel from 'mixpanel';
9
+ import { serverLogger as logger } from './logger.js';
10
+
11
+ const MIXPANEL_TOKEN = '7608bedf7495b09ac092209387facb52';
12
+ const { NODE_ENV = 'unknown' } = process.env;
13
+
14
+ // Initialize Mixpanel
15
+ let mixpanel = null;
16
+ try {
17
+ mixpanel = Mixpanel.init(MIXPANEL_TOKEN);
18
+ logger.debug('Mixpanel initialized for server-side tracking');
19
+ } catch (error) {
20
+ logger.error({ err: error }, 'Failed to initialize Mixpanel');
21
+ }
22
+
23
+ /**
24
+ * Track an event to Mixpanel (server-side)
25
+ * @param {string} eventName - Event name
26
+ * @param {object} properties - Event properties
27
+ * @param {string} [userId] - Optional user ID for distinct_id
28
+ */
29
+ export function track(eventName, properties = {}, userId = null) {
30
+ if (!mixpanel) {
31
+ logger.warn({ eventName }, 'Mixpanel not initialized, skipping track');
32
+ return;
33
+ }
34
+
35
+ try {
36
+ const trackProps = {
37
+ ...properties,
38
+ distinct_id: userId || 'anonymous',
39
+ source: 'dm4-server',
40
+ $insert_id: `${eventName}-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
41
+ };
42
+
43
+ mixpanel.track(eventName, trackProps);
44
+ } catch (error) {
45
+ // Non-blocking - log but don't throw
46
+ logger.error({ err: error, eventName }, 'Failed to track Mixpanel event');
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Track an AI job completion event with structured metadata
52
+ * @param {object} params - AI job parameters
53
+ * @param {string} params.component - AI component (schema, hooks, refine, funnels, generic)
54
+ * @param {string} params.prompt - The prompt sent to the AI
55
+ * @param {object|string} params.response - The AI response
56
+ * @param {number} params.duration_ms - Duration in milliseconds
57
+ * @param {boolean} params.success - Whether the job succeeded
58
+ * @param {string} [params.error] - Error message if failed
59
+ * @param {object} [params.usage] - Token usage from ai.getLastUsage()
60
+ * @param {string} [params.user_id] - User ID for distinct_id
61
+ */
62
+ export function trackAIJob(params) {
63
+ const {
64
+ component,
65
+ prompt,
66
+ response,
67
+ duration_ms,
68
+ success,
69
+ error = null,
70
+ usage = null,
71
+ user_id = null
72
+ } = params;
73
+
74
+ const responseStr = typeof response === 'string' ? response : JSON.stringify(response);
75
+
76
+ const eventProps = {
77
+ component: `ai-${component}`,
78
+ prompt_preview: prompt?.substring(0, 200) || '',
79
+ prompt_length: prompt?.length || 0,
80
+ output_length: responseStr?.length || 0,
81
+ duration_ms,
82
+ duration_human: `${(duration_ms / 1000).toFixed(2)} seconds`,
83
+ runtime: process.env.RUNTIME_CONTEXT || 'dm4',
84
+ environment: NODE_ENV,
85
+ success,
86
+ error,
87
+ // Token usage (all fields from getLastUsage)
88
+ ...(usage && {
89
+ promptTokens: usage.promptTokens,
90
+ responseTokens: usage.responseTokens,
91
+ totalTokens: usage.totalTokens,
92
+ attempts: usage.attempts,
93
+ modelVersion: usage.modelVersion,
94
+ requestedModel: usage.requestedModel,
95
+ }),
96
+ };
97
+
98
+ track('AI Job', eventProps, user_id);
99
+ }
100
+
101
+ export default { track, trackAIJob };
@@ -1,5 +1,6 @@
1
1
  import 'dotenv/config';
2
2
  import * as akTools from 'ak-tools';
3
+ import { dataLogger as logger } from './logger.js';
3
4
  const { rand, makeName } = akTools;
4
5
  let { OAUTH_TOKEN = "" } = process.env;
5
6
  const { NODE_ENV = "unknown" } = process.env;
@@ -114,7 +115,7 @@ async function getUser(oauthToken = OAUTH_TOKEN) {
114
115
  }
115
116
  }
116
117
  catch (err) {
117
- console.error('get user err', err);
118
+ logger.error({ err }, 'Error getting user information');
118
119
  }
119
120
 
120
121
  return user;
@@ -144,7 +145,7 @@ async function addGroupKeys(groupKeyDfns = [], projectId, oauthToken = OAUTH_TOK
144
145
  results.push(data?.results);
145
146
  }
146
147
  catch (err) {
147
- console.error('add group keys err', err);
148
+ logger.error({ err, property_name }, 'Error adding group key');
148
149
  continue loopKeys;
149
150
  }
150
151
 
@@ -208,10 +208,10 @@ function choose(value) {
208
208
 
209
209
  }
210
210
 
211
- // if the thing has a .next() method, call that
211
+ // if the thing has a .next() method, call that (e.g., generators/iterators)
212
212
  try {
213
- if (value && typeof value.next === 'function') {
214
- return value.next();
213
+ if (value && typeof /** @type {any} */ (value).next === 'function') {
214
+ return /** @type {any} */ (value).next();
215
215
  }
216
216
  } catch (e) {
217
217
  console.error(`Error occurred while calling next(): ${e}`);
@@ -1070,7 +1070,42 @@ function buildFileNames(config) {
1070
1070
  }
1071
1071
 
1072
1072
  /**
1073
- * @param {[string, number][]} arrayOfArrays
1073
+ * Human-readable byte size
1074
+ * @param {number} bytes
1075
+ * @param {number} dp - decimal places
1076
+ * @param {boolean} si - use SI units
1077
+ * @returns {string}
1078
+ */
1079
+ function bytesHuman(bytes, dp = 2, si = true) {
1080
+ const thresh = si ? 1000 : 1024;
1081
+ if (Math.abs(bytes) < thresh) {
1082
+ return bytes + ' B';
1083
+ }
1084
+ const units = si ? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] : ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
1085
+ let u = -1;
1086
+ const r = 10 ** dp;
1087
+ do {
1088
+ bytes /= thresh;
1089
+ ++u;
1090
+ } while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
1091
+ return bytes.toFixed(dp) + ' ' + units[u];
1092
+ }
1093
+
1094
+ /**
1095
+ * Format milliseconds as HH:MM:SS
1096
+ * @param {number} ms - Milliseconds
1097
+ * @returns {string} Formatted duration string
1098
+ */
1099
+ function formatDuration(ms) {
1100
+ const seconds = Math.floor(ms / 1000);
1101
+ const hours = Math.floor(seconds / 3600);
1102
+ const minutes = Math.floor((seconds % 3600) / 60);
1103
+ const secs = seconds % 60;
1104
+ return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
1105
+ }
1106
+
1107
+ /**
1108
+ * @param {[string, string | number][]} arrayOfArrays
1074
1109
  */
1075
1110
  function progress(arrayOfArrays) {
1076
1111
  const terminalWidth = process.stdout.columns || 120;
@@ -1503,4 +1538,6 @@ export {
1503
1538
  datesBetween,
1504
1539
  weighChoices,
1505
1540
  wrapFunc,
1541
+ bytesHuman,
1542
+ formatDuration,
1506
1543
  };
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "make-mp-data",
3
- "version": "2.1.6",
3
+ "version": "3.0.1",
4
4
  "description": "builds all mixpanel primitives for a given project",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "types": "types.d.ts",
8
8
  "files": [
9
9
  "index.js",
10
- "cli.js",
10
+ "entry.js",
11
11
  "types.d.ts",
12
12
  "lib/",
13
13
  "dungeons/",
@@ -16,23 +16,16 @@
16
16
  "README.md"
17
17
  ],
18
18
  "scripts": {
19
- "new:dungeon": "node ./scripts/create-dungeon.mjs",
20
- "start": "node ./index.js",
21
- "dev": "nodemon scratch.mjs --ignore ./data/*",
22
- "prune": "rm -f ./data/* && rm -f ./tmp/* && rm -f vscode-profile-*",
23
19
  "post": "npm publish",
24
- "deps": "./scripts/update-deps.sh",
25
20
  "test": "NODE_ENV=test vitest run",
26
- "coverage": "vitest run --coverage && open ./coverage/index.html",
27
21
  "typecheck": "tsc --noEmit",
28
- "typecheck:build": "tsc --noEmit --project tsconfig.build.json",
29
- "typecheck:strict": "echo '⚠️ Running pedantic strict checks (many false positives expected)...' && tsc --noEmit --project tsconfig.build.json --strict",
30
- "typecheck:summary": "echo '🔍 Type-checking all shipped files...' && npm run typecheck:build && echo '✅ All TypeScript checks passed!'",
31
- "test:quick": "node ./tests/benchmark/quick-test.mjs",
32
- "exp:soup": "node ./tests/testSoup.mjs",
33
- "func:local": "functions-framework --target=entry",
34
- "func:deploy": "./scripts/deploy.sh",
35
- "textGen": "node ./lib/templates/textQuickTest.js"
22
+ "dev": "nodemon scratch.mjs --ignore ./data/*",
23
+ "prune": "rm -f ./data/* && rm -f ./tmp/* && rm -f vscode-profile-*",
24
+ "deps": "./scripts/update-deps.sh",
25
+ "dungeon:new": "node ./scripts/create-dungeon.mjs",
26
+ "dungeon:run": "node ./scripts/run-dungeon.mjs",
27
+ "dungeon:to-json": "node ./scripts/dungeon-to-json.mjs",
28
+ "dungeon:from-json": "node ./scripts/json-to-dungeon.mjs"
36
29
  },
37
30
  "repository": {
38
31
  "type": "git",
@@ -60,7 +53,6 @@
60
53
  },
61
54
  "homepage": "https://github.com/ak--47/make-mp-data#readme",
62
55
  "dependencies": {
63
- "@google-cloud/functions-framework": "^3.4.2",
64
56
  "@google-cloud/storage": "^7.14.0",
65
57
  "ak-fetch": "^2.0.15",
66
58
  "ak-gemini": "^1.1.13",
@@ -68,10 +60,12 @@
68
60
  "chance": "^1.1.11",
69
61
  "dayjs": "^1.11.11",
70
62
  "dotenv": "^16.4.5",
71
- "google-auth-library": "^9.15.0",
72
63
  "hyparquet-writer": "^0.6.1",
73
- "mixpanel-import": "^3.2.7",
64
+ "mixpanel": "^0.18.0",
65
+ "mixpanel-import": "^3.2.8",
74
66
  "p-limit": "^3.1.0",
67
+ "pino": "^9.0.0",
68
+ "pino-pretty": "^11.0.0",
75
69
  "seedrandom": "^3.0.5",
76
70
  "sentiment": "^5.0.2",
77
71
  "tracery-grammar": "^2.8.4",
package/types.d.ts CHANGED
@@ -48,7 +48,10 @@ export interface Dungeon {
48
48
  hasAnonIds?: boolean;
49
49
  hasSessionIds?: boolean;
50
50
  alsoInferFunnels?: boolean;
51
+ makeChart?: boolean | string;
51
52
  singleCountry?: string;
53
+ strictEventCount?: boolean;
54
+ isUIJob?: boolean;
52
55
 
53
56
  //models
54
57
  events?: EventConfig[]; //| string[]; //can also be a array of strings
@@ -69,13 +72,15 @@ export interface Dungeon {
69
72
 
70
73
  //probabilities
71
74
  percentUsersBornInDataset?: number;
75
+ /** Bias toward recent birth dates for users born in dataset (0 = uniform, 1 = heavily recent). Default: 0.3 */
76
+ bornRecentBias?: number;
72
77
  }
73
78
 
74
79
  export type SCDProp = {
75
80
  type?: string | "user" | "company_id" | "team_id" | "department_id";
76
- frequency: "day" | "week" | "month" | "year";
81
+ frequency?: "day" | "week" | "month" | "year";
77
82
  values: ValueValid;
78
- timing: "fixed" | "fuzzy";
83
+ timing?: "fixed" | "fuzzy";
79
84
  max?: number;
80
85
  };
81
86
 
@@ -164,7 +169,6 @@ export interface RuntimeState {
164
169
  userCount: number;
165
170
  isBatchMode: boolean;
166
171
  verbose: boolean;
167
- isCLI: boolean;
168
172
  }
169
173
 
170
174
  /**
@@ -209,7 +213,6 @@ export interface Context {
209
213
  incrementUserCount(): void;
210
214
  incrementEventCount(): void;
211
215
  isBatchMode(): boolean;
212
- isCLI(): boolean;
213
216
 
214
217
  // Time helper methods
215
218
  getTimeShift(): number;
@@ -227,6 +230,7 @@ export interface EventConfig {
227
230
  isChurnEvent?: boolean;
228
231
  isSessionStartEvent?: boolean;
229
232
  relativeTimeMs?: number;
233
+ isStrictEvent?: boolean;
230
234
  }
231
235
 
232
236
  export interface GroupEventConfig extends EventConfig {
@@ -322,6 +326,10 @@ export interface Funnel {
322
326
  *
323
327
  */
324
328
  experiment?: boolean;
329
+ /**
330
+ * optional: if set, in sequential funnels, this will determine WHEN the property is bound to the rest of the events in the funnel
331
+ */
332
+ bindPropsIndex?: number;
325
333
  }
326
334
 
327
335
  /**
@@ -424,6 +432,8 @@ export type Result = {
424
432
  operations?: number;
425
433
  eventCount?: number;
426
434
  userCount?: number;
435
+ groupCount?: number;
436
+ avgEPS?: number;
427
437
  };
428
438
 
429
439
  /**
@@ -698,7 +708,7 @@ export interface TextGenerator {
698
708
  * @param config - Configuration options for the generator
699
709
  * @returns Text generator instance
700
710
  */
701
- export declare function createGenerator(config?: TextGeneratorConfig): TextGenerator;
711
+ export declare function createTextGenerator(config?: TextGeneratorConfig): TextGenerator;
702
712
 
703
713
  /**
704
714
  * Generate a batch of text items directly (standalone function)