Refactor calculating score. Add sort node for vanity

This commit is contained in:
William Jeynes
2026-02-12 23:46:00 +00:00
parent b06c08daab
commit 7fe63d6a98
6 changed files with 56 additions and 30 deletions
+5 -1
View File
@@ -9,6 +9,7 @@ import { ragasMetrics } from "./nodes/ragasMetrics";
import { produceRanking } from "./nodes/produceRanking"; import { produceRanking } from "./nodes/produceRanking";
import { createModelNode } from "./nodes/model"; import { createModelNode } from "./nodes/model";
import { loopEndConditional } from "./conditionals/loop_end"; import { loopEndConditional } from "./conditionals/loop_end";
import { sort } from "./nodes/sort";
const triggerEventToolNode = createToolNode(triggerEventToolsByName); const triggerEventToolNode = createToolNode(triggerEventToolsByName);
@@ -34,6 +35,7 @@ const agent = new StateGraph(MessagesState)
.addNode("relationModel", relationModel) .addNode("relationModel", relationModel)
.addNode(produceRanking.name, produceRanking) .addNode(produceRanking.name, produceRanking)
.addNode(sort.name, sort)
.addEdge(START, normalizationSetup.name) .addEdge(START, normalizationSetup.name)
.addEdge(normalizationSetup.name, "normalisationModel") .addEdge(normalizationSetup.name, "normalisationModel")
@@ -52,7 +54,9 @@ const agent = new StateGraph(MessagesState)
.addEdge("relationModel", produceRanking.name) .addEdge("relationModel", produceRanking.name)
// @ts-expect-error // @ts-expect-error
.addConditionalEdges(produceRanking.name, loopEndConditional, [verificationSetup.name, END]) .addConditionalEdges(produceRanking.name, loopEndConditional, [verificationSetup.name, sort.name])
.addEdge(sort.name, END)
.compile(); .compile();
+1 -1
View File
@@ -7,7 +7,7 @@ export const loopEndConditional: ConditionalEdgeRouter<typeof MessagesState, Str
const triggerEventsIndex = state.proposedTriggerEventIndex; const triggerEventsIndex = state.proposedTriggerEventIndex;
if (triggerEventsIndex == triggerEvents.length-1) { if (triggerEventsIndex == triggerEvents.length-1) {
return END return "sort"
} }
else { else {
return "verificationSetup" return "verificationSetup"
+26 -16
View File
@@ -2,48 +2,58 @@ import { GraphNode } from "@langchain/langgraph";
import { MessagesState } from "../state"; import { MessagesState } from "../state";
import { BaseMessage } from "@langchain/core/messages"; import { BaseMessage } from "@langchain/core/messages";
type Priority = keyof typeof mapping; //TODO: Each of these might need different weights
const keys = ["CONFIDENCE", "RAGAS", "RELATION"];
const mapping = { const mapping = {
VERYHIGH: 1.0, VERYHIGH: 1.0,
HIGH: 0.75, HIGH: 0.75,
MEDIUM: 0.5, MEDIUM: 0.5,
LOW: 0.25, LOW: 0.25,
VERYLOW: 0.0 VERYLOW: 0.0,
} as const; } as const;
function mapResponse(value: string): number { type Priority = keyof typeof mapping;
const upper = value.toUpperCase() as Priority;
if (upper in mapping) { function mapResponse(value: string | undefined | null): number {
return mapping[upper]; if (!value) return 0;
}
return 0; const trimmed = value.trim();
const num = parseFloat(trimmed);
// If number, return it
if (!isNaN(num)) return num;
// Otherwise, map to value
const upper = trimmed.toUpperCase() as Priority;
return mapping[upper] ?? 0;
} }
function getLastMessageContaining( function getLastMessageContaining(
messages: BaseMessage[], messages: BaseMessage[],
searchString: string searchString: string
): string { ): string | null {
for (let i = messages.length - 1; i >= 0; i--) { for (let i = messages.length - 1; i >= 0; i--) {
const content = messages[i].content; const content = messages[i].content;
if (typeof content === "string" && content.includes(searchString)) { if (typeof content === "string" && content.includes(searchString)) {
return content; return content;
} }
} }
return ""; return null;
} }
export const produceRanking: GraphNode<typeof MessagesState> = async (state) => { export const produceRanking: GraphNode<typeof MessagesState> = async (state) => {
//TODO: what should these weights be // Extract and map values
let conf = getLastMessageContaining(state.messages, "CONFIDENCE")?.split(":")[1] //TODO: we can better error handle here const values = keys.map((key) => {
let ragas = getLastMessageContaining(state.messages, "RAGAS")?.split(":")[1] //TODO: we can genericify this too surely const msg = getLastMessageContaining(state.messages, key);
let rel = getLastMessageContaining(state.messages, "RELATION")?.split(":")[1] const part = msg?.split(":").at(1);
return mapResponse(part);
});
let result = mapResponse(conf) * Number.parseFloat(ragas) * mapResponse(rel) // Multiply!
const result = values.reduce((acc, val) => acc * val, 1);
let current = state.proposedTriggerEvent; const current = state.proposedTriggerEvent;
current[state.proposedTriggerEventIndex].score = result; current[state.proposedTriggerEventIndex].score = result;
return { proposedTriggerEvent: current }; return { proposedTriggerEvent: current };
-2
View File
@@ -8,8 +8,6 @@ export const ragasMetrics: GraphNode<typeof MessagesState> = async (state) => {
const answer = state.proposedTriggerEvent[state.proposedTriggerEventIndex].Event const answer = state.proposedTriggerEvent[state.proposedTriggerEventIndex].Event
const contexts = state.proposedTriggerEvent[state.proposedTriggerEventIndex].context?.split("^^^") ?? [] const contexts = state.proposedTriggerEvent[state.proposedTriggerEventIndex].context?.split("^^^") ?? []
console.log(contexts)
const results = await evaluateWithRagas({question, answer, contexts}) const results = await evaluateWithRagas({question, answer, contexts})
return { return {
+20
View File
@@ -0,0 +1,20 @@
import { GraphNode } from "@langchain/langgraph";
import { MessagesState } from "../state";
import { AIMessage } from "@langchain/core/messages";
export const sort: GraphNode<typeof MessagesState> = async (state) => {
//not sure which will be better from API, just do both
let current = state.proposedTriggerEvent;
current.sort((a, b) => ((b.score as number) ?? 0) - ((a.score as number) ?? 0));
const displayVersion = current.map((item) => ({
event: item.Event,
reasoningWhyRelevant: item.ReasoningWhyRelevant,
score: item.score ?? 0,
}));
let message = new AIMessage(JSON.stringify(displayVersion))
return { proposedTriggerEvent: current, messages: [message] };
};
+1 -7
View File
@@ -1,12 +1,6 @@
import { import {
StateGraph,
StateSchema, StateSchema,
MessagesValue, MessagesValue,
ReducedValue,
GraphNode,
ConditionalEdgeRouter,
START,
END,
} from "@langchain/langgraph"; } from "@langchain/langgraph";
import { z } from "zod/v4"; import { z } from "zod/v4";
@@ -17,7 +11,7 @@ export const ProposedTriggerEvent = z.object({
Url: z.url(), Url: z.url(),
IsItselfDisinformation: z.boolean(), IsItselfDisinformation: z.boolean(),
context: z.string().optional(), context: z.string().optional(),
score: z.number().optional score: z.number().optional()
}) })
export const ProposedTriggerEventArray = z.array(ProposedTriggerEvent); export const ProposedTriggerEventArray = z.array(ProposedTriggerEvent);