treequest 0.2.0__tar.gz → 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- treequest-0.2.0/README.md → treequest-0.3.1/PKG-INFO +123 -11
- treequest-0.2.0/PKG-INFO → treequest-0.3.1/README.md +87 -31
- {treequest-0.2.0 → treequest-0.3.1}/pyproject.toml +27 -2
- {treequest-0.2.0 → treequest-0.3.1}/src/treequest/__init__.py +2 -0
- {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/ab_mcts_a/algo.py +5 -3
- {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/ab_mcts_m/algo.py +7 -5
- {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/base.py +2 -2
- {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/best_first_search.py +5 -3
- {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/multi_armed_bandit_ucb.py +7 -3
- {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/standard_mcts.py +5 -3
- {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/tree.py +13 -3
- {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/tree_of_thought_bfs.py +3 -3
- {treequest-0.2.0 → treequest-0.3.1}/src/treequest/trial.py +1 -3
- {treequest-0.2.0 → treequest-0.3.1}/src/treequest/types.py +5 -1
- treequest-0.3.1/src/treequest/vis/__init__.py +82 -0
- treequest-0.3.1/src/treequest/vis/algo_adapters/__init__.py +68 -0
- treequest-0.3.1/src/treequest/vis/algo_adapters/ab_mcts_a.py +116 -0
- treequest-0.3.1/src/treequest/vis/algo_adapters/ab_mcts_m.py +78 -0
- treequest-0.3.1/src/treequest/vis/algo_adapters/base.py +39 -0
- treequest-0.3.1/src/treequest/vis/algo_adapters/best_first_search.py +52 -0
- treequest-0.3.1/src/treequest/vis/algo_adapters/multi_armed_bandit_ucb.py +99 -0
- treequest-0.3.1/src/treequest/vis/algo_adapters/standard_mcts.py +72 -0
- treequest-0.3.1/src/treequest/vis/algo_adapters/tree_of_thoughts_bfs.py +25 -0
- treequest-0.3.1/src/treequest/vis/assets/colormaps.json +1 -0
- treequest-0.3.1/src/treequest/vis/assets/d3.LICENSE.txt +13 -0
- treequest-0.3.1/src/treequest/vis/assets/d3.v7.min.js +2 -0
- treequest-0.3.1/src/treequest/vis/assets/d3_tree.html.jinja2 +1383 -0
- treequest-0.3.1/src/treequest/vis/build_snapshot.py +249 -0
- treequest-0.3.1/src/treequest/vis/errors.py +31 -0
- treequest-0.3.1/src/treequest/vis/render.py +112 -0
- treequest-0.3.1/src/treequest/vis/renderers/__init__.py +27 -0
- treequest-0.3.1/src/treequest/vis/renderers/color_utils.py +265 -0
- treequest-0.3.1/src/treequest/vis/renderers/graphviz_renderer.py +140 -0
- treequest-0.3.1/src/treequest/vis/renderers/html.py +157 -0
- treequest-0.3.1/src/treequest/vis/renderers/json_yaml.py +107 -0
- treequest-0.3.1/src/treequest/vis/renderers/mermaid.py +124 -0
- treequest-0.3.1/src/treequest/vis/snapshot.py +121 -0
- {treequest-0.2.0 → treequest-0.3.1}/src/treequest/visualization.py +23 -0
- treequest-0.2.0/.github/workflows/checks.yml +0 -44
- treequest-0.2.0/.github/workflows/release.yml +0 -23
- treequest-0.2.0/.github/workflows/tests.yml +0 -49
- treequest-0.2.0/.pre-commit-config.yaml +0 -6
- treequest-0.2.0/.python-version +0 -1
- treequest-0.2.0/CONTRIBUTING.md +0 -30
- treequest-0.2.0/docs/PROFILING.md +0 -98
- treequest-0.2.0/images/batch/abmcts_m_batch_size_1.jpg +0 -0
- treequest-0.2.0/images/batch/abmcts_m_batch_size_10.jpg +0 -0
- treequest-0.2.0/images/batch/abmcts_m_batch_size_2.jpg +0 -0
- treequest-0.2.0/images/batch/abmcts_m_batch_size_5.jpg +0 -0
- treequest-0.2.0/pytest.ini +0 -2
- treequest-0.2.0/tests/__init__.py +0 -0
- treequest-0.2.0/tests/profiling/ab_mcts_a.py +0 -101
- treequest-0.2.0/tests/profiling/ab_mcts_m.py +0 -114
- treequest-0.2.0/tests/test_ab_mcts_a.py +0 -151
- treequest-0.2.0/tests/test_ab_mcts_a_visualization.py +0 -257
- treequest-0.2.0/tests/test_ab_mcts_m_algo.py +0 -76
- treequest-0.2.0/tests/test_ab_mcts_m_algo_advanced.py +0 -318
- treequest-0.2.0/tests/test_ask_batch.py +0 -111
- treequest-0.2.0/tests/test_best_first_search.py +0 -72
- treequest-0.2.0/tests/test_e2e_batch_flow.py +0 -71
- treequest-0.2.0/tests/test_interface.py +0 -49
- treequest-0.2.0/tests/test_interleaved_ask_tell.py +0 -123
- treequest-0.2.0/tests/test_multi_armed_bandit_ucb.py +0 -62
- treequest-0.2.0/tests/test_next_nodes_behavior.py +0 -139
- treequest-0.2.0/tests/test_reflection_logic.py +0 -166
- treequest-0.2.0/tests/test_standard_mcts.py +0 -301
- treequest-0.2.0/tests/test_tot_bfs.py +0 -89
- treequest-0.2.0/tests/test_tree.py +0 -232
- treequest-0.2.0/tests/test_trial_tell_idempotent.py +0 -77
- treequest-0.2.0/uv.lock +0 -1493
- {treequest-0.2.0 → treequest-0.3.1}/.gitignore +0 -0
- {treequest-0.2.0 → treequest-0.3.1}/LICENSE +0 -0
- {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/ab_mcts_a/prob_state.py +0 -0
- {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/ab_mcts_m/_ab_mcts_m_imports.py +0 -0
- {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/ab_mcts_m/numpyro_utils.py +0 -0
- {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/ab_mcts_m/pymc_interface.py +0 -0
- {treequest-0.2.0 → treequest-0.3.1}/src/treequest/imports.py +0 -0
- {treequest-0.2.0 → treequest-0.3.1}/src/treequest/py.typed +0 -0
- {treequest-0.2.0 → treequest-0.3.1}/src/treequest/ranker.py +0 -0
|
@@ -1,3 +1,39 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: treequest
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: A Tree Search Library with Flexible API for LLM Inference-Time Scaling
|
|
5
|
+
Author-email: Kou Misaki <kou.misaki@sakana.ai>, Takuya Akiba <takiba@sakana.ai>
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: joblib>=1.5.2
|
|
9
|
+
Requires-Dist: pyyaml>=6.0.0
|
|
10
|
+
Requires-Dist: scipy>=1.0.0
|
|
11
|
+
Requires-Dist: ulid-py>=1.1.0
|
|
12
|
+
Provides-Extra: abmcts-m
|
|
13
|
+
Requires-Dist: jax>=0.5.2; extra == 'abmcts-m'
|
|
14
|
+
Requires-Dist: numpy>=1.26.4; extra == 'abmcts-m'
|
|
15
|
+
Requires-Dist: numpyro>=0.17.0; extra == 'abmcts-m'
|
|
16
|
+
Requires-Dist: packaging>=23; extra == 'abmcts-m'
|
|
17
|
+
Requires-Dist: pandas>=2.2.3; extra == 'abmcts-m'
|
|
18
|
+
Requires-Dist: pymc>=5.21.0; extra == 'abmcts-m'
|
|
19
|
+
Requires-Dist: xarray>=2025.1.2; extra == 'abmcts-m'
|
|
20
|
+
Provides-Extra: all
|
|
21
|
+
Requires-Dist: graphviz>=0.20.3; extra == 'all'
|
|
22
|
+
Requires-Dist: jax>=0.5.2; extra == 'all'
|
|
23
|
+
Requires-Dist: jinja2>=3.1.0; extra == 'all'
|
|
24
|
+
Requires-Dist: numpy>=1.26.4; extra == 'all'
|
|
25
|
+
Requires-Dist: numpyro>=0.17.0; extra == 'all'
|
|
26
|
+
Requires-Dist: packaging>=23; extra == 'all'
|
|
27
|
+
Requires-Dist: pandas>=2.2.3; extra == 'all'
|
|
28
|
+
Requires-Dist: pillow>=10.0.0; extra == 'all'
|
|
29
|
+
Requires-Dist: pymc>=5.21.0; extra == 'all'
|
|
30
|
+
Requires-Dist: xarray>=2025.1.2; extra == 'all'
|
|
31
|
+
Provides-Extra: vis
|
|
32
|
+
Requires-Dist: graphviz>=0.20.3; extra == 'vis'
|
|
33
|
+
Requires-Dist: jinja2>=3.1.0; extra == 'vis'
|
|
34
|
+
Requires-Dist: pillow>=10.0.0; extra == 'vis'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
1
37
|
# TreeQuest
|
|
2
38
|
[](https://www.python.org)
|
|
3
39
|
[](https://www.apache.org/licenses/LICENSE-2.0)
|
|
@@ -14,6 +50,7 @@ A flexible answer tree search library featuring **AB-MCTS**, useful for (but not
|
|
|
14
50
|
## Quick Start
|
|
15
51
|
```python
|
|
16
52
|
import random
|
|
53
|
+
from pathlib import Path
|
|
17
54
|
|
|
18
55
|
import treequest as tq
|
|
19
56
|
|
|
@@ -42,6 +79,10 @@ for _ in range(10):
|
|
|
42
79
|
# 4. Extract the best score and state.
|
|
43
80
|
best_state, best_node_score = tq.top_k(search_tree, algo, k=1)[0]
|
|
44
81
|
print(f"Best state: {best_state}, Score: {best_node_score}")
|
|
82
|
+
|
|
83
|
+
# 5. Visualize the search tree.
|
|
84
|
+
output_file_basename = Path("ab_mcts_a_search_tree")
|
|
85
|
+
tq.render(search_tree, output_file_basename, format="html") # Generates `ab_mcts_a_search_tree.html`
|
|
45
86
|
```
|
|
46
87
|
|
|
47
88
|
Alternatively, you can use an ask–tell interface with batched AB-MCTS sampling steps:
|
|
@@ -88,16 +129,27 @@ We recommend `batch_size<=5` as a starting point.
|
|
|
88
129
|
- Checkpointing and resuming searches.
|
|
89
130
|
|
|
90
131
|
## Installation
|
|
91
|
-
### uv
|
|
92
132
|
First, install [`uv`](https://github.com/astral-sh/uv?tab=readme-ov-file#installation). Then you can install TreeQuest with the following command:
|
|
93
133
|
```bash
|
|
94
|
-
uv add "treequest[
|
|
134
|
+
uv add "treequest[all]"
|
|
95
135
|
```
|
|
96
136
|
|
|
97
|
-
### pip
|
|
98
137
|
Alternatively, you can use pip to install TreeQuest:
|
|
99
138
|
```bash
|
|
100
|
-
pip install "treequest[
|
|
139
|
+
pip install "treequest[all]"
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
There are optional dependencies for ABMCTS-M and visualization features. You can install them with:
|
|
143
|
+
```bash
|
|
144
|
+
# with all dependencies
|
|
145
|
+
uv add "treequest[all]"
|
|
146
|
+
|
|
147
|
+
# without dependencies
|
|
148
|
+
uv add treequest
|
|
149
|
+
|
|
150
|
+
# with individual dependencies
|
|
151
|
+
uv add "treequest[abmcts-m]"
|
|
152
|
+
uv add "treequest[vis]"
|
|
101
153
|
```
|
|
102
154
|
|
|
103
155
|
## Usage
|
|
@@ -121,7 +173,7 @@ def generate(parent_state: State | None) -> tuple[State, float]:
|
|
|
121
173
|
state = refine_answer(parent_state.llm_answer, parent_state.score)
|
|
122
174
|
|
|
123
175
|
return state, state.score
|
|
124
|
-
|
|
176
|
+
|
|
125
177
|
def initial_generation() -> State:
|
|
126
178
|
"""
|
|
127
179
|
Call LLM API to generate an initial answer.
|
|
@@ -218,7 +270,57 @@ for _ in range(30):
|
|
|
218
270
|
search_tree = ab_mcts_m.step(search_tree, generate_fns)
|
|
219
271
|
```
|
|
220
272
|
|
|
221
|
-
**NOTE**: To run AB-MCTS-M, you need to install extra dependencies with the `treequest[abmcts-m]` option.
|
|
273
|
+
**NOTE**: To run AB-MCTS-M, you need to install extra dependencies with the `treequest[abmcts-m]` or `treequest[all]` option.
|
|
274
|
+
|
|
275
|
+
## Visualization
|
|
276
|
+
TreeQuest provides visualization utilities to render the search tree. You can visualize the search tree using the `tq.render` function as shown in the Quick Start section.
|
|
277
|
+
You need to install optional dependencies for visualization either by `uv add treequest[vis]` or `uv add treequest[all]`.
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
import html
|
|
281
|
+
import base64
|
|
282
|
+
from io import BytesIO
|
|
283
|
+
from pathlib import Path
|
|
284
|
+
|
|
285
|
+
import treequest as tq
|
|
286
|
+
from PIL import Image
|
|
287
|
+
|
|
288
|
+
class State:
|
|
289
|
+
text: str
|
|
290
|
+
image: Image.Image
|
|
291
|
+
|
|
292
|
+
def state_formatter_html(state: State) -> str:
|
|
293
|
+
"""Formats the state for HTML visualization."""
|
|
294
|
+
buffer = BytesIO()
|
|
295
|
+
state.image.save(buffer, format="WEBP")
|
|
296
|
+
img_str = base64.b64encode(buffer.getvalue()).decode()
|
|
297
|
+
# HTML representation is allowed (ensure safety when using untrusted content)
|
|
298
|
+
# In case state.text cannot be trusted, we recommend e.g., html.escape(state.text) to avoid html injection.
|
|
299
|
+
return f'<p>{state.text}</p><br/><img src="data:image/webp;base64,{img_str}" width=100% />'
|
|
300
|
+
|
|
301
|
+
generate_fns = {
|
|
302
|
+
"action_1": ... # Define your node generation functions (actions) here
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
algo = tq.ABMCTSA()
|
|
306
|
+
search_tree = algo.init_tree()
|
|
307
|
+
output_dir = Path("search_tree"); output_dir.mkdir(exist_ok=True)
|
|
308
|
+
for i in range(10):
|
|
309
|
+
search_tree = algo.step(search_tree, generate_fns)
|
|
310
|
+
# Rendering "latest.html" for checking search progress on the fly
|
|
311
|
+
if i % 2 == 0 or i == 9:
|
|
312
|
+
tq.render(
|
|
313
|
+
search_tree,
|
|
314
|
+
output_basename=output_dir / "latest",
|
|
315
|
+
format="html",
|
|
316
|
+
state_formatter=state_formatter_html, # Use html custom formetter
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
tq.render(search_tree, output_basename=output_dir / "final", format="pdf")
|
|
320
|
+
tq.render(search_tree, output_basename=output_dir / "final", format="html", state_formatter=state_formatter_html)
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
> **IMPORTANT:** When using HTML format, ensure that the HTML file is securely handled, especially if the state formatter includes raw HTML content. Avoid opening untrusted HTML files in your browser. For example, XSS (cross site scripting) attacks can occur if the state includes malicious HTML/JavaScript code.
|
|
222
324
|
|
|
223
325
|
## Requirements
|
|
224
326
|
|
|
@@ -230,14 +332,24 @@ Contributions are welcome! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for d
|
|
|
230
332
|
|
|
231
333
|
## Citation
|
|
232
334
|
```bibtex
|
|
233
|
-
@
|
|
234
|
-
title={Wider or Deeper?
|
|
235
|
-
author={Inoue
|
|
236
|
-
|
|
237
|
-
year={2025}
|
|
335
|
+
@inproceedings{inoue2025wider,
|
|
336
|
+
title={Wider or Deeper? Scaling {LLM} Inference-Time Compute with Adaptive Branching Tree Search},
|
|
337
|
+
author={Yuichi Inoue and Kou Misaki and Yuki Imajuku and So Kuroki and Taishi Nakamura and Takuya Akiba},
|
|
338
|
+
booktitle={The Thirty-ninth Annual Conference on Neural Information Processing Systems},
|
|
339
|
+
year={2025},
|
|
340
|
+
url={https://openreview.net/forum?id=jAsr5GHt3P}
|
|
238
341
|
}
|
|
239
342
|
```
|
|
240
343
|
|
|
241
344
|
## License
|
|
242
345
|
|
|
243
346
|
[Apache 2.0](./LICENSE)
|
|
347
|
+
|
|
348
|
+
### Third-party notices
|
|
349
|
+
|
|
350
|
+
TreeQuest bundles a few assets whose original authors retain copyright:
|
|
351
|
+
|
|
352
|
+
- **D3.js v7** by Mike Bostock and contributors, distributed under the ISC License. The full license text is included in `src/treequest/vis/assets/d3.LICENSE.txt` alongside the `d3.v7.min.js` binary.
|
|
353
|
+
- **Colormap samples** extracted from matplotlib and seaborn. The data and license references are embedded in `src/treequest/vis/assets/colormaps.json`.
|
|
354
|
+
|
|
355
|
+
These notices must be preserved in any redistribution of TreeQuest, including any compiled artifacts.
|
|
@@ -1,23 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: treequest
|
|
3
|
-
Version: 0.2.0
|
|
4
|
-
Summary: A Tree Search Library with Flexible API for LLM Inference-Time Scaling
|
|
5
|
-
Author-email: Kou Misaki <kou.misaki@sakana.ai>, Takuya Akiba <takiba@sakana.ai>
|
|
6
|
-
License-File: LICENSE
|
|
7
|
-
Requires-Python: >=3.11
|
|
8
|
-
Requires-Dist: joblib>=1.5.2
|
|
9
|
-
Requires-Dist: scipy>=1.0.0
|
|
10
|
-
Requires-Dist: ulid-py>=1.1.0
|
|
11
|
-
Provides-Extra: abmcts-m
|
|
12
|
-
Requires-Dist: jax>=0.5.2; extra == 'abmcts-m'
|
|
13
|
-
Requires-Dist: numpy>=1.26.4; extra == 'abmcts-m'
|
|
14
|
-
Requires-Dist: numpyro>=0.17.0; extra == 'abmcts-m'
|
|
15
|
-
Requires-Dist: packaging>=23; extra == 'abmcts-m'
|
|
16
|
-
Requires-Dist: pandas>=2.2.3; extra == 'abmcts-m'
|
|
17
|
-
Requires-Dist: pymc>=5.21.0; extra == 'abmcts-m'
|
|
18
|
-
Requires-Dist: xarray>=2025.1.2; extra == 'abmcts-m'
|
|
19
|
-
Description-Content-Type: text/markdown
|
|
20
|
-
|
|
21
1
|
# TreeQuest
|
|
22
2
|
[](https://www.python.org)
|
|
23
3
|
[](https://www.apache.org/licenses/LICENSE-2.0)
|
|
@@ -34,6 +14,7 @@ A flexible answer tree search library featuring **AB-MCTS**, useful for (but not
|
|
|
34
14
|
## Quick Start
|
|
35
15
|
```python
|
|
36
16
|
import random
|
|
17
|
+
from pathlib import Path
|
|
37
18
|
|
|
38
19
|
import treequest as tq
|
|
39
20
|
|
|
@@ -62,6 +43,10 @@ for _ in range(10):
|
|
|
62
43
|
# 4. Extract the best score and state.
|
|
63
44
|
best_state, best_node_score = tq.top_k(search_tree, algo, k=1)[0]
|
|
64
45
|
print(f"Best state: {best_state}, Score: {best_node_score}")
|
|
46
|
+
|
|
47
|
+
# 5. Visualize the search tree.
|
|
48
|
+
output_file_basename = Path("ab_mcts_a_search_tree")
|
|
49
|
+
tq.render(search_tree, output_file_basename, format="html") # Generates `ab_mcts_a_search_tree.html`
|
|
65
50
|
```
|
|
66
51
|
|
|
67
52
|
Alternatively, you can use an ask–tell interface with batched AB-MCTS sampling steps:
|
|
@@ -108,16 +93,27 @@ We recommend `batch_size<=5` as a starting point.
|
|
|
108
93
|
- Checkpointing and resuming searches.
|
|
109
94
|
|
|
110
95
|
## Installation
|
|
111
|
-
### uv
|
|
112
96
|
First, install [`uv`](https://github.com/astral-sh/uv?tab=readme-ov-file#installation). Then you can install TreeQuest with the following command:
|
|
113
97
|
```bash
|
|
114
|
-
uv add "treequest[
|
|
98
|
+
uv add "treequest[all]"
|
|
115
99
|
```
|
|
116
100
|
|
|
117
|
-
### pip
|
|
118
101
|
Alternatively, you can use pip to install TreeQuest:
|
|
119
102
|
```bash
|
|
120
|
-
pip install "treequest[
|
|
103
|
+
pip install "treequest[all]"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
There are optional dependencies for ABMCTS-M and visualization features. You can install them with:
|
|
107
|
+
```bash
|
|
108
|
+
# with all dependencies
|
|
109
|
+
uv add "treequest[all]"
|
|
110
|
+
|
|
111
|
+
# without dependencies
|
|
112
|
+
uv add treequest
|
|
113
|
+
|
|
114
|
+
# with individual dependencies
|
|
115
|
+
uv add "treequest[abmcts-m]"
|
|
116
|
+
uv add "treequest[vis]"
|
|
121
117
|
```
|
|
122
118
|
|
|
123
119
|
## Usage
|
|
@@ -141,7 +137,7 @@ def generate(parent_state: State | None) -> tuple[State, float]:
|
|
|
141
137
|
state = refine_answer(parent_state.llm_answer, parent_state.score)
|
|
142
138
|
|
|
143
139
|
return state, state.score
|
|
144
|
-
|
|
140
|
+
|
|
145
141
|
def initial_generation() -> State:
|
|
146
142
|
"""
|
|
147
143
|
Call LLM API to generate an initial answer.
|
|
@@ -238,7 +234,57 @@ for _ in range(30):
|
|
|
238
234
|
search_tree = ab_mcts_m.step(search_tree, generate_fns)
|
|
239
235
|
```
|
|
240
236
|
|
|
241
|
-
**NOTE**: To run AB-MCTS-M, you need to install extra dependencies with the `treequest[abmcts-m]` option.
|
|
237
|
+
**NOTE**: To run AB-MCTS-M, you need to install extra dependencies with the `treequest[abmcts-m]` or `treequest[all]` option.
|
|
238
|
+
|
|
239
|
+
## Visualization
|
|
240
|
+
TreeQuest provides visualization utilities to render the search tree. You can visualize the search tree using the `tq.render` function as shown in the Quick Start section.
|
|
241
|
+
You need to install optional dependencies for visualization either by `uv add treequest[vis]` or `uv add treequest[all]`.
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
import html
|
|
245
|
+
import base64
|
|
246
|
+
from io import BytesIO
|
|
247
|
+
from pathlib import Path
|
|
248
|
+
|
|
249
|
+
import treequest as tq
|
|
250
|
+
from PIL import Image
|
|
251
|
+
|
|
252
|
+
class State:
|
|
253
|
+
text: str
|
|
254
|
+
image: Image.Image
|
|
255
|
+
|
|
256
|
+
def state_formatter_html(state: State) -> str:
|
|
257
|
+
"""Formats the state for HTML visualization."""
|
|
258
|
+
buffer = BytesIO()
|
|
259
|
+
state.image.save(buffer, format="WEBP")
|
|
260
|
+
img_str = base64.b64encode(buffer.getvalue()).decode()
|
|
261
|
+
# HTML representation is allowed (ensure safety when using untrusted content)
|
|
262
|
+
# In case state.text cannot be trusted, we recommend e.g., html.escape(state.text) to avoid html injection.
|
|
263
|
+
return f'<p>{state.text}</p><br/><img src="data:image/webp;base64,{img_str}" width=100% />'
|
|
264
|
+
|
|
265
|
+
generate_fns = {
|
|
266
|
+
"action_1": ... # Define your node generation functions (actions) here
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
algo = tq.ABMCTSA()
|
|
270
|
+
search_tree = algo.init_tree()
|
|
271
|
+
output_dir = Path("search_tree"); output_dir.mkdir(exist_ok=True)
|
|
272
|
+
for i in range(10):
|
|
273
|
+
search_tree = algo.step(search_tree, generate_fns)
|
|
274
|
+
# Rendering "latest.html" for checking search progress on the fly
|
|
275
|
+
if i % 2 == 0 or i == 9:
|
|
276
|
+
tq.render(
|
|
277
|
+
search_tree,
|
|
278
|
+
output_basename=output_dir / "latest",
|
|
279
|
+
format="html",
|
|
280
|
+
state_formatter=state_formatter_html, # Use html custom formetter
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
tq.render(search_tree, output_basename=output_dir / "final", format="pdf")
|
|
284
|
+
tq.render(search_tree, output_basename=output_dir / "final", format="html", state_formatter=state_formatter_html)
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
> **IMPORTANT:** When using HTML format, ensure that the HTML file is securely handled, especially if the state formatter includes raw HTML content. Avoid opening untrusted HTML files in your browser. For example, XSS (cross site scripting) attacks can occur if the state includes malicious HTML/JavaScript code.
|
|
242
288
|
|
|
243
289
|
## Requirements
|
|
244
290
|
|
|
@@ -250,14 +296,24 @@ Contributions are welcome! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for d
|
|
|
250
296
|
|
|
251
297
|
## Citation
|
|
252
298
|
```bibtex
|
|
253
|
-
@
|
|
254
|
-
title={Wider or Deeper?
|
|
255
|
-
author={Inoue
|
|
256
|
-
|
|
257
|
-
year={2025}
|
|
299
|
+
@inproceedings{inoue2025wider,
|
|
300
|
+
title={Wider or Deeper? Scaling {LLM} Inference-Time Compute with Adaptive Branching Tree Search},
|
|
301
|
+
author={Yuichi Inoue and Kou Misaki and Yuki Imajuku and So Kuroki and Taishi Nakamura and Takuya Akiba},
|
|
302
|
+
booktitle={The Thirty-ninth Annual Conference on Neural Information Processing Systems},
|
|
303
|
+
year={2025},
|
|
304
|
+
url={https://openreview.net/forum?id=jAsr5GHt3P}
|
|
258
305
|
}
|
|
259
306
|
```
|
|
260
307
|
|
|
261
308
|
## License
|
|
262
309
|
|
|
263
310
|
[Apache 2.0](./LICENSE)
|
|
311
|
+
|
|
312
|
+
### Third-party notices
|
|
313
|
+
|
|
314
|
+
TreeQuest bundles a few assets whose original authors retain copyright:
|
|
315
|
+
|
|
316
|
+
- **D3.js v7** by Mike Bostock and contributors, distributed under the ISC License. The full license text is included in `src/treequest/vis/assets/d3.LICENSE.txt` alongside the `d3.v7.min.js` binary.
|
|
317
|
+
- **Colormap samples** extracted from matplotlib and seaborn. The data and license references are embedded in `src/treequest/vis/assets/colormaps.json`.
|
|
318
|
+
|
|
319
|
+
These notices must be preserved in any redistribution of TreeQuest, including any compiled artifacts.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "treequest"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.1"
|
|
4
4
|
description = "A Tree Search Library with Flexible API for LLM Inference-Time Scaling"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -9,6 +9,7 @@ authors = [
|
|
|
9
9
|
]
|
|
10
10
|
requires-python = ">=3.11"
|
|
11
11
|
dependencies = [
|
|
12
|
+
"PyYAML>=6.0.0",
|
|
12
13
|
"joblib>=1.5.2",
|
|
13
14
|
"scipy>=1.0.0",
|
|
14
15
|
"ulid-py>=1.1.0",
|
|
@@ -25,17 +26,41 @@ abmcts-m = [
|
|
|
25
26
|
"packaging>=23",
|
|
26
27
|
]
|
|
27
28
|
|
|
29
|
+
vis = [
|
|
30
|
+
"Pillow>=10.0.0",
|
|
31
|
+
"graphviz>=0.20.3",
|
|
32
|
+
"jinja2>=3.1.0",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
all = [
|
|
36
|
+
"treequest[abmcts-m]",
|
|
37
|
+
"treequest[vis]",
|
|
38
|
+
]
|
|
39
|
+
|
|
28
40
|
[build-system]
|
|
29
41
|
requires = ["hatchling"]
|
|
30
42
|
build-backend = "hatchling.build"
|
|
31
43
|
|
|
32
44
|
[dependency-groups]
|
|
33
45
|
dev = [
|
|
34
|
-
"graphviz>=0.20.3",
|
|
35
46
|
"line-profiler>=4.2.0",
|
|
36
47
|
"mypy>=1.15.0",
|
|
37
48
|
"pre-commit>=4.2.0",
|
|
38
49
|
"pytest>=8.3.4",
|
|
39
50
|
"pytest-xdist>=3.6.1",
|
|
40
51
|
"ruff>=0.9.9",
|
|
52
|
+
"tqdm>=4.64.0",
|
|
53
|
+
"types-tqdm>=4.64.0",
|
|
54
|
+
"types-PyYAML>=6.0.0",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
[tool.hatch.build.targets.wheel]
|
|
58
|
+
packages = ["src/treequest"]
|
|
59
|
+
include = [
|
|
60
|
+
"src/treequest",
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
[tool.hatch.build.targets.sdist]
|
|
64
|
+
include = [
|
|
65
|
+
"src/treequest",
|
|
41
66
|
]
|
|
@@ -20,6 +20,7 @@ from .algos.base import Algorithm
|
|
|
20
20
|
from .algos.standard_mcts import StandardMCTS
|
|
21
21
|
from .algos.tree_of_thought_bfs import TreeOfThoughtsBFSAlgo
|
|
22
22
|
from .ranker import top_k
|
|
23
|
+
from .vis import render
|
|
23
24
|
|
|
24
25
|
__all__ = [
|
|
25
26
|
"StandardMCTS",
|
|
@@ -28,4 +29,5 @@ __all__ = [
|
|
|
28
29
|
"ABMCTSA",
|
|
29
30
|
"ABMCTSM",
|
|
30
31
|
"Algorithm",
|
|
32
|
+
"render",
|
|
31
33
|
]
|
|
@@ -8,8 +8,8 @@ from typing import Dict, Generic, List, Literal, Optional, Tuple, TypeVar, Union
|
|
|
8
8
|
from treequest.algos.ab_mcts_a.prob_state import NodeProbState, PriorConfig
|
|
9
9
|
from treequest.algos.base import Algorithm
|
|
10
10
|
from treequest.algos.tree import Node, Tree
|
|
11
|
-
from treequest.trial import Trial,
|
|
12
|
-
from treequest.types import GenerateFnType, StateScoreType
|
|
11
|
+
from treequest.trial import Trial, TrialStore
|
|
12
|
+
from treequest.types import GenerateFnType, StateScoreType, TrialId
|
|
13
13
|
|
|
14
14
|
# Type variable for state
|
|
15
15
|
StateT = TypeVar("StateT")
|
|
@@ -369,7 +369,9 @@ class ABMCTSA(Algorithm[StateT, ABMCTSAAlgoState[StateT]]):
|
|
|
369
369
|
parent_node = state.tree.get_node(finished_trial.node_to_expand)
|
|
370
370
|
action = finished_trial.action
|
|
371
371
|
# Add new node to the tree
|
|
372
|
-
new_node = state.tree.add_node(
|
|
372
|
+
new_node = state.tree.add_node(
|
|
373
|
+
result, parent_node, trial_id=finished_trial.trial_id
|
|
374
|
+
)
|
|
373
375
|
|
|
374
376
|
# Update Thompson state with the new node
|
|
375
377
|
thompson_state = state.thompson_states.get(parent_node)
|
|
@@ -14,12 +14,12 @@ from treequest.algos.ab_mcts_m.pymc_interface import (
|
|
|
14
14
|
)
|
|
15
15
|
from treequest.algos.base import Algorithm
|
|
16
16
|
from treequest.algos.tree import Node, Tree
|
|
17
|
-
from treequest.trial import Trial,
|
|
18
|
-
from treequest.types import GenerateFnType, StateScoreType
|
|
17
|
+
from treequest.trial import Trial, TrialStore
|
|
18
|
+
from treequest.types import GenerateFnType, StateScoreType, TrialId
|
|
19
19
|
|
|
20
20
|
StateT = TypeVar("StateT")
|
|
21
21
|
|
|
22
|
-
_WORKER_ALGO = None
|
|
22
|
+
_WORKER_ALGO: Optional["ABMCTSM"] = None
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
def _worker_init_abmctsm(config: dict, per_worker_cpu_devices: int):
|
|
@@ -169,7 +169,7 @@ class ABMCTSM(Algorithm[StateT, ABMCTSMState[StateT]]):
|
|
|
169
169
|
Args:
|
|
170
170
|
state: Current algorithm state
|
|
171
171
|
node: Node to select child from
|
|
172
|
-
|
|
172
|
+
actions: List of action names
|
|
173
173
|
|
|
174
174
|
Returns:
|
|
175
175
|
Tuple of (selected node, action if new node was generated)
|
|
@@ -297,7 +297,9 @@ class ABMCTSM(Algorithm[StateT, ABMCTSMState[StateT]]):
|
|
|
297
297
|
parent_node = state.tree.get_node(finished_trial.node_to_expand)
|
|
298
298
|
|
|
299
299
|
# Add new node to the tree
|
|
300
|
-
new_node = state.tree.add_node(
|
|
300
|
+
new_node = state.tree.add_node(
|
|
301
|
+
result, parent_node, trial_id=finished_trial.trial_id
|
|
302
|
+
)
|
|
301
303
|
|
|
302
304
|
# Record observation
|
|
303
305
|
state.all_observations[new_node.expand_idx] = Observation(
|
|
@@ -2,8 +2,8 @@ import abc
|
|
|
2
2
|
from collections.abc import Mapping
|
|
3
3
|
from typing import Generic, List, TypeVar
|
|
4
4
|
|
|
5
|
-
from treequest.trial import Trial
|
|
6
|
-
from treequest.types import GenerateFnType, StateScoreType
|
|
5
|
+
from treequest.trial import Trial
|
|
6
|
+
from treequest.types import GenerateFnType, StateScoreType, TrialId
|
|
7
7
|
|
|
8
8
|
# Type variables for node state and algorithm state
|
|
9
9
|
NodeStateT = TypeVar("NodeStateT")
|
|
@@ -7,8 +7,8 @@ from typing import Generic, List, Tuple, TypeVar
|
|
|
7
7
|
|
|
8
8
|
from treequest.algos.base import Algorithm
|
|
9
9
|
from treequest.algos.tree import Node, Tree
|
|
10
|
-
from treequest.trial import Trial,
|
|
11
|
-
from treequest.types import GenerateFnType, StateScoreType
|
|
10
|
+
from treequest.trial import Trial, TrialStoreWithNodeQueue
|
|
11
|
+
from treequest.types import GenerateFnType, StateScoreType, TrialId
|
|
12
12
|
|
|
13
13
|
# Type variable for state
|
|
14
14
|
StateT = TypeVar("StateT")
|
|
@@ -189,7 +189,9 @@ class BestFirstSearchAlgo(Algorithm[StateT, BFSState[StateT]]):
|
|
|
189
189
|
|
|
190
190
|
parent_node = state.tree.get_node(finished_trial.node_to_expand)
|
|
191
191
|
|
|
192
|
-
new_node = state.tree.add_node(
|
|
192
|
+
new_node = state.tree.add_node(
|
|
193
|
+
result, parent_node, trial_id=finished_trial.trial_id
|
|
194
|
+
)
|
|
193
195
|
|
|
194
196
|
# Add the new node to the priority queue
|
|
195
197
|
heappush(state.leaves, BFSHeapItem(node=new_node, score=new_score))
|
|
@@ -7,8 +7,8 @@ from typing import Generic, List, TypeVar
|
|
|
7
7
|
|
|
8
8
|
from treequest.algos.base import Algorithm
|
|
9
9
|
from treequest.algos.tree import Tree
|
|
10
|
-
from treequest.trial import Trial,
|
|
11
|
-
from treequest.types import GenerateFnType, StateScoreType
|
|
10
|
+
from treequest.trial import Trial, TrialStore
|
|
11
|
+
from treequest.types import GenerateFnType, StateScoreType, TrialId
|
|
12
12
|
|
|
13
13
|
# Type variable for state
|
|
14
14
|
StateT = TypeVar("StateT")
|
|
@@ -172,7 +172,11 @@ class MultiArmedBanditUCBAlgo(Algorithm[StateT, UCBState[StateT]]):
|
|
|
172
172
|
): # Trial is no longer valid, so we do not reflect the result to state
|
|
173
173
|
return state
|
|
174
174
|
|
|
175
|
-
state.tree.add_node(
|
|
175
|
+
state.tree.add_node(
|
|
176
|
+
result,
|
|
177
|
+
state.tree.get_node(finished_trial.node_to_expand),
|
|
178
|
+
trial_id=finished_trial.trial_id,
|
|
179
|
+
)
|
|
176
180
|
|
|
177
181
|
# Update scores for the selected action
|
|
178
182
|
state.scores_by_action[finished_trial.action].append(new_score)
|
|
@@ -6,8 +6,8 @@ from typing import Dict, Generic, List, Optional, TypeVar
|
|
|
6
6
|
|
|
7
7
|
from treequest.algos.base import Algorithm
|
|
8
8
|
from treequest.algos.tree import Node, Tree
|
|
9
|
-
from treequest.trial import Trial,
|
|
10
|
-
from treequest.types import GenerateFnType, StateScoreType
|
|
9
|
+
from treequest.trial import Trial, TrialStoreWithNodeQueue
|
|
10
|
+
from treequest.types import GenerateFnType, StateScoreType, TrialId
|
|
11
11
|
|
|
12
12
|
# Type variable for state
|
|
13
13
|
StateT = TypeVar("StateT")
|
|
@@ -128,7 +128,9 @@ class StandardMCTS(Algorithm[StateT, MCTSState[StateT]]):
|
|
|
128
128
|
|
|
129
129
|
parent_node = state.tree.get_node(finished_trial.node_to_expand)
|
|
130
130
|
# Add the new node to the tree
|
|
131
|
-
new_node = state.tree.add_node(
|
|
131
|
+
new_node = state.tree.add_node(
|
|
132
|
+
result, parent_node, trial_id=finished_trial.trial_id
|
|
133
|
+
)
|
|
132
134
|
|
|
133
135
|
# Backpropagation updates visit_counts and value_sums for the new node and all ancestors.
|
|
134
136
|
self._backpropagate(state, new_node, new_score)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import dataclasses
|
|
2
2
|
from typing import Generic, List, Optional, TypeVar
|
|
3
3
|
|
|
4
|
-
from treequest.types import StateScoreType
|
|
4
|
+
from treequest.types import StateScoreType, TrialId
|
|
5
5
|
|
|
6
6
|
# Type variable for state
|
|
7
7
|
StateT = TypeVar("StateT")
|
|
@@ -19,6 +19,7 @@ class Node(Generic[StateT]):
|
|
|
19
19
|
|
|
20
20
|
parent: Optional["Node[StateT]"] = None
|
|
21
21
|
children: List["Node[StateT]"] = dataclasses.field(default_factory=list)
|
|
22
|
+
trial_id: Optional[TrialId] = None
|
|
22
23
|
|
|
23
24
|
def __post_init__(self) -> None:
|
|
24
25
|
if not self.is_root():
|
|
@@ -112,10 +113,19 @@ class Tree(Generic[StateT]):
|
|
|
112
113
|
]
|
|
113
114
|
|
|
114
115
|
def add_node(
|
|
115
|
-
self,
|
|
116
|
+
self,
|
|
117
|
+
state_score: StateScoreType[StateT],
|
|
118
|
+
parent: Node[StateT],
|
|
119
|
+
trial_id: Optional[TrialId] = None,
|
|
116
120
|
) -> Node[StateT]:
|
|
117
121
|
state, score = state_score
|
|
118
|
-
node = Node(
|
|
122
|
+
node = Node(
|
|
123
|
+
state=state,
|
|
124
|
+
score=score,
|
|
125
|
+
parent=parent,
|
|
126
|
+
expand_idx=self.size - 1,
|
|
127
|
+
trial_id=trial_id,
|
|
128
|
+
)
|
|
119
129
|
parent.children.append(node)
|
|
120
130
|
self.size += 1
|
|
121
131
|
return node
|
|
@@ -8,8 +8,8 @@ from typing import Generic, List, TypeVar
|
|
|
8
8
|
|
|
9
9
|
from treequest.algos.base import Algorithm
|
|
10
10
|
from treequest.algos.tree import Node, Tree
|
|
11
|
-
from treequest.trial import Trial,
|
|
12
|
-
from treequest.types import GenerateFnType, StateScoreType
|
|
11
|
+
from treequest.trial import Trial, TrialStoreWithNodeQueue
|
|
12
|
+
from treequest.types import GenerateFnType, StateScoreType, TrialId
|
|
13
13
|
|
|
14
14
|
# Type variable for state
|
|
15
15
|
StateT = TypeVar("StateT")
|
|
@@ -228,7 +228,7 @@ class TreeOfThoughtsBFSAlgo(Algorithm[StateT, ToTBFSState[StateT]]):
|
|
|
228
228
|
|
|
229
229
|
parent = state.tree.get_node(finished_trial.node_to_expand)
|
|
230
230
|
# Add to the tree
|
|
231
|
-
state.tree.add_node(result, parent)
|
|
231
|
+
state.tree.add_node(result, parent, trial_id=finished_trial.trial_id)
|
|
232
232
|
# Update current depth
|
|
233
233
|
state.current_depth = max(state.current_depth, parent.depth + 1)
|
|
234
234
|
|
|
@@ -6,9 +6,7 @@ from typing import Generic, Literal, TypeAlias, TypeVar, get_args
|
|
|
6
6
|
import ulid
|
|
7
7
|
|
|
8
8
|
from treequest.algos.tree import Node
|
|
9
|
-
|
|
10
|
-
TrialId: TypeAlias = str
|
|
11
|
-
NodeId: TypeAlias = int
|
|
9
|
+
from treequest.types import NodeId, TrialId
|
|
12
10
|
|
|
13
11
|
TrialStatus: TypeAlias = Literal["RUNNING", "INVALID", "COMPLETE"]
|
|
14
12
|
TRIAL_STATUSES: tuple[TrialStatus, ...] = get_args(TrialStatus)
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
from collections.abc import Callable
|
|
2
|
-
from typing import List, Optional, Tuple, TypeVar
|
|
2
|
+
from typing import List, Optional, Tuple, TypeAlias, TypeVar
|
|
3
|
+
|
|
4
|
+
# Type alias for NodeId and TrialId
|
|
5
|
+
NodeId: TypeAlias = int
|
|
6
|
+
TrialId: TypeAlias = str
|
|
3
7
|
|
|
4
8
|
# Type variable for node state
|
|
5
9
|
NodeStateT = TypeVar("NodeStateT")
|