### Creating a Custom Node and Running a Basic Flow (TypeScript) Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/guide.md This example shows how to create a custom node (`MyNode`) by extending `BaseNode`, implement its lifecycle methods, connect it to another node using `addSuccessor`, construct a `Flow` starting with the initial node, and execute the flow with an initial state. ```typescript import { BaseNode, Flow, DEFAULT_ACTION } from "pocket"; // Assuming 'pocket' is the package name class MyNode extends BaseNode { async prep(sharedState: any): Promise { console.log("MyNode Prep:", sharedState); // Example: Prepare data based on shared state return { nodeInput: sharedState.initialData }; } async execCore(prepResult: any): Promise { console.log("MyNode ExecCore:", prepResult); // Example: Perform the core task return { resultData: `Processed: ${prepResult.nodeInput}` }; } async post(prepResult: any, execResult: any, sharedState: any): Promise { console.log("MyNode Post:", execResult); // Example: Update shared state sharedState.lastResult = execResult.resultData; // Determine the next action return DEFAULT_ACTION; // Proceed to the default successor } // Required for the flow to function correctly _clone(): BaseNode { return new MyNode(); } } // --- Flow Setup --- // Create node instances const nodeA = new MyNode(); const nodeB = new MyNode(); // Another node instance // Connect nodeA's default action to nodeB nodeA.addSuccessor(nodeB, DEFAULT_ACTION); // Create the flow starting with nodeA const flow = new Flow(nodeA); // Define initial shared state const initialState = { initialData: "Hello PocketFlow!" }; // Run the flow console.log("Starting flow..."); const finalState = await flow.run(initialState); console.log("Flow finished. Final State:", finalState); ``` -------------------------------- ### Example RAG Flow Usage (TypeScript) Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/rag.md Demonstrates how to set up and run the RAG flow. It initializes shared state with sample texts, creates instances of the PrepareEmbeddingsNode and AnswerQuestionNode, connects them sequentially, and starts the flow execution. ```TypeScript /** * Example usage: */ (async () => { const shared: any = { texts: [ "Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.", "TypeScript extends JavaScript by adding types, improving developer productivity.", "Retrieval Augmented Generation (RAG) helps LLMs ground responses in real sources." ] }; // Create the nodes const prepNode = new PrepareEmbeddingsNode(); const answerNode = new AnswerQuestionNode(); // Connect them prepNode.addSuccessor(answerNode, "default"); // Build the Flow const flow = new Flow(prepNode); // Execute the flow (assuming a run method exists in Flow) // await flow.run(shared); })(); ``` -------------------------------- ### Example YAML Output: Product Details Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/structure.md Shows a sample YAML structure representing extracted product information, including name, price, and a multi-line description, as might be generated by an LLM. ```yaml product: name: Widget Pro price: 199.99 description: | A high-quality widget designed for professionals. Suitable for advanced users. ``` -------------------------------- ### Example YAML Output: Server Configuration Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/structure.md Shows a sample YAML structure for generating server configuration settings like host, port, and SSL status, demonstrating how LLMs can be used for configuration file generation. ```yaml server: host: 127.0.0.1 port: 8080 ssl: true ``` -------------------------------- ### Example Usage of Node Call Stack Debugging (TypeScript) Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/viz.md This code snippet provides an example demonstrating how to integrate the `getNodeCallStack` utility into a Pocket Flow node. It defines several example `BaseNode` and `Flow` subclasses, constructs a simple flow, and shows how to call `getNodeCallStack` within a node's method (specifically `prepAsync` in `EvaluateModelNode`) to capture and log the execution path leading to that node. ```TypeScript class DataPrepBatchNode extends BaseNode { async prepAsync(shared: any): Promise { return; } } class ValidateDataNode extends BaseNode {} class FeatureExtractionNode extends BaseNode {} class TrainModelNode extends BaseNode {} class EvaluateModelNode extends BaseNode { async prepAsync(shared: any): Promise { const stack = getNodeCallStack(); console.log("Call stack:", stack); } } class ModelFlow extends Flow {} class DataScienceFlow extends Flow {} // Build the flow const featureNode = new FeatureExtractionNode(); const trainNode = new TrainModelNode(); const evaluateNode = new EvaluateModelNode(); featureNode.addSuccessor(trainNode, "default"); trainNode.addSuccessor(evaluateNode, "default"); const modelFlow = new ModelFlow(featureNode); const dataPrepNode = new DataPrepBatchNode(); const validateNode = new ValidateDataNode(); dataPrepNode.addSuccessor(validateNode, "default"); validateNode.addSuccessor(modelFlow, "default"); const dataScienceFlow = new DataScienceFlow(dataPrepNode); // Run the flow dataScienceFlow.run({}); ``` -------------------------------- ### Orchestrating Sequential Flow Execution - TypeScript Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/preparation.md The Flow class manages the sequential execution of nodes. The orchestrate method starts from a designated start node, sets parameters, runs the node (which includes prep, execWrapper, and post), gets the next node based on the action returned by post, and continues until there is no successor node. ```TypeScript class Flow extends BaseNode { private start: BaseNode; async orchestrate(sharedState: any, flowParams?: any): Promise { let currentNode: BaseNode | undefined = await this.getStartNode(); while (currentNode) { currentNode.setParams((flowParams) ? flowParams : this.flow_params); const action = await currentNode.run(sharedState); currentNode = currentNode.getSuccessor(action); } } } ``` -------------------------------- ### Example Mermaid Visualization Output Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/viz.md This snippet shows the expected Mermaid diagram syntax generated by the `buildMermaid` function when applied to the example `DataScienceFlow`. It illustrates how flows are represented as subgraphs and nodes as boxes, with arrows indicating the connections between them, providing a visual representation of the flow structure. ```Mermaid graph LR subgraph sub_flow_N1[DataScienceFlow] N2["DataPrepBatchNode"] N3["ValidateDataNode"] N2 --> N3 N3 --> N4 subgraph sub_flow_N5[ModelFlow] N4["FeatureExtractionNode"] N6["TrainModelNode"] N4 --> N6 N7["EvaluateModelNode"] N6 --> N7 end end ``` -------------------------------- ### Installing pocket-ts (Bash) Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/usage.md Instructions for installing the pocket-ts library using the npm package manager. ```Bash npm install pocket-ts ``` -------------------------------- ### Defining the BaseNode Abstract Class (TypeScript) Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/guide.md This snippet defines the abstract `BaseNode` class, which is the foundation for all functional units in a Pocket Flow. It requires implementing `prep`, `execCore`, `post` for lifecycle management and `_clone` for instance management. ```typescript abstract class BaseNode { // Prepare input data or transform shared state before core execution abstract prep(sharedState: any): Promise; // Contains the main logic of the node abstract execCore(prepResult: any): Promise; // Processes results, updates shared state, and determines the next step (action) abstract post(prepResult: any, execResult: any, sharedState: any): Promise; // Creates a new instance of the node (required for flow execution) abstract _clone(): BaseNode; } ``` -------------------------------- ### Example YAML Output: Summary Bullet Points Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/structure.md Shows a sample YAML structure representing a list of summary points, typically generated by an LLM when asked to summarize text in a bulleted format. ```yaml summary: - This product is easy to use. - It is cost-effective. - Suitable for novices and experts alike. ``` -------------------------------- ### Implementing AsyncNode and AsyncFlow in Python Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/async.md This snippet demonstrates creating a custom `AsyncNode` called `SummarizeThenVerify` with asynchronous `prep_async`, `exec_async`, and `post_async` methods. It shows how to define transitions between nodes, instantiate an `AsyncFlow`, and run the flow asynchronously using `asyncio.run`. The example simulates reading a file, calling an LLM, and gathering user feedback. ```python class SummarizeThenVerify(AsyncNode): async def prep_async(self, shared): # Example: read a file asynchronously doc_text = await read_file_async(shared["doc_path"]) return doc_text async def exec_async(self, prep_res): # Example: async LLM call summary = await call_llm_async(f"Summarize: {prep_res}") return summary async def post_async(self, shared, prep_res, exec_res): # Example: wait for user feedback decision = await gather_user_feedback(exec_res) if decision == "approve": shared["summary"] = exec_res return "approve" return "deny" summarize_node = SummarizeThenVerify() final_node = Finalize() # Define transitions summarize_node - "approve" >> final_node summarize_node - "deny" >> summarize_node # retry flow = AsyncFlow(start=summarize_node) async def main(): shared = {"doc_path": "document.txt"} await flow.run_async(shared) print("Final Summary:", shared.get("summary")) asyncio.run(main()) ``` -------------------------------- ### Example Usage of SummarizeFile Node - TypeScript Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/node.md Demonstrates how to instantiate the `SummarizeFile` node with configuration, set its parameters, prepare a mock `sharedState` object with input data, and execute the node's workflow asynchronously using `runAsync`. It includes logging the final action and the resulting summary stored in the shared state. ```typescript // Example usage: const summarizeNode = new SummarizeFile({ maxRetries: 3, wait: 10 }); summarizeNode.setParams({ filename: "test_file.txt" }); // Prepare a shared state with data const shared: any = { data: { "test_file.txt": "Once upon a time in a faraway land...", }, }; // node.runAsync(...) calls prepAsync->execAsync->postAsync // If execAsync() fails repeatedly, it calls execFallbackAsync() before postAsync(). summarizeNode.runAsync(shared).then((actionReturned) => { console.log("Action returned:", actionReturned); // Usually "default" console.log("Summary stored:", shared.summary?.["test_file.txt"]); }).catch((error) => { console.error("Node execution error:", error); }); ``` -------------------------------- ### Instantiating and Connecting Pocket Nodes - TypeScript Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/mapreduce.md Code to create instances of the MapSummaries and ReduceSummaries nodes, connect the map node to the reduce node using addSuccessor, and initialize the Pocket Flow starting with the map node. ```typescript const mapNode = new MapSummaries(); const reduceNode = new ReduceSummaries(); // Connect mapNode to reduceNode mapNode.addSuccessor(reduceNode, DEFAULT_ACTION); // Create the Flow const summarizeFlow = new Flow(mapNode); ``` -------------------------------- ### Running the Summarization Flow - TypeScript Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/mapreduce.md An example demonstrating how to execute the configured Pocket Flow. It initializes shared state with input text, runs the flow asynchronously, and logs the final summary stored in the shared state. ```typescript (async () => { const shared: any = { text: "Very large text content goes here...", }; await summarizeFlow.runAsync(shared); console.log("Final summary:", shared.final_summary); })(); ``` -------------------------------- ### AsyncGuesser Logic and Multi-Agent Game Example (TypeScript) Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/multi_agent.md This snippet shows the concluding logic of an AsyncGuesser node, handling incorrect guesses by updating shared state and sending the guess back to the Hinter via a queue. It also includes an example demonstrating how to set up shared state, initialize AsyncHinter and AsyncGuesser nodes within Flow instances, configure self-looping successors for continuous turns, and run the agent flows concurrently to simulate a multi-agent game. ```TypeScript hinterQueue.push("GAME_OVER"); return "end"; } // If guess is wrong, add to pastGuesses if (!sharedState.past_guesses) { sharedState.past_guesses = []; } sharedState.past_guesses.push(execResult); // Send guess to the Hinter for feedback const hinterQueue = sharedState.hinterQueue as string[]; hinterQueue.push(execResult); return "continue"; } } // Example usage (async () => { const shared = { target_word: "nostalgia", forbidden_words: ["memory", "past", "remember", "feeling", "longing"], hinterQueue: [] as string[], guesserQueue: [] as string[], past_guesses: [] as string[], }; console.log("Game starting!"); console.log(`Target word: ${shared.target_word}`); console.log(`Forbidden words: ${shared.forbidden_words}`); // Initialize by sending an empty guess to Hinter shared.hinterQueue.push(""); const hinter = new AsyncHinter(); const guesser = new AsyncGuesser(); // In pocket.ts, you might have AsyncFlow (if your BaseNode variants are async). // For demonstration, assume Flow can handle async as well. const hinterFlow = new Flow(hinter); const guesserFlow = new Flow(guesser); // Connect each node to itself to allow multiple turns hinter.addSuccessor(hinter, "continue"); guesser.addSuccessor(guesser, "continue"); // Start both flows concurrently // Typically you'd want a coordination mechanism like Promise.all or a dedicated runner hinterFlow.runAsync(shared).catch((err) => console.error("Hinter flow failed:", err)); guesserFlow.runAsync(shared).catch((err) => console.error("Guesser flow failed:", err)); })(); ``` -------------------------------- ### Implementing a Custom BatchFlow Node in TypeScript Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/guide.md Defines a TypeScript class `MyBatchFlow` that extends `BatchFlow` to process an array of items. It overrides `prep` to prepare the batch data, `execCore` to process individual items, `post` to handle results and update shared state, and `_clone` for flow instantiation. This demonstrates the core lifecycle methods for batch processing within the framework. ```typescript import { BatchFlow } from "pocket"; // Assuming a base BatchFlow exists class MyBatchFlow extends BatchFlow { // Extend the appropriate BatchFlow base async prep(sharedState: any): Promise { // Example: Return an array of items from shared state return sharedState.itemsToProcess || []; } // execCore would typically process a single item from the batch async execCore(singleItem: any): Promise { console.log("Processing item:", singleItem); return { processedItem: singleItem * 2 }; // Example processing } // post might aggregate results or update state after batch completion async post(prepResult: any, execResult: any, sharedState: any): Promise { // execResult might be an array of results from execCore calls sharedState.batchResults = execResult; return DEFAULT_ACTION; } _clone(): BaseNode { return new MyBatchFlow(); } // Adjust as needed } ``` -------------------------------- ### Extending RetryNode for Automatic Retries (TypeScript) Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/guide.md This snippet illustrates extending the built-in `RetryNode` to create a node that automatically retries its `execCore` method upon failure. It requires implementing the standard lifecycle methods and the `_clone` method, while leveraging the retry logic provided by the base class. ```typescript import { RetryNode } from "pocket"; class MyRetryNode extends RetryNode { constructor(maxRetries: number = 3, retryIntervalMs: number = 1000) { super(maxRetries, retryIntervalMs); } async prep(sharedState: any): Promise { /* ... */ return {}; } async execCore(prepResult: any): Promise { // Example: Simulate potential failure if (Math.random() < 0.5) { throw new Error("Simulated failure"); } return { success: true }; } async post(prepResult: any, execResult: any, sharedState: any): Promise { /* ... */ return DEFAULT_ACTION; } _clone(): BaseNode { return new MyRetryNode(); } } ``` -------------------------------- ### Implementing Search Agent Pocket Flow TypeScript Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/agent.md This snippet defines the core components of the search agent using Pocket Flow's BaseNode. It includes placeholder functions for LLM calls and web search, and three nodes: DecideAction for determining the next step, SearchWebNode for performing searches and updating state, and DirectAnswer for generating the final response. The nodes are connected to create a loop for searching before answering. ```typescript import { BaseNode, Flow, DEFAULT_ACTION } from "../src/pocket"; // Placeholder for an LLM call async function callLLM(prompt: string): Promise { // Example function to call a Large Language Model // Return a YAML-like string (or any structured string) in practice return ` ```yaml action: "search" reason: "Need more results" search_term: "Nobel Prize 2024" ``` `; } // Placeholder for a web search async function searchWeb(searchTerm: string): Promise { // Example function that interacts with an external API return `Search Results for: ${searchTerm}`; } export class DecideAction extends BaseNode { // The 'prep' method extracts data from sharedState to pass into execCore public async prep(sharedState: any): Promise<[string, string]> { const context = sharedState.context ?? "No previous search"; const query = sharedState.query; return [query, context]; } // The main logic calls callLLM to decide whether to search again or to answer public async execCore(inputs: [string, string]): Promise { const [query, context] = inputs; const prompt = ` Given input: ${query} Previous search results: ${context} Should I: 1) Search web for more info 2) Answer with current knowledge Output in YAML: ```yaml action: search/answer reason: Explanation search_term: search phrase if action is search ``` `; const resp = await callLLM(prompt); // Parse YAML from resp (this is example logic; you'd use a real YAML parser) const yamlStr = resp.split("```yaml")[1]?.split("```")[0]?.trim() || ""; // Assume the structure is { action, reason, search_term? } const parsed = { action: "search", reason: "Need more results", search_term: "Nobel Prize 2024" }; // In a real scenario, you'd do something like: // const parsed = yaml.load(yamlStr); // using js-yaml or similar if (parsed.action === "search" && !parsed.search_term) { throw new Error("Missing search_term for 'search' action!"); } return parsed; } public async post(prepResult: [string, string], execResult: any, sharedState: any): Promise { if (execResult.action === "search") { sharedState.search_term = execResult.search_term; } return execResult.action; } } export class SearchWebNode extends BaseNode { public async prep(sharedState: any): Promise { return sharedState.search_term; } public async execCore(searchTerm: string): Promise { return await searchWeb(searchTerm); } public async post(prepResult: string, execResult: string, sharedState: any): Promise { const previous = sharedState.context || []; sharedState.context = [...previous, { term: prepResult, result: execResult }]; // Loop back to the DecideAction node return "decide"; } } export class DirectAnswer extends BaseNode { public async prep(sharedState: any): Promise<[string, any]> { return [sharedState.query, sharedState.context ?? ""]; } public async execCore(inputs: [string, any]): Promise { const [query, context] = inputs; const prompt = `Context: ${JSON.stringify(context)}\nAnswer this query: ${query}`; return await callLLM(prompt); } public async post( prepResult: [string, any], execResult: string, sharedState: any ): Promise { console.log("Answer:", execResult); sharedState.answer = execResult; return DEFAULT_ACTION; // or any string that indicates the flow is done } } // Connect nodes const decide = new DecideAction(); const search = new SearchWebNode(); const answer = new DirectAnswer(); decide.addSuccessor(search, "search"); decide.addSuccessor(answer, "answer"); search.addSuccessor(decide, "decide"); // loop back const flow = new Flow(decide); flow.run({ query: "Who won the Nobel Prize in Physics 2024?" }); ``` -------------------------------- ### Creating and Running a Sequential Flow with Pocket-TS (TypeScript) Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/usage.md This snippet defines three custom nodes (`GreetNode`, `GetNameNode`, `FarewellNode`) extending `BaseNode`, representing steps in a simple sequential process. It then instantiates these nodes, links them using `addSuccessor` to define the flow sequence, creates a `Flow` object starting with the `GreetNode`, and finally runs the flow asynchronously. It demonstrates the basic pattern of defining nodes, chaining them, and executing a flow in `pocket-ts`. ```TypeScript import { BaseNode, Flow, DEFAULT_ACTION } from "pocket-ts"; // Node #1: Greet user class GreetNode extends BaseNode { public async execAsync(_: unknown): Promise { return "Hello! What's your name?"; } public async postAsync(sharedState: any, _: any, greeting: string): Promise { console.log(greeting); return DEFAULT_ACTION; } } // Node #2: Get user's name (mock input) class GetNameNode extends BaseNode { public async execAsync(_: unknown): Promise { return "Alice"; // In real life, you'd ask or read from user input } public async postAsync(sharedState: any, _: any, name: string): Promise { sharedState.userName = name; return DEFAULT_ACTION; } } // Node #3: Personalize farewell class FarewellNode extends BaseNode { public async execAsync(_: unknown): Promise { return "Nice to meet you!"; } public async postAsync(sharedState: any, _: any, farewell: string): Promise { console.log(`${farewell} Goodbye, ${sharedState.userName}!`); return DEFAULT_ACTION; } } // Build flow const greetNode = new GreetNode(); const nameNode = new GetNameNode(); const farewellNode = new FarewellNode(); greetNode.addSuccessor(nameNode, DEFAULT_ACTION); nameNode.addSuccessor(farewellNode, DEFAULT_ACTION); const flow = new Flow(greetNode); // Run flow flow.runAsync({}).then(() => { console.log("Flow complete!"); }); ``` -------------------------------- ### Connecting Nodes into a Flow (TypeScript) Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/usage.md Shows how to create instances of different node classes and link them sequentially using the `addSuccessor` method with a specified action string (like `DEFAULT_ACTION`), finally creating a `Flow` starting from the first node. ```TypeScript class AskFavoriteColorNode extends BaseNode { // ... } class RespondColorNode extends BaseNode { // ... } // Create instances const greetNode = new GreetNode(); const askColorNode = new AskFavoriteColorNode(); const respondColorNode = new RespondColorNode(); // Connect greetNode.addSuccessor(askColorNode, DEFAULT_ACTION); askColorNode.addSuccessor(respondColorNode, DEFAULT_ACTION); // Build a Flow that starts with greetNode const flow = new Flow(greetNode); ``` -------------------------------- ### Implementing BatchNode for Chunk Processing (TypeScript) Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/batch.md Demonstrates how to create a BatchNode by implementing the prep, execCore, and post methods. The prep method splits the input into chunks, execCore processes each chunk individually, and post combines the results. Includes example usage within a standard Flow. ```typescript import { BaseNode, Flow, DEFAULT_ACTION } from "../src/pocket"; class MapSummaries extends BaseNode { // The 'prep' method returns chunks to process public async prep(sharedState: any): Promise { const content = sharedState.data["large_text.txt"] || ""; const chunkSize = 10000; const chunks = []; for (let i = 0; i < content.length; i += chunkSize) { chunks.push(content.substring(i, i + chunkSize)); } return chunks; } // The 'execCore' method processes each chunk public async execCore(chunk: string): Promise { const prompt = `Summarize this chunk in 10 words: ${chunk}`; return await callLLM(prompt); } // The 'post' method combines all summaries public async post(prepResult: string[], execResult: string, sharedState: any): Promise { sharedState.summary["large_text.txt"] = execResult; return DEFAULT_ACTION; } } // Create and run the flow const mapSummaries = new MapSummaries(); const flow = new Flow(mapSummaries); await flow.run({ data: { "large_text.txt": "Your very large text content goes here..." }, summary: {} }); ``` -------------------------------- ### Parsing LLM Response js-yaml TypeScript Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/agent.md This snippet shows how to replace the placeholder YAML parsing logic in the DecideAction node's execCore method with the 'js-yaml' library for robust parsing of the LLM's structured output. ```typescript import yaml from 'js-yaml'; // Inside execCore const parsed = yaml.load(yamlStr) as { action: string; reason: string; search_term?: string }; ``` -------------------------------- ### Conditional Branching Flow - Pocket Flow Framework - TypeScript Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/apps.md This example illustrates how to create a flow with conditional branching. A `ValidationNode` determines the next step based on its output, directing the flow to either a `SuccessNode` or an `ErrorNode` using named successors. ```typescript class ValidationNode extends BaseNode { async prep(sharedState: any) { return sharedState.data; } async execCore(data: any) { return data.isValid; } async post(prepResult: any, execResult: any, sharedState: any) { return execResult ? "valid" : "invalid"; } } // Create nodes const validator = new ValidationNode(); const successHandler = new SuccessNode(); const errorHandler = new ErrorNode(); // Set up branching validator.addSuccessor(successHandler, "valid"); validator.addSuccessor(errorHandler, "invalid"); // Create and run flow const flow = new Flow(validator); await flow.run({ data: { isValid: true } }); ``` -------------------------------- ### Implementing Agent Communication with Shared Queue using Pocket Flow (TypeScript) Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/multi_agent.md This snippet demonstrates how to create an agent (`AgentNode`) that communicates by polling a shared message queue. The agent uses `prepAsync` to dequeue a message, `execAsync` to process it, and returns "loop" in `postAsync` to continuously check for new messages. The example includes setting up the flow, the shared queue, and a simple message generator. ```TypeScript import { BaseNode, Flow, DEFAULT_ACTION } from "../src/pocket"; // We'll define a simple queue interface interface MessageQueue { messages: string[]; // optional signals or a real concurrency approach } // This is our AgentNode, which reads a message from the queue each time it runs. // For demonstration, we poll the queue at intervals to simulate asynchronous arrival of messages. export class AgentNode extends BaseNode { public async prepAsync(sharedState: any): Promise { const messageQueue: MessageQueue = this.params["messages"] as MessageQueue; if (!messageQueue || !Array.isArray(messageQueue.messages)) { throw new Error("Invalid message queue"); } // Wait until there's at least one message or return null if no messages are pending if (messageQueue.messages.length === 0) { return null; } // Dequeue the first message return messageQueue.messages.shift() || null; } public async execAsync(message: string | null): Promise { if (message === null) { // No new message return null; } // Process or log the message console.log(`Agent received: ${message}`); // You could also call an LLM or do more sophisticated processing here return message; } public async postAsync(sharedState: any, prepResult: string | null, execResult: string | null): Promise { // We can continue if there's more work to do; otherwise, we might wait or exit. // For this example, we just loop forever (polling), so we return "loop" return "loop"; } } // Example usage (async () => { // Our simulated queue const messageQueue: MessageQueue = { messages: [], }; // Create the agent node const agent = new AgentNode(); // Connect the agent node to itself on the "loop" action agent.addSuccessor(agent, "loop"); // Create a flow starting from our agent const flow = new Flow(agent); // Set the flow params so the agent node knows about the queue flow.setParams({ messages: messageQueue }); // We'll also define a simple message generator that appends to the queue periodically setInterval(() => { const timestamp = Date.now(); const sampleMessages = [ "System status: all systems operational", "Memory usage: normal", "Network connectivity: stable", "Processing load: optimal", ]; const msg = `${sampleMessages[timestamp % sampleMessages.length]} | timestamp_${timestamp}`; messageQueue.messages.push(msg); }, 1000); // Run the flow const shared = {}; // Because it loops indefinitely, this flow never truly ends unless we forcibly stop it. // In a real app, you'd have a stopping condition or signal. flow.runAsync(shared).catch((err) => { console.error("Flow execution failed:", err); }); })(); ``` -------------------------------- ### Performing Google Search via SerpAPI (Python) Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/tool.md Defines a Python function to perform a Google search by calling the SerpAPI endpoint. It requires the 'requests' library and a SerpAPI key. The function takes a query string as input, sends a GET request to the SerpAPI, and returns the search results as a JSON object. ```python def search_google(query): import requests params = { "engine": "google", "q": query, "api_key": "YOUR_API_KEY" } r = requests.get("https://serpapi.com/search", params=params) return r.json() ``` -------------------------------- ### Creating a Simple Sequence Flow in TypeScript Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/flow.md This snippet demonstrates how to define two simple nodes (NodeA, NodeB) extending BaseNode, connect them sequentially using addSuccessor with the "default" action, instantiate a Flow starting with NodeA, and run the flow with an initial shared state. It shows the basic structure of defining nodes, setting up transitions, and executing a flow. ```typescript import { BaseNode, Flow, DEFAULT_ACTION } from "../src/pocket"; // Define NodeA class NodeA extends BaseNode { public async prepAsync(sharedState: any): Promise { // Preparation logic for NodeA } public async execAsync(_: void): Promise { // Execution logic for NodeA } public async postAsync(sharedState: any, _: void, __: void): Promise { // Transition to NodeB return "default"; } } // Define NodeB class NodeB extends BaseNode { public async prepAsync(sharedState: any): Promise { // Preparation logic for NodeB } public async execAsync(_: void): Promise { // Execution logic for NodeB } public async postAsync(sharedState: any, _: void, __: void): Promise { // No further nodes to transition to return DEFAULT_ACTION; } } // Instantiate nodes const nodeA = new NodeA(); const nodeB = new NodeB(); // Define the flow connections nodeA.addSuccessor(nodeB, "default"); // Create the flow starting with nodeA const flow = new Flow(nodeA); // Initial shared state const sharedState = {}; // Run the flow flow.runAsync(sharedState).then(() => { console.log("Flow completed successfully."); }).catch(error => { console.error("Flow execution failed:", error); }); ``` -------------------------------- ### Implementing Retry Execution Wrapper - TypeScript Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/preparation.md This example shows a RetryNode that extends BaseNode and overrides execWrapper. It attempts to execute execCore multiple times, waiting for a specified interval between attempts, before throwing an error if the maximum number of retries is reached. ```TypeScript abstract class RetryNode extends BaseNode { constructor(maxRetries: number, intervalMs: number) { super(); this.maxRetries = maxRetries; this.intervalMs = intervalMs; } public async execWrapper(prepResult: any): Promise { for (let i = 0; i < this.maxRetries; i++) { try { return await this.execCore(prepResult); } catch (error) { await new Promise(resolve => setTimeout(resolve, this.intervalMs)); } } throw new Error("Max retries reached after " + this.maxRetries + " attempts"); } } ``` -------------------------------- ### Data Processing with Retry Logic - Pocket Flow Framework - TypeScript Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/apps.md This example shows how to implement retry logic for operations that might fail, such as API calls, by extending the `RetryNode`. It configures the number of retries and the interval between attempts, simulating a potentially failing API call within the `execCore` method. ```typescript class ApiNode extends RetryNode { constructor() { super(3, 1000); // 3 retries, 1 second interval } async prep(sharedState: any) { return sharedState.apiEndpoint; } async execCore(endpoint: string) { // Simulate API call that might fail const response = await fetch(endpoint); if (!response.ok) throw new Error("API call failed"); return response.json(); } async post(prepResult: any, execResult: any, sharedState: any) { sharedState.apiResponse = execResult; return DEFAULT_ACTION; } } ``` -------------------------------- ### Implement Node for LLM YAML Summary (TypeScript) Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/structure.md Demonstrates a TypeScript class (`SummarizeNode`) within the Pocket Flow Framework that prompts an LLM to generate a 3-bullet-point summary in YAML format, then parses and validates the YAML response. It uses `prepAsync` to get input, `execAsync` to call the LLM, and `postAsync` to process the output, including error handling for invalid YAML. ```typescript import { BaseNode, DEFAULT_ACTION } from "../src/pocket"; import { callLLM } from "../path/to/your/llm-wrapper"; /** * SummarizeNode: * 1) Prepares a prompt with instructions for YAML output. * 2) Calls the LLM to generate the structured YAML. * 3) Parses the YAML and validates the result. */ export class SummarizeNode extends BaseNode { public async prepAsync(sharedState: any): Promise { // Grab the text to summarize const textToSummarize: string = sharedState.text ?? "No text provided."; return textToSummarize; } public async execAsync(text: string): Promise { // Construct a prompt that instructs the LLM to return exactly 3 bullet points in YAML const prompt = ` Please summarize the following text in YAML with exactly 3 bullet points. Text: ${text} The YAML should look like this: ```yaml summary: - bullet 1 - bullet 2 - bullet 3 ``` Only return the YAML (including the fences). `; // Call the LLM with your custom logic const response = await callLLM(prompt); return response; } public async postAsync( sharedState: any, prepResult: string, llmResponse: string ): Promise { // Extract the YAML content between the fences let yamlStr = ""; try { const startTag = "```yaml"; const endTag = "```"; const startIndex = llmResponse.indexOf(startTag); const endIndex = llmResponse.indexOf(endTag, startIndex + startTag.length); if (startIndex !== -1 && endIndex !== -1) { yamlStr = llmResponse.substring(startIndex + startTag.length, endIndex).trim(); } else { throw new Error("LLM response did not contain valid ```yaml``` fences."); } // Parse the YAML // (You might need "js-yaml" or similar library for safe YAML parsing.) const yaml = await import("js-yaml"); const structResult = yaml.load(yamlStr); // Validate the structure if (!structResult || typeof structResult !== "object") { throw new Error("Parsed result is not a valid YAML object."); } if (!("summary" in structResult) || !Array.isArray((structResult as any).summary)) { throw new Error("Expected a 'summary' array in the YAML output."); } // Save the structured output sharedState.summary = structResult; } catch (err) { // Optionally retry or provide fallback. For now, just log and store an error. console.error("Error parsing YAML from LLM:", err); sharedState.summary = { error: "Invalid YAML output from LLM." }; } return DEFAULT_ACTION; // Continue the flow } } ``` -------------------------------- ### YAML Multiline String Example Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/structure.md Illustrates using the YAML | block literal style to represent multiline strings naturally. This method preserves newlines and quotes without special escaping, making YAML more readable and often preferred for LLM outputs. ```yaml dialogue: | Alice said: "Hello Bob. How are you? I am good." ``` -------------------------------- ### Generating Mermaid Visualization Syntax (TypeScript) Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/viz.md This function recursively traverses a Pocket Flow graph starting from a given node or flow. It assigns unique IDs to each node and flow, treating flows as Mermaid subgraphs. The function generates and returns a string containing the Mermaid syntax required to visualize the graph structure, including nodes and connections. It handles cycles by tracking visited nodes. ```TypeScript import { BaseNode, Flow } from "../src/pocket"; export function buildMermaid(start: BaseNode | Flow): string { const ids = new Map(); const visited = new Set(); const lines: string[] = ["graph LR"]; let counter = 1; function getId(node: any): string { if (!ids.has(node)) { ids.set(node, `N${counter++}`); } return ids.get(node)!; } function link(a: string, b: string): void { lines.push(` ${a} --> ${b}`); } function walk(node: any, parent?: string): void { if (visited.has(node)) { if (parent) link(parent, getId(node)); return; } visited.add(node); if (node instanceof Flow) { if (node.start && parent) { link(parent, getId(node.start)); } lines.push(`\n subgraph sub_flow_${getId(node)}[${node.constructor.name}]`); if (node.start) { walk(node.start); } for (const nxt of node.successors.values()) { if (node.start) { walk(nxt, getId(node.start)); } else if (parent) { link(parent, getId(nxt)); } else { walk(nxt); } } lines.push(" end\n"); } else { const nodeId = getId(node); lines.push(` ${nodeId}["${node.constructor.name}"]`); if (parent) link(parent, nodeId); for (const nxt of node.successors.values()) { walk(nxt, nodeId); } } } walk(start); return lines.join("\n"); } ``` -------------------------------- ### Cloning the Repository - Bash Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/README.md Instructions to clone the Pocket-Flow-Framework repository from GitHub and navigate into the project directory using bash commands. ```bash git clone https://github.com/The-Pocket-World/Pocket-Flow-Framework.git cd Pocket-Flow-Framework ``` -------------------------------- ### Running a RAG Flow - JavaScript Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/rag.md This snippet demonstrates how to configure a node by setting its parameters and then execute the entire flow asynchronously. It shows a common pattern in the Pocket Flow Framework for initiating a process, specifically within the context of a Retrieval Augmented Generation (RAG) flow where the 'answerNode' likely processes the provided question. ```JavaScript // Optionally set the question as a param for answerNode answerNode.setParams({ question: "What is Node.js concurrency model?" }); // Run the flow await flow.runAsync(shared); })(); ``` -------------------------------- ### Implementing Vector Database with Faiss (Python) Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/tool.md Provides Python functions to create and search a vector index using the Faiss library. Requires 'faiss' and 'numpy'. 'create_index' builds an L2 index from a list of embeddings, and 'search_index' performs a k-nearest neighbor search on the index with a query embedding. ```python import faiss import numpy as np def create_index(embeddings): dim = len(embeddings[0]) index = faiss.IndexFlatL2(dim) index.add(np.array(embeddings).astype('float32')) return index def search_index(index, query_embedding, top_k=5): D, I = index.search( np.array([query_embedding]).astype('float32'), top_k ) return I, D index = create_index(embeddings) search_index(index, query_embedding) ``` -------------------------------- ### Defining BaseNode Core Methods - TypeScript Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/preparation.md The abstract BaseNode class serves as the foundation for all nodes in the framework. It defines the essential lifecycle methods that every concrete node implementation must provide: prep for data preparation, execCore for the main logic, post for result processing and determining the next action, and _clone for creating copies, necessary for parallel execution. ```TypeScript abstract class BaseNode { // Prepare data for execution abstract prep(sharedState: any): Promise; // Core execution logic abstract execCore(prepResult: any): Promise; // Post-processing and determine next action abstract post(prepResult: any, execResult: any, sharedState: any): Promise; // Required for cloning in parallel execution abstract _clone(): BaseNode; } ``` -------------------------------- ### Visualizing Branching and Looping Flow with Mermaid Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/flow.md Provides a Mermaid flowchart diagram that visually represents the expense approval flow defined in the TypeScript example, showing the nodes and the action-based transitions between them, including the loop for revisions. ```mermaid flowchart TD reviewExpense[Review Expense] -->|approved| payment[Process Payment] reviewExpense -->|needs_revision| reviseExpense[Revise Report] reviewExpense -->|rejected| finish[Finish Process] reviseExpense --> reviewExpense payment --> finish ``` -------------------------------- ### Document Processing Pipeline - Pocket Flow Framework - TypeScript Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/apps.md This snippet demonstrates a basic sequential document processing pipeline using the Pocket Flow Framework. It defines nodes for loading and extracting text from a document, connects them sequentially, and shows how to run the flow with initial state. ```typescript import { BaseNode, Flow, DEFAULT_ACTION } from "../src/pocket"; class DocumentLoaderNode extends BaseNode { async prep(sharedState: any) { return sharedState.documentPath; } async execCore(path: string) { // Simulate loading document return { content: "Sample document content" }; } async post(prepResult: any, execResult: any, sharedState: any) { sharedState.document = execResult; return DEFAULT_ACTION; } } class TextExtractorNode extends BaseNode { async prep(sharedState: any) { return sharedState.document; } async execCore(document: any) { return document.content.toLowerCase(); } async post(prepResult: any, execResult: any, sharedState: any) { sharedState.extractedText = execResult; return DEFAULT_ACTION; } } // Create and connect nodes const loader = new DocumentLoaderNode(); const extractor = new TextExtractorNode(); loader.addSuccessor(extractor); // Create flow const docFlow = new Flow(loader); // Run the flow await docFlow.run({ documentPath: "path/to/document.pdf" }); ``` -------------------------------- ### Performing Text-to-Speech with pyttsx3 (Python) Source: https://github.com/the-pocket-world/pocket-flow-framework/blob/main/docs/tool.md Defines a Python function to convert text to speech using the 'pyttsx3' library. It requires the 'pyttsx3' library. The function initializes the TTS engine, queues the text to be spoken, and waits for the speech to complete using the system's available voices. ```python def text_to_speech(text): import pyttsx3 engine = pyttsx3.init() engine.say(text) engine.runAndWait() ```