@mate-academy/prompt-client 2.0.0 → 2.1.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/README.md +27 -6
- package/dist/LLMPromptClient.errors.d.ts +4 -2
- package/dist/LLMPromptClient.errors.js +5 -3
- package/dist/LLMPromptClient.typedefs.d.ts +10 -3
- package/dist/providers/InMemory/InMemory.typedefs.d.ts +2 -0
- package/dist/providers/InMemory/InMemoryPrompt.client.d.ts +2 -0
- package/dist/providers/InMemory/InMemoryPrompt.client.js +11 -3
- package/dist/providers/Langfuse/Langfuse.constants.d.ts +1 -0
- package/dist/providers/Langfuse/Langfuse.constants.js +4 -0
- package/dist/providers/Langfuse/Langfuse.factory.js +2 -2
- package/dist/providers/Langfuse/LangfusePrompt.client.d.ts +2 -0
- package/dist/providers/Langfuse/LangfusePrompt.client.js +24 -7
- package/dist/providers/Langfuse/LangfusePrompt.d.ts +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -102,11 +102,20 @@ Error model (only when NO `fallback` is provided):
|
|
|
102
102
|
existence probe).
|
|
103
103
|
- `LLMPromptFetchError` — infrastructure failure (network/HTTP) or a
|
|
104
104
|
wrong-shape prompt (a chat prompt fetched via `getPrompt`, or vice versa).
|
|
105
|
-
Both extend `LLMPromptError` and carry `promptName`
|
|
105
|
+
Both extend `LLMPromptError` and carry `promptName`. `cause` is set when an
|
|
106
|
+
underlying provider error is available.
|
|
106
107
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
`LLMPromptListError` (thrown only by `listPrompts`, see [Catalog](#catalog-for-codegen))
|
|
109
|
+
sits outside this hierarchy on purpose: it is **selection-scoped**, not
|
|
110
|
+
prompt-scoped, so it extends `Error` directly and carries the requested
|
|
111
|
+
`label` + `tag` plus `cause` instead of `promptName`. A broad `catch (error) {
|
|
112
|
+
if (error instanceof LLMPromptError) ... }` will **not** catch it — handle it
|
|
113
|
+
explicitly, or match on `error.name === 'LLMPromptListError'`.
|
|
114
|
+
|
|
115
|
+
With `fallback` set, `getPrompt` never rejects: it resolves to the fallback text
|
|
116
|
+
with `version: 0` and `isFallback: true`, and logs the fallback resolution. If
|
|
117
|
+
the SDK throws instead of returning its fallback, the client also logs the
|
|
118
|
+
underlying fetch error before building the same fallback locally.
|
|
110
119
|
|
|
111
120
|
### Chat prompts
|
|
112
121
|
|
|
@@ -128,8 +137,9 @@ prompt.compile({ leadName: 'Maria' }); // substitutes {{var}} in each message's
|
|
|
128
137
|
Fallback is an `LLMPromptMessage[]` instead of a string, with the same
|
|
129
138
|
never-throw / `version: 0` / `isFallback: true` semantics as `getPrompt`.
|
|
130
139
|
Langfuse placeholder entries and messages with an unrecognized role are
|
|
131
|
-
|
|
132
|
-
gap is visible without breaking the call
|
|
140
|
+
filtered when the prompt is fetched. A warning is logged once at fetch time so
|
|
141
|
+
the gap is visible without breaking the call; `messages` and `compile()` then
|
|
142
|
+
use the filtered result.
|
|
133
143
|
|
|
134
144
|
### Catalog (for codegen)
|
|
135
145
|
|
|
@@ -139,6 +149,7 @@ Langfuse-prompt codegen (the gateway's `generateSnapshot`):
|
|
|
139
149
|
```typescript
|
|
140
150
|
const { promptNames, page, totalPages } = await promptClient.listPrompts({
|
|
141
151
|
label: 'production',
|
|
152
|
+
tag: 'catalog', // optional; omit to list every prompt carrying the label
|
|
142
153
|
page: 1, // default 1
|
|
143
154
|
pageSize: 100, // default 100
|
|
144
155
|
});
|
|
@@ -149,6 +160,12 @@ const record = await promptClient.getPromptRecord('chatAgent.instructions', {
|
|
|
149
160
|
// record.type === LLMPromptTypes.Text | LLMPromptTypes.Chat
|
|
150
161
|
```
|
|
151
162
|
|
|
163
|
+
`label` is required and `tag` narrows the same listing further — a prompt is
|
|
164
|
+
returned only when it carries **both**. The filter is applied by the provider,
|
|
165
|
+
never by widening the query and post-filtering names, so an unmatched tag
|
|
166
|
+
yields an empty `promptNames` page rather than the full label listing.
|
|
167
|
+
Pagination applies to the filtered set.
|
|
168
|
+
|
|
152
169
|
Both methods are uncached and never fall back — they **throw** on failure
|
|
153
170
|
(`LLMPromptListError` for `listPrompts`, `LLMPromptNotFoundError` /
|
|
154
171
|
`LLMPromptFetchError` for `getPromptRecord`) so a codegen run can fail loudly
|
|
@@ -260,6 +277,10 @@ never applies to `getPromptRecord` — a missing seed always throws
|
|
|
260
277
|
`LLMPromptNotFoundError`, since the catalog must report seeded truth for
|
|
261
278
|
codegen tests.
|
|
262
279
|
|
|
280
|
+
Seeds accept an optional `tags` array so `listPrompts({ label, tag })` filters
|
|
281
|
+
the same way it does against Langfuse. Tags have no default: a seed without
|
|
282
|
+
`tags` matches every tag-less listing and no tag-filtered one.
|
|
283
|
+
|
|
263
284
|
## Provider notes (Langfuse)
|
|
264
285
|
|
|
265
286
|
- `label` and `version` are mutually exclusive; when `version` is set the
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type LLMPromptListSelection } from './LLMPromptClient.typedefs';
|
|
1
2
|
export declare class LLMPromptError extends Error {
|
|
2
3
|
readonly promptName: string;
|
|
3
4
|
readonly cause?: unknown | undefined;
|
|
@@ -11,6 +12,7 @@ export declare class LLMPromptFetchError extends LLMPromptError {
|
|
|
11
12
|
}
|
|
12
13
|
export declare class LLMPromptListError extends Error {
|
|
13
14
|
readonly label: string;
|
|
14
|
-
readonly
|
|
15
|
-
|
|
15
|
+
readonly tag?: string;
|
|
16
|
+
readonly cause?: unknown;
|
|
17
|
+
constructor(selection: LLMPromptListSelection, cause?: unknown);
|
|
16
18
|
}
|
|
@@ -25,9 +25,11 @@ class LLMPromptFetchError extends LLMPromptError {
|
|
|
25
25
|
}
|
|
26
26
|
exports.LLMPromptFetchError = LLMPromptFetchError;
|
|
27
27
|
class LLMPromptListError extends Error {
|
|
28
|
-
constructor(
|
|
29
|
-
super(`Failed to list LLM prompts for label: ${label}`
|
|
30
|
-
|
|
28
|
+
constructor(selection, cause) {
|
|
29
|
+
super(`Failed to list LLM prompts for label: ${selection.label}`
|
|
30
|
+
+ (selection.tag === undefined ? '' : `, tag: ${selection.tag}`));
|
|
31
|
+
this.label = selection.label;
|
|
32
|
+
this.tag = selection.tag;
|
|
31
33
|
this.cause = cause;
|
|
32
34
|
this.name = 'LLMPromptListError';
|
|
33
35
|
}
|
|
@@ -14,7 +14,7 @@ export interface LLMPromptMessage {
|
|
|
14
14
|
export interface LLMPrompt {
|
|
15
15
|
name: string;
|
|
16
16
|
version: number;
|
|
17
|
-
type: LLMPromptTypes;
|
|
17
|
+
type: LLMPromptTypes.Text;
|
|
18
18
|
prompt: string;
|
|
19
19
|
config: unknown;
|
|
20
20
|
isFallback: boolean;
|
|
@@ -28,7 +28,7 @@ export interface LLMPrompt {
|
|
|
28
28
|
export interface LLMChatPrompt {
|
|
29
29
|
name: string;
|
|
30
30
|
version: number;
|
|
31
|
-
type: LLMPromptTypes;
|
|
31
|
+
type: LLMPromptTypes.Chat;
|
|
32
32
|
messages: LLMPromptMessage[];
|
|
33
33
|
config: unknown;
|
|
34
34
|
isFallback: boolean;
|
|
@@ -60,8 +60,15 @@ interface GetChatPromptByLabelOptions extends GetChatPromptCommonOptions {
|
|
|
60
60
|
label?: string;
|
|
61
61
|
}
|
|
62
62
|
export type GetChatPromptOptions = GetChatPromptByVersionOptions | GetChatPromptByLabelOptions;
|
|
63
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Which prompts a catalog listing selects: a required label, narrowed further
|
|
65
|
+
* by an optional tag.
|
|
66
|
+
*/
|
|
67
|
+
export interface LLMPromptListSelection {
|
|
64
68
|
label: string;
|
|
69
|
+
tag?: string;
|
|
70
|
+
}
|
|
71
|
+
export interface ListPromptsOptions extends LLMPromptListSelection {
|
|
65
72
|
page?: number;
|
|
66
73
|
pageSize?: number;
|
|
67
74
|
}
|
|
@@ -8,12 +8,14 @@ export interface InMemorySeedPrompt {
|
|
|
8
8
|
version?: number;
|
|
9
9
|
config?: unknown;
|
|
10
10
|
labels?: string[];
|
|
11
|
+
tags?: string[];
|
|
11
12
|
}
|
|
12
13
|
export interface InMemorySeedChatPrompt {
|
|
13
14
|
messages: LLMPromptMessage[];
|
|
14
15
|
version?: number;
|
|
15
16
|
config?: unknown;
|
|
16
17
|
labels?: string[];
|
|
18
|
+
tags?: string[];
|
|
17
19
|
}
|
|
18
20
|
export interface InMemoryProviderOptions {
|
|
19
21
|
prompts?: Record<string, InMemorySeedPrompt>;
|
|
@@ -19,7 +19,9 @@ export declare class InMemoryPromptClient implements LLMPromptClient {
|
|
|
19
19
|
private buildFallbackChatPrompt;
|
|
20
20
|
private compileMessages;
|
|
21
21
|
private collectMatchingNames;
|
|
22
|
+
private matchesSelection;
|
|
22
23
|
private matchesLabel;
|
|
24
|
+
private matchesTag;
|
|
23
25
|
private buildTextPromptRecord;
|
|
24
26
|
private buildChatPromptRecord;
|
|
25
27
|
}
|
|
@@ -47,7 +47,7 @@ class InMemoryPromptClient {
|
|
|
47
47
|
throw new LLMPromptClient_errors_1.LLMPromptNotFoundError(name);
|
|
48
48
|
}
|
|
49
49
|
async listPrompts(options) {
|
|
50
|
-
const matchingNames = this.collectMatchingNames(options
|
|
50
|
+
const matchingNames = this.collectMatchingNames(options);
|
|
51
51
|
const page = options.page ?? LLMPromptClient_constants_1.DEFAULT_PROMPT_LIST_PAGE;
|
|
52
52
|
const pageSize = options.pageSize ?? LLMPromptClient_constants_1.DEFAULT_PROMPT_LIST_PAGE_SIZE;
|
|
53
53
|
const startIndex = (page - 1) * pageSize;
|
|
@@ -131,12 +131,12 @@ class InMemoryPromptClient {
|
|
|
131
131
|
content: (0, template_1.compileTemplateVariables)(message.content, variables),
|
|
132
132
|
}));
|
|
133
133
|
}
|
|
134
|
-
collectMatchingNames(
|
|
134
|
+
collectMatchingNames(selection) {
|
|
135
135
|
const matchingNames = [];
|
|
136
136
|
const seenNames = new Set();
|
|
137
137
|
const collect = (entries) => {
|
|
138
138
|
entries.forEach(([name, seedPrompt]) => {
|
|
139
|
-
if (seenNames.has(name) || !this.
|
|
139
|
+
if (seenNames.has(name) || !this.matchesSelection(seedPrompt, selection)) {
|
|
140
140
|
return;
|
|
141
141
|
}
|
|
142
142
|
matchingNames.push(name);
|
|
@@ -147,9 +147,17 @@ class InMemoryPromptClient {
|
|
|
147
147
|
collect([...this.chatPrompts.entries()]);
|
|
148
148
|
return matchingNames;
|
|
149
149
|
}
|
|
150
|
+
matchesSelection(seedPrompt, selection) {
|
|
151
|
+
return this.matchesLabel(seedPrompt.labels, selection.label)
|
|
152
|
+
&& this.matchesTag(seedPrompt.tags, selection.tag);
|
|
153
|
+
}
|
|
150
154
|
matchesLabel(labels, label) {
|
|
151
155
|
return (labels ?? [LLMPromptClient_constants_1.DEFAULT_PROMPT_LABEL]).includes(label);
|
|
152
156
|
}
|
|
157
|
+
matchesTag(tags, tag) {
|
|
158
|
+
const requestedTag = tag ?? undefined;
|
|
159
|
+
return requestedTag === undefined || (tags ?? []).includes(requestedTag);
|
|
160
|
+
}
|
|
153
161
|
buildTextPromptRecord(name, seedPrompt) {
|
|
154
162
|
return {
|
|
155
163
|
name,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const MILLISECONDS_PER_SECOND = 1000;
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createLangfusePromptManagement = void 0;
|
|
4
4
|
const client_1 = require("@langfuse/client");
|
|
5
|
+
const Langfuse_constants_1 = require("../../providers/Langfuse/Langfuse.constants");
|
|
5
6
|
const LangfusePrompt_client_1 = require("../../providers/Langfuse/LangfusePrompt.client");
|
|
6
|
-
const MILLISECONDS_PER_SECOND = 1000;
|
|
7
7
|
const createLangfusePromptManagement = (options, logger) => {
|
|
8
8
|
const client = new client_1.LangfuseClient({
|
|
9
9
|
publicKey: options.credentials.publicKey,
|
|
10
10
|
secretKey: options.credentials.secretKey,
|
|
11
11
|
baseUrl: options.credentials.baseUrl,
|
|
12
12
|
...(options.fetchTimeoutMs !== undefined
|
|
13
|
-
? { timeout: Math.ceil(options.fetchTimeoutMs / MILLISECONDS_PER_SECOND) }
|
|
13
|
+
? { timeout: Math.ceil(options.fetchTimeoutMs / Langfuse_constants_1.MILLISECONDS_PER_SECOND) }
|
|
14
14
|
: {}),
|
|
15
15
|
});
|
|
16
16
|
return {
|
|
@@ -14,6 +14,7 @@ export declare class LangfusePromptClient implements LLMPromptClient {
|
|
|
14
14
|
shutdown(): Promise<void>;
|
|
15
15
|
private fetchTextPrompt;
|
|
16
16
|
private fetchChatPrompt;
|
|
17
|
+
private reportFallbackFetchError;
|
|
17
18
|
private resolveSelectionOptions;
|
|
18
19
|
private resolveCacheTtlSeconds;
|
|
19
20
|
private buildTextFallbackPrompt;
|
|
@@ -24,6 +25,7 @@ export declare class LangfusePromptClient implements LLMPromptClient {
|
|
|
24
25
|
private ensureChatPrompt;
|
|
25
26
|
private reportPromptResolution;
|
|
26
27
|
private reportSkippedChatEntries;
|
|
28
|
+
private resolveListSelection;
|
|
27
29
|
private fetchRawPrompt;
|
|
28
30
|
private buildRequestOptions;
|
|
29
31
|
}
|
|
@@ -7,9 +7,10 @@ const LLMPromptClient_constants_1 = require("../../LLMPromptClient.constants");
|
|
|
7
7
|
const LLMPromptClient_errors_1 = require("../../LLMPromptClient.errors");
|
|
8
8
|
const Langfuse_helpers_1 = require("../../providers/Langfuse/Langfuse.helpers");
|
|
9
9
|
const LangfusePrompt_1 = require("../../providers/Langfuse/LangfusePrompt");
|
|
10
|
+
const Langfuse_constants_1 = require("../../providers/Langfuse/Langfuse.constants");
|
|
10
11
|
const LANGFUSE_TEXT_PROMPT_TYPE = 'text';
|
|
11
12
|
const LANGFUSE_CHAT_PROMPT_TYPE = 'chat';
|
|
12
|
-
const
|
|
13
|
+
const FALLBACK_PROMPT_VERSION = 0;
|
|
13
14
|
class LangfusePromptClient {
|
|
14
15
|
constructor(client, providerOptions, logger) {
|
|
15
16
|
this.client = client;
|
|
@@ -30,9 +31,10 @@ class LangfusePromptClient {
|
|
|
30
31
|
return new LangfusePrompt_1.LangfuseChatPrompt(nativePrompt);
|
|
31
32
|
}
|
|
32
33
|
async listPrompts(options) {
|
|
34
|
+
const selection = this.resolveListSelection(options);
|
|
33
35
|
try {
|
|
34
36
|
const promptListPage = await this.client.api.prompts.list({
|
|
35
|
-
|
|
37
|
+
...selection,
|
|
36
38
|
page: options.page ?? LLMPromptClient_constants_1.DEFAULT_PROMPT_LIST_PAGE,
|
|
37
39
|
limit: options.pageSize ?? LLMPromptClient_constants_1.DEFAULT_PROMPT_LIST_PAGE_SIZE,
|
|
38
40
|
}, this.buildRequestOptions());
|
|
@@ -45,9 +47,9 @@ class LangfusePromptClient {
|
|
|
45
47
|
catch (error) {
|
|
46
48
|
this.logger.error('Failed to list Langfuse prompts', {
|
|
47
49
|
error,
|
|
48
|
-
|
|
50
|
+
...selection,
|
|
49
51
|
});
|
|
50
|
-
throw new LLMPromptClient_errors_1.LLMPromptListError(
|
|
52
|
+
throw new LLMPromptClient_errors_1.LLMPromptListError(selection, error);
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
async getPromptRecord(name, options) {
|
|
@@ -87,6 +89,7 @@ class LangfusePromptClient {
|
|
|
87
89
|
}
|
|
88
90
|
catch (error) {
|
|
89
91
|
if (options?.fallback !== undefined) {
|
|
92
|
+
this.reportFallbackFetchError(name, error);
|
|
90
93
|
return this.buildTextFallbackPrompt(name, options.fallback, options);
|
|
91
94
|
}
|
|
92
95
|
throw this.classifyFetchError(name, error);
|
|
@@ -105,11 +108,18 @@ class LangfusePromptClient {
|
|
|
105
108
|
}
|
|
106
109
|
catch (error) {
|
|
107
110
|
if (options?.fallback !== undefined) {
|
|
111
|
+
this.reportFallbackFetchError(name, error);
|
|
108
112
|
return this.buildChatFallbackPrompt(name, options.fallback, options);
|
|
109
113
|
}
|
|
110
114
|
throw this.classifyFetchError(name, error);
|
|
111
115
|
}
|
|
112
116
|
}
|
|
117
|
+
reportFallbackFetchError(name, error) {
|
|
118
|
+
this.logger.error('Failed to fetch Langfuse prompt; using fallback', {
|
|
119
|
+
error,
|
|
120
|
+
promptName: name,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
113
123
|
resolveSelectionOptions(options) {
|
|
114
124
|
if (options?.version !== undefined) {
|
|
115
125
|
return { version: options.version };
|
|
@@ -128,7 +138,7 @@ class LangfusePromptClient {
|
|
|
128
138
|
buildTextFallbackPrompt(name, fallback, options) {
|
|
129
139
|
return new client_1.TextPromptClient({
|
|
130
140
|
name,
|
|
131
|
-
version:
|
|
141
|
+
version: FALLBACK_PROMPT_VERSION,
|
|
132
142
|
labels: this.resolveFallbackLabels(options),
|
|
133
143
|
tags: [],
|
|
134
144
|
config: {},
|
|
@@ -143,7 +153,7 @@ class LangfusePromptClient {
|
|
|
143
153
|
buildChatFallbackPrompt(name, fallback, options) {
|
|
144
154
|
return new client_1.ChatPromptClient({
|
|
145
155
|
name,
|
|
146
|
-
version:
|
|
156
|
+
version: FALLBACK_PROMPT_VERSION,
|
|
147
157
|
labels: this.resolveFallbackLabels(options),
|
|
148
158
|
tags: [],
|
|
149
159
|
config: {},
|
|
@@ -208,6 +218,13 @@ class LangfusePromptClient {
|
|
|
208
218
|
});
|
|
209
219
|
}
|
|
210
220
|
}
|
|
221
|
+
resolveListSelection(options) {
|
|
222
|
+
const requestedTag = options.tag ?? undefined;
|
|
223
|
+
return {
|
|
224
|
+
label: options.label,
|
|
225
|
+
...(requestedTag === undefined ? {} : { tag: requestedTag }),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
211
228
|
async fetchRawPrompt(name, options) {
|
|
212
229
|
try {
|
|
213
230
|
return await this.client.api.prompts.get(name, { label: options.label }, this.buildRequestOptions());
|
|
@@ -223,7 +240,7 @@ class LangfusePromptClient {
|
|
|
223
240
|
: {}),
|
|
224
241
|
...(this.providerOptions.fetchTimeoutMs !== undefined
|
|
225
242
|
? {
|
|
226
|
-
timeoutInSeconds: Math.ceil(this.providerOptions.fetchTimeoutMs / MILLISECONDS_PER_SECOND),
|
|
243
|
+
timeoutInSeconds: Math.ceil(this.providerOptions.fetchTimeoutMs / Langfuse_constants_1.MILLISECONDS_PER_SECOND),
|
|
227
244
|
}
|
|
228
245
|
: {}),
|
|
229
246
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ChatPromptClient, type TextPromptClient } from '@langfuse/client';
|
|
2
2
|
import { LLMPromptTypes, type LLMChatPrompt, type LLMPrompt, type LLMPromptMessage } from '../../LLMPromptClient.typedefs';
|
|
3
3
|
export declare class LangfusePrompt implements LLMPrompt {
|
|
4
|
-
readonly nativePrompt
|
|
4
|
+
private readonly nativePrompt;
|
|
5
5
|
readonly type = LLMPromptTypes.Text;
|
|
6
6
|
constructor(nativePrompt: TextPromptClient);
|
|
7
7
|
get name(): string;
|
|
@@ -12,7 +12,7 @@ export declare class LangfusePrompt implements LLMPrompt {
|
|
|
12
12
|
compile(variables?: Record<string, string>): string;
|
|
13
13
|
}
|
|
14
14
|
export declare class LangfuseChatPrompt implements LLMChatPrompt {
|
|
15
|
-
readonly nativePrompt
|
|
15
|
+
private readonly nativePrompt;
|
|
16
16
|
readonly type = LLMPromptTypes.Chat;
|
|
17
17
|
constructor(nativePrompt: ChatPromptClient);
|
|
18
18
|
get name(): string;
|