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.
Files changed (79) hide show
  1. treequest-0.2.0/README.md → treequest-0.3.1/PKG-INFO +123 -11
  2. treequest-0.2.0/PKG-INFO → treequest-0.3.1/README.md +87 -31
  3. {treequest-0.2.0 → treequest-0.3.1}/pyproject.toml +27 -2
  4. {treequest-0.2.0 → treequest-0.3.1}/src/treequest/__init__.py +2 -0
  5. {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/ab_mcts_a/algo.py +5 -3
  6. {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/ab_mcts_m/algo.py +7 -5
  7. {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/base.py +2 -2
  8. {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/best_first_search.py +5 -3
  9. {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/multi_armed_bandit_ucb.py +7 -3
  10. {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/standard_mcts.py +5 -3
  11. {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/tree.py +13 -3
  12. {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/tree_of_thought_bfs.py +3 -3
  13. {treequest-0.2.0 → treequest-0.3.1}/src/treequest/trial.py +1 -3
  14. {treequest-0.2.0 → treequest-0.3.1}/src/treequest/types.py +5 -1
  15. treequest-0.3.1/src/treequest/vis/__init__.py +82 -0
  16. treequest-0.3.1/src/treequest/vis/algo_adapters/__init__.py +68 -0
  17. treequest-0.3.1/src/treequest/vis/algo_adapters/ab_mcts_a.py +116 -0
  18. treequest-0.3.1/src/treequest/vis/algo_adapters/ab_mcts_m.py +78 -0
  19. treequest-0.3.1/src/treequest/vis/algo_adapters/base.py +39 -0
  20. treequest-0.3.1/src/treequest/vis/algo_adapters/best_first_search.py +52 -0
  21. treequest-0.3.1/src/treequest/vis/algo_adapters/multi_armed_bandit_ucb.py +99 -0
  22. treequest-0.3.1/src/treequest/vis/algo_adapters/standard_mcts.py +72 -0
  23. treequest-0.3.1/src/treequest/vis/algo_adapters/tree_of_thoughts_bfs.py +25 -0
  24. treequest-0.3.1/src/treequest/vis/assets/colormaps.json +1 -0
  25. treequest-0.3.1/src/treequest/vis/assets/d3.LICENSE.txt +13 -0
  26. treequest-0.3.1/src/treequest/vis/assets/d3.v7.min.js +2 -0
  27. treequest-0.3.1/src/treequest/vis/assets/d3_tree.html.jinja2 +1383 -0
  28. treequest-0.3.1/src/treequest/vis/build_snapshot.py +249 -0
  29. treequest-0.3.1/src/treequest/vis/errors.py +31 -0
  30. treequest-0.3.1/src/treequest/vis/render.py +112 -0
  31. treequest-0.3.1/src/treequest/vis/renderers/__init__.py +27 -0
  32. treequest-0.3.1/src/treequest/vis/renderers/color_utils.py +265 -0
  33. treequest-0.3.1/src/treequest/vis/renderers/graphviz_renderer.py +140 -0
  34. treequest-0.3.1/src/treequest/vis/renderers/html.py +157 -0
  35. treequest-0.3.1/src/treequest/vis/renderers/json_yaml.py +107 -0
  36. treequest-0.3.1/src/treequest/vis/renderers/mermaid.py +124 -0
  37. treequest-0.3.1/src/treequest/vis/snapshot.py +121 -0
  38. {treequest-0.2.0 → treequest-0.3.1}/src/treequest/visualization.py +23 -0
  39. treequest-0.2.0/.github/workflows/checks.yml +0 -44
  40. treequest-0.2.0/.github/workflows/release.yml +0 -23
  41. treequest-0.2.0/.github/workflows/tests.yml +0 -49
  42. treequest-0.2.0/.pre-commit-config.yaml +0 -6
  43. treequest-0.2.0/.python-version +0 -1
  44. treequest-0.2.0/CONTRIBUTING.md +0 -30
  45. treequest-0.2.0/docs/PROFILING.md +0 -98
  46. treequest-0.2.0/images/batch/abmcts_m_batch_size_1.jpg +0 -0
  47. treequest-0.2.0/images/batch/abmcts_m_batch_size_10.jpg +0 -0
  48. treequest-0.2.0/images/batch/abmcts_m_batch_size_2.jpg +0 -0
  49. treequest-0.2.0/images/batch/abmcts_m_batch_size_5.jpg +0 -0
  50. treequest-0.2.0/pytest.ini +0 -2
  51. treequest-0.2.0/tests/__init__.py +0 -0
  52. treequest-0.2.0/tests/profiling/ab_mcts_a.py +0 -101
  53. treequest-0.2.0/tests/profiling/ab_mcts_m.py +0 -114
  54. treequest-0.2.0/tests/test_ab_mcts_a.py +0 -151
  55. treequest-0.2.0/tests/test_ab_mcts_a_visualization.py +0 -257
  56. treequest-0.2.0/tests/test_ab_mcts_m_algo.py +0 -76
  57. treequest-0.2.0/tests/test_ab_mcts_m_algo_advanced.py +0 -318
  58. treequest-0.2.0/tests/test_ask_batch.py +0 -111
  59. treequest-0.2.0/tests/test_best_first_search.py +0 -72
  60. treequest-0.2.0/tests/test_e2e_batch_flow.py +0 -71
  61. treequest-0.2.0/tests/test_interface.py +0 -49
  62. treequest-0.2.0/tests/test_interleaved_ask_tell.py +0 -123
  63. treequest-0.2.0/tests/test_multi_armed_bandit_ucb.py +0 -62
  64. treequest-0.2.0/tests/test_next_nodes_behavior.py +0 -139
  65. treequest-0.2.0/tests/test_reflection_logic.py +0 -166
  66. treequest-0.2.0/tests/test_standard_mcts.py +0 -301
  67. treequest-0.2.0/tests/test_tot_bfs.py +0 -89
  68. treequest-0.2.0/tests/test_tree.py +0 -232
  69. treequest-0.2.0/tests/test_trial_tell_idempotent.py +0 -77
  70. treequest-0.2.0/uv.lock +0 -1493
  71. {treequest-0.2.0 → treequest-0.3.1}/.gitignore +0 -0
  72. {treequest-0.2.0 → treequest-0.3.1}/LICENSE +0 -0
  73. {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/ab_mcts_a/prob_state.py +0 -0
  74. {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/ab_mcts_m/_ab_mcts_m_imports.py +0 -0
  75. {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/ab_mcts_m/numpyro_utils.py +0 -0
  76. {treequest-0.2.0 → treequest-0.3.1}/src/treequest/algos/ab_mcts_m/pymc_interface.py +0 -0
  77. {treequest-0.2.0 → treequest-0.3.1}/src/treequest/imports.py +0 -0
  78. {treequest-0.2.0 → treequest-0.3.1}/src/treequest/py.typed +0 -0
  79. {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
  [![Python](https://img.shields.io/badge/python-3.11-blue)](https://www.python.org)
3
39
  [![GitHub license](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](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[abmcts-m]"
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[abmcts-m]"
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
- @article{inoue2025wider,
234
- title={Wider or Deeper? Scaling LLM Inference-Time Compute with Adaptive Branching Tree Search},
235
- author={Inoue, Yuichi and Misaki, Kou and Imajuku, Yuki and Kuroki, So and Nakamura, Taishi and Akiba, Takuya},
236
- journal={arXiv preprint arXiv:2503.04412},
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
  [![Python](https://img.shields.io/badge/python-3.11-blue)](https://www.python.org)
23
3
  [![GitHub license](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](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[abmcts-m]"
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[abmcts-m]"
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
- @article{inoue2025wider,
254
- title={Wider or Deeper? Scaling LLM Inference-Time Compute with Adaptive Branching Tree Search},
255
- author={Inoue, Yuichi and Misaki, Kou and Imajuku, Yuki and Kuroki, So and Nakamura, Taishi and Akiba, Takuya},
256
- journal={arXiv preprint arXiv:2503.04412},
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.2.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, TrialId, TrialStore
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(result, parent_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, TrialId, TrialStore
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
- generate_fn: Mapping of action names to generation functions
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(result, parent_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, TrialId
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, TrialId, TrialStoreWithNodeQueue
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(result, parent_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, TrialId, TrialStore
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(result, state.tree.get_node(finished_trial.node_to_expand))
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, TrialId, TrialStoreWithNodeQueue
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(result, parent_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, state_score: StateScoreType[StateT], parent: Node[StateT]
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(state=state, score=score, parent=parent, expand_idx=self.size - 1)
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, TrialId, TrialStoreWithNodeQueue
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")