start adding dummy nodes
This commit is contained in:
@@ -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}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"dependencies": ["."],
|
"dependencies": ["."],
|
||||||
"graphs": {
|
"graphs": {
|
||||||
"agent": "./index.ts:agent"
|
"agent": "./agent.ts:agent"
|
||||||
},
|
},
|
||||||
"env": ".env"
|
"env": ".env"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")]
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -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)]
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -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)]
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -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
@@ -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,
|
||||||
|
// };
|
||||||
|
// };
|
||||||
@@ -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)] };
|
||||||
|
};
|
||||||
+22
-5
@@ -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 lastMessage = state.messages.at(-1);
|
||||||
|
|
||||||
|
//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 tool = arithmeticToolsByName[toolCall.name];
|
||||||
return tool.invoke(toolCall);
|
const observation = await tool.invoke(toolCall);
|
||||||
});
|
result.push(observation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { messages: result };
|
||||||
|
};
|
||||||
@@ -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
@@ -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",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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()
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user