evalution 1.0.2 → 1.0.4
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/cli/index.js +430 -180
- package/dist/client/assets/{index-CORbBplP.js → index-C4XXh0na.js} +38 -38
- package/dist/client/assets/{index-CgcFVsRZ.css → index-CHPZsnKo.css} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/index.d.ts +86 -56
- package/dist/index.js +92 -126
- package/dist/{vercel-ai-sdk-CareWPDM.js → vercel-ai-sdk-B8ivuOzP.js} +129 -35
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as MemoryTraceProvider, c as
|
|
1
|
+
import { a as MemoryTraceProvider, c as PROMPT_NAME_ATTRIBUTE, d as createTracerForPrompt, f as getPromptSpanAttributes, l as PROMPT_PROVIDER_ID_ATTRIBUTE, n as findPackageDts, o as PROMPT_ID_ATTRIBUTE, p as isEditable, s as PROMPT_INPUTS_ATTRIBUTE, t as VercelAISDK, u as SPAN_KIND_ATTRIBUTE } from "./vercel-ai-sdk-B8ivuOzP.js";
|
|
2
2
|
import fs, { glob } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
@@ -8,13 +8,79 @@ import ts from "typescript";
|
|
|
8
8
|
import chokidar from "chokidar";
|
|
9
9
|
import { makeRe, minimatch } from "minimatch";
|
|
10
10
|
import { GoogleGenAI } from "@google/genai";
|
|
11
|
-
//#region src/file-provider.ts
|
|
11
|
+
//#region src/file-provider-local.ts
|
|
12
|
+
/**
|
|
13
|
+
* A {@link FileProvider} backed by the local file system.
|
|
14
|
+
*
|
|
15
|
+
* Uses `fs/promises` for I/O, `fs/promises.glob` (Node.js ≥ 22) for pattern
|
|
16
|
+
* matching, and [chokidar](https://github.com/paulmillr/chokidar) for file
|
|
17
|
+
* watching.
|
|
18
|
+
*
|
|
19
|
+
* This is the default implementation used by {@link FilePromptProvider} and
|
|
20
|
+
* {@link TSPromptFileType} when no custom provider is supplied.
|
|
21
|
+
*/
|
|
22
|
+
var LocalFileProvider = class {
|
|
23
|
+
async readFile(filePath) {
|
|
24
|
+
return fs.readFile(filePath, "utf-8");
|
|
25
|
+
}
|
|
26
|
+
async writeFile(filePath, content) {
|
|
27
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
28
|
+
await fs.writeFile(filePath, content, "utf-8");
|
|
29
|
+
}
|
|
30
|
+
async deleteFile(filePath) {
|
|
31
|
+
await fs.unlink(filePath);
|
|
32
|
+
}
|
|
33
|
+
async import(filePath) {
|
|
34
|
+
return import(pathToFileURL(filePath).href);
|
|
35
|
+
}
|
|
36
|
+
async *glob(pattern, options = {}) {
|
|
37
|
+
const { cwd, ignore = [], absolute = false } = options;
|
|
38
|
+
const baseCwd = cwd ?? process.cwd();
|
|
39
|
+
for await (const file of glob(pattern, { cwd: baseCwd })) {
|
|
40
|
+
const relativePath = file.replace(/\\/g, "/");
|
|
41
|
+
if (ignore.some((p) => minimatch(relativePath, p))) continue;
|
|
42
|
+
yield absolute ? path.resolve(baseCwd, file) : file;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
watch(patterns, options, callback) {
|
|
46
|
+
const cwd = options.cwd ?? process.cwd();
|
|
47
|
+
const ignored = options.ignored ?? [];
|
|
48
|
+
const includeMatchers = patterns.map((p) => makeRe(p)).filter((re) => re !== false);
|
|
49
|
+
const matches = (fp) => includeMatchers.some((re) => re.test(fp));
|
|
50
|
+
const watcher = chokidar.watch(".", {
|
|
51
|
+
cwd,
|
|
52
|
+
ignored: (absPath) => {
|
|
53
|
+
const rel = path.relative(cwd, absPath).replace(/\\/g, "/");
|
|
54
|
+
return ignored.some((p) => minimatch(rel, p, { dot: true }));
|
|
55
|
+
},
|
|
56
|
+
persistent: true,
|
|
57
|
+
ignoreInitial: options.ignoreInitial ?? true
|
|
58
|
+
});
|
|
59
|
+
watcher.on("change", (fp) => {
|
|
60
|
+
if (matches(fp)) callback("change", fp);
|
|
61
|
+
});
|
|
62
|
+
watcher.on("add", (fp) => {
|
|
63
|
+
if (matches(fp)) callback("add", fp);
|
|
64
|
+
});
|
|
65
|
+
watcher.on("unlink", (fp) => {
|
|
66
|
+
if (matches(fp)) callback("remove", fp);
|
|
67
|
+
});
|
|
68
|
+
return () => {
|
|
69
|
+
watcher.close();
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/file-provider-memory.ts
|
|
12
75
|
/**
|
|
13
76
|
* An in-memory {@link FileProvider} backed by a `Map<string, string>`.
|
|
14
77
|
*
|
|
15
|
-
* Intended for unit tests
|
|
16
|
-
*
|
|
17
|
-
*
|
|
78
|
+
* Intended for unit tests and non-local environments (e.g. a browser or
|
|
79
|
+
* service-worker bundle) — all file I/O stays in-process with no disk access.
|
|
80
|
+
* It depends only on `node:path` and `minimatch`, so it carries no Node-only
|
|
81
|
+
* runtime dependencies (no `node:fs`, `chokidar`, etc.). Calling
|
|
82
|
+
* {@link writeFile} triggers any active {@link watch} callbacks synchronously,
|
|
83
|
+
* making it easy to test reactive code paths.
|
|
18
84
|
*
|
|
19
85
|
* @example
|
|
20
86
|
* ```ts
|
|
@@ -85,67 +151,6 @@ var MemoryFileProvider = class {
|
|
|
85
151
|
}
|
|
86
152
|
}
|
|
87
153
|
};
|
|
88
|
-
/**
|
|
89
|
-
* A {@link FileProvider} backed by the local file system.
|
|
90
|
-
*
|
|
91
|
-
* Uses `fs/promises` for I/O, `fs/promises.glob` (Node.js ≥ 22) for pattern
|
|
92
|
-
* matching, and [chokidar](https://github.com/paulmillr/chokidar) for file
|
|
93
|
-
* watching.
|
|
94
|
-
*
|
|
95
|
-
* This is the default implementation used by {@link FilePromptProvider} and
|
|
96
|
-
* {@link TSPromptFileType} when no custom provider is supplied.
|
|
97
|
-
*/
|
|
98
|
-
var LocalFileProvider = class {
|
|
99
|
-
async readFile(filePath) {
|
|
100
|
-
return fs.readFile(filePath, "utf-8");
|
|
101
|
-
}
|
|
102
|
-
async writeFile(filePath, content) {
|
|
103
|
-
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
104
|
-
await fs.writeFile(filePath, content, "utf-8");
|
|
105
|
-
}
|
|
106
|
-
async deleteFile(filePath) {
|
|
107
|
-
await fs.unlink(filePath);
|
|
108
|
-
}
|
|
109
|
-
async import(filePath) {
|
|
110
|
-
return import(pathToFileURL(filePath).href);
|
|
111
|
-
}
|
|
112
|
-
async *glob(pattern, options = {}) {
|
|
113
|
-
const { cwd, ignore = [], absolute = false } = options;
|
|
114
|
-
const baseCwd = cwd ?? process.cwd();
|
|
115
|
-
for await (const file of glob(pattern, { cwd: baseCwd })) {
|
|
116
|
-
const relativePath = file.replace(/\\/g, "/");
|
|
117
|
-
if (ignore.some((p) => minimatch(relativePath, p))) continue;
|
|
118
|
-
yield absolute ? path.resolve(baseCwd, file) : file;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
watch(patterns, options, callback) {
|
|
122
|
-
const cwd = options.cwd ?? process.cwd();
|
|
123
|
-
const ignored = options.ignored ?? [];
|
|
124
|
-
const includeMatchers = patterns.map((p) => makeRe(p)).filter((re) => re !== false);
|
|
125
|
-
const matches = (fp) => includeMatchers.some((re) => re.test(fp));
|
|
126
|
-
const watcher = chokidar.watch(".", {
|
|
127
|
-
cwd,
|
|
128
|
-
ignored: (absPath) => {
|
|
129
|
-
const rel = path.relative(cwd, absPath).replace(/\\/g, "/");
|
|
130
|
-
return ignored.some((p) => minimatch(rel, p, { dot: true }));
|
|
131
|
-
},
|
|
132
|
-
persistent: true,
|
|
133
|
-
ignoreInitial: options.ignoreInitial ?? true
|
|
134
|
-
});
|
|
135
|
-
watcher.on("change", (fp) => {
|
|
136
|
-
if (matches(fp)) callback("change", fp);
|
|
137
|
-
});
|
|
138
|
-
watcher.on("add", (fp) => {
|
|
139
|
-
if (matches(fp)) callback("add", fp);
|
|
140
|
-
});
|
|
141
|
-
watcher.on("unlink", (fp) => {
|
|
142
|
-
if (matches(fp)) callback("remove", fp);
|
|
143
|
-
});
|
|
144
|
-
return () => {
|
|
145
|
-
watcher.close();
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
154
|
//#endregion
|
|
150
155
|
//#region src/prompt/file/ts/ts-prompt-file-type.ts
|
|
151
156
|
/**
|
|
@@ -183,7 +188,7 @@ var TSPromptFileType = class {
|
|
|
183
188
|
return `import { prompts } from ${JSON.stringify(importPath)};
|
|
184
189
|
|
|
185
190
|
export default prompts(
|
|
186
|
-
${JSON.stringify(promptsId)},
|
|
191
|
+
{ id: ${JSON.stringify(promptsId)} },
|
|
187
192
|
() => ({
|
|
188
193
|
${key}: () => ({
|
|
189
194
|
})
|
|
@@ -382,10 +387,9 @@ export default prompts(
|
|
|
382
387
|
}
|
|
383
388
|
};
|
|
384
389
|
/**
|
|
385
|
-
* If `expr` is a call like `prompts(id, factory)`
|
|
386
|
-
*
|
|
387
|
-
*
|
|
388
|
-
* Otherwise null.
|
|
390
|
+
* If `expr` is a call like `prompts({ id }, factory)` whose factory immediately
|
|
391
|
+
* returns an object literal, return that object literal together with the module
|
|
392
|
+
* ID extracted from the options object. Otherwise null.
|
|
389
393
|
*/
|
|
390
394
|
function findPromptsHelperCall(expr) {
|
|
391
395
|
if (!ts.isCallExpression(expr)) return null;
|
|
@@ -397,9 +401,12 @@ function findPromptsHelperCall(expr) {
|
|
|
397
401
|
const first = expr.arguments[0];
|
|
398
402
|
return {
|
|
399
403
|
object,
|
|
400
|
-
moduleId: first && ts.
|
|
404
|
+
moduleId: first && ts.isObjectLiteralExpression(first) ? extractStringProperty(first, "id") : void 0
|
|
401
405
|
};
|
|
402
406
|
}
|
|
407
|
+
function extractStringProperty(obj, key) {
|
|
408
|
+
for (const prop of obj.properties) if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === key && ts.isStringLiteralLike(prop.initializer)) return prop.initializer.text;
|
|
409
|
+
}
|
|
403
410
|
function getPropertyName(prop) {
|
|
404
411
|
if (ts.isMethodDeclaration(prop) || ts.isPropertyAssignment(prop)) {
|
|
405
412
|
const name = prop.name;
|
|
@@ -600,7 +607,6 @@ var FilePromptProvider = class {
|
|
|
600
607
|
includePatterns;
|
|
601
608
|
ignorePatterns;
|
|
602
609
|
sdkAdapter;
|
|
603
|
-
suppressedWatchEvents = /* @__PURE__ */ new Map();
|
|
604
610
|
constructor({ id = "fs" + (defaultIDCounter++ ? defaultIDCounter : ""), rootDir = process.cwd(), fileProvider = new LocalFileProvider(), fileType, includePatterns, ignorePatterns = DEFAULT_IGNORE_PATTERNS, sdk }) {
|
|
605
611
|
fileType ??= new TSPromptFileType(fileProvider);
|
|
606
612
|
this.id = id;
|
|
@@ -626,7 +632,6 @@ var FilePromptProvider = class {
|
|
|
626
632
|
const { definitions, values } = parsed.extractedProps;
|
|
627
633
|
const rawUpdates = this.sdkAdapter.denormalizeUpdates(updates, values);
|
|
628
634
|
for (const [propertyName, value] of Object.entries(rawUpdates)) {
|
|
629
|
-
this.suppressNextWatchEvent(filePath, "change");
|
|
630
635
|
const propDef = definitions.find((d) => d.name === propertyName);
|
|
631
636
|
const currentValue = values?.[propertyName];
|
|
632
637
|
if (value === null) {
|
|
@@ -663,14 +668,13 @@ var FilePromptProvider = class {
|
|
|
663
668
|
getModelParameters() {
|
|
664
669
|
return this.sdkAdapter.getModelParameters(this.rootDir);
|
|
665
670
|
}
|
|
666
|
-
async execute(promptId, params
|
|
671
|
+
async execute(promptId, params) {
|
|
667
672
|
const [filePath, promptName] = this.parsePromptId(promptId);
|
|
668
673
|
const config = await this.fileType.loadConfig(filePath, promptName, params);
|
|
669
|
-
|
|
674
|
+
await this.sdkAdapter.executeConfig(config);
|
|
670
675
|
}
|
|
671
676
|
async renamePrompt(promptId, newName) {
|
|
672
677
|
const [filePath, oldName] = this.parsePromptId(promptId);
|
|
673
|
-
this.suppressNextWatchEvent(filePath, "change");
|
|
674
678
|
await this.fileType.renamePrompt(filePath, oldName, newName);
|
|
675
679
|
const relFilePath = path.relative(this.rootDir, filePath);
|
|
676
680
|
const prompt = await this.getPrompt(`${relFilePath}#${newName}`);
|
|
@@ -687,7 +691,6 @@ var FilePromptProvider = class {
|
|
|
687
691
|
const promptsId = firstDot >= 0 ? baseName.slice(0, firstDot) : baseName;
|
|
688
692
|
const name = partial.name ?? promptsId;
|
|
689
693
|
const content = this.fileType.newPromptSkeleton(promptsId, name, this.sdkAdapter.promptsHelperImport);
|
|
690
|
-
this.suppressNextWatchEvent(absPath, "add");
|
|
691
694
|
await this.fileProvider.writeFile(absPath, content);
|
|
692
695
|
if (this.files && !this.files.includes(absPath)) this.files.push(absPath);
|
|
693
696
|
const prompt = await this.getPrompt(`${normalizedRelFilePath}#${name}`);
|
|
@@ -741,7 +744,6 @@ var FilePromptProvider = class {
|
|
|
741
744
|
ignored: this.ignorePatterns
|
|
742
745
|
}, async (eventType, filePath) => {
|
|
743
746
|
const absolutePath = this.resolveFilePath(filePath);
|
|
744
|
-
if (this.consumeSuppressedWatchEvent(absolutePath, eventType)) return;
|
|
745
747
|
if (eventType === "change" || eventType === "add") {
|
|
746
748
|
if (this.files && !this.files.includes(absolutePath)) this.files.push(absolutePath);
|
|
747
749
|
(await this.fileType.parsePrompts([absolutePath], this.rootDir)).forEach((prompt) => {
|
|
@@ -762,30 +764,6 @@ var FilePromptProvider = class {
|
|
|
762
764
|
async ensureFiles() {
|
|
763
765
|
if (!this.files) this.files = await Array.fromAsync(this.findPromptFiles());
|
|
764
766
|
}
|
|
765
|
-
suppressNextWatchEvent(filePath, eventType) {
|
|
766
|
-
if (eventType !== "change" && eventType !== "add") return;
|
|
767
|
-
const key = `${eventType}:${filePath}`;
|
|
768
|
-
const entry = this.suppressedWatchEvents.get(key);
|
|
769
|
-
this.suppressedWatchEvents.set(key, {
|
|
770
|
-
remaining: (entry?.remaining ?? 0) + 1,
|
|
771
|
-
expiresAt: Date.now() + 2e3
|
|
772
|
-
});
|
|
773
|
-
}
|
|
774
|
-
consumeSuppressedWatchEvent(filePath, eventType) {
|
|
775
|
-
const key = `${eventType}:${filePath}`;
|
|
776
|
-
const entry = this.suppressedWatchEvents.get(key);
|
|
777
|
-
if (!entry) return false;
|
|
778
|
-
if (entry.expiresAt < Date.now()) {
|
|
779
|
-
this.suppressedWatchEvents.delete(key);
|
|
780
|
-
return false;
|
|
781
|
-
}
|
|
782
|
-
if (entry.remaining <= 1) this.suppressedWatchEvents.delete(key);
|
|
783
|
-
else this.suppressedWatchEvents.set(key, {
|
|
784
|
-
remaining: entry.remaining - 1,
|
|
785
|
-
expiresAt: entry.expiresAt
|
|
786
|
-
});
|
|
787
|
-
return true;
|
|
788
|
-
}
|
|
789
767
|
async *findPromptFiles() {
|
|
790
768
|
const uniqueFiles = /* @__PURE__ */ new Set();
|
|
791
769
|
for (const pattern of this.includePatterns) {
|
|
@@ -1116,26 +1094,22 @@ var GeminiInteractionsSDK = class {
|
|
|
1116
1094
|
});
|
|
1117
1095
|
}
|
|
1118
1096
|
getModelParameters(rootDir) {
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1097
|
+
try {
|
|
1098
|
+
const dtsPath = findPackageDts("@google/genai", "dist/genai.d.ts", rootDir);
|
|
1099
|
+
if (dtsPath) {
|
|
1100
|
+
const sourceText = fs$1.readFileSync(dtsPath, "utf-8");
|
|
1101
|
+
const sourceFile = ts.createSourceFile(dtsPath, sourceText, ts.ScriptTarget.Latest, true);
|
|
1102
|
+
const decl = findTypeDeclaration(sourceFile, "GenerationConfig_2");
|
|
1103
|
+
if (decl) return extractPropertiesFromDeclaration(decl, sourceFile).definitions;
|
|
1104
|
+
}
|
|
1125
1105
|
} catch {}
|
|
1126
1106
|
return FALLBACK_GENERATION_CONFIG_PARAMS;
|
|
1127
1107
|
}
|
|
1128
|
-
async executeConfig(config
|
|
1129
|
-
|
|
1108
|
+
async executeConfig(config) {
|
|
1109
|
+
await new GoogleGenAI({}).interactions.create({
|
|
1130
1110
|
...config,
|
|
1131
|
-
stream,
|
|
1132
1111
|
store: false
|
|
1133
1112
|
});
|
|
1134
|
-
if ("id" in result) return {
|
|
1135
|
-
text: (result.outputs ?? []).find((o) => o.type === "text")?.text ?? "",
|
|
1136
|
-
usage: result.usage
|
|
1137
|
-
};
|
|
1138
|
-
else return streamTextFromSSE(result);
|
|
1139
1113
|
}
|
|
1140
1114
|
normalizePrompt(prompt) {
|
|
1141
1115
|
const { definitions, values } = prompt.extractedProps;
|
|
@@ -1313,13 +1287,5 @@ function extractMessages(value) {
|
|
|
1313
1287
|
}
|
|
1314
1288
|
return results;
|
|
1315
1289
|
}
|
|
1316
|
-
/**
|
|
1317
|
-
* Yields text chunks from the Interactions API SSE stream.
|
|
1318
|
-
*/
|
|
1319
|
-
async function* streamTextFromSSE(stream) {
|
|
1320
|
-
for await (const chunk of stream) if (chunk.event_type === "content.delta") {
|
|
1321
|
-
if (chunk.delta?.type === "text" && chunk.delta.text) yield chunk.delta.text;
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
1290
|
//#endregion
|
|
1325
|
-
export { FilePromptProvider, GeminiInteractionsSDK, LocalFileProvider, MemoryFileProvider, MemoryTraceProvider, PROMPT_ID_ATTRIBUTE, PROMPT_NAME_ATTRIBUTE, PROMPT_PROVIDER_ID_ATTRIBUTE, SPAN_KIND_ATTRIBUTE, TSPromptFileType, VercelAISDK, createTracerForPrompt,
|
|
1291
|
+
export { FilePromptProvider, GeminiInteractionsSDK, LocalFileProvider, MemoryFileProvider, MemoryTraceProvider, PROMPT_ID_ATTRIBUTE, PROMPT_INPUTS_ATTRIBUTE, PROMPT_NAME_ATTRIBUTE, PROMPT_PROVIDER_ID_ATTRIBUTE, SPAN_KIND_ATTRIBUTE, TSPromptFileType, VercelAISDK, createTracerForPrompt, getPromptSpanAttributes };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
3
3
|
import fs from "node:fs";
|
|
4
|
-
import { generateText
|
|
4
|
+
import { generateText } from "ai";
|
|
5
5
|
import { extractPropertiesFromDeclaration, findTypeDeclaration } from "ts-proppy";
|
|
6
6
|
import ts from "typescript";
|
|
7
7
|
//#region src/shared/helpers.ts
|
|
@@ -31,10 +31,9 @@ function isEditable(value) {
|
|
|
31
31
|
*/
|
|
32
32
|
const SPAN_KIND_ATTRIBUTE = "evalution.span.type";
|
|
33
33
|
/**
|
|
34
|
-
* Attribute name a span can set to
|
|
35
|
-
* specific prompt provider. When absent, the prompt ID is treated as global.
|
|
34
|
+
* Attribute name a span can set to give a human-readable name to the prompt.
|
|
36
35
|
*/
|
|
37
|
-
const
|
|
36
|
+
const PROMPT_NAME_ATTRIBUTE = "gen_ai.prompt.name";
|
|
38
37
|
/**
|
|
39
38
|
* Attribute name a span can set to link itself to a specific prompt. The value
|
|
40
39
|
* is a globally-unique prompt ID unless {@link PROMPT_PROVIDER_ID_ATTRIBUTE} is
|
|
@@ -42,9 +41,24 @@ const PROMPT_PROVIDER_ID_ATTRIBUTE = "evalution.prompt.provider.id";
|
|
|
42
41
|
*/
|
|
43
42
|
const PROMPT_ID_ATTRIBUTE = "evalution.prompt.id";
|
|
44
43
|
/**
|
|
45
|
-
* Attribute name a span can set to
|
|
44
|
+
* Attribute name a span can set to scope its {@link PROMPT_ID_ATTRIBUTE} to a
|
|
45
|
+
* specific prompt provider. When absent, the prompt ID is treated as global.
|
|
46
46
|
*/
|
|
47
|
-
const
|
|
47
|
+
const PROMPT_PROVIDER_ID_ATTRIBUTE = "evalution.prompt.provider.id";
|
|
48
|
+
/**
|
|
49
|
+
* Attribute name a span can set to include an array of input parameters used
|
|
50
|
+
* to construct the final prompt.
|
|
51
|
+
*/
|
|
52
|
+
const PROMPT_INPUTS_ATTRIBUTE = "evalution.prompt.inputs";
|
|
53
|
+
function getPromptSpanAttributes(prompt, attributes = {}) {
|
|
54
|
+
return Object.fromEntries([
|
|
55
|
+
[SPAN_KIND_ATTRIBUTE, "LLM"],
|
|
56
|
+
[PROMPT_NAME_ATTRIBUTE, prompt.name],
|
|
57
|
+
[PROMPT_ID_ATTRIBUTE, prompt.id],
|
|
58
|
+
[PROMPT_INPUTS_ATTRIBUTE, prompt.functionParameters],
|
|
59
|
+
...Object.entries(attributes)
|
|
60
|
+
].filter(([, v]) => v !== void 0 && v !== null));
|
|
61
|
+
}
|
|
48
62
|
/**
|
|
49
63
|
* Wraps a {@link Tracer} so that every span it produces is tagged with the
|
|
50
64
|
* attributes that associate it with a prompt — the prompt's name
|
|
@@ -69,12 +83,7 @@ function createTracerForPrompt(prompt, tracer) {
|
|
|
69
83
|
const inner = tracer ?? trace.getTracer("evalution");
|
|
70
84
|
const withPromptAttributes = (options) => ({
|
|
71
85
|
...options,
|
|
72
|
-
attributes:
|
|
73
|
-
[SPAN_KIND_ATTRIBUTE]: "LLM",
|
|
74
|
-
[PROMPT_NAME_ATTRIBUTE]: prompt.name,
|
|
75
|
-
...prompt.id !== void 0 && { ["evalution.prompt.id"]: prompt.id },
|
|
76
|
-
...options?.attributes
|
|
77
|
-
}
|
|
86
|
+
attributes: getPromptSpanAttributes(prompt, options?.attributes)
|
|
78
87
|
});
|
|
79
88
|
return {
|
|
80
89
|
startSpan(name, options, context) {
|
|
@@ -171,9 +180,9 @@ function readLLM(attributes) {
|
|
|
171
180
|
const messages = parseMessages(attributes["gen_ai.input.messages"] ?? attributes["ai.prompt.messages"]);
|
|
172
181
|
const output = parseOutput(attributes["gen_ai.output.messages"]) ?? str(attributes["ai.response.text"]);
|
|
173
182
|
const paramEntries = PARAM_ATTRIBUTES.map((key) => [key.replace("gen_ai.request.", ""), attributes[key]]).filter(([, v]) => v !== void 0);
|
|
174
|
-
const
|
|
183
|
+
const modelParameters = paramEntries.length > 0 ? Object.fromEntries(paramEntries) : void 0;
|
|
175
184
|
const totalTokens = promptTokens !== void 0 && completionTokens !== void 0 ? promptTokens + completionTokens : void 0;
|
|
176
|
-
if (!provider && !model && !promptTokens && !completionTokens && !messages && !output && !
|
|
185
|
+
if (!provider && !model && !promptTokens && !completionTokens && !messages && !output && !modelParameters) return;
|
|
177
186
|
return {
|
|
178
187
|
...provider && { provider },
|
|
179
188
|
...model && { model },
|
|
@@ -182,7 +191,7 @@ function readLLM(attributes) {
|
|
|
182
191
|
...promptTokens !== void 0 && { promptTokens },
|
|
183
192
|
...completionTokens !== void 0 && { completionTokens },
|
|
184
193
|
...totalTokens !== void 0 && { totalTokens },
|
|
185
|
-
...
|
|
194
|
+
...modelParameters && { modelParameters }
|
|
186
195
|
};
|
|
187
196
|
}
|
|
188
197
|
function llmAndPrompt(attributes) {
|
|
@@ -502,6 +511,93 @@ const RESERVED_KEYS = new Set([
|
|
|
502
511
|
SYSTEM_KEY,
|
|
503
512
|
MESSAGES_KEY
|
|
504
513
|
]);
|
|
514
|
+
const FALLBACK_CALL_SETTINGS_PARAMS = [
|
|
515
|
+
{
|
|
516
|
+
name: "maxOutputTokens",
|
|
517
|
+
description: "Maximum number of tokens to generate.",
|
|
518
|
+
type: {
|
|
519
|
+
kind: "primitive",
|
|
520
|
+
syntax: "number"
|
|
521
|
+
},
|
|
522
|
+
optional: true
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
name: "temperature",
|
|
526
|
+
description: "Temperature setting. The value is passed through to the provider. The range depends on the provider and model.",
|
|
527
|
+
type: {
|
|
528
|
+
kind: "primitive",
|
|
529
|
+
syntax: "number"
|
|
530
|
+
},
|
|
531
|
+
optional: true
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
name: "topP",
|
|
535
|
+
description: "Nucleus sampling. The value is passed through to the provider. The range depends on the provider and model.",
|
|
536
|
+
type: {
|
|
537
|
+
kind: "primitive",
|
|
538
|
+
syntax: "number"
|
|
539
|
+
},
|
|
540
|
+
optional: true
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
name: "topK",
|
|
544
|
+
description: "Only sample from the top K options for each subsequent token.",
|
|
545
|
+
type: {
|
|
546
|
+
kind: "primitive",
|
|
547
|
+
syntax: "number"
|
|
548
|
+
},
|
|
549
|
+
optional: true
|
|
550
|
+
},
|
|
551
|
+
{
|
|
552
|
+
name: "presencePenalty",
|
|
553
|
+
description: "Presence penalty setting. It affects the likelihood of the model to repeat information that is already in the prompt.",
|
|
554
|
+
type: {
|
|
555
|
+
kind: "primitive",
|
|
556
|
+
syntax: "number"
|
|
557
|
+
},
|
|
558
|
+
optional: true
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
name: "frequencyPenalty",
|
|
562
|
+
description: "Frequency penalty setting. It affects the likelihood of the model to repeatedly use the same words or phrases.",
|
|
563
|
+
type: {
|
|
564
|
+
kind: "primitive",
|
|
565
|
+
syntax: "number"
|
|
566
|
+
},
|
|
567
|
+
optional: true
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
name: "stopSequences",
|
|
571
|
+
description: "Stop sequences. If set, the model will stop generating text.",
|
|
572
|
+
type: {
|
|
573
|
+
kind: "array",
|
|
574
|
+
syntax: "string[]",
|
|
575
|
+
elementType: {
|
|
576
|
+
kind: "primitive",
|
|
577
|
+
syntax: "string"
|
|
578
|
+
}
|
|
579
|
+
},
|
|
580
|
+
optional: true
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
name: "seed",
|
|
584
|
+
description: "The seed (integer) to use for random sampling.",
|
|
585
|
+
type: {
|
|
586
|
+
kind: "primitive",
|
|
587
|
+
syntax: "number"
|
|
588
|
+
},
|
|
589
|
+
optional: true
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
name: "maxRetries",
|
|
593
|
+
description: "Maximum number of retries. Set to 0 to disable retries.",
|
|
594
|
+
type: {
|
|
595
|
+
kind: "primitive",
|
|
596
|
+
syntax: "number"
|
|
597
|
+
},
|
|
598
|
+
optional: true
|
|
599
|
+
}
|
|
600
|
+
];
|
|
505
601
|
/** The `prompts()` factory from `@evalution/vercel-ai-sdk`. */
|
|
506
602
|
const PROMPTS_HELPER_CALL = {
|
|
507
603
|
callee: "prompts",
|
|
@@ -581,7 +677,10 @@ export default {
|
|
|
581
677
|
*/
|
|
582
678
|
var VercelAISDK = class {
|
|
583
679
|
promptsHelperImport = PROMPTS_HELPER_CALL.import.from;
|
|
584
|
-
/**
|
|
680
|
+
/**
|
|
681
|
+
* Onboarding task: install the SDK package, then drop a starter config.
|
|
682
|
+
* @internal
|
|
683
|
+
*/
|
|
585
684
|
static setupTask = {
|
|
586
685
|
id: "vercel-ai-sdk",
|
|
587
686
|
label: "AI SDK",
|
|
@@ -639,24 +738,19 @@ var VercelAISDK = class {
|
|
|
639
738
|
});
|
|
640
739
|
}
|
|
641
740
|
getModelParameters(rootDir) {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
741
|
+
try {
|
|
742
|
+
const dtsPath = findPackageDts("ai", "dist/index.d.ts", rootDir);
|
|
743
|
+
if (dtsPath) {
|
|
744
|
+
const sourceText = fs.readFileSync(dtsPath, "utf-8");
|
|
745
|
+
const sourceFile = ts.createSourceFile(dtsPath, sourceText, ts.ScriptTarget.Latest, true);
|
|
746
|
+
const decl = findTypeDeclaration(sourceFile, "CallSettings");
|
|
747
|
+
if (decl) return extractPropertiesFromDeclaration(decl, sourceFile).definitions;
|
|
748
|
+
}
|
|
749
|
+
} catch {}
|
|
750
|
+
return FALLBACK_CALL_SETTINGS_PARAMS;
|
|
649
751
|
}
|
|
650
|
-
async executeConfig(config
|
|
651
|
-
|
|
652
|
-
else {
|
|
653
|
-
const result = await generateText(config);
|
|
654
|
-
return {
|
|
655
|
-
text: result.text,
|
|
656
|
-
usage: result.usage,
|
|
657
|
-
finishReason: result.finishReason
|
|
658
|
-
};
|
|
659
|
-
}
|
|
752
|
+
async executeConfig(config) {
|
|
753
|
+
await generateText(config);
|
|
660
754
|
}
|
|
661
755
|
normalizePrompt(prompt) {
|
|
662
756
|
const { definitions, values } = prompt.extractedProps;
|
|
@@ -742,7 +836,7 @@ function extractMessages(value) {
|
|
|
742
836
|
});
|
|
743
837
|
}
|
|
744
838
|
function extractToolCalls(value) {
|
|
745
|
-
if (
|
|
839
|
+
if (value?.kind !== "array") return void 0;
|
|
746
840
|
const out = [];
|
|
747
841
|
for (const el of value.elements) {
|
|
748
842
|
if (el.kind !== "object") continue;
|
|
@@ -756,4 +850,4 @@ function extractToolCalls(value) {
|
|
|
756
850
|
return out.length > 0 ? out : void 0;
|
|
757
851
|
}
|
|
758
852
|
//#endregion
|
|
759
|
-
export { MemoryTraceProvider as a,
|
|
853
|
+
export { MemoryTraceProvider as a, PROMPT_NAME_ATTRIBUTE as c, createTracerForPrompt as d, getPromptSpanAttributes as f, setupStepCommand as i, PROMPT_PROVIDER_ID_ATTRIBUTE as l, findPackageDts as n, PROMPT_ID_ATTRIBUTE as o, isEditable as p, CONFIG_FILE_RELATIVE_PATH as r, PROMPT_INPUTS_ATTRIBUTE as s, VercelAISDK as t, SPAN_KIND_ATTRIBUTE as u };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "evalution",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "TypeScript AI Prompt Playground - Edit and execute AI prompts with live preview",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -58,12 +58,12 @@
|
|
|
58
58
|
"minimatch": "^10.2.4",
|
|
59
59
|
"react": ">=18.2.0",
|
|
60
60
|
"react-dom": ">=18.2.0",
|
|
61
|
-
"ts-proppy": "0.2.
|
|
61
|
+
"ts-proppy": "0.2.3",
|
|
62
62
|
"typescript": ">=5.3.3",
|
|
63
63
|
"ws": "^8.21.0"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@biomejs/biome": "^2.
|
|
66
|
+
"@biomejs/biome": "^2.5.0",
|
|
67
67
|
"@playwright/experimental-ct-react": "^1.58.2",
|
|
68
68
|
"@playwright/test": "^1.58.2",
|
|
69
69
|
"@types/node": ">=22.18.0",
|