start adding dummy nodes

This commit is contained in:
William Jeynes
2026-01-28 21:26:34 +00:00
parent a3201d17a2
commit c6416622e4
14 changed files with 188 additions and 61 deletions
+30
View File
@@ -0,0 +1,30 @@
import { END, START, StateGraph } from "@langchain/langgraph";
import { MessagesState } from "./state";
import { toolNode } from "./nodes/tool";
import { createToolConditional } from "./conditionals/tool_end";
import { normalizationSetup } from "./nodes/normalizationSetup";
import { dummyNormalisationModel } from "./nodes/dummyNormalisationModel";
import { dummyTriggerEventModel } from "./nodes/dummyTriggerEventModel";
const triggerEventToolConditional = createToolConditional(toolNode.name, END)
const agent = new StateGraph(MessagesState)
//NODES
.addNode("toolNode", toolNode)
.addNode(normalizationSetup.name, normalizationSetup)
.addNode(dummyNormalisationModel.name, dummyNormalisationModel)
.addNode(dummyTriggerEventModel.name, dummyTriggerEventModel)
.addEdge(START, normalizationSetup.name)
.addEdge(normalizationSetup.name, dummyNormalisationModel.name)
.addEdge(dummyNormalisationModel.name, dummyTriggerEventModel.name)
// @ts-expect-error
.addConditionalEdges(dummyTriggerEventModel.name, triggerEventToolConditional, [toolNode.name, END])
.addEdge(toolNode.name, dummyTriggerEventModel.name)
.addEdge(dummyTriggerEventModel.name, END)
.compile();
export {agent}
+31
View File
@@ -0,0 +1,31 @@
import { ConditionalEdgeRouter, END } from "@langchain/langgraph";
import { MessagesState } from "../state";
import { AIMessage } from "@langchain/core/messages";
export function createToolConditional(a: String, b: String): ConditionalEdgeRouter<typeof MessagesState, String> {
// @ts-expect-error
var genericToolConditional: ConditionalEdgeRouter<typeof MessagesState, String> = (state) => {
const lastMessage = state.messages.at(-1);
//STARTTEMP
if (lastMessage?.content?.toString().indexOf("qwe") != -1) {
return a
}
//ENDTEMP
// Check if it's an AIMessage before accessing tool_calls
if (!lastMessage || !AIMessage.isInstance(lastMessage)) {
return b;
}
// If the LLM makes a tool call, then perform an action
if (lastMessage.tool_calls?.length) {
return a;
}
// Otherwise, we stop (reply to the user)
return b;
};
return genericToolConditional
}
-33
View File
@@ -1,33 +0,0 @@
import { addMessages, entrypoint } from "@langchain/langgraph";
import { type BaseMessage } from "@langchain/core/messages";
import { HumanMessage } from "@langchain/core/messages";
import { modelNode } from "./nodes/model";
import { toolNode } from "./nodes/tool";
import 'dotenv/config';
const agent = entrypoint({ name: "agent" }, async (messages: BaseMessage[]) => {
let modelResponse = await modelNode(messages);
while (true) {
if (!modelResponse.tool_calls?.length) {
break;
}
// Execute tools
const toolResults = await Promise.all(
modelResponse.tool_calls.map((toolCall) => toolNode(toolCall))
);
messages = addMessages(messages, [modelResponse, ...toolResults]);
modelResponse = await modelNode(messages);
}
return messages;
});
export {agent}
const result = await agent.invoke([new HumanMessage("Add 3 and 4.")]);
for (const message of result) {
console.log(`[${message.getType()}]: ${message.text}`);
}
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"dependencies": ["."], "dependencies": ["."],
"graphs": { "graphs": {
"agent": "./index.ts:agent" "agent": "./agent.ts:agent"
}, },
"env": ".env" "env": ".env"
} }
+11
View File
@@ -0,0 +1,11 @@
import { GraphNode } from "@langchain/langgraph";
import { MessagesState } from "../state";
import { AIMessage, HumanMessage } from "@langchain/core/messages";
export const dummyNormalisationModel: GraphNode<typeof MessagesState> = async (state) => {
//TODO: call AI model with collected data
return {
messages: [ new AIMessage(state.messages.at(-1)?.content + " Processed")]
};
};
+11
View File
@@ -0,0 +1,11 @@
import { GraphNode } from "@langchain/langgraph";
import { MessagesState } from "../state";
import { AIMessage, HumanMessage } from "@langchain/core/messages";
export const dummyRagasMetrics: GraphNode<typeof MessagesState> = async (state) => {
//TODO: get ragas metrics
return {
messages: [ new AIMessage("RAGASSED : " + state.messages.at(-1)?.content)]
};
};
+11
View File
@@ -0,0 +1,11 @@
import { GraphNode } from "@langchain/langgraph";
import { MessagesState } from "../state";
import { AIMessage, HumanMessage } from "@langchain/core/messages";
export const dummyTriggerEventModel: GraphNode<typeof MessagesState> = async (state) => {
//TODO: call AI model with collected data
return {
messages: [ new AIMessage("Trigger events of: " + state.messages.at(-1)?.content)]
};
};
+11
View File
@@ -0,0 +1,11 @@
import { GraphNode } from "@langchain/langgraph";
import { MessagesState } from "../state";
import { AIMessage, HumanMessage } from "@langchain/core/messages";
export const dummyVerificationModel: GraphNode<typeof MessagesState> = async (state) => {
//TODO: call AI model with collected data
return {
messages: [ new AIMessage("Verified : " + state.messages.at(-1)?.content)]
};
};
+21 -18
View File
@@ -1,21 +1,24 @@
import { task, entrypoint } from "@langchain/langgraph"; // import { SystemMessage } from "@langchain/core/messages";
import { BaseMessage, SystemMessage } from "@langchain/core/messages"; // import { GraphNode } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai" // import { MessagesState } from "../state";
import { arithmeticTools } from "../tools/arithmetic"; // import { arithmeticTools } from "../tools/arithmetic";
// import { ChatOpenAI } from "@langchain/openai"
const model = new ChatOpenAI({ // const model = new ChatOpenAI({
model: "gpt-5-mini" // model: "gpt-5-mini"
}); // });
const modelWithTools = model.bindTools(arithmeticTools); // const modelWithTools = model.bindTools(arithmeticTools);
export const modelNode = task({ name: "callLlm" }, async (messages: BaseMessage[]) => { // export const llmCall: GraphNode<typeof MessagesState> = async (state) => {
// const response = await modelWithTools.invoke([
// new SystemMessage(
return modelWithTools.invoke([ // "You are a helpful assistant tasked with performing arithmetic on a set of inputs. Any calculation, no matter how trivial, should be done with tools. Output the final answer with %%% on each side"
new SystemMessage( // ),
"You are a helpful assistant tasked with performing arithmetic on a set of inputs." // ...state.messages,
), // ]);
...messages, // return {
]); // messages: [response],
}); // llmCalls: 1,
// };
// };
+9
View File
@@ -0,0 +1,9 @@
import { GraphNode } from "@langchain/langgraph";
import { MessagesState } from "../state";
import { HumanMessage } from "@langchain/core/messages";
export const normalizationSetup: GraphNode<typeof MessagesState> = async (state) => {
//TODO: Implement claim normalisation, using few shot prompting and CLAN Dataset
return { messages: [ new HumanMessage(state.disinformationTitle)] };
};
+23 -6
View File
@@ -1,8 +1,25 @@
import type { ToolCall } from "@langchain/core/messages/tool"; import { AIMessage, ToolMessage } from "@langchain/core/messages";
import { task } from "@langchain/langgraph"; import { GraphNode } from "@langchain/langgraph";
import { MessagesState } from "../state";
import { arithmeticToolsByName } from "../tools/arithmetic"; import { arithmeticToolsByName } from "../tools/arithmetic";
export const toolNode = task({ name: "callTool" }, async (toolCall: ToolCall) => { export const toolNode: GraphNode<typeof MessagesState> = async (state) => {
const tool = arithmeticToolsByName[toolCall.name]; const lastMessage = state.messages.at(-1);
return tool.invoke(toolCall);
}); //STARTTEMP
return {messages: [new AIMessage("yeman")]}
//ENDTEMP
if (lastMessage == null || !AIMessage.isInstance(lastMessage)) {
return { messages: [] };
}
const result: ToolMessage[] = [];
for (const toolCall of lastMessage.tool_calls ?? []) {
const tool = arithmeticToolsByName[toolCall.name];
const observation = await tool.invoke(toolCall);
result.push(observation);
}
return { messages: result };
};
+9
View File
@@ -0,0 +1,9 @@
import { GraphNode } from "@langchain/langgraph";
import { MessagesState } from "../state";
import { HumanMessage } from "@langchain/core/messages";
export const verificationSetup: GraphNode<typeof MessagesState> = async (state) => {
//TODO: this might not be needed, looks nice on the graph tho
return { messages: [ new HumanMessage(state.messages.at(-1)?.content ?? "undefined")] };
};
+3 -3
View File
@@ -8,9 +8,9 @@ const streamResponse = client.runs.stream(
thread["thread_id"], thread["thread_id"],
"agent", "agent",
{ {
input: [ input: {
{ role: "user", content: "3+5" } "messages": ["3+5" ]
], },
streamMode: "messages-tuple", streamMode: "messages-tuple",
} }
); );
+17
View File
@@ -0,0 +1,17 @@
import {
StateGraph,
StateSchema,
MessagesValue,
ReducedValue,
GraphNode,
ConditionalEdgeRouter,
START,
END,
} from "@langchain/langgraph";
import { z } from "zod/v4";
export const MessagesState = new StateSchema({
messages: MessagesValue,
// normalizationContext: z.map(z.string(), z.string()),
disinformationTitle: z.string()
});