@evalgate/sdk 2.2.0 → 2.2.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/CHANGELOG.md +51 -1
- package/README.md +34 -7
- package/dist/assertions.d.ts +58 -0
- package/dist/assertions.js +704 -33
- package/dist/export.d.ts +1 -1
- package/dist/export.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +12 -2
- package/dist/snapshot.d.ts +5 -5
- package/dist/snapshot.js +9 -5
- package/dist/types.d.ts +7 -2
- package/dist/types.js +7 -2
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/dist/workflows.js +6 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,48 @@ All notable changes to the @evalgate/sdk package will be documented in this file
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.2.2] - 2026-03-03
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **8 stub assertions replaced with real implementations:**
|
|
13
|
+
- `hasSentiment` — substring matching + expanded 34/31-word positive/negative lexicon (was exact-match, 4 words each)
|
|
14
|
+
- `hasNoHallucinations` — case-insensitive fact matching (was case-sensitive)
|
|
15
|
+
- `hasFactualAccuracy` — case-insensitive fact matching (was case-sensitive)
|
|
16
|
+
- `containsLanguage` — expanded from 3 languages (en/es/fr) to 12 (+ de/it/pt/nl/ru/zh/ja/ko/ar) with BCP-47 subtag support (`zh-CN` → `zh`)
|
|
17
|
+
- `hasValidCodeSyntax` — real bracket/brace/parenthesis balance checker with string literal and comment awareness (handles JS `//`/`/* */`, Python `#`, template literals, single/double quotes); JSON fast-path via `JSON.parse`
|
|
18
|
+
- `hasNoToxicity` — expanded from 4 words to ~80 terms across 9 categories: insults, degradation, violence/threats, self-harm directed at others, dehumanization, hate/rejection, harassment, profanity-as-attacks, bullying/appearance/mental-health weaponization
|
|
19
|
+
- `hasReadabilityScore` — fixed Flesch-Kincaid syllable counting to be per-word (was treating entire text as one word)
|
|
20
|
+
- `matchesSchema` — now dispatches on schema format: JSON Schema `required` array (`{ required: ['name'] }` → checks required keys exist), JSON Schema `properties` object (`{ properties: { name: {} } }` → checks property keys exist), or simple key-presence template (existing behavior preserved for backward compat). Fixes regression: `matchesSchema({ name: 'test', score: 95 }, { type: 'object', required: ['name'] })` was returning `false`
|
|
21
|
+
- **`importData` crash** — `options: ImportOptions` parameter now defaults to `{}` to prevent `Cannot read properties of undefined (reading 'dryRun')` when called as `importData(client, data)`
|
|
22
|
+
- **`compareWithSnapshot` / `SnapshotManager.compare` object coercion** — both now accept `unknown` input and coerce non-string values via `JSON.stringify` before comparison, matching the existing behavior of `SnapshotManager.save()`
|
|
23
|
+
- **`WorkflowTracer` constructor crash** — defensive guard: `typeof client?.getOrganizationId === "function"` before calling it; prevents `TypeError: client.getOrganizationId is not a function` when using partial clients or initializing without an API key
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- **LLM-backed async assertion variants** — 6 new exported functions:
|
|
28
|
+
- `hasSentimentAsync(text, expected, config?)` — LLM classifies sentiment with full context awareness
|
|
29
|
+
- `hasNoToxicityAsync(text, config?)` — LLM detects sarcastic, implicit, and culturally specific toxic content that blocklists miss
|
|
30
|
+
- `containsLanguageAsync(text, language, config?)` — LLM language detection for any language
|
|
31
|
+
- `hasValidCodeSyntaxAsync(code, language, config?)` — LLM deep syntax analysis beyond bracket balance
|
|
32
|
+
- `hasFactualAccuracyAsync(text, facts, config?)` — LLM checks facts semantically, catches paraphrased inaccuracies
|
|
33
|
+
- `hasNoHallucinationsAsync(text, groundTruth, config?)` — LLM detects fabricated claims even when paraphrased
|
|
34
|
+
- **`configureAssertions(config: AssertionLLMConfig)`** — set global LLM provider/apiKey/model/baseUrl once; all `*Async` functions use it automatically; per-call `config` overrides it
|
|
35
|
+
- **`getAssertionConfig()`** — retrieve current global assertion LLM config
|
|
36
|
+
- **`AssertionLLMConfig` type** — exported interface: `{ provider: "openai" | "anthropic"; apiKey: string; model?: string; baseUrl?: string }`
|
|
37
|
+
- **JSDoc `**Fast and approximate**` / `**Slow and accurate**` markers** on all sync/async assertion pairs with `{@link xAsync}` cross-references that appear in IDE tooltips
|
|
38
|
+
- **115 new tests** in `assertions.test.ts` covering all improved sync assertions (expanded lexicons, JSON Schema formats, bracket balance edge cases, 12-language detection, BCP-47) and all 6 async variants (OpenAI path, Anthropic path, global config, error cases, HTTP 4xx handling)
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## [2.2.1] - 2026-03-03
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- **`snapshot(name, output)` accepts objects** — passing `{ score: 92 }` no longer throws; non-string values are auto-serialized via `JSON.stringify`. `SnapshotManager.save()` and `update()` widened to `output: unknown` accordingly.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
8
50
|
## [2.2.0] - 2026-03-03
|
|
9
51
|
|
|
10
52
|
### Breaking
|
|
@@ -42,6 +84,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
42
84
|
- **Low:** Explain no longer shows "unnamed" for builtin gate failures
|
|
43
85
|
- **Docs:** Added missing `discover --manifest` step to local quickstart
|
|
44
86
|
|
|
87
|
+
---
|
|
88
|
+
|
|
45
89
|
## [2.1.2] - 2026-03-02
|
|
46
90
|
|
|
47
91
|
### Fixed
|
|
@@ -49,12 +93,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
49
93
|
- **Type safety** — aligned with platform 2.1.2; zero TypeScript errors across all integration points
|
|
50
94
|
- **CI gate** — all SDK tests, lint, and build checks passing
|
|
51
95
|
|
|
96
|
+
---
|
|
97
|
+
|
|
52
98
|
## [2.1.1] - 2026-03-02
|
|
53
99
|
|
|
54
100
|
### Fixed
|
|
55
101
|
|
|
56
102
|
- Version alignment with platform 2.1.1
|
|
57
103
|
|
|
104
|
+
---
|
|
105
|
+
|
|
58
106
|
## [2.0.0] - 2026-03-01
|
|
59
107
|
|
|
60
108
|
### Breaking — EvalGate Rebrand
|
|
@@ -356,7 +404,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
356
404
|
- error catalog stability + graceful handling of unknown codes
|
|
357
405
|
- exports contract (retention visibility, 410 semantics)
|
|
358
406
|
|
|
359
|
-
|
|
407
|
+
---
|
|
360
408
|
|
|
361
409
|
## [1.5.0] - 2026-02-18
|
|
362
410
|
|
|
@@ -404,6 +452,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
404
452
|
- **Package hardening** — `files`, `module`, `sideEffects: false` for leaner npm publish
|
|
405
453
|
- **CLI** — Passes `baseline` param to quality API for deterministic CI gates
|
|
406
454
|
|
|
455
|
+
---
|
|
456
|
+
|
|
407
457
|
## [1.3.0] - 2025-10-21
|
|
408
458
|
|
|
409
459
|
### ✨ Added
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@evalgate/sdk)
|
|
4
4
|
[](https://www.npmjs.com/package/@evalgate/sdk)
|
|
5
5
|
[](https://www.typescriptlang.org/)
|
|
6
|
-
[](#)
|
|
7
7
|
[](#)
|
|
8
8
|
[](https://opensource.org/licenses/MIT)
|
|
9
9
|
|
|
@@ -366,6 +366,33 @@ import type {
|
|
|
366
366
|
} from "@evalgate/sdk/regression";
|
|
367
367
|
```
|
|
368
368
|
|
|
369
|
+
### Assertions — Sync (fast, heuristic) and Async (slow, LLM-backed)
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
import {
|
|
373
|
+
// Sync — fast and approximate (no API key needed)
|
|
374
|
+
hasSentiment, hasNoToxicity, hasValidCodeSyntax,
|
|
375
|
+
containsLanguage, hasFactualAccuracy, hasNoHallucinations,
|
|
376
|
+
matchesSchema,
|
|
377
|
+
// Async — slow and accurate (requires API key)
|
|
378
|
+
configureAssertions, hasSentimentAsync, hasNoToxicityAsync,
|
|
379
|
+
hasValidCodeSyntaxAsync, containsLanguageAsync,
|
|
380
|
+
hasFactualAccuracyAsync, hasNoHallucinationsAsync,
|
|
381
|
+
} from "@evalgate/sdk";
|
|
382
|
+
|
|
383
|
+
// Configure once (or pass per-call)
|
|
384
|
+
configureAssertions({ provider: "openai", apiKey: process.env.OPENAI_API_KEY });
|
|
385
|
+
|
|
386
|
+
// Sync — fast, no network
|
|
387
|
+
console.log(hasSentiment("I love this!", "positive")); // true
|
|
388
|
+
console.log(hasNoToxicity("Have a great day!")); // true
|
|
389
|
+
console.log(hasValidCodeSyntax("function f() {}", "js")); // true
|
|
390
|
+
|
|
391
|
+
// Async — LLM-backed, context-aware
|
|
392
|
+
console.log(await hasSentimentAsync("subtle irony...", "negative")); // true
|
|
393
|
+
console.log(await hasNoToxicityAsync("sarcastic attack text")); // false
|
|
394
|
+
```
|
|
395
|
+
|
|
369
396
|
### Platform Client
|
|
370
397
|
|
|
371
398
|
```typescript
|
|
@@ -423,17 +450,17 @@ Your local `openAIChatEval` runs continue to work. No account cancellation. No d
|
|
|
423
450
|
|
|
424
451
|
See [CHANGELOG.md](CHANGELOG.md) for the full release history.
|
|
425
452
|
|
|
426
|
-
**
|
|
453
|
+
**v2.2.2** — 8 stub assertions replaced with real implementations (`hasSentiment` expanded lexicon, `hasNoToxicity` ~80-term blocklist, `hasValidCodeSyntax` real bracket balance, `containsLanguage` 12 languages + BCP-47, `hasFactualAccuracy`/`hasNoHallucinations` case-insensitive, `hasReadabilityScore` per-word syllable fix, `matchesSchema` JSON Schema support). Added LLM-backed `*Async` variants + `configureAssertions`. Fixed `importData` crash, `compareWithSnapshot` object coercion, `WorkflowTracer` defensive guard. 115 new tests.
|
|
427
454
|
|
|
428
|
-
**
|
|
455
|
+
**v2.2.1** — `snapshot(name, output)` accepts objects; auto-serialized via `JSON.stringify`
|
|
429
456
|
|
|
430
|
-
**
|
|
457
|
+
**v2.2.0** — `expect().not` modifier, `hasPII()`, `defineSuite` object form, `snapshot` parameter order fix, `specId` collision fix
|
|
431
458
|
|
|
432
|
-
**v1.
|
|
459
|
+
**v1.8.0** — `evalgate doctor` rewrite (9-check checklist), `evalgate explain` command, guided failure flow, CI template with doctor preflight
|
|
433
460
|
|
|
434
|
-
**v1.
|
|
461
|
+
**v1.7.0** — `evalgate init` scaffolder, `evalgate upgrade --full`, `detectRunner()`, machine-readable gate output, init test matrix
|
|
435
462
|
|
|
436
|
-
**v1.
|
|
463
|
+
**v1.6.0** — `evalgate gate`, `evalgate baseline`, regression gate constants & types
|
|
437
464
|
|
|
438
465
|
## License
|
|
439
466
|
|
package/dist/assertions.d.ts
CHANGED
|
@@ -193,18 +193,76 @@ export declare function notContainsPII(text: string): boolean;
|
|
|
193
193
|
* if (hasPII(response)) throw new Error("PII leak");
|
|
194
194
|
*/
|
|
195
195
|
export declare function hasPII(text: string): boolean;
|
|
196
|
+
/**
|
|
197
|
+
* Lexicon-based sentiment check. **Fast and approximate** — suitable for
|
|
198
|
+
* low-stakes filtering or CI smoke tests. For production safety gates use
|
|
199
|
+
* {@link hasSentimentAsync} with an LLM provider for context-aware accuracy.
|
|
200
|
+
*/
|
|
196
201
|
export declare function hasSentiment(text: string, expected: "positive" | "negative" | "neutral"): boolean;
|
|
197
202
|
export declare function similarTo(text1: string, text2: string, threshold?: number): boolean;
|
|
198
203
|
export declare function withinRange(value: number, min: number, max: number): boolean;
|
|
199
204
|
export declare function isValidEmail(email: string): boolean;
|
|
200
205
|
export declare function isValidURL(url: string): boolean;
|
|
206
|
+
/**
|
|
207
|
+
* Substring-based hallucination check — verifies each ground-truth fact
|
|
208
|
+
* appears verbatim in the text. **Fast and approximate**: catches missing
|
|
209
|
+
* facts but cannot detect paraphrased fabrications. Use
|
|
210
|
+
* {@link hasNoHallucinationsAsync} for semantic accuracy.
|
|
211
|
+
*/
|
|
201
212
|
export declare function hasNoHallucinations(text: string, groundTruth: string[]): boolean;
|
|
202
213
|
export declare function matchesSchema(value: unknown, schema: Record<string, unknown>): boolean;
|
|
203
214
|
export declare function hasReadabilityScore(text: string, minScore: number): boolean;
|
|
215
|
+
/**
|
|
216
|
+
* Keyword-frequency language detector supporting 12 languages.
|
|
217
|
+
* **Fast and approximate** — detects the most common languages reliably
|
|
218
|
+
* but may struggle with short texts or closely related languages.
|
|
219
|
+
* Use {@link containsLanguageAsync} for reliable detection of any language.
|
|
220
|
+
*/
|
|
204
221
|
export declare function containsLanguage(text: string, language: string): boolean;
|
|
222
|
+
/**
|
|
223
|
+
* Substring-based factual accuracy check. **Fast and approximate** — verifies
|
|
224
|
+
* each fact string appears in the text but cannot reason about meaning or
|
|
225
|
+
* paraphrasing. Use {@link hasFactualAccuracyAsync} for semantic accuracy.
|
|
226
|
+
*/
|
|
205
227
|
export declare function hasFactualAccuracy(text: string, facts: string[]): boolean;
|
|
206
228
|
export declare function respondedWithinTime(startTime: number, maxMs: number): boolean;
|
|
229
|
+
/**
|
|
230
|
+
* Blocklist-based toxicity check (~80 terms across 9 categories).
|
|
231
|
+
* **Fast and approximate** — catches explicit harmful language but has
|
|
232
|
+
* inherent gaps and context-blind false positives. Do NOT rely on this
|
|
233
|
+
* alone for production content safety gates; use {@link hasNoToxicityAsync}
|
|
234
|
+
* with an LLM for context-aware moderation.
|
|
235
|
+
*/
|
|
207
236
|
export declare function hasNoToxicity(text: string): boolean;
|
|
208
237
|
export declare function followsInstructions(text: string, instructions: string[]): boolean;
|
|
209
238
|
export declare function containsAllRequiredFields(obj: unknown, requiredFields: string[]): boolean;
|
|
239
|
+
export interface AssertionLLMConfig {
|
|
240
|
+
provider: "openai" | "anthropic";
|
|
241
|
+
apiKey: string;
|
|
242
|
+
model?: string;
|
|
243
|
+
baseUrl?: string;
|
|
244
|
+
}
|
|
245
|
+
export declare function configureAssertions(config: AssertionLLMConfig): void;
|
|
246
|
+
export declare function getAssertionConfig(): AssertionLLMConfig | null;
|
|
247
|
+
/**
|
|
248
|
+
* LLM-backed sentiment check. **Slow and accurate** — uses an LLM to
|
|
249
|
+
* classify sentiment with full context awareness. Requires
|
|
250
|
+
* {@link configureAssertions} or an inline `config` argument.
|
|
251
|
+
* Falls back gracefully with a clear error if no API key is configured.
|
|
252
|
+
*/
|
|
253
|
+
export declare function hasSentimentAsync(text: string, expected: "positive" | "negative" | "neutral", config?: AssertionLLMConfig): Promise<boolean>;
|
|
254
|
+
/**
|
|
255
|
+
* LLM-backed toxicity check. **Slow and accurate** — context-aware, handles
|
|
256
|
+
* sarcasm, implicit threats, and culturally specific harmful content that
|
|
257
|
+
* blocklists miss. Recommended for production content safety gates.
|
|
258
|
+
*/
|
|
259
|
+
export declare function hasNoToxicityAsync(text: string, config?: AssertionLLMConfig): Promise<boolean>;
|
|
260
|
+
export declare function containsLanguageAsync(text: string, language: string, config?: AssertionLLMConfig): Promise<boolean>;
|
|
261
|
+
export declare function hasValidCodeSyntaxAsync(code: string, language: string, config?: AssertionLLMConfig): Promise<boolean>;
|
|
262
|
+
export declare function hasFactualAccuracyAsync(text: string, facts: string[], config?: AssertionLLMConfig): Promise<boolean>;
|
|
263
|
+
/**
|
|
264
|
+
* LLM-backed hallucination check. **Slow and accurate** — detects fabricated
|
|
265
|
+
* claims even when they are paraphrased or contradict facts indirectly.
|
|
266
|
+
*/
|
|
267
|
+
export declare function hasNoHallucinationsAsync(text: string, groundTruth: string[], config?: AssertionLLMConfig): Promise<boolean>;
|
|
210
268
|
export declare function hasValidCodeSyntax(code: string, language: string): boolean;
|
package/dist/assertions.js
CHANGED
|
@@ -39,6 +39,14 @@ exports.respondedWithinTime = respondedWithinTime;
|
|
|
39
39
|
exports.hasNoToxicity = hasNoToxicity;
|
|
40
40
|
exports.followsInstructions = followsInstructions;
|
|
41
41
|
exports.containsAllRequiredFields = containsAllRequiredFields;
|
|
42
|
+
exports.configureAssertions = configureAssertions;
|
|
43
|
+
exports.getAssertionConfig = getAssertionConfig;
|
|
44
|
+
exports.hasSentimentAsync = hasSentimentAsync;
|
|
45
|
+
exports.hasNoToxicityAsync = hasNoToxicityAsync;
|
|
46
|
+
exports.containsLanguageAsync = containsLanguageAsync;
|
|
47
|
+
exports.hasValidCodeSyntaxAsync = hasValidCodeSyntaxAsync;
|
|
48
|
+
exports.hasFactualAccuracyAsync = hasFactualAccuracyAsync;
|
|
49
|
+
exports.hasNoHallucinationsAsync = hasNoHallucinationsAsync;
|
|
42
50
|
exports.hasValidCodeSyntax = hasValidCodeSyntax;
|
|
43
51
|
class AssertionError extends Error {
|
|
44
52
|
constructor(message, expected, actual) {
|
|
@@ -591,13 +599,91 @@ function notContainsPII(text) {
|
|
|
591
599
|
function hasPII(text) {
|
|
592
600
|
return !notContainsPII(text);
|
|
593
601
|
}
|
|
602
|
+
/**
|
|
603
|
+
* Lexicon-based sentiment check. **Fast and approximate** — suitable for
|
|
604
|
+
* low-stakes filtering or CI smoke tests. For production safety gates use
|
|
605
|
+
* {@link hasSentimentAsync} with an LLM provider for context-aware accuracy.
|
|
606
|
+
*/
|
|
594
607
|
function hasSentiment(text, expected) {
|
|
595
|
-
|
|
596
|
-
const positiveWords = [
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
608
|
+
const lower = text.toLowerCase();
|
|
609
|
+
const positiveWords = [
|
|
610
|
+
"good",
|
|
611
|
+
"great",
|
|
612
|
+
"excellent",
|
|
613
|
+
"amazing",
|
|
614
|
+
"wonderful",
|
|
615
|
+
"fantastic",
|
|
616
|
+
"love",
|
|
617
|
+
"best",
|
|
618
|
+
"happy",
|
|
619
|
+
"helpful",
|
|
620
|
+
"awesome",
|
|
621
|
+
"superb",
|
|
622
|
+
"outstanding",
|
|
623
|
+
"brilliant",
|
|
624
|
+
"perfect",
|
|
625
|
+
"delightful",
|
|
626
|
+
"joyful",
|
|
627
|
+
"pleased",
|
|
628
|
+
"glad",
|
|
629
|
+
"terrific",
|
|
630
|
+
"fabulous",
|
|
631
|
+
"exceptional",
|
|
632
|
+
"impressive",
|
|
633
|
+
"magnificent",
|
|
634
|
+
"marvelous",
|
|
635
|
+
"splendid",
|
|
636
|
+
"positive",
|
|
637
|
+
"enjoy",
|
|
638
|
+
"enjoyed",
|
|
639
|
+
"like",
|
|
640
|
+
"liked",
|
|
641
|
+
"beautiful",
|
|
642
|
+
"innovative",
|
|
643
|
+
"inspiring",
|
|
644
|
+
"effective",
|
|
645
|
+
"useful",
|
|
646
|
+
"valuable",
|
|
647
|
+
];
|
|
648
|
+
const negativeWords = [
|
|
649
|
+
"bad",
|
|
650
|
+
"terrible",
|
|
651
|
+
"awful",
|
|
652
|
+
"horrible",
|
|
653
|
+
"worst",
|
|
654
|
+
"hate",
|
|
655
|
+
"poor",
|
|
656
|
+
"disappointing",
|
|
657
|
+
"sad",
|
|
658
|
+
"useless",
|
|
659
|
+
"dreadful",
|
|
660
|
+
"miserable",
|
|
661
|
+
"angry",
|
|
662
|
+
"frustrated",
|
|
663
|
+
"broken",
|
|
664
|
+
"failed",
|
|
665
|
+
"pathetic",
|
|
666
|
+
"stupid",
|
|
667
|
+
"disgusting",
|
|
668
|
+
"unacceptable",
|
|
669
|
+
"wrong",
|
|
670
|
+
"error",
|
|
671
|
+
"fail",
|
|
672
|
+
"problem",
|
|
673
|
+
"negative",
|
|
674
|
+
"dislike",
|
|
675
|
+
"annoying",
|
|
676
|
+
"irritating",
|
|
677
|
+
"offensive",
|
|
678
|
+
"regret",
|
|
679
|
+
"disappointment",
|
|
680
|
+
"inadequate",
|
|
681
|
+
"mediocre",
|
|
682
|
+
"flawed",
|
|
683
|
+
"unreliable",
|
|
684
|
+
];
|
|
685
|
+
const positiveCount = positiveWords.filter((w) => lower.includes(w)).length;
|
|
686
|
+
const negativeCount = negativeWords.filter((w) => lower.includes(w)).length;
|
|
601
687
|
if (expected === "positive")
|
|
602
688
|
return positiveCount > negativeCount;
|
|
603
689
|
if (expected === "negative")
|
|
@@ -627,21 +713,37 @@ function isValidURL(url) {
|
|
|
627
713
|
return false;
|
|
628
714
|
}
|
|
629
715
|
}
|
|
716
|
+
/**
|
|
717
|
+
* Substring-based hallucination check — verifies each ground-truth fact
|
|
718
|
+
* appears verbatim in the text. **Fast and approximate**: catches missing
|
|
719
|
+
* facts but cannot detect paraphrased fabrications. Use
|
|
720
|
+
* {@link hasNoHallucinationsAsync} for semantic accuracy.
|
|
721
|
+
*/
|
|
630
722
|
function hasNoHallucinations(text, groundTruth) {
|
|
631
|
-
|
|
632
|
-
return groundTruth.every((truth) =>
|
|
723
|
+
const lower = text.toLowerCase();
|
|
724
|
+
return groundTruth.every((truth) => lower.includes(truth.toLowerCase()));
|
|
633
725
|
}
|
|
634
726
|
function matchesSchema(value, schema) {
|
|
635
|
-
// This is a simplified implementation
|
|
636
727
|
if (typeof value !== "object" || value === null)
|
|
637
728
|
return false;
|
|
638
|
-
|
|
729
|
+
const obj = value;
|
|
730
|
+
// JSON Schema: { required: ['name', 'age'] } — check required keys exist
|
|
731
|
+
if (Array.isArray(schema.required)) {
|
|
732
|
+
return schema.required.every((key) => key in obj);
|
|
733
|
+
}
|
|
734
|
+
// JSON Schema: { properties: { name: {}, age: {} } } — check property keys exist
|
|
735
|
+
if (schema.properties && typeof schema.properties === "object") {
|
|
736
|
+
return Object.keys(schema.properties).every((key) => key in obj);
|
|
737
|
+
}
|
|
738
|
+
// Simple template format: { name: '', value: '' } — all schema keys must exist in value
|
|
739
|
+
return Object.keys(schema).every((key) => key in obj);
|
|
639
740
|
}
|
|
640
741
|
function hasReadabilityScore(text, minScore) {
|
|
641
|
-
|
|
642
|
-
const words =
|
|
643
|
-
const sentences = text.split(/[.!?]+/).length;
|
|
644
|
-
const
|
|
742
|
+
const wordList = text.trim().split(/\s+/).filter(Boolean);
|
|
743
|
+
const words = wordList.length || 1;
|
|
744
|
+
const sentences = text.split(/[.!?]+/).filter((s) => s.trim().length > 0).length || 1;
|
|
745
|
+
const totalSyllables = wordList.reduce((sum, w) => sum + syllables(w), 0);
|
|
746
|
+
const score = 206.835 - 1.015 * (words / sentences) - 84.6 * (totalSyllables / words);
|
|
645
747
|
return score >= minScore;
|
|
646
748
|
}
|
|
647
749
|
function syllables(word) {
|
|
@@ -654,28 +756,402 @@ function syllables(word) {
|
|
|
654
756
|
.trim()
|
|
655
757
|
.split(/\s+/).length;
|
|
656
758
|
}
|
|
759
|
+
/**
|
|
760
|
+
* Keyword-frequency language detector supporting 12 languages.
|
|
761
|
+
* **Fast and approximate** — detects the most common languages reliably
|
|
762
|
+
* but may struggle with short texts or closely related languages.
|
|
763
|
+
* Use {@link containsLanguageAsync} for reliable detection of any language.
|
|
764
|
+
*/
|
|
657
765
|
function containsLanguage(text, language) {
|
|
658
|
-
// This is a simplified implementation
|
|
659
|
-
// In a real app, you'd use a language detection library
|
|
660
766
|
const languageKeywords = {
|
|
661
|
-
en: [
|
|
662
|
-
|
|
663
|
-
|
|
767
|
+
en: [
|
|
768
|
+
"the",
|
|
769
|
+
"and",
|
|
770
|
+
"you",
|
|
771
|
+
"that",
|
|
772
|
+
"was",
|
|
773
|
+
"for",
|
|
774
|
+
"are",
|
|
775
|
+
"with",
|
|
776
|
+
"have",
|
|
777
|
+
"this",
|
|
778
|
+
"from",
|
|
779
|
+
"they",
|
|
780
|
+
"will",
|
|
781
|
+
"would",
|
|
782
|
+
"been",
|
|
783
|
+
"their",
|
|
784
|
+
],
|
|
785
|
+
es: [
|
|
786
|
+
"el",
|
|
787
|
+
"la",
|
|
788
|
+
"los",
|
|
789
|
+
"las",
|
|
790
|
+
"de",
|
|
791
|
+
"que",
|
|
792
|
+
"y",
|
|
793
|
+
"en",
|
|
794
|
+
"es",
|
|
795
|
+
"por",
|
|
796
|
+
"para",
|
|
797
|
+
"con",
|
|
798
|
+
"una",
|
|
799
|
+
"como",
|
|
800
|
+
"pero",
|
|
801
|
+
"también",
|
|
802
|
+
],
|
|
803
|
+
fr: [
|
|
804
|
+
"le",
|
|
805
|
+
"la",
|
|
806
|
+
"les",
|
|
807
|
+
"de",
|
|
808
|
+
"et",
|
|
809
|
+
"à",
|
|
810
|
+
"un",
|
|
811
|
+
"une",
|
|
812
|
+
"du",
|
|
813
|
+
"des",
|
|
814
|
+
"est",
|
|
815
|
+
"que",
|
|
816
|
+
"dans",
|
|
817
|
+
"pour",
|
|
818
|
+
"sur",
|
|
819
|
+
"avec",
|
|
820
|
+
],
|
|
821
|
+
de: [
|
|
822
|
+
"der",
|
|
823
|
+
"die",
|
|
824
|
+
"das",
|
|
825
|
+
"und",
|
|
826
|
+
"ist",
|
|
827
|
+
"ich",
|
|
828
|
+
"nicht",
|
|
829
|
+
"mit",
|
|
830
|
+
"sie",
|
|
831
|
+
"ein",
|
|
832
|
+
"eine",
|
|
833
|
+
"von",
|
|
834
|
+
"zu",
|
|
835
|
+
"auf",
|
|
836
|
+
"auch",
|
|
837
|
+
"dem",
|
|
838
|
+
],
|
|
839
|
+
it: [
|
|
840
|
+
"il",
|
|
841
|
+
"di",
|
|
842
|
+
"che",
|
|
843
|
+
"non",
|
|
844
|
+
"si",
|
|
845
|
+
"per",
|
|
846
|
+
"del",
|
|
847
|
+
"un",
|
|
848
|
+
"una",
|
|
849
|
+
"con",
|
|
850
|
+
"sono",
|
|
851
|
+
"nel",
|
|
852
|
+
"una",
|
|
853
|
+
"questo",
|
|
854
|
+
"come",
|
|
855
|
+
],
|
|
856
|
+
pt: [
|
|
857
|
+
"de",
|
|
858
|
+
"que",
|
|
859
|
+
"do",
|
|
860
|
+
"da",
|
|
861
|
+
"em",
|
|
862
|
+
"um",
|
|
863
|
+
"para",
|
|
864
|
+
"com",
|
|
865
|
+
"uma",
|
|
866
|
+
"os",
|
|
867
|
+
"as",
|
|
868
|
+
"não",
|
|
869
|
+
"mas",
|
|
870
|
+
"por",
|
|
871
|
+
"mais",
|
|
872
|
+
],
|
|
873
|
+
nl: [
|
|
874
|
+
"de",
|
|
875
|
+
"het",
|
|
876
|
+
"een",
|
|
877
|
+
"van",
|
|
878
|
+
"en",
|
|
879
|
+
"in",
|
|
880
|
+
"is",
|
|
881
|
+
"dat",
|
|
882
|
+
"op",
|
|
883
|
+
"te",
|
|
884
|
+
"zijn",
|
|
885
|
+
"niet",
|
|
886
|
+
"ook",
|
|
887
|
+
"met",
|
|
888
|
+
"voor",
|
|
889
|
+
],
|
|
890
|
+
ru: [
|
|
891
|
+
"и",
|
|
892
|
+
"в",
|
|
893
|
+
"не",
|
|
894
|
+
"на",
|
|
895
|
+
"я",
|
|
896
|
+
"что",
|
|
897
|
+
"с",
|
|
898
|
+
"по",
|
|
899
|
+
"это",
|
|
900
|
+
"как",
|
|
901
|
+
"но",
|
|
902
|
+
"он",
|
|
903
|
+
"она",
|
|
904
|
+
"мы",
|
|
905
|
+
"они",
|
|
906
|
+
],
|
|
907
|
+
zh: [
|
|
908
|
+
"的",
|
|
909
|
+
"了",
|
|
910
|
+
"是",
|
|
911
|
+
"在",
|
|
912
|
+
"我",
|
|
913
|
+
"有",
|
|
914
|
+
"和",
|
|
915
|
+
"就",
|
|
916
|
+
"不",
|
|
917
|
+
"都",
|
|
918
|
+
"也",
|
|
919
|
+
"很",
|
|
920
|
+
"会",
|
|
921
|
+
"这",
|
|
922
|
+
"他",
|
|
923
|
+
],
|
|
924
|
+
ja: [
|
|
925
|
+
"は",
|
|
926
|
+
"が",
|
|
927
|
+
"の",
|
|
928
|
+
"に",
|
|
929
|
+
"を",
|
|
930
|
+
"で",
|
|
931
|
+
"と",
|
|
932
|
+
"た",
|
|
933
|
+
"し",
|
|
934
|
+
"て",
|
|
935
|
+
"も",
|
|
936
|
+
"な",
|
|
937
|
+
"か",
|
|
938
|
+
"から",
|
|
939
|
+
"まで",
|
|
940
|
+
],
|
|
941
|
+
ko: [
|
|
942
|
+
"이",
|
|
943
|
+
"은",
|
|
944
|
+
"는",
|
|
945
|
+
"을",
|
|
946
|
+
"를",
|
|
947
|
+
"의",
|
|
948
|
+
"에",
|
|
949
|
+
"가",
|
|
950
|
+
"로",
|
|
951
|
+
"도",
|
|
952
|
+
"와",
|
|
953
|
+
"과",
|
|
954
|
+
"하",
|
|
955
|
+
"있",
|
|
956
|
+
"합",
|
|
957
|
+
],
|
|
958
|
+
ar: [
|
|
959
|
+
"في",
|
|
960
|
+
"من",
|
|
961
|
+
"على",
|
|
962
|
+
"إلى",
|
|
963
|
+
"هذا",
|
|
964
|
+
"مع",
|
|
965
|
+
"أن",
|
|
966
|
+
"هو",
|
|
967
|
+
"كان",
|
|
968
|
+
"كل",
|
|
969
|
+
"التي",
|
|
970
|
+
"الذي",
|
|
971
|
+
"عن",
|
|
972
|
+
"لا",
|
|
973
|
+
],
|
|
664
974
|
};
|
|
665
|
-
const
|
|
975
|
+
const lang = language.toLowerCase();
|
|
976
|
+
const keywords = languageKeywords[lang] || languageKeywords[lang.split("-")[0]] || [];
|
|
666
977
|
return keywords.some((keyword) => text.toLowerCase().includes(keyword));
|
|
667
978
|
}
|
|
979
|
+
/**
|
|
980
|
+
* Substring-based factual accuracy check. **Fast and approximate** — verifies
|
|
981
|
+
* each fact string appears in the text but cannot reason about meaning or
|
|
982
|
+
* paraphrasing. Use {@link hasFactualAccuracyAsync} for semantic accuracy.
|
|
983
|
+
*/
|
|
668
984
|
function hasFactualAccuracy(text, facts) {
|
|
669
|
-
|
|
670
|
-
return facts.every((fact) =>
|
|
985
|
+
const lower = text.toLowerCase();
|
|
986
|
+
return facts.every((fact) => lower.includes(fact.toLowerCase()));
|
|
671
987
|
}
|
|
672
988
|
function respondedWithinTime(startTime, maxMs) {
|
|
673
989
|
return Date.now() - startTime <= maxMs;
|
|
674
990
|
}
|
|
991
|
+
/**
|
|
992
|
+
* Blocklist-based toxicity check (~80 terms across 9 categories).
|
|
993
|
+
* **Fast and approximate** — catches explicit harmful language but has
|
|
994
|
+
* inherent gaps and context-blind false positives. Do NOT rely on this
|
|
995
|
+
* alone for production content safety gates; use {@link hasNoToxicityAsync}
|
|
996
|
+
* with an LLM for context-aware moderation.
|
|
997
|
+
*/
|
|
675
998
|
function hasNoToxicity(text) {
|
|
676
|
-
|
|
677
|
-
const
|
|
678
|
-
|
|
999
|
+
const lower = text.toLowerCase();
|
|
1000
|
+
const toxicTerms = [
|
|
1001
|
+
// Insults and derogatory attacks
|
|
1002
|
+
"idiot",
|
|
1003
|
+
"moron",
|
|
1004
|
+
"imbecile",
|
|
1005
|
+
"dumbass",
|
|
1006
|
+
"dumb ass",
|
|
1007
|
+
"jackass",
|
|
1008
|
+
"dimwit",
|
|
1009
|
+
"nitwit",
|
|
1010
|
+
"halfwit",
|
|
1011
|
+
"numbskull",
|
|
1012
|
+
"bonehead",
|
|
1013
|
+
"blockhead",
|
|
1014
|
+
"scumbag",
|
|
1015
|
+
"dirtbag",
|
|
1016
|
+
"slimeball",
|
|
1017
|
+
"lowlife",
|
|
1018
|
+
"piece of shit",
|
|
1019
|
+
"piece of crap",
|
|
1020
|
+
"piece of garbage",
|
|
1021
|
+
"piece of trash",
|
|
1022
|
+
"waste of space",
|
|
1023
|
+
"waste of oxygen",
|
|
1024
|
+
// Degradation directed at people
|
|
1025
|
+
"you're worthless",
|
|
1026
|
+
"you are worthless",
|
|
1027
|
+
"you're useless",
|
|
1028
|
+
"you are useless",
|
|
1029
|
+
"you're pathetic",
|
|
1030
|
+
"you are pathetic",
|
|
1031
|
+
"you're disgusting",
|
|
1032
|
+
"you are disgusting",
|
|
1033
|
+
"you're nothing",
|
|
1034
|
+
"you are nothing",
|
|
1035
|
+
"you're scum",
|
|
1036
|
+
"you're garbage",
|
|
1037
|
+
"you're trash",
|
|
1038
|
+
"you're a loser",
|
|
1039
|
+
"complete loser",
|
|
1040
|
+
"total loser",
|
|
1041
|
+
"absolute loser",
|
|
1042
|
+
// Violence and threats
|
|
1043
|
+
"kill you",
|
|
1044
|
+
"kill yourself",
|
|
1045
|
+
"i'll kill",
|
|
1046
|
+
"i will kill",
|
|
1047
|
+
"gonna kill",
|
|
1048
|
+
"going to kill",
|
|
1049
|
+
"murder you",
|
|
1050
|
+
"i'll murder",
|
|
1051
|
+
"i will murder",
|
|
1052
|
+
"want you dead",
|
|
1053
|
+
"want them dead",
|
|
1054
|
+
"you should die",
|
|
1055
|
+
"hope you die",
|
|
1056
|
+
"hope you rot",
|
|
1057
|
+
"hope you suffer",
|
|
1058
|
+
"shoot you",
|
|
1059
|
+
"stab you",
|
|
1060
|
+
"beat you up",
|
|
1061
|
+
"hurt you",
|
|
1062
|
+
"harm you",
|
|
1063
|
+
"i'll hurt",
|
|
1064
|
+
"i will hurt",
|
|
1065
|
+
"i'll destroy you",
|
|
1066
|
+
"i will destroy you",
|
|
1067
|
+
"i'll end you",
|
|
1068
|
+
"you're dead",
|
|
1069
|
+
"you're going to die",
|
|
1070
|
+
"i know where you live",
|
|
1071
|
+
"i'll find you",
|
|
1072
|
+
"i will find you",
|
|
1073
|
+
// Self-harm directed at others
|
|
1074
|
+
"go kill yourself",
|
|
1075
|
+
"kys",
|
|
1076
|
+
"end yourself",
|
|
1077
|
+
"off yourself",
|
|
1078
|
+
"just die",
|
|
1079
|
+
"better off dead",
|
|
1080
|
+
"world is better without you",
|
|
1081
|
+
"no one would miss you",
|
|
1082
|
+
"nobody would miss you",
|
|
1083
|
+
"do us a favor and die",
|
|
1084
|
+
// Dehumanization
|
|
1085
|
+
"not even human",
|
|
1086
|
+
"barely human",
|
|
1087
|
+
"subhuman",
|
|
1088
|
+
"less than human",
|
|
1089
|
+
"you're vermin",
|
|
1090
|
+
"you're a parasite",
|
|
1091
|
+
"deserve to die",
|
|
1092
|
+
"deserve to suffer",
|
|
1093
|
+
"deserve pain",
|
|
1094
|
+
"deserve misery",
|
|
1095
|
+
// Hate and rejection
|
|
1096
|
+
"i hate you",
|
|
1097
|
+
"everyone hates you",
|
|
1098
|
+
"they all hate you",
|
|
1099
|
+
"no one likes you",
|
|
1100
|
+
"nobody likes you",
|
|
1101
|
+
"nobody wants you",
|
|
1102
|
+
"nobody cares about you",
|
|
1103
|
+
"no one cares about you",
|
|
1104
|
+
"go to hell",
|
|
1105
|
+
"burn in hell",
|
|
1106
|
+
"rot in hell",
|
|
1107
|
+
"drop dead",
|
|
1108
|
+
"go die",
|
|
1109
|
+
"i hope you suffer",
|
|
1110
|
+
// Harassment and threats
|
|
1111
|
+
"i'll ruin you",
|
|
1112
|
+
"ruin your life",
|
|
1113
|
+
"make your life hell",
|
|
1114
|
+
"i'll expose you",
|
|
1115
|
+
"i'll dox you",
|
|
1116
|
+
"i will dox you",
|
|
1117
|
+
"doxxing",
|
|
1118
|
+
"i'll come for you",
|
|
1119
|
+
"you'll pay for this",
|
|
1120
|
+
// Profanity as direct attacks
|
|
1121
|
+
"fuck you",
|
|
1122
|
+
"fuck off",
|
|
1123
|
+
"go fuck yourself",
|
|
1124
|
+
"screw you",
|
|
1125
|
+
"shut the fuck up",
|
|
1126
|
+
"to hell with you",
|
|
1127
|
+
// Bullying
|
|
1128
|
+
"you're a joke",
|
|
1129
|
+
"you're a laughingstock",
|
|
1130
|
+
"everyone is laughing at you",
|
|
1131
|
+
"pathetic loser",
|
|
1132
|
+
// Appearance attacks
|
|
1133
|
+
"fat pig",
|
|
1134
|
+
"fat slob",
|
|
1135
|
+
"hideous freak",
|
|
1136
|
+
// Mental health weaponized
|
|
1137
|
+
"you're crazy",
|
|
1138
|
+
"you're insane",
|
|
1139
|
+
"you're a psycho",
|
|
1140
|
+
"you're delusional",
|
|
1141
|
+
"you're mental",
|
|
1142
|
+
"you belong in an asylum",
|
|
1143
|
+
"you're a lunatic",
|
|
1144
|
+
// Explicit profanity used as insults
|
|
1145
|
+
"bastard",
|
|
1146
|
+
"bitch",
|
|
1147
|
+
"cunt",
|
|
1148
|
+
"asshole",
|
|
1149
|
+
"dipshit",
|
|
1150
|
+
"douchebag",
|
|
1151
|
+
"motherfucker",
|
|
1152
|
+
"fucktard",
|
|
1153
|
+
];
|
|
1154
|
+
return !toxicTerms.some((term) => lower.includes(term));
|
|
679
1155
|
}
|
|
680
1156
|
function followsInstructions(text, instructions) {
|
|
681
1157
|
return instructions.every((instruction) => {
|
|
@@ -688,16 +1164,211 @@ function followsInstructions(text, instructions) {
|
|
|
688
1164
|
function containsAllRequiredFields(obj, requiredFields) {
|
|
689
1165
|
return requiredFields.every((field) => obj && typeof obj === "object" && field in obj);
|
|
690
1166
|
}
|
|
1167
|
+
let _assertionLLMConfig = null;
|
|
1168
|
+
function configureAssertions(config) {
|
|
1169
|
+
_assertionLLMConfig = config;
|
|
1170
|
+
}
|
|
1171
|
+
function getAssertionConfig() {
|
|
1172
|
+
return _assertionLLMConfig;
|
|
1173
|
+
}
|
|
1174
|
+
async function callAssertionLLM(prompt, config) {
|
|
1175
|
+
const cfg = config ?? _assertionLLMConfig;
|
|
1176
|
+
if (!cfg) {
|
|
1177
|
+
throw new Error("No LLM config set. Call configureAssertions({ provider, apiKey }) first, or pass a config as the last argument.");
|
|
1178
|
+
}
|
|
1179
|
+
if (cfg.provider === "openai") {
|
|
1180
|
+
const baseUrl = cfg.baseUrl ?? "https://api.openai.com";
|
|
1181
|
+
const model = cfg.model ?? "gpt-4o-mini";
|
|
1182
|
+
const res = await fetch(`${baseUrl}/v1/chat/completions`, {
|
|
1183
|
+
method: "POST",
|
|
1184
|
+
headers: {
|
|
1185
|
+
"Content-Type": "application/json",
|
|
1186
|
+
Authorization: `Bearer ${cfg.apiKey}`,
|
|
1187
|
+
},
|
|
1188
|
+
body: JSON.stringify({
|
|
1189
|
+
model,
|
|
1190
|
+
messages: [{ role: "user", content: prompt }],
|
|
1191
|
+
max_tokens: 10,
|
|
1192
|
+
temperature: 0,
|
|
1193
|
+
}),
|
|
1194
|
+
});
|
|
1195
|
+
if (!res.ok) {
|
|
1196
|
+
throw new Error(`OpenAI API error ${res.status}: ${await res.text()}`);
|
|
1197
|
+
}
|
|
1198
|
+
const data = (await res.json());
|
|
1199
|
+
return data.choices[0]?.message?.content?.trim().toLowerCase() ?? "";
|
|
1200
|
+
}
|
|
1201
|
+
if (cfg.provider === "anthropic") {
|
|
1202
|
+
const baseUrl = cfg.baseUrl ?? "https://api.anthropic.com";
|
|
1203
|
+
const model = cfg.model ?? "claude-3-haiku-20240307";
|
|
1204
|
+
const res = await fetch(`${baseUrl}/v1/messages`, {
|
|
1205
|
+
method: "POST",
|
|
1206
|
+
headers: {
|
|
1207
|
+
"Content-Type": "application/json",
|
|
1208
|
+
"x-api-key": cfg.apiKey,
|
|
1209
|
+
"anthropic-version": "2023-06-01",
|
|
1210
|
+
},
|
|
1211
|
+
body: JSON.stringify({
|
|
1212
|
+
model,
|
|
1213
|
+
max_tokens: 10,
|
|
1214
|
+
messages: [{ role: "user", content: prompt }],
|
|
1215
|
+
}),
|
|
1216
|
+
});
|
|
1217
|
+
if (!res.ok) {
|
|
1218
|
+
throw new Error(`Anthropic API error ${res.status}: ${await res.text()}`);
|
|
1219
|
+
}
|
|
1220
|
+
const data = (await res.json());
|
|
1221
|
+
return data.content[0]?.text?.trim().toLowerCase() ?? "";
|
|
1222
|
+
}
|
|
1223
|
+
throw new Error(`Unsupported provider: "${cfg.provider}". Use "openai" or "anthropic".`);
|
|
1224
|
+
}
|
|
1225
|
+
// ============================================================================
|
|
1226
|
+
// LLM-BACKED ASYNC ASSERTION FUNCTIONS
|
|
1227
|
+
// ============================================================================
|
|
1228
|
+
/**
|
|
1229
|
+
* LLM-backed sentiment check. **Slow and accurate** — uses an LLM to
|
|
1230
|
+
* classify sentiment with full context awareness. Requires
|
|
1231
|
+
* {@link configureAssertions} or an inline `config` argument.
|
|
1232
|
+
* Falls back gracefully with a clear error if no API key is configured.
|
|
1233
|
+
*/
|
|
1234
|
+
async function hasSentimentAsync(text, expected, config) {
|
|
1235
|
+
const prompt = `Classify the sentiment of the following text. Reply with exactly one word — positive, negative, or neutral — and nothing else.\n\nText: "${text}"`;
|
|
1236
|
+
const result = await callAssertionLLM(prompt, config);
|
|
1237
|
+
return result.replace(/[^a-z]/g, "") === expected;
|
|
1238
|
+
}
|
|
1239
|
+
/**
|
|
1240
|
+
* LLM-backed toxicity check. **Slow and accurate** — context-aware, handles
|
|
1241
|
+
* sarcasm, implicit threats, and culturally specific harmful content that
|
|
1242
|
+
* blocklists miss. Recommended for production content safety gates.
|
|
1243
|
+
*/
|
|
1244
|
+
async function hasNoToxicityAsync(text, config) {
|
|
1245
|
+
const prompt = `Does the following text contain toxic, harmful, hateful, threatening, or abusive content? Reply with only "yes" or "no".\n\nText: "${text}"`;
|
|
1246
|
+
const result = await callAssertionLLM(prompt, config);
|
|
1247
|
+
return result.replace(/[^a-z]/g, "") === "no";
|
|
1248
|
+
}
|
|
1249
|
+
async function containsLanguageAsync(text, language, config) {
|
|
1250
|
+
const prompt = `Is the following text primarily written in ${language}? Reply with only "yes" or "no".\n\nText: "${text}"`;
|
|
1251
|
+
const result = await callAssertionLLM(prompt, config);
|
|
1252
|
+
return result.replace(/[^a-z]/g, "") === "yes";
|
|
1253
|
+
}
|
|
1254
|
+
async function hasValidCodeSyntaxAsync(code, language, config) {
|
|
1255
|
+
const prompt = `Is the following ${language} code free of syntax errors? Reply with only "yes" or "no".\n\nCode:\n\`\`\`${language}\n${code}\n\`\`\``;
|
|
1256
|
+
const result = await callAssertionLLM(prompt, config);
|
|
1257
|
+
return result.replace(/[^a-z]/g, "") === "yes";
|
|
1258
|
+
}
|
|
1259
|
+
async function hasFactualAccuracyAsync(text, facts, config) {
|
|
1260
|
+
const factList = facts.map((f, i) => `${i + 1}. ${f}`).join("\n");
|
|
1261
|
+
const prompt = `Does the following text accurately convey all of these facts without contradicting or omitting any?\n\nFacts:\n${factList}\n\nText: "${text}"\n\nReply with only "yes" or "no".`;
|
|
1262
|
+
const result = await callAssertionLLM(prompt, config);
|
|
1263
|
+
return result.replace(/[^a-z]/g, "") === "yes";
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* LLM-backed hallucination check. **Slow and accurate** — detects fabricated
|
|
1267
|
+
* claims even when they are paraphrased or contradict facts indirectly.
|
|
1268
|
+
*/
|
|
1269
|
+
async function hasNoHallucinationsAsync(text, groundTruth, config) {
|
|
1270
|
+
const truthList = groundTruth.map((f, i) => `${i + 1}. ${f}`).join("\n");
|
|
1271
|
+
const prompt = `Does the following text stay consistent with the ground truth facts below, without introducing fabricated or hallucinated claims?\n\nGround truth:\n${truthList}\n\nText: "${text}"\n\nReply with only "yes" or "no".`;
|
|
1272
|
+
const result = await callAssertionLLM(prompt, config);
|
|
1273
|
+
return result.replace(/[^a-z]/g, "") === "yes";
|
|
1274
|
+
}
|
|
691
1275
|
function hasValidCodeSyntax(code, language) {
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
if (language === "json")
|
|
1276
|
+
const lang = language.toLowerCase();
|
|
1277
|
+
if (lang === "json") {
|
|
1278
|
+
try {
|
|
696
1279
|
JSON.parse(code);
|
|
697
|
-
|
|
698
|
-
|
|
1280
|
+
return true;
|
|
1281
|
+
}
|
|
1282
|
+
catch {
|
|
1283
|
+
return false;
|
|
1284
|
+
}
|
|
699
1285
|
}
|
|
700
|
-
|
|
701
|
-
|
|
1286
|
+
// Bracket, brace, and parenthesis balance check with string/comment awareness.
|
|
1287
|
+
// Catches unmatched delimiters in JS, TS, Python, Java, C, Go, Rust, and most languages.
|
|
1288
|
+
// Template literals (backtick strings) are treated as opaque — their entire
|
|
1289
|
+
// content including ${...} expressions is skipped, so braces inside them
|
|
1290
|
+
// do not affect the balance count. This is intentional and correct.
|
|
1291
|
+
// Use hasValidCodeSyntaxAsync for deeper semantic analysis.
|
|
1292
|
+
const stack = [];
|
|
1293
|
+
const pairs = { ")": "(", "]": "[", "}": "{" };
|
|
1294
|
+
const opens = new Set(["(", "[", "{"]);
|
|
1295
|
+
const closes = new Set([")", "]", "}"]);
|
|
1296
|
+
const isPythonLike = lang === "python" || lang === "py" || lang === "ruby" || lang === "rb";
|
|
1297
|
+
const isJSLike = lang === "javascript" ||
|
|
1298
|
+
lang === "js" ||
|
|
1299
|
+
lang === "typescript" ||
|
|
1300
|
+
lang === "ts";
|
|
1301
|
+
let inSingleQuote = false;
|
|
1302
|
+
let inDoubleQuote = false;
|
|
1303
|
+
let inTemplateLiteral = false;
|
|
1304
|
+
let inLineComment = false;
|
|
1305
|
+
let inBlockComment = false;
|
|
1306
|
+
for (let i = 0; i < code.length; i++) {
|
|
1307
|
+
const ch = code[i];
|
|
1308
|
+
const next = code[i + 1] ?? "";
|
|
1309
|
+
const prev = code[i - 1] ?? "";
|
|
1310
|
+
if (inLineComment) {
|
|
1311
|
+
if (ch === "\n")
|
|
1312
|
+
inLineComment = false;
|
|
1313
|
+
continue;
|
|
1314
|
+
}
|
|
1315
|
+
if (inBlockComment) {
|
|
1316
|
+
if (ch === "*" && next === "/") {
|
|
1317
|
+
inBlockComment = false;
|
|
1318
|
+
i++;
|
|
1319
|
+
}
|
|
1320
|
+
continue;
|
|
1321
|
+
}
|
|
1322
|
+
if (inSingleQuote) {
|
|
1323
|
+
if (ch === "'" && prev !== "\\")
|
|
1324
|
+
inSingleQuote = false;
|
|
1325
|
+
continue;
|
|
1326
|
+
}
|
|
1327
|
+
if (inDoubleQuote) {
|
|
1328
|
+
if (ch === '"' && prev !== "\\")
|
|
1329
|
+
inDoubleQuote = false;
|
|
1330
|
+
continue;
|
|
1331
|
+
}
|
|
1332
|
+
if (inTemplateLiteral) {
|
|
1333
|
+
if (ch === "`" && prev !== "\\")
|
|
1334
|
+
inTemplateLiteral = false;
|
|
1335
|
+
continue;
|
|
1336
|
+
}
|
|
1337
|
+
if (ch === "/" && next === "/") {
|
|
1338
|
+
inLineComment = true;
|
|
1339
|
+
i++;
|
|
1340
|
+
continue;
|
|
1341
|
+
}
|
|
1342
|
+
if (ch === "/" && next === "*") {
|
|
1343
|
+
inBlockComment = true;
|
|
1344
|
+
i++;
|
|
1345
|
+
continue;
|
|
1346
|
+
}
|
|
1347
|
+
if (isPythonLike && ch === "#") {
|
|
1348
|
+
inLineComment = true;
|
|
1349
|
+
continue;
|
|
1350
|
+
}
|
|
1351
|
+
if (ch === "'") {
|
|
1352
|
+
inSingleQuote = true;
|
|
1353
|
+
continue;
|
|
1354
|
+
}
|
|
1355
|
+
if (ch === '"') {
|
|
1356
|
+
inDoubleQuote = true;
|
|
1357
|
+
continue;
|
|
1358
|
+
}
|
|
1359
|
+
if (isJSLike && ch === "`") {
|
|
1360
|
+
inTemplateLiteral = true;
|
|
1361
|
+
continue;
|
|
1362
|
+
}
|
|
1363
|
+
if (opens.has(ch)) {
|
|
1364
|
+
stack.push(ch);
|
|
1365
|
+
}
|
|
1366
|
+
else if (closes.has(ch)) {
|
|
1367
|
+
if (stack.length === 0 || stack[stack.length - 1] !== pairs[ch]) {
|
|
1368
|
+
return false;
|
|
1369
|
+
}
|
|
1370
|
+
stack.pop();
|
|
1371
|
+
}
|
|
702
1372
|
}
|
|
1373
|
+
return stack.length === 0;
|
|
703
1374
|
}
|
package/dist/export.d.ts
CHANGED
|
@@ -140,7 +140,7 @@ export declare function exportData(client: AIEvalClient, options: ExportOptions)
|
|
|
140
140
|
* console.log(`Imported ${result.summary.imported} items`);
|
|
141
141
|
* ```
|
|
142
142
|
*/
|
|
143
|
-
export declare function importData(client: AIEvalClient, data: ExportData, options
|
|
143
|
+
export declare function importData(client: AIEvalClient, data: ExportData, options?: ImportOptions): Promise<ImportResult>;
|
|
144
144
|
/**
|
|
145
145
|
* Export data to JSON file
|
|
146
146
|
*
|
package/dist/export.js
CHANGED
|
@@ -136,7 +136,7 @@ async function exportData(client, options) {
|
|
|
136
136
|
* console.log(`Imported ${result.summary.imported} items`);
|
|
137
137
|
* ```
|
|
138
138
|
*/
|
|
139
|
-
async function importData(client, data, options) {
|
|
139
|
+
async function importData(client, data, options = {}) {
|
|
140
140
|
const result = {
|
|
141
141
|
summary: { total: 0, imported: 0, skipped: 0, failed: 0 },
|
|
142
142
|
details: {},
|
package/dist/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export { AIEvalClient } from "./client";
|
|
|
10
10
|
import { AuthenticationError, EvalGateError, NetworkError, RateLimitError, SDKError } from "./errors";
|
|
11
11
|
export { EvalGateError, RateLimitError, AuthenticationError, SDKError as ValidationError, // Using SDKError as ValidationError for backward compatibility
|
|
12
12
|
NetworkError, };
|
|
13
|
-
export { containsAllRequiredFields, containsJSON, containsKeywords, containsLanguage, expect, followsInstructions, hasFactualAccuracy, hasLength, hasNoHallucinations, hasNoToxicity, hasPII, hasReadabilityScore, hasSentiment, hasValidCodeSyntax, isValidEmail, isValidURL, matchesPattern, matchesSchema, notContainsPII, respondedWithinTime, similarTo, withinRange, } from "./assertions";
|
|
13
|
+
export { type AssertionLLMConfig, configureAssertions, containsAllRequiredFields, containsJSON, containsKeywords, containsLanguage, containsLanguageAsync, expect, followsInstructions, getAssertionConfig, hasFactualAccuracy, hasFactualAccuracyAsync, hasLength, hasNoHallucinations, hasNoHallucinationsAsync, hasNoToxicity, hasNoToxicityAsync, hasPII, hasReadabilityScore, hasSentiment, hasSentimentAsync, hasValidCodeSyntax, hasValidCodeSyntaxAsync, isValidEmail, isValidURL, matchesPattern, matchesSchema, notContainsPII, respondedWithinTime, similarTo, withinRange, } from "./assertions";
|
|
14
14
|
import { createContext, EvalContext, getCurrentContext, withContext } from "./context";
|
|
15
15
|
export { createContext, getCurrentContext as getContext, withContext, EvalContext as ContextManager, };
|
|
16
16
|
export { cloneContext, mergeContexts, validateContext, } from "./runtime/context";
|
package/dist/index.js
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* @packageDocumentation
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.
|
|
12
|
-
exports.WorkflowTracer = exports.traceWorkflowStep = exports.traceLangChainAgent = exports.traceCrewAI = exports.traceAutoGen = exports.createWorkflowTracer = exports.EvaluationTemplates = exports.streamEvaluation = exports.RateLimiter = exports.batchRead = exports.batchProcess = exports.REPORT_SCHEMA_VERSION = exports.GATE_EXIT = exports.GATE_CATEGORY = exports.ARTIFACTS = exports.PaginatedIterator = exports.encodeCursor = exports.decodeCursor = exports.createPaginatedIterator = exports.autoPaginate = exports.extendExpectWithToPassGate = exports.Logger = exports.openAIChatEval = exports.traceOpenAI = exports.traceAnthropic = exports.runCheck = exports.parseArgs = exports.EXIT = exports.RequestCache = exports.CacheTTL = exports.RequestBatcher = exports.importData = exports.exportData = exports.compareSnapshots = exports.saveSnapshot = exports.compareWithSnapshot = exports.snapshot = exports.TestSuite = exports.createTestSuite = void 0;
|
|
11
|
+
exports.defaultLocalExecutor = exports.createLocalExecutor = exports.evalai = exports.defineSuite = exports.defineEval = exports.createResult = exports.createEvalContext = exports.validateContext = exports.mergeContexts = exports.cloneContext = exports.ContextManager = exports.withContext = exports.getContext = exports.createContext = exports.withinRange = exports.similarTo = exports.respondedWithinTime = exports.notContainsPII = exports.matchesSchema = exports.matchesPattern = exports.isValidURL = exports.isValidEmail = exports.hasValidCodeSyntaxAsync = exports.hasValidCodeSyntax = exports.hasSentimentAsync = exports.hasSentiment = exports.hasReadabilityScore = exports.hasPII = exports.hasNoToxicityAsync = exports.hasNoToxicity = exports.hasNoHallucinationsAsync = exports.hasNoHallucinations = exports.hasLength = exports.hasFactualAccuracyAsync = exports.hasFactualAccuracy = exports.getAssertionConfig = exports.followsInstructions = exports.expect = exports.containsLanguageAsync = exports.containsLanguage = exports.containsKeywords = exports.containsJSON = exports.containsAllRequiredFields = exports.configureAssertions = exports.NetworkError = exports.ValidationError = exports.AuthenticationError = exports.RateLimitError = exports.EvalGateError = exports.AIEvalClient = void 0;
|
|
12
|
+
exports.WorkflowTracer = exports.traceWorkflowStep = exports.traceLangChainAgent = exports.traceCrewAI = exports.traceAutoGen = exports.createWorkflowTracer = exports.EvaluationTemplates = exports.streamEvaluation = exports.RateLimiter = exports.batchRead = exports.batchProcess = exports.REPORT_SCHEMA_VERSION = exports.GATE_EXIT = exports.GATE_CATEGORY = exports.ARTIFACTS = exports.PaginatedIterator = exports.encodeCursor = exports.decodeCursor = exports.createPaginatedIterator = exports.autoPaginate = exports.extendExpectWithToPassGate = exports.Logger = exports.openAIChatEval = exports.traceOpenAI = exports.traceAnthropic = exports.runCheck = exports.parseArgs = exports.EXIT = exports.RequestCache = exports.CacheTTL = exports.RequestBatcher = exports.importData = exports.exportData = exports.compareSnapshots = exports.saveSnapshot = exports.compareWithSnapshot = exports.snapshot = exports.TestSuite = exports.createTestSuite = exports.SpecRegistrationError = exports.SpecExecutionError = exports.RuntimeError = exports.EvalRuntimeError = exports.setActiveRuntime = exports.getActiveRuntime = exports.disposeActiveRuntime = exports.createEvalRuntime = void 0;
|
|
13
13
|
// Main SDK exports
|
|
14
14
|
var client_1 = require("./client");
|
|
15
15
|
Object.defineProperty(exports, "AIEvalClient", { enumerable: true, get: function () { return client_1.AIEvalClient; } });
|
|
@@ -22,20 +22,30 @@ Object.defineProperty(exports, "RateLimitError", { enumerable: true, get: functi
|
|
|
22
22
|
Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function () { return errors_1.SDKError; } });
|
|
23
23
|
// Enhanced assertions (Tier 1.3)
|
|
24
24
|
var assertions_1 = require("./assertions");
|
|
25
|
+
// LLM config
|
|
26
|
+
Object.defineProperty(exports, "configureAssertions", { enumerable: true, get: function () { return assertions_1.configureAssertions; } });
|
|
25
27
|
Object.defineProperty(exports, "containsAllRequiredFields", { enumerable: true, get: function () { return assertions_1.containsAllRequiredFields; } });
|
|
26
28
|
Object.defineProperty(exports, "containsJSON", { enumerable: true, get: function () { return assertions_1.containsJSON; } });
|
|
27
29
|
Object.defineProperty(exports, "containsKeywords", { enumerable: true, get: function () { return assertions_1.containsKeywords; } });
|
|
28
30
|
Object.defineProperty(exports, "containsLanguage", { enumerable: true, get: function () { return assertions_1.containsLanguage; } });
|
|
31
|
+
// LLM-backed async variants
|
|
32
|
+
Object.defineProperty(exports, "containsLanguageAsync", { enumerable: true, get: function () { return assertions_1.containsLanguageAsync; } });
|
|
29
33
|
Object.defineProperty(exports, "expect", { enumerable: true, get: function () { return assertions_1.expect; } });
|
|
30
34
|
Object.defineProperty(exports, "followsInstructions", { enumerable: true, get: function () { return assertions_1.followsInstructions; } });
|
|
35
|
+
Object.defineProperty(exports, "getAssertionConfig", { enumerable: true, get: function () { return assertions_1.getAssertionConfig; } });
|
|
31
36
|
Object.defineProperty(exports, "hasFactualAccuracy", { enumerable: true, get: function () { return assertions_1.hasFactualAccuracy; } });
|
|
37
|
+
Object.defineProperty(exports, "hasFactualAccuracyAsync", { enumerable: true, get: function () { return assertions_1.hasFactualAccuracyAsync; } });
|
|
32
38
|
Object.defineProperty(exports, "hasLength", { enumerable: true, get: function () { return assertions_1.hasLength; } });
|
|
33
39
|
Object.defineProperty(exports, "hasNoHallucinations", { enumerable: true, get: function () { return assertions_1.hasNoHallucinations; } });
|
|
40
|
+
Object.defineProperty(exports, "hasNoHallucinationsAsync", { enumerable: true, get: function () { return assertions_1.hasNoHallucinationsAsync; } });
|
|
34
41
|
Object.defineProperty(exports, "hasNoToxicity", { enumerable: true, get: function () { return assertions_1.hasNoToxicity; } });
|
|
42
|
+
Object.defineProperty(exports, "hasNoToxicityAsync", { enumerable: true, get: function () { return assertions_1.hasNoToxicityAsync; } });
|
|
35
43
|
Object.defineProperty(exports, "hasPII", { enumerable: true, get: function () { return assertions_1.hasPII; } });
|
|
36
44
|
Object.defineProperty(exports, "hasReadabilityScore", { enumerable: true, get: function () { return assertions_1.hasReadabilityScore; } });
|
|
37
45
|
Object.defineProperty(exports, "hasSentiment", { enumerable: true, get: function () { return assertions_1.hasSentiment; } });
|
|
46
|
+
Object.defineProperty(exports, "hasSentimentAsync", { enumerable: true, get: function () { return assertions_1.hasSentimentAsync; } });
|
|
38
47
|
Object.defineProperty(exports, "hasValidCodeSyntax", { enumerable: true, get: function () { return assertions_1.hasValidCodeSyntax; } });
|
|
48
|
+
Object.defineProperty(exports, "hasValidCodeSyntaxAsync", { enumerable: true, get: function () { return assertions_1.hasValidCodeSyntaxAsync; } });
|
|
39
49
|
Object.defineProperty(exports, "isValidEmail", { enumerable: true, get: function () { return assertions_1.isValidEmail; } });
|
|
40
50
|
Object.defineProperty(exports, "isValidURL", { enumerable: true, get: function () { return assertions_1.isValidURL; } });
|
|
41
51
|
Object.defineProperty(exports, "matchesPattern", { enumerable: true, get: function () { return assertions_1.matchesPattern; } });
|
package/dist/snapshot.d.ts
CHANGED
|
@@ -73,7 +73,7 @@ export declare class SnapshotManager {
|
|
|
73
73
|
* await manager.save('haiku-test', output, { tags: ['poetry'] });
|
|
74
74
|
* ```
|
|
75
75
|
*/
|
|
76
|
-
save(name: string, output:
|
|
76
|
+
save(name: string, output: unknown, options?: {
|
|
77
77
|
tags?: string[];
|
|
78
78
|
metadata?: Record<string, unknown>;
|
|
79
79
|
overwrite?: boolean;
|
|
@@ -99,7 +99,7 @@ export declare class SnapshotManager {
|
|
|
99
99
|
* }
|
|
100
100
|
* ```
|
|
101
101
|
*/
|
|
102
|
-
compare(name: string, currentOutput:
|
|
102
|
+
compare(name: string, currentOutput: unknown): Promise<SnapshotComparison>;
|
|
103
103
|
/**
|
|
104
104
|
* List all snapshots
|
|
105
105
|
*
|
|
@@ -127,7 +127,7 @@ export declare class SnapshotManager {
|
|
|
127
127
|
* await manager.update('haiku-test', newOutput);
|
|
128
128
|
* ```
|
|
129
129
|
*/
|
|
130
|
-
update(name: string, output:
|
|
130
|
+
update(name: string, output: unknown): Promise<SnapshotData>;
|
|
131
131
|
}
|
|
132
132
|
/**
|
|
133
133
|
* Save a snapshot (convenience function)
|
|
@@ -138,7 +138,7 @@ export declare class SnapshotManager {
|
|
|
138
138
|
* await snapshot('haiku-test', output);
|
|
139
139
|
* ```
|
|
140
140
|
*/
|
|
141
|
-
export declare function snapshot(name: string, output:
|
|
141
|
+
export declare function snapshot(name: string, output: unknown, options?: {
|
|
142
142
|
tags?: string[];
|
|
143
143
|
metadata?: Record<string, unknown>;
|
|
144
144
|
overwrite?: boolean;
|
|
@@ -165,7 +165,7 @@ export declare function loadSnapshot(name: string, dir?: string): Promise<Snapsh
|
|
|
165
165
|
* }
|
|
166
166
|
* ```
|
|
167
167
|
*/
|
|
168
|
-
export declare function compareWithSnapshot(name: string, currentOutput:
|
|
168
|
+
export declare function compareWithSnapshot(name: string, currentOutput: unknown, dir?: string): Promise<SnapshotComparison>;
|
|
169
169
|
/**
|
|
170
170
|
* Delete a snapshot (convenience function)
|
|
171
171
|
*/
|
package/dist/snapshot.js
CHANGED
|
@@ -130,12 +130,13 @@ class SnapshotManager {
|
|
|
130
130
|
if (!options?.overwrite && fs.existsSync(filePath)) {
|
|
131
131
|
throw new Error(`Snapshot '${name}' already exists. Use overwrite: true to update.`);
|
|
132
132
|
}
|
|
133
|
+
const serialized = typeof output === "string" ? output : JSON.stringify(output);
|
|
133
134
|
const snapshotData = {
|
|
134
|
-
output,
|
|
135
|
+
output: serialized,
|
|
135
136
|
metadata: {
|
|
136
137
|
name,
|
|
137
138
|
createdAt: new Date().toISOString(),
|
|
138
|
-
hash: this.generateHash(
|
|
139
|
+
hash: this.generateHash(serialized),
|
|
139
140
|
tags: options?.tags,
|
|
140
141
|
metadata: options?.metadata,
|
|
141
142
|
},
|
|
@@ -174,11 +175,14 @@ class SnapshotManager {
|
|
|
174
175
|
async compare(name, currentOutput) {
|
|
175
176
|
const snapshot = await this.load(name);
|
|
176
177
|
const original = snapshot.output;
|
|
178
|
+
const currentOutputStr = typeof currentOutput === "string"
|
|
179
|
+
? currentOutput
|
|
180
|
+
: JSON.stringify(currentOutput);
|
|
177
181
|
// Exact match check
|
|
178
|
-
const exactMatch = original ===
|
|
182
|
+
const exactMatch = original === currentOutputStr;
|
|
179
183
|
// Calculate similarity (simple line-based diff)
|
|
180
184
|
const originalLines = original.split("\n");
|
|
181
|
-
const currentLines =
|
|
185
|
+
const currentLines = currentOutputStr.split("\n");
|
|
182
186
|
const differences = [];
|
|
183
187
|
const maxLines = Math.max(originalLines.length, currentLines.length);
|
|
184
188
|
let matchingLines = 0;
|
|
@@ -198,7 +202,7 @@ class SnapshotManager {
|
|
|
198
202
|
similarity,
|
|
199
203
|
differences,
|
|
200
204
|
original,
|
|
201
|
-
current:
|
|
205
|
+
current: currentOutputStr,
|
|
202
206
|
};
|
|
203
207
|
}
|
|
204
208
|
/**
|
package/dist/types.d.ts
CHANGED
|
@@ -38,8 +38,13 @@ export interface ClientConfig {
|
|
|
38
38
|
keepAlive?: boolean;
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
|
-
* Evaluation template
|
|
42
|
-
*
|
|
41
|
+
* Evaluation template identifier constants for use with the EvalAI platform API.
|
|
42
|
+
*
|
|
43
|
+
* These are **string identifiers** (e.g. `"unit-testing"`) that reference
|
|
44
|
+
* pre-built templates on the platform — not template definition objects.
|
|
45
|
+
* Pass these values to `evaluations.create({ templateId: EvaluationTemplates.UNIT_TESTING })`
|
|
46
|
+
* to spin up a pre-configured evaluation. For custom criteria, thresholds, and
|
|
47
|
+
* test cases, build your own evaluation config instead.
|
|
43
48
|
*/
|
|
44
49
|
export declare const EvaluationTemplates: {
|
|
45
50
|
readonly UNIT_TESTING: "unit-testing";
|
package/dist/types.js
CHANGED
|
@@ -2,8 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SDKError = exports.EvaluationTemplates = void 0;
|
|
4
4
|
/**
|
|
5
|
-
* Evaluation template
|
|
6
|
-
*
|
|
5
|
+
* Evaluation template identifier constants for use with the EvalAI platform API.
|
|
6
|
+
*
|
|
7
|
+
* These are **string identifiers** (e.g. `"unit-testing"`) that reference
|
|
8
|
+
* pre-built templates on the platform — not template definition objects.
|
|
9
|
+
* Pass these values to `evaluations.create({ templateId: EvaluationTemplates.UNIT_TESTING })`
|
|
10
|
+
* to spin up a pre-configured evaluation. For custom criteria, thresholds, and
|
|
11
|
+
* test cases, build your own evaluation config instead.
|
|
7
12
|
*/
|
|
8
13
|
exports.EvaluationTemplates = {
|
|
9
14
|
// Core Testing
|
package/dist/version.d.ts
CHANGED
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
* X-EvalGate-SDK-Version: SDK package version
|
|
4
4
|
* X-EvalGate-Spec-Version: OpenAPI spec version (docs/openapi.json info.version)
|
|
5
5
|
*/
|
|
6
|
-
export declare const SDK_VERSION = "2.2.
|
|
7
|
-
export declare const SPEC_VERSION = "2.2.
|
|
6
|
+
export declare const SDK_VERSION = "2.2.2";
|
|
7
|
+
export declare const SPEC_VERSION = "2.2.2";
|
package/dist/version.js
CHANGED
|
@@ -6,5 +6,5 @@ exports.SPEC_VERSION = exports.SDK_VERSION = void 0;
|
|
|
6
6
|
* X-EvalGate-SDK-Version: SDK package version
|
|
7
7
|
* X-EvalGate-Spec-Version: OpenAPI spec version (docs/openapi.json info.version)
|
|
8
8
|
*/
|
|
9
|
-
exports.SDK_VERSION = "2.2.
|
|
10
|
-
exports.SPEC_VERSION = "2.2.
|
|
9
|
+
exports.SDK_VERSION = "2.2.2";
|
|
10
|
+
exports.SPEC_VERSION = "2.2.2";
|
package/dist/workflows.js
CHANGED
|
@@ -64,8 +64,13 @@ class WorkflowTracer {
|
|
|
64
64
|
this.costs = [];
|
|
65
65
|
this.spanCounter = 0;
|
|
66
66
|
this.client = client;
|
|
67
|
+
const resolvedOrgId = options.organizationId ??
|
|
68
|
+
(typeof client?.getOrganizationId === "function"
|
|
69
|
+
? client.getOrganizationId()
|
|
70
|
+
: undefined) ??
|
|
71
|
+
0;
|
|
67
72
|
this.options = {
|
|
68
|
-
organizationId:
|
|
73
|
+
organizationId: resolvedOrgId,
|
|
69
74
|
autoCalculateCost: options.autoCalculateCost ?? true,
|
|
70
75
|
tracePrefix: options.tracePrefix || "workflow",
|
|
71
76
|
captureFullPayloads: options.captureFullPayloads ?? true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evalgate/sdk",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public",
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"CHANGELOG.md"
|
|
17
17
|
],
|
|
18
18
|
"bin": {
|
|
19
|
-
"evalgate": "
|
|
19
|
+
"evalgate": "dist/cli/index.js"
|
|
20
20
|
},
|
|
21
21
|
"engines": {
|
|
22
22
|
"node": ">=16.0.0"
|