Implementation:Spcl Graph of thoughts Selector Operation
| Knowledge Sources | |
|---|---|
| Domains | Graph_Reasoning, Thought_Operations |
| Last Updated | 2026-02-14 |
| Implements | Principle:Spcl_Graph_of_thoughts_Thought_Selection |
Overview
Implementation of the thought selection pattern that partitions thoughts from predecessors using a custom selection function, enabling different subsequent operations on different thought subsets.
Description
The Selector class is a concrete operation in the Graph of Thoughts framework that applies a user-defined selection function to choose a subset of thoughts from predecessors. It is implemented as a subclass of Operation with operation type OperationType.selector.
The execution flow is:
- Retrieve all predecessor thoughts via
get_previous_thoughts() - If there are no predecessor thoughts (including when there are no predecessors), create a single thought from the operation's
kwargs(passed from the Controller) as a fallback - Apply the selector function to the list of thoughts
- Clone each selected thought via
Thought.from_thought() - Store the selected thoughts and log the count
This operation does not interact with the language model, Prompter, or Parser. Unlike most other operations, the Selector does not assert that it has predecessors -- it can operate as an entry point by using kwargs to create an initial thought.
Usage
from graph_of_thoughts.operations import Selector
# Select a specific sublist by index
sel_first = Selector(selector=lambda thoughts: [thoughts[0]])
sel_second = Selector(selector=lambda thoughts: [thoughts[1]])
# Select thoughts matching a condition
sel_large = Selector(
selector=lambda thoughts: [t for t in thoughts if len(t.state.get("list", [])) > 10]
)
# Wire into graph after a Generate that produces sub-problems
sel_first.add_predecessor(generate_split_op)
sel_second.add_predecessor(generate_split_op)
Code Reference
Source Location
- File:
graph_of_thoughts/operations/operations.py, Lines 840-900 - Import:
from graph_of_thoughts.operations import Selector
Class Signature
class Selector(Operation):
operation_type: OperationType = OperationType.selector
def __init__(self, selector: Callable[[List[Thought]], List[Thought]]) -> None:
"""
Initializes a new Selector operation.
:param selector: A function to select thoughts from the predecessors' thoughts.
:type selector: A function that takes a list of thoughts and returns a list of thoughts.
"""
Key Methods
__init__(self, selector: Callable[[List[Thought]], List[Thought]]) -> None-- Initializes the operation with the selector function and an emptythoughtslist.get_thoughts(self) -> List[Thought]-- Returns the list of selected thoughts after execution._execute(self, lm, prompter, parser, **kwargs) -> None-- Core execution logic: retrieves predecessors (or creates from kwargs), applies selector, clones results.
Internal State
self.selector: Callable[[List[Thought]], List[Thought]]-- The user-defined selection function.self.thoughts: List[Thought]-- Stores the selected thoughts after execution.
I/O Contract
| Input | Output | Side Effects |
|---|---|---|
Predecessor thoughts (or kwargs as Thought if no predecessors). When predecessors exist, all their thoughts are collected. When no predecessor thoughts are available, a single Thought(kwargs) is created from the Controller's keyword arguments.
|
Selected subset of thoughts (cloned) -- new Thought objects for each thought returned by the selector function. The number and identity of output thoughts depends entirely on the selector function's logic.
|
No language model interaction. Logs selected thoughts at DEBUG level and count at INFO level. |
Selection logic:
previous_thoughts = self.get_previous_thoughts()
if len(previous_thoughts) == 0:
previous_thoughts = [Thought(kwargs)]
self.thoughts = [
Thought.from_thought(thought)
for thought in self.selector(previous_thoughts)
]
No assertions on predecessors: Unlike most operations, Selector does not require predecessors. It gracefully handles the no-predecessor case by creating a thought from kwargs.
Usage Examples
Sorting: Route Sublists to Separate Branches
from graph_of_thoughts.operations import Generate, Selector, Score, KeepBestN
# Generate splits the input list into sublists
gen_split = Generate(num_branches_prompt=1, num_branches_response=1)
# Each Selector routes one sublist to its own processing branch
sel_0 = Selector(selector=lambda thoughts, i=0: [thoughts[i]])
sel_1 = Selector(selector=lambda thoughts, i=1: [thoughts[i]])
sel_0.add_predecessor(gen_split)
sel_1.add_predecessor(gen_split)
# Each branch independently sorts its sublist
gen_sort_0 = Generate(num_branches_prompt=5, num_branches_response=1)
gen_sort_0.add_predecessor(sel_0)
gen_sort_1 = Generate(num_branches_prompt=5, num_branches_response=1)
gen_sort_1.add_predecessor(sel_1)
Conditional Branching
from graph_of_thoughts.operations import Selector
# Route thoughts to different branches based on state content
sel_easy = Selector(
selector=lambda thoughts: [t for t in thoughts if t.state.get("difficulty") == "easy"]
)
sel_hard = Selector(
selector=lambda thoughts: [t for t in thoughts if t.state.get("difficulty") == "hard"]
)
sel_easy.add_predecessor(classification_op)
sel_hard.add_predecessor(classification_op)
Entry Point Without Predecessors
from graph_of_thoughts.operations import Selector
# Use Selector as an entry point to partition initial state
sel = Selector(selector=lambda thoughts: thoughts)
# When executed with kwargs (e.g., via Controller), the kwargs
# are wrapped in a Thought and passed to the selector function.
# Controller.run(initial_state={"input": "some data"})
Related Pages
- Principle:Spcl_Graph_of_thoughts_Thought_Selection - The principle this implementation realizes
- Implementation:Spcl_Graph_of_thoughts_Aggregate_Operation - Aggregate recombines branches created by Selector
- Implementation:Spcl_Graph_of_thoughts_KeepBestN_Operation - Score-based selection (complementary to custom Selector)
- Workflow:Spcl_Graph_of_thoughts_GoT_Sorting_Pipeline - Sorting workflow using Selector for sub-problem routing