> ## Documentation Index
> Fetch the complete documentation index at: https://docs.inworld.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a Custom Node with Multiple Inputs

This template demonstrates how to use Custom Nodes with more than one input in a graph. This is possible because you can pass an arbitrary number of inputs to a Custom Node's `process()` method.

In our example, we'll ask two LLMs to write a poem, and then pass both outputs to a third LLM to choose which poem is better. This results in a graph which can be visualized like this:

<img src="https://mintcdn.com/inworldai/au0dXjR_jWXAG2ts/img/multi-input/multi-input.png?fit=max&auto=format&n=au0dXjR_jWXAG2ts&q=85&s=23c4e5ea61e20c911b9eaef374cd5b16" alt="" width="781" height="131" data-path="img/multi-input/multi-input.png" />

<Note>
  **Architecture**

  * **Backend:** Inworld Agent Runtime
  * **Frontend:** N/A (CLI example)
</Note>

## Prerequisites

* Node.js v20 or higher: [Download here](https://nodejs.org/en/download)
* Inworld API key (required): [Sign up here](https://platform.inworld.ai/signup) or see [quickstart guide](/node/authentication#getting-an-api-key)

## Getting Started

1. Clone the [templates repository](https://github.com/inworld-ai/inworld-runtime-templates-node):
   ```bash theme={"system"}
   git clone https://github.com/inworld-ai/inworld-runtime-templates-node
   cd inworld-runtime-templates-node
   ```
2. In that project folder, run `npm install` to download the Inworld Agent Runtime and other dependencies
3. Create an .env file and add your Base64 [Runtime API key](/node/authentication):

```.env .env theme={"system"}
INWORLD_API_KEY=<your_api_key>
```

4. Create a new file called `multi-input.ts` and paste in the following code:

```typescript multi-input.ts  theme={"system"}
import 'dotenv/config';

import {
  CustomNode,
  GraphBuilder,
  GraphTypes,
  ProcessContext,
  RemoteLLMChatNode
} from '@inworld/runtime/graph';

let poemPrompt = "Return ONLY a limerick about: "
let reviewPrompt = `Review these two poems and analyze which one is better.`

// Define a custom node which turns a prompt into messages for an LLM
class PoemPromptNode extends CustomNode {
    process(context: ProcessContext, input: string): GraphTypes.LLMChatRequest {
        let composedPrompt = poemPrompt + input
        return new GraphTypes.LLMChatRequest({
            messages: [
                {
                    role: 'user',
                    content: composedPrompt,
                },
            ]
        });
    }
}

class ReviewPromptNode extends CustomNode {
    process(context: ProcessContext, poem1: GraphTypes.Content, poem2: GraphTypes.Content): GraphTypes.LLMChatRequest {
        let composedPrompt = `${reviewPrompt}\n\nPoem 1:\n\n${poem1.content}\n\nPoem 2:\n\n${poem2.content}`
        return new GraphTypes.LLMChatRequest({
            messages: [
                {
                    role: 'user',
                    content: composedPrompt,
                },
            ]
        });
    }
}

let reviewPromptNode = new ReviewPromptNode({
    id: 'review-prompt-node',
});
let poemPromptNode = new PoemPromptNode({
    id: 'poem-prompt-node',
});
let openaiLLMNode = new RemoteLLMChatNode({
    id: 'openai-llm-node',
    modelName: 'gpt-4o-mini',
    provider: 'openai',
    textGenerationConfig: {
        maxNewTokens: 1000,
    },
    reportToClient: true
});

let anthropicLLMNode = new RemoteLLMChatNode({
    id: 'anthropic-llm-node',
    modelName: 'claude-3-5-haiku-latest',   
    provider: 'anthropic',
    textGenerationConfig: {
        maxNewTokens: 1000,
    },
    reportToClient: true
});

let googleLLMNode = new RemoteLLMChatNode({
    id: 'google-llm-node',
    modelName: 'gemini-2.0-flash',  
    provider: 'google',
    textGenerationConfig: {
        maxNewTokens: 1000,
    },
    reportToClient: true
});

// Creating a graph builder instance and adding the node to it
const graphBuilder = new GraphBuilder({
  id: 'custom-text-node',
  apiKey: process.env.INWORLD_API_KEY,
  enableRemoteConfig: false
})
  .addNode(poemPromptNode)
  .addNode(reviewPromptNode)
  .addNode(openaiLLMNode)
  .addNode(anthropicLLMNode)
  .addNode(googleLLMNode)
  .addEdge(poemPromptNode, openaiLLMNode)
  .addEdge(poemPromptNode, anthropicLLMNode)
  .addEdge(anthropicLLMNode, reviewPromptNode)
  .addEdge(openaiLLMNode, reviewPromptNode)
  .addEdge(reviewPromptNode, googleLLMNode)
  .setStartNode(poemPromptNode)
  .setEndNode(googleLLMNode);

// Creating an executor instance from the graph builder
const executor = graphBuilder.build();
executor.visualize('graph.png')

main();

// Main function that executes the graph
async function main() {
  // Execute graph and waiting for output stream to be returned.
  const { outputStream } = await executor.start('pizza');
  for await (const event of outputStream) {
    await event.processResponse({
        Content: (data: GraphTypes.Content) => {
            console.log(`\n${data.content}\n`)
        },
    })
  }
}
```

5. Run `npx ts-node multi-input.ts` to run the graph, observing both the poems and the review
