I am making a python langgraph application, and I have defined a bunch of nodes on their own classes but every single one of them has repeated code, how can I make a parent class that has these repeated code?

I will paste two classes and the rest are similar:

class TableFinder:
    def __init__(self, config, name="tablefinder"):
        self.prompt_template = ChatPromptTemplate.from_template(config['prompts']['tablefinder']['template'])
        self.llm = ChatOpenAI(
            model=config['nodes']['tablefinder']['model'],
            openai_api_key=os.environ.get("OPENROUTER_API_KEY"),
            openai_api_base="https://openrouter.ai/api/v1",
        )
        self.name = name

    async def run(self, state):
        #...

        if "prompts" not in state:
            state["prompts"] = []

        if "node_name" not in state:
            state["node_name"] = []

        if "llm" not in state:
            state["llm"] = []

        if "elapsed_time" not in state:
            state["elapsed_time"] = []

        if "usage_metadata" not in state:
            state["usage_metadata"] = []
        
        if "custom_metadata" not in state:
            state["custom_metadata"] = {}
        
        formatted_prompt_str = "\n".join([msg.content for msg in formatted_prompt])
        state["prompts"].append(formatted_prompt_str)
        state["node_name"].append(self.name)
        state["elapsed_time"].append(elapsed_time)
        state["llm"].append(self.llm.model_name)
        state["usage_metadata"].append(response.usage_metadata)

        return state

class RulesFinder:
    def __init__(self, config, name="rulesfinder"):
        self.prompt_template = ChatPromptTemplate.from_template(config['prompts']['rulesfinder']['template'])
        self.llm = ChatOpenAI(
            model=config['nodes']['rulesfinder']['model'],
            openai_api_key=os.environ.get("OPENROUTER_API_KEY"),
            openai_api_base="https://openrouter.ai/api/v1", 
            temperature=config['nodes']['rulesfinder']['temperature']
        )
        self.name = name
    
    async def run(self, state):
        #...
        
        if "prompts" not in state:
            state["prompts"] = []

        if "node_name" not in state:
            state["node_name"] = []

        if "llm" not in state:
            state["llm"] = []

        if "elapsed_time" not in state:
            state["elapsed_time"] = []

        if "usage_metadata" not in state:
            state["usage_metadata"] = []
                
        if "custom_metadata" not in state:
            state["custom_metadata"] = {}
        
        formatted_prompt_str = "\n".join([msg.content for msg in formatted_prompt])
        state["prompts"].append(formatted_prompt_str)
        state["node_name"].append(self.name)
        state["elapsed_time"].append(elapsed_time)
        state["llm"].append(self.llm.model_name)
        state["usage_metadata"].append(response.usage_metadata)

        return state

# Graph creation

class State(TypedDict):
    llm: List[str]
    usage_metadata: List[dict]
    prompts: List[str]
    messages: Annotated[list, add_messages]
    node_name: List[str]
    elapsed_time: List[float]
    custom_metadata: dict

graph_builder = StateGraph(State)

tablefinder_node = TableFinder(hub_config, name="tablefinder")
rulesfinder_node = RulesFinder(hub_config, name="rulesfinder")
#...