@adminiumjs/llm 0.1.0-rc.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.
- package/LICENSE +661 -0
- package/dist/apply/diff.d.ts +79 -0
- package/dist/apply/diff.d.ts.map +1 -0
- package/dist/apply/diff.js +459 -0
- package/dist/apply/diff.js.map +1 -0
- package/dist/apply/index.d.ts +6 -0
- package/dist/apply/index.d.ts.map +1 -0
- package/dist/apply/index.js +6 -0
- package/dist/apply/index.js.map +1 -0
- package/dist/apply/normalize.d.ts +55 -0
- package/dist/apply/normalize.d.ts.map +1 -0
- package/dist/apply/normalize.js +232 -0
- package/dist/apply/normalize.js.map +1 -0
- package/dist/apply/plan.d.ts +206 -0
- package/dist/apply/plan.d.ts.map +1 -0
- package/dist/apply/plan.js +422 -0
- package/dist/apply/plan.js.map +1 -0
- package/dist/apply/suggestion-id.d.ts +103 -0
- package/dist/apply/suggestion-id.d.ts.map +1 -0
- package/dist/apply/suggestion-id.js +121 -0
- package/dist/apply/suggestion-id.js.map +1 -0
- package/dist/crypto.d.ts +48 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +35 -0
- package/dist/crypto.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/prompt/builder.d.ts +27 -0
- package/dist/prompt/builder.d.ts.map +1 -0
- package/dist/prompt/builder.js +263 -0
- package/dist/prompt/builder.js.map +1 -0
- package/dist/prompt/chunker.d.ts +89 -0
- package/dist/prompt/chunker.d.ts.map +1 -0
- package/dist/prompt/chunker.js +383 -0
- package/dist/prompt/chunker.js.map +1 -0
- package/dist/prompt/index.d.ts +14 -0
- package/dist/prompt/index.d.ts.map +1 -0
- package/dist/prompt/index.js +14 -0
- package/dist/prompt/index.js.map +1 -0
- package/dist/prompt/serializer.d.ts +138 -0
- package/dist/prompt/serializer.d.ts.map +1 -0
- package/dist/prompt/serializer.js +178 -0
- package/dist/prompt/serializer.js.map +1 -0
- package/dist/prompt/templates/v1.d.ts +41 -0
- package/dist/prompt/templates/v1.d.ts.map +1 -0
- package/dist/prompt/templates/v1.js +312 -0
- package/dist/prompt/templates/v1.js.map +1 -0
- package/dist/prompt/token-estimate.d.ts +16 -0
- package/dist/prompt/token-estimate.d.ts.map +1 -0
- package/dist/prompt/token-estimate.js +20 -0
- package/dist/prompt/token-estimate.js.map +1 -0
- package/dist/prompt/types.d.ts +134 -0
- package/dist/prompt/types.d.ts.map +1 -0
- package/dist/prompt/types.js +44 -0
- package/dist/prompt/types.js.map +1 -0
- package/dist/providers/anthropic.d.ts +3 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +65 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/factory.d.ts +3 -0
- package/dist/providers/factory.d.ts.map +1 -0
- package/dist/providers/factory.js +37 -0
- package/dist/providers/factory.js.map +1 -0
- package/dist/providers/http.d.ts +56 -0
- package/dist/providers/http.d.ts.map +1 -0
- package/dist/providers/http.js +148 -0
- package/dist/providers/http.js.map +1 -0
- package/dist/providers/index.d.ts +15 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +15 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/model-catalog.d.ts +21 -0
- package/dist/providers/model-catalog.d.ts.map +1 -0
- package/dist/providers/model-catalog.js +121 -0
- package/dist/providers/model-catalog.js.map +1 -0
- package/dist/providers/ollama.d.ts +3 -0
- package/dist/providers/ollama.d.ts.map +1 -0
- package/dist/providers/ollama.js +55 -0
- package/dist/providers/ollama.js.map +1 -0
- package/dist/providers/openai-compatible.d.ts +3 -0
- package/dist/providers/openai-compatible.d.ts.map +1 -0
- package/dist/providers/openai-compatible.js +50 -0
- package/dist/providers/openai-compatible.js.map +1 -0
- package/dist/providers/openai.d.ts +18 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +68 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/types.d.ts +112 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +55 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/response/errors.d.ts +60 -0
- package/dist/response/errors.d.ts.map +1 -0
- package/dist/response/errors.js +101 -0
- package/dist/response/errors.js.map +1 -0
- package/dist/response/extract.d.ts +38 -0
- package/dist/response/extract.d.ts.map +1 -0
- package/dist/response/extract.js +77 -0
- package/dist/response/extract.js.map +1 -0
- package/dist/response/index.d.ts +8 -0
- package/dist/response/index.d.ts.map +1 -0
- package/dist/response/index.js +8 -0
- package/dist/response/index.js.map +1 -0
- package/dist/response/merge.d.ts +53 -0
- package/dist/response/merge.d.ts.map +1 -0
- package/dist/response/merge.js +348 -0
- package/dist/response/merge.js.map +1 -0
- package/dist/response/referential.d.ts +113 -0
- package/dist/response/referential.d.ts.map +1 -0
- package/dist/response/referential.js +556 -0
- package/dist/response/referential.js.map +1 -0
- package/dist/response/schema.d.ts +506 -0
- package/dist/response/schema.d.ts.map +1 -0
- package/dist/response/schema.js +191 -0
- package/dist/response/schema.js.map +1 -0
- package/dist/response/validate.d.ts +29 -0
- package/dist/response/validate.d.ts.map +1 -0
- package/dist/response/validate.js +167 -0
- package/dist/response/validate.js.map +1 -0
- package/dist/types.d.ts +95 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +49 -0
- package/dist/types.js.map +1 -0
- package/package.json +30 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/** Text columns with `distinctCount` at or below this are pseudo-enum candidates (§4.2). */
|
|
2
|
+
export const PSEUDO_ENUM_MAX_DISTINCT = 24;
|
|
3
|
+
/** Header line the opt-in sampling block leads with (§5.2 builder note). */
|
|
4
|
+
export const SAMPLING_BLOCK_HEADER = '=== INPUT: SAMPLE VALUES (user opted in; PII columns excluded) ===';
|
|
5
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
6
|
+
/** Compact, deterministic JSON for injection into the prompt. */
|
|
7
|
+
export function toPromptJson(value) {
|
|
8
|
+
return JSON.stringify(value);
|
|
9
|
+
}
|
|
10
|
+
/** `TableRef` → the model's qualified `"schema.table"` id (mysql/sqlite have a null schema). */
|
|
11
|
+
export function qualifyTableRef(ref, defaultSchema) {
|
|
12
|
+
return `${ref.schema ?? defaultSchema}.${ref.name}`;
|
|
13
|
+
}
|
|
14
|
+
function statsKey(tableId, column) {
|
|
15
|
+
return `${tableId}\x00${column}`;
|
|
16
|
+
}
|
|
17
|
+
/** `(tableId, column) → distinctCount | null`, built from the stats. */
|
|
18
|
+
function distinctCountLookup(stats, defaultSchema) {
|
|
19
|
+
const map = new Map();
|
|
20
|
+
for (const result of stats) {
|
|
21
|
+
const tableId = qualifyTableRef(result.table, defaultSchema);
|
|
22
|
+
for (const column of result.columns) {
|
|
23
|
+
map.set(statsKey(tableId, column.column), column.distinctCount);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return (tableId, column) => map.get(statsKey(tableId, column)) ?? null;
|
|
27
|
+
}
|
|
28
|
+
/** `(tableId, column) → true` for PII-suspected or secret columns (never sampled). */
|
|
29
|
+
function sensitiveColumnLookup(model) {
|
|
30
|
+
const set = new Set();
|
|
31
|
+
for (const table of model.tables) {
|
|
32
|
+
for (const column of table.columns) {
|
|
33
|
+
if (isPiiSuspected(column) || isSecret(column)) {
|
|
34
|
+
set.add(statsKey(table.id, column.name));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return (tableId, column) => set.has(statsKey(tableId, column));
|
|
39
|
+
}
|
|
40
|
+
function isPiiSuspected(column) {
|
|
41
|
+
return (column.semantics?.flags.pii ?? null) !== null;
|
|
42
|
+
}
|
|
43
|
+
function isSecret(column) {
|
|
44
|
+
return column.semantics?.flags.secret ?? false;
|
|
45
|
+
}
|
|
46
|
+
export function serializeSchemaIr(model, options = {}) {
|
|
47
|
+
const stubs = options.stubTables ?? new Set();
|
|
48
|
+
const distinctCount = distinctCountLookup(options.stats ?? [], model.defaultSchema);
|
|
49
|
+
const enumValues = new Map(model.enums.map((def) => [def.id, def.values]));
|
|
50
|
+
const tables = model.tables.map((table) => {
|
|
51
|
+
if (stubs.has(table.id)) {
|
|
52
|
+
return { table: table.id, stub: true, columns: table.columns.map((c) => c.name) };
|
|
53
|
+
}
|
|
54
|
+
const role = table.semantics?.role;
|
|
55
|
+
return {
|
|
56
|
+
table: table.id,
|
|
57
|
+
...(role !== undefined ? { role } : {}),
|
|
58
|
+
columns: table.columns.map((column) => serializeColumn(column, distinctCount(table.id, column.name))),
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
const enumColumns = [];
|
|
62
|
+
for (const table of model.tables) {
|
|
63
|
+
if (stubs.has(table.id))
|
|
64
|
+
continue;
|
|
65
|
+
for (const column of table.columns) {
|
|
66
|
+
if (column.enumRef === null)
|
|
67
|
+
continue;
|
|
68
|
+
const values = enumValues.get(column.enumRef);
|
|
69
|
+
if (values === undefined)
|
|
70
|
+
continue;
|
|
71
|
+
enumColumns.push({ table: table.id, column: column.name, values: [...values] });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const declaredRelations = model.relations
|
|
75
|
+
.filter((relation) => relation.kind === 'declared-fk')
|
|
76
|
+
.map((relation) => ({
|
|
77
|
+
fromTable: relation.from.tableId,
|
|
78
|
+
fromColumns: [...relation.from.columns],
|
|
79
|
+
toTable: relation.to.tableId,
|
|
80
|
+
toColumns: [...relation.to.columns],
|
|
81
|
+
cardinality: relation.cardinality,
|
|
82
|
+
}));
|
|
83
|
+
return {
|
|
84
|
+
database: { name: model.name, dialect: model.dialect, defaultSchema: model.defaultSchema },
|
|
85
|
+
tables,
|
|
86
|
+
enumColumns,
|
|
87
|
+
declaredRelations,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function serializeColumn(column, distinctCount) {
|
|
91
|
+
const pii = isPiiSuspected(column);
|
|
92
|
+
const secret = isSecret(column);
|
|
93
|
+
const isText = column.logicalType === 'text' || column.logicalType === 'varchar';
|
|
94
|
+
const pseudoEnum = isText &&
|
|
95
|
+
!pii &&
|
|
96
|
+
!secret &&
|
|
97
|
+
column.enumRef === null &&
|
|
98
|
+
distinctCount !== null &&
|
|
99
|
+
distinctCount <= PSEUDO_ENUM_MAX_DISTINCT;
|
|
100
|
+
return {
|
|
101
|
+
column: column.name,
|
|
102
|
+
type: column.logicalType,
|
|
103
|
+
nullable: column.nullable,
|
|
104
|
+
...(column.isPrimaryKey ? { primaryKey: true } : {}),
|
|
105
|
+
...(column.isUnique ? { unique: true } : {}),
|
|
106
|
+
...(column.enumRef !== null ? { enum: column.enumRef } : {}),
|
|
107
|
+
...(column.references !== null
|
|
108
|
+
? { references: `${column.references.tableId}.${column.references.column}` }
|
|
109
|
+
: {}),
|
|
110
|
+
...(pii ? { piiSuspected: true } : {}),
|
|
111
|
+
...(secret ? { secret: true } : {}),
|
|
112
|
+
...(pseudoEnum ? { pseudoEnumCandidate: true } : {}),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Format `StatsResult[]` into the aggregates-only stats block. Deliberately
|
|
117
|
+
* copies ONLY row counts, null fractions and distinct counts — never `min`,
|
|
118
|
+
* `max` or `sampleValues`, which are cell values and belong to the opt-in
|
|
119
|
+
* sampling block. This is what guarantees the sample-free default at the stats
|
|
120
|
+
* layer regardless of what the adapter happened to collect.
|
|
121
|
+
*/
|
|
122
|
+
export function serializeStats(stats, options = {}) {
|
|
123
|
+
const defaultSchema = options.defaultSchema ?? 'public';
|
|
124
|
+
return {
|
|
125
|
+
tables: stats.map((result) => ({
|
|
126
|
+
table: qualifyTableRef(result.table, defaultSchema),
|
|
127
|
+
rowCountEstimate: result.rowCountEstimate,
|
|
128
|
+
rowCountExact: result.rowCountExact,
|
|
129
|
+
columns: result.columns.map((column) => ({
|
|
130
|
+
column: column.column,
|
|
131
|
+
nullFraction: column.nullFraction,
|
|
132
|
+
distinctCount: column.distinctCount,
|
|
133
|
+
})),
|
|
134
|
+
})),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
// ─── Sampling block (opt-in; the ONLY place cell values appear) ──────────────
|
|
138
|
+
/**
|
|
139
|
+
* Build the `{{SAMPLING_BLOCK}}` text. Returns `''` when `sampling` is null
|
|
140
|
+
* (sample-free default). When opted in, renders the header plus a JSON object of
|
|
141
|
+
* per-column `min`/`max`/`sampleValues` — EXCLUDING every PII-suspected or
|
|
142
|
+
* secret column (looked up from the model), and truncating each column's value
|
|
143
|
+
* list to `sampling.maxValuesPerColumn`.
|
|
144
|
+
*/
|
|
145
|
+
export function serializeSampling(model, stats, sampling) {
|
|
146
|
+
if (sampling === null)
|
|
147
|
+
return '';
|
|
148
|
+
const payload = serializeSamplingPayload(model, stats, sampling.maxValuesPerColumn);
|
|
149
|
+
return `${SAMPLING_BLOCK_HEADER}\n${toPromptJson(payload)}`;
|
|
150
|
+
}
|
|
151
|
+
export function serializeSamplingPayload(model, stats, maxValuesPerColumn) {
|
|
152
|
+
const isSensitive = sensitiveColumnLookup(model);
|
|
153
|
+
const tables = [];
|
|
154
|
+
for (const result of stats) {
|
|
155
|
+
const tableId = qualifyTableRef(result.table, model.defaultSchema);
|
|
156
|
+
const columns = [];
|
|
157
|
+
for (const column of result.columns) {
|
|
158
|
+
if (isSensitive(tableId, column.column))
|
|
159
|
+
continue;
|
|
160
|
+
const hasMin = column.min !== undefined;
|
|
161
|
+
const hasMax = column.max !== undefined;
|
|
162
|
+
const values = column.sampleValues;
|
|
163
|
+
const hasValues = values !== undefined && values.length > 0;
|
|
164
|
+
if (!hasMin && !hasMax && !hasValues)
|
|
165
|
+
continue;
|
|
166
|
+
columns.push({
|
|
167
|
+
column: column.column,
|
|
168
|
+
...(hasMin ? { min: column.min } : {}),
|
|
169
|
+
...(hasMax ? { max: column.max } : {}),
|
|
170
|
+
...(hasValues ? { sampleValues: values.slice(0, maxValuesPerColumn) } : {}),
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
if (columns.length > 0)
|
|
174
|
+
tables.push({ table: tableId, columns });
|
|
175
|
+
}
|
|
176
|
+
return { tables };
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=serializer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serializer.js","sourceRoot":"","sources":["../../src/prompt/serializer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §5.1 — the system section. Fixed policy text (no tokens); it frames the task,
|
|
3
|
+
* the quality bar and the STRICT output rules. Reused verbatim by
|
|
4
|
+
* {@link PROMPT_MERGE_V1} (§4.5).
|
|
5
|
+
*/
|
|
6
|
+
export declare const PROMPT_V1_SYSTEM = "You are a senior data architect and admin-interface designer. You work for\nAdminium, an engine that introspects a relational database schema and\nauto-generates a complete admin dashboard from it. A heuristic pipeline has\nalready produced a baseline; your judgment refines it. Your output is machine-\napplied: it will be schema-validated, cross-checked against the real database\nschema, and reviewed by a human as a diff before anything is applied.\n\nTASK\nAnalyze the database schema and statistics provided in the user message and\nreturn enrichment decisions as a single JSON object, following the response\nschema and the decision instructions exactly.\n\nQUALITY BAR\n- Domain understanding first: infer what the business behind this schema does\n from table/column names, types, relations and statistics, and make every\n label, grouping and recommendation read like it was written by an expert in\n that domain \u2014 not like it was mechanically derived from identifiers.\n- Labels are for non-technical staff: \"Purchase orders\", never \"tbl_po_hdr\".\n Descriptions are one sentence, concrete, and state what a row represents.\n- Translations must be native-quality, using the domain's standard terminology\n in each language, and consistent across the whole response (the same concept\n gets the same term everywhere). Do not transliterate; translate.\n- Be decisive but honest: every suggestion carries a \"confidence\" between 0.0\n and 1.0. Prefer a lower confidence over inventing detail. If the input gives\n no basis for a decision, omit that suggestion entirely.\n- Ground every recommendation in evidence from the input (name patterns, types,\n cardinalities, null ratios, relation shapes). \"reason\" fields must cite that\n evidence in one short sentence.\n\nOUTPUT RULES \u2014 STRICT\n1. Respond with EXACTLY ONE JSON object and nothing else. No prose, no markdown,\n no code fences, no comments, no trailing commas. The first character of your\n response must be \"{\" and the last must be \"}\".\n2. The first key must be \"schema_version\" with the exact value\n \"adminium.llm/v1\".\n3. Reference ONLY tables and columns that appear in the input schema. Never\n invent, rename, pluralize or \"correct\" an identifier. Tables marked\n \"stub\": true are context only \u2014 emit no suggestions for them.\n4. Use only the allowed page-template ids, widget ids, tone values and masking\n values listed in the user message. Icon names must be kebab-case lucide\n icon names (e.g. \"shopping-cart\", \"users\", \"package\").\n5. Localized text objects must contain exactly the requested locale keys \u2014\n no more, no fewer. \"en_US\" is always required.\n6. Numbers for \"confidence\" use at most 2 decimal places. Keep all \"reason\"\n fields under 200 characters.\n7. If you cannot comply with any rule, return\n {\"schema_version\":\"adminium.llm/v1\",\"error\":\"<one-sentence reason>\"} and\n nothing else.";
|
|
7
|
+
/**
|
|
8
|
+
* §5.2 — the user section template. Carries the `{{DOUBLE_BRACE}}` tokens the
|
|
9
|
+
* builder fills: run id, chunk info, requested-sections list, the allowed
|
|
10
|
+
* vocabularies, target locales, and the serialized schema IR / statistics /
|
|
11
|
+
* (opt-in) sample-values blocks. The ten numbered decision blocks map 1:1 to
|
|
12
|
+
* {@link RequestedSection} ids; the builder deletes deselected blocks (§4.4).
|
|
13
|
+
*/
|
|
14
|
+
export declare const PROMPT_V1_USER = "=== CONTEXT ===\nAdminium generated an admin dashboard for the database described below. Your\ndecisions refine: naming, navigation, enum semantics, relations, key columns,\npage layouts, dashboard widgets, privacy flags, icons and micro-copy.\nrun_id: {{RUN_ID}} (echo it back as \"run_id\" if convenient; optional)\n{{CHUNK_INFO}}\n\n=== YOUR DECISIONS ===\nProduce the following, per the response schema at the end of this message.\n{{REQUESTED_SECTIONS_LIST}}\n\n1. LABELS & DESCRIPTIONS (response: tables[].label, tables[].description,\n tables[].columns[].label, tables[].columns[].description)\n For every non-stub table and every column: a short human label (Title Case\n for tables, Sentence case for columns; \u2264 28 chars for tables, \u2264 24 for\n columns) and a one-sentence description. Localize both into every requested\n locale. Column descriptions are optional for self-explanatory columns\n (id, created_at) \u2014 omit rather than pad.\n\n2. DOMAIN GROUPING (response: navGroups)\n Group all tables into 3\u20137 navigation groups that reflect business domains\n (e.g. \"Sales\", \"Catalog\", \"People\"), not technical layers. Every non-stub\n table appears in exactly one group. Order groups by operational importance;\n order tables within a group the same way. Give each group a localized label\n and a lucide icon.\n\n3. ENUM SEMANTICS (response: enums) \u2014 for every column listed under\n \"enumColumns\" in the input:\n - kind: \"workflow\" if values are states a record moves through over time\n (e.g. draft \u2192 active \u2192 closed); \"category\" if values are unordered\n classifications (e.g. region, plan tier).\n - order: for workflow enums, the full value list in lifecycle order\n (must be a permutation of the given values). null for category enums.\n - tones: map EVERY value to exactly one of:\n \"pos\" (successfully completed / healthy), \"warn\" (needs attention /\n pending / at risk), \"danger\" (failed / blocked / cancelled),\n \"accent\" (actively in progress), \"muted\" (inactive / draft / neutral).\n - terminal: for workflow enums, the values that end the lifecycle.\n\n4. RELATIONS (response: relations)\n - confirmed: for each relation under \"declaredRelations\" in the input,\n state whether it is semantically what it looks like, and name the\n relationship (e.g. \"orders belong to customers\").\n - inferred: relations that SHOULD exist but are not declared as foreign\n keys. Use naming conventions (customer_id \u2192 customers.id), matching\n types, and statistics (a column whose distinctCount \u2248 another table's\n rowCountEstimate is a strong signal). Give kind\n (\"many-to-one\" | \"one-to-one\" | \"many-to-many-via\"), evidence, and\n confidence. Never re-state a declared relation as inferred.\n\n5. KEY COLUMNS (response: tables[].displayColumn, tables[].naturalKey)\n - displayColumn: the single column whose value best represents a row to a\n human (a name/title/number \u2014 never a UUID/serial id). null only if truly\n nothing qualifies.\n - naturalKey: minimal column set a human would use to uniquely identify a\n row (e.g. [\"sku\"], [\"email\"], [\"order_number\"]). null if none.\n\n6. PAGE TEMPLATES (response: tables[].pageTemplates)\n Recommend 1\u20133 page templates per table from ALLOWED PAGE TEMPLATES below,\n ranked. \"page-crud\" is always generated and must not be recommended.\n For each: cite which trigger(s) from the taxonomy fired (\"triggers\"), and\n give a one-sentence reason grounded in this schema. Do not recommend a\n template whose trigger conditions this table does not meet.\n\n7. DASHBOARD WIDGETS (response: dashboards)\n Propose up to 6 dashboards, one per major domain (aligned with your nav\n groups). For each, rank up to 8 widgets from ALLOWED WIDGETS below. Every\n widget binds to real columns: metric column (numeric), dimension/group-by\n column, time column where applicable, and an aggregation\n (\"count\" | \"sum\" | \"avg\" | \"min\" | \"max\"). Spans on a 12-column grid:\n KPI = 3, donut/funnel = 4, bar/table = 6, line/area = 8.\n\n8. PII & MASKING (response: tables[].columns[].pii)\n Flag every column plausibly containing personal or sensitive data.\n kind: one of \"email\" | \"phone\" | \"name\" | \"address\" | \"gov-id\" | \"dob\" |\n \"financial\" | \"credentials\" | \"health\" | \"ip\" | \"location\" | \"other\".\n masking: one of \"mask-email\" (j***@d***.com) | \"mask-partial\" (first/last\n char visible) | \"last4\" | \"redact\" (fully hidden) | \"hash\" | \"none\"\n (flag only). Columns already marked \"piiSuspected\": true in the input were\n flagged by heuristics \u2014 confirm or reject them explicitly (pii: null means\n explicitly not PII).\n\n9. ICONS (response: tables[].icon, navGroups[].icon)\n One lucide icon per table and per nav group, kebab-case (e.g. \"users\",\n \"shopping-cart\", \"package\", \"receipt\", \"calendar\", \"building-2\",\n \"credit-card\", \"truck\", \"message-square\", \"file-text\"). Choose the most\n domain-specific icon; avoid reusing one icon for multiple tables in the\n same group.\n\n10. MICRO-COPY (response: tables[].microcopy)\n Localized, per table:\n - emptyState.headline (\u2264 40 chars) and emptyState.guidance (one sentence,\n ends with what to do first) \u2014 shown when the table has no rows.\n - pageSubtitle: \u2264 60 chars shown under the page title, describing what\n the page manages (e.g. \"Track and fulfil customer orders\").\n Tone: calm, concrete, no exclamation marks, no marketing language.\n\n=== TRIGGER TAXONOMY (for decision 6) ===\n| template id | trigger conditions |\n|--------------------|--------------------|\n| page-dashboard | numeric measure columns + a timestamp column exist in the domain |\n| page-master-detail | enum-heavy table with rich per-record detail (many columns / child conversation table) |\n| page-queue-inbox | workflow enum with pending/approved-style states, or read/unread boolean, + assignee/requester FK |\n| page-board | status enum classified \"workflow\"; optional second categorical column \u2192 swimlanes; period column \u2192 roadmap |\n| page-calendar | date/datetime column + a title-like text column |\n| page-scheduler | person FK \u00D7 date \u00D7 shift/type columns, or hours-per-project assignment shape |\n| page-directory | people-shaped table: name + email (+ role/department enum, avatar) |\n| page-log-viewer | append-only shape: timestamp + actor + action/level, high row count, few updates |\n| page-files | file/attachment shape: filename/mime/size/url columns or parent-folder self-FK |\n| page-chat | conversation + message table pair (thread FK, sender FK, body, timestamp) |\n(Templates not listed here \u2014 settings, wizard, builder, auth, billing, api,\nmarketing, system pages \u2014 are platform-owned; never recommend them for user\ntables.)\n\n=== ALLOWED PAGE TEMPLATES ===\n{{ALLOWED_PAGE_TEMPLATE_IDS_JSON}}\n\n=== ALLOWED WIDGETS ===\n{{ALLOWED_WIDGET_IDS_JSON}}\n\n=== TARGET LOCALES ===\n{{LOCALES_JSON}}\nEvery localized text object uses exactly these keys.\n\n=== RESPONSE SCHEMA (adminium.llm/v1) ===\nReturn one JSON object of this shape. TypeScript-style notation; \"?\" = may be\nomitted; L10n = { \"<locale>\": string } with exactly the target locales.\n\n{\n schema_version: \"adminium.llm/v1\", // REQUIRED, first key\n run_id?: string,\n tables: [{\n table: string, // \"schema.table\", exactly as in input\n confidence: number, // 0..1, overall for this table's suggestions\n label: L10n, description: L10n,\n icon: string, // lucide kebab-case\n displayColumn: string | null,\n naturalKey: string[] | null,\n pageTemplates: [{ template: string, rank: number, triggers: string[],\n reason: string, confidence: number }],\n microcopy: { emptyState: { headline: L10n, guidance: L10n },\n pageSubtitle: L10n },\n columns: [{\n column: string,\n label: L10n, description?: L10n,\n pii: null | { kind: string, masking: string, reason: string,\n confidence: number }\n }]\n }],\n enums: [{ table: string, column: string, kind: \"workflow\" | \"category\",\n order: string[] | null, terminal?: string[],\n tones: { [value: string]: \"pos\"|\"warn\"|\"danger\"|\"accent\"|\"muted\" },\n reason: string, confidence: number }],\n relations: {\n confirmed: [{ fromTable: string, fromColumns: string[], toTable: string,\n toColumns: string[], semantics: string, correct: boolean,\n confidence: number }],\n inferred: [{ fromTable: string, fromColumns: string[], toTable: string,\n toColumns: string[],\n kind: \"many-to-one\"|\"one-to-one\"|\"many-to-many-via\",\n viaTable?: string, evidence: string, confidence: number }]\n },\n navGroups: [{ id: string, // kebab-case slug\n label: L10n, icon: string, order: number,\n tables: string[], confidence: number }],\n dashboards: [{ id: string, domain: string, label: L10n, order: number,\n tables: string[],\n widgets: [{ widget: string, rank: number, span: number,\n table: string, metricColumn?: string,\n dimensionColumn?: string, timeColumn?: string,\n agg?: \"count\"|\"sum\"|\"avg\"|\"min\"|\"max\",\n titleEn: string, reason: string,\n confidence: number }] }],\n notes?: string[] // \u2264 5 short caveats about the schema\n}\n\n=== INPUT: DATABASE SCHEMA ===\n{{SCHEMA_IR_JSON}}\n\n=== INPUT: STATISTICS (aggregates only \u2014 no row data) ===\n{{STATS_JSON}}\n{{SAMPLING_BLOCK}}";
|
|
15
|
+
/**
|
|
16
|
+
* The v1 prompt as a pair. The builder renders these into a
|
|
17
|
+
* {@link PromptArtifact}; nothing else should read the raw strings except the
|
|
18
|
+
* snapshot test.
|
|
19
|
+
*/
|
|
20
|
+
export declare const PROMPT_V1: {
|
|
21
|
+
readonly version: "adminium.prompt/v1.1";
|
|
22
|
+
readonly system: "You are a senior data architect and admin-interface designer. You work for\nAdminium, an engine that introspects a relational database schema and\nauto-generates a complete admin dashboard from it. A heuristic pipeline has\nalready produced a baseline; your judgment refines it. Your output is machine-\napplied: it will be schema-validated, cross-checked against the real database\nschema, and reviewed by a human as a diff before anything is applied.\n\nTASK\nAnalyze the database schema and statistics provided in the user message and\nreturn enrichment decisions as a single JSON object, following the response\nschema and the decision instructions exactly.\n\nQUALITY BAR\n- Domain understanding first: infer what the business behind this schema does\n from table/column names, types, relations and statistics, and make every\n label, grouping and recommendation read like it was written by an expert in\n that domain — not like it was mechanically derived from identifiers.\n- Labels are for non-technical staff: \"Purchase orders\", never \"tbl_po_hdr\".\n Descriptions are one sentence, concrete, and state what a row represents.\n- Translations must be native-quality, using the domain's standard terminology\n in each language, and consistent across the whole response (the same concept\n gets the same term everywhere). Do not transliterate; translate.\n- Be decisive but honest: every suggestion carries a \"confidence\" between 0.0\n and 1.0. Prefer a lower confidence over inventing detail. If the input gives\n no basis for a decision, omit that suggestion entirely.\n- Ground every recommendation in evidence from the input (name patterns, types,\n cardinalities, null ratios, relation shapes). \"reason\" fields must cite that\n evidence in one short sentence.\n\nOUTPUT RULES — STRICT\n1. Respond with EXACTLY ONE JSON object and nothing else. No prose, no markdown,\n no code fences, no comments, no trailing commas. The first character of your\n response must be \"{\" and the last must be \"}\".\n2. The first key must be \"schema_version\" with the exact value\n \"adminium.llm/v1\".\n3. Reference ONLY tables and columns that appear in the input schema. Never\n invent, rename, pluralize or \"correct\" an identifier. Tables marked\n \"stub\": true are context only — emit no suggestions for them.\n4. Use only the allowed page-template ids, widget ids, tone values and masking\n values listed in the user message. Icon names must be kebab-case lucide\n icon names (e.g. \"shopping-cart\", \"users\", \"package\").\n5. Localized text objects must contain exactly the requested locale keys —\n no more, no fewer. \"en_US\" is always required.\n6. Numbers for \"confidence\" use at most 2 decimal places. Keep all \"reason\"\n fields under 200 characters.\n7. If you cannot comply with any rule, return\n {\"schema_version\":\"adminium.llm/v1\",\"error\":\"<one-sentence reason>\"} and\n nothing else.";
|
|
23
|
+
readonly user: "=== CONTEXT ===\nAdminium generated an admin dashboard for the database described below. Your\ndecisions refine: naming, navigation, enum semantics, relations, key columns,\npage layouts, dashboard widgets, privacy flags, icons and micro-copy.\nrun_id: {{RUN_ID}} (echo it back as \"run_id\" if convenient; optional)\n{{CHUNK_INFO}}\n\n=== YOUR DECISIONS ===\nProduce the following, per the response schema at the end of this message.\n{{REQUESTED_SECTIONS_LIST}}\n\n1. LABELS & DESCRIPTIONS (response: tables[].label, tables[].description,\n tables[].columns[].label, tables[].columns[].description)\n For every non-stub table and every column: a short human label (Title Case\n for tables, Sentence case for columns; ≤ 28 chars for tables, ≤ 24 for\n columns) and a one-sentence description. Localize both into every requested\n locale. Column descriptions are optional for self-explanatory columns\n (id, created_at) — omit rather than pad.\n\n2. DOMAIN GROUPING (response: navGroups)\n Group all tables into 3–7 navigation groups that reflect business domains\n (e.g. \"Sales\", \"Catalog\", \"People\"), not technical layers. Every non-stub\n table appears in exactly one group. Order groups by operational importance;\n order tables within a group the same way. Give each group a localized label\n and a lucide icon.\n\n3. ENUM SEMANTICS (response: enums) — for every column listed under\n \"enumColumns\" in the input:\n - kind: \"workflow\" if values are states a record moves through over time\n (e.g. draft → active → closed); \"category\" if values are unordered\n classifications (e.g. region, plan tier).\n - order: for workflow enums, the full value list in lifecycle order\n (must be a permutation of the given values). null for category enums.\n - tones: map EVERY value to exactly one of:\n \"pos\" (successfully completed / healthy), \"warn\" (needs attention /\n pending / at risk), \"danger\" (failed / blocked / cancelled),\n \"accent\" (actively in progress), \"muted\" (inactive / draft / neutral).\n - terminal: for workflow enums, the values that end the lifecycle.\n\n4. RELATIONS (response: relations)\n - confirmed: for each relation under \"declaredRelations\" in the input,\n state whether it is semantically what it looks like, and name the\n relationship (e.g. \"orders belong to customers\").\n - inferred: relations that SHOULD exist but are not declared as foreign\n keys. Use naming conventions (customer_id → customers.id), matching\n types, and statistics (a column whose distinctCount ≈ another table's\n rowCountEstimate is a strong signal). Give kind\n (\"many-to-one\" | \"one-to-one\" | \"many-to-many-via\"), evidence, and\n confidence. Never re-state a declared relation as inferred.\n\n5. KEY COLUMNS (response: tables[].displayColumn, tables[].naturalKey)\n - displayColumn: the single column whose value best represents a row to a\n human (a name/title/number — never a UUID/serial id). null only if truly\n nothing qualifies.\n - naturalKey: minimal column set a human would use to uniquely identify a\n row (e.g. [\"sku\"], [\"email\"], [\"order_number\"]). null if none.\n\n6. PAGE TEMPLATES (response: tables[].pageTemplates)\n Recommend 1–3 page templates per table from ALLOWED PAGE TEMPLATES below,\n ranked. \"page-crud\" is always generated and must not be recommended.\n For each: cite which trigger(s) from the taxonomy fired (\"triggers\"), and\n give a one-sentence reason grounded in this schema. Do not recommend a\n template whose trigger conditions this table does not meet.\n\n7. DASHBOARD WIDGETS (response: dashboards)\n Propose up to 6 dashboards, one per major domain (aligned with your nav\n groups). For each, rank up to 8 widgets from ALLOWED WIDGETS below. Every\n widget binds to real columns: metric column (numeric), dimension/group-by\n column, time column where applicable, and an aggregation\n (\"count\" | \"sum\" | \"avg\" | \"min\" | \"max\"). Spans on a 12-column grid:\n KPI = 3, donut/funnel = 4, bar/table = 6, line/area = 8.\n\n8. PII & MASKING (response: tables[].columns[].pii)\n Flag every column plausibly containing personal or sensitive data.\n kind: one of \"email\" | \"phone\" | \"name\" | \"address\" | \"gov-id\" | \"dob\" |\n \"financial\" | \"credentials\" | \"health\" | \"ip\" | \"location\" | \"other\".\n masking: one of \"mask-email\" (j***@d***.com) | \"mask-partial\" (first/last\n char visible) | \"last4\" | \"redact\" (fully hidden) | \"hash\" | \"none\"\n (flag only). Columns already marked \"piiSuspected\": true in the input were\n flagged by heuristics — confirm or reject them explicitly (pii: null means\n explicitly not PII).\n\n9. ICONS (response: tables[].icon, navGroups[].icon)\n One lucide icon per table and per nav group, kebab-case (e.g. \"users\",\n \"shopping-cart\", \"package\", \"receipt\", \"calendar\", \"building-2\",\n \"credit-card\", \"truck\", \"message-square\", \"file-text\"). Choose the most\n domain-specific icon; avoid reusing one icon for multiple tables in the\n same group.\n\n10. MICRO-COPY (response: tables[].microcopy)\n Localized, per table:\n - emptyState.headline (≤ 40 chars) and emptyState.guidance (one sentence,\n ends with what to do first) — shown when the table has no rows.\n - pageSubtitle: ≤ 60 chars shown under the page title, describing what\n the page manages (e.g. \"Track and fulfil customer orders\").\n Tone: calm, concrete, no exclamation marks, no marketing language.\n\n=== TRIGGER TAXONOMY (for decision 6) ===\n| template id | trigger conditions |\n|--------------------|--------------------|\n| page-dashboard | numeric measure columns + a timestamp column exist in the domain |\n| page-master-detail | enum-heavy table with rich per-record detail (many columns / child conversation table) |\n| page-queue-inbox | workflow enum with pending/approved-style states, or read/unread boolean, + assignee/requester FK |\n| page-board | status enum classified \"workflow\"; optional second categorical column → swimlanes; period column → roadmap |\n| page-calendar | date/datetime column + a title-like text column |\n| page-scheduler | person FK × date × shift/type columns, or hours-per-project assignment shape |\n| page-directory | people-shaped table: name + email (+ role/department enum, avatar) |\n| page-log-viewer | append-only shape: timestamp + actor + action/level, high row count, few updates |\n| page-files | file/attachment shape: filename/mime/size/url columns or parent-folder self-FK |\n| page-chat | conversation + message table pair (thread FK, sender FK, body, timestamp) |\n(Templates not listed here — settings, wizard, builder, auth, billing, api,\nmarketing, system pages — are platform-owned; never recommend them for user\ntables.)\n\n=== ALLOWED PAGE TEMPLATES ===\n{{ALLOWED_PAGE_TEMPLATE_IDS_JSON}}\n\n=== ALLOWED WIDGETS ===\n{{ALLOWED_WIDGET_IDS_JSON}}\n\n=== TARGET LOCALES ===\n{{LOCALES_JSON}}\nEvery localized text object uses exactly these keys.\n\n=== RESPONSE SCHEMA (adminium.llm/v1) ===\nReturn one JSON object of this shape. TypeScript-style notation; \"?\" = may be\nomitted; L10n = { \"<locale>\": string } with exactly the target locales.\n\n{\n schema_version: \"adminium.llm/v1\", // REQUIRED, first key\n run_id?: string,\n tables: [{\n table: string, // \"schema.table\", exactly as in input\n confidence: number, // 0..1, overall for this table's suggestions\n label: L10n, description: L10n,\n icon: string, // lucide kebab-case\n displayColumn: string | null,\n naturalKey: string[] | null,\n pageTemplates: [{ template: string, rank: number, triggers: string[],\n reason: string, confidence: number }],\n microcopy: { emptyState: { headline: L10n, guidance: L10n },\n pageSubtitle: L10n },\n columns: [{\n column: string,\n label: L10n, description?: L10n,\n pii: null | { kind: string, masking: string, reason: string,\n confidence: number }\n }]\n }],\n enums: [{ table: string, column: string, kind: \"workflow\" | \"category\",\n order: string[] | null, terminal?: string[],\n tones: { [value: string]: \"pos\"|\"warn\"|\"danger\"|\"accent\"|\"muted\" },\n reason: string, confidence: number }],\n relations: {\n confirmed: [{ fromTable: string, fromColumns: string[], toTable: string,\n toColumns: string[], semantics: string, correct: boolean,\n confidence: number }],\n inferred: [{ fromTable: string, fromColumns: string[], toTable: string,\n toColumns: string[],\n kind: \"many-to-one\"|\"one-to-one\"|\"many-to-many-via\",\n viaTable?: string, evidence: string, confidence: number }]\n },\n navGroups: [{ id: string, // kebab-case slug\n label: L10n, icon: string, order: number,\n tables: string[], confidence: number }],\n dashboards: [{ id: string, domain: string, label: L10n, order: number,\n tables: string[],\n widgets: [{ widget: string, rank: number, span: number,\n table: string, metricColumn?: string,\n dimensionColumn?: string, timeColumn?: string,\n agg?: \"count\"|\"sum\"|\"avg\"|\"min\"|\"max\",\n titleEn: string, reason: string,\n confidence: number }] }],\n notes?: string[] // ≤ 5 short caveats about the schema\n}\n\n=== INPUT: DATABASE SCHEMA ===\n{{SCHEMA_IR_JSON}}\n\n=== INPUT: STATISTICS (aggregates only — no row data) ===\n{{STATS_JSON}}\n{{SAMPLING_BLOCK}}";
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* §4.5 — the LLM reduce ("merge") user template, used only when a huge schema is
|
|
27
|
+
* chunked and the global sections (`groups`, `widgets`) need consolidating. It
|
|
28
|
+
* reuses {@link PROMPT_V1_SYSTEM} verbatim; the map/reduce orchestration itself
|
|
29
|
+
* lives in `response/merge.ts` (a separate track). This is the ship-verbatim
|
|
30
|
+
* template text it embeds.
|
|
31
|
+
*/
|
|
32
|
+
export declare const PROMPT_MERGE_V1_USER = "Below are partial navigation-group and dashboard proposals produced independently\nfor {{TOTAL}} chunks of the same database schema. Merge them into ONE coherent\nresult for the whole schema:\n- Merge groups that clearly describe the same domain; keep total groups between 3 and 7.\n- Every table in the full table list must appear in exactly one group.\n- Re-rank dashboards; keep at most 6; merge duplicates covering the same domain.\n- Keep the output schema identical to the partial inputs; same locale keys; same\n output rules (single JSON object, no prose, schema_version first).\n\nFull table list: {{ALL_TABLE_NAMES_JSON}}\nPartial proposals: {{PARTIAL_GROUPS_AND_DASHBOARDS_JSON}}";
|
|
33
|
+
/** The merge prompt as a pair (system reused verbatim from v1). */
|
|
34
|
+
export declare const PROMPT_MERGE_V1: {
|
|
35
|
+
readonly version: "adminium.prompt/v1.1";
|
|
36
|
+
readonly system: "You are a senior data architect and admin-interface designer. You work for\nAdminium, an engine that introspects a relational database schema and\nauto-generates a complete admin dashboard from it. A heuristic pipeline has\nalready produced a baseline; your judgment refines it. Your output is machine-\napplied: it will be schema-validated, cross-checked against the real database\nschema, and reviewed by a human as a diff before anything is applied.\n\nTASK\nAnalyze the database schema and statistics provided in the user message and\nreturn enrichment decisions as a single JSON object, following the response\nschema and the decision instructions exactly.\n\nQUALITY BAR\n- Domain understanding first: infer what the business behind this schema does\n from table/column names, types, relations and statistics, and make every\n label, grouping and recommendation read like it was written by an expert in\n that domain — not like it was mechanically derived from identifiers.\n- Labels are for non-technical staff: \"Purchase orders\", never \"tbl_po_hdr\".\n Descriptions are one sentence, concrete, and state what a row represents.\n- Translations must be native-quality, using the domain's standard terminology\n in each language, and consistent across the whole response (the same concept\n gets the same term everywhere). Do not transliterate; translate.\n- Be decisive but honest: every suggestion carries a \"confidence\" between 0.0\n and 1.0. Prefer a lower confidence over inventing detail. If the input gives\n no basis for a decision, omit that suggestion entirely.\n- Ground every recommendation in evidence from the input (name patterns, types,\n cardinalities, null ratios, relation shapes). \"reason\" fields must cite that\n evidence in one short sentence.\n\nOUTPUT RULES — STRICT\n1. Respond with EXACTLY ONE JSON object and nothing else. No prose, no markdown,\n no code fences, no comments, no trailing commas. The first character of your\n response must be \"{\" and the last must be \"}\".\n2. The first key must be \"schema_version\" with the exact value\n \"adminium.llm/v1\".\n3. Reference ONLY tables and columns that appear in the input schema. Never\n invent, rename, pluralize or \"correct\" an identifier. Tables marked\n \"stub\": true are context only — emit no suggestions for them.\n4. Use only the allowed page-template ids, widget ids, tone values and masking\n values listed in the user message. Icon names must be kebab-case lucide\n icon names (e.g. \"shopping-cart\", \"users\", \"package\").\n5. Localized text objects must contain exactly the requested locale keys —\n no more, no fewer. \"en_US\" is always required.\n6. Numbers for \"confidence\" use at most 2 decimal places. Keep all \"reason\"\n fields under 200 characters.\n7. If you cannot comply with any rule, return\n {\"schema_version\":\"adminium.llm/v1\",\"error\":\"<one-sentence reason>\"} and\n nothing else.";
|
|
37
|
+
readonly user: "Below are partial navigation-group and dashboard proposals produced independently\nfor {{TOTAL}} chunks of the same database schema. Merge them into ONE coherent\nresult for the whole schema:\n- Merge groups that clearly describe the same domain; keep total groups between 3 and 7.\n- Every table in the full table list must appear in exactly one group.\n- Re-rank dashboards; keep at most 6; merge duplicates covering the same domain.\n- Keep the output schema identical to the partial inputs; same locale keys; same\n output rules (single JSON object, no prose, schema_version first).\n\nFull table list: {{ALL_TABLE_NAMES_JSON}}\nPartial proposals: {{PARTIAL_GROUPS_AND_DASHBOARDS_JSON}}";
|
|
38
|
+
};
|
|
39
|
+
/** Tokens the merge template exposes for `response/merge.ts` to fill. */
|
|
40
|
+
export declare const PROMPT_MERGE_V1_TOKENS: readonly ["{{TOTAL}}", "{{ALL_TABLE_NAMES_JSON}}", "{{PARTIAL_GROUPS_AND_DASHBOARDS_JSON}}"];
|
|
41
|
+
//# sourceMappingURL=v1.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v1.d.ts","sourceRoot":"","sources":["../../../src/prompt/templates/v1.ts"],"names":[],"mappings":"AA8BA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,m6FA+CZ,CAAC;AAElB;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,u5TAqLR,CAAC;AAEpB;;;;GAIG;AACH,eAAO,MAAM,SAAS;;;;CAIZ,CAAC;AAEX;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,wrBAUyB,CAAC;AAE3D,mEAAmE;AACnE,eAAO,MAAM,eAAe;;;;CAIlB,CAAC;AAEX,yEAAyE;AACzE,eAAO,MAAM,sBAAsB,8FAIzB,CAAC"}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `PROMPT_V1` — the production schema-enrichment prompt (06-llm-assist.md §5).
|
|
3
|
+
*
|
|
4
|
+
* SHIP VERBATIM. The two exported template strings reproduce §5.1 (system) and
|
|
5
|
+
* §5.2 (user); `{{DOUBLE_BRACE}}` tokens are replaced by the builder, everything
|
|
6
|
+
* else is literal. One deliberate divergence from the spec text (v1.1): §5.2's
|
|
7
|
+
* trigger taxonomy lists a `page-builder` row, but `page-builder` is a
|
|
8
|
+
* non-recommendable tool surface (registry `recommendable: false` — no
|
|
9
|
+
* `composeRequestedArchetype` materialization path) and is therefore absent from
|
|
10
|
+
* `LLM_ALLOWED_TEMPLATES`; the row is omitted so the taxonomy is exactly the
|
|
11
|
+
* injected allowed list and the "not listed here" disclaimer names `builder`
|
|
12
|
+
* among the platform-owned surfaces. The spec table predates that registry
|
|
13
|
+
* decision and awaits a sync (workplan/ is being revised on a separate track).
|
|
14
|
+
* Both paths (direct-API and BYO) send the identical text (§1 invariant 1) —
|
|
15
|
+
* the BYO flattening joins them as `=== SYSTEM ===\n…\n\n=== USER ===\n…`
|
|
16
|
+
* (see builder.ts / `flattenByo`).
|
|
17
|
+
*
|
|
18
|
+
* A snapshot test (`templates.test.ts`) pins these strings together with
|
|
19
|
+
* {@link PROMPT_VERSION}: any edit to the template text fails the snapshot,
|
|
20
|
+
* forcing a conscious `PROMPT_VERSION` bump (acceptance criterion 2).
|
|
21
|
+
*
|
|
22
|
+
* `PROMPT_VERSION` is imported from the response contract rather than redefined
|
|
23
|
+
* here: §4.3 designates this module as the authoritative home, but T01 already
|
|
24
|
+
* shipped the constant in `response/schema.ts`; importing the single existing
|
|
25
|
+
* definition guarantees the prompt and response contracts can never drift. It is
|
|
26
|
+
* NOT re-exported from this subdir — `response/` already owns the public export,
|
|
27
|
+
* and re-exporting it here would collide at the assembled top-level barrel.
|
|
28
|
+
*/
|
|
29
|
+
import { PROMPT_VERSION } from '../../response/schema.js';
|
|
30
|
+
/**
|
|
31
|
+
* §5.1 — the system section. Fixed policy text (no tokens); it frames the task,
|
|
32
|
+
* the quality bar and the STRICT output rules. Reused verbatim by
|
|
33
|
+
* {@link PROMPT_MERGE_V1} (§4.5).
|
|
34
|
+
*/
|
|
35
|
+
export const PROMPT_V1_SYSTEM = `You are a senior data architect and admin-interface designer. You work for
|
|
36
|
+
Adminium, an engine that introspects a relational database schema and
|
|
37
|
+
auto-generates a complete admin dashboard from it. A heuristic pipeline has
|
|
38
|
+
already produced a baseline; your judgment refines it. Your output is machine-
|
|
39
|
+
applied: it will be schema-validated, cross-checked against the real database
|
|
40
|
+
schema, and reviewed by a human as a diff before anything is applied.
|
|
41
|
+
|
|
42
|
+
TASK
|
|
43
|
+
Analyze the database schema and statistics provided in the user message and
|
|
44
|
+
return enrichment decisions as a single JSON object, following the response
|
|
45
|
+
schema and the decision instructions exactly.
|
|
46
|
+
|
|
47
|
+
QUALITY BAR
|
|
48
|
+
- Domain understanding first: infer what the business behind this schema does
|
|
49
|
+
from table/column names, types, relations and statistics, and make every
|
|
50
|
+
label, grouping and recommendation read like it was written by an expert in
|
|
51
|
+
that domain — not like it was mechanically derived from identifiers.
|
|
52
|
+
- Labels are for non-technical staff: "Purchase orders", never "tbl_po_hdr".
|
|
53
|
+
Descriptions are one sentence, concrete, and state what a row represents.
|
|
54
|
+
- Translations must be native-quality, using the domain's standard terminology
|
|
55
|
+
in each language, and consistent across the whole response (the same concept
|
|
56
|
+
gets the same term everywhere). Do not transliterate; translate.
|
|
57
|
+
- Be decisive but honest: every suggestion carries a "confidence" between 0.0
|
|
58
|
+
and 1.0. Prefer a lower confidence over inventing detail. If the input gives
|
|
59
|
+
no basis for a decision, omit that suggestion entirely.
|
|
60
|
+
- Ground every recommendation in evidence from the input (name patterns, types,
|
|
61
|
+
cardinalities, null ratios, relation shapes). "reason" fields must cite that
|
|
62
|
+
evidence in one short sentence.
|
|
63
|
+
|
|
64
|
+
OUTPUT RULES — STRICT
|
|
65
|
+
1. Respond with EXACTLY ONE JSON object and nothing else. No prose, no markdown,
|
|
66
|
+
no code fences, no comments, no trailing commas. The first character of your
|
|
67
|
+
response must be "{" and the last must be "}".
|
|
68
|
+
2. The first key must be "schema_version" with the exact value
|
|
69
|
+
"adminium.llm/v1".
|
|
70
|
+
3. Reference ONLY tables and columns that appear in the input schema. Never
|
|
71
|
+
invent, rename, pluralize or "correct" an identifier. Tables marked
|
|
72
|
+
"stub": true are context only — emit no suggestions for them.
|
|
73
|
+
4. Use only the allowed page-template ids, widget ids, tone values and masking
|
|
74
|
+
values listed in the user message. Icon names must be kebab-case lucide
|
|
75
|
+
icon names (e.g. "shopping-cart", "users", "package").
|
|
76
|
+
5. Localized text objects must contain exactly the requested locale keys —
|
|
77
|
+
no more, no fewer. "en_US" is always required.
|
|
78
|
+
6. Numbers for "confidence" use at most 2 decimal places. Keep all "reason"
|
|
79
|
+
fields under 200 characters.
|
|
80
|
+
7. If you cannot comply with any rule, return
|
|
81
|
+
{"schema_version":"adminium.llm/v1","error":"<one-sentence reason>"} and
|
|
82
|
+
nothing else.`;
|
|
83
|
+
/**
|
|
84
|
+
* §5.2 — the user section template. Carries the `{{DOUBLE_BRACE}}` tokens the
|
|
85
|
+
* builder fills: run id, chunk info, requested-sections list, the allowed
|
|
86
|
+
* vocabularies, target locales, and the serialized schema IR / statistics /
|
|
87
|
+
* (opt-in) sample-values blocks. The ten numbered decision blocks map 1:1 to
|
|
88
|
+
* {@link RequestedSection} ids; the builder deletes deselected blocks (§4.4).
|
|
89
|
+
*/
|
|
90
|
+
export const PROMPT_V1_USER = `=== CONTEXT ===
|
|
91
|
+
Adminium generated an admin dashboard for the database described below. Your
|
|
92
|
+
decisions refine: naming, navigation, enum semantics, relations, key columns,
|
|
93
|
+
page layouts, dashboard widgets, privacy flags, icons and micro-copy.
|
|
94
|
+
run_id: {{RUN_ID}} (echo it back as "run_id" if convenient; optional)
|
|
95
|
+
{{CHUNK_INFO}}
|
|
96
|
+
|
|
97
|
+
=== YOUR DECISIONS ===
|
|
98
|
+
Produce the following, per the response schema at the end of this message.
|
|
99
|
+
{{REQUESTED_SECTIONS_LIST}}
|
|
100
|
+
|
|
101
|
+
1. LABELS & DESCRIPTIONS (response: tables[].label, tables[].description,
|
|
102
|
+
tables[].columns[].label, tables[].columns[].description)
|
|
103
|
+
For every non-stub table and every column: a short human label (Title Case
|
|
104
|
+
for tables, Sentence case for columns; ≤ 28 chars for tables, ≤ 24 for
|
|
105
|
+
columns) and a one-sentence description. Localize both into every requested
|
|
106
|
+
locale. Column descriptions are optional for self-explanatory columns
|
|
107
|
+
(id, created_at) — omit rather than pad.
|
|
108
|
+
|
|
109
|
+
2. DOMAIN GROUPING (response: navGroups)
|
|
110
|
+
Group all tables into 3–7 navigation groups that reflect business domains
|
|
111
|
+
(e.g. "Sales", "Catalog", "People"), not technical layers. Every non-stub
|
|
112
|
+
table appears in exactly one group. Order groups by operational importance;
|
|
113
|
+
order tables within a group the same way. Give each group a localized label
|
|
114
|
+
and a lucide icon.
|
|
115
|
+
|
|
116
|
+
3. ENUM SEMANTICS (response: enums) — for every column listed under
|
|
117
|
+
"enumColumns" in the input:
|
|
118
|
+
- kind: "workflow" if values are states a record moves through over time
|
|
119
|
+
(e.g. draft → active → closed); "category" if values are unordered
|
|
120
|
+
classifications (e.g. region, plan tier).
|
|
121
|
+
- order: for workflow enums, the full value list in lifecycle order
|
|
122
|
+
(must be a permutation of the given values). null for category enums.
|
|
123
|
+
- tones: map EVERY value to exactly one of:
|
|
124
|
+
"pos" (successfully completed / healthy), "warn" (needs attention /
|
|
125
|
+
pending / at risk), "danger" (failed / blocked / cancelled),
|
|
126
|
+
"accent" (actively in progress), "muted" (inactive / draft / neutral).
|
|
127
|
+
- terminal: for workflow enums, the values that end the lifecycle.
|
|
128
|
+
|
|
129
|
+
4. RELATIONS (response: relations)
|
|
130
|
+
- confirmed: for each relation under "declaredRelations" in the input,
|
|
131
|
+
state whether it is semantically what it looks like, and name the
|
|
132
|
+
relationship (e.g. "orders belong to customers").
|
|
133
|
+
- inferred: relations that SHOULD exist but are not declared as foreign
|
|
134
|
+
keys. Use naming conventions (customer_id → customers.id), matching
|
|
135
|
+
types, and statistics (a column whose distinctCount ≈ another table's
|
|
136
|
+
rowCountEstimate is a strong signal). Give kind
|
|
137
|
+
("many-to-one" | "one-to-one" | "many-to-many-via"), evidence, and
|
|
138
|
+
confidence. Never re-state a declared relation as inferred.
|
|
139
|
+
|
|
140
|
+
5. KEY COLUMNS (response: tables[].displayColumn, tables[].naturalKey)
|
|
141
|
+
- displayColumn: the single column whose value best represents a row to a
|
|
142
|
+
human (a name/title/number — never a UUID/serial id). null only if truly
|
|
143
|
+
nothing qualifies.
|
|
144
|
+
- naturalKey: minimal column set a human would use to uniquely identify a
|
|
145
|
+
row (e.g. ["sku"], ["email"], ["order_number"]). null if none.
|
|
146
|
+
|
|
147
|
+
6. PAGE TEMPLATES (response: tables[].pageTemplates)
|
|
148
|
+
Recommend 1–3 page templates per table from ALLOWED PAGE TEMPLATES below,
|
|
149
|
+
ranked. "page-crud" is always generated and must not be recommended.
|
|
150
|
+
For each: cite which trigger(s) from the taxonomy fired ("triggers"), and
|
|
151
|
+
give a one-sentence reason grounded in this schema. Do not recommend a
|
|
152
|
+
template whose trigger conditions this table does not meet.
|
|
153
|
+
|
|
154
|
+
7. DASHBOARD WIDGETS (response: dashboards)
|
|
155
|
+
Propose up to 6 dashboards, one per major domain (aligned with your nav
|
|
156
|
+
groups). For each, rank up to 8 widgets from ALLOWED WIDGETS below. Every
|
|
157
|
+
widget binds to real columns: metric column (numeric), dimension/group-by
|
|
158
|
+
column, time column where applicable, and an aggregation
|
|
159
|
+
("count" | "sum" | "avg" | "min" | "max"). Spans on a 12-column grid:
|
|
160
|
+
KPI = 3, donut/funnel = 4, bar/table = 6, line/area = 8.
|
|
161
|
+
|
|
162
|
+
8. PII & MASKING (response: tables[].columns[].pii)
|
|
163
|
+
Flag every column plausibly containing personal or sensitive data.
|
|
164
|
+
kind: one of "email" | "phone" | "name" | "address" | "gov-id" | "dob" |
|
|
165
|
+
"financial" | "credentials" | "health" | "ip" | "location" | "other".
|
|
166
|
+
masking: one of "mask-email" (j***@d***.com) | "mask-partial" (first/last
|
|
167
|
+
char visible) | "last4" | "redact" (fully hidden) | "hash" | "none"
|
|
168
|
+
(flag only). Columns already marked "piiSuspected": true in the input were
|
|
169
|
+
flagged by heuristics — confirm or reject them explicitly (pii: null means
|
|
170
|
+
explicitly not PII).
|
|
171
|
+
|
|
172
|
+
9. ICONS (response: tables[].icon, navGroups[].icon)
|
|
173
|
+
One lucide icon per table and per nav group, kebab-case (e.g. "users",
|
|
174
|
+
"shopping-cart", "package", "receipt", "calendar", "building-2",
|
|
175
|
+
"credit-card", "truck", "message-square", "file-text"). Choose the most
|
|
176
|
+
domain-specific icon; avoid reusing one icon for multiple tables in the
|
|
177
|
+
same group.
|
|
178
|
+
|
|
179
|
+
10. MICRO-COPY (response: tables[].microcopy)
|
|
180
|
+
Localized, per table:
|
|
181
|
+
- emptyState.headline (≤ 40 chars) and emptyState.guidance (one sentence,
|
|
182
|
+
ends with what to do first) — shown when the table has no rows.
|
|
183
|
+
- pageSubtitle: ≤ 60 chars shown under the page title, describing what
|
|
184
|
+
the page manages (e.g. "Track and fulfil customer orders").
|
|
185
|
+
Tone: calm, concrete, no exclamation marks, no marketing language.
|
|
186
|
+
|
|
187
|
+
=== TRIGGER TAXONOMY (for decision 6) ===
|
|
188
|
+
| template id | trigger conditions |
|
|
189
|
+
|--------------------|--------------------|
|
|
190
|
+
| page-dashboard | numeric measure columns + a timestamp column exist in the domain |
|
|
191
|
+
| page-master-detail | enum-heavy table with rich per-record detail (many columns / child conversation table) |
|
|
192
|
+
| page-queue-inbox | workflow enum with pending/approved-style states, or read/unread boolean, + assignee/requester FK |
|
|
193
|
+
| page-board | status enum classified "workflow"; optional second categorical column → swimlanes; period column → roadmap |
|
|
194
|
+
| page-calendar | date/datetime column + a title-like text column |
|
|
195
|
+
| page-scheduler | person FK × date × shift/type columns, or hours-per-project assignment shape |
|
|
196
|
+
| page-directory | people-shaped table: name + email (+ role/department enum, avatar) |
|
|
197
|
+
| page-log-viewer | append-only shape: timestamp + actor + action/level, high row count, few updates |
|
|
198
|
+
| page-files | file/attachment shape: filename/mime/size/url columns or parent-folder self-FK |
|
|
199
|
+
| page-chat | conversation + message table pair (thread FK, sender FK, body, timestamp) |
|
|
200
|
+
(Templates not listed here — settings, wizard, builder, auth, billing, api,
|
|
201
|
+
marketing, system pages — are platform-owned; never recommend them for user
|
|
202
|
+
tables.)
|
|
203
|
+
|
|
204
|
+
=== ALLOWED PAGE TEMPLATES ===
|
|
205
|
+
{{ALLOWED_PAGE_TEMPLATE_IDS_JSON}}
|
|
206
|
+
|
|
207
|
+
=== ALLOWED WIDGETS ===
|
|
208
|
+
{{ALLOWED_WIDGET_IDS_JSON}}
|
|
209
|
+
|
|
210
|
+
=== TARGET LOCALES ===
|
|
211
|
+
{{LOCALES_JSON}}
|
|
212
|
+
Every localized text object uses exactly these keys.
|
|
213
|
+
|
|
214
|
+
=== RESPONSE SCHEMA (adminium.llm/v1) ===
|
|
215
|
+
Return one JSON object of this shape. TypeScript-style notation; "?" = may be
|
|
216
|
+
omitted; L10n = { "<locale>": string } with exactly the target locales.
|
|
217
|
+
|
|
218
|
+
{
|
|
219
|
+
schema_version: "adminium.llm/v1", // REQUIRED, first key
|
|
220
|
+
run_id?: string,
|
|
221
|
+
tables: [{
|
|
222
|
+
table: string, // "schema.table", exactly as in input
|
|
223
|
+
confidence: number, // 0..1, overall for this table's suggestions
|
|
224
|
+
label: L10n, description: L10n,
|
|
225
|
+
icon: string, // lucide kebab-case
|
|
226
|
+
displayColumn: string | null,
|
|
227
|
+
naturalKey: string[] | null,
|
|
228
|
+
pageTemplates: [{ template: string, rank: number, triggers: string[],
|
|
229
|
+
reason: string, confidence: number }],
|
|
230
|
+
microcopy: { emptyState: { headline: L10n, guidance: L10n },
|
|
231
|
+
pageSubtitle: L10n },
|
|
232
|
+
columns: [{
|
|
233
|
+
column: string,
|
|
234
|
+
label: L10n, description?: L10n,
|
|
235
|
+
pii: null | { kind: string, masking: string, reason: string,
|
|
236
|
+
confidence: number }
|
|
237
|
+
}]
|
|
238
|
+
}],
|
|
239
|
+
enums: [{ table: string, column: string, kind: "workflow" | "category",
|
|
240
|
+
order: string[] | null, terminal?: string[],
|
|
241
|
+
tones: { [value: string]: "pos"|"warn"|"danger"|"accent"|"muted" },
|
|
242
|
+
reason: string, confidence: number }],
|
|
243
|
+
relations: {
|
|
244
|
+
confirmed: [{ fromTable: string, fromColumns: string[], toTable: string,
|
|
245
|
+
toColumns: string[], semantics: string, correct: boolean,
|
|
246
|
+
confidence: number }],
|
|
247
|
+
inferred: [{ fromTable: string, fromColumns: string[], toTable: string,
|
|
248
|
+
toColumns: string[],
|
|
249
|
+
kind: "many-to-one"|"one-to-one"|"many-to-many-via",
|
|
250
|
+
viaTable?: string, evidence: string, confidence: number }]
|
|
251
|
+
},
|
|
252
|
+
navGroups: [{ id: string, // kebab-case slug
|
|
253
|
+
label: L10n, icon: string, order: number,
|
|
254
|
+
tables: string[], confidence: number }],
|
|
255
|
+
dashboards: [{ id: string, domain: string, label: L10n, order: number,
|
|
256
|
+
tables: string[],
|
|
257
|
+
widgets: [{ widget: string, rank: number, span: number,
|
|
258
|
+
table: string, metricColumn?: string,
|
|
259
|
+
dimensionColumn?: string, timeColumn?: string,
|
|
260
|
+
agg?: "count"|"sum"|"avg"|"min"|"max",
|
|
261
|
+
titleEn: string, reason: string,
|
|
262
|
+
confidence: number }] }],
|
|
263
|
+
notes?: string[] // ≤ 5 short caveats about the schema
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
=== INPUT: DATABASE SCHEMA ===
|
|
267
|
+
{{SCHEMA_IR_JSON}}
|
|
268
|
+
|
|
269
|
+
=== INPUT: STATISTICS (aggregates only — no row data) ===
|
|
270
|
+
{{STATS_JSON}}
|
|
271
|
+
{{SAMPLING_BLOCK}}`;
|
|
272
|
+
/**
|
|
273
|
+
* The v1 prompt as a pair. The builder renders these into a
|
|
274
|
+
* {@link PromptArtifact}; nothing else should read the raw strings except the
|
|
275
|
+
* snapshot test.
|
|
276
|
+
*/
|
|
277
|
+
export const PROMPT_V1 = {
|
|
278
|
+
version: PROMPT_VERSION,
|
|
279
|
+
system: PROMPT_V1_SYSTEM,
|
|
280
|
+
user: PROMPT_V1_USER,
|
|
281
|
+
};
|
|
282
|
+
/**
|
|
283
|
+
* §4.5 — the LLM reduce ("merge") user template, used only when a huge schema is
|
|
284
|
+
* chunked and the global sections (`groups`, `widgets`) need consolidating. It
|
|
285
|
+
* reuses {@link PROMPT_V1_SYSTEM} verbatim; the map/reduce orchestration itself
|
|
286
|
+
* lives in `response/merge.ts` (a separate track). This is the ship-verbatim
|
|
287
|
+
* template text it embeds.
|
|
288
|
+
*/
|
|
289
|
+
export const PROMPT_MERGE_V1_USER = `Below are partial navigation-group and dashboard proposals produced independently
|
|
290
|
+
for {{TOTAL}} chunks of the same database schema. Merge them into ONE coherent
|
|
291
|
+
result for the whole schema:
|
|
292
|
+
- Merge groups that clearly describe the same domain; keep total groups between 3 and 7.
|
|
293
|
+
- Every table in the full table list must appear in exactly one group.
|
|
294
|
+
- Re-rank dashboards; keep at most 6; merge duplicates covering the same domain.
|
|
295
|
+
- Keep the output schema identical to the partial inputs; same locale keys; same
|
|
296
|
+
output rules (single JSON object, no prose, schema_version first).
|
|
297
|
+
|
|
298
|
+
Full table list: {{ALL_TABLE_NAMES_JSON}}
|
|
299
|
+
Partial proposals: {{PARTIAL_GROUPS_AND_DASHBOARDS_JSON}}`;
|
|
300
|
+
/** The merge prompt as a pair (system reused verbatim from v1). */
|
|
301
|
+
export const PROMPT_MERGE_V1 = {
|
|
302
|
+
version: PROMPT_VERSION,
|
|
303
|
+
system: PROMPT_V1_SYSTEM,
|
|
304
|
+
user: PROMPT_MERGE_V1_USER,
|
|
305
|
+
};
|
|
306
|
+
/** Tokens the merge template exposes for `response/merge.ts` to fill. */
|
|
307
|
+
export const PROMPT_MERGE_V1_TOKENS = [
|
|
308
|
+
'{{TOTAL}}',
|
|
309
|
+
'{{ALL_TABLE_NAMES_JSON}}',
|
|
310
|
+
'{{PARTIAL_GROUPS_AND_DASHBOARDS_JSON}}',
|
|
311
|
+
];
|
|
312
|
+
//# sourceMappingURL=v1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v1.js","sourceRoot":"","sources":["../../../src/prompt/templates/v1.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+Cf,CAAC;AAElB;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAqLX,CAAC;AAEpB;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,OAAO,EAAE,cAAc;IACvB,MAAM,EAAE,gBAAgB;IACxB,IAAI,EAAE,cAAc;CACZ,CAAC;AAEX;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;;;;;;0DAUsB,CAAC;AAE3D,mEAAmE;AACnE,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,OAAO,EAAE,cAAc;IACvB,MAAM,EAAE,gBAAgB;IACxB,IAAI,EAAE,oBAAoB;CAClB,CAAC;AAEX,yEAAyE;AACzE,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,WAAW;IACX,0BAA0B;IAC1B,wCAAwC;CAChC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `chars / 3.6` heuristic token estimator (06-llm-assist.md §2, §4.5).
|
|
3
|
+
*
|
|
4
|
+
* Deliberately provider-agnostic and conservative: it slightly over-counts for
|
|
5
|
+
* typical English + JSON so a prompt that "fits" the budget fits every supported
|
|
6
|
+
* provider's real tokenizer with headroom. Pure and deterministic — no model
|
|
7
|
+
* download, no network, no `Date.now`/`Math.random`.
|
|
8
|
+
*/
|
|
9
|
+
/** Characters-per-token divisor (§4.5: `tokens ≈ ceil(chars / 3.6)`). */
|
|
10
|
+
export declare const CHARS_PER_TOKEN = 3.6;
|
|
11
|
+
/**
|
|
12
|
+
* Estimate the input-token count of `text` as `ceil(text.length / 3.6)`.
|
|
13
|
+
* Empty input is 0 tokens.
|
|
14
|
+
*/
|
|
15
|
+
export declare function estimateTokens(text: string): number;
|
|
16
|
+
//# sourceMappingURL=token-estimate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-estimate.d.ts","sourceRoot":"","sources":["../../src/prompt/token-estimate.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,yEAAyE;AACzE,eAAO,MAAM,eAAe,MAAM,CAAC;AAEnC;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGnD"}
|