make-mp-data 2.0.18 → 2.0.21
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/dungeons/big.js +7 -6
- package/dungeons/business.js +21 -3
- package/dungeons/complex.js +1 -1
- package/dungeons/experiments.js +8 -7
- package/dungeons/media.js +7 -7
- package/dungeons/sanity.js +8 -13
- package/dungeons/simple.js +2 -0
- package/dungeons/student-teacher.js +475 -0
- package/dungeons/userAgent.js +7 -7
- package/entry.js +13 -2
- package/index.js +74 -3
- package/lib/cli/cli.js +1 -1
- package/lib/core/config-validator.js +22 -7
- package/lib/core/context.js +31 -16
- package/lib/core/storage.js +20 -19
- package/lib/generators/events.js +41 -18
- package/lib/orchestrators/worker-manager.js +5 -2
- package/lib/templates/abbreviated.d.ts +158 -0
- package/lib/{data → templates}/defaults.js +2 -2
- package/lib/templates/dungeon-template.js +110 -0
- package/lib/templates/instructions.txt +77 -0
- package/lib/templates/verbose-schema.js +311 -0
- package/lib/utils/ai.js +42 -64
- package/lib/utils/chart.js +5 -0
- package/lib/utils/utils.js +69 -45
- package/package.json +10 -11
- package/types.d.ts +134 -126
- package/lib/cloud-function.js +0 -20
- /package/lib/{utils/prompt.txt → templates/prompt (old).txt} +0 -0
package/lib/utils/ai.js
CHANGED
|
@@ -1,89 +1,67 @@
|
|
|
1
|
-
import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
2
|
-
import * as u from "ak-tools";
|
|
3
1
|
|
|
4
|
-
import
|
|
2
|
+
/** @typedef {import('../../types.js').Dungeon} Dungeon */
|
|
3
|
+
/** @typedef {import('../../types.js').EventConfig} EventConfig */
|
|
5
4
|
|
|
5
|
+
import * as u from "ak-tools";
|
|
6
|
+
import 'dotenv/config';
|
|
6
7
|
const { GEMINI_API_KEY: API_KEY, NODE_ENV = "unknown" } = process.env;
|
|
7
8
|
if (!API_KEY) throw new Error("Please provide a Gemini API key");
|
|
9
|
+
import AITransformer from 'ak-gemini';
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const model = gemini.getGenerativeModel({ model: "gemini-2.0-flash" });
|
|
12
|
-
const PROOMPTY = await u.load("./components/prompt.txt");
|
|
13
|
-
const prompt = `
|
|
14
|
-
Given the following information about a website or app:
|
|
15
|
-
|
|
16
|
-
${userInput}
|
|
17
|
-
|
|
18
|
-
${PROOMPTY}
|
|
11
|
+
let CURRENT_PROMPT = ``;
|
|
12
|
+
CURRENT_PROMPT = `build me a dungeon stream with these events and structure
|
|
19
13
|
|
|
20
|
-
|
|
14
|
+
{ "event": "impression", "carousel": [{"product": "big mac"}] }
|
|
15
|
+
{ "event": "viewed", "product_viewed": "big mac" }
|
|
16
|
+
{ "event": "add to basket", "product_added": "big mac" }
|
|
17
|
+
{ "event": "customized", "product_customized": "big mac" }
|
|
18
|
+
{ "event": "checked out", "cart": [{"item": "big mac"}] }
|
|
21
19
|
|
|
22
|
-
${userInput}
|
|
23
|
-
`.trim();
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
let attempts = 0;
|
|
28
|
-
do {
|
|
29
|
-
attempts++;
|
|
30
|
-
const result = await model.generateContent(prompt);
|
|
31
|
-
const response = await result.response;
|
|
32
|
-
const text = response.text();
|
|
33
|
-
schema = processResponse(text);
|
|
34
|
-
schemaIsValid = validator(schema);
|
|
35
|
-
} while (!schemaIsValid);
|
|
21
|
+
but use all the different mcdonalds products as a possible values`;
|
|
22
|
+
CURRENT_PROMPT = ``;
|
|
36
23
|
|
|
37
|
-
return schema;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function processResponse(text) {
|
|
41
|
-
let json;
|
|
42
|
-
// check for ```json
|
|
43
|
-
const start = text.indexOf("```json");
|
|
44
|
-
const end = text.indexOf("```", start + 1);
|
|
45
|
-
|
|
46
|
-
if (start === -1 || end === -1) {
|
|
47
|
-
const start = text.indexOf("{");
|
|
48
|
-
const end = text.lastIndexOf("}");
|
|
49
|
-
json = text.slice(start, end + 1).trim();
|
|
50
|
-
}
|
|
51
24
|
|
|
52
|
-
json = text.slice(start + 7, end).trim();
|
|
53
25
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
26
|
+
async function main(params) {
|
|
27
|
+
const { prompt } = params;
|
|
28
|
+
if (!prompt) throw new Error("Please provide a prompt");
|
|
29
|
+
let INSTRUCTIONS = await u.load('./lib/templates/instructions.txt', false);
|
|
30
|
+
const TYPES = await u.load('./lib/templates/abbreviated.d.ts', false);
|
|
31
|
+
const VERBOSE_SCHEMA_FILE = await u.load('./lib/templates/verbose-schema.js', false);
|
|
32
|
+
const VERBOSE_SCHEMA = VERBOSE_SCHEMA_FILE.split(`//SPLIT HERE`).pop()?.trim() || ``;
|
|
33
|
+
INSTRUCTIONS = INSTRUCTIONS
|
|
34
|
+
.replace(/<TYPES>/g, TYPES)
|
|
35
|
+
.replace(/<VERBOSE_SCHEMA>/g, VERBOSE_SCHEMA);
|
|
63
36
|
|
|
64
|
-
|
|
65
|
-
|
|
37
|
+
const ai = new AITransformer({
|
|
38
|
+
apiKey: API_KEY,
|
|
39
|
+
onlyJSON: false,
|
|
40
|
+
systemInstructions: INSTRUCTIONS?.trim(),
|
|
41
|
+
modelName: "gemini-2.5-pro",
|
|
66
42
|
|
|
67
|
-
//null schema are always invalid
|
|
68
|
-
if (!schema) valid = false;
|
|
69
43
|
|
|
70
|
-
|
|
71
|
-
|
|
44
|
+
});
|
|
45
|
+
await ai.init();
|
|
46
|
+
const response = await ai.message(prompt);
|
|
72
47
|
|
|
73
|
-
//
|
|
74
|
-
|
|
48
|
+
// if (NODE_ENV === "dev") {
|
|
49
|
+
// debugger;
|
|
50
|
+
// }
|
|
75
51
|
|
|
76
|
-
|
|
77
|
-
if (Object.keys(schema.userProps).length < 2) valid = false;
|
|
52
|
+
return response;
|
|
78
53
|
|
|
79
|
-
return valid;
|
|
80
54
|
}
|
|
81
55
|
|
|
82
56
|
|
|
83
|
-
export
|
|
57
|
+
export default main;
|
|
84
58
|
|
|
85
59
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
86
|
-
|
|
60
|
+
main(
|
|
61
|
+
{
|
|
62
|
+
prompt: CURRENT_PROMPT || "Generate a dungeon spec for a simple e-commerce site with checkout and add to cart events."
|
|
63
|
+
}
|
|
64
|
+
)
|
|
87
65
|
.then((result) => {
|
|
88
66
|
if (NODE_ENV === "dev") debugger;
|
|
89
67
|
})
|
package/lib/utils/chart.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/** @typedef {import('../../types.js').EventSchema} EventSchema */
|
|
2
|
+
/** @typedef {import('../../types.js').Result} Result */
|
|
3
|
+
/** @typedef {import('../../types.js').Context} Context */
|
|
4
|
+
|
|
1
5
|
import { ChartJSNodeCanvas } from 'chartjs-node-canvas';
|
|
2
6
|
import fs from 'fs';
|
|
3
7
|
import * as u from 'ak-tools';
|
|
@@ -179,6 +183,7 @@ async function generateLineChart(rawData, signupEvents = ["sign up"], fileName)
|
|
|
179
183
|
const imageBuffer = await chartJSNodeCanvas.renderToBuffer(configuration);
|
|
180
184
|
const filePath = path.join(tempDir, `${fileName}.png`);
|
|
181
185
|
const removed = await u.rm(filePath);
|
|
186
|
+
// @ts-ignore - imageBuffer is a Buffer but touch accepts it
|
|
182
187
|
const file = await u.touch(filePath, imageBuffer);
|
|
183
188
|
|
|
184
189
|
console.log(`Chart saved as ${fileName}.png`);
|
package/lib/utils/utils.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import Chance from 'chance';
|
|
3
3
|
import readline from 'readline';
|
|
4
|
-
import { comma, uid} from 'ak-tools';
|
|
4
|
+
import { comma, uid } from 'ak-tools';
|
|
5
5
|
import { spawn } from 'child_process';
|
|
6
6
|
import dayjs from 'dayjs';
|
|
7
7
|
import utc from 'dayjs/plugin/utc.js';
|
|
@@ -10,7 +10,7 @@ import { mkdir, parseGCSUri } from 'ak-tools';
|
|
|
10
10
|
import { existsSync } from 'fs';
|
|
11
11
|
dayjs.extend(utc);
|
|
12
12
|
import 'dotenv/config';
|
|
13
|
-
import { domainSuffix, domainPrefix } from '../
|
|
13
|
+
import { domainSuffix, domainPrefix } from '../templates/defaults.js';
|
|
14
14
|
|
|
15
15
|
/** @typedef {import('../../types').Dungeon} Config */
|
|
16
16
|
/** @typedef {import('../../types').EventConfig} EventConfig */
|
|
@@ -186,6 +186,27 @@ function day(start, end) {
|
|
|
186
186
|
function choose(value) {
|
|
187
187
|
const chance = getChance();
|
|
188
188
|
|
|
189
|
+
// most of the time this will receive a list of strings;
|
|
190
|
+
// when that is the case, we need to ensure some 'keywords' like 'variant' or 'test' aren't in the array
|
|
191
|
+
// next we want to see if the array is unweighted ... i.e. no dupe strings and each string only occurs once ['a', 'b', 'c', 'd']
|
|
192
|
+
// if all these are true we will pickAWinner(value)()
|
|
193
|
+
if (Array.isArray(value) && value.length > 2 && value.length < 20 && value.every(item => typeof item === 'string')) {
|
|
194
|
+
// ensure terms 'variant' 'group' 'experiment' or 'population' are NOT in any of the items
|
|
195
|
+
if (!value.some(item => item.includes('variant') || item.includes('group') || item.includes('experiment') || item.includes('population'))) {
|
|
196
|
+
// check to make sure that each element in the array only occurs once...
|
|
197
|
+
const uniqueItems = new Set(value);
|
|
198
|
+
if (uniqueItems.size === value.length) {
|
|
199
|
+
// Array has no duplicates, use pickAWinner
|
|
200
|
+
const quickList = pickAWinner(value, 0)();
|
|
201
|
+
const theChosenOne = chance.pickone(quickList);
|
|
202
|
+
return theChosenOne;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
|
|
189
210
|
try {
|
|
190
211
|
// Keep resolving the value if it's a function (with caching)
|
|
191
212
|
while (typeof value === 'function') {
|
|
@@ -218,13 +239,12 @@ function choose(value) {
|
|
|
218
239
|
return chance.pickone(value);
|
|
219
240
|
}
|
|
220
241
|
|
|
221
|
-
//
|
|
222
|
-
if (Array.isArray(value) && value.
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
return chance.pickone(value);
|
|
242
|
+
// PERFORMANCE: Optimized array handling - check first item type instead of every()
|
|
243
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
244
|
+
const firstType = typeof value[0];
|
|
245
|
+
if (firstType === 'string' || firstType === 'number') {
|
|
246
|
+
return chance.pickone(value);
|
|
247
|
+
}
|
|
228
248
|
}
|
|
229
249
|
|
|
230
250
|
if (Array.isArray(value) && value.every(item => typeof item === 'object')) {
|
|
@@ -331,7 +351,10 @@ function integer(min = 1, max = 100) {
|
|
|
331
351
|
};
|
|
332
352
|
|
|
333
353
|
|
|
334
|
-
|
|
354
|
+
function decimal(min = 0, max = 1, fixed = 2) {
|
|
355
|
+
const chance = getChance();
|
|
356
|
+
return chance.floating({ min, max, fixed });
|
|
357
|
+
}
|
|
335
358
|
|
|
336
359
|
|
|
337
360
|
/*
|
|
@@ -684,18 +707,18 @@ function pickAWinner(items, mostChosenIndex) {
|
|
|
684
707
|
}
|
|
685
708
|
|
|
686
709
|
function quickHash(str, seed = 0) {
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
710
|
+
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
|
|
711
|
+
for (let i = 0, ch; i < str.length; i++) {
|
|
712
|
+
ch = str.charCodeAt(i);
|
|
713
|
+
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
714
|
+
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
715
|
+
}
|
|
716
|
+
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
|
|
717
|
+
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
718
|
+
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
|
|
719
|
+
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
|
720
|
+
|
|
721
|
+
return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString();
|
|
699
722
|
};
|
|
700
723
|
|
|
701
724
|
/*
|
|
@@ -717,10 +740,10 @@ function shuffleArray(array) {
|
|
|
717
740
|
}
|
|
718
741
|
|
|
719
742
|
function pickRandom(array) {
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
743
|
+
if (!array || array.length === 0) return undefined;
|
|
744
|
+
// PERFORMANCE: Use Math.random() instead of chance.integer() for simple cases
|
|
745
|
+
const randomIndex = Math.floor(Math.random() * array.length);
|
|
746
|
+
return array[randomIndex];
|
|
724
747
|
}
|
|
725
748
|
|
|
726
749
|
function shuffleExceptFirst(array) {
|
|
@@ -918,25 +941,25 @@ function buildFileNames(config) {
|
|
|
918
941
|
* @param {[string, number][]} arrayOfArrays
|
|
919
942
|
*/
|
|
920
943
|
function progress(arrayOfArrays) {
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
944
|
+
const terminalWidth = process.stdout.columns || 120;
|
|
945
|
+
|
|
946
|
+
// Clear the entire line
|
|
947
|
+
readline.cursorTo(process.stdout, 0);
|
|
948
|
+
readline.clearLine(process.stdout, 0);
|
|
949
|
+
|
|
950
|
+
// Build message with better formatting
|
|
951
|
+
const items = arrayOfArrays.map(([thing, p]) => {
|
|
952
|
+
return `${thing}: ${comma(p)}`;
|
|
953
|
+
});
|
|
954
|
+
|
|
955
|
+
const message = items.join(' │ ');
|
|
956
|
+
|
|
957
|
+
// Ensure we don't exceed terminal width
|
|
958
|
+
const finalMessage = message.length > terminalWidth
|
|
959
|
+
? message.substring(0, terminalWidth - 3) + '...'
|
|
960
|
+
: message.padEnd(terminalWidth, ' ');
|
|
961
|
+
|
|
962
|
+
process.stdout.write(finalMessage);
|
|
940
963
|
}
|
|
941
964
|
|
|
942
965
|
function openFinder(path, callback) {
|
|
@@ -1271,6 +1294,7 @@ export {
|
|
|
1271
1294
|
deepClone,
|
|
1272
1295
|
initChance,
|
|
1273
1296
|
getChance,
|
|
1297
|
+
decimal,
|
|
1274
1298
|
|
|
1275
1299
|
validTime,
|
|
1276
1300
|
validEvent,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "make-mp-data",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.21",
|
|
4
4
|
"description": "builds all mixpanel primitives for a given project",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"README.md"
|
|
17
17
|
],
|
|
18
18
|
"scripts": {
|
|
19
|
+
"new:dungeon": "node ./scripts/create-dungeon.mjs",
|
|
19
20
|
"start": "node ./index.js",
|
|
20
21
|
"dev": "nodemon scratch.mjs --ignore ./data/*",
|
|
21
22
|
"prune": "rm -f ./data/* && rm -f ./tmp/* && rm -f vscode-profile-*",
|
|
@@ -24,10 +25,9 @@
|
|
|
24
25
|
"test": "NODE_ENV=test vitest run",
|
|
25
26
|
"coverage": "vitest run --coverage && open ./coverage/index.html",
|
|
26
27
|
"typecheck": "tsc --noEmit",
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"benchmark:phase1": "node ./tests/benchmark/phase1-performance.mjs",
|
|
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
31
|
"test:quick": "node ./tests/benchmark/quick-test.mjs",
|
|
32
32
|
"exp:soup": "node ./tests/testSoup.mjs",
|
|
33
33
|
"func:local": "functions-framework --target=entry",
|
|
@@ -61,17 +61,16 @@
|
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@google-cloud/functions-framework": "^3.4.2",
|
|
63
63
|
"@google-cloud/storage": "^7.14.0",
|
|
64
|
-
"
|
|
65
|
-
"ak-
|
|
66
|
-
"ak-
|
|
67
|
-
"ak-tools": "^1.1.0",
|
|
64
|
+
"ak-fetch": "^2.0.12",
|
|
65
|
+
"ak-gemini": "^1.0.59",
|
|
66
|
+
"ak-tools": "^1.1.1",
|
|
68
67
|
"chance": "^1.1.11",
|
|
69
68
|
"chart.js": "^3.9.1",
|
|
70
69
|
"chartjs-node-canvas": "^4.1.6",
|
|
71
70
|
"dayjs": "^1.11.11",
|
|
72
71
|
"dotenv": "^16.4.5",
|
|
73
72
|
"google-auth-library": "^9.15.0",
|
|
74
|
-
"mixpanel-import": "^2.8.
|
|
73
|
+
"mixpanel-import": "^2.8.15",
|
|
75
74
|
"p-limit": "^3.1.0",
|
|
76
75
|
"yargs": "^17.7.2"
|
|
77
76
|
},
|
|
@@ -87,4 +86,4 @@
|
|
|
87
86
|
"tmp/"
|
|
88
87
|
]
|
|
89
88
|
}
|
|
90
|
-
}
|
|
89
|
+
}
|