DeepFabric 4.11.0__tar.gz → 4.12.0__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 (229) hide show
  1. {deepfabric-4.11.0 → deepfabric-4.12.0}/PKG-INFO +1 -1
  2. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/cli.py +541 -6
  3. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/config.py +8 -1
  4. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/config_manager.py +6 -1
  5. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/graph.py +177 -1
  6. deepfabric-4.12.0/deepfabric/graph_pruner.py +122 -0
  7. deepfabric-4.12.0/deepfabric/topic_inspector.py +237 -0
  8. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/topic_manager.py +32 -0
  9. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/tree.py +40 -25
  10. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/cli/generate.md +20 -3
  11. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/cli/index.md +8 -0
  12. deepfabric-4.12.0/docs/cli/topic-inspect.md +162 -0
  13. deepfabric-4.12.0/docs/cli/topic-prune.md +120 -0
  14. deepfabric-4.12.0/docs/cli/topic.md +50 -0
  15. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/dataset-generation/agent.md +1 -1
  16. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/dataset-generation/basic.md +1 -1
  17. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/dataset-generation/configuration.md +14 -1
  18. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/dataset-generation/reasoning.md +1 -1
  19. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/index.md +2 -2
  20. {deepfabric-4.11.0 → deepfabric-4.12.0}/mkdocs.yml +4 -0
  21. {deepfabric-4.11.0 → deepfabric-4.12.0}/pyproject.toml +1 -1
  22. deepfabric-4.12.0/tests/unit/test_graph_pruner.py +397 -0
  23. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_topic_graph.py +120 -0
  24. deepfabric-4.12.0/tests/unit/test_topic_inspector.py +294 -0
  25. {deepfabric-4.11.0 → deepfabric-4.12.0}/uv.lock +1 -1
  26. {deepfabric-4.11.0 → deepfabric-4.12.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  27. {deepfabric-4.11.0 → deepfabric-4.12.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  28. {deepfabric-4.11.0 → deepfabric-4.12.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  29. {deepfabric-4.11.0 → deepfabric-4.12.0}/.github/config.yml +0 -0
  30. {deepfabric-4.11.0 → deepfabric-4.12.0}/.github/dependabot.yml +0 -0
  31. {deepfabric-4.11.0 → deepfabric-4.12.0}/.github/workflows/docs.yml +0 -0
  32. {deepfabric-4.11.0 → deepfabric-4.12.0}/.github/workflows/integration.yml +0 -0
  33. {deepfabric-4.11.0 → deepfabric-4.12.0}/.github/workflows/publish.yml +0 -0
  34. {deepfabric-4.11.0 → deepfabric-4.12.0}/.github/workflows/test.yml +0 -0
  35. {deepfabric-4.11.0 → deepfabric-4.12.0}/.github/workflows/tools-sdk-docker.yml +0 -0
  36. {deepfabric-4.11.0 → deepfabric-4.12.0}/.gitignore +0 -0
  37. {deepfabric-4.11.0 → deepfabric-4.12.0}/CLAUDE.md +0 -0
  38. {deepfabric-4.11.0 → deepfabric-4.12.0}/CODE_OF_CONDUCT.md +0 -0
  39. {deepfabric-4.11.0 → deepfabric-4.12.0}/CONTRIBUTING.md +0 -0
  40. {deepfabric-4.11.0 → deepfabric-4.12.0}/LICENSE +0 -0
  41. {deepfabric-4.11.0 → deepfabric-4.12.0}/Makefile +0 -0
  42. {deepfabric-4.11.0 → deepfabric-4.12.0}/README.md +0 -0
  43. {deepfabric-4.11.0 → deepfabric-4.12.0}/assets/df-demo.gif +0 -0
  44. {deepfabric-4.11.0 → deepfabric-4.12.0}/assets/logo-light-hols.png +0 -0
  45. {deepfabric-4.11.0 → deepfabric-4.12.0}/assets/logo-light.png +0 -0
  46. {deepfabric-4.11.0 → deepfabric-4.12.0}/assets/star.gif +0 -0
  47. {deepfabric-4.11.0 → deepfabric-4.12.0}/coverage.xml +0 -0
  48. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/__init__.py +0 -0
  49. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/__main__.py +0 -0
  50. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/auth.py +0 -0
  51. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/builders.py +0 -0
  52. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/builders_agent.py +0 -0
  53. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/cloud_upload.py +0 -0
  54. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/constants.py +0 -0
  55. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/dataset.py +0 -0
  56. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/dataset_manager.py +0 -0
  57. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/error_codes.py +0 -0
  58. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/__init__.py +0 -0
  59. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/backends/__init__.py +0 -0
  60. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/backends/llm_eval_backend.py +0 -0
  61. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/backends/ollama_backend.py +0 -0
  62. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/backends/tool_call_parsers.py +0 -0
  63. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/backends/transformers_backend.py +0 -0
  64. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/evaluator.py +0 -0
  65. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/evaluators/__init__.py +0 -0
  66. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/evaluators/base.py +0 -0
  67. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/evaluators/builtin/__init__.py +0 -0
  68. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/evaluators/builtin/tool_calling.py +0 -0
  69. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/evaluators/registry.py +0 -0
  70. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/inference.py +0 -0
  71. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/metrics.py +0 -0
  72. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/parser.py +0 -0
  73. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/reporters/__init__.py +0 -0
  74. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/reporters/base.py +0 -0
  75. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/reporters/cloud_reporter.py +0 -0
  76. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/reporters/file_reporter.py +0 -0
  77. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/evaluation/reporters/multi_reporter.py +0 -0
  78. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/exceptions.py +0 -0
  79. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/factory.py +0 -0
  80. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/generator.py +0 -0
  81. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/hf_hub.py +0 -0
  82. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/kaggle_hub.py +0 -0
  83. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/llm/__init__.py +0 -0
  84. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/llm/api_key_verifier.py +0 -0
  85. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/llm/client.py +0 -0
  86. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/llm/errors.py +0 -0
  87. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/llm/rate_limit_config.py +0 -0
  88. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/llm/rate_limit_detector.py +0 -0
  89. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/llm/retry_handler.py +0 -0
  90. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/loader.py +0 -0
  91. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/metrics.py +0 -0
  92. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/progress.py +0 -0
  93. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/prompts.py +0 -0
  94. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/schemas.py +0 -0
  95. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/spin/__init__.py +0 -0
  96. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/spin/client.py +0 -0
  97. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/spin/models.py +0 -0
  98. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/stream_simulator.py +0 -0
  99. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/tools/__init__.py +0 -0
  100. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/tools/defaults.py +0 -0
  101. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/tools/loader.py +0 -0
  102. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/tools/mcp_client.py +0 -0
  103. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/topic_model.py +0 -0
  104. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/training/__init__.py +0 -0
  105. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/training/api_key_prompt.py +0 -0
  106. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/training/callback.py +0 -0
  107. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/training/dataset_utils.py +0 -0
  108. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/training/metrics_sender.py +0 -0
  109. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/tui.py +0 -0
  110. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/update_checker.py +0 -0
  111. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/utils.py +0 -0
  112. {deepfabric-4.11.0 → deepfabric-4.12.0}/deepfabric/validation.py +0 -0
  113. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/cli/evaluate.md +0 -0
  114. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/cli/import-tools.md +0 -0
  115. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/cli/info.md +0 -0
  116. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/cli/upload-hf.md +0 -0
  117. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/cli/upload-kaggle.md +0 -0
  118. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/cli/upload.md +0 -0
  119. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/cli/validate.md +0 -0
  120. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/cli/visualize.md +0 -0
  121. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/dataset-generation/index.md +0 -0
  122. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/dataset-generation/rate-limiting.md +0 -0
  123. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/evaluation/index.md +0 -0
  124. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/evaluation/metrics.md +0 -0
  125. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/evaluation/running.md +0 -0
  126. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/images/logo-light.png +0 -0
  127. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/images/python-spin.png +0 -0
  128. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/tools/custom.md +0 -0
  129. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/tools/index.md +0 -0
  130. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/tools/mock.md +0 -0
  131. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/tools/spin.md +0 -0
  132. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/tools/tutorials/go.md +0 -0
  133. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/tools/tutorials/index.md +0 -0
  134. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/tools/tutorials/python.md +0 -0
  135. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/tools/tutorials/rust.md +0 -0
  136. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/tools/tutorials/typescript.md +0 -0
  137. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/tools/vfs.md +0 -0
  138. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/training/chat-templates.md +0 -0
  139. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/training/dataset-preparation.md +0 -0
  140. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/training/frameworks.md +0 -0
  141. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/training/index.md +0 -0
  142. {deepfabric-4.11.0 → deepfabric-4.12.0}/docs/training/loading.md +0 -0
  143. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/agent-tools.yaml +0 -0
  144. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/basic-anthropic.yaml +0 -0
  145. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/basic-gemini.yaml +0 -0
  146. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/basic-graph.yaml +0 -0
  147. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/basic-ollama.yaml +0 -0
  148. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/basic-openai.yaml +0 -0
  149. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/basic-openrouter.yaml +0 -0
  150. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/basic-tree.yaml +0 -0
  151. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/coding-agent.yaml +0 -0
  152. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/complete.yaml +0 -0
  153. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/custom-tools.yaml +0 -0
  154. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/github-mock-tools.yaml +0 -0
  155. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/reasoning.yaml +0 -0
  156. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/spin-vfs-tools.yaml +0 -0
  157. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/tools-sdk-examples/blender/load-blender-mock-data.sh +0 -0
  158. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/tools-sdk-examples/blender/spin-blender.yaml +0 -0
  159. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/tools-sdk-examples/figma/load-figma-mock-data.sh +0 -0
  160. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/tools-sdk-examples/figma/spin-figma.yaml +0 -0
  161. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/tools-sdk-examples/github/load-github-mock-data.sh +0 -0
  162. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/tools-sdk-examples/github/spin-github-tools.yaml +0 -0
  163. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/tools-sdk-examples/kubernetes/load-kubernetes-mock-data.sh +0 -0
  164. {deepfabric-4.11.0 → deepfabric-4.12.0}/examples/tools-sdk-examples/kubernetes/spin-kubernetes.yaml +0 -0
  165. {deepfabric-4.11.0 → deepfabric-4.12.0}/misc/test_update_manual.py +0 -0
  166. {deepfabric-4.11.0 → deepfabric-4.12.0}/notebooks/Train_and_Evaluate_Qwen3_4B_Thinking_as_a_Blender_MCP_Agent.ipynb +0 -0
  167. {deepfabric-4.11.0 → deepfabric-4.12.0}/notebooks/dataset-compatibility-check.ipynb +0 -0
  168. {deepfabric-4.11.0 → deepfabric-4.12.0}/notebooks/evaluations.ipynb +0 -0
  169. {deepfabric-4.11.0 → deepfabric-4.12.0}/test-run/01-alpaca.txt +0 -0
  170. {deepfabric-4.11.0 → deepfabric-4.12.0}/test-run/01-chatml.txt +0 -0
  171. {deepfabric-4.11.0 → deepfabric-4.12.0}/test-run/01-grpo.txt +0 -0
  172. {deepfabric-4.11.0 → deepfabric-4.12.0}/test-run/01-xlam_v2.txt +0 -0
  173. {deepfabric-4.11.0 → deepfabric-4.12.0}/test-run/02-trl2.txt +0 -0
  174. {deepfabric-4.11.0 → deepfabric-4.12.0}/test-run/02-xlam_v2.txt +0 -0
  175. {deepfabric-4.11.0 → deepfabric-4.12.0}/test-run/04-agent-tool-conversations.jsnl +0 -0
  176. {deepfabric-4.11.0 → deepfabric-4.12.0}/test-run/04-single-agent-tools +0 -0
  177. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/__init__.py +0 -0
  178. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/integration/__init__.py +0 -0
  179. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/integration/conftest.py +0 -0
  180. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/integration/test_generator_integration.py +0 -0
  181. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/integration/test_graph_integration.py +0 -0
  182. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/integration/test_llm_client_integration.py +0 -0
  183. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/integration/test_spin_integration.py +0 -0
  184. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/integration/test_tree_integration.py +0 -0
  185. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/__init__.py +0 -0
  186. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/conftest.py +0 -0
  187. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_api_key_validation.py +0 -0
  188. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_checkpoint.py +0 -0
  189. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_cli.py +0 -0
  190. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_cloud_upload.py +0 -0
  191. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_config.py +0 -0
  192. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_dataset.py +0 -0
  193. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_error_codes.py +0 -0
  194. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_gemini_schema.py +0 -0
  195. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_generator.py +0 -0
  196. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_hf_hub.py +0 -0
  197. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_kaggle_hub.py +0 -0
  198. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_llm_eval_backend.py +0 -0
  199. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_loader.py +0 -0
  200. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_modular_config.py +0 -0
  201. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_rate_limiting.py +0 -0
  202. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_schemas.py +0 -0
  203. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_tool_call_parsers.py +0 -0
  204. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_training_callback.py +0 -0
  205. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_tui.py +0 -0
  206. {deepfabric-4.11.0 → deepfabric-4.12.0}/tests/unit/test_update_checker.py +0 -0
  207. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools/extract_messages.py +0 -0
  208. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools/function.py +0 -0
  209. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools/yaml_to_openai_tools.py +0 -0
  210. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/.dockerignore +0 -0
  211. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/Dockerfile +0 -0
  212. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/README.md +0 -0
  213. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/components/github/README.md +0 -0
  214. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/components/github/app.py +0 -0
  215. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/components/github/github.wasm +0 -0
  216. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/components/github/pyproject.toml +0 -0
  217. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/components/github/requirements.txt +0 -0
  218. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/components/github/spin.toml +0 -0
  219. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/components/github/uv.lock +0 -0
  220. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/components/mock/Cargo.lock +0 -0
  221. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/components/mock/Cargo.toml +0 -0
  222. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/components/mock/README.md +0 -0
  223. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/components/mock/src/lib.rs +0 -0
  224. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/components/vfs/Cargo.lock +0 -0
  225. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/components/vfs/Cargo.toml +0 -0
  226. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/components/vfs/src/lib.rs +0 -0
  227. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/docker-compose.yaml +0 -0
  228. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/runtime-config.toml +0 -0
  229. {deepfabric-4.11.0 → deepfabric-4.12.0}/tools-sdk/spin.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: DeepFabric
3
- Version: 4.11.0
3
+ Version: 4.12.0
4
4
  Summary: Curate High Quality Datasets, Train, Evaluate and Ship
5
5
  Author-email: DeepFabric Team <oss@alwaysfurther.ai>
6
6
  License-File: LICENSE
@@ -8,7 +8,13 @@ import signal
8
8
  import sys
9
9
 
10
10
  from pathlib import Path
11
- from typing import Literal, NoReturn, cast
11
+ from typing import TYPE_CHECKING, Literal, NoReturn, cast
12
+
13
+ if TYPE_CHECKING:
14
+ from rich.tree import Tree as RichTree
15
+
16
+ from .topic_inspector import TopicInspectionResult
17
+ from .tui import DeepFabricTUI
12
18
 
13
19
  import click
14
20
  import yaml
@@ -139,7 +145,7 @@ class GenerateOptions(BaseModel):
139
145
  batch_size: int | None = None
140
146
  base_url: str | None = None
141
147
  include_system_message: bool | None = None
142
- mode: Literal["tree", "graph"] = Field(default="tree")
148
+ mode: Literal["tree", "graph"] | None = Field(default=None)
143
149
  debug: bool = False
144
150
  topic_only: bool = False
145
151
  tui: Literal["rich", "simple"] = Field(default="rich")
@@ -332,7 +338,7 @@ def _load_and_prepare_generation_context(
332
338
  # Skip path validation for topic-only mode since we're not generating dataset samples
333
339
  if not skip_path_validation:
334
340
  validate_path_requirements(
335
- mode=options.mode,
341
+ mode=config.topics.mode,
336
342
  depth=final_depth,
337
343
  degree=final_degree,
338
344
  num_samples=final_num_samples,
@@ -341,7 +347,7 @@ def _load_and_prepare_generation_context(
341
347
  )
342
348
 
343
349
  show_validation_success(
344
- mode=options.mode,
350
+ mode=config.topics.mode,
345
351
  depth=final_depth,
346
352
  degree=final_degree,
347
353
  num_samples=final_num_samples,
@@ -655,8 +661,8 @@ def _run_generation(
655
661
  @click.option(
656
662
  "--mode",
657
663
  type=click.Choice(["tree", "graph"]),
658
- default="tree",
659
- help="Topic generation mode (default: tree)",
664
+ default=None,
665
+ help="Topic generation mode (default: graph)",
660
666
  )
661
667
  @click.option(
662
668
  "--debug",
@@ -2050,5 +2056,534 @@ def checkpoint_status(config_file: str) -> None:
2050
2056
  )
2051
2057
 
2052
2058
 
2059
+ # Topic inspection command group
2060
+ @click.group()
2061
+ def topic() -> None:
2062
+ """Topic management commands."""
2063
+ pass
2064
+
2065
+
2066
+ @topic.command("inspect")
2067
+ @click.argument("file", type=click.Path(exists=True))
2068
+ @click.option(
2069
+ "--level",
2070
+ "-l",
2071
+ type=int,
2072
+ default=None,
2073
+ help="Show topics at a specific depth level (0=root, 1=first children, etc.)",
2074
+ )
2075
+ @click.option(
2076
+ "--expand",
2077
+ "-e",
2078
+ type=int,
2079
+ default=None,
2080
+ is_flag=False,
2081
+ flag_value=-1, # -1 means expand all levels
2082
+ help="Show subtopics in tree format. Use alone for all levels, or specify depth (e.g., --expand 2)",
2083
+ )
2084
+ @click.option(
2085
+ "--all",
2086
+ "-a",
2087
+ "show_all",
2088
+ is_flag=True,
2089
+ help="Show the entire tree structure with indentation",
2090
+ )
2091
+ @click.option(
2092
+ "--format",
2093
+ "-f",
2094
+ "output_format",
2095
+ type=click.Choice(["tree", "table", "json"]),
2096
+ default="tree",
2097
+ help="Output format (default: tree)",
2098
+ )
2099
+ @click.option(
2100
+ "--uuid",
2101
+ "-u",
2102
+ "show_uuid",
2103
+ is_flag=True,
2104
+ help="Show UUID/topic_id for each leaf node",
2105
+ )
2106
+ def topic_inspect(
2107
+ file: str,
2108
+ level: int | None,
2109
+ expand: int | None,
2110
+ show_all: bool,
2111
+ output_format: str,
2112
+ show_uuid: bool,
2113
+ ) -> None:
2114
+ """Inspect a topic tree or graph file.
2115
+
2116
+ Displays metadata and structure of topic files generated by DeepFabric.
2117
+ Supports both Tree (JSONL) and Graph (JSON) formats with auto-detection.
2118
+
2119
+ Examples:
2120
+
2121
+ \b
2122
+ # Show file metadata and summary
2123
+ deepfabric topic inspect topic_tree.jsonl
2124
+
2125
+ \b
2126
+ # Show topics at depth level 2 (just topic names)
2127
+ deepfabric topic inspect topic_tree.jsonl --level 2
2128
+
2129
+ \b
2130
+ # Show level 2 topics and all subtopics (tree format)
2131
+ deepfabric topic inspect topic_tree.jsonl --level 2 --expand
2132
+
2133
+ \b
2134
+ # Show level 2 topics and 1 sublevel only (tree format)
2135
+ deepfabric topic inspect topic_tree.jsonl --level 2 --expand 1
2136
+
2137
+ \b
2138
+ # Show entire tree with indentation
2139
+ deepfabric topic inspect topic_graph.json --all
2140
+
2141
+ \b
2142
+ # Output as JSON for scripting
2143
+ deepfabric topic inspect topic_tree.jsonl --format json
2144
+
2145
+ \b
2146
+ # Show UUIDs for each leaf node
2147
+ deepfabric topic inspect topic_tree.jsonl --all --uuid
2148
+ """
2149
+ from .topic_inspector import inspect_topic_file # noqa: PLC0415
2150
+
2151
+ tui = get_tui()
2152
+
2153
+ try:
2154
+ # Perform inspection
2155
+ result = inspect_topic_file(file, level=level, expand_depth=expand, show_all=show_all)
2156
+
2157
+ # Handle JSON output format
2158
+ if output_format == "json":
2159
+ output = {
2160
+ "format": result.format,
2161
+ "source_file": result.source_file,
2162
+ "total_paths": result.total_paths,
2163
+ "max_depth": result.max_depth,
2164
+ "metadata": result.metadata,
2165
+ }
2166
+ if result.paths_at_level is not None:
2167
+ output["paths_at_level"] = result.paths_at_level
2168
+ if result.expanded_paths is not None:
2169
+ output["expanded_paths"] = result.expanded_paths
2170
+ if result.all_paths is not None:
2171
+ output["all_paths"] = result.all_paths
2172
+ tui.console.print_json(json.dumps(output))
2173
+ return
2174
+
2175
+ # Rich output (tree or table format)
2176
+ _display_inspection_result(tui, result, output_format, level, expand, show_all, show_uuid)
2177
+
2178
+ except FileNotFoundError as e:
2179
+ tui.error(str(e))
2180
+ sys.exit(1)
2181
+ except ValueError as e:
2182
+ tui.error(f"Invalid file: {e}")
2183
+ sys.exit(1)
2184
+ except Exception as e:
2185
+ tui.error(f"Error inspecting file: {e}")
2186
+ sys.exit(1)
2187
+
2188
+
2189
+ def _display_inspection_result(
2190
+ tui: "DeepFabricTUI",
2191
+ result: "TopicInspectionResult",
2192
+ output_format: str,
2193
+ level: int | None,
2194
+ expand: int | None,
2195
+ show_all: bool,
2196
+ show_uuid: bool = False,
2197
+ ) -> None:
2198
+ """Display inspection result using rich formatting."""
2199
+ from rich.panel import Panel # noqa: PLC0415
2200
+ from rich.table import Table # noqa: PLC0415
2201
+
2202
+ # Header with file info
2203
+ format_label = "Graph (JSON)" if result.format == "graph" else "Tree (JSONL)"
2204
+
2205
+ tui.console.print()
2206
+ tui.console.print("[bold cyan]Topic Inspector[/bold cyan]")
2207
+ tui.console.print(f"[dim]{result.source_file}[/dim]")
2208
+ tui.console.print()
2209
+
2210
+ # Statistics panel
2211
+ stats_table = Table(show_header=False, box=None, padding=(0, 1))
2212
+ stats_table.add_column(style="cyan", no_wrap=True)
2213
+ stats_table.add_column(style="white")
2214
+
2215
+ stats_table.add_row("Format:", format_label)
2216
+ stats_table.add_row("Total Paths:", str(result.total_paths))
2217
+ stats_table.add_row("Max Depth:", str(result.max_depth))
2218
+
2219
+ if result.metadata.get("root_topic"):
2220
+ root = result.metadata["root_topic"]
2221
+ if len(root) > 60: # noqa: PLR2004
2222
+ root = root[:57] + "..."
2223
+ stats_table.add_row("Root Topic:", root)
2224
+
2225
+ if result.metadata.get("total_nodes"):
2226
+ stats_table.add_row("Total Nodes:", str(result.metadata["total_nodes"]))
2227
+
2228
+ if result.metadata.get("has_cycles") is not None:
2229
+ has_cycles = "Yes" if result.metadata["has_cycles"] else "No"
2230
+ stats_table.add_row("Has Cycles:", has_cycles)
2231
+
2232
+ if result.metadata.get("provider"):
2233
+ stats_table.add_row("Provider:", result.metadata["provider"])
2234
+
2235
+ if result.metadata.get("model"):
2236
+ stats_table.add_row("Model:", result.metadata["model"])
2237
+
2238
+ if result.metadata.get("created_at"):
2239
+ stats_table.add_row("Created:", result.metadata["created_at"])
2240
+
2241
+ tui.console.print(Panel(stats_table, title="Statistics", border_style="dim"))
2242
+
2243
+ # Show level-specific topics (without expand) - simple list of topic names
2244
+ if level is not None and expand is None and result.paths_at_level is not None:
2245
+ tui.console.print()
2246
+ tui.console.print(f"[cyan bold]Topics at Level {level}:[/cyan bold]")
2247
+
2248
+ if not result.paths_at_level:
2249
+ tui.console.print(f" [dim]No topics at level {level}[/dim]")
2250
+ else:
2251
+ # Display as simple list of topic names (with UUIDs)
2252
+ for topic_path in result.paths_at_level:
2253
+ topic_name = topic_path[0] if topic_path else ""
2254
+ if show_uuid:
2255
+ # For graph format, use topic_to_uuid (node UUIDs)
2256
+ # For tree format, use path_to_uuid (leaf UUIDs only)
2257
+ uuid = ""
2258
+ if result.topic_to_uuid:
2259
+ uuid = result.topic_to_uuid.get(topic_name, "")
2260
+ if not uuid and result.path_to_uuid:
2261
+ uuid = result.path_to_uuid.get(tuple(topic_path), "")
2262
+ if uuid:
2263
+ tui.console.print(
2264
+ f" • {topic_name} [dim](UUID: {uuid})[/dim]", highlight=False
2265
+ )
2266
+ else:
2267
+ tui.console.print(f" • {topic_name}")
2268
+ else:
2269
+ tui.console.print(f" • {topic_name}")
2270
+
2271
+ # Show expanded subtree from level (with --expand)
2272
+ if level is not None and expand is not None and result.expanded_paths is not None:
2273
+ tui.console.print()
2274
+ depth_info = "all sublevels" if expand == -1 else f"{expand} sublevel(s)"
2275
+ tui.console.print(f"[cyan bold]Subtree from Level {level} ({depth_info}):[/cyan bold]")
2276
+
2277
+ if not result.expanded_paths:
2278
+ tui.console.print(f" [dim]No topics at or below level {level}[/dim]")
2279
+ elif output_format == "table":
2280
+ _display_paths_as_table(tui, result.expanded_paths)
2281
+ else:
2282
+ _display_paths_as_tree(
2283
+ tui,
2284
+ result.expanded_paths,
2285
+ result.path_to_uuid if show_uuid else None,
2286
+ result.topic_to_uuid if show_uuid else None,
2287
+ )
2288
+
2289
+ # Show all paths with tree structure
2290
+ if show_all and result.all_paths:
2291
+ tui.console.print()
2292
+ tui.console.print("[cyan bold]Full Tree Structure:[/cyan bold]")
2293
+
2294
+ if output_format == "table":
2295
+ _display_paths_as_table(tui, result.all_paths)
2296
+ else:
2297
+ _display_paths_as_tree(
2298
+ tui,
2299
+ result.all_paths,
2300
+ result.path_to_uuid if show_uuid else None,
2301
+ result.topic_to_uuid if show_uuid else None,
2302
+ )
2303
+
2304
+
2305
+ def _display_paths_as_table(tui: "DeepFabricTUI", paths: list[list[str]]) -> None:
2306
+ """Display paths in a table format."""
2307
+ from rich.table import Table # noqa: PLC0415
2308
+
2309
+ table = Table(show_header=True, header_style="bold cyan")
2310
+ table.add_column("#", style="dim")
2311
+ table.add_column("Path", style="white")
2312
+ table.add_column("Depth", style="green")
2313
+
2314
+ for i, path in enumerate(paths[:100], 1):
2315
+ path_str = " > ".join(path)
2316
+ if len(path_str) > 80: # noqa: PLR2004
2317
+ path_str = path_str[:77] + "..."
2318
+ table.add_row(str(i), path_str, str(len(path)))
2319
+
2320
+ if len(paths) > 100: # noqa: PLR2004
2321
+ table.add_row("...", f"[dim]{len(paths) - 100} more paths[/dim]", "")
2322
+
2323
+ tui.console.print(table)
2324
+
2325
+
2326
+ def _display_paths_as_tree(
2327
+ tui: "DeepFabricTUI",
2328
+ paths: list[list[str]],
2329
+ path_to_uuid: dict[tuple[str, ...], str] | None = None,
2330
+ topic_to_uuid: dict[str, str] | None = None,
2331
+ ) -> None:
2332
+ """Display paths in an indented tree format."""
2333
+ from rich.tree import Tree as RichTree # noqa: PLC0415
2334
+
2335
+ if not paths:
2336
+ return
2337
+
2338
+ # Build a tree structure from paths
2339
+ # Group paths by their root topic
2340
+ root_groups: dict[str, list[list[str]]] = {}
2341
+ for path in paths:
2342
+ if path:
2343
+ root = path[0]
2344
+ if root not in root_groups:
2345
+ root_groups[root] = []
2346
+ root_groups[root].append(path)
2347
+
2348
+ if len(root_groups) == 1:
2349
+ # Single root - show directly
2350
+ root_topic = paths[0][0]
2351
+ # Show UUID for root if available (graph format)
2352
+ root_label = f"[bold]{root_topic}[/bold]"
2353
+ if topic_to_uuid and root_topic in topic_to_uuid:
2354
+ root_label += f" [dim](UUID: {topic_to_uuid[root_topic]})[/dim]"
2355
+ tree = RichTree(root_label)
2356
+ _add_children_to_tree(
2357
+ tree, paths, 1, path_to_uuid=path_to_uuid, topic_to_uuid=topic_to_uuid
2358
+ )
2359
+ tui.console.print(tree)
2360
+ else:
2361
+ # Multiple roots - show each as a separate tree
2362
+ for root_topic, root_paths in list(root_groups.items())[:20]:
2363
+ root_label = f"[bold]{root_topic}[/bold]"
2364
+ if topic_to_uuid and root_topic in topic_to_uuid:
2365
+ root_label += f" [dim](UUID: {topic_to_uuid[root_topic]})[/dim]"
2366
+ tree = RichTree(root_label)
2367
+ _add_children_to_tree(
2368
+ tree, root_paths, 1, path_to_uuid=path_to_uuid, topic_to_uuid=topic_to_uuid
2369
+ )
2370
+ tui.console.print(tree)
2371
+ if len(root_groups) > 20: # noqa: PLR2004
2372
+ tui.console.print(f"[dim]... and {len(root_groups) - 20} more topics[/dim]")
2373
+
2374
+
2375
+ def _add_children_to_tree(
2376
+ parent: "RichTree",
2377
+ paths: list[list[str]],
2378
+ depth: int,
2379
+ max_depth: int = 5,
2380
+ path_to_uuid: dict[tuple[str, ...], str] | None = None,
2381
+ topic_to_uuid: dict[str, str] | None = None,
2382
+ ) -> None:
2383
+ """Recursively add children to a rich tree (limited depth for display)."""
2384
+ if depth > max_depth:
2385
+ remaining = len([p for p in paths if len(p) > depth])
2386
+ if remaining > 0:
2387
+ parent.add(f"[dim]... {remaining} more levels[/dim]")
2388
+ return
2389
+
2390
+ # Group paths by their element at current depth
2391
+ children: dict[str, list[list[str]]] = {}
2392
+ for path in paths:
2393
+ if len(path) > depth:
2394
+ child_topic = path[depth]
2395
+ if child_topic not in children:
2396
+ children[child_topic] = []
2397
+ children[child_topic].append(path)
2398
+
2399
+ # Add children to tree
2400
+ for child_topic, child_paths in list(children.items())[:20]:
2401
+ # Check for UUID: first try topic_to_uuid (graph nodes), then path_to_uuid (leaves)
2402
+ uuid = ""
2403
+ if topic_to_uuid and child_topic in topic_to_uuid:
2404
+ uuid = topic_to_uuid[child_topic]
2405
+ elif path_to_uuid:
2406
+ # Check if this child is a leaf (path ends at depth + 1)
2407
+ is_leaf = any(len(p) == depth + 1 for p in child_paths)
2408
+ if is_leaf:
2409
+ leaf_path = next((p for p in child_paths if len(p) == depth + 1), None)
2410
+ if leaf_path:
2411
+ uuid = path_to_uuid.get(tuple(leaf_path), "")
2412
+
2413
+ if uuid:
2414
+ child_node = parent.add(f"{child_topic} [dim](UUID: {uuid})[/dim]")
2415
+ else:
2416
+ child_node = parent.add(child_topic)
2417
+ _add_children_to_tree(
2418
+ child_node, child_paths, depth + 1, max_depth, path_to_uuid, topic_to_uuid
2419
+ )
2420
+
2421
+ if len(children) > 20: # noqa: PLR2004
2422
+ parent.add(f"[dim]... and {len(children) - 20} more siblings[/dim]")
2423
+
2424
+
2425
+ @topic.command("prune")
2426
+ @click.argument("file", type=click.Path(exists=True))
2427
+ @click.option(
2428
+ "--level",
2429
+ "-l",
2430
+ type=int,
2431
+ default=None,
2432
+ help="Prune all nodes below this depth level (0=root, 1=children, etc.)",
2433
+ )
2434
+ @click.option(
2435
+ "--uuid",
2436
+ "-u",
2437
+ type=str,
2438
+ default=None,
2439
+ help="Remove the node with this UUID and its entire subtree",
2440
+ )
2441
+ @click.option(
2442
+ "--output",
2443
+ "-o",
2444
+ type=click.Path(),
2445
+ default=None,
2446
+ help="Output file path (default: auto-generated from input filename)",
2447
+ )
2448
+ @click.option(
2449
+ "--force",
2450
+ "-f",
2451
+ is_flag=True,
2452
+ help="Overwrite the input file instead of creating a new one",
2453
+ )
2454
+ @click.option(
2455
+ "--dry-run",
2456
+ is_flag=True,
2457
+ help="Show what would be removed without making changes",
2458
+ )
2459
+ def topic_prune(
2460
+ file: str,
2461
+ level: int | None,
2462
+ uuid: str | None,
2463
+ output: str | None,
2464
+ force: bool,
2465
+ dry_run: bool,
2466
+ ) -> None:
2467
+ """Prune a topic graph by removing nodes.
2468
+
2469
+ Supports two modes:
2470
+
2471
+ \b
2472
+ # Remove all nodes below depth level 2
2473
+ deepfabric topic prune topic_graph.json --level 2
2474
+
2475
+ \b
2476
+ # Remove a specific node and its subtree by UUID
2477
+ deepfabric topic prune topic_graph.json --uuid abc-123-def
2478
+
2479
+ \b
2480
+ # Preview what would be removed (no file written)
2481
+ deepfabric topic prune topic_graph.json --level 1 --dry-run
2482
+
2483
+ \b
2484
+ # Overwrite the original file
2485
+ deepfabric topic prune topic_graph.json --uuid abc-123 --force
2486
+ """
2487
+ from .graph_pruner import ( # noqa: PLC0415
2488
+ load_graph_for_pruning,
2489
+ prune_graph_at_level,
2490
+ prune_graph_by_uuid,
2491
+ )
2492
+
2493
+ tui = get_tui()
2494
+
2495
+ # Validate: exactly one mode must be specified
2496
+ if level is None and uuid is None:
2497
+ tui.error("Specify either --level or --uuid")
2498
+ sys.exit(1)
2499
+ if level is not None and uuid is not None:
2500
+ tui.error("Cannot use --level and --uuid together")
2501
+ sys.exit(1)
2502
+
2503
+ try:
2504
+ if dry_run:
2505
+ graph = load_graph_for_pruning(file)
2506
+ total_nodes = len(graph.nodes)
2507
+
2508
+ tui.console.print()
2509
+ tui.console.print("[bold]DRY RUN[/bold] — no changes will be made")
2510
+ tui.console.print()
2511
+
2512
+ if level is not None:
2513
+ # BFS to compute node depths
2514
+ node_depths: dict[int, int] = {}
2515
+ queue: list[tuple] = [(graph.root, 0)]
2516
+ visited: set[int] = set()
2517
+ while queue:
2518
+ current, d = queue.pop(0)
2519
+ if current.id in visited:
2520
+ continue
2521
+ visited.add(current.id)
2522
+ node_depths[current.id] = d
2523
+ for child in current.children:
2524
+ if child.id not in visited:
2525
+ queue.append((child, d + 1))
2526
+
2527
+ to_remove = {nid for nid, d in node_depths.items() if d > level}
2528
+ tui.console.print(f" Graph: {total_nodes} unique nodes")
2529
+ tui.console.print(f" Would remove: {len(to_remove)} nodes below level {level}")
2530
+ tui.console.print(f" Would keep: {total_nodes - len(to_remove)} nodes")
2531
+ else:
2532
+ target = graph.find_node_by_uuid(uuid)
2533
+ if target is None:
2534
+ tui.error(f"No node found with UUID: {uuid}")
2535
+ sys.exit(1)
2536
+
2537
+ # BFS to count subtree
2538
+ subtree_count = 0
2539
+ bfs_queue = [target]
2540
+ visited_ids: set[int] = set()
2541
+ while bfs_queue:
2542
+ current = bfs_queue.pop(0)
2543
+ if current.id in visited_ids:
2544
+ continue
2545
+ visited_ids.add(current.id)
2546
+ subtree_count += 1
2547
+ for child in current.children:
2548
+ if child.id not in visited_ids:
2549
+ bfs_queue.append(child)
2550
+
2551
+ tui.console.print(f" Graph: {total_nodes} unique nodes")
2552
+ tui.console.print(
2553
+ f" Target: {target.topic}",
2554
+ highlight=False,
2555
+ )
2556
+ tui.console.print(f" Would remove: {subtree_count} nodes (including subtree)")
2557
+ tui.console.print(f" Would keep: {total_nodes - subtree_count} nodes")
2558
+ return
2559
+
2560
+ # Determine output path
2561
+ output_path = file if force else output
2562
+
2563
+ if level is not None:
2564
+ result = prune_graph_at_level(file, level, output_path)
2565
+ else:
2566
+ result = prune_graph_by_uuid(file, uuid, output_path)
2567
+
2568
+ tui.console.print()
2569
+ tui.success("Graph pruned successfully")
2570
+ tui.console.print(f" Removed: {result.removed_count} nodes")
2571
+ tui.console.print(
2572
+ f" Remaining: {result.remaining_nodes} nodes, {result.remaining_paths} paths"
2573
+ )
2574
+ tui.console.print(f" Saved to: {result.output_path}")
2575
+
2576
+ except FileNotFoundError as e:
2577
+ tui.error(str(e))
2578
+ sys.exit(1)
2579
+ except ValueError as e:
2580
+ tui.error(str(e))
2581
+ sys.exit(1)
2582
+
2583
+
2584
+ # Register the topic command group
2585
+ cli.add_command(topic)
2586
+
2587
+
2053
2588
  if __name__ == "__main__":
2054
2589
  cli()
@@ -8,6 +8,7 @@ from pydantic import BaseModel, Field, field_validator, model_validator
8
8
 
9
9
  from .constants import (
10
10
  DEFAULT_MAX_RETRIES,
11
+ DEFAULT_MAX_TOKENS,
11
12
  DEFAULT_MODEL,
12
13
  DEFAULT_PROVIDER,
13
14
  DEFAULT_SAMPLE_RETRIES,
@@ -86,7 +87,7 @@ class TopicsConfig(BaseModel):
86
87
  ..., min_length=1, description="The initial prompt to start topic generation"
87
88
  )
88
89
  mode: Literal["tree", "graph"] = Field(
89
- default="tree", description="Topic generation mode: tree or graph"
90
+ default="graph", description="Topic generation mode: tree or graph"
90
91
  )
91
92
  system_prompt: str = Field(
92
93
  default="", description="System prompt for topic exploration and generation"
@@ -109,6 +110,11 @@ class TopicsConfig(BaseModel):
109
110
  le=20,
110
111
  description="Maximum concurrent LLM calls during graph expansion (helps avoid rate limits)",
111
112
  )
113
+ max_tokens: int = Field(
114
+ default=DEFAULT_MAX_TOKENS,
115
+ ge=1,
116
+ description="Maximum tokens for topic generation LLM calls",
117
+ )
112
118
  save_as: str | None = Field(default=None, description="Where to save the generated topics")
113
119
  prompt_style: Literal["default", "isolated", "anchored"] = Field(
114
120
  default="default",
@@ -589,6 +595,7 @@ See documentation for full examples.
589
595
  "depth": self.topics.depth,
590
596
  "degree": self.topics.degree,
591
597
  "max_concurrent": self.topics.max_concurrent,
598
+ "max_tokens": self.topics.max_tokens,
592
599
  "prompt_style": self.topics.prompt_style,
593
600
  }
594
601
 
@@ -37,7 +37,7 @@ def load_config( # noqa: PLR0913
37
37
  topics_save_as: str | None = None,
38
38
  output_save_as: str | None = None,
39
39
  include_system_message: bool | None = None,
40
- mode: str = "tree",
40
+ mode: str | None = None,
41
41
  # Modular conversation configuration
42
42
  conversation_type: str | None = None,
43
43
  reasoning_style: str | None = None,
@@ -83,6 +83,8 @@ def load_config( # noqa: PLR0913
83
83
  except Exception as e:
84
84
  raise ConfigurationError(f"Error loading config file: {str(e)}") from e
85
85
  else:
86
+ if mode is not None:
87
+ config.topics.mode = mode
86
88
  return config
87
89
 
88
90
  # No config file provided - create minimal configuration from CLI args
@@ -92,6 +94,9 @@ def load_config( # noqa: PLR0913
92
94
  tui = get_tui()
93
95
  tui.info("No config file provided - using CLI parameters")
94
96
 
97
+ # Default to graph mode when no config file and no explicit mode
98
+ mode = mode or "graph"
99
+
95
100
  # Create minimal config dict with new structure
96
101
  default_prompt = generation_system_prompt or "You are a helpful AI assistant."
97
102