github-haystack 1.2.0__tar.gz → 2.0.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 (48) hide show
  1. {github_haystack-1.2.0 → github_haystack-2.0.0}/CHANGELOG.md +20 -0
  2. {github_haystack-1.2.0 → github_haystack-2.0.0}/PKG-INFO +3 -4
  3. github_haystack-2.0.0/pydoc/config_docusaurus.yml +33 -0
  4. {github_haystack-1.2.0 → github_haystack-2.0.0}/pyproject.toml +5 -11
  5. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/components/connectors/github/file_editor.py +18 -16
  6. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/components/connectors/github/issue_commenter.py +6 -4
  7. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/components/connectors/github/issue_viewer.py +8 -6
  8. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/components/connectors/github/pr_creator.py +8 -6
  9. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/components/connectors/github/repo_forker.py +7 -5
  10. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/components/connectors/github/repo_viewer.py +12 -10
  11. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/tools/github/file_editor_tool.py +11 -10
  12. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/tools/github/issue_commenter_tool.py +10 -9
  13. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/tools/github/issue_viewer_tool.py +11 -10
  14. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/tools/github/pr_creator_tool.py +10 -9
  15. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/tools/github/repo_forker_tool.py +10 -9
  16. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/tools/github/repo_viewer_tool.py +12 -11
  17. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/tools/github/utils.py +7 -6
  18. {github_haystack-1.2.0 → github_haystack-2.0.0}/tests/test_file_editor.py +11 -0
  19. {github_haystack-1.2.0 → github_haystack-2.0.0}/tests/test_file_editor_tool.py +7 -5
  20. {github_haystack-1.2.0 → github_haystack-2.0.0}/tests/test_issue_commenter.py +11 -0
  21. {github_haystack-1.2.0 → github_haystack-2.0.0}/tests/test_issue_commenter_tool.py +8 -8
  22. {github_haystack-1.2.0 → github_haystack-2.0.0}/tests/test_issue_viewer.py +34 -0
  23. {github_haystack-1.2.0 → github_haystack-2.0.0}/tests/test_issue_viewer_tool.py +8 -8
  24. {github_haystack-1.2.0 → github_haystack-2.0.0}/tests/test_pr_creator.py +11 -0
  25. {github_haystack-1.2.0 → github_haystack-2.0.0}/tests/test_pr_creator_tool.py +8 -8
  26. {github_haystack-1.2.0 → github_haystack-2.0.0}/tests/test_repo_forker.py +11 -0
  27. {github_haystack-1.2.0 → github_haystack-2.0.0}/tests/test_repo_forker_tool.py +9 -9
  28. {github_haystack-1.2.0 → github_haystack-2.0.0}/tests/test_repo_viewer.py +12 -0
  29. {github_haystack-1.2.0 → github_haystack-2.0.0}/tests/test_repo_viewer_tool.py +4 -4
  30. github_haystack-1.2.0/pydoc/config.yml +0 -34
  31. {github_haystack-1.2.0 → github_haystack-2.0.0}/.gitignore +0 -0
  32. {github_haystack-1.2.0 → github_haystack-2.0.0}/LICENSE.txt +0 -0
  33. {github_haystack-1.2.0 → github_haystack-2.0.0}/README.md +0 -0
  34. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/components/connectors/github/__init__.py +0 -0
  35. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/components/connectors/py.typed +0 -0
  36. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/prompts/github/__init__.py +0 -0
  37. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/prompts/github/context_prompt.py +0 -0
  38. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/prompts/github/file_editor_prompt.py +0 -0
  39. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/prompts/github/issue_commenter_prompt.py +0 -0
  40. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/prompts/github/issue_viewer_prompt.py +0 -0
  41. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/prompts/github/pr_creator_prompt.py +0 -0
  42. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/prompts/github/repo_forker_prompt.py +0 -0
  43. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/prompts/github/repo_viewer_prompt.py +0 -0
  44. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/prompts/github/system_prompt.py +0 -0
  45. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/prompts/py.typed +0 -0
  46. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/tools/github/__init__.py +0 -0
  47. {github_haystack-1.2.0 → github_haystack-2.0.0}/src/haystack_integrations/tools/py.typed +0 -0
  48. {github_haystack-1.2.0 → github_haystack-2.0.0}/tests/__init__.py +0 -0
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## [integrations/github-v1.2.1] - 2025-10-20
4
+
5
+ ### 🚀 Features
6
+
7
+ - Add support for GitHubRepoForkerTool (#1968)
8
+
9
+
10
+ ### 🧪 Testing
11
+
12
+ - Fix GitHub test (#2119)
13
+
14
+ ### 🧹 Chores
15
+
16
+ - Standardize readmes - part 2 (#2205)
17
+
18
+ ### 🌀 Miscellaneous
19
+
20
+ - Chore: Fix linting github (#2255)
21
+ - Fix: handle empty GitHub tokens in authentication headers (#2400)
22
+
3
23
  ## [integrations/github-v1.1.0] - 2025-07-01
4
24
 
5
25
  ### 🐛 Bug Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github-haystack
3
- Version: 1.2.0
3
+ Version: 2.0.0
4
4
  Summary: Haystack components for interacting with GitHub repositories
5
5
  Project-URL: Source, https://github.com/deepset-ai/haystack-core-integrations/github
6
6
  Project-URL: Documentation, https://github.com/deepset-ai/haystack-core-integrations/blob/main/integrations/github/README.md
@@ -11,15 +11,14 @@ License-File: LICENSE.txt
11
11
  Classifier: Development Status :: 4 - Beta
12
12
  Classifier: License :: OSI Approved :: Apache Software License
13
13
  Classifier: Programming Language :: Python
14
- Classifier: Programming Language :: Python :: 3.9
15
14
  Classifier: Programming Language :: Python :: 3.10
16
15
  Classifier: Programming Language :: Python :: 3.11
17
16
  Classifier: Programming Language :: Python :: 3.12
18
17
  Classifier: Programming Language :: Python :: 3.13
19
18
  Classifier: Programming Language :: Python :: Implementation :: CPython
20
19
  Classifier: Programming Language :: Python :: Implementation :: PyPy
21
- Requires-Python: >=3.9
22
- Requires-Dist: haystack-ai>=2.12.0
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: haystack-ai>=2.22.0
23
22
  Description-Content-Type: text/markdown
24
23
 
25
24
  # github-haystack
@@ -0,0 +1,33 @@
1
+ loaders:
2
+ - ignore_when_discovered:
3
+ - __init__
4
+ modules:
5
+ - haystack_integrations.components.connectors.github.file_editor
6
+ - haystack_integrations.components.connectors.github.issue_commenter
7
+ - haystack_integrations.components.connectors.github.issue_viewer
8
+ - haystack_integrations.components.connectors.github.pr_creator
9
+ - haystack_integrations.components.connectors.github.repo_viewer
10
+ - haystack_integrations.components.connectors.github.repo_forker
11
+ search_path:
12
+ - ../src
13
+ type: haystack_pydoc_tools.loaders.CustomPythonLoader
14
+ processors:
15
+ - do_not_filter_modules: false
16
+ documented_only: true
17
+ expression: null
18
+ skip_empty_modules: true
19
+ type: filter
20
+ - type: smart
21
+ - type: crossref
22
+ renderer:
23
+ description: GitHub integration for Haystack
24
+ id: integrations-github
25
+ markdown:
26
+ add_member_class_prefix: false
27
+ add_method_class_prefix: true
28
+ classdef_code_block: false
29
+ descriptive_class_title: false
30
+ descriptive_module_title: true
31
+ filename: github.md
32
+ title: GitHub
33
+ type: haystack_pydoc_tools.renderers.DocusaurusRenderer
@@ -7,7 +7,7 @@ name = "github-haystack"
7
7
  dynamic = ["version"]
8
8
  description = 'Haystack components for interacting with GitHub repositories'
9
9
  readme = "README.md"
10
- requires-python = ">=3.9"
10
+ requires-python = ">=3.10"
11
11
  license = "Apache-2.0"
12
12
  keywords = []
13
13
  authors = [{ name = "deepset GmbH", email = "info@deepset.ai" }]
@@ -15,7 +15,6 @@ classifiers = [
15
15
  "License :: OSI Approved :: Apache Software License",
16
16
  "Development Status :: 4 - Beta",
17
17
  "Programming Language :: Python",
18
- "Programming Language :: Python :: 3.9",
19
18
  "Programming Language :: Python :: 3.10",
20
19
  "Programming Language :: Python :: 3.11",
21
20
  "Programming Language :: Python :: 3.12",
@@ -23,7 +22,7 @@ classifiers = [
23
22
  "Programming Language :: Python :: Implementation :: CPython",
24
23
  "Programming Language :: Python :: Implementation :: PyPy",
25
24
  ]
26
- dependencies = ["haystack-ai>=2.12.0"]
25
+ dependencies = ["haystack-ai>=2.22.0"]
27
26
 
28
27
  [project.urls]
29
28
  Source = "https://github.com/deepset-ai/haystack-core-integrations/github"
@@ -46,8 +45,8 @@ installer = "uv"
46
45
  dependencies = ["haystack-pydoc-tools", "ruff"]
47
46
 
48
47
  [tool.hatch.envs.default.scripts]
49
- docs = ["pydoc-markdown pydoc/config.yml"]
50
- fmt = "ruff check --fix {args} && ruff format {args}"
48
+ docs = ["pydoc-markdown pydoc/config_docusaurus.yml"]
49
+ fmt = "ruff check --fix {args}; ruff format {args}"
51
50
  fmt-check = "ruff check {args} && ruff format --check {args}"
52
51
 
53
52
  [tool.hatch.envs.test]
@@ -64,7 +63,7 @@ dependencies = [
64
63
  unit = 'pytest -m "not integration" {args:tests}'
65
64
  integration = 'pytest -m "integration" {args:tests}'
66
65
  all = 'pytest {args:tests}'
67
- cov-retry = 'all --cov=haystack_integrations --reruns 3 --reruns-delay 30 -x'
66
+ cov-retry = 'pytest --cov=haystack_integrations --reruns 3 --reruns-delay 30 -x {args:tests}'
68
67
 
69
68
  types = """mypy -p haystack_integrations.components.connectors.github \
70
69
  -p haystack_integrations.prompts.github \
@@ -77,7 +76,6 @@ check_untyped_defs = true
77
76
  disallow_incomplete_defs = true
78
77
 
79
78
  [tool.ruff]
80
- target-version = "py38"
81
79
  line-length = 120
82
80
 
83
81
  [tool.ruff.lint]
@@ -124,10 +122,6 @@ ignore = [
124
122
  "B008",
125
123
  "S101",
126
124
  ]
127
- unfixable = [
128
- # Don't touch unused imports
129
- "F401",
130
- ]
131
125
 
132
126
  [tool.ruff.lint.isort]
133
127
  known-first-party = ["haystack_integrations"]
@@ -3,7 +3,7 @@
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
  from base64 import b64decode, b64encode
5
5
  from enum import Enum
6
- from typing import Any, Dict, Optional, Union
6
+ from typing import Any
7
7
 
8
8
  import requests
9
9
  from haystack import component, default_from_dict, default_to_dict, logging
@@ -79,7 +79,7 @@ class GitHubFileEditor:
79
79
  self,
80
80
  *,
81
81
  github_token: Secret = Secret.from_env_var("GITHUB_TOKEN"),
82
- repo: Optional[str] = None,
82
+ repo: str | None = None,
83
83
  branch: str = "main",
84
84
  raise_on_failure: bool = True,
85
85
  ):
@@ -115,7 +115,9 @@ class GitHubFileEditor:
115
115
  """
116
116
  headers = self.base_headers.copy()
117
117
  if self.github_token is not None:
118
- headers["Authorization"] = f"Bearer {self.github_token.resolve_value()}"
118
+ token_value = self.github_token.resolve_value()
119
+ if token_value:
120
+ headers["Authorization"] = f"Bearer {token_value}"
119
121
  return headers
120
122
 
121
123
  def _get_file_content(self, owner: str, repo: str, path: str, branch: str) -> tuple[str, str]:
@@ -143,7 +145,7 @@ class GitHubFileEditor:
143
145
  def _check_last_commit(self, owner: str, repo: str, branch: str) -> bool:
144
146
  """Check if last commit was made by the current token user."""
145
147
  url = f"https://api.github.com/repos/{owner}/{repo}/commits"
146
- params: Dict[str, Union[str, int]] = {"per_page": 1, "sha": branch}
148
+ params: dict[str, str | int] = {"per_page": 1, "sha": branch}
147
149
  response = requests.get(url, headers=self._get_request_headers(), params=params, timeout=10)
148
150
  response.raise_for_status()
149
151
  last_commit = response.json()[0]
@@ -156,7 +158,7 @@ class GitHubFileEditor:
156
158
 
157
159
  return commit_author == current_user
158
160
 
159
- def _edit_file(self, owner: str, repo: str, payload: Dict[str, str], branch: str) -> str:
161
+ def _edit_file(self, owner: str, repo: str, payload: dict[str, str], branch: str) -> str:
160
162
  """Handle file editing."""
161
163
  try:
162
164
  content, sha = self._get_file_content(owner, repo, payload["path"], branch)
@@ -178,7 +180,7 @@ class GitHubFileEditor:
178
180
  raise
179
181
  return f"Error: {e!s}"
180
182
 
181
- def _undo_changes(self, owner: str, repo: str, payload: Dict[str, Any], branch: str) -> str:
183
+ def _undo_changes(self, owner: str, repo: str, payload: dict[str, Any], branch: str) -> str:
182
184
  """Handle undoing changes."""
183
185
  try:
184
186
  if not self._check_last_commit(owner, repo, branch):
@@ -189,7 +191,7 @@ class GitHubFileEditor:
189
191
  commits_url = f"https://api.github.com/repos/{owner}/{repo}/commits"
190
192
 
191
193
  # Get the previous commit SHA
192
- params: Dict[str, Union[str, int]] = {"per_page": 2, "sha": branch}
194
+ params: dict[str, str | int] = {"per_page": 2, "sha": branch}
193
195
  commits = requests.get(commits_url, headers=self._get_request_headers(), params=params, timeout=10).json()
194
196
  previous_sha = commits[1]["sha"]
195
197
 
@@ -205,7 +207,7 @@ class GitHubFileEditor:
205
207
  raise
206
208
  return f"Error: {e!s}"
207
209
 
208
- def _create_file(self, owner: str, repo: str, payload: Dict[str, str], branch: str) -> str:
210
+ def _create_file(self, owner: str, repo: str, payload: dict[str, str], branch: str) -> str:
209
211
  """Handle file creation."""
210
212
  try:
211
213
  url = f"https://api.github.com/repos/{owner}/{repo}/contents/{payload['path']}"
@@ -222,7 +224,7 @@ class GitHubFileEditor:
222
224
  raise
223
225
  return f"Error: {e!s}"
224
226
 
225
- def _delete_file(self, owner: str, repo: str, payload: Dict[str, str], branch: str) -> str:
227
+ def _delete_file(self, owner: str, repo: str, payload: dict[str, str], branch: str) -> str:
226
228
  """Handle file deletion."""
227
229
  try:
228
230
  _, sha = self._get_file_content(owner, repo, payload["path"], branch)
@@ -242,11 +244,11 @@ class GitHubFileEditor:
242
244
  @component.output_types(result=str)
243
245
  def run(
244
246
  self,
245
- command: Union[Command, str],
246
- payload: Dict[str, Any],
247
- repo: Optional[str] = None,
248
- branch: Optional[str] = None,
249
- ) -> Dict[str, str]:
247
+ command: Command | str,
248
+ payload: dict[str, Any],
249
+ repo: str | None = None,
250
+ branch: str | None = None,
251
+ ) -> dict[str, str]:
250
252
  """
251
253
  Process GitHub file operations.
252
254
 
@@ -285,7 +287,7 @@ class GitHubFileEditor:
285
287
  result = command_handlers[command](owner, repo_name, payload, working_branch)
286
288
  return {"result": result}
287
289
 
288
- def to_dict(self) -> Dict[str, Any]:
290
+ def to_dict(self) -> dict[str, Any]:
289
291
  """Serialize the component to a dictionary."""
290
292
  return default_to_dict(
291
293
  self,
@@ -296,7 +298,7 @@ class GitHubFileEditor:
296
298
  )
297
299
 
298
300
  @classmethod
299
- def from_dict(cls, data: Dict[str, Any]) -> "GitHubFileEditor":
301
+ def from_dict(cls, data: dict[str, Any]) -> "GitHubFileEditor":
300
302
  """Deserialize the component from a dictionary."""
301
303
  init_params = data["init_parameters"]
302
304
  deserialize_secrets_inplace(init_params, keys=["github_token"])
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
  import re
5
- from typing import Any, Dict
5
+ from typing import Any
6
6
 
7
7
  import requests
8
8
  from haystack import component, default_from_dict, default_to_dict, logging
@@ -67,7 +67,9 @@ class GitHubIssueCommenter:
67
67
  """
68
68
  headers = self.base_headers.copy()
69
69
  if self.github_token is not None:
70
- headers["Authorization"] = f"Bearer {self.github_token.resolve_value()}"
70
+ token_value = self.github_token.resolve_value()
71
+ if token_value:
72
+ headers["Authorization"] = f"Bearer {token_value}"
71
73
  return headers
72
74
 
73
75
  def _parse_github_url(self, url: str) -> tuple[str, str, int]:
@@ -113,7 +115,7 @@ class GitHubIssueCommenter:
113
115
 
114
116
  return False
115
117
 
116
- def to_dict(self) -> Dict[str, Any]:
118
+ def to_dict(self) -> dict[str, Any]:
117
119
  """
118
120
  Serialize the component to a dictionary.
119
121
 
@@ -127,7 +129,7 @@ class GitHubIssueCommenter:
127
129
  )
128
130
 
129
131
  @classmethod
130
- def from_dict(cls, data: Dict[str, Any]) -> "GitHubIssueCommenter":
132
+ def from_dict(cls, data: dict[str, Any]) -> "GitHubIssueCommenter":
131
133
  """
132
134
  Deserialize the component from a dictionary.
133
135
 
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
  import re
5
- from typing import Any, Dict, List, Optional
5
+ from typing import Any
6
6
 
7
7
  import requests
8
8
  from haystack import Document, component, default_from_dict, default_to_dict, logging
@@ -37,7 +37,7 @@ class GitHubIssueViewer:
37
37
  def __init__(
38
38
  self,
39
39
  *,
40
- github_token: Optional[Secret] = None,
40
+ github_token: Secret | None = None,
41
41
  raise_on_failure: bool = True,
42
42
  retry_attempts: int = 2,
43
43
  ):
@@ -66,7 +66,9 @@ class GitHubIssueViewer:
66
66
  """
67
67
  headers = self.base_headers.copy()
68
68
  if self.github_token:
69
- headers["Authorization"] = f"Bearer {self.github_token.resolve_value()}"
69
+ token_value = self.github_token.resolve_value()
70
+ if token_value:
71
+ headers["Authorization"] = f"Bearer {token_value}"
70
72
  return headers
71
73
 
72
74
  def _parse_github_url(self, url: str) -> tuple[str, str, int]:
@@ -152,7 +154,7 @@ class GitHubIssueViewer:
152
154
  },
153
155
  )
154
156
 
155
- def to_dict(self) -> Dict[str, Any]:
157
+ def to_dict(self) -> dict[str, Any]:
156
158
  """
157
159
  Serialize the component to a dictionary.
158
160
 
@@ -166,7 +168,7 @@ class GitHubIssueViewer:
166
168
  )
167
169
 
168
170
  @classmethod
169
- def from_dict(cls, data: Dict[str, Any]) -> "GitHubIssueViewer":
171
+ def from_dict(cls, data: dict[str, Any]) -> "GitHubIssueViewer":
170
172
  """
171
173
  Deserialize the component from a dictionary.
172
174
 
@@ -177,7 +179,7 @@ class GitHubIssueViewer:
177
179
  deserialize_secrets_inplace(init_params, keys=["github_token"])
178
180
  return default_from_dict(cls, data)
179
181
 
180
- @component.output_types(documents=List[Document])
182
+ @component.output_types(documents=list[Document])
181
183
  def run(self, url: str) -> dict:
182
184
  """
183
185
  Process a GitHub issue URL and return documents.
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
  import re
5
- from typing import Any, Dict, Optional
5
+ from typing import Any
6
6
 
7
7
  import requests
8
8
  from haystack import component, default_from_dict, default_to_dict, logging
@@ -65,7 +65,9 @@ class GitHubPRCreator:
65
65
  """
66
66
  headers = self.base_headers.copy()
67
67
  if self.github_token is not None:
68
- headers["Authorization"] = f"Bearer {self.github_token.resolve_value()}"
68
+ token_value = self.github_token.resolve_value()
69
+ if token_value:
70
+ headers["Authorization"] = f"Bearer {token_value}"
69
71
  return headers
70
72
 
71
73
  def _parse_issue_url(self, issue_url: str) -> tuple[str, str, str]:
@@ -100,7 +102,7 @@ class GitHubPRCreator:
100
102
  except requests.RequestException:
101
103
  return False
102
104
 
103
- def _create_fork(self, owner: str, repo: str) -> Optional[str]:
105
+ def _create_fork(self, owner: str, repo: str) -> str | None:
104
106
  """Create a fork of the repository."""
105
107
  url = f"https://api.github.com/repos/{owner}/{repo}/forks"
106
108
  try:
@@ -213,7 +215,7 @@ class GitHubPRCreator:
213
215
  @component.output_types(result=str)
214
216
  def run(
215
217
  self, issue_url: str, title: str, branch: str, base: str, body: str = "", draft: bool = False
216
- ) -> Dict[str, str]:
218
+ ) -> dict[str, str]:
217
219
  """
218
220
  Create a new pull request from your fork to the original repository, linked to the specified issue.
219
221
 
@@ -261,7 +263,7 @@ class GitHubPRCreator:
261
263
  raise
262
264
  return {"result": f"Error: {e!s}"}
263
265
 
264
- def to_dict(self) -> Dict[str, Any]:
266
+ def to_dict(self) -> dict[str, Any]:
265
267
  """Serialize the component to a dictionary."""
266
268
  return default_to_dict(
267
269
  self,
@@ -270,7 +272,7 @@ class GitHubPRCreator:
270
272
  )
271
273
 
272
274
  @classmethod
273
- def from_dict(cls, data: Dict[str, Any]) -> "GitHubPRCreator":
275
+ def from_dict(cls, data: dict[str, Any]) -> "GitHubPRCreator":
274
276
  """Deserialize the component from a dictionary."""
275
277
  init_params = data["init_parameters"]
276
278
  deserialize_secrets_inplace(init_params, keys=["github_token"])
@@ -3,7 +3,7 @@
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
  import re
5
5
  import time
6
- from typing import Any, Dict, Optional
6
+ from typing import Any
7
7
 
8
8
  import requests
9
9
  from haystack import component, default_from_dict, default_to_dict, logging
@@ -85,7 +85,9 @@ class GitHubRepoForker:
85
85
  """
86
86
  headers = self.base_headers.copy()
87
87
  if self.github_token is not None:
88
- headers["Authorization"] = f"Bearer {self.github_token.resolve_value()}"
88
+ token_value = self.github_token.resolve_value()
89
+ if token_value:
90
+ headers["Authorization"] = f"Bearer {token_value}"
89
91
  return headers
90
92
 
91
93
  def _parse_github_url(self, url: str) -> tuple[str, str, str]:
@@ -135,7 +137,7 @@ class GitHubRepoForker:
135
137
  response.raise_for_status()
136
138
  return response.json()["login"]
137
139
 
138
- def _get_existing_repository(self, repo_name: str) -> Optional[str]:
140
+ def _get_existing_repository(self, repo_name: str) -> str | None:
139
141
  """
140
142
  Check if a repository with the given name already exists in the authenticated user's account.
141
143
 
@@ -219,7 +221,7 @@ class GitHubRepoForker:
219
221
  fork_data = response.json()
220
222
  return f"{fork_data['owner']['login']}/{fork_data['name']}"
221
223
 
222
- def to_dict(self) -> Dict[str, Any]:
224
+ def to_dict(self) -> dict[str, Any]:
223
225
  """
224
226
  Serialize the component to a dictionary.
225
227
 
@@ -237,7 +239,7 @@ class GitHubRepoForker:
237
239
  )
238
240
 
239
241
  @classmethod
240
- def from_dict(cls, data: Dict[str, Any]) -> "GitHubRepoForker":
242
+ def from_dict(cls, data: dict[str, Any]) -> "GitHubRepoForker":
241
243
  """
242
244
  Deserialize the component from a dictionary.
243
245
 
@@ -3,7 +3,7 @@
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
  import base64
5
5
  from dataclasses import dataclass
6
- from typing import Any, Dict, List, Optional
6
+ from typing import Any
7
7
 
8
8
  import requests
9
9
  from haystack import Document, component, default_from_dict, default_to_dict, logging
@@ -21,7 +21,7 @@ class GitHubItem:
21
21
  path: str
22
22
  size: int
23
23
  url: str
24
- content: Optional[str] = None
24
+ content: str | None = None
25
25
 
26
26
 
27
27
  @component
@@ -71,10 +71,10 @@ class GitHubRepoViewer:
71
71
  def __init__(
72
72
  self,
73
73
  *,
74
- github_token: Optional[Secret] = None,
74
+ github_token: Secret | None = None,
75
75
  raise_on_failure: bool = True,
76
76
  max_file_size: int = 1_000_000, # 1MB default limit
77
- repo: Optional[str] = None,
77
+ repo: str | None = None,
78
78
  branch: str = "main",
79
79
  ):
80
80
  """
@@ -109,10 +109,12 @@ class GitHubRepoViewer:
109
109
  """
110
110
  headers = self.base_headers.copy()
111
111
  if self.github_token is not None:
112
- headers["Authorization"] = f"Bearer {self.github_token.resolve_value()}"
112
+ token_value = self.github_token.resolve_value()
113
+ if token_value:
114
+ headers["Authorization"] = f"Bearer {token_value}"
113
115
  return headers
114
116
 
115
- def to_dict(self) -> Dict[str, Any]:
117
+ def to_dict(self) -> dict[str, Any]:
116
118
  """
117
119
  Serialize the component to a dictionary.
118
120
 
@@ -128,7 +130,7 @@ class GitHubRepoViewer:
128
130
  )
129
131
 
130
132
  @classmethod
131
- def from_dict(cls, data: Dict[str, Any]) -> "GitHubRepoViewer":
133
+ def from_dict(cls, data: dict[str, Any]) -> "GitHubRepoViewer":
132
134
  """
133
135
  Deserialize the component from a dictionary.
134
136
 
@@ -179,7 +181,7 @@ class GitHubRepoViewer:
179
181
  },
180
182
  )
181
183
 
182
- def _create_directory_documents(self, items: List[GitHubItem]) -> List[Document]:
184
+ def _create_directory_documents(self, items: list[GitHubItem]) -> list[Document]:
183
185
  """Create a list of Documents from directory contents"""
184
186
  return [
185
187
  Document(
@@ -204,8 +206,8 @@ class GitHubRepoViewer:
204
206
  },
205
207
  )
206
208
 
207
- @component.output_types(documents=List[Document])
208
- def run(self, path: str, repo: Optional[str] = None, branch: Optional[str] = None) -> Dict[str, List[Document]]:
209
+ @component.output_types(documents=list[Document])
210
+ def run(self, path: str, repo: str | None = None, branch: str | None = None) -> dict[str, list[Document]]:
209
211
  """
210
212
  Process a GitHub repository path and return documents.
211
213
 
@@ -1,7 +1,8 @@
1
1
  # SPDX-FileCopyrightText: 2023-present deepset GmbH <info@deepset.ai>
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
- from typing import Any, Callable, Dict, Optional, Union
4
+ from collections.abc import Callable
5
+ from typing import Any
5
6
 
6
7
  from haystack.core.serialization import generate_qualified_class_name
7
8
  from haystack.tools import ComponentTool
@@ -20,16 +21,16 @@ class GitHubFileEditorTool(ComponentTool):
20
21
  def __init__(
21
22
  self,
22
23
  *,
23
- name: Optional[str] = "file_editor",
24
- description: Optional[str] = FILE_EDITOR_PROMPT,
25
- parameters: Optional[Dict[str, Any]] = FILE_EDITOR_SCHEMA,
24
+ name: str | None = "file_editor",
25
+ description: str | None = FILE_EDITOR_PROMPT,
26
+ parameters: dict[str, Any] | None = FILE_EDITOR_SCHEMA,
26
27
  github_token: Secret = Secret.from_env_var("GITHUB_TOKEN"),
27
- repo: Optional[str] = None,
28
+ repo: str | None = None,
28
29
  branch: str = "main",
29
30
  raise_on_failure: bool = True,
30
- outputs_to_string: Optional[Dict[str, Union[str, Callable[[Any], str]]]] = None,
31
- inputs_from_state: Optional[Dict[str, str]] = None,
32
- outputs_to_state: Optional[Dict[str, Dict[str, Union[str, Callable]]]] = None,
31
+ outputs_to_string: dict[str, str | Callable[[Any], str]] | None = None,
32
+ inputs_from_state: dict[str, str] | None = None,
33
+ outputs_to_state: dict[str, dict[str, str | Callable]] | None = None,
33
34
  ):
34
35
  """
35
36
  Initialize the GitHub file editor tool.
@@ -86,7 +87,7 @@ class GitHubFileEditorTool(ComponentTool):
86
87
  outputs_to_state=outputs_to_state,
87
88
  )
88
89
 
89
- def to_dict(self) -> Dict[str, Any]:
90
+ def to_dict(self) -> dict[str, Any]:
90
91
  """
91
92
  Serializes the tool to a dictionary.
92
93
 
@@ -110,7 +111,7 @@ class GitHubFileEditorTool(ComponentTool):
110
111
  return {"type": generate_qualified_class_name(type(self)), "data": serialized}
111
112
 
112
113
  @classmethod
113
- def from_dict(cls, data: Dict[str, Any]) -> "GitHubFileEditorTool":
114
+ def from_dict(cls, data: dict[str, Any]) -> "GitHubFileEditorTool":
114
115
  """
115
116
  Deserializes the tool from a dictionary.
116
117
 
@@ -1,7 +1,8 @@
1
1
  # SPDX-FileCopyrightText: 2023-present deepset GmbH <info@deepset.ai>
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
- from typing import Any, Callable, Dict, Optional, Union
4
+ from collections.abc import Callable
5
+ from typing import Any
5
6
 
6
7
  from haystack.core.serialization import generate_qualified_class_name
7
8
  from haystack.tools import ComponentTool
@@ -20,15 +21,15 @@ class GitHubIssueCommenterTool(ComponentTool):
20
21
  def __init__(
21
22
  self,
22
23
  *,
23
- name: Optional[str] = "issue_commenter",
24
- description: Optional[str] = ISSUE_COMMENTER_PROMPT,
25
- parameters: Optional[Dict[str, Any]] = ISSUE_COMMENTER_SCHEMA,
24
+ name: str | None = "issue_commenter",
25
+ description: str | None = ISSUE_COMMENTER_PROMPT,
26
+ parameters: dict[str, Any] | None = ISSUE_COMMENTER_SCHEMA,
26
27
  github_token: Secret = Secret.from_env_var("GITHUB_TOKEN"),
27
28
  raise_on_failure: bool = True,
28
29
  retry_attempts: int = 2,
29
- outputs_to_string: Optional[Dict[str, Union[str, Callable[[Any], str]]]] = None,
30
- inputs_from_state: Optional[Dict[str, str]] = None,
31
- outputs_to_state: Optional[Dict[str, Dict[str, Union[str, Callable]]]] = None,
30
+ outputs_to_string: dict[str, str | Callable[[Any], str]] | None = None,
31
+ inputs_from_state: dict[str, str] | None = None,
32
+ outputs_to_state: dict[str, dict[str, str | Callable]] | None = None,
32
33
  ):
33
34
  """
34
35
  Initialize the GitHub issue commenter tool.
@@ -82,7 +83,7 @@ class GitHubIssueCommenterTool(ComponentTool):
82
83
  outputs_to_state=outputs_to_state,
83
84
  )
84
85
 
85
- def to_dict(self) -> Dict[str, Any]:
86
+ def to_dict(self) -> dict[str, Any]:
86
87
  """
87
88
  Serializes the tool to a dictionary.
88
89
 
@@ -105,7 +106,7 @@ class GitHubIssueCommenterTool(ComponentTool):
105
106
  return {"type": generate_qualified_class_name(type(self)), "data": serialized}
106
107
 
107
108
  @classmethod
108
- def from_dict(cls, data: Dict[str, Any]) -> "GitHubIssueCommenterTool":
109
+ def from_dict(cls, data: dict[str, Any]) -> "GitHubIssueCommenterTool":
109
110
  """
110
111
  Deserializes the tool from a dictionary.
111
112