prompt-identifiers-baml 0.1.0 → 0.1.2
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/README.md +55 -47
- package/dist/index.d.mts +33 -3
- package/dist/index.d.ts +33 -3
- package/dist/index.js +57 -113
- package/dist/index.mjs +58 -114
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -11,24 +11,22 @@ npm install prompt-identifiers-baml prompt-identifiers @boundaryml/baml
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
import { wrapBamlFunction } from
|
|
15
|
-
import { b } from
|
|
14
|
+
import { wrapBamlFunction } from "prompt-identifiers-baml";
|
|
15
|
+
import { b } from "./baml_client";
|
|
16
16
|
|
|
17
17
|
// Wrap your BAML function
|
|
18
18
|
const analyzeUser = wrapBamlFunction(b.AnalyzeUser, {
|
|
19
|
-
config: { inputFormat:
|
|
19
|
+
config: { inputFormat: "UUID", outputFormat: "SafeNumeric" },
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
// Use normally - IDs are automatically encoded/decoded
|
|
23
23
|
const result = await analyzeUser({
|
|
24
|
-
user_id:
|
|
25
|
-
items: [
|
|
26
|
-
{ id: '987fcdeb-51a2-43f7-8d9c-0123456789ab', name: 'Order 1' },
|
|
27
|
-
],
|
|
24
|
+
user_id: "123e4567-e89b-42d3-a456-426655440000",
|
|
25
|
+
items: [{ id: "987fcdeb-51a2-43f7-8d9c-0123456789ab", name: "Order 1" }],
|
|
28
26
|
});
|
|
29
27
|
|
|
30
28
|
// The BAML function receives:
|
|
31
|
-
// { user_id: '
|
|
29
|
+
// { user_id: '~000~', items: [{ id: '~001~', name: 'Order 1' }] }
|
|
32
30
|
//
|
|
33
31
|
// You receive the response with original UUIDs restored
|
|
34
32
|
```
|
|
@@ -36,10 +34,10 @@ const result = await analyzeUser({
|
|
|
36
34
|
## How It Works
|
|
37
35
|
|
|
38
36
|
1. **Before BAML call**: Deep traverses input object and encodes all ID fields
|
|
39
|
-
- `123e4567-e89b-42d3-a456-426655440000` →
|
|
37
|
+
- `123e4567-e89b-42d3-a456-426655440000` → `~000~`
|
|
40
38
|
|
|
41
39
|
2. **After BAML call**: Deep traverses output object and decodes all placeholders
|
|
42
|
-
-
|
|
40
|
+
- `~000~` → `123e4567-e89b-42d3-a456-426655440000`
|
|
43
41
|
|
|
44
42
|
This is completely transparent - you work with real IDs, the LLM works with compact placeholders.
|
|
45
43
|
|
|
@@ -49,21 +47,29 @@ This is completely transparent - you work with real IDs, the LLM works with comp
|
|
|
49
47
|
wrapBamlFunction(fn, {
|
|
50
48
|
// Required: encoding configuration
|
|
51
49
|
config: {
|
|
52
|
-
inputFormat:
|
|
53
|
-
outputFormat:
|
|
50
|
+
inputFormat: "UUID", // or 'ULID' or custom RegExp
|
|
51
|
+
outputFormat: "SafeNumeric", // or 'Numeric', 'IdToken', { template: '...' }
|
|
54
52
|
},
|
|
55
53
|
|
|
56
54
|
// Optional: specify which fields to encode
|
|
57
55
|
// If not provided, all matching strings are encoded
|
|
58
|
-
encodeFields: [
|
|
56
|
+
encodeFields: ["user_id", "items[].id", "metadata.owner_id"],
|
|
57
|
+
|
|
58
|
+
// Optional: enable debug mode for detailed diagnostics
|
|
59
|
+
debug: true,
|
|
59
60
|
|
|
60
61
|
// Optional: callbacks for logging/debugging
|
|
61
62
|
onEncode: (result) => {
|
|
62
|
-
console.log(
|
|
63
|
-
|
|
63
|
+
console.log("Mapping:", result.mapping);
|
|
64
|
+
// debugData is only present when debug: true
|
|
65
|
+
if (result.debugData) {
|
|
66
|
+
console.log(`Encoded ${result.debugData.encodedCount} IDs in ${result.debugData.durationMs}ms`);
|
|
67
|
+
}
|
|
64
68
|
},
|
|
65
69
|
onDecode: (result) => {
|
|
66
|
-
|
|
70
|
+
if (result.debugData) {
|
|
71
|
+
console.log(`Decoded ${result.debugData.decodedCount} placeholders in ${result.debugData.durationMs}ms`);
|
|
72
|
+
}
|
|
67
73
|
},
|
|
68
74
|
});
|
|
69
75
|
```
|
|
@@ -72,40 +78,40 @@ wrapBamlFunction(fn, {
|
|
|
72
78
|
|
|
73
79
|
The `encodeFields` option supports dot notation and array wildcards:
|
|
74
80
|
|
|
75
|
-
| Pattern
|
|
76
|
-
|
|
77
|
-
| `user_id`
|
|
78
|
-
| `data.user_id`
|
|
79
|
-
| `items[].id`
|
|
80
|
-
| `data.users[].profile.id` | Deep nested
|
|
81
|
+
| Pattern | Description | Example Match |
|
|
82
|
+
| ------------------------- | ---------------- | --------------------------------------------------- |
|
|
83
|
+
| `user_id` | Top-level field | `{ user_id: '...' }` |
|
|
84
|
+
| `data.user_id` | Nested field | `{ data: { user_id: '...' } }` |
|
|
85
|
+
| `items[].id` | Array item field | `{ items: [{ id: '...' }] }` |
|
|
86
|
+
| `data.users[].profile.id` | Deep nested | `{ data: { users: [{ profile: { id: '...' } }] } }` |
|
|
81
87
|
|
|
82
88
|
### Input Formats
|
|
83
89
|
|
|
84
|
-
| Format
|
|
85
|
-
|
|
86
|
-
| `'UUID'` | RFC 4122 UUIDs
|
|
87
|
-
| `'ULID'` | Crockford Base32 ULIDs | `01ARZ3NDEKTSV4RRFFQ69G5FAV`
|
|
88
|
-
| `RegExp` | Custom pattern
|
|
90
|
+
| Format | Description | Example |
|
|
91
|
+
| -------- | ---------------------- | -------------------------------------- |
|
|
92
|
+
| `'UUID'` | RFC 4122 UUIDs | `123e4567-e89b-42d3-a456-426655440000` |
|
|
93
|
+
| `'ULID'` | Crockford Base32 ULIDs | `01ARZ3NDEKTSV4RRFFQ69G5FAV` |
|
|
94
|
+
| `RegExp` | Custom pattern | `/user-\d{6}/gi` |
|
|
89
95
|
|
|
90
96
|
### Output Formats
|
|
91
97
|
|
|
92
|
-
| Format
|
|
93
|
-
|
|
94
|
-
| `'SafeNumeric'`
|
|
95
|
-
| `'Numeric'`
|
|
96
|
-
| `'IdToken'`
|
|
97
|
-
| `{ template: '...' }` | Custom template
|
|
98
|
+
| Format | Description | Example |
|
|
99
|
+
| --------------------- | ------------------------------------------------- | ------------------------------------- |
|
|
100
|
+
| `'SafeNumeric'` | Collision-safe with tildes (recommended) | `~000~`, `~001~` |
|
|
101
|
+
| `'Numeric'` | Simple numeric with smart triplet expansion | `000`, `001` |
|
|
102
|
+
| `'IdToken'` | Base62 encoding | `0`, `A`, `z`, `10` |
|
|
103
|
+
| `{ template: '...' }` | Custom template | `{ template: '[ID:{i}]' }` → `[ID:0]` |
|
|
98
104
|
|
|
99
105
|
## Streaming Support
|
|
100
106
|
|
|
101
107
|
Use `wrapBamlStreamingFunction` for BAML streaming functions:
|
|
102
108
|
|
|
103
109
|
```typescript
|
|
104
|
-
import { wrapBamlStreamingFunction } from
|
|
105
|
-
import { b } from
|
|
110
|
+
import { wrapBamlStreamingFunction } from "prompt-identifiers-baml";
|
|
111
|
+
import { b } from "./baml_client";
|
|
106
112
|
|
|
107
113
|
const streamAnalysis = wrapBamlStreamingFunction(b.stream.AnalyzeUser, {
|
|
108
|
-
config: { inputFormat:
|
|
114
|
+
config: { inputFormat: "UUID", outputFormat: "SafeNumeric" },
|
|
109
115
|
});
|
|
110
116
|
|
|
111
117
|
// IDs are decoded in real-time as partials arrive
|
|
@@ -121,15 +127,15 @@ for await (const partial of streamAnalysis({ user_id: uuid })) {
|
|
|
121
127
|
Manually encode an object (useful for custom integrations):
|
|
122
128
|
|
|
123
129
|
```typescript
|
|
124
|
-
import { encodeObject } from
|
|
130
|
+
import { encodeObject } from "prompt-identifiers-baml";
|
|
125
131
|
|
|
126
132
|
const { encoded, mapping } = encodeObject(
|
|
127
|
-
{ user_id:
|
|
128
|
-
{ inputFormat:
|
|
133
|
+
{ user_id: "uuid-here", data: { owner: "other-uuid" } },
|
|
134
|
+
{ inputFormat: "UUID", outputFormat: "SafeNumeric" }
|
|
129
135
|
);
|
|
130
136
|
|
|
131
|
-
// encoded: { user_id: '
|
|
132
|
-
// mapping: { '
|
|
137
|
+
// encoded: { user_id: '~000~', data: { owner: '~001~' } }
|
|
138
|
+
// mapping: { '~000~': 'uuid-here', '~001~': 'other-uuid' }
|
|
133
139
|
```
|
|
134
140
|
|
|
135
141
|
### decodeObject
|
|
@@ -137,11 +143,11 @@ const { encoded, mapping } = encodeObject(
|
|
|
137
143
|
Manually decode an object:
|
|
138
144
|
|
|
139
145
|
```typescript
|
|
140
|
-
import { decodeObject } from
|
|
146
|
+
import { decodeObject } from "prompt-identifiers-baml";
|
|
141
147
|
|
|
142
148
|
const decoded = decodeObject(
|
|
143
|
-
{ user_id:
|
|
144
|
-
{
|
|
149
|
+
{ user_id: "~000~", summary: "User ~000~ is active" },
|
|
150
|
+
{ "~000~": "uuid-here" }
|
|
145
151
|
);
|
|
146
152
|
|
|
147
153
|
// decoded: { user_id: 'uuid-here', summary: 'User uuid-here is active' }
|
|
@@ -165,11 +171,13 @@ const result: AnalyzeUserOutput = await analyzeUser({
|
|
|
165
171
|
## Auto-Detection vs Explicit Fields
|
|
166
172
|
|
|
167
173
|
**Auto-detection mode** (no `encodeFields`):
|
|
174
|
+
|
|
168
175
|
- Encodes ALL string fields matching the input pattern
|
|
169
176
|
- Simple to use, works for most cases
|
|
170
177
|
- May encode fields you don't want encoded
|
|
171
178
|
|
|
172
179
|
**Explicit fields** (with `encodeFields`):
|
|
180
|
+
|
|
173
181
|
- Only encodes specified fields
|
|
174
182
|
- More precise control
|
|
175
183
|
- Recommended for production use
|
|
@@ -177,13 +185,13 @@ const result: AnalyzeUserOutput = await analyzeUser({
|
|
|
177
185
|
```typescript
|
|
178
186
|
// Auto-detection: all UUIDs encoded
|
|
179
187
|
const wrapped1 = wrapBamlFunction(fn, {
|
|
180
|
-
config: { inputFormat:
|
|
188
|
+
config: { inputFormat: "UUID", outputFormat: "SafeNumeric" },
|
|
181
189
|
});
|
|
182
190
|
|
|
183
191
|
// Explicit: only user_id and item IDs encoded
|
|
184
192
|
const wrapped2 = wrapBamlFunction(fn, {
|
|
185
|
-
config: { inputFormat:
|
|
186
|
-
encodeFields: [
|
|
193
|
+
config: { inputFormat: "UUID", outputFormat: "SafeNumeric" },
|
|
194
|
+
encodeFields: ["user_id", "items[].id"],
|
|
187
195
|
});
|
|
188
196
|
```
|
|
189
197
|
|
package/dist/index.d.mts
CHANGED
|
@@ -7,6 +7,28 @@ import { EncodeConfig } from 'prompt-identifiers';
|
|
|
7
7
|
* in inputs and decode them in outputs.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
/** Debug data included in onEncode callback when debug is true */
|
|
11
|
+
interface EncodeDebugData {
|
|
12
|
+
/** Number of unique IDs encoded */
|
|
13
|
+
encodedCount: number;
|
|
14
|
+
/** Original input object before encoding */
|
|
15
|
+
input: unknown;
|
|
16
|
+
/** Encoded input object */
|
|
17
|
+
output: unknown;
|
|
18
|
+
/** Time spent encoding in milliseconds */
|
|
19
|
+
durationMs: number;
|
|
20
|
+
}
|
|
21
|
+
/** Debug data included in onDecode callback when debug is true */
|
|
22
|
+
interface DecodeDebugData {
|
|
23
|
+
/** Number of fields containing decoded placeholders */
|
|
24
|
+
decodedCount: number;
|
|
25
|
+
/** Raw output from LLM (encoded) */
|
|
26
|
+
input: unknown;
|
|
27
|
+
/** Decoded output with original IDs restored */
|
|
28
|
+
output: unknown;
|
|
29
|
+
/** Time spent decoding in milliseconds */
|
|
30
|
+
durationMs: number;
|
|
31
|
+
}
|
|
10
32
|
/** Configuration options for the BAML wrapper */
|
|
11
33
|
interface WrapBamlFunctionOptions {
|
|
12
34
|
/** Encoding configuration (inputFormat and outputFormat) */
|
|
@@ -25,18 +47,26 @@ interface WrapBamlFunctionOptions {
|
|
|
25
47
|
* encodeFields: ['user_id', 'items[].id', 'metadata.owner_id']
|
|
26
48
|
*/
|
|
27
49
|
encodeFields?: string[];
|
|
50
|
+
/**
|
|
51
|
+
* Enable debug mode to populate debugData in callbacks with
|
|
52
|
+
* input/output snapshots, counts, and timing information.
|
|
53
|
+
*/
|
|
54
|
+
debug?: boolean;
|
|
28
55
|
/**
|
|
29
56
|
* Optional callback fired after encoding IDs in the input.
|
|
57
|
+
* Receives the placeholder→ID mapping. When debug is true,
|
|
58
|
+
* also receives debugData with input, output, counts, and timing.
|
|
30
59
|
*/
|
|
31
60
|
onEncode?: (result: {
|
|
32
61
|
mapping: Record<string, string>;
|
|
33
|
-
|
|
62
|
+
debugData?: EncodeDebugData;
|
|
34
63
|
}) => void;
|
|
35
64
|
/**
|
|
36
65
|
* Optional callback fired after decoding IDs in the output.
|
|
66
|
+
* When debug is true, receives debugData with input, output, counts, and timing.
|
|
37
67
|
*/
|
|
38
68
|
onDecode?: (result: {
|
|
39
|
-
|
|
69
|
+
debugData?: DecodeDebugData;
|
|
40
70
|
}) => void;
|
|
41
71
|
}
|
|
42
72
|
/** A BAML function type (sync or async) */
|
|
@@ -111,4 +141,4 @@ declare function encodeObject<T>(obj: T, config: EncodeConfig, encodeFields?: st
|
|
|
111
141
|
*/
|
|
112
142
|
declare function decodeObject<T>(obj: T, mapping: Record<string, string>): T;
|
|
113
143
|
|
|
114
|
-
export { type BamlFunction, type BamlStreamingFunction, type WrapBamlFunctionOptions, decodeObject, encodeObject, wrapBamlFunction, wrapBamlStreamingFunction };
|
|
144
|
+
export { type BamlFunction, type BamlStreamingFunction, type DecodeDebugData, type EncodeDebugData, type WrapBamlFunctionOptions, decodeObject, encodeObject, wrapBamlFunction, wrapBamlStreamingFunction };
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,28 @@ import { EncodeConfig } from 'prompt-identifiers';
|
|
|
7
7
|
* in inputs and decode them in outputs.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
/** Debug data included in onEncode callback when debug is true */
|
|
11
|
+
interface EncodeDebugData {
|
|
12
|
+
/** Number of unique IDs encoded */
|
|
13
|
+
encodedCount: number;
|
|
14
|
+
/** Original input object before encoding */
|
|
15
|
+
input: unknown;
|
|
16
|
+
/** Encoded input object */
|
|
17
|
+
output: unknown;
|
|
18
|
+
/** Time spent encoding in milliseconds */
|
|
19
|
+
durationMs: number;
|
|
20
|
+
}
|
|
21
|
+
/** Debug data included in onDecode callback when debug is true */
|
|
22
|
+
interface DecodeDebugData {
|
|
23
|
+
/** Number of fields containing decoded placeholders */
|
|
24
|
+
decodedCount: number;
|
|
25
|
+
/** Raw output from LLM (encoded) */
|
|
26
|
+
input: unknown;
|
|
27
|
+
/** Decoded output with original IDs restored */
|
|
28
|
+
output: unknown;
|
|
29
|
+
/** Time spent decoding in milliseconds */
|
|
30
|
+
durationMs: number;
|
|
31
|
+
}
|
|
10
32
|
/** Configuration options for the BAML wrapper */
|
|
11
33
|
interface WrapBamlFunctionOptions {
|
|
12
34
|
/** Encoding configuration (inputFormat and outputFormat) */
|
|
@@ -25,18 +47,26 @@ interface WrapBamlFunctionOptions {
|
|
|
25
47
|
* encodeFields: ['user_id', 'items[].id', 'metadata.owner_id']
|
|
26
48
|
*/
|
|
27
49
|
encodeFields?: string[];
|
|
50
|
+
/**
|
|
51
|
+
* Enable debug mode to populate debugData in callbacks with
|
|
52
|
+
* input/output snapshots, counts, and timing information.
|
|
53
|
+
*/
|
|
54
|
+
debug?: boolean;
|
|
28
55
|
/**
|
|
29
56
|
* Optional callback fired after encoding IDs in the input.
|
|
57
|
+
* Receives the placeholder→ID mapping. When debug is true,
|
|
58
|
+
* also receives debugData with input, output, counts, and timing.
|
|
30
59
|
*/
|
|
31
60
|
onEncode?: (result: {
|
|
32
61
|
mapping: Record<string, string>;
|
|
33
|
-
|
|
62
|
+
debugData?: EncodeDebugData;
|
|
34
63
|
}) => void;
|
|
35
64
|
/**
|
|
36
65
|
* Optional callback fired after decoding IDs in the output.
|
|
66
|
+
* When debug is true, receives debugData with input, output, counts, and timing.
|
|
37
67
|
*/
|
|
38
68
|
onDecode?: (result: {
|
|
39
|
-
|
|
69
|
+
debugData?: DecodeDebugData;
|
|
40
70
|
}) => void;
|
|
41
71
|
}
|
|
42
72
|
/** A BAML function type (sync or async) */
|
|
@@ -111,4 +141,4 @@ declare function encodeObject<T>(obj: T, config: EncodeConfig, encodeFields?: st
|
|
|
111
141
|
*/
|
|
112
142
|
declare function decodeObject<T>(obj: T, mapping: Record<string, string>): T;
|
|
113
143
|
|
|
114
|
-
export { type BamlFunction, type BamlStreamingFunction, type WrapBamlFunctionOptions, decodeObject, encodeObject, wrapBamlFunction, wrapBamlStreamingFunction };
|
|
144
|
+
export { type BamlFunction, type BamlStreamingFunction, type DecodeDebugData, type EncodeDebugData, type WrapBamlFunctionOptions, decodeObject, encodeObject, wrapBamlFunction, wrapBamlStreamingFunction };
|
package/dist/index.js
CHANGED
|
@@ -70,94 +70,6 @@ function matchesFieldPath(currentPath, targetSegments) {
|
|
|
70
70
|
}
|
|
71
71
|
return true;
|
|
72
72
|
}
|
|
73
|
-
function getInputPattern(inputFormat) {
|
|
74
|
-
if (inputFormat instanceof RegExp) {
|
|
75
|
-
const flags = inputFormat.flags.includes("g") ? inputFormat.flags : inputFormat.flags + "g";
|
|
76
|
-
return new RegExp(inputFormat.source, flags);
|
|
77
|
-
}
|
|
78
|
-
if (inputFormat === "UUID") {
|
|
79
|
-
return /\b[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/gi;
|
|
80
|
-
}
|
|
81
|
-
return /\b[0-9A-HJKMNP-TV-Z]{26}\b/gi;
|
|
82
|
-
}
|
|
83
|
-
function formatPlaceholder(outputFormat, index) {
|
|
84
|
-
if (outputFormat === "SafeNumeric") {
|
|
85
|
-
const s = index.toString();
|
|
86
|
-
const width = Math.max(3, Math.ceil(s.length / 3) * 3);
|
|
87
|
-
return `<${s.padStart(width, "0")}>`;
|
|
88
|
-
}
|
|
89
|
-
if (outputFormat === "Numeric") {
|
|
90
|
-
const s = index.toString();
|
|
91
|
-
const width = Math.max(3, Math.ceil(s.length / 3) * 3);
|
|
92
|
-
return s.padStart(width, "0");
|
|
93
|
-
}
|
|
94
|
-
if (outputFormat === "IdToken") {
|
|
95
|
-
const BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
96
|
-
if (index < 62) return BASE62[index];
|
|
97
|
-
let result = "";
|
|
98
|
-
let n = index;
|
|
99
|
-
while (n > 0) {
|
|
100
|
-
result = BASE62[n % 62] + result;
|
|
101
|
-
n = Math.floor(n / 62);
|
|
102
|
-
}
|
|
103
|
-
return result;
|
|
104
|
-
}
|
|
105
|
-
if (outputFormat === "Passthrough") {
|
|
106
|
-
return "";
|
|
107
|
-
}
|
|
108
|
-
if (typeof outputFormat === "function") {
|
|
109
|
-
return outputFormat(index);
|
|
110
|
-
}
|
|
111
|
-
const template = outputFormat.template;
|
|
112
|
-
const match = template.match(/\{i(?::([^}]+))?\}/);
|
|
113
|
-
if (!match) return `${index}`;
|
|
114
|
-
const specifier = match[1];
|
|
115
|
-
let formatted;
|
|
116
|
-
if (!specifier) {
|
|
117
|
-
formatted = index.toString();
|
|
118
|
-
} else if (specifier === "base62") {
|
|
119
|
-
const BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
120
|
-
if (index < 62) formatted = BASE62[index];
|
|
121
|
-
else {
|
|
122
|
-
formatted = "";
|
|
123
|
-
let n = index;
|
|
124
|
-
while (n > 0) {
|
|
125
|
-
formatted = BASE62[n % 62] + formatted;
|
|
126
|
-
n = Math.floor(n / 62);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
} else if (specifier === "zeroFilled") {
|
|
130
|
-
const s = index.toString();
|
|
131
|
-
const width = Math.max(3, Math.ceil(s.length / 3) * 3);
|
|
132
|
-
formatted = s.padStart(width, "0");
|
|
133
|
-
} else {
|
|
134
|
-
const padMatch = specifier.match(/^(\d+)$/);
|
|
135
|
-
if (padMatch) {
|
|
136
|
-
formatted = index.toString().padStart(parseInt(padMatch[1], 10), "0");
|
|
137
|
-
} else {
|
|
138
|
-
formatted = index.toString();
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
return template.replace(match[0], formatted);
|
|
142
|
-
}
|
|
143
|
-
function encodeStringWithContext(text, ctx) {
|
|
144
|
-
if (ctx.config.outputFormat === "Passthrough") {
|
|
145
|
-
return text;
|
|
146
|
-
}
|
|
147
|
-
const pattern = getInputPattern(ctx.config.inputFormat);
|
|
148
|
-
pattern.lastIndex = 0;
|
|
149
|
-
return text.replace(pattern, (match) => {
|
|
150
|
-
const id = match.toLowerCase();
|
|
151
|
-
if (ctx.idToPlaceholder.has(id)) {
|
|
152
|
-
return ctx.idToPlaceholder.get(id);
|
|
153
|
-
}
|
|
154
|
-
const placeholder = formatPlaceholder(ctx.config.outputFormat, ctx.nextIndex);
|
|
155
|
-
ctx.nextIndex++;
|
|
156
|
-
ctx.idToPlaceholder.set(id, placeholder);
|
|
157
|
-
ctx.mapping[placeholder] = id;
|
|
158
|
-
return placeholder;
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
73
|
function deepEncode(value, ctx, path = []) {
|
|
162
74
|
if (value === null || value === void 0) {
|
|
163
75
|
return value;
|
|
@@ -167,12 +79,10 @@ function deepEncode(value, ctx, path = []) {
|
|
|
167
79
|
if (!shouldEncode) {
|
|
168
80
|
return value;
|
|
169
81
|
}
|
|
170
|
-
return
|
|
82
|
+
return (0, import_prompt_identifiers.encode)(value, ctx.config, ctx.state).encoded;
|
|
171
83
|
}
|
|
172
84
|
if (Array.isArray(value)) {
|
|
173
|
-
return value.map(
|
|
174
|
-
(item, index) => deepEncode(item, ctx, [...path, String(index)])
|
|
175
|
-
);
|
|
85
|
+
return value.map((item, index) => deepEncode(item, ctx, [...path, String(index)]));
|
|
176
86
|
}
|
|
177
87
|
if (typeof value === "object") {
|
|
178
88
|
const result = {};
|
|
@@ -207,57 +117,93 @@ function deepDecode(value, mapping, countRef) {
|
|
|
207
117
|
return value;
|
|
208
118
|
}
|
|
209
119
|
function wrapBamlFunction(fn, options) {
|
|
210
|
-
const { config, encodeFields, onEncode, onDecode } = options;
|
|
120
|
+
const { config, encodeFields, onEncode, onDecode, debug } = options;
|
|
211
121
|
const fieldPaths = encodeFields ? encodeFields.map(parseFieldPath) : null;
|
|
212
122
|
return async (input) => {
|
|
213
123
|
const ctx = {
|
|
214
124
|
config,
|
|
215
125
|
fieldPaths,
|
|
216
|
-
|
|
217
|
-
mapping: {},
|
|
218
|
-
nextIndex: 0
|
|
126
|
+
state: (0, import_prompt_identifiers.createEncodeState)()
|
|
219
127
|
};
|
|
128
|
+
const startEncode = debug ? performance.now() : 0;
|
|
220
129
|
const encodedInput = deepEncode(input, ctx);
|
|
130
|
+
const encodeDurationMs = debug ? performance.now() - startEncode : 0;
|
|
221
131
|
onEncode?.({
|
|
222
|
-
mapping: ctx.mapping,
|
|
223
|
-
|
|
132
|
+
mapping: ctx.state.mapping,
|
|
133
|
+
...debug && {
|
|
134
|
+
debugData: {
|
|
135
|
+
encodedCount: Object.keys(ctx.state.mapping).length,
|
|
136
|
+
input,
|
|
137
|
+
output: encodedInput,
|
|
138
|
+
durationMs: encodeDurationMs
|
|
139
|
+
}
|
|
140
|
+
}
|
|
224
141
|
});
|
|
225
142
|
const output = await fn(encodedInput);
|
|
226
143
|
const countRef = { count: 0 };
|
|
227
|
-
const
|
|
228
|
-
|
|
144
|
+
const startDecode = debug ? performance.now() : 0;
|
|
145
|
+
const decodedOutput = deepDecode(output, ctx.state.mapping, countRef);
|
|
146
|
+
const decodeDurationMs = debug ? performance.now() - startDecode : 0;
|
|
147
|
+
onDecode?.({
|
|
148
|
+
...debug && {
|
|
149
|
+
debugData: {
|
|
150
|
+
decodedCount: countRef.count,
|
|
151
|
+
input: output,
|
|
152
|
+
output: decodedOutput,
|
|
153
|
+
durationMs: decodeDurationMs
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
});
|
|
229
157
|
return decodedOutput;
|
|
230
158
|
};
|
|
231
159
|
}
|
|
232
160
|
function wrapBamlStreamingFunction(fn, options) {
|
|
233
|
-
const { config, encodeFields, onEncode, onDecode } = options;
|
|
161
|
+
const { config, encodeFields, onEncode, onDecode, debug } = options;
|
|
234
162
|
const fieldPaths = encodeFields ? encodeFields.map(parseFieldPath) : null;
|
|
235
163
|
return async function* (input) {
|
|
236
164
|
const ctx = {
|
|
237
165
|
config,
|
|
238
166
|
fieldPaths,
|
|
239
|
-
|
|
240
|
-
mapping: {},
|
|
241
|
-
nextIndex: 0
|
|
167
|
+
state: (0, import_prompt_identifiers.createEncodeState)()
|
|
242
168
|
};
|
|
169
|
+
const startEncode = debug ? performance.now() : 0;
|
|
243
170
|
const encodedInput = deepEncode(input, ctx);
|
|
171
|
+
const encodeDurationMs = debug ? performance.now() - startEncode : 0;
|
|
244
172
|
onEncode?.({
|
|
245
|
-
mapping: ctx.mapping,
|
|
246
|
-
|
|
173
|
+
mapping: ctx.state.mapping,
|
|
174
|
+
...debug && {
|
|
175
|
+
debugData: {
|
|
176
|
+
encodedCount: Object.keys(ctx.state.mapping).length,
|
|
177
|
+
input,
|
|
178
|
+
output: encodedInput,
|
|
179
|
+
durationMs: encodeDurationMs
|
|
180
|
+
}
|
|
181
|
+
}
|
|
247
182
|
});
|
|
248
183
|
const generator = fn(encodedInput);
|
|
249
184
|
let totalDecoded = 0;
|
|
185
|
+
const startDecode = debug ? performance.now() : 0;
|
|
250
186
|
while (true) {
|
|
251
187
|
const { value, done } = await generator.next();
|
|
252
188
|
if (done) {
|
|
253
189
|
const countRef2 = { count: 0 };
|
|
254
|
-
const decodedValue2 = deepDecode(value, ctx.mapping, countRef2);
|
|
190
|
+
const decodedValue2 = deepDecode(value, ctx.state.mapping, countRef2);
|
|
255
191
|
totalDecoded += countRef2.count;
|
|
256
|
-
|
|
192
|
+
const decodeDurationMs = debug ? performance.now() - startDecode : 0;
|
|
193
|
+
onDecode?.({
|
|
194
|
+
...debug && {
|
|
195
|
+
debugData: {
|
|
196
|
+
decodedCount: totalDecoded,
|
|
197
|
+
input: value,
|
|
198
|
+
output: decodedValue2,
|
|
199
|
+
durationMs: decodeDurationMs
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
});
|
|
257
203
|
return decodedValue2;
|
|
258
204
|
}
|
|
259
205
|
const countRef = { count: 0 };
|
|
260
|
-
const decodedValue = deepDecode(value, ctx.mapping, countRef);
|
|
206
|
+
const decodedValue = deepDecode(value, ctx.state.mapping, countRef);
|
|
261
207
|
totalDecoded += countRef.count;
|
|
262
208
|
yield decodedValue;
|
|
263
209
|
}
|
|
@@ -268,12 +214,10 @@ function encodeObject(obj, config, encodeFields) {
|
|
|
268
214
|
const ctx = {
|
|
269
215
|
config,
|
|
270
216
|
fieldPaths,
|
|
271
|
-
|
|
272
|
-
mapping: {},
|
|
273
|
-
nextIndex: 0
|
|
217
|
+
state: (0, import_prompt_identifiers.createEncodeState)()
|
|
274
218
|
};
|
|
275
219
|
const encoded = deepEncode(obj, ctx);
|
|
276
|
-
return { encoded, mapping: ctx.mapping };
|
|
220
|
+
return { encoded, mapping: ctx.state.mapping };
|
|
277
221
|
}
|
|
278
222
|
function decodeObject(obj, mapping) {
|
|
279
223
|
const countRef = { count: 0 };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import { decode } from "prompt-identifiers";
|
|
2
|
+
import { createEncodeState, decode, encode } from "prompt-identifiers";
|
|
3
3
|
function parseFieldPath(path) {
|
|
4
4
|
const segments = [];
|
|
5
5
|
let current = "";
|
|
@@ -43,94 +43,6 @@ function matchesFieldPath(currentPath, targetSegments) {
|
|
|
43
43
|
}
|
|
44
44
|
return true;
|
|
45
45
|
}
|
|
46
|
-
function getInputPattern(inputFormat) {
|
|
47
|
-
if (inputFormat instanceof RegExp) {
|
|
48
|
-
const flags = inputFormat.flags.includes("g") ? inputFormat.flags : inputFormat.flags + "g";
|
|
49
|
-
return new RegExp(inputFormat.source, flags);
|
|
50
|
-
}
|
|
51
|
-
if (inputFormat === "UUID") {
|
|
52
|
-
return /\b[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/gi;
|
|
53
|
-
}
|
|
54
|
-
return /\b[0-9A-HJKMNP-TV-Z]{26}\b/gi;
|
|
55
|
-
}
|
|
56
|
-
function formatPlaceholder(outputFormat, index) {
|
|
57
|
-
if (outputFormat === "SafeNumeric") {
|
|
58
|
-
const s = index.toString();
|
|
59
|
-
const width = Math.max(3, Math.ceil(s.length / 3) * 3);
|
|
60
|
-
return `<${s.padStart(width, "0")}>`;
|
|
61
|
-
}
|
|
62
|
-
if (outputFormat === "Numeric") {
|
|
63
|
-
const s = index.toString();
|
|
64
|
-
const width = Math.max(3, Math.ceil(s.length / 3) * 3);
|
|
65
|
-
return s.padStart(width, "0");
|
|
66
|
-
}
|
|
67
|
-
if (outputFormat === "IdToken") {
|
|
68
|
-
const BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
69
|
-
if (index < 62) return BASE62[index];
|
|
70
|
-
let result = "";
|
|
71
|
-
let n = index;
|
|
72
|
-
while (n > 0) {
|
|
73
|
-
result = BASE62[n % 62] + result;
|
|
74
|
-
n = Math.floor(n / 62);
|
|
75
|
-
}
|
|
76
|
-
return result;
|
|
77
|
-
}
|
|
78
|
-
if (outputFormat === "Passthrough") {
|
|
79
|
-
return "";
|
|
80
|
-
}
|
|
81
|
-
if (typeof outputFormat === "function") {
|
|
82
|
-
return outputFormat(index);
|
|
83
|
-
}
|
|
84
|
-
const template = outputFormat.template;
|
|
85
|
-
const match = template.match(/\{i(?::([^}]+))?\}/);
|
|
86
|
-
if (!match) return `${index}`;
|
|
87
|
-
const specifier = match[1];
|
|
88
|
-
let formatted;
|
|
89
|
-
if (!specifier) {
|
|
90
|
-
formatted = index.toString();
|
|
91
|
-
} else if (specifier === "base62") {
|
|
92
|
-
const BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
93
|
-
if (index < 62) formatted = BASE62[index];
|
|
94
|
-
else {
|
|
95
|
-
formatted = "";
|
|
96
|
-
let n = index;
|
|
97
|
-
while (n > 0) {
|
|
98
|
-
formatted = BASE62[n % 62] + formatted;
|
|
99
|
-
n = Math.floor(n / 62);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
} else if (specifier === "zeroFilled") {
|
|
103
|
-
const s = index.toString();
|
|
104
|
-
const width = Math.max(3, Math.ceil(s.length / 3) * 3);
|
|
105
|
-
formatted = s.padStart(width, "0");
|
|
106
|
-
} else {
|
|
107
|
-
const padMatch = specifier.match(/^(\d+)$/);
|
|
108
|
-
if (padMatch) {
|
|
109
|
-
formatted = index.toString().padStart(parseInt(padMatch[1], 10), "0");
|
|
110
|
-
} else {
|
|
111
|
-
formatted = index.toString();
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
return template.replace(match[0], formatted);
|
|
115
|
-
}
|
|
116
|
-
function encodeStringWithContext(text, ctx) {
|
|
117
|
-
if (ctx.config.outputFormat === "Passthrough") {
|
|
118
|
-
return text;
|
|
119
|
-
}
|
|
120
|
-
const pattern = getInputPattern(ctx.config.inputFormat);
|
|
121
|
-
pattern.lastIndex = 0;
|
|
122
|
-
return text.replace(pattern, (match) => {
|
|
123
|
-
const id = match.toLowerCase();
|
|
124
|
-
if (ctx.idToPlaceholder.has(id)) {
|
|
125
|
-
return ctx.idToPlaceholder.get(id);
|
|
126
|
-
}
|
|
127
|
-
const placeholder = formatPlaceholder(ctx.config.outputFormat, ctx.nextIndex);
|
|
128
|
-
ctx.nextIndex++;
|
|
129
|
-
ctx.idToPlaceholder.set(id, placeholder);
|
|
130
|
-
ctx.mapping[placeholder] = id;
|
|
131
|
-
return placeholder;
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
46
|
function deepEncode(value, ctx, path = []) {
|
|
135
47
|
if (value === null || value === void 0) {
|
|
136
48
|
return value;
|
|
@@ -140,12 +52,10 @@ function deepEncode(value, ctx, path = []) {
|
|
|
140
52
|
if (!shouldEncode) {
|
|
141
53
|
return value;
|
|
142
54
|
}
|
|
143
|
-
return
|
|
55
|
+
return encode(value, ctx.config, ctx.state).encoded;
|
|
144
56
|
}
|
|
145
57
|
if (Array.isArray(value)) {
|
|
146
|
-
return value.map(
|
|
147
|
-
(item, index) => deepEncode(item, ctx, [...path, String(index)])
|
|
148
|
-
);
|
|
58
|
+
return value.map((item, index) => deepEncode(item, ctx, [...path, String(index)]));
|
|
149
59
|
}
|
|
150
60
|
if (typeof value === "object") {
|
|
151
61
|
const result = {};
|
|
@@ -180,57 +90,93 @@ function deepDecode(value, mapping, countRef) {
|
|
|
180
90
|
return value;
|
|
181
91
|
}
|
|
182
92
|
function wrapBamlFunction(fn, options) {
|
|
183
|
-
const { config, encodeFields, onEncode, onDecode } = options;
|
|
93
|
+
const { config, encodeFields, onEncode, onDecode, debug } = options;
|
|
184
94
|
const fieldPaths = encodeFields ? encodeFields.map(parseFieldPath) : null;
|
|
185
95
|
return async (input) => {
|
|
186
96
|
const ctx = {
|
|
187
97
|
config,
|
|
188
98
|
fieldPaths,
|
|
189
|
-
|
|
190
|
-
mapping: {},
|
|
191
|
-
nextIndex: 0
|
|
99
|
+
state: createEncodeState()
|
|
192
100
|
};
|
|
101
|
+
const startEncode = debug ? performance.now() : 0;
|
|
193
102
|
const encodedInput = deepEncode(input, ctx);
|
|
103
|
+
const encodeDurationMs = debug ? performance.now() - startEncode : 0;
|
|
194
104
|
onEncode?.({
|
|
195
|
-
mapping: ctx.mapping,
|
|
196
|
-
|
|
105
|
+
mapping: ctx.state.mapping,
|
|
106
|
+
...debug && {
|
|
107
|
+
debugData: {
|
|
108
|
+
encodedCount: Object.keys(ctx.state.mapping).length,
|
|
109
|
+
input,
|
|
110
|
+
output: encodedInput,
|
|
111
|
+
durationMs: encodeDurationMs
|
|
112
|
+
}
|
|
113
|
+
}
|
|
197
114
|
});
|
|
198
115
|
const output = await fn(encodedInput);
|
|
199
116
|
const countRef = { count: 0 };
|
|
200
|
-
const
|
|
201
|
-
|
|
117
|
+
const startDecode = debug ? performance.now() : 0;
|
|
118
|
+
const decodedOutput = deepDecode(output, ctx.state.mapping, countRef);
|
|
119
|
+
const decodeDurationMs = debug ? performance.now() - startDecode : 0;
|
|
120
|
+
onDecode?.({
|
|
121
|
+
...debug && {
|
|
122
|
+
debugData: {
|
|
123
|
+
decodedCount: countRef.count,
|
|
124
|
+
input: output,
|
|
125
|
+
output: decodedOutput,
|
|
126
|
+
durationMs: decodeDurationMs
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
202
130
|
return decodedOutput;
|
|
203
131
|
};
|
|
204
132
|
}
|
|
205
133
|
function wrapBamlStreamingFunction(fn, options) {
|
|
206
|
-
const { config, encodeFields, onEncode, onDecode } = options;
|
|
134
|
+
const { config, encodeFields, onEncode, onDecode, debug } = options;
|
|
207
135
|
const fieldPaths = encodeFields ? encodeFields.map(parseFieldPath) : null;
|
|
208
136
|
return async function* (input) {
|
|
209
137
|
const ctx = {
|
|
210
138
|
config,
|
|
211
139
|
fieldPaths,
|
|
212
|
-
|
|
213
|
-
mapping: {},
|
|
214
|
-
nextIndex: 0
|
|
140
|
+
state: createEncodeState()
|
|
215
141
|
};
|
|
142
|
+
const startEncode = debug ? performance.now() : 0;
|
|
216
143
|
const encodedInput = deepEncode(input, ctx);
|
|
144
|
+
const encodeDurationMs = debug ? performance.now() - startEncode : 0;
|
|
217
145
|
onEncode?.({
|
|
218
|
-
mapping: ctx.mapping,
|
|
219
|
-
|
|
146
|
+
mapping: ctx.state.mapping,
|
|
147
|
+
...debug && {
|
|
148
|
+
debugData: {
|
|
149
|
+
encodedCount: Object.keys(ctx.state.mapping).length,
|
|
150
|
+
input,
|
|
151
|
+
output: encodedInput,
|
|
152
|
+
durationMs: encodeDurationMs
|
|
153
|
+
}
|
|
154
|
+
}
|
|
220
155
|
});
|
|
221
156
|
const generator = fn(encodedInput);
|
|
222
157
|
let totalDecoded = 0;
|
|
158
|
+
const startDecode = debug ? performance.now() : 0;
|
|
223
159
|
while (true) {
|
|
224
160
|
const { value, done } = await generator.next();
|
|
225
161
|
if (done) {
|
|
226
162
|
const countRef2 = { count: 0 };
|
|
227
|
-
const decodedValue2 = deepDecode(value, ctx.mapping, countRef2);
|
|
163
|
+
const decodedValue2 = deepDecode(value, ctx.state.mapping, countRef2);
|
|
228
164
|
totalDecoded += countRef2.count;
|
|
229
|
-
|
|
165
|
+
const decodeDurationMs = debug ? performance.now() - startDecode : 0;
|
|
166
|
+
onDecode?.({
|
|
167
|
+
...debug && {
|
|
168
|
+
debugData: {
|
|
169
|
+
decodedCount: totalDecoded,
|
|
170
|
+
input: value,
|
|
171
|
+
output: decodedValue2,
|
|
172
|
+
durationMs: decodeDurationMs
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
});
|
|
230
176
|
return decodedValue2;
|
|
231
177
|
}
|
|
232
178
|
const countRef = { count: 0 };
|
|
233
|
-
const decodedValue = deepDecode(value, ctx.mapping, countRef);
|
|
179
|
+
const decodedValue = deepDecode(value, ctx.state.mapping, countRef);
|
|
234
180
|
totalDecoded += countRef.count;
|
|
235
181
|
yield decodedValue;
|
|
236
182
|
}
|
|
@@ -241,12 +187,10 @@ function encodeObject(obj, config, encodeFields) {
|
|
|
241
187
|
const ctx = {
|
|
242
188
|
config,
|
|
243
189
|
fieldPaths,
|
|
244
|
-
|
|
245
|
-
mapping: {},
|
|
246
|
-
nextIndex: 0
|
|
190
|
+
state: createEncodeState()
|
|
247
191
|
};
|
|
248
192
|
const encoded = deepEncode(obj, ctx);
|
|
249
|
-
return { encoded, mapping: ctx.mapping };
|
|
193
|
+
return { encoded, mapping: ctx.state.mapping };
|
|
250
194
|
}
|
|
251
195
|
function decodeObject(obj, mapping) {
|
|
252
196
|
const countRef = { count: 0 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prompt-identifiers-baml",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "BAML integration for prompt-identifiers. Efficient ID compression for token optimization.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"url": "https://github.com/fogx/prompt-identifiers"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"prompt-identifiers": ">=0.1.
|
|
42
|
+
"prompt-identifiers": ">=0.1.2",
|
|
43
43
|
"@boundaryml/baml": ">=0.70.0"
|
|
44
44
|
},
|
|
45
45
|
"peerDependenciesMeta": {
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"ts-jest": "^29.0.0",
|
|
55
55
|
"tsup": "^8.5.1",
|
|
56
56
|
"typescript": "^5.0.0",
|
|
57
|
-
"prompt-identifiers": "0.1.
|
|
57
|
+
"prompt-identifiers": "0.1.2"
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
|
60
60
|
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|