@posthog/ai 8.10.4 → 8.11.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.d.ts CHANGED
@@ -151,6 +151,9 @@ type PromptsOptions = PromptsWithPostHogOptions | PromptsDirectOptions;
151
151
  * label: 'production',
152
152
  * })
153
153
  *
154
+ * // Or fetch all prompts at a label in one request and warm the cache
155
+ * const prodPrompts = await prompts.getAll({ label: 'production' })
156
+ *
154
157
  * // Compile with variables
155
158
  * const systemPrompt = prompts.compile(result.prompt, {
156
159
  * company: 'Acme Corp',
@@ -177,6 +180,25 @@ declare class Prompts {
177
180
  * configuration stored with the version.
178
181
  */
179
182
  get(name: string, options?: GetPromptOptions): Promise<PromptResult>;
183
+ /**
184
+ * Fetch every prompt that carries a label, in one batch.
185
+ *
186
+ * Returns an object mapping prompt name to `PromptRemoteResult`, with each
187
+ * prompt at the version the label points to. Prompts without the label are
188
+ * not included.
189
+ *
190
+ * Each fetched prompt is stored in the cache, so later
191
+ * `get(name, { label })` calls are served from cache within the TTL. An app
192
+ * with many prompts can call this once per cache cycle instead of making one
193
+ * `get()` request per prompt.
194
+ *
195
+ * Throws if the request fails, or if the server does not support fetching
196
+ * prompts by label on the list endpoint (PostHog releases from before
197
+ * September 2026).
198
+ */
199
+ getAll(options: {
200
+ label: string;
201
+ }): Promise<Record<string, PromptRemoteResult>>;
180
202
  /**
181
203
  * Internal method that handles cache + fetch logic, returning full metadata.
182
204
  * Does NOT handle the string `fallback` option — callers handle that.
@@ -201,6 +223,12 @@ declare class Prompts {
201
223
  * @param version - Optional prompt version to clear. Requires a prompt name.
202
224
  */
203
225
  clearCache(name?: string, version?: number): void;
226
+ private requireCredentials;
227
+ /**
228
+ * Fetch all prompts at a label from the paginated list endpoint.
229
+ * Follows pagination links until the last page and returns the raw rows.
230
+ */
231
+ private fetchPromptListFromApi;
204
232
  private fetchPromptFromApi;
205
233
  }
206
234
  //#endregion
package/dist/index.mjs CHANGED
@@ -391,7 +391,7 @@ function sanitizeValues(obj) {
391
391
  }
392
392
  //#endregion
393
393
  //#region package.json
394
- var version = "8.10.4";
394
+ var version = "8.11.0";
395
395
  //#endregion
396
396
  //#region src/serializeError.ts
397
397
  const DEFAULT_MAX_DEPTH = 3;
@@ -1083,6 +1083,7 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
1083
1083
  //#region src/prompts.ts
1084
1084
  const DEFAULT_CACHE_TTL_SECONDS = 300;
1085
1085
  const DEFAULT_PROMPTS_HOST = "https://us.posthog.com";
1086
+ const MAX_PROMPT_LIST_PAGES = 100;
1086
1087
  const DEFAULT_REFETCH_COOLDOWN_SECONDS = 60;
1087
1088
  const MAX_REFETCH_COOLDOWN_SECONDS = 3600;
1088
1089
  function normalizeApiKey(value) {
@@ -1120,6 +1121,31 @@ function isPromptApiResponse(data) {
1120
1121
  const record = data;
1121
1122
  return typeof record.prompt === "string" && typeof record.name === "string" && typeof record.version === "number" && (record.label === void 0 || typeof record.label === "string");
1122
1123
  }
1124
+ function isSameOrigin(url, host) {
1125
+ try {
1126
+ return new URL(url).origin === new URL(host).origin;
1127
+ } catch {
1128
+ return false;
1129
+ }
1130
+ }
1131
+ /**
1132
+ * Classify how a list row relates to the requested label, via its all_labels
1133
+ * field.
1134
+ *
1135
+ * 'resolved': the row is the version the label points to.
1136
+ * 'moved': the prompt carries the label, but on another version. Happens when
1137
+ * the label moves between the query and the response.
1138
+ * 'absent': the prompt does not carry the label at any version. A server that
1139
+ * filters by label never returns such a row, so this means the server ignored
1140
+ * the label param (an older PostHog release) and served latest versions.
1141
+ */
1142
+ function rowLabelState(row, label) {
1143
+ const allLabels = row.all_labels;
1144
+ if (!Array.isArray(allLabels)) return "absent";
1145
+ const entry = allLabels.find((candidate) => typeof candidate === "object" && candidate !== null && candidate.name === label);
1146
+ if (entry === void 0) return "absent";
1147
+ return entry.version === row.version ? "resolved" : "moved";
1148
+ }
1123
1149
  function isPromptsWithPostHog(options) {
1124
1150
  return "posthog" in options;
1125
1151
  }
@@ -1154,6 +1180,9 @@ function isPromptsWithPostHog(options) {
1154
1180
  * label: 'production',
1155
1181
  * })
1156
1182
  *
1183
+ * // Or fetch all prompts at a label in one request and warm the cache
1184
+ * const prodPrompts = await prompts.getAll({ label: 'production' })
1185
+ *
1157
1186
  * // Compile with variables
1158
1187
  * const systemPrompt = prompts.compile(result.prompt, {
1159
1188
  * company: 'Acme Corp',
@@ -1220,6 +1249,61 @@ var Prompts = class {
1220
1249
  }
1221
1250
  }
1222
1251
  /**
1252
+ * Fetch every prompt that carries a label, in one batch.
1253
+ *
1254
+ * Returns an object mapping prompt name to `PromptRemoteResult`, with each
1255
+ * prompt at the version the label points to. Prompts without the label are
1256
+ * not included.
1257
+ *
1258
+ * Each fetched prompt is stored in the cache, so later
1259
+ * `get(name, { label })` calls are served from cache within the TTL. An app
1260
+ * with many prompts can call this once per cache cycle instead of making one
1261
+ * `get()` request per prompt.
1262
+ *
1263
+ * Throws if the request fails, or if the server does not support fetching
1264
+ * prompts by label on the list endpoint (PostHog releases from before
1265
+ * September 2026).
1266
+ */
1267
+ async getAll(options) {
1268
+ const label = options.label;
1269
+ const rows = await this.fetchPromptListFromApi(label);
1270
+ const resolvedRows = [];
1271
+ const skipped = [];
1272
+ for (const row of rows) {
1273
+ if (!isPromptApiResponse(row)) throw new Error(`[PostHog Prompts] Invalid response format for prompts with label "${label}"`);
1274
+ const labelState = rowLabelState(row, label);
1275
+ if (labelState === "absent") throw new Error(`[PostHog Prompts] The server returned a prompt that does not carry label "${label}". It may not support fetching prompts by label on the list endpoint yet. Upgrade PostHog, or fetch prompts one by one with get().`);
1276
+ if (labelState === "moved") {
1277
+ skipped.push(row.name);
1278
+ continue;
1279
+ }
1280
+ resolvedRows.push(row);
1281
+ }
1282
+ const now = Date.now();
1283
+ const results = /* @__PURE__ */ new Map();
1284
+ for (const row of resolvedRows) {
1285
+ const config = extractConfig(row.config);
1286
+ this.getOrCreatePromptCache(row.name).set(label, {
1287
+ prompt: row.prompt,
1288
+ name: row.name,
1289
+ version: row.version,
1290
+ label,
1291
+ config,
1292
+ fetchedAt: now
1293
+ });
1294
+ results.set(row.name, {
1295
+ source: "api",
1296
+ prompt: row.prompt,
1297
+ name: row.name,
1298
+ version: row.version,
1299
+ label,
1300
+ config: cloneConfig(config)
1301
+ });
1302
+ }
1303
+ if (skipped.length > 0) console.warn(`[PostHog Prompts] Skipped ${skipped.length} prompt(s) that did not resolve label "${label}": ${skipped.join(", ")}`);
1304
+ return Object.fromEntries(results);
1305
+ }
1306
+ /**
1223
1307
  * Internal method that handles cache + fetch logic, returning full metadata.
1224
1308
  * Does NOT handle the string `fallback` option — callers handle that.
1225
1309
  */
@@ -1309,9 +1393,43 @@ var Prompts = class {
1309
1393
  promptVersions?.delete(version);
1310
1394
  if (promptVersions?.size === 0) this.cache.delete(name);
1311
1395
  }
1312
- async fetchPromptFromApi(name, version, label) {
1396
+ requireCredentials() {
1313
1397
  if (!this.personalApiKey) throw new Error("[PostHog Prompts] personalApiKey is required to fetch prompts. Please provide it when initializing the Prompts instance.");
1314
1398
  if (!this.projectApiKey) throw new Error("[PostHog Prompts] projectApiKey is required to fetch prompts. Please provide it when initializing the Prompts instance.");
1399
+ }
1400
+ /**
1401
+ * Fetch all prompts at a label from the paginated list endpoint.
1402
+ * Follows pagination links until the last page and returns the raw rows.
1403
+ */
1404
+ async fetchPromptListFromApi(label) {
1405
+ this.requireCredentials();
1406
+ const query = `token=${encodeURIComponent(this.projectApiKey)}&label=${encodeURIComponent(label)}&content=full`;
1407
+ let url = `${this.host}/api/environments/@current/llm_prompts/?${query}`;
1408
+ const reference = `prompts with label "${label}"`;
1409
+ const rows = [];
1410
+ let pages = 0;
1411
+ while (url !== void 0) {
1412
+ if (pages >= MAX_PROMPT_LIST_PAGES) throw new Error(`[PostHog Prompts] ${reference} spans more than ${MAX_PROMPT_LIST_PAGES} pages. Refusing to return an incomplete result.`);
1413
+ const response = await fetch(url, {
1414
+ method: "GET",
1415
+ headers: { Authorization: `Bearer ${this.personalApiKey}` }
1416
+ });
1417
+ if (!response.ok) {
1418
+ if (response.status === 403) throw new Error(`[PostHog Prompts] Access denied for ${reference}. Check that your personalApiKey has the correct permissions and the LLM prompts feature is enabled.`);
1419
+ throw new PromptFetchError(`[PostHog Prompts] Failed to fetch ${reference}: HTTP ${response.status}`, response.status === 429 ? parseRetryAfterSeconds(response.headers?.get("Retry-After")) : void 0);
1420
+ }
1421
+ const data = await response.json();
1422
+ if (typeof data !== "object" || data === null || !Array.isArray(data.results)) throw new Error(`[PostHog Prompts] Invalid response format for ${reference}`);
1423
+ rows.push(...data.results);
1424
+ const nextUrl = data.next;
1425
+ if (nextUrl !== null && nextUrl !== void 0 && (typeof nextUrl !== "string" || !isSameOrigin(nextUrl, this.host))) throw new Error(`[PostHog Prompts] Refusing to follow a pagination link off the configured host while fetching ${reference}.`);
1426
+ url = nextUrl === null || nextUrl === void 0 ? void 0 : nextUrl;
1427
+ pages += 1;
1428
+ }
1429
+ return rows;
1430
+ }
1431
+ async fetchPromptFromApi(name, version, label) {
1432
+ this.requireCredentials();
1315
1433
  const encodedPromptName = encodeURIComponent(name);
1316
1434
  const encodedProjectApiKey = encodeURIComponent(this.projectApiKey);
1317
1435
  const versionQuery = version === void 0 ? "" : `&version=${encodeURIComponent(String(version))}`;