@happyvertical/smrt-prompts 0.30.0

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/dist/index.js ADDED
@@ -0,0 +1,784 @@
1
+ import { ObjectRegistry, field, smrt, SmrtObject, SmrtCollection } from "@happyvertical/smrt-core";
2
+ import { getPackageConfig } from "@happyvertical/smrt-config";
3
+ import { getTenantId } from "@happyvertical/smrt-tenancy";
4
+ ObjectRegistry.registerPackageManifest(
5
+ new URL("./manifest.json", import.meta.url)
6
+ );
7
+ const PROMPT_CACHE_TTL_MS = 3e4;
8
+ const promptCache = /* @__PURE__ */ new Map();
9
+ const dbInstanceIds = /* @__PURE__ */ new WeakMap();
10
+ let nextDbId = 1;
11
+ function getDbNamespace(db) {
12
+ if (!db) {
13
+ return "no-db";
14
+ }
15
+ if (typeof db === "string") {
16
+ return `db:${db}`;
17
+ }
18
+ if (typeof db === "object") {
19
+ const dbObject = db;
20
+ if (typeof dbObject.query === "function") {
21
+ if (!dbInstanceIds.has(dbObject)) {
22
+ dbInstanceIds.set(dbObject, `db-instance:${nextDbId++}`);
23
+ }
24
+ const namespace = dbInstanceIds.get(dbObject);
25
+ if (namespace) {
26
+ return namespace;
27
+ }
28
+ return "db-instance:unknown";
29
+ }
30
+ try {
31
+ return `db-config:${JSON.stringify(dbObject)}`;
32
+ } catch {
33
+ return "db-config:opaque";
34
+ }
35
+ }
36
+ return "db:unknown";
37
+ }
38
+ function buildCacheKey(key, tenantId, db) {
39
+ return `${getDbNamespace(db)}::${key}::${tenantId ?? "app"}`;
40
+ }
41
+ function getPromptCacheTtlMs() {
42
+ return PROMPT_CACHE_TTL_MS;
43
+ }
44
+ function getCachedPromptBase(key, tenantId, db) {
45
+ const cacheKey = buildCacheKey(key, tenantId, db);
46
+ const cached = promptCache.get(cacheKey);
47
+ if (!cached) {
48
+ return null;
49
+ }
50
+ if (cached.expiresAt <= Date.now()) {
51
+ promptCache.delete(cacheKey);
52
+ return null;
53
+ }
54
+ return cached.value;
55
+ }
56
+ function setCachedPromptBase(key, tenantId, db, value) {
57
+ promptCache.set(buildCacheKey(key, tenantId, db), {
58
+ expiresAt: Date.now() + PROMPT_CACHE_TTL_MS,
59
+ value
60
+ });
61
+ }
62
+ function invalidatePromptCache(key, tenantId, db) {
63
+ const dbNamespace = getDbNamespace(db);
64
+ if (tenantId !== null && tenantId !== void 0) {
65
+ promptCache.delete(buildCacheKey(key, tenantId, db));
66
+ return;
67
+ }
68
+ const keyPrefix = `${dbNamespace}::${key}::`;
69
+ for (const cacheKey of promptCache.keys()) {
70
+ if (cacheKey.startsWith(keyPrefix)) {
71
+ promptCache.delete(cacheKey);
72
+ }
73
+ }
74
+ }
75
+ function clearPromptCache() {
76
+ promptCache.clear();
77
+ }
78
+ const DEFAULT_EDITABLE = {
79
+ template: false,
80
+ profile: false,
81
+ model: false,
82
+ params: false
83
+ };
84
+ const TOP_LEVEL_RESERVED_KEYS = /* @__PURE__ */ new Set([
85
+ "key",
86
+ "editable",
87
+ "template",
88
+ "ai",
89
+ "profile",
90
+ "provider",
91
+ "model",
92
+ "params",
93
+ "temperature",
94
+ "maxTokens"
95
+ ]);
96
+ const AI_RESERVED_KEYS = /* @__PURE__ */ new Set([
97
+ "profile",
98
+ "provider",
99
+ "model",
100
+ "params",
101
+ "temperature",
102
+ "maxTokens"
103
+ ]);
104
+ const AI_ROUTING_PARAM_KEYS = /* @__PURE__ */ new Set([
105
+ "profile",
106
+ "provider",
107
+ "type",
108
+ "model",
109
+ "defaultModel"
110
+ ]);
111
+ function isPlainObject(value) {
112
+ return !!value && typeof value === "object" && !Array.isArray(value);
113
+ }
114
+ function normalizeEditableConfig(editable) {
115
+ return {
116
+ template: editable?.template ?? DEFAULT_EDITABLE.template,
117
+ profile: editable?.profile ?? DEFAULT_EDITABLE.profile,
118
+ model: editable?.model ?? DEFAULT_EDITABLE.model,
119
+ params: editable?.params ?? DEFAULT_EDITABLE.params
120
+ };
121
+ }
122
+ function extractPromptParams(source, reservedKeys) {
123
+ const params = {};
124
+ if (isPlainObject(source.params)) {
125
+ Object.assign(params, sanitizePromptParams(source.params));
126
+ }
127
+ if ("temperature" in source && source.temperature !== void 0) {
128
+ params.temperature = source.temperature;
129
+ }
130
+ if ("maxTokens" in source && source.maxTokens !== void 0) {
131
+ params.maxTokens = source.maxTokens;
132
+ }
133
+ for (const [key, value] of Object.entries(source)) {
134
+ if (reservedKeys.has(key) || value === void 0) {
135
+ continue;
136
+ }
137
+ assignPromptParam(params, key, value);
138
+ }
139
+ return params;
140
+ }
141
+ function assignPromptParam(params, key, value) {
142
+ if (AI_ROUTING_PARAM_KEYS.has(key) || value === void 0) {
143
+ return;
144
+ }
145
+ params[key] = value;
146
+ }
147
+ function sanitizePromptParams(params) {
148
+ const sanitized = {};
149
+ if (!params) {
150
+ return sanitized;
151
+ }
152
+ for (const [key, value] of Object.entries(params)) {
153
+ assignPromptParam(sanitized, key, value);
154
+ }
155
+ return sanitized;
156
+ }
157
+ function mergePromptParams(...paramLayers) {
158
+ const merged = {};
159
+ for (const layer of paramLayers) {
160
+ if (!layer) {
161
+ continue;
162
+ }
163
+ for (const [key, value] of Object.entries(layer)) {
164
+ assignPromptParam(merged, key, value);
165
+ }
166
+ }
167
+ return merged;
168
+ }
169
+ function normalizeAIInput(ai) {
170
+ if (!isPlainObject(ai)) {
171
+ return { params: {} };
172
+ }
173
+ const profile = typeof ai.profile === "string" || ai.profile === null ? ai.profile : void 0;
174
+ const model = typeof ai.model === "string" || ai.model === null ? ai.model : void 0;
175
+ return {
176
+ profile: profile ?? void 0,
177
+ model: model ?? void 0,
178
+ params: extractPromptParams(ai, AI_RESERVED_KEYS)
179
+ };
180
+ }
181
+ function normalizePromptDefinitionInput(input) {
182
+ return {
183
+ key: input.key,
184
+ template: input.template,
185
+ ai: normalizeAIInput(input.ai),
186
+ editable: normalizeEditableConfig(input.editable)
187
+ };
188
+ }
189
+ function normalizePromptLayer(input) {
190
+ if (!isPlainObject(input)) {
191
+ return {};
192
+ }
193
+ const nestedAi = isPlainObject(input.ai) ? normalizeAIInput(input.ai) : { params: {} };
194
+ const topLevelParams = extractPromptParams(input, TOP_LEVEL_RESERVED_KEYS);
195
+ return {
196
+ template: input.template === void 0 ? void 0 : input.template === null ? null : String(input.template),
197
+ profile: input.profile === void 0 ? nestedAi.profile : input.profile === null ? null : String(input.profile),
198
+ model: input.model === void 0 ? nestedAi.model : input.model === null ? null : String(input.model),
199
+ params: mergePromptParams(topLevelParams, nestedAi.params)
200
+ };
201
+ }
202
+ function mergePromptLayers(...layers) {
203
+ let template = "";
204
+ let profile;
205
+ let model;
206
+ let params = {};
207
+ for (const layer of layers) {
208
+ if (!layer) {
209
+ continue;
210
+ }
211
+ if (layer.template !== void 0 && layer.template !== null) {
212
+ template = layer.template;
213
+ }
214
+ if (layer.profile !== void 0 && layer.profile !== null) {
215
+ profile = layer.profile;
216
+ }
217
+ if (layer.model !== void 0 && layer.model !== null) {
218
+ model = layer.model;
219
+ }
220
+ if (layer.params !== void 0 && layer.params !== null) {
221
+ params = mergePromptParams(params, layer.params);
222
+ }
223
+ }
224
+ return {
225
+ template,
226
+ ai: {
227
+ profile,
228
+ model,
229
+ params
230
+ }
231
+ };
232
+ }
233
+ function normalizeProfileConfig(profile) {
234
+ const source = isPlainObject(profile) ? profile : {};
235
+ const params = extractPromptParams(source, /* @__PURE__ */ new Set(["provider", "model"]));
236
+ return {
237
+ provider: String(profile.provider),
238
+ model: String(profile.model),
239
+ params
240
+ };
241
+ }
242
+ function validateProfileName(profileName, config) {
243
+ if (profileName.trim() === "") {
244
+ throw new Error("Prompt profile names cannot be empty");
245
+ }
246
+ const allowedProfileNames = config.allowedProfileNames;
247
+ if (Array.isArray(allowedProfileNames) && !allowedProfileNames.includes(profileName)) {
248
+ throw new Error(
249
+ `Prompt profile "${profileName}" is not in the allowed profile list`
250
+ );
251
+ }
252
+ if (!config.profiles?.[profileName]) {
253
+ throw new Error(`Prompt profile "${profileName}" is not configured`);
254
+ }
255
+ }
256
+ function validateModelName(modelName, config) {
257
+ if (modelName.trim() === "") {
258
+ throw new Error("Prompt model overrides cannot be empty");
259
+ }
260
+ const allowedModels = config.allowedModels;
261
+ if (Array.isArray(allowedModels) && !allowedModels.includes(modelName)) {
262
+ throw new Error(
263
+ `Prompt model "${modelName}" is not in the allowed model list`
264
+ );
265
+ }
266
+ }
267
+ function buildResolvedAI(ai, config) {
268
+ let provider;
269
+ let model = ai.model;
270
+ let params = ai.params;
271
+ if (ai.profile) {
272
+ validateProfileName(ai.profile, config);
273
+ const configuredProfile = config.profiles?.[ai.profile];
274
+ if (!configuredProfile) {
275
+ throw new Error(`Prompt profile "${ai.profile}" is not configured`);
276
+ }
277
+ const profileConfig = normalizeProfileConfig(configuredProfile);
278
+ provider = profileConfig.provider;
279
+ model = model ?? profileConfig.model;
280
+ params = mergePromptParams(profileConfig.params, params);
281
+ }
282
+ if (ai.model) {
283
+ validateModelName(ai.model, config);
284
+ }
285
+ if (ai.model && !ai.profile) {
286
+ throw new Error(
287
+ "Direct prompt model overrides require an effective prompt profile"
288
+ );
289
+ }
290
+ if (model && !ai.model && config.allowedModels) {
291
+ validateModelName(model, config);
292
+ }
293
+ const resolved = {
294
+ ...params,
295
+ profile: ai.profile,
296
+ provider,
297
+ model,
298
+ ...config.allowedModels ? { allowedModels: [...config.allowedModels] } : {},
299
+ params
300
+ };
301
+ if (typeof params.temperature === "number") {
302
+ resolved.temperature = params.temperature;
303
+ }
304
+ if (typeof params.maxTokens === "number") {
305
+ resolved.maxTokens = params.maxTokens;
306
+ }
307
+ return resolved;
308
+ }
309
+ function renderPromptTemplate(template, variables) {
310
+ if (!variables || Object.keys(variables).length === 0) {
311
+ return template;
312
+ }
313
+ return template.replace(/\{([^{}]+)\}/g, (_match, rawKey) => {
314
+ const key = rawKey.trim();
315
+ const value = variables[key];
316
+ if (value === void 0 || value === null) {
317
+ return "";
318
+ }
319
+ if (value instanceof Date) {
320
+ return value.toISOString();
321
+ }
322
+ if (Array.isArray(value) || isPlainObject(value)) {
323
+ try {
324
+ return JSON.stringify(value);
325
+ } catch {
326
+ return "";
327
+ }
328
+ }
329
+ return String(value);
330
+ });
331
+ }
332
+ function parsePromptParams(raw) {
333
+ if (!raw) {
334
+ return {};
335
+ }
336
+ try {
337
+ const parsed = JSON.parse(raw);
338
+ return isPlainObject(parsed) ? sanitizePromptParams(parsed) : {};
339
+ } catch {
340
+ return {};
341
+ }
342
+ }
343
+ function serializePromptParams(params) {
344
+ if (params === null || params === void 0) {
345
+ return null;
346
+ }
347
+ return JSON.stringify(sanitizePromptParams(params));
348
+ }
349
+ function getRegistry() {
350
+ if (!globalThis.__smrtPromptRegistry) {
351
+ globalThis.__smrtPromptRegistry = /* @__PURE__ */ new Map();
352
+ }
353
+ return globalThis.__smrtPromptRegistry;
354
+ }
355
+ function stableStringify(value) {
356
+ if (Array.isArray(value)) {
357
+ return `[${value.map((item) => stableStringify(item)).join(",")}]`;
358
+ }
359
+ if (value && typeof value === "object") {
360
+ const entries = Object.entries(value).sort(
361
+ ([left], [right]) => left.localeCompare(right)
362
+ );
363
+ return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${stableStringify(item)}`).join(",")}}`;
364
+ }
365
+ return JSON.stringify(value);
366
+ }
367
+ const PromptRegistry = {
368
+ register(input) {
369
+ if (!input.key || input.key.trim() === "") {
370
+ throw new Error("Prompt definitions require a non-empty key");
371
+ }
372
+ const definition = normalizePromptDefinitionInput(input);
373
+ const registry = getRegistry();
374
+ const existing = registry.get(definition.key);
375
+ if (existing) {
376
+ const existingSignature = stableStringify(existing);
377
+ const incomingSignature = stableStringify(definition);
378
+ if (existingSignature !== incomingSignature) {
379
+ throw new Error(
380
+ `Prompt "${definition.key}" is already registered with a different definition`
381
+ );
382
+ }
383
+ return existing;
384
+ }
385
+ registry.set(definition.key, definition);
386
+ return definition;
387
+ },
388
+ get(key) {
389
+ return getRegistry().get(key);
390
+ },
391
+ has(key) {
392
+ return getRegistry().has(key);
393
+ },
394
+ getAll() {
395
+ return Array.from(getRegistry().values());
396
+ },
397
+ clear() {
398
+ getRegistry().clear();
399
+ }
400
+ };
401
+ function definePrompt(input) {
402
+ return PromptRegistry.register(input);
403
+ }
404
+ var __defProp = Object.defineProperty;
405
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
406
+ var __decorateClass = (decorators, target, key, kind) => {
407
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
408
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
409
+ if (decorator = decorators[i])
410
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
411
+ if (kind && result) __defProp(target, key, result);
412
+ return result;
413
+ };
414
+ function getPromptConfig$1() {
415
+ return getPackageConfig("prompts", {
416
+ profiles: {},
417
+ prompts: {}
418
+ });
419
+ }
420
+ let PromptOverride = class extends SmrtObject {
421
+ key = "";
422
+ tenantId = null;
423
+ template = null;
424
+ profile = null;
425
+ model = null;
426
+ params = null;
427
+ constructor(options = {}) {
428
+ super(options);
429
+ if (options.key !== void 0) this.key = options.key;
430
+ if (options.tenantId !== void 0) this.tenantId = options.tenantId;
431
+ if (options.template !== void 0) this.template = options.template;
432
+ if (options.profile !== void 0) this.profile = options.profile;
433
+ if (options.model !== void 0) this.model = options.model;
434
+ if (options.params !== void 0) {
435
+ if (typeof options.params === "string" || options.params === null) {
436
+ this.params = options.params;
437
+ } else {
438
+ this.params = serializePromptParams(options.params);
439
+ }
440
+ }
441
+ }
442
+ getParams() {
443
+ return parsePromptParams(this.params);
444
+ }
445
+ setParams(params) {
446
+ this.params = serializePromptParams(params);
447
+ }
448
+ toPromptLayer() {
449
+ return {
450
+ template: this.template,
451
+ profile: this.profile,
452
+ model: this.model,
453
+ params: this.params === null ? null : this.getParams()
454
+ };
455
+ }
456
+ async save() {
457
+ const previousIdentity = await this.getPersistedIdentity();
458
+ this.normalizeParamsForPersistence();
459
+ await this.validatePromptOverride();
460
+ this.context = this.tenantId ?? "__app__";
461
+ const identityChanged = previousIdentity && (previousIdentity.key !== this.key || previousIdentity.tenantId !== this.tenantId);
462
+ const result = identityChanged && previousIdentity ? await this.saveAfterIdentityChange() : await super.save();
463
+ if (identityChanged && previousIdentity) {
464
+ invalidatePromptCache(
465
+ previousIdentity.key,
466
+ previousIdentity.tenantId,
467
+ this.db
468
+ );
469
+ }
470
+ invalidatePromptCache(this.key, this.tenantId, this.db);
471
+ return result;
472
+ }
473
+ async saveAfterIdentityChange() {
474
+ if (typeof this.db.beginTransaction === "function") {
475
+ return this.saveAfterIdentityChangeInTransaction();
476
+ }
477
+ return this.saveAfterIdentityChangeWithDeferredDelete();
478
+ }
479
+ async saveAfterIdentityChangeInTransaction() {
480
+ const originalDb = this._db;
481
+ const originalOptionsDb = this.options.db;
482
+ const tx = await this.db.beginTransaction?.();
483
+ if (!tx) {
484
+ return this.saveAfterIdentityChangeWithDeferredDelete();
485
+ }
486
+ try {
487
+ this._db = tx;
488
+ this.options.db = tx;
489
+ await super.delete();
490
+ const result = await super.save();
491
+ await tx.commit();
492
+ return result;
493
+ } catch (error) {
494
+ try {
495
+ await tx.rollback();
496
+ } catch {
497
+ }
498
+ throw error;
499
+ } finally {
500
+ this._db = originalDb;
501
+ this.options.db = originalOptionsDb;
502
+ }
503
+ }
504
+ async saveAfterIdentityChangeWithDeferredDelete() {
505
+ const previousId = this.id;
506
+ if (!previousId) {
507
+ return super.save();
508
+ }
509
+ const replacementId = crypto.randomUUID();
510
+ let replacementSaved = false;
511
+ this.id = replacementId;
512
+ try {
513
+ const result = await super.save();
514
+ replacementSaved = true;
515
+ await this.db.delete(this.tableName, { id: previousId });
516
+ return result;
517
+ } catch (error) {
518
+ if (replacementSaved) {
519
+ try {
520
+ await this.db.delete(this.tableName, { id: replacementId });
521
+ } catch {
522
+ }
523
+ }
524
+ this.id = previousId;
525
+ throw error;
526
+ }
527
+ }
528
+ async delete() {
529
+ const key = this.key;
530
+ const tenantId = this.tenantId;
531
+ await super.delete();
532
+ invalidatePromptCache(key, tenantId, this.db);
533
+ }
534
+ async validatePromptOverride() {
535
+ if (!this.key || this.key.trim() === "") {
536
+ throw new Error("PromptOverride.key is required");
537
+ }
538
+ const definition = PromptRegistry.get(this.key);
539
+ if (!definition) {
540
+ throw new Error(`Unknown prompt key "${this.key}"`);
541
+ }
542
+ const config = getPromptConfig$1();
543
+ const editable = definition.editable;
544
+ if (this.template !== null && !editable.template) {
545
+ throw new Error(`Prompt "${this.key}" does not allow template overrides`);
546
+ }
547
+ if (this.profile !== null && !editable.profile) {
548
+ throw new Error(`Prompt "${this.key}" does not allow profile overrides`);
549
+ }
550
+ if (this.model !== null && !editable.model) {
551
+ throw new Error(`Prompt "${this.key}" does not allow model overrides`);
552
+ }
553
+ if (this.params !== null && !editable.params) {
554
+ throw new Error(`Prompt "${this.key}" does not allow params overrides`);
555
+ }
556
+ if (this.params !== null) {
557
+ try {
558
+ const parsed = JSON.parse(this.params);
559
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
560
+ throw new Error("Prompt params must be a JSON object");
561
+ }
562
+ } catch (error) {
563
+ throw new Error(
564
+ `Prompt "${this.key}" has invalid params JSON: ${error instanceof Error ? error.message : String(error)}`
565
+ );
566
+ }
567
+ }
568
+ if (this.profile !== null) {
569
+ validateProfileName(this.profile, config);
570
+ }
571
+ if (this.model !== null) {
572
+ validateModelName(this.model, config);
573
+ }
574
+ const lowerLayers = await this.getLowerPrecedenceLayers();
575
+ const currentLayer = this.toPromptLayer();
576
+ const merged = mergePromptLayers(...lowerLayers, currentLayer);
577
+ if (currentLayer.model && !merged.ai.profile) {
578
+ throw new Error(
579
+ `Prompt "${this.key}" model overrides require an effective prompt profile`
580
+ );
581
+ }
582
+ }
583
+ async getLowerPrecedenceLayers() {
584
+ const definition = PromptRegistry.get(this.key);
585
+ const config = getPromptConfig$1();
586
+ const layers = [];
587
+ if (definition) {
588
+ layers.push({
589
+ template: definition.template,
590
+ profile: definition.ai.profile ?? null,
591
+ model: definition.ai.model ?? null,
592
+ params: definition.ai.params
593
+ });
594
+ }
595
+ layers.push(normalizePromptLayer(config.prompts?.[this.key]));
596
+ const { PromptOverrideCollection: PromptOverrideCollection2 } = await Promise.resolve().then(() => PromptOverrideCollection$1);
597
+ const collection = await PromptOverrideCollection2.create({
598
+ db: this.options.db ?? this.options.persistence
599
+ });
600
+ if (this.tenantId) {
601
+ const appOverride = await collection.getAppOverride(this.key, {
602
+ excludeId: this.id
603
+ });
604
+ if (appOverride) {
605
+ layers.push(appOverride.toPromptLayer());
606
+ }
607
+ }
608
+ return layers;
609
+ }
610
+ normalizeParamsForPersistence() {
611
+ const rawParams = this.params;
612
+ if (rawParams === null || rawParams === void 0) {
613
+ this.params = null;
614
+ return;
615
+ }
616
+ if (typeof rawParams === "string") {
617
+ this.params = rawParams;
618
+ return;
619
+ }
620
+ if (typeof rawParams === "object" && !Array.isArray(rawParams)) {
621
+ this.params = serializePromptParams(rawParams);
622
+ return;
623
+ }
624
+ throw new Error(
625
+ `Prompt "${this.key}" params must be a JSON object or JSON string`
626
+ );
627
+ }
628
+ async getPersistedIdentity() {
629
+ if (!this.id) {
630
+ return null;
631
+ }
632
+ const existing = await this.db.get(this.tableName, { id: this.id });
633
+ if (!existing) {
634
+ return null;
635
+ }
636
+ return {
637
+ key: String(existing.key ?? this.key),
638
+ tenantId: existing.tenantId !== void 0 ? existing.tenantId : existing.tenant_id ?? null
639
+ };
640
+ }
641
+ };
642
+ __decorateClass([
643
+ field({ required: true })
644
+ ], PromptOverride.prototype, "key", 2);
645
+ __decorateClass([
646
+ field({ type: "text", nullable: true })
647
+ ], PromptOverride.prototype, "tenantId", 2);
648
+ __decorateClass([
649
+ field({ type: "text", nullable: true })
650
+ ], PromptOverride.prototype, "template", 2);
651
+ __decorateClass([
652
+ field({ type: "text", nullable: true })
653
+ ], PromptOverride.prototype, "profile", 2);
654
+ __decorateClass([
655
+ field({ type: "text", nullable: true })
656
+ ], PromptOverride.prototype, "model", 2);
657
+ __decorateClass([
658
+ field({ type: "text", nullable: true })
659
+ ], PromptOverride.prototype, "params", 2);
660
+ PromptOverride = __decorateClass([
661
+ smrt({
662
+ tableName: "_smrt_prompt_overrides",
663
+ conflictColumns: ["key", "context"],
664
+ api: { include: ["list", "get", "create", "update", "delete"] },
665
+ cli: {
666
+ include: ["list", "get", "create", "update", "delete"],
667
+ exclude: ["getParams", "setParams", "toPromptLayer"]
668
+ },
669
+ mcp: { include: [] }
670
+ })
671
+ ], PromptOverride);
672
+ class PromptOverrideCollection extends SmrtCollection {
673
+ static _itemClass = PromptOverride;
674
+ excludeOverrideId(items, excludeId) {
675
+ return items.filter((item) => excludeId ? item.id !== excludeId : true);
676
+ }
677
+ async getAppOverride(key, options = {}) {
678
+ const items = await this.list({ where: { key, tenantId: null } });
679
+ return this.excludeOverrideId(items, options.excludeId)[0] ?? null;
680
+ }
681
+ async getTenantOverride(key, tenantId, options = {}) {
682
+ const items = await this.list({ where: { key, tenantId } });
683
+ return this.excludeOverrideId(items, options.excludeId)[0] ?? null;
684
+ }
685
+ async getResolutionLayers(key, tenantId, options = {}) {
686
+ const [app, tenant] = await Promise.all([
687
+ this.getAppOverride(key, options),
688
+ tenantId != null ? this.getTenantOverride(key, tenantId, options) : Promise.resolve(null)
689
+ ]);
690
+ return {
691
+ app,
692
+ tenant
693
+ };
694
+ }
695
+ }
696
+ const PromptOverrideCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
697
+ __proto__: null,
698
+ PromptOverrideCollection
699
+ }, Symbol.toStringTag, { value: "Module" }));
700
+ function getPromptConfig() {
701
+ return getPackageConfig("prompts", {
702
+ profiles: {},
703
+ prompts: {}
704
+ });
705
+ }
706
+ async function loadPromptBase(key, options) {
707
+ const definition = PromptRegistry.get(key);
708
+ if (!definition) {
709
+ throw new Error(`Unknown prompt key "${key}"`);
710
+ }
711
+ const tenantId = options.tenantId !== void 0 ? options.tenantId : getTenantId() ?? null;
712
+ let collection = null;
713
+ let cacheDb = options.db;
714
+ if (options.db) {
715
+ const initializedCollection = await PromptOverrideCollection.create({
716
+ db: options.db
717
+ });
718
+ collection = initializedCollection;
719
+ cacheDb = initializedCollection.db;
720
+ }
721
+ const cached = getCachedPromptBase(key, tenantId, cacheDb);
722
+ if (cached) {
723
+ return cached;
724
+ }
725
+ const config = getPromptConfig();
726
+ const layers = [
727
+ {
728
+ template: definition.template,
729
+ profile: definition.ai.profile ?? null,
730
+ model: definition.ai.model ?? null,
731
+ params: definition.ai.params
732
+ },
733
+ normalizePromptLayer(config.prompts?.[key])
734
+ ];
735
+ if (collection) {
736
+ const stored = await collection.getResolutionLayers(key, tenantId);
737
+ if (stored.app) {
738
+ layers.push(stored.app.toPromptLayer());
739
+ }
740
+ if (stored.tenant) {
741
+ layers.push(stored.tenant.toPromptLayer());
742
+ }
743
+ }
744
+ const merged = mergePromptLayers(...layers);
745
+ const value = {
746
+ key,
747
+ template: merged.template,
748
+ ai: merged.ai
749
+ };
750
+ setCachedPromptBase(key, tenantId, cacheDb, value);
751
+ return value;
752
+ }
753
+ async function resolvePrompt(key, options = {}) {
754
+ const config = getPromptConfig();
755
+ const base = await loadPromptBase(key, options);
756
+ const runtimeOverride = normalizePromptLayer(options.override);
757
+ const merged = mergePromptLayers(
758
+ {
759
+ template: base.template,
760
+ profile: base.ai.profile ?? null,
761
+ model: base.ai.model ?? null,
762
+ params: base.ai.params
763
+ },
764
+ runtimeOverride
765
+ );
766
+ return {
767
+ key,
768
+ template: merged.template,
769
+ text: renderPromptTemplate(merged.template, options.variables),
770
+ ai: buildResolvedAI(merged.ai, config)
771
+ };
772
+ }
773
+ const PACKAGE_VERSION_INITIALIZED = true;
774
+ export {
775
+ PACKAGE_VERSION_INITIALIZED,
776
+ PromptOverride,
777
+ PromptOverrideCollection,
778
+ PromptRegistry,
779
+ clearPromptCache,
780
+ definePrompt,
781
+ getPromptCacheTtlMs,
782
+ resolvePrompt
783
+ };
784
+ //# sourceMappingURL=index.js.map