speedy-utils 2.0.2__tar.gz → 2.0.4__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 (165) hide show
  1. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.githooks/pre-commit +28 -54
  2. speedy_utils-2.0.4/.github/skills/make-vscode-happy/SKILL.md +80 -0
  3. speedy_utils-2.0.4/.github/skills/make-vscode-happy/references/diagnostic-playbook.md +94 -0
  4. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.gitignore +2 -1
  5. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/CLAUDE.md +62 -5
  6. speedy_utils-2.0.4/CODING_STYLE.md +521 -0
  7. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/PKG-INFO +12 -10
  8. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/README.md +9 -8
  9. speedy_utils-2.0.4/docs/PR_LIBRARY_ERROR_HANDLING_REFACTOR_PLAN.md +165 -0
  10. speedy_utils-2.0.4/notebooks/demo_multi_process.py +128 -0
  11. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/notebooks/llm_utils/llm_as_a_judge.ipynb +4 -16
  12. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/pyproject.toml +7 -2
  13. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/pyrightconfig.json +3 -3
  14. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/ruff.toml +16 -0
  15. speedy_utils-2.0.4/scripts/debug_import_time.py +417 -0
  16. speedy_utils-2.0.4/scripts/debug_multi_process.py +20 -0
  17. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/datasets_utils/viz_chat.py +3 -2
  18. speedy_utils-2.0.4/src/llm_utils/__init__.py +34 -0
  19. speedy_utils-2.0.4/src/llm_utils/_traceback.py +235 -0
  20. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/llm_utils/chat_format/display.py +4 -2
  21. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/llm_utils/lm/__init__.py +0 -2
  22. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/llm_utils/lm/llm.py +26 -13
  23. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/llm_utils/lm/llm_qwen3.py +93 -22
  24. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/llm_utils/lm/llm_signature.py +55 -4
  25. speedy_utils-2.0.4/src/llm_utils/lm/openai_memoize.py +430 -0
  26. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/llm_utils/lm/utils.py +2 -3
  27. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/llm_utils/scripts/sp_chat.py +1 -0
  28. speedy_utils-2.0.4/src/llm_utils/utils.py +42 -0
  29. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/__imports.py +1 -6
  30. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/__init__.py +17 -38
  31. speedy_utils-2.0.4/src/speedy_utils/common/exceptions.py +69 -0
  32. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/common/utils_cache.py +10 -8
  33. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/common/utils_error.py +8 -14
  34. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/common/utils_io.py +6 -2
  35. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/common/utils_misc.py +2 -0
  36. speedy_utils-2.0.4/src/speedy_utils/multi_worker/_mp_backends.py +814 -0
  37. speedy_utils-2.0.4/src/speedy_utils/multi_worker/_mp_progress.py +128 -0
  38. speedy_utils-2.0.4/src/speedy_utils/multi_worker/_mp_spawn.py +335 -0
  39. speedy_utils-2.0.4/src/speedy_utils/multi_worker/_multi_process.py +321 -0
  40. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/multi_worker/common.py +190 -180
  41. speedy_utils-2.0.4/src/speedy_utils/multi_worker/dataset_sharding.py +226 -0
  42. speedy_utils-2.0.4/src/speedy_utils/multi_worker/process.py +23 -0
  43. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/multi_worker/thread.py +302 -179
  44. speedy_utils-2.0.4/src/vision_utils/__init__.py +19 -0
  45. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/llm_utils/test_llm_call_contract.py +33 -1
  46. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/llm_utils/test_llm_mixins.py +55 -5
  47. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/llm_utils/test_llm_signature.py +1 -1
  48. speedy_utils-2.0.4/tests/llm_utils/test_public_api.py +117 -0
  49. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/test.py +6 -3
  50. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/test_memoize_typing.py +4 -0
  51. speedy_utils-2.0.4/tests/test_mp_no_main_guard.py +154 -0
  52. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/test_multi_process.py +66 -63
  53. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/test_multi_thread.py +4 -2
  54. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/test_process.py +133 -40
  55. speedy_utils-2.0.4/tests/test_process_update.py +343 -0
  56. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/test_thread.py +14 -2
  57. speedy_utils-2.0.4/tests/test_utils_io.py +18 -0
  58. speedy_utils-2.0.4/tools/check_syntax.py +185 -0
  59. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tools/uv_test.sh +1 -1
  60. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/uv.lock +8 -3
  61. speedy_utils-2.0.2/chainlit.md +0 -14
  62. speedy_utils-2.0.2/notebooks/test_multi_thread.ipynb +0 -82
  63. speedy_utils-2.0.2/scripts/debug_import_time.py +0 -210
  64. speedy_utils-2.0.2/src/llm_utils/__init__.py +0 -75
  65. speedy_utils-2.0.2/src/llm_utils/group_messages.py +0 -123
  66. speedy_utils-2.0.2/src/llm_utils/lm/openai_memoize.py +0 -172
  67. speedy_utils-2.0.2/src/speedy_utils/multi_worker/_multi_process.py +0 -819
  68. speedy_utils-2.0.2/src/speedy_utils/multi_worker/dataset_sharding.py +0 -192
  69. speedy_utils-2.0.2/src/speedy_utils/multi_worker/process.py +0 -71
  70. speedy_utils-2.0.2/src/vision_utils/__init__.py +0 -39
  71. speedy_utils-2.0.2/tests/test_process_update.py +0 -129
  72. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.codex +0 -0
  73. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.githooks/pre-push +0 -0
  74. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.github/prompts/improveParallelErrorHandling.prompt.md +0 -0
  75. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.github/skills/caching-utilities/SKILL.md +0 -0
  76. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.github/skills/caching-utilities/examples/caching_example.py +0 -0
  77. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.github/skills/dataset-processing-multiprocessing/CONFIG_REFERENCE.md +0 -0
  78. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.github/skills/dataset-processing-multiprocessing/SKILL.md +0 -0
  79. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.github/skills/dataset-processing-multiprocessing/example_tokenize_pack.py +0 -0
  80. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.github/skills/edit-llm-inference-style/SKILL.md +0 -0
  81. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.github/skills/io-utilities/SKILL.md +0 -0
  82. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.github/skills/io-utilities/examples/io_example.py +0 -0
  83. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.github/skills/llm-integration/SKILL.md +0 -0
  84. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.github/skills/llm-integration/examples/llm_example.py +0 -0
  85. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.github/skills/multi-threading-processing/SKILL.md +0 -0
  86. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.github/skills/ray-distributed-computing/SKILL.md +0 -0
  87. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.github/skills/skill-creation/SKILL.md +0 -0
  88. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.github/skills/vision-utilities/SKILL.md +0 -0
  89. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.github/skills/vision-utilities/examples/vision_example.py +0 -0
  90. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.github/workflows/publish.yml +0 -0
  91. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/.pre-commit-config.yaml +0 -0
  92. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/AGENTS.md +0 -0
  93. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/bumpversion.sh +0 -0
  94. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/debug/debug_generate_response.py +0 -0
  95. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/debug/debug_n_param.py +0 -0
  96. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/debug/debug_n_structure.py +0 -0
  97. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/debug/integration_test.py +0 -0
  98. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/debug/multi_thread.py +0 -0
  99. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/debug/test_decode_api.py +0 -0
  100. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/debug/test_endpoints.py +0 -0
  101. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/debug/test_generate.py +0 -0
  102. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/debug/test_generate_endpoint.py +0 -0
  103. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/debug/test_qwen3_generate.md +0 -0
  104. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/debug/test_qwen3_generate.py +0 -0
  105. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/docs/GENERATE_QUICKREF.md +0 -0
  106. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/docs/MIGRATION_THINKING.md +0 -0
  107. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/docs/TOKENIZATION.md +0 -0
  108. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/docs/TOKENIZATION_IMPLEMENTATION.md +0 -0
  109. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/examples/generate_example.py +0 -0
  110. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/examples/llm_public_api_example.ipynb +0 -0
  111. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/examples/model_specific_thinking_example.py +0 -0
  112. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/examples/tokenization_example.py +0 -0
  113. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/examples/vision_utils_example.py +0 -0
  114. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/experiments/exp1/dockerfile +0 -0
  115. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/experiments/exp1/run_in_docker.sh +0 -0
  116. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/experiments/exp1/test.png +0 -0
  117. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/notebooks/test.ipynb +0 -0
  118. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/scripts/bug_simple.py +0 -0
  119. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/scripts/deploy.sh +0 -0
  120. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/scripts/imports.sh +0 -0
  121. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/scripts/install_hooks.sh +0 -0
  122. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/scripts/test.py +0 -0
  123. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/scripts/test_error_handling.py +0 -0
  124. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/scripts/test_import_time_vision.py +0 -0
  125. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/scripts/test_locals.py +0 -0
  126. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/scripts/test_ray_mp.py +0 -0
  127. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/setup.cfg +0 -0
  128. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/datasets_utils/convert_to_arrow.py +0 -0
  129. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/llm_utils/chat_format/__init__.py +0 -0
  130. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/llm_utils/chat_format/transform.py +0 -0
  131. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/llm_utils/chat_format/utils.py +0 -0
  132. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/llm_utils/lm/base_prompt_builder.py +0 -0
  133. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/llm_utils/lm/lm_base.py +0 -0
  134. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/llm_utils/lm/signature.py +0 -0
  135. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/common/__init__.py +0 -0
  136. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/common/clock.py +0 -0
  137. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/common/function_decorator.py +0 -0
  138. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/common/logger.py +0 -0
  139. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/common/notebook_utils.py +0 -0
  140. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/common/patcher.py +0 -0
  141. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/common/report_manager.py +0 -0
  142. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/common/utils_print.py +0 -0
  143. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/multi_worker/__init__.py +0 -0
  144. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/scripts/__init__.py +0 -0
  145. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/scripts/kill_mpython.py +0 -0
  146. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/scripts/mpython.py +0 -0
  147. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/scripts/openapi_client_codegen.py +0 -0
  148. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/speedy_utils/scripts/prefetch_large_model.py +0 -0
  149. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/vision_utils/README.md +0 -0
  150. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/vision_utils/io_utils.py +0 -0
  151. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/src/vision_utils/plot.py +0 -0
  152. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/import_all.py +0 -0
  153. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/import_time_report.py +0 -0
  154. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/llm_utils/test_openai_memoize_completion_cache.py +0 -0
  155. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/llm_utils/test_openai_proxy_localhost.py +0 -0
  156. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/llm_utils/test_sp_chat.py +0 -0
  157. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/sample_objects.py +0 -0
  158. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/test_import_time.py +0 -0
  159. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/test_logger.py +0 -0
  160. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/test_logger_format.py +0 -0
  161. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/test_mpython.py +0 -0
  162. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/test_multithread_error_trace.py +0 -0
  163. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/test_prefetch_large_model.py +0 -0
  164. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/test_tokenization.py +0 -0
  165. {speedy_utils-2.0.2 → speedy_utils-2.0.4}/tests/test_viz_chat.py +0 -0
@@ -1,13 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """
3
- Pre-commit hook to ensure import time is under 0.4 seconds.
4
-
5
- This enforces the project's performance requirement that speedy_utils
6
- must import quickly (< 0.4s). This is critical for user experience.
7
-
8
- Strategy: Use lazy imports for heavy modules (torch, ray, matplotlib, pandas)
9
- inside functions that need them, not at module level.
10
- """
2
+ """Pre-commit hook to enforce import-time budgets with actionable output."""
11
3
 
12
4
  import os
13
5
  import subprocess
@@ -73,57 +65,39 @@ def _build_env() -> dict[str, str]:
73
65
  return env
74
66
 
75
67
 
76
- def check_import_time(module: str) -> tuple[bool, float, str]:
77
- """Check import time for a module. Returns (passed, time_seconds)."""
78
- code = f"import time; start=time.perf_counter(); import {module}; print(time.perf_counter()-start)"
68
+ def _helper_script() -> Path:
69
+ return _REPO_ROOT / "scripts" / "debug_import_time.py"
70
+
71
+
72
+ def main() -> int:
73
+ """Run import-time checks through the shared helper script."""
74
+ print("Checking import times... (max: {:.1f}s)".format(MAX_IMPORT_TIME))
75
+
76
+ helper = _helper_script()
79
77
  result = subprocess.run(
80
- [_PYTHON, "-c", code],
81
- capture_output=True,
82
- text=True,
78
+ [
79
+ _PYTHON,
80
+ str(helper),
81
+ "--max-total-sec",
82
+ str(MAX_IMPORT_TIME),
83
+ "--min-sec",
84
+ "0.01",
85
+ "--top",
86
+ "12",
87
+ "--no-stdlib",
88
+ "--quiet-success",
89
+ *MODULES,
90
+ ],
83
91
  env=_build_env(),
84
92
  cwd=str(_REPO_ROOT),
85
93
  )
86
- if result.returncode != 0:
87
- err = (result.stderr or result.stdout or "").strip()
88
- return False, -1, err
89
- try:
90
- import_time = float(result.stdout.strip())
91
- return import_time <= MAX_IMPORT_TIME, import_time, ""
92
- except ValueError:
93
- err = (result.stderr or result.stdout or "").strip()
94
- return False, -1, err
95
94
 
95
+ if result.returncode != 0:
96
+ print("\nImport time check failed.")
97
+ print("Rerun the helper above after moving heavy imports into function scope")
98
+ print("or a lazy __getattr__ export to pinpoint the slow path.")
96
99
 
97
- def main() -> int:
98
- """Run import time checks."""
99
- failed = False
100
-
101
- print("Checking import times... (max: {:.1f}s)".format(MAX_IMPORT_TIME))
102
-
103
- for module in MODULES:
104
- passed, import_time, err = check_import_time(module)
105
-
106
- if import_time < 0:
107
- print(f" {module}: FAILED - import error")
108
- if err:
109
- last_line = err.splitlines()[-1]
110
- print(f" ↳ {last_line}")
111
- failed = True
112
- elif passed:
113
- print(f" {module}: {import_time:.3f}s")
114
- else:
115
- print(f" {module}: FAILED - {import_time:.3f}s exceeds {MAX_IMPORT_TIME:.1f}s")
116
- failed = True
117
-
118
- if failed:
119
- print("\nImport time check failed! To fix:")
120
- print(" 1. Use lazy imports for heavy modules inside functions")
121
- print(" 2. Move heavy imports to function scope, not module level")
122
- print(" 3. Use the _LazyModule pattern from __imports.py")
123
- print("\nHeavy modules to lazy-load: torch, ray, matplotlib, pandas")
124
- return 1
125
-
126
- return 0
100
+ return result.returncode
127
101
 
128
102
 
129
103
  if __name__ == "__main__":
@@ -0,0 +1,80 @@
1
+ ---
2
+ name: 'make-vscode-happy'
3
+ description: 'Reduce VS Code Python red squiggles to zero in this repository. Use when Pylance or pyright reports errors, when tools/check_syntax.py fails, or when a change should leave VS Code diagnostics clean.'
4
+ argument-hint: 'Optional file path, module name, or failing area to fix'
5
+ ---
6
+
7
+ # Make VS Code Happy
8
+
9
+ Use this skill when the goal is to make VS Code and Pylance happy in this
10
+ repository by driving Python diagnostics to zero with the repo's existing
11
+ checker: `tools/check_syntax.py`.
12
+
13
+ ## When to Use
14
+
15
+ Use this skill when you need to:
16
+ - Fix Python red squiggles in VS Code.
17
+ - Reconcile Pylance diagnostics with command-line validation.
18
+ - Clean up pyright issues before committing.
19
+ - Verify that a targeted change did not leave new diagnostics behind.
20
+
21
+ ## Primary Checker
22
+
23
+ The authoritative checker for this repo is:
24
+
25
+ ```bash
26
+ uv run python tools/check_syntax.py
27
+ ```
28
+
29
+ Useful variants:
30
+
31
+ ```bash
32
+ uv run python tools/check_syntax.py --file src/llm_utils/lm/llm_qwen3.py
33
+ uv run python tools/check_syntax.py --include tests
34
+ uv run python tools/check_syntax.py --json
35
+ ```
36
+
37
+ This script mirrors the diagnostics VS Code Pylance shows for the repository's
38
+ `pyrightconfig.json`.
39
+
40
+ ## Procedure
41
+
42
+ 1. Pick scope first.
43
+ - If the request names a file, start with `--file` for that file.
44
+ - If the issue sounds cross-cutting or unclear, run the full check first.
45
+ 2. Read diagnostics and group them by root cause instead of fixing them one by
46
+ one in isolation.
47
+ 3. Fix the real source of the type error.
48
+ - Prefer correcting annotations, overloads, return types, and control flow.
49
+ - Use repo-approved ignores only when the code is intentionally dynamic.
50
+ 4. Re-run the smallest relevant check after each edit.
51
+ 5. Before concluding, run the broader validation needed for the scope.
52
+ - If you touched one file, re-run that file and then a full check if the
53
+ change affects shared APIs.
54
+ - If you changed common utilities, package exports, or typing helpers, run
55
+ the full check.
56
+ 6. If the fix changes imports in public packages, protect the import-time
57
+ budget as well.
58
+
59
+ ## Decision Points
60
+
61
+ - Single-file symptom vs shared typing pattern:
62
+ Start narrow for local regressions; go broad if the same diagnostic appears in
63
+ multiple files or stems from a shared helper.
64
+ - True bug vs accepted dynamic pattern:
65
+ Prefer a real type fix first. Only use a targeted ignore when the repository
66
+ already treats that pattern as intentionally dynamic.
67
+ - Typing fix vs performance regression:
68
+ Do not fix types by introducing heavy top-level imports in `__init__.py` or
69
+ other import-sensitive modules.
70
+
71
+ ## Completion Criteria
72
+
73
+ - `tools/check_syntax.py` reports zero relevant errors for the changed scope.
74
+ - Touched files do not introduce new warnings unless the user accepted them.
75
+ - The fix matches repository typing conventions.
76
+ - No eager heavy imports were added just to satisfy typing.
77
+
78
+ ## References
79
+
80
+ - [Diagnostic playbook](./references/diagnostic-playbook.md)
@@ -0,0 +1,94 @@
1
+ # Diagnostic Playbook
2
+
3
+ This reference supports the `make-vscode-happy` skill for the `speedy_utils`
4
+ repository.
5
+
6
+ ## Repo Rules That Matter
7
+
8
+ - Use `uv run python tools/check_syntax.py` as the source of truth for Python
9
+ diagnostics.
10
+ - Fix root causes instead of masking errors with broad ignores.
11
+ - Keep public package import time under `0.4s`.
12
+ - Do not introduce heavy top-level imports such as `torch`, `pandas`,
13
+ `matplotlib`, or `IPython` in `__init__.py` files.
14
+
15
+ ## Fast Triage
16
+
17
+ ### If the prompt names a specific file
18
+
19
+ Run:
20
+
21
+ ```bash
22
+ uv run python tools/check_syntax.py --file path/to/file.py
23
+ ```
24
+
25
+ Then fix the reported issue and re-run the same command.
26
+
27
+ ### If the prompt is vague or broad
28
+
29
+ Run:
30
+
31
+ ```bash
32
+ uv run python tools/check_syntax.py
33
+ ```
34
+
35
+ If the change touches tests or test helpers, consider:
36
+
37
+ ```bash
38
+ uv run python tools/check_syntax.py --include tests
39
+ ```
40
+
41
+ ## Common Fix Patterns In This Repo
42
+
43
+ ### Overload ordering
44
+
45
+ Put the `Awaitable[...]` overload before the generic non-awaitable overload to
46
+ avoid overlapping-overload diagnostics.
47
+
48
+ ### Dynamic Pydantic-style attributes
49
+
50
+ When the code intentionally sets attributes not declared in the model schema,
51
+ use a narrow ignore such as:
52
+
53
+ ```python
54
+ # type: ignore[attr-defined]
55
+ ```
56
+
57
+ Do not apply it broadly if a better annotation is possible.
58
+
59
+ ### `tqdm` annotations
60
+
61
+ Use the string literal form in annotations:
62
+
63
+ ```python
64
+ "tqdm"
65
+ ```
66
+
67
+ ### Lazy exports
68
+
69
+ For names loaded through `__getattr__`, follow the existing lazy-export pattern
70
+ instead of forcing eager imports for type convenience.
71
+
72
+ ### `__all__` with heavy lazy names
73
+
74
+ Do not add heavy lazy names to `__all__`, because star imports will defeat lazy
75
+ loading and hurt import time.
76
+
77
+ ## When a Typing Fix Touches Imports
78
+
79
+ If you changed imports in a public package, especially `speedy_utils`,
80
+ `llm_utils`, or `vision_utils`, run:
81
+
82
+ ```bash
83
+ uv run python scripts/debug_import_time.py speedy_utils llm_utils vision_utils --max-total-sec 0.4 --top 12 --min-sec 0.01 --no-stdlib
84
+ ```
85
+
86
+ Use that output to move heavy imports behind lazy boundaries if needed.
87
+
88
+ ## Definition of Done
89
+
90
+ - Targeted diagnostics are gone.
91
+ - Shared changes were validated with a broader repo check.
92
+ - The fix preserves existing lazy-import and typing conventions.
93
+ - VS Code should agree with command-line output because the checker mirrors
94
+ Pylance behavior for this repository.
@@ -125,4 +125,5 @@ dmypy.json
125
125
  edu_results.json
126
126
  .playwright-mcp
127
127
  .DS_Store
128
- .chainlit
128
+ .chainlit
129
+ chainlit.md
@@ -9,19 +9,22 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
9
9
  uv sync
10
10
 
11
11
  # Run tests
12
- uv run pytest -n 32
12
+ ./tools/uv_test.sh -n 32
13
13
 
14
14
  # Run a single test file
15
- uv run pytest tests/test_thread.py
15
+ ./tools/uv_test.sh tests/test_thread.py
16
16
 
17
17
  # Run with verbose output
18
- uv run pytest -v
18
+ ./tools/uv_test.sh -v
19
19
 
20
20
  # Check import time (must be < 0.4s)
21
21
  uv run python -c "import time; start=time.perf_counter(); import speedy_utils; print(f'{time.perf_counter()-start:.3f}s')"
22
22
 
23
- # Detailed import analysis
24
- uv run python scripts/debug_import_time.py speedy_utils --min-sec 0.05 --no-stdlib
23
+ # Detailed import analysis / import budget check
24
+ uv run python scripts/debug_import_time.py speedy_utils --max-total-sec 0.4 --top 12 --min-sec 0.01 --no-stdlib
25
+
26
+ # Check all public packages with the same helper used by pre-commit
27
+ uv run python scripts/debug_import_time.py speedy_utils llm_utils vision_utils --max-total-sec 0.4 --top 12 --min-sec 0.01 --no-stdlib
25
28
 
26
29
  # Lint with ruff
27
30
  uv run ruff check .
@@ -40,6 +43,16 @@ uv run ruff format .
40
43
 
41
44
  **CRITICAL:** All modules must import in under 0.4 seconds. This is enforced by a git pre-commit hook.
42
45
 
46
+ When the import-time hook fails, rerun `scripts/debug_import_time.py` with the
47
+ same package list and budget. The helper now prints both:
48
+
49
+ - top-level contributors by aggregated self time
50
+ - slowest full import paths from `python -X importtime`
51
+
52
+ Use that output to pinpoint which import chain needs to be moved behind a lazy
53
+ import. Keep the helper's default package list and thresholds aligned with the
54
+ packages this repo exports as the project evolves.
55
+
43
56
  ### Lazy Import Strategy
44
57
 
45
58
  Import heavy modules inside functions, NOT at module level:
@@ -170,6 +183,50 @@ The package provides these CLI commands:
170
183
  - Ignore rules `E402`, `F401`, `F403` in `__init__.py` files for lazy import patterns
171
184
  - Quote style: double quotes
172
185
  - Avoid hacky workarounds like sys.path.insert
186
+ - Follow `CODE_STYLE.md` for general style guidelines
187
+
188
+ ## Type Checking with Pyright
189
+
190
+ **CRITICAL:** Before committing, always run `uv run python tools/check_syntax.py` to
191
+ verify there are **zero** pyright errors. This tool mirrors exactly what VS Code
192
+ Pylance shows as red diagnostics.
193
+
194
+ ```bash
195
+ # Full check (src/ + tests/, honours pyrightconfig.json)
196
+ uv run python tools/check_syntax.py
197
+
198
+ # Import-time budget check with root-cause breakdown
199
+ uv run python scripts/debug_import_time.py speedy_utils llm_utils vision_utils --max-total-sec 0.4 --top 12 --min-sec 0.01 --no-stdlib
200
+
201
+ # Check a single file
202
+ uv run python tools/check_syntax.py --file src/llm_utils/lm/llm_qwen3.py
203
+
204
+ # Machine-readable JSON output
205
+ uv run python tools/check_syntax.py --json
206
+ ```
207
+
208
+ ### pyright config (`pyrightconfig.json`)
209
+
210
+ The config lives at the repo root. Key rules that are **suppressed** (because
211
+ they produce false positives in this codebase):
212
+
213
+ | Rule | Why suppressed |
214
+ |------|---------------|
215
+ | `reportMissingImports` | Optional GPU deps (`torch`, `nvidia.dali`) not installed everywhere |
216
+ | `reportAttributeAccessIssue` | Dynamic attrs on OpenAI pydantic models (`choice.usage`, `message.call_count`) |
217
+ | `reportUnsupportedDunderAll` | Lazy `__getattr__` exports listed in `__all__` |
218
+
219
+ ### Common type-annotation patterns
220
+
221
+ - **Lazy-loaded `__all__` entries** — never trigger `__getattr__`; use `# type: ignore[override]`
222
+ only on the `__getitem__` override pattern in datasets.
223
+ - **Dynamic pydantic attrs** — use `# type: ignore[attr-defined]` when setting attrs
224
+ not in the schema (e.g. `message.call_count = …`).
225
+ - **Overload ordering** — put the `Awaitable[R]` overload **before** the generic `R`
226
+ overload; otherwise pyright reports `reportOverlappingOverload`.
227
+ - **`tqdm` in type hints** — use string literal `"tqdm"` to avoid `reportInvalidTypeForm`.
228
+ - **Future custom attrs** — `concurrent.futures.Future` doesn't declare custom attrs;
229
+ suppress with `reportAttributeAccessIssue: "none"` in config.
173
230
 
174
231
 
175
232
  ## Version Management