| import {defineChatSessionFunction, getLlama, LlamaChatSession} from "node-llama-cpp";
|
| import {fileURLToPath} from "url";
|
| import path from "path";
|
| import {PromptDebugger} from "../../helper/prompt-debugger.js";
|
|
|
| const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
| const debug = false;
|
|
|
| const llama = await getLlama({debug});
|
| const model = await llama.loadModel({
|
| modelPath: path.join(
|
| __dirname,
|
| '..',
|
| '..',
|
| 'models',
|
| 'hf_giladgd_gpt-oss-20b.MXFP4.gguf'
|
| )
|
| });
|
| const context = await model.createContext({contextSize: 2000});
|
|
|
|
|
| const systemPrompt = `You are a mathematical assistant that uses the ReAct (Reasoning + Acting) approach.
|
|
|
| CRITICAL: You must follow this EXACT pattern for every problem:
|
|
|
| Thought: [Explain what calculation you need to do next and why]
|
| Action: [Call ONE tool with specific numbers]
|
| Observation: [Wait for the tool result]
|
| Thought: [Analyze the result and decide next step]
|
| Action: [Call another tool if needed]
|
| Observation: [Wait for the tool result]
|
| ... (repeat as many times as needed)
|
| Thought: [Once you have ALL the information needed to answer the question]
|
| Answer: [Give the final answer and STOP]
|
|
|
| RULES:
|
| 1. Only write "Answer:" when you have the complete final answer to the user's question
|
| 2. After writing "Answer:", DO NOT continue calculating or thinking
|
| 3. Break complex problems into the smallest possible steps
|
| 4. Use tools for ALL calculations - never calculate in your head
|
| 5. Each Action should call exactly ONE tool
|
|
|
| EXAMPLE:
|
| User: "What is 5 + 3, then multiply that by 2?"
|
|
|
| Thought: First I need to add 5 and 3
|
| Action: add(5, 3)
|
| Observation: 8
|
| Thought: Now I need to multiply that result by 2
|
| Action: multiply(8, 2)
|
| Observation: 16
|
| Thought: I now have the final result
|
| Answer: 16`;
|
|
|
| const session = new LlamaChatSession({
|
| contextSequence: context.getSequence(),
|
| systemPrompt,
|
| });
|
|
|
|
|
| const add = defineChatSessionFunction({
|
| description: "Add two numbers together",
|
| params: {
|
| type: "object",
|
| properties: {
|
| a: {
|
| type: "number",
|
| description: "First number"
|
| },
|
| b: {
|
| type: "number",
|
| description: "Second number"
|
| }
|
| },
|
| required: ["a", "b"]
|
| },
|
| async handler(params) {
|
| const result = params.a + params.b;
|
| console.log(`\n π§ TOOL CALLED: add(${params.a}, ${params.b})`);
|
| console.log(` π RESULT: ${result}\n`);
|
| return result.toString();
|
| }
|
| });
|
|
|
| const multiply = defineChatSessionFunction({
|
| description: "Multiply two numbers together",
|
| params: {
|
| type: "object",
|
| properties: {
|
| a: {
|
| type: "number",
|
| description: "First number"
|
| },
|
| b: {
|
| type: "number",
|
| description: "Second number"
|
| }
|
| },
|
| required: ["a", "b"]
|
| },
|
| async handler(params) {
|
| const result = params.a * params.b;
|
| console.log(`\n π§ TOOL CALLED: multiply(${params.a}, ${params.b})`);
|
| console.log(` π RESULT: ${result}\n`);
|
| return result.toString();
|
| }
|
| });
|
|
|
| const subtract = defineChatSessionFunction({
|
| description: "Subtract second number from first number",
|
| params: {
|
| type: "object",
|
| properties: {
|
| a: {
|
| type: "number",
|
| description: "Number to subtract from"
|
| },
|
| b: {
|
| type: "number",
|
| description: "Number to subtract"
|
| }
|
| },
|
| required: ["a", "b"]
|
| },
|
| async handler(params) {
|
| const result = params.a - params.b;
|
| console.log(`\n π§ TOOL CALLED: subtract(${params.a}, ${params.b})`);
|
| console.log(` π RESULT: ${result}\n`);
|
| return result.toString();
|
| }
|
| });
|
|
|
| const divide = defineChatSessionFunction({
|
| description: "Divide first number by second number",
|
| params: {
|
| type: "object",
|
| properties: {
|
| a: {
|
| type: "number",
|
| description: "Dividend (number to be divided)"
|
| },
|
| b: {
|
| type: "number",
|
| description: "Divisor (number to divide by)"
|
| }
|
| },
|
| required: ["a", "b"]
|
| },
|
| async handler(params) {
|
| if (params.b === 0) {
|
| console.log(`\n π§ TOOL CALLED: divide(${params.a}, ${params.b})`);
|
| console.log(` β ERROR: Division by zero\n`);
|
| return "Error: Cannot divide by zero";
|
| }
|
| const result = params.a / params.b;
|
| console.log(`\n π§ TOOL CALLED: divide(${params.a}, ${params.b})`);
|
| console.log(` π RESULT: ${result}\n`);
|
| return result.toString();
|
| }
|
| });
|
|
|
| const functions = {add, multiply, subtract, divide};
|
|
|
|
|
| async function reactAgent(userPrompt, maxIterations = 10) {
|
| console.log("\n" + "=".repeat(70));
|
| console.log("USER QUESTION:", userPrompt);
|
| console.log("=".repeat(70) + "\n");
|
|
|
| let iteration = 0;
|
| let fullResponse = "";
|
|
|
| while (iteration < maxIterations) {
|
| iteration++;
|
| console.log(`--- Iteration ${iteration} ---`);
|
|
|
|
|
| let currentChunk = "";
|
| const response = await session.prompt(
|
| iteration === 1 ? userPrompt : "Continue your reasoning. What's the next step?",
|
| {
|
| functions,
|
| maxTokens: 300,
|
| onTextChunk: (chunk) => {
|
|
|
| process.stdout.write(chunk);
|
| currentChunk += chunk;
|
| }
|
| }
|
| );
|
|
|
| console.log();
|
|
|
| fullResponse += currentChunk;
|
|
|
|
|
| if (!currentChunk.trim() && !response.trim()) {
|
| console.log(" (No output generated this iteration)\n");
|
| }
|
|
|
|
|
| if (response.toLowerCase().includes("answer:") ||
|
| fullResponse.toLowerCase().includes("answer:")) {
|
| console.log("\n" + "=".repeat(70));
|
| console.log("FINAL ANSWER REACHED");
|
| console.log("=".repeat(70));
|
| return fullResponse;
|
| }
|
| }
|
|
|
| console.log("\nβ οΈ Max iterations reached without final answer");
|
| return fullResponse || "Could not complete reasoning within iteration limit.";
|
| }
|
|
|
|
|
| const queries = [
|
|
|
|
|
|
|
| "A store sells 15 items on Monday at $8 each, 20 items on Tuesday at $8 each, and 10 items on Wednesday at $8 each. What's the average number of items sold per day, and what's the total revenue?",
|
| ];
|
|
|
| for (const query of queries) {
|
| await reactAgent(query, 3);
|
| console.log("\n");
|
| }
|
|
|
|
|
| const promptDebugger = new PromptDebugger({
|
| outputDir: './logs',
|
| filename: 'react_calculator.txt',
|
| includeTimestamp: true,
|
| appendMode: false
|
| });
|
| await promptDebugger.debugContextState({session, model});
|
|
|
|
|
| session.dispose()
|
| context.dispose()
|
| model.dispose()
|
| llama.dispose() |