llama-github 0.4.0__tar.gz → 0.4.2__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 (58) hide show
  1. {llama_github-0.4.0 → llama_github-0.4.2}/CHANGELOG.md +37 -0
  2. llama_github-0.4.2/MANIFEST.in +6 -0
  3. {llama_github-0.4.0/llama_github.egg-info → llama_github-0.4.2}/PKG-INFO +32 -48
  4. {llama_github-0.4.0 → llama_github-0.4.2}/README.md +13 -34
  5. llama_github-0.4.2/llama_github/__init__.py +20 -0
  6. {llama_github-0.4.0 → llama_github-0.4.2}/llama_github/config/config.json +3 -0
  7. llama_github-0.4.2/llama_github/data_retrieval/__init__.py +19 -0
  8. {llama_github-0.4.0 → llama_github-0.4.2}/llama_github/data_retrieval/github_api.py +181 -31
  9. llama_github-0.4.2/llama_github/data_retrieval/github_entities.py +1800 -0
  10. llama_github-0.4.2/llama_github/github_integration/__init__.py +13 -0
  11. llama_github-0.4.2/llama_github/github_integration/github_auth_manager.py +590 -0
  12. {llama_github-0.4.0 → llama_github-0.4.2}/llama_github/github_rag.py +43 -10
  13. {llama_github-0.4.0 → llama_github-0.4.2}/llama_github/llm_integration/initial_load.py +41 -4
  14. {llama_github-0.4.0 → llama_github-0.4.2}/llama_github/llm_integration/llm_handler.py +4 -4
  15. {llama_github-0.4.0 → llama_github-0.4.2}/llama_github/rag_processing/rag_processor.py +36 -20
  16. {llama_github-0.4.0 → llama_github-0.4.2}/llama_github/utils.py +28 -10
  17. llama_github-0.4.2/llama_github/version.py +1 -0
  18. {llama_github-0.4.0 → llama_github-0.4.2/llama_github.egg-info}/PKG-INFO +32 -48
  19. {llama_github-0.4.0 → llama_github-0.4.2}/llama_github.egg-info/SOURCES.txt +8 -0
  20. llama_github-0.4.2/llama_github.egg-info/requires.txt +23 -0
  21. {llama_github-0.4.0 → llama_github-0.4.2}/pyproject.toml +19 -14
  22. llama_github-0.4.2/pytest.ini +6 -0
  23. llama_github-0.4.2/scripts/__init__.py +1 -0
  24. llama_github-0.4.2/scripts/verify_distribution_contents.py +78 -0
  25. llama_github-0.4.2/tests/__init__.py +2 -0
  26. llama_github-0.4.2/tests/conftest.py +95 -0
  27. llama_github-0.4.2/tests/test_data_retrieval.py +850 -0
  28. llama_github-0.4.2/tests/test_distribution_contents.py +39 -0
  29. llama_github-0.4.2/tests/test_github_auth_manager.py +224 -0
  30. llama_github-0.4.2/tests/test_github_rag.py +78 -0
  31. {llama_github-0.4.0 → llama_github-0.4.2}/tests/test_initial_load.py +62 -0
  32. {llama_github-0.4.0 → llama_github-0.4.2}/tests/test_logger.py +2 -2
  33. llama_github-0.4.2/tests/test_logging_privacy.py +46 -0
  34. llama_github-0.4.2/tests/test_minimal_imports.py +35 -0
  35. {llama_github-0.4.0 → llama_github-0.4.2}/tests/test_utils.py +14 -1
  36. llama_github-0.4.0/MANIFEST.in +0 -2
  37. llama_github-0.4.0/llama_github/__init__.py +0 -3
  38. llama_github-0.4.0/llama_github/data_retrieval/github_entities.py +0 -882
  39. llama_github-0.4.0/llama_github/github_integration/__init__.py +0 -7
  40. llama_github-0.4.0/llama_github/github_integration/github_auth_manager.py +0 -222
  41. llama_github-0.4.0/llama_github/rag_processing/__init__.py +0 -0
  42. llama_github-0.4.0/llama_github/version.py +0 -1
  43. llama_github-0.4.0/llama_github.egg-info/requires.txt +0 -20
  44. llama_github-0.4.0/tests/test_data_retrieval.py +0 -118
  45. llama_github-0.4.0/tests/test_github_auth_manager.py +0 -85
  46. llama_github-0.4.0/tests/test_github_rag.py +0 -33
  47. {llama_github-0.4.0 → llama_github-0.4.2}/LICENSE +0 -0
  48. {llama_github-0.4.0 → llama_github-0.4.2}/llama_github/config/__init__.py +0 -0
  49. {llama_github-0.4.0 → llama_github-0.4.2}/llama_github/config/config.py +0 -0
  50. {llama_github-0.4.0/llama_github/data_retrieval → llama_github-0.4.2/llama_github/llm_integration}/__init__.py +0 -0
  51. {llama_github-0.4.0 → llama_github-0.4.2}/llama_github/logger.py +0 -0
  52. {llama_github-0.4.0/llama_github/llm_integration → llama_github-0.4.2/llama_github/rag_processing}/__init__.py +0 -0
  53. {llama_github-0.4.0 → llama_github-0.4.2}/llama_github.egg-info/dependency_links.txt +0 -0
  54. {llama_github-0.4.0 → llama_github-0.4.2}/llama_github.egg-info/top_level.txt +0 -0
  55. {llama_github-0.4.0 → llama_github-0.4.2}/setup.cfg +0 -0
  56. {llama_github-0.4.0 → llama_github-0.4.2}/setup.py +0 -0
  57. {llama_github-0.4.0 → llama_github-0.4.2}/tests/test_llm_handler.py +0 -0
  58. {llama_github-0.4.0 → llama_github-0.4.2}/tests/test_rag_processor.py +0 -0
@@ -5,6 +5,41 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.2] - 2026-07-15
9
+
10
+ ### Added
11
+ - Added a typed, fixed-cap repository text-read API with distinct success, policy, missing, oversize, binary, directory, and transport outcomes
12
+ - Added explicit `dependency_lock` and `ci_config` opt-ins without changing the legacy `get_file_content()` exclusion or cache contract
13
+
14
+ ### Security
15
+ - Enforced the 2 MiB product cap both from GitHub size metadata and while streaming raw bytes
16
+ - Kept binary, generated, minified, and sensitive paths outside the new opt-in surface
17
+ - Raised the tested `setuptools` and `transformers` lower bounds to versions that clear the current dependency-audit security gate
18
+
19
+ ## [0.4.1] - 2026-07-12
20
+
21
+ ### Added
22
+ - Added status-aware code/issue search APIs that distinguish `ok`, `no_hit`, `partial`, and `error`
23
+ - Added bounded pagination metadata for PR files and comments, plus the captured PR `head_sha`
24
+ - Added exact-head, bounded CI refresh with independently typed status/check-run evidence
25
+ - Added explicit repository-pool shutdown and an option to disable its cleanup worker
26
+
27
+ ### Changed
28
+ - Made the public `GithubRAG` import lazy so retrieval-only consumers do not import the ML/RAG stack
29
+ - Added connect/read timeouts, bounded REST pagination, privacy-safe logging, and immutable revisions for built-in Jina models
30
+ - Bounded related-issue extraction and expansion with configurable limits and truncation metadata
31
+ - Limited related-issue discovery to PR title/body and top-level PR comments, while retaining bare `#123` references in long descriptions
32
+ - Preserved review summaries and every inline review comment as separate chronological interactions
33
+ - Reduced commit-status history to the newest result per logical context while preserving fetched/current counts
34
+ - Raised dependency security floors while keeping the full install and public high-level RAG API compatible
35
+ - Hardened release and secret-scanning workflows with pinned actions, full-history scanning, checksum verification, and job-scoped PyPI OIDC
36
+ - Added a release artifact allowlist that rejects stale or unexplained files in the built wheel
37
+
38
+ ### Fixed
39
+ - Prevented Python AST parsing from being selected for non-Python paths based only on repository language metadata
40
+ - Fixed repository-pool cleanup so expired entries are actually evicted instead of retaining empty objects
41
+ - Fixed `get_pr_content()` so already-fetched PR comments participate in related-issue discovery instead of being processed too late
42
+
8
43
  ## [0.4.0] - 2026-03-25
9
44
 
10
45
  ### Changed
@@ -157,6 +192,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
157
192
  - Integration with LLM for processing and generating responses
158
193
 
159
194
  [0.1.4]: https://github.com/JetXu-LLM/llama-github/compare/v0.1.3...v0.1.4
195
+ [0.4.2]: https://github.com/JetXu-LLM/llama-github/compare/v0.4.1...v0.4.2
196
+ [0.4.1]: https://github.com/JetXu-LLM/llama-github/compare/v0.4.0...v0.4.1
160
197
  [0.1.3]: https://github.com/JetXu-LLM/llama-github/compare/v0.1.2...v0.1.3
161
198
  [0.1.2]: https://github.com/JetXu-LLM/llama-github/compare/v0.1.1...v0.1.2
162
199
  [0.1.1]: https://github.com/JetXu-LLM/llama-github/compare/v0.1.0...v0.1.1
@@ -0,0 +1,6 @@
1
+ include llama_github/config/config.json
2
+ include CHANGELOG.md
3
+ include pytest.ini
4
+ include tests/*.py
5
+ include scripts/*.py
6
+ prune tests/self
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llama-github
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
4
  Summary: GitHub retrieval and context generation utilities for coding assistants and AI developer workflows.
5
5
  Author-email: Jet Xu <Voldemort.xu@foxmail.com>
6
6
  License-Expression: Apache-2.0
@@ -11,66 +11,38 @@ Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.10
12
12
  Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
14
16
  Classifier: Operating System :: OS Independent
15
17
  Requires-Python: >=3.10
16
18
  Description-Content-Type: text/markdown
17
19
  License-File: LICENSE
18
20
  Requires-Dist: PyGithub<3,>=2.4
19
- Requires-Dist: aiohttp<4,>=3.9
20
- Requires-Dist: ipython<9,>=8
21
- Requires-Dist: langchain-core<0.4,>=0.3.30
22
- Requires-Dist: langchain-mistralai<0.3,>=0.2
23
- Requires-Dist: langchain-openai<0.4,>=0.3.0
24
- Requires-Dist: langchain-text-splitters<0.4,>=0.3.0
21
+ Requires-Dist: PyJWT[crypto]<3,>=2.13
22
+ Requires-Dist: aiohttp<4,>=3.14.1
23
+ Requires-Dist: ipython<9,>=8.10
24
+ Requires-Dist: langchain-core<2,>=1.3.3
25
+ Requires-Dist: langchain-mistralai<2,>=1.0
26
+ Requires-Dist: langchain-openai<2,>=1.1.14
27
+ Requires-Dist: langchain-text-splitters<2,>=1.1.2
25
28
  Requires-Dist: nest-asyncio<2,>=1.6
26
- Requires-Dist: numpy<3,>=1.24
29
+ Requires-Dist: numpy<3,>=1.26.4
27
30
  Requires-Dist: pydantic<3,>=2.10
28
31
  Requires-Dist: python-dateutil<3,>=2.9
29
- Requires-Dist: requests<3,>=2.31
30
- Requires-Dist: tokenizers<1,>=0.20
31
- Requires-Dist: transformers<6,>=4.47
32
- Requires-Dist: urllib3<3,>=2
32
+ Requires-Dist: requests<3,>=2.33
33
+ Requires-Dist: tokenizers<1,>=0.22.1
34
+ Requires-Dist: transformers<6,>=5.5
35
+ Requires-Dist: urllib3<3,>=2.7
33
36
  Provides-Extra: dev
34
37
  Requires-Dist: build<2,>=1.2; extra == "dev"
35
- Requires-Dist: pytest<9,>=8.3; extra == "dev"
36
- Requires-Dist: pytest-asyncio<1,>=0.25; extra == "dev"
38
+ Requires-Dist: pip-audit<3,>=2.10; extra == "dev"
39
+ Requires-Dist: pytest<10,>=9.0.3; extra == "dev"
40
+ Requires-Dist: pytest-asyncio<2,>=1.3; extra == "dev"
41
+ Requires-Dist: twine<7,>=6.2; extra == "dev"
37
42
  Dynamic: license-file
38
43
 
39
-
40
- <div align="right">
41
- <details>
42
- <summary >🌐 Language</summary>
43
- <div>
44
- <div align="center">
45
- <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=en">English</a>
46
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=zh-CN">简体中文</a>
47
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=zh-TW">繁體中文</a>
48
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=ja">日本語</a>
49
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=ko">한국어</a>
50
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=hi">हिन्दी</a>
51
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=th">ไทย</a>
52
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=fr">Français</a>
53
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=de">Deutsch</a>
54
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=es">Español</a>
55
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=it">Italiano</a>
56
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=ru">Русский</a>
57
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=pt">Português</a>
58
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=nl">Nederlands</a>
59
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=pl">Polski</a>
60
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=ar">العربية</a>
61
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=fa">فارسی</a>
62
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=tr">Türkçe</a>
63
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=vi">Tiếng Việt</a>
64
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=id">Bahasa Indonesia</a>
65
- </div>
66
- </div>
67
- </details>
68
- </div>
69
-
70
44
  # llama-github
71
45
 
72
- [Detail Document] https://deepwiki.com/JetXu-LLM/llama-github
73
-
74
46
  [![PyPI version](https://badge.fury.io/py/llama-github.svg)](https://badge.fury.io/py/llama-github)
75
47
  [![Downloads](https://static.pepy.tech/badge/Llama-github)](https://pepy.tech/project/Llama-github)
76
48
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
@@ -87,7 +59,16 @@ If you like this project or believe it has potential, please give it a ⭐️. Y
87
59
  pip install llama-github
88
60
  ```
89
61
 
90
- Current maintained runtime target: Python `3.10+`.
62
+ Current maintained runtime target: Python `3.10` through `3.14`.
63
+
64
+ Version `0.4.2` keeps the existing high-level API while allowing retrieval-only
65
+ integrations to import GitHub and diff helpers without loading the ML/RAG dependency
66
+ stack. Built-in Jina models are pinned to immutable revisions; custom remote-code
67
+ models should provide their own immutable revision. Pull-request retrieval now keeps
68
+ bounded fetch-health metadata, expands related issues only from PR conversation
69
+ evidence, and preserves review summaries plus each inline comment without collapsing
70
+ them together. Deterministic planners can explicitly opt into typed, 2 MiB-capped
71
+ lockfile or CI-config reads without weakening the generic file policy.
91
72
 
92
73
  ## Usage
93
74
 
@@ -112,6 +93,9 @@ contexts = github_rag.retrieve_context(
112
93
 
113
94
  print(contexts[0]["url"])
114
95
  print(contexts[0]["context"])
96
+
97
+ # Stop the optional repository-cache cleanup worker in long-lived applications.
98
+ github_rag.close()
115
99
  ```
116
100
 
117
101
  `retrieve_context()` returns a list of context dictionaries. Each item contains at least `context` and `url`.
@@ -1,38 +1,5 @@
1
-
2
- <div align="right">
3
- <details>
4
- <summary >🌐 Language</summary>
5
- <div>
6
- <div align="center">
7
- <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=en">English</a>
8
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=zh-CN">简体中文</a>
9
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=zh-TW">繁體中文</a>
10
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=ja">日本語</a>
11
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=ko">한국어</a>
12
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=hi">हिन्दी</a>
13
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=th">ไทย</a>
14
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=fr">Français</a>
15
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=de">Deutsch</a>
16
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=es">Español</a>
17
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=it">Italiano</a>
18
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=ru">Русский</a>
19
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=pt">Português</a>
20
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=nl">Nederlands</a>
21
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=pl">Polski</a>
22
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=ar">العربية</a>
23
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=fa">فارسی</a>
24
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=tr">Türkçe</a>
25
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=vi">Tiếng Việt</a>
26
- | <a href="https://openaitx.github.io/view.html?user=JetXu-LLM&project=llama-github&lang=id">Bahasa Indonesia</a>
27
- </div>
28
- </div>
29
- </details>
30
- </div>
31
-
32
1
  # llama-github
33
2
 
34
- [Detail Document] https://deepwiki.com/JetXu-LLM/llama-github
35
-
36
3
  [![PyPI version](https://badge.fury.io/py/llama-github.svg)](https://badge.fury.io/py/llama-github)
37
4
  [![Downloads](https://static.pepy.tech/badge/Llama-github)](https://pepy.tech/project/Llama-github)
38
5
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
@@ -49,7 +16,16 @@ If you like this project or believe it has potential, please give it a ⭐️. Y
49
16
  pip install llama-github
50
17
  ```
51
18
 
52
- Current maintained runtime target: Python `3.10+`.
19
+ Current maintained runtime target: Python `3.10` through `3.14`.
20
+
21
+ Version `0.4.2` keeps the existing high-level API while allowing retrieval-only
22
+ integrations to import GitHub and diff helpers without loading the ML/RAG dependency
23
+ stack. Built-in Jina models are pinned to immutable revisions; custom remote-code
24
+ models should provide their own immutable revision. Pull-request retrieval now keeps
25
+ bounded fetch-health metadata, expands related issues only from PR conversation
26
+ evidence, and preserves review summaries plus each inline comment without collapsing
27
+ them together. Deterministic planners can explicitly opt into typed, 2 MiB-capped
28
+ lockfile or CI-config reads without weakening the generic file policy.
53
29
 
54
30
  ## Usage
55
31
 
@@ -74,6 +50,9 @@ contexts = github_rag.retrieve_context(
74
50
 
75
51
  print(contexts[0]["url"])
76
52
  print(contexts[0]["context"])
53
+
54
+ # Stop the optional repository-cache cleanup worker in long-lived applications.
55
+ github_rag.close()
77
56
  ```
78
57
 
79
58
  `retrieve_context()` returns a list of context dictionaries. Each item contains at least `context` and `url`.
@@ -0,0 +1,20 @@
1
+ """Public package surface with heavyweight RAG imports loaded on demand."""
2
+
3
+ from .logger import configure_logging
4
+ from .version import __version__
5
+
6
+ __all__ = ["GithubRAG", "__version__", "configure_logging"]
7
+
8
+
9
+ def __getattr__(name: str):
10
+ """Preserve ``from llama_github import GithubRAG`` without eager ML imports."""
11
+ if name == "GithubRAG":
12
+ from .github_rag import GithubRAG
13
+
14
+ return GithubRAG
15
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
16
+
17
+
18
+ def __dir__():
19
+ """Expose lazy public attributes to introspection tools."""
20
+ return sorted(set(globals()) | set(__all__))
@@ -10,12 +10,15 @@
10
10
  "default_mistral_model": "mistral-small-latest",
11
11
  "default_mistral_simple_model": "mistral-small-latest",
12
12
  "default_embedding": "jinaai/jina-embeddings-v2-base-code",
13
+ "default_embedding_revision": "516f4baf13dec4ddddda8631e019b5737c8bc250",
13
14
  "default_reranker": "jinaai/jina-reranker-v2-base-multilingual",
15
+ "default_reranker_revision": "9cfeff2df7d40d1b78e75e5e9cebec92a99813c9",
14
16
  "min_stars_to_keep_result": 20,
15
17
  "max_workers": 8,
16
18
  "code_search_max_hits": 30,
17
19
  "issue_search_max_hits": 30,
18
20
  "repo_search_max_hits": 10,
21
+ "related_issue_max_hits": 20,
19
22
  "chunk_size": 2000,
20
23
  "issue_chunk_size": 7000,
21
24
  "repo_chunk_size": 7000,
@@ -0,0 +1,19 @@
1
+ """Typed retrieval primitives exposed by llama-github."""
2
+
3
+ from .github_entities import (
4
+ BoundedTextReadOptIn,
5
+ BoundedTextReadOutcome,
6
+ BoundedTextReadResult,
7
+ CIStatusSnapshot,
8
+ Repository,
9
+ RepositoryPool,
10
+ )
11
+
12
+ __all__ = [
13
+ "BoundedTextReadOptIn",
14
+ "BoundedTextReadOutcome",
15
+ "BoundedTextReadResult",
16
+ "CIStatusSnapshot",
17
+ "Repository",
18
+ "RepositoryPool",
19
+ ]
@@ -2,15 +2,19 @@ from __future__ import annotations
2
2
 
3
3
  import re
4
4
  from concurrent.futures import ThreadPoolExecutor, as_completed
5
- from typing import Dict, List, Optional
5
+ from typing import Optional
6
6
 
7
7
  from github import GithubException
8
8
 
9
9
  from llama_github.config.config import config
10
- from llama_github.github_integration.github_auth_manager import ExtendedGithub
10
+ from llama_github.github_integration.github_auth_manager import (
11
+ ExtendedGithub,
12
+ RetrievalOutcome,
13
+ RetrievalResult,
14
+ )
11
15
  from llama_github.logger import logger
12
16
 
13
- from .github_entities import Repository, RepositoryPool
17
+ from .github_entities import RepositoryPool
14
18
 
15
19
 
16
20
  class GitHubAPIHandler:
@@ -23,8 +27,21 @@ class GitHubAPIHandler:
23
27
  ):
24
28
  """Create a retrieval helper bound to one GitHub client and one repository pool."""
25
29
  self._github = github_instance
30
+ self._owns_pool = pool is None
26
31
  self.pool = pool if pool is not None else RepositoryPool(github_instance)
27
32
 
33
+ def close(self) -> None:
34
+ """Close the internally-created pool; injected pools remain caller-owned."""
35
+ if self._owns_pool:
36
+ self.pool.close()
37
+
38
+ def __enter__(self):
39
+ return self
40
+
41
+ def __exit__(self, exc_type, exc_value, traceback):
42
+ self.close()
43
+ return False
44
+
28
45
  def _require_github(self) -> ExtendedGithub:
29
46
  """Return the configured GitHub client or raise a clear runtime error."""
30
47
  if self._github is None:
@@ -59,8 +76,12 @@ class GitHubAPIHandler:
59
76
  )
60
77
  )
61
78
  return result
62
- except (GithubException, RuntimeError) as exc:
63
- logger.exception("Error searching repositories with query '%s': %s", query, exc)
79
+ except Exception as exc:
80
+ logger.error(
81
+ "Repository search failed query_length=%s error_type=%s",
82
+ len(query or ""),
83
+ type(exc).__name__,
84
+ )
64
85
  return []
65
86
 
66
87
  def get_repository(self, full_repo_name, github_instance=None):
@@ -76,20 +97,41 @@ class GitHubAPIHandler:
76
97
  file_content = repository_obj.get_file_content(code_search_result["path"])
77
98
  return repository_obj, file_content
78
99
 
79
- def search_code(self, query, repo_full_name=None):
80
- """Search GitHub code and expand search hits into ranked file-content records."""
100
+ def search_code_with_status(self, query, repo_full_name=None) -> RetrievalResult:
101
+ """Search code and retain fetch status through content expansion."""
81
102
  try:
82
103
  github = self._require_github()
83
- logger.debug("Searching code with query '%s'...", query)
104
+ logger.debug("Searching code query_length=%s", len(query or ""))
84
105
  if repo_full_name:
85
106
  query = f"{query} repo:{repo_full_name}"
86
107
 
87
- code_results = github.search_code(
88
- query=query,
89
- per_page=config.get("code_search_max_hits"),
108
+ status_search = getattr(github, "search_code_with_status", None)
109
+ raw_result = (
110
+ status_search(
111
+ query=query,
112
+ per_page=config.get("code_search_max_hits"),
113
+ )
114
+ if callable(status_search)
115
+ else None
90
116
  )
117
+ if not isinstance(raw_result, RetrievalResult):
118
+ legacy_items = github.search_code(
119
+ query=query,
120
+ per_page=config.get("code_search_max_hits"),
121
+ )
122
+ raw_result = RetrievalResult(
123
+ items=list(legacy_items or []),
124
+ outcome=(
125
+ RetrievalOutcome.OK
126
+ if legacy_items
127
+ else RetrievalOutcome.NO_HIT
128
+ ),
129
+ pages_fetched=1,
130
+ )
131
+ code_results = raw_result.items
91
132
 
92
133
  results_with_index = []
134
+ content_failures = 0
93
135
  with ThreadPoolExecutor(max_workers=config.get("max_workers")) as executor:
94
136
  future_to_index = {
95
137
  executor.submit(self._get_file_content_through_repository, code_result): index
@@ -117,12 +159,54 @@ class GitHubAPIHandler:
117
159
  }
118
160
  )
119
161
  except Exception:
120
- logger.exception("%s generated an exception:", code_result["name"])
162
+ content_failures += 1
163
+ logger.error(
164
+ "Code-search content expansion failed result_index=%s",
165
+ index,
166
+ )
121
167
 
122
- return sorted(results_with_index, key=lambda item: item["index"])
123
- except (GithubException, RuntimeError) as exc:
124
- logger.exception("Error searching code with query '%s': %s", query, exc)
125
- return []
168
+ items = sorted(results_with_index, key=lambda item: item["index"])
169
+ missing_content = len(code_results) - len(items)
170
+ incomplete = raw_result.outcome in {
171
+ RetrievalOutcome.PARTIAL,
172
+ RetrievalOutcome.ERROR,
173
+ } or missing_content > 0
174
+ if incomplete:
175
+ outcome = (
176
+ RetrievalOutcome.ERROR
177
+ if raw_result.outcome is RetrievalOutcome.ERROR and not items
178
+ else RetrievalOutcome.PARTIAL
179
+ )
180
+ elif items:
181
+ outcome = RetrievalOutcome.OK
182
+ else:
183
+ outcome = RetrievalOutcome.NO_HIT
184
+ return RetrievalResult(
185
+ items=items,
186
+ outcome=outcome,
187
+ pages_fetched=raw_result.pages_fetched,
188
+ truncated=raw_result.truncated,
189
+ status_code=raw_result.status_code,
190
+ error_type=(
191
+ "content_fetch_incomplete"
192
+ if missing_content or content_failures
193
+ else raw_result.error_type
194
+ ),
195
+ )
196
+ except Exception as exc:
197
+ logger.error(
198
+ "Code search failed query_length=%s error_type=%s",
199
+ len(query or ""),
200
+ type(exc).__name__,
201
+ )
202
+ return RetrievalResult(
203
+ outcome=RetrievalOutcome.ERROR,
204
+ error_type=type(exc).__name__,
205
+ )
206
+
207
+ def search_code(self, query, repo_full_name=None):
208
+ """Compatibility API returning expanded code-search items only."""
209
+ return self.search_code_with_status(query, repo_full_name).items
126
210
 
127
211
  def _get_issue_content_through_repository(self, issue):
128
212
  """Resolve an issue search hit into the rendered issue-content text block."""
@@ -130,8 +214,7 @@ class GitHubAPIHandler:
130
214
  match = re.search(r"https://api.github.com/repos/([^/]+/[^/]+)/issues/(\d+)", issue_url)
131
215
  if not match:
132
216
  logger.warning(
133
- "Failed to extract repo_full_name and issue_number from issue url: %s",
134
- issue_url,
217
+ "Failed to extract repository and issue number from issue URL",
135
218
  )
136
219
  return None
137
220
 
@@ -140,18 +223,39 @@ class GitHubAPIHandler:
140
223
  repository_obj = self.get_repository(repo_full_name, github_instance=self._github)
141
224
  return repository_obj.get_issue_content(number=issue_number, issue=issue)
142
225
 
143
- def search_issues(self, query, repo_full_name=None):
144
- """Search GitHub issues and expand each result into a normalized issue-content record."""
226
+ def search_issues_with_status(self, query, repo_full_name=None) -> RetrievalResult:
227
+ """Search issues and retain fetch status through content expansion."""
145
228
  try:
146
229
  github = self._require_github()
147
- logger.debug("Searching issues with query '%s'...", query)
230
+ logger.debug("Searching issues query_length=%s", len(query or ""))
148
231
  if repo_full_name:
149
232
  query = f"{query} repo:{repo_full_name}"
150
233
 
151
- issue_results = github.search_issues(
152
- query=query,
153
- per_page=config.get("issue_search_max_hits"),
234
+ status_search = getattr(github, "search_issues_with_status", None)
235
+ raw_result = (
236
+ status_search(
237
+ query=query,
238
+ per_page=config.get("issue_search_max_hits"),
239
+ )
240
+ if callable(status_search)
241
+ else None
154
242
  )
243
+ if not isinstance(raw_result, RetrievalResult):
244
+ legacy_items = github.search_issues(
245
+ query=query,
246
+ per_page=config.get("issue_search_max_hits"),
247
+ )
248
+ raw_result = RetrievalResult(
249
+ items=list(legacy_items or []),
250
+ outcome=(
251
+ RetrievalOutcome.OK
252
+ if legacy_items
253
+ else RetrievalOutcome.NO_HIT
254
+ ),
255
+ pages_fetched=1,
256
+ )
257
+ raw_issue_count = len(raw_result.items)
258
+ issue_results = raw_result.items
155
259
  issue_results = [
156
260
  issue
157
261
  for issue in issue_results
@@ -159,6 +263,7 @@ class GitHubAPIHandler:
159
263
  ]
160
264
 
161
265
  results_with_index = []
266
+ content_failures = 0
162
267
  with ThreadPoolExecutor(max_workers=config.get("max_workers")) as executor:
163
268
  future_to_index = {
164
269
  executor.submit(self._get_issue_content_through_repository, issue): index
@@ -180,12 +285,54 @@ class GitHubAPIHandler:
180
285
  }
181
286
  )
182
287
  except Exception:
183
- logger.exception("%s generated an exception:", issue_result["url"])
288
+ content_failures += 1
289
+ logger.error(
290
+ "Issue-search content expansion failed result_index=%s",
291
+ index,
292
+ )
184
293
 
185
- return sorted(results_with_index, key=lambda item: item["index"])
186
- except (GithubException, RuntimeError) as exc:
187
- logger.exception("Error searching issue with query '%s': %s", query, exc)
188
- return []
294
+ items = sorted(results_with_index, key=lambda item: item["index"])
295
+ missing_content = raw_issue_count - len(items)
296
+ incomplete = raw_result.outcome in {
297
+ RetrievalOutcome.PARTIAL,
298
+ RetrievalOutcome.ERROR,
299
+ } or missing_content > 0
300
+ if incomplete:
301
+ outcome = (
302
+ RetrievalOutcome.ERROR
303
+ if raw_result.outcome is RetrievalOutcome.ERROR and not items
304
+ else RetrievalOutcome.PARTIAL
305
+ )
306
+ elif items:
307
+ outcome = RetrievalOutcome.OK
308
+ else:
309
+ outcome = RetrievalOutcome.NO_HIT
310
+ return RetrievalResult(
311
+ items=items,
312
+ outcome=outcome,
313
+ pages_fetched=raw_result.pages_fetched,
314
+ truncated=raw_result.truncated,
315
+ status_code=raw_result.status_code,
316
+ error_type=(
317
+ "content_fetch_incomplete"
318
+ if missing_content or content_failures
319
+ else raw_result.error_type
320
+ ),
321
+ )
322
+ except Exception as exc:
323
+ logger.error(
324
+ "Issue search failed query_length=%s error_type=%s",
325
+ len(query or ""),
326
+ type(exc).__name__,
327
+ )
328
+ return RetrievalResult(
329
+ outcome=RetrievalOutcome.ERROR,
330
+ error_type=type(exc).__name__,
331
+ )
332
+
333
+ def search_issues(self, query, repo_full_name=None):
334
+ """Compatibility API returning expanded issue-search items only."""
335
+ return self.search_issues_with_status(query, repo_full_name).items
189
336
 
190
337
  @staticmethod
191
338
  def _categorize_github_url(url):
@@ -209,7 +356,7 @@ class GitHubAPIHandler:
209
356
  """Expand a GitHub repository / issue / file URL into text content for retrieval."""
210
357
  try:
211
358
  self._require_github()
212
- logger.debug("Retrieving content from GitHub URL '%s'...", url)
359
+ logger.debug("Retrieving content from categorized GitHub URL")
213
360
  content = None
214
361
  category = GitHubAPIHandler._categorize_github_url(url)
215
362
  if category == "repo":
@@ -239,5 +386,8 @@ class GitHubAPIHandler:
239
386
  logger.warning("Unsupported GitHub URL category: %s", category)
240
387
  return content
241
388
  except (GithubException, RuntimeError) as exc:
242
- logger.exception("Error retrieving content from GitHub URL '%s': %s", url, exc)
389
+ logger.error(
390
+ "GitHub URL retrieval failed error_type=%s",
391
+ type(exc).__name__,
392
+ )
243
393
  return None