@lunora/testing 1.0.0-alpha.320 → 1.0.0-alpha.321
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/absentScorer-tcGbGxnq.mjs +2 -0
- package/dist/packem_shared/groundednessScorer-D8-EWo1j.mjs +3 -0
- package/package.json +9 -9
- package/dist/packem_shared/containsScorer-DoLfFOs6.mjs +0 -2
- package/dist/packem_shared/groundednessScorer-kgXJCZDg.mjs +0 -3
package/dist/index.d.mts
CHANGED
|
@@ -970,6 +970,20 @@ interface EvalResult {
|
|
|
970
970
|
declare const containsScorer: (needle: string, options?: {
|
|
971
971
|
caseSensitive?: boolean;
|
|
972
972
|
}) => Scorer;
|
|
973
|
+
/**
|
|
974
|
+
* Score 1 when the output does NOT contain `needle` — the negative half of
|
|
975
|
+
* {@link containsScorer}.
|
|
976
|
+
*
|
|
977
|
+
* Every other scorer here asserts something is present, which cannot express
|
|
978
|
+
* the expectations that matter most for a refusal or a leak: "never dispatched
|
|
979
|
+
* the write", "never offered the tool this tier forbids", "the injected fence
|
|
980
|
+
* marker did not survive". Spelling those as a negative-lookahead
|
|
981
|
+
* {@link regexScorer} works but reads as a puzzle, and gets the escaping wrong
|
|
982
|
+
* the first time someone copies it.
|
|
983
|
+
*/
|
|
984
|
+
declare const absentScorer: (needle: string, options?: {
|
|
985
|
+
caseSensitive?: boolean;
|
|
986
|
+
}) => Scorer;
|
|
973
987
|
/** Score 1 when `pattern` matches the output. */
|
|
974
988
|
declare const regexScorer: (pattern: RegExp, name?: string) => Scorer;
|
|
975
989
|
/** Score 1 when the trimmed output exactly equals the trimmed `expected`. */
|
|
@@ -1064,4 +1078,4 @@ declare const groundednessScorer: (options: {
|
|
|
1064
1078
|
judge: (prompt: string) => Promise<string>;
|
|
1065
1079
|
name?: string;
|
|
1066
1080
|
}) => Scorer;
|
|
1067
|
-
export { type AgentHarness, type AgentHarnessOptions, type AgentRunOverrides, type ArtifactsFake, type EvalCase, type EvalItemResult, type EvalResult, type EvaluationAttributeValue, type EvaluationMetrics, type EvaluationSpanHandle, type FakeNotifyControls, type FakeQueueControls, type FakeScheduledJob, type FakeSchedulerControls, type FakeSecretValues, type FakeTopicControls, type FunctionRegistry, type HarnessDispatch, type HarnessMessage, type HarnessThread, type LunoraTestOptions, type NotifyChannel, type ProducedOutput, type PublishedTopicMessage, type RecordEvaluationInput, type RecordedStreamPart, type RecordedTurn, type RetrievalScorerOptions, type ScheduledJobFailure, type ScoreResult, type Scorer, type ScorerSample, type SentNotification, type SentQueueMessage, type StreamGate, type SweepOptions, type TestHarness, type TestIdentity, type TestSubscription, agentHarness, containsScorer, createArtifactsFake, createFakeSecretValues, createStreamGate, evaluate, evaluationAttributes, exactMatchScorer, finalTurn, groundednessScorer, keywordScorer, llmScorer, lunoraTest, mrrScorer, ndcgAtK, precisionAtK, recallAtK, recordEvaluation, recordedModel, regexScorer, scoreSample, toolCallTurn };
|
|
1081
|
+
export { type AgentHarness, type AgentHarnessOptions, type AgentRunOverrides, type ArtifactsFake, type EvalCase, type EvalItemResult, type EvalResult, type EvaluationAttributeValue, type EvaluationMetrics, type EvaluationSpanHandle, type FakeNotifyControls, type FakeQueueControls, type FakeScheduledJob, type FakeSchedulerControls, type FakeSecretValues, type FakeTopicControls, type FunctionRegistry, type HarnessDispatch, type HarnessMessage, type HarnessThread, type LunoraTestOptions, type NotifyChannel, type ProducedOutput, type PublishedTopicMessage, type RecordEvaluationInput, type RecordedStreamPart, type RecordedTurn, type RetrievalScorerOptions, type ScheduledJobFailure, type ScoreResult, type Scorer, type ScorerSample, type SentNotification, type SentQueueMessage, type StreamGate, type SweepOptions, type TestHarness, type TestIdentity, type TestSubscription, absentScorer, agentHarness, containsScorer, createArtifactsFake, createFakeSecretValues, createStreamGate, evaluate, evaluationAttributes, exactMatchScorer, finalTurn, groundednessScorer, keywordScorer, llmScorer, lunoraTest, mrrScorer, ndcgAtK, precisionAtK, recallAtK, recordEvaluation, recordedModel, regexScorer, scoreSample, toolCallTurn };
|
package/dist/index.d.ts
CHANGED
|
@@ -970,6 +970,20 @@ interface EvalResult {
|
|
|
970
970
|
declare const containsScorer: (needle: string, options?: {
|
|
971
971
|
caseSensitive?: boolean;
|
|
972
972
|
}) => Scorer;
|
|
973
|
+
/**
|
|
974
|
+
* Score 1 when the output does NOT contain `needle` — the negative half of
|
|
975
|
+
* {@link containsScorer}.
|
|
976
|
+
*
|
|
977
|
+
* Every other scorer here asserts something is present, which cannot express
|
|
978
|
+
* the expectations that matter most for a refusal or a leak: "never dispatched
|
|
979
|
+
* the write", "never offered the tool this tier forbids", "the injected fence
|
|
980
|
+
* marker did not survive". Spelling those as a negative-lookahead
|
|
981
|
+
* {@link regexScorer} works but reads as a puzzle, and gets the escaping wrong
|
|
982
|
+
* the first time someone copies it.
|
|
983
|
+
*/
|
|
984
|
+
declare const absentScorer: (needle: string, options?: {
|
|
985
|
+
caseSensitive?: boolean;
|
|
986
|
+
}) => Scorer;
|
|
973
987
|
/** Score 1 when `pattern` matches the output. */
|
|
974
988
|
declare const regexScorer: (pattern: RegExp, name?: string) => Scorer;
|
|
975
989
|
/** Score 1 when the trimmed output exactly equals the trimmed `expected`. */
|
|
@@ -1064,4 +1078,4 @@ declare const groundednessScorer: (options: {
|
|
|
1064
1078
|
judge: (prompt: string) => Promise<string>;
|
|
1065
1079
|
name?: string;
|
|
1066
1080
|
}) => Scorer;
|
|
1067
|
-
export { type AgentHarness, type AgentHarnessOptions, type AgentRunOverrides, type ArtifactsFake, type EvalCase, type EvalItemResult, type EvalResult, type EvaluationAttributeValue, type EvaluationMetrics, type EvaluationSpanHandle, type FakeNotifyControls, type FakeQueueControls, type FakeScheduledJob, type FakeSchedulerControls, type FakeSecretValues, type FakeTopicControls, type FunctionRegistry, type HarnessDispatch, type HarnessMessage, type HarnessThread, type LunoraTestOptions, type NotifyChannel, type ProducedOutput, type PublishedTopicMessage, type RecordEvaluationInput, type RecordedStreamPart, type RecordedTurn, type RetrievalScorerOptions, type ScheduledJobFailure, type ScoreResult, type Scorer, type ScorerSample, type SentNotification, type SentQueueMessage, type StreamGate, type SweepOptions, type TestHarness, type TestIdentity, type TestSubscription, agentHarness, containsScorer, createArtifactsFake, createFakeSecretValues, createStreamGate, evaluate, evaluationAttributes, exactMatchScorer, finalTurn, groundednessScorer, keywordScorer, llmScorer, lunoraTest, mrrScorer, ndcgAtK, precisionAtK, recallAtK, recordEvaluation, recordedModel, regexScorer, scoreSample, toolCallTurn };
|
|
1081
|
+
export { type AgentHarness, type AgentHarnessOptions, type AgentRunOverrides, type ArtifactsFake, type EvalCase, type EvalItemResult, type EvalResult, type EvaluationAttributeValue, type EvaluationMetrics, type EvaluationSpanHandle, type FakeNotifyControls, type FakeQueueControls, type FakeScheduledJob, type FakeSchedulerControls, type FakeSecretValues, type FakeTopicControls, type FunctionRegistry, type HarnessDispatch, type HarnessMessage, type HarnessThread, type LunoraTestOptions, type NotifyChannel, type ProducedOutput, type PublishedTopicMessage, type RecordEvaluationInput, type RecordedStreamPart, type RecordedTurn, type RetrievalScorerOptions, type ScheduledJobFailure, type ScoreResult, type Scorer, type ScorerSample, type SentNotification, type SentQueueMessage, type StreamGate, type SweepOptions, type TestHarness, type TestIdentity, type TestSubscription, absentScorer, agentHarness, containsScorer, createArtifactsFake, createFakeSecretValues, createStreamGate, evaluate, evaluationAttributes, exactMatchScorer, finalTurn, groundednessScorer, keywordScorer, llmScorer, lunoraTest, mrrScorer, ndcgAtK, precisionAtK, recallAtK, recordEvaluation, recordedModel, regexScorer, scoreSample, toolCallTurn };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{agentHarness as o,finalTurn as t,toolCallTurn as a}from"./packem_shared/agentHarness-BM24QFnh.mjs";import{evaluationAttributes as l,recordEvaluation as n}from"./packem_shared/evaluationAttributes-CYLgTP1P.mjs";import{createArtifactsFake as m}from"./packem_shared/createArtifactsFake-L0tjki11.mjs";import{createFakeSecretValues as
|
|
1
|
+
import{agentHarness as o,finalTurn as t,toolCallTurn as a}from"./packem_shared/agentHarness-BM24QFnh.mjs";import{evaluationAttributes as l,recordEvaluation as n}from"./packem_shared/evaluationAttributes-CYLgTP1P.mjs";import{createArtifactsFake as m}from"./packem_shared/createArtifactsFake-L0tjki11.mjs";import{createFakeSecretValues as p}from"./packem_shared/createFakeSecretValues-BTNOscqQ.mjs";import{lunoraTest as f}from"./packem_shared/lunoraTest-KJJjjDCt.mjs";import{createStreamGate as u,recordedModel as d}from"./packem_shared/createStreamGate-Geg-yvx6.mjs";import{groundednessScorer as g,mrrScorer as k,ndcgAtK as M,precisionAtK as v,recallAtK as F}from"./packem_shared/groundednessScorer-D8-EWo1j.mjs";import{absentScorer as T,containsScorer as b,evaluate as w,exactMatchScorer as C,keywordScorer as h,llmScorer as y,regexScorer as E,scoreSample as G}from"./packem_shared/absentScorer-tcGbGxnq.mjs";import{extractLink as L,listCapturedMail as V,waitForMail as j}from"@lunora/mail/testing";export{T as absentScorer,o as agentHarness,b as containsScorer,m as createArtifactsFake,p as createFakeSecretValues,u as createStreamGate,w as evaluate,l as evaluationAttributes,C as exactMatchScorer,L as extractLink,t as finalTurn,g as groundednessScorer,h as keywordScorer,V as listCapturedMail,y as llmScorer,f as lunoraTest,k as mrrScorer,M as ndcgAtK,v as precisionAtK,F as recallAtK,n as recordEvaluation,d as recordedModel,E as regexScorer,G as scoreSample,a as toolCallTurn,j as waitForMail};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{LunoraError as i}from"@lunora/errors";const p=/^\s*(-?\d+(?:\.\d+)?)(?=$|[\s\-–—])/u,u=e=>Number.isFinite(e)?Math.min(1,Math.max(0,e)):0,S=e=>typeof e=="number"?{score:u(e)}:{score:u(e.score),...e.reason===void 0?{}:{reason:e.reason}},m=e=>e.length===0?0:e.reduce((t,n)=>t+n,0)/e.length,v=(e,t={})=>{if(e.length===0)throw new i("BAD_REQUEST","@lunora/testing: containsScorer requires a non-empty needle");return{name:`contains:${e}`,score:({output:n})=>{const a=t.caseSensitive?n:n.toLowerCase(),r=t.caseSensitive?e:e.toLowerCase();return a.includes(r)?1:0}}},x=(e,t={})=>{if(e.length===0)throw new i("BAD_REQUEST","@lunora/testing: absentScorer requires a non-empty needle");return{name:`absent:${e}`,score:({output:n})=>{const a=t.caseSensitive?n:n.toLowerCase(),r=t.caseSensitive?e:e.toLowerCase();return a.includes(r)?0:1}}},E=(e,t="regex")=>{const n=new RegExp(e.source,e.flags.replaceAll(/[gy]/gu,""));return{name:t,score:({output:a})=>n.test(a)?1:0}},$=()=>({name:"exact-match",score:({expected:e,output:t})=>t.trim()===e?.trim()?1:0}),b=e=>{if(e.length===0)throw new i("BAD_REQUEST","@lunora/testing: keywordScorer requires at least one keyword");return{name:"keyword-coverage",score:({output:t})=>{const n=t.toLowerCase(),a=e.filter(r=>n.includes(r.toLowerCase())).length;return{reason:`${String(a)}/${String(e.length)} keywords present`,score:a/e.length}}}},h=(e,t)=>[`Rate the ASSISTANT OUTPUT against this criterion: ${e}`,"Respond with a single number from 0 (fails) to 1 (fully meets), then a dash and a one-line reason.",...t.input===void 0?[]:["",`Input: ${t.input}`],...t.expected===void 0?[]:["",`Reference answer: ${t.expected}`],"",`Assistant output: ${t.output}`].join(`
|
|
2
|
+
`),w=e=>{const t=p.exec(e),n=t===null?Number.NaN:Number(t[1]);if(!Number.isFinite(n)||n<0||n>1)throw new i("BAD_REQUEST",`@lunora/testing: the LLM judge did not answer with a score in [0, 1] — got ${JSON.stringify(e.trim().slice(0,200))}`);return{reason:e.trim(),score:n}},L=e=>({name:e.name??"llm-judge",score:async t=>w(await e.judge(h(e.criteria,t)))}),f=async(e,t)=>{const n=await Promise.all(t.map(async o=>({name:o.name,result:S(await o.score(e))}))),a={},r=new Map;for(const{name:o,result:c}of n){const s=r.get(o)??0;r.set(o,s+1),a[s===0?o:`${o}#${String(s+1)}`]=c}return{average:m(n.map(({result:o})=>o.score)),scores:a}},A=async(e,t,n)=>{const a=await Promise.all(e.map(async r=>{const o=await t(r.input),c=typeof o=="string"?o:o.output,s=typeof o=="string"||o.metadata===void 0?r.metadata:{...r.metadata,...o.metadata},g={input:r.input,output:c,...r.expected===void 0?{}:{expected:r.expected},...s===void 0?{}:{metadata:s}},{average:l,scores:d}=await f(g,n);return{average:l,input:r.input,output:c,scores:d}}));return{average:m(a.map(r=>r.average)),items:a}};export{x as absentScorer,v as containsScorer,A as evaluate,$ as exactMatchScorer,b as keywordScorer,L as llmScorer,w as parseJudgeScore,E as regexScorer,f as scoreSample};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{LunoraError as g}from"@lunora/errors";import{parseJudgeScore as h}from"./absentScorer-tcGbGxnq.mjs";const w="retrieved",v="relevant",l=(e,t)=>{const n=e.metadata?.[t];return Array.isArray(n)?n.filter(r=>typeof r=="string"&&r.length>0):[]},s=(e,t)=>{const n=l(e,t?.relevantKey??v);if(n.length!==0)return{relevant:new Set(n),retrieved:l(e,t?.retrievedKey??w)}},a=e=>({reason:`no gold ids under metadata.${e?.relevantKey??v} — cannot score retrieval`,score:0}),c=(e,t)=>{if(e!==void 0&&(!Number.isInteger(e)||e<1))throw new g("BAD_REQUEST",`@lunora/testing: ${t} \`k\` must be a positive integer`)},u=(e,t)=>t===void 0?e.retrieved:e.retrieved.slice(0,t),f=(e,t)=>{const n=new Set;for(const r of e)t.has(r)&&n.add(r);return n.size},S=(e,t)=>{const n=new Set;let r=0;for(const[o,i]of e.entries())t.has(i)&&!n.has(i)&&(n.add(i),r+=1/Math.log2(o+2));return r},m=e=>{let t=0;for(let n=0;n<e;n+=1)t+=1/Math.log2(n+2);return t},p=(e,t)=>(c(e,"recallAtK"),{name:e===void 0?"recall":`recall@${String(e)}`,score:n=>{const r=s(n,t);if(r===void 0)return a(t);const o=f(u(r,e),r.relevant);return{reason:`${String(o)}/${String(r.relevant.size)} gold ids retrieved`,score:o/r.relevant.size}}}),x=(e,t)=>(c(e,"precisionAtK"),{name:e===void 0?"precision":`precision@${String(e)}`,score:n=>{const r=s(n,t);if(r===void 0)return a(t);const o=u(r,e);if(o.length===0)return{reason:"nothing retrieved",score:0};const i=f(o,r.relevant);return{reason:`${String(i)}/${String(o.length)} retrieved ids are gold`,score:i/o.length}}}),y=e=>({name:"mrr",score:t=>{const n=s(t,e);if(n===void 0)return a(e);const r=n.retrieved.findIndex(o=>n.relevant.has(o));return r===-1?{reason:"no gold id retrieved",score:0}:{reason:`first gold id at rank ${String(r+1)}`,score:1/(r+1)}}}),$=(e,t)=>(c(e,"ndcgAtK"),{name:e===void 0?"ndcg":`ndcg@${String(e)}`,score:n=>{const r=s(n,t);if(r===void 0)return a(t);const o=u(r,e);if(o.length===0)return{reason:"nothing retrieved",score:0};const i=S(o,r.relevant),d=m(e===void 0?r.relevant.size:Math.min(r.relevant.size,e));return{reason:`dcg ${i.toFixed(3)} / ideal ${d.toFixed(3)}`,score:d===0?0:i/d}}}),K=e=>{if(typeof e.judge!="function")throw new g("BAD_REQUEST","@lunora/testing: groundednessScorer requires an injected `judge` function");const t=e.contextKey??"context";return{name:e.name??"groundedness",score:async n=>{const r=n.metadata?.[t];if(typeof r!="string"||r.trim().length===0)return{reason:`no retrieved context under metadata.${t}`,score:0};const o=await e.judge(["Rate how well the ASSISTANT ANSWER is supported by the RETRIEVED CONTEXT below.","Score 1 if every claim in the answer is supported by the context, 0 if the answer asserts","anything the context does not support. Judge support only — do NOT reward correctness","the context does not contain.","Respond with a single number from 0 to 1, then a dash and a one-line reason.","",`Retrieved context:
|
|
2
|
+
${r}`,"",`Assistant answer: ${n.output}`].join(`
|
|
3
|
+
`));return h(o)}}};export{K as groundednessScorer,y as mrrScorer,$ as ndcgAtK,x as precisionAtK,p as recallAtK};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/testing",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.321",
|
|
4
4
|
"description": "Testing toolkit for Lunora: an in-memory harness for queries, mutations, and actions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -50,16 +50,16 @@
|
|
|
50
50
|
"access": "public"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@lunora/agent": "1.0.0-alpha.
|
|
54
|
-
"@lunora/bindings": "1.0.0-alpha.
|
|
53
|
+
"@lunora/agent": "1.0.0-alpha.268",
|
|
54
|
+
"@lunora/bindings": "1.0.0-alpha.144",
|
|
55
55
|
"@lunora/errors": ">=1.0.0-alpha.51 <2.0.0-0",
|
|
56
|
-
"@lunora/mail": "1.0.0-alpha.
|
|
57
|
-
"@lunora/notify": "1.0.0-alpha.
|
|
56
|
+
"@lunora/mail": "1.0.0-alpha.132",
|
|
57
|
+
"@lunora/notify": "1.0.0-alpha.104",
|
|
58
58
|
"@lunora/platform": "1.0.0-alpha.76",
|
|
59
|
-
"@lunora/queue": "1.0.0-alpha.
|
|
60
|
-
"@lunora/scheduler": "1.0.0-alpha.
|
|
61
|
-
"@lunora/server": "1.0.0-alpha.
|
|
62
|
-
"@lunora/wire": "1.0.0-alpha.
|
|
59
|
+
"@lunora/queue": "1.0.0-alpha.136",
|
|
60
|
+
"@lunora/scheduler": "1.0.0-alpha.161",
|
|
61
|
+
"@lunora/server": "1.0.0-alpha.258",
|
|
62
|
+
"@lunora/wire": "1.0.0-alpha.6",
|
|
63
63
|
"@visulima/notification": "2.0.2",
|
|
64
64
|
"ai": "7.0.127"
|
|
65
65
|
},
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{LunoraError as i}from"@lunora/errors";const p=/^\s*(-?\d+(?:\.\d+)?)(?=$|[\s\-–—])/u,u=e=>Number.isFinite(e)?Math.min(1,Math.max(0,e)):0,h=e=>typeof e=="number"?{score:u(e)}:{score:u(e.score),...e.reason===void 0?{}:{reason:e.reason}},m=e=>e.length===0?0:e.reduce((t,n)=>t+n,0)/e.length,x=(e,t={})=>{if(e.length===0)throw new i("BAD_REQUEST","@lunora/testing: containsScorer requires a non-empty needle");return{name:`contains:${e}`,score:({output:n})=>{const a=t.caseSensitive?n:n.toLowerCase(),r=t.caseSensitive?e:e.toLowerCase();return a.includes(r)?1:0}}},v=(e,t="regex")=>{const n=new RegExp(e.source,e.flags.replaceAll(/[gy]/gu,""));return{name:t,score:({output:a})=>n.test(a)?1:0}},$=()=>({name:"exact-match",score:({expected:e,output:t})=>t.trim()===e?.trim()?1:0}),E=e=>{if(e.length===0)throw new i("BAD_REQUEST","@lunora/testing: keywordScorer requires at least one keyword");return{name:"keyword-coverage",score:({output:t})=>{const n=t.toLowerCase(),a=e.filter(r=>n.includes(r.toLowerCase())).length;return{reason:`${String(a)}/${String(e.length)} keywords present`,score:a/e.length}}}},w=(e,t)=>[`Rate the ASSISTANT OUTPUT against this criterion: ${e}`,"Respond with a single number from 0 (fails) to 1 (fully meets), then a dash and a one-line reason.",...t.input===void 0?[]:["",`Input: ${t.input}`],...t.expected===void 0?[]:["",`Reference answer: ${t.expected}`],"",`Assistant output: ${t.output}`].join(`
|
|
2
|
-
`),S=e=>{const t=p.exec(e),n=t===null?Number.NaN:Number(t[1]);if(!Number.isFinite(n)||n<0||n>1)throw new i("BAD_REQUEST",`@lunora/testing: the LLM judge did not answer with a score in [0, 1] — got ${JSON.stringify(e.trim().slice(0,200))}`);return{reason:e.trim(),score:n}},N=e=>({name:e.name??"llm-judge",score:async t=>S(await e.judge(w(e.criteria,t)))}),f=async(e,t)=>{const n=await Promise.all(t.map(async o=>({name:o.name,result:h(await o.score(e))}))),a={},r=new Map;for(const{name:o,result:c}of n){const s=r.get(o)??0;r.set(o,s+1),a[s===0?o:`${o}#${String(s+1)}`]=c}return{average:m(n.map(({result:o})=>o.score)),scores:a}},A=async(e,t,n)=>{const a=await Promise.all(e.map(async r=>{const o=await t(r.input),c=typeof o=="string"?o:o.output,s=typeof o=="string"||o.metadata===void 0?r.metadata:{...r.metadata,...o.metadata},g={input:r.input,output:c,...r.expected===void 0?{}:{expected:r.expected},...s===void 0?{}:{metadata:s}},{average:d,scores:l}=await f(g,n);return{average:d,input:r.input,output:c,scores:l}}));return{average:m(a.map(r=>r.average)),items:a}};export{x as containsScorer,A as evaluate,$ as exactMatchScorer,E as keywordScorer,N as llmScorer,S as parseJudgeScore,v as regexScorer,f as scoreSample};
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{LunoraError as g}from"@lunora/errors";import{parseJudgeScore as h}from"./containsScorer-DoLfFOs6.mjs";const w="retrieved",v="relevant",l=(e,t)=>{const n=e.metadata?.[t];return Array.isArray(n)?n.filter(r=>typeof r=="string"&&r.length>0):[]},s=(e,t)=>{const n=l(e,t?.relevantKey??v);if(n.length!==0)return{relevant:new Set(n),retrieved:l(e,t?.retrievedKey??w)}},a=e=>({reason:`no gold ids under metadata.${e?.relevantKey??v} — cannot score retrieval`,score:0}),c=(e,t)=>{if(e!==void 0&&(!Number.isInteger(e)||e<1))throw new g("BAD_REQUEST",`@lunora/testing: ${t} \`k\` must be a positive integer`)},u=(e,t)=>t===void 0?e.retrieved:e.retrieved.slice(0,t),f=(e,t)=>{const n=new Set;for(const r of e)t.has(r)&&n.add(r);return n.size},S=(e,t)=>{const n=new Set;let r=0;for(const[o,i]of e.entries())t.has(i)&&!n.has(i)&&(n.add(i),r+=1/Math.log2(o+2));return r},m=e=>{let t=0;for(let n=0;n<e;n+=1)t+=1/Math.log2(n+2);return t},p=(e,t)=>(c(e,"recallAtK"),{name:e===void 0?"recall":`recall@${String(e)}`,score:n=>{const r=s(n,t);if(r===void 0)return a(t);const o=f(u(r,e),r.relevant);return{reason:`${String(o)}/${String(r.relevant.size)} gold ids retrieved`,score:o/r.relevant.size}}}),x=(e,t)=>(c(e,"precisionAtK"),{name:e===void 0?"precision":`precision@${String(e)}`,score:n=>{const r=s(n,t);if(r===void 0)return a(t);const o=u(r,e);if(o.length===0)return{reason:"nothing retrieved",score:0};const i=f(o,r.relevant);return{reason:`${String(i)}/${String(o.length)} retrieved ids are gold`,score:i/o.length}}}),y=e=>({name:"mrr",score:t=>{const n=s(t,e);if(n===void 0)return a(e);const r=n.retrieved.findIndex(o=>n.relevant.has(o));return r===-1?{reason:"no gold id retrieved",score:0}:{reason:`first gold id at rank ${String(r+1)}`,score:1/(r+1)}}}),$=(e,t)=>(c(e,"ndcgAtK"),{name:e===void 0?"ndcg":`ndcg@${String(e)}`,score:n=>{const r=s(n,t);if(r===void 0)return a(t);const o=u(r,e);if(o.length===0)return{reason:"nothing retrieved",score:0};const i=S(o,r.relevant),d=m(e===void 0?r.relevant.size:Math.min(r.relevant.size,e));return{reason:`dcg ${i.toFixed(3)} / ideal ${d.toFixed(3)}`,score:d===0?0:i/d}}}),K=e=>{if(typeof e.judge!="function")throw new g("BAD_REQUEST","@lunora/testing: groundednessScorer requires an injected `judge` function");const t=e.contextKey??"context";return{name:e.name??"groundedness",score:async n=>{const r=n.metadata?.[t];if(typeof r!="string"||r.trim().length===0)return{reason:`no retrieved context under metadata.${t}`,score:0};const o=await e.judge(["Rate how well the ASSISTANT ANSWER is supported by the RETRIEVED CONTEXT below.","Score 1 if every claim in the answer is supported by the context, 0 if the answer asserts","anything the context does not support. Judge support only — do NOT reward correctness","the context does not contain.","Respond with a single number from 0 to 1, then a dash and a one-line reason.","",`Retrieved context:
|
|
2
|
-
${r}`,"",`Assistant answer: ${n.output}`].join(`
|
|
3
|
-
`));return h(o)}}};export{K as groundednessScorer,y as mrrScorer,$ as ndcgAtK,x as precisionAtK,p as recallAtK};
|