swegen 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,266 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+
6
+ from harbor.models.task.config import (
7
+ AgentConfig,
8
+ EnvironmentConfig,
9
+ TaskConfig,
10
+ VerifierConfig,
11
+ )
12
+
13
+ from .utils import strip_tests_prefix
14
+
15
+
16
+ @dataclass
17
+ class SkeletonParams:
18
+ """Parameters for skeleton generation (all deterministic from git)."""
19
+
20
+ repo_url: str
21
+ head_sha: str
22
+ base_sha: str
23
+ pr_number: int
24
+
25
+
26
+ def generate_dockerfile(params: SkeletonParams) -> str:
27
+ """
28
+ Generate a minimal, language-agnostic Dockerfile skeleton.
29
+
30
+ The skeleton contains:
31
+ - Deterministic parts filled in (git clone, SHAs, bug.patch application)
32
+ - TODO comments for Claude Code to fill in (runtime, deps, build)
33
+
34
+ Claude Code will analyze the repo and fill in:
35
+ - Language runtime installation
36
+ - Package manager setup
37
+ - Dependency installation
38
+ - Build steps (if needed)
39
+ - Post-patch rebuild (if needed)
40
+
41
+ Git clone strategy:
42
+ - Simple + robust: clone, then fetch the exact commit SHA.
43
+ - NOTE: `head_sha` currently comes from the PR's HEAD branch tip (GitHub API).
44
+ - If the PR was squash-merged/rebased, that commit may not be on any normal branch.
45
+ - In that case, fetching `refs/pull/<n>/head` is a robust fallback without fetching ALL PR refs.
46
+ """
47
+ return f"""FROM ubuntu:24.04
48
+
49
+ # Base system packages (common to all languages)
50
+ RUN apt-get update && apt-get install -y \\
51
+ git \\
52
+ curl \\
53
+ ca-certificates \\
54
+ patch \\
55
+ build-essential \\
56
+ && rm -rf /var/lib/apt/lists/*
57
+
58
+ # TODO: Install language runtime
59
+ # Analyze the repo to determine what's needed. Examples:
60
+ # Python: apt-get install python3 python3-pip python3-venv python3-dev
61
+ # Node.js: curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && apt-get install -y nodejs
62
+ # Go: Download from golang.org/dl or use apt
63
+ # Rust: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
64
+ # Ruby: apt-get install ruby ruby-dev
65
+ # Java: apt-get install openjdk-17-jdk
66
+ # Check .nvmrc, .python-version, .ruby-version, go.mod, rust-toolchain.toml, etc.
67
+
68
+ # TODO: Install additional system packages if needed
69
+ # Check CI config (.github/workflows/*.yml) for hints about required packages
70
+ # Examples: python3-dev, libssl-dev, pkg-config, cmake, etc.
71
+
72
+ # TODO: Set up package manager if needed
73
+ # For Python: PREFER uv (much faster than pip)
74
+ # curl -LsSf https://astral.sh/uv/install.sh | sh && mv /root/.local/bin/uv /usr/local/bin/uv
75
+ # For Node.js: corepack enable (for yarn/pnpm) or npm is built-in
76
+ # For Ruby: gem install bundler
77
+
78
+ WORKDIR /app
79
+
80
+ # Clone repo at HEAD commit (with fix applied)
81
+ RUN git clone {params.repo_url} src && \\
82
+ cd src && \\
83
+ (git fetch --depth 1 origin {params.head_sha} || git fetch --depth 1 origin "+refs/pull/{params.pr_number}/head:refs/remotes/origin/pr/{params.pr_number}") && \\
84
+ git checkout --detach FETCH_HEAD && \\
85
+ git submodule update --init --recursive
86
+
87
+ WORKDIR /app/src
88
+
89
+ # TODO: Set environment variables if needed
90
+ # Check CI config and README for required env vars
91
+ # Examples: CI=true, NODE_ENV=test, CARGO_TERM_COLOR=never
92
+
93
+ # TODO: Install dependencies
94
+ # For Python: PREFER uv (much faster). Create venv and install:
95
+ # uv venv /opt/venv
96
+ # uv pip install --python /opt/venv/bin/python -e ".[dev,test]"
97
+ # # Or: uv pip install --python /opt/venv/bin/python -r requirements.txt
98
+ # # Then add to PATH: ENV PATH="/opt/venv/bin:${{PATH}}"
99
+ # For Node.js: npm ci, yarn install --frozen-lockfile, pnpm install --frozen-lockfile
100
+ # For Go: go mod download
101
+ # For Rust: cargo fetch
102
+ # For Ruby: bundle install
103
+ # For Java: mvn dependency:resolve or gradle dependencies
104
+
105
+ # TODO: Build if needed (check if it's a compiled language or has build step)
106
+ # Examples:
107
+ # TypeScript: npm run build, tsc
108
+ # Rust: cargo build
109
+ # Go: go build ./...
110
+ # Java: mvn compile, gradle build
111
+
112
+ # If install/build steps touched tracked files, reset them so bug.patch applies cleanly,
113
+ RUN git reset --hard
114
+
115
+ # Apply bug.patch to revert to buggy state (BASE)
116
+ COPY bug.patch /tmp/bug.patch
117
+ RUN patch -p1 < /tmp/bug.patch && rm /tmp/bug.patch
118
+
119
+ # TODO: Rebuild after applying bug.patch if needed
120
+ # For compiled languages (TypeScript, Rust, Go, Java), you MUST rebuild after patching
121
+
122
+ RUN rm -rf /app/src/.git
123
+
124
+ WORKDIR /app/src
125
+ """
126
+
127
+
128
+ def generate_test_sh(
129
+ test_files: list[str],
130
+ ) -> str:
131
+ """
132
+ Generate a minimal, language-agnostic test.sh skeleton.
133
+
134
+ The skeleton contains:
135
+ - Test file copy commands (deterministic)
136
+ - TODO for Claude Code to fill in the actual test command
137
+
138
+ Claude Code will analyze the repo and fill in:
139
+ - Test framework detection
140
+ - Correct test command with specific file paths
141
+ """
142
+ # Build copy commands for test files
143
+ if test_files:
144
+ copy_lines = []
145
+ for tf in test_files:
146
+ # Handle common test directory prefixes
147
+ source_path = strip_tests_prefix(tf)
148
+ target_dir = str(Path(tf).parent)
149
+ copy_lines.append(f'mkdir -p "{target_dir}"')
150
+ copy_lines.append(f'cp "/tests/{source_path}" "{tf}"')
151
+ copy_commands = "\n".join(copy_lines)
152
+
153
+ # Build example test file list for comments
154
+ test_files_example = " ".join([f'"{tf}"' for tf in test_files[:5]])
155
+ if len(test_files) > 5:
156
+ test_files_example += f" # ... and {len(test_files) - 5} more"
157
+ else:
158
+ copy_commands = "# No test files to copy"
159
+ test_files_example = ""
160
+
161
+ return f"""#!/bin/bash
162
+
163
+ cd /app/src
164
+
165
+ # TODO: Set environment variables if needed for tests
166
+ # Examples: CI=true, NODE_ENV=test, RUST_BACKTRACE=1
167
+
168
+ # Copy HEAD test files from /tests (overwrites BASE state)
169
+ {copy_commands}
170
+
171
+ # CRITICAL: Run ONLY the specific test files from the PR, NOT the entire test suite!
172
+ # The test files to run are: {test_files_example if test_files_example else "(see list above)"}
173
+ #
174
+ # TODO: Fill in the actual test command to run ONLY these specific files
175
+ #
176
+ # DO NOT run the entire test suite - it's too slow and may have unrelated failures!
177
+ #
178
+ # Examples for different languages/frameworks:
179
+ #
180
+ # Python (pytest with uv):
181
+ # # If using uv venv at /opt/venv:
182
+ # source /opt/venv/bin/activate
183
+ # uv pip install -e . --no-deps 2>/dev/null || true # Reinstall to pick up changes
184
+ # pytest -xvs path/to/test_file.py
185
+ # # Or without venv activation:
186
+ # /opt/venv/bin/pytest -xvs path/to/test_file.py
187
+ #
188
+ # JavaScript/TypeScript (IMPORTANT: disable coverage thresholds when running subset!):
189
+ # npx jest path/to/test.js path/to/test2.js --coverage=false
190
+ # npx vitest run path/to/test.ts --coverage.enabled=false
191
+ # npx mocha path/to/test.js path/to/test2.js
192
+ # npx borp path/to/test.js --no-check-coverage # Used by fastify, pino, etc.
193
+ # npx tap path/to/test.js --no-check-coverage # Node TAP framework
194
+ # npx ava path/to/test.js # AVA framework
195
+ #
196
+ # CRITICAL for JS/TS: DO NOT use "npm test" or "npm run test" without args!
197
+ # These run the ENTIRE suite. Pass specific files via the test runner directly.
198
+ # If you must use npm: npm run test -- path/to/test.js (note the -- separator)
199
+ #
200
+ # Go:
201
+ # go test -v ./path/to/package/...
202
+ # go test -v -run TestSpecificName ./...
203
+ #
204
+ # Rust:
205
+ # cargo test --test test_name -- --nocapture
206
+ # cargo test specific_test_name -- --nocapture
207
+ #
208
+ # Ruby (RSpec/Minitest):
209
+ # bundle exec rspec path/to/spec.rb
210
+ # bundle exec ruby -Itest path/to/test.rb
211
+ #
212
+ # Java (JUnit/Maven/Gradle):
213
+ # mvn test -Dtest=TestClassName
214
+ # gradle test --tests TestClassName
215
+
216
+ # TODO: Replace this placeholder with actual test command running ONLY the specific test files above
217
+ echo "ERROR: Test command not filled in! Must run specific test files, not entire suite." >&2
218
+ false
219
+ test_status=$?
220
+
221
+ if [ $test_status -eq 0 ]; then
222
+ echo 1 > /logs/verifier/reward.txt
223
+ else
224
+ echo 0 > /logs/verifier/reward.txt
225
+ fi
226
+ exit "$test_status"
227
+ """
228
+
229
+
230
+ def generate_solve_sh() -> str:
231
+ """Generate solution/solve.sh script (same for all tasks)."""
232
+ return """#!/bin/bash
233
+
234
+ set -euo pipefail
235
+ cd /app/src
236
+
237
+ patch -p1 < /solution/fix.patch
238
+ """
239
+
240
+
241
+ def generate_instruction_md(instruction_data: dict) -> str:
242
+ """Generate instruction.md file for Harbor format."""
243
+ return instruction_data["instruction"]
244
+
245
+
246
+ def generate_task_toml(instruction_data: dict) -> str:
247
+ """Generate task.toml config file for Harbor format.
248
+
249
+ Uses Harbor's TaskConfig for proper serialization and validation.
250
+ """
251
+ config = TaskConfig(
252
+ metadata={
253
+ "difficulty": instruction_data.get("difficulty", "medium"),
254
+ "category": instruction_data.get("category", "bugfix"),
255
+ "tags": instruction_data.get("tags", []),
256
+ },
257
+ verifier=VerifierConfig(timeout_sec=600.0),
258
+ agent=AgentConfig(timeout_sec=600.0),
259
+ environment=EnvironmentConfig(
260
+ build_timeout_sec=600.0,
261
+ cpus=1,
262
+ memory_mb=2048,
263
+ storage_mb=10240,
264
+ ),
265
+ )
266
+ return config.model_dump_toml()
swegen/create/utils.py ADDED
@@ -0,0 +1,350 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from pydantic import BaseModel, Field
6
+
7
+
8
+ class CombinedPRTaskEvaluation(BaseModel):
9
+ """Combined evaluation and task generation for a PR.
10
+
11
+ First evaluates if PR is substantial, then generates task details if it is.
12
+ """
13
+
14
+ is_substantial: bool = Field(
15
+ ..., description="Whether the PR is substantial enough to generate a task"
16
+ )
17
+ reason: str = Field(..., description="Brief explanation of why the PR is or isn't substantial")
18
+ instruction: str | None = Field(
19
+ None,
20
+ description="Concise bug report describing problem, reproduction, expected behavior. No bullet lists or verbose sections.",
21
+ )
22
+ difficulty: str = Field("medium", description="Task difficulty: easy, medium, or hard")
23
+ category: str = Field("bugfix", description="Task category, typically 'bugfix' or 'feature'")
24
+ tags: list[str] = Field(
25
+ default_factory=list,
26
+ description="Exactly 3 tags: [language, tier, framework/category]. Example: ['python', 'backend', 'fastapi']",
27
+ )
28
+
29
+
30
+ def strip_tests_prefix(path: str) -> str:
31
+ """Strip leading test directory prefix if present.
32
+
33
+ Handles common patterns across languages:
34
+ - tests/, test/, __tests__/ (Python, JS/TS)
35
+ - spec/ (Ruby)
36
+ - src/test/ (Java/Kotlin)
37
+
38
+ Args:
39
+ path: File path that may start with a test directory prefix
40
+
41
+ Returns:
42
+ Path with test directory prefix removed if present
43
+ """
44
+ p = Path(path)
45
+ parts = p.parts
46
+
47
+ if not parts:
48
+ return path
49
+
50
+ first = parts[0].lower()
51
+
52
+ # Python, JS/TS, Ruby
53
+ if first in ("tests", "test", "__tests__", "spec"):
54
+ return str(Path(*parts[1:]))
55
+
56
+ # Java/Kotlin: src/test/java/... or src/test/kotlin/...
57
+ if len(parts) >= 2 and parts[0].lower() == "src" and parts[1].lower() == "test":
58
+ return str(Path(*parts[2:]))
59
+
60
+ return path
61
+
62
+
63
+ def is_test_file(filename: str) -> bool:
64
+ """Check if a filename represents a test file or test-related resource.
65
+
66
+ Supports all languages: Python, JS/TS, Go, Rust, Ruby, Java, C/C++, PHP, C#.
67
+
68
+ Args:
69
+ filename: File path (repo-relative)
70
+
71
+ Returns:
72
+ True if the file is a test file or test resource (fixtures, data, etc.)
73
+ """
74
+ if not filename:
75
+ return False
76
+
77
+ name_lower = filename.lower()
78
+ base_name = filename.split("/")[-1].lower()
79
+
80
+ # Check if file is under a test directory (common across languages)
81
+ in_test_dir = (
82
+ # Python/generic
83
+ name_lower.startswith("tests/")
84
+ or "/tests/" in name_lower
85
+ or name_lower.startswith("test/")
86
+ or "/test/" in name_lower
87
+ # JS/TS
88
+ or name_lower.startswith("__tests__/")
89
+ or "/__tests__/" in name_lower
90
+ # Ruby
91
+ or name_lower.startswith("spec/")
92
+ or "/spec/" in name_lower
93
+ # Java/Kotlin (Maven/Gradle convention)
94
+ or "/src/test/" in name_lower
95
+ or name_lower.startswith("src/test/")
96
+ )
97
+
98
+ # Python patterns
99
+ is_python_test = (
100
+ base_name.startswith("test_") and name_lower.endswith(".py")
101
+ ) or base_name.endswith("_test.py")
102
+
103
+ # JavaScript/TypeScript patterns
104
+ is_js_ts_test = (
105
+ base_name.endswith(".test.js")
106
+ or base_name.endswith(".test.ts")
107
+ or base_name.endswith(".test.jsx")
108
+ or base_name.endswith(".test.tsx")
109
+ or base_name.endswith(".test.mjs")
110
+ or base_name.endswith(".test.cjs")
111
+ or base_name.endswith(".spec.js")
112
+ or base_name.endswith(".spec.ts")
113
+ or base_name.endswith(".spec.jsx")
114
+ or base_name.endswith(".spec.tsx")
115
+ or base_name.endswith(".spec.mjs")
116
+ or base_name.endswith(".spec.cjs")
117
+ )
118
+
119
+ # Go patterns
120
+ is_go_test = base_name.endswith("_test.go")
121
+
122
+ # Rust patterns
123
+ is_rust_test = base_name.endswith("_test.rs") or base_name == "tests.rs"
124
+
125
+ # Ruby patterns
126
+ is_ruby_test = (
127
+ base_name.endswith("_spec.rb")
128
+ or base_name.endswith("_test.rb")
129
+ or base_name.startswith("test_")
130
+ and name_lower.endswith(".rb")
131
+ )
132
+
133
+ # Java/Kotlin patterns
134
+ is_java_test = (
135
+ base_name.endswith("test.java")
136
+ or base_name.endswith("tests.java")
137
+ or base_name.endswith("test.kt")
138
+ or base_name.endswith("tests.kt")
139
+ or base_name.startswith("test")
140
+ and (name_lower.endswith(".java") or name_lower.endswith(".kt"))
141
+ )
142
+
143
+ # C/C++ patterns
144
+ is_cpp_test = (
145
+ base_name.endswith("_test.cpp")
146
+ or base_name.endswith("_test.cc")
147
+ or base_name.endswith("_test.c")
148
+ or base_name.startswith("test_")
149
+ and name_lower.endswith((".cpp", ".cc", ".c"))
150
+ )
151
+
152
+ # PHP patterns
153
+ is_php_test = (
154
+ base_name.endswith("test.php")
155
+ or base_name.startswith("test")
156
+ and name_lower.endswith(".php")
157
+ )
158
+
159
+ # C# patterns
160
+ is_csharp_test = base_name.endswith("tests.cs") or base_name.endswith("test.cs")
161
+
162
+ return (
163
+ in_test_dir
164
+ or is_python_test
165
+ or is_js_ts_test
166
+ or is_go_test
167
+ or is_rust_test
168
+ or is_ruby_test
169
+ or is_java_test
170
+ or is_cpp_test
171
+ or is_php_test
172
+ or is_csharp_test
173
+ )
174
+
175
+
176
+ def identify_test_files(files: list[dict]) -> list[str]:
177
+ """Identify test files from a list of changed files.
178
+
179
+ Supports all languages: Python, JS/TS, Go, Rust, Ruby, Java, C/C++, PHP, C#.
180
+
181
+ Args:
182
+ files: List of file dicts with 'filename' key (from GitHub API)
183
+
184
+ Returns:
185
+ List of test file paths (repo-relative)
186
+ """
187
+ test_files = []
188
+
189
+ for f in files:
190
+ filename = f.get("filename", "")
191
+ if is_test_file(filename):
192
+ test_files.append(filename)
193
+
194
+ return test_files
195
+
196
+
197
+ def _is_relevant_source(path: str) -> bool:
198
+ """Check if a file path is relevant for the fix (not tests, CI, or build artifacts).
199
+
200
+ NOTE: We include docs, examples, and other non-test files to keep fix.patch
201
+ consistent with bug.patch. This prevents issues where bug.patch reverts docs
202
+ but fix.patch doesn't re-apply them, causing inconsistencies.
203
+
204
+ Supports all languages: Python, JS/TS, Go, Rust, Ruby, Java, C/C++, PHP, C#.
205
+
206
+ Args:
207
+ path: File path to check
208
+
209
+ Returns:
210
+ True if the file should be included in fix.patch
211
+ """
212
+ pl = path.lower()
213
+ base = path.split("/")[-1].lower()
214
+
215
+ # === Common exclusions (all languages) ===
216
+
217
+ # Exclude test directories
218
+ if pl.startswith("tests/") or "/tests/" in pl:
219
+ return False
220
+ if pl.startswith("test/") or "/test/" in pl:
221
+ return False
222
+ if pl.startswith("__tests__/") or "/__tests__/" in pl:
223
+ return False
224
+ if pl.startswith("spec/") or "/spec/" in pl: # Ruby
225
+ return False
226
+ if "/src/test/" in pl or pl.startswith("src/test/"): # Java/Kotlin
227
+ return False
228
+
229
+ # Exclude CI and meta (these shouldn't be in fix.patch)
230
+ if pl.startswith(".github/") or "/.github/" in pl:
231
+ return False
232
+ if pl.startswith(".gitlab/") or "/.gitlab/" in pl:
233
+ return False
234
+ if pl.startswith(".circleci/") or "/.circleci/" in pl:
235
+ return False
236
+
237
+ # Exclude build outputs and dependency directories (should never be in a PR)
238
+ build_dirs = [
239
+ "node_modules/",
240
+ "dist/",
241
+ "build/",
242
+ ".next/",
243
+ "__pycache__/",
244
+ ".tox/",
245
+ ".pytest_cache/",
246
+ "*.egg-info/",
247
+ "target/",
248
+ "vendor/",
249
+ "bin/",
250
+ "obj/",
251
+ "out/",
252
+ ]
253
+ for bd in build_dirs:
254
+ if bd in pl or pl.startswith(bd.rstrip("/")):
255
+ return False
256
+
257
+ # Exclude test files by naming convention (comprehensive, language-agnostic)
258
+
259
+ # Python
260
+ if base.startswith("test_") and base.endswith(".py"):
261
+ return False
262
+ if base.endswith("_test.py"):
263
+ return False
264
+
265
+ # JavaScript/TypeScript
266
+ if base.endswith((".test.js", ".test.ts", ".test.jsx", ".test.tsx", ".test.mjs", ".test.cjs")):
267
+ return False
268
+ if base.endswith((".spec.js", ".spec.ts", ".spec.jsx", ".spec.tsx", ".spec.mjs", ".spec.cjs")):
269
+ return False
270
+
271
+ # Go
272
+ if base.endswith("_test.go"):
273
+ return False
274
+
275
+ # Rust
276
+ if base.endswith("_test.rs") or base == "tests.rs":
277
+ return False
278
+
279
+ # Ruby
280
+ if base.endswith("_spec.rb") or base.endswith("_test.rb"):
281
+ return False
282
+ if base.startswith("test_") and base.endswith(".rb"):
283
+ return False
284
+
285
+ # Java/Kotlin
286
+ if base.endswith(("test.java", "tests.java", "test.kt", "tests.kt")):
287
+ return False
288
+
289
+ # C/C++
290
+ if base.endswith(("_test.cpp", "_test.cc", "_test.c")):
291
+ return False
292
+ if base.startswith("test_") and base.endswith((".cpp", ".cc", ".c")):
293
+ return False
294
+
295
+ # PHP
296
+ if base.endswith("test.php"):
297
+ return False
298
+
299
+ # C#
300
+ if base.endswith(("tests.cs", "test.cs")):
301
+ return False
302
+
303
+ # Include everything else (source code, docs, examples, type definitions, etc.)
304
+ # This ensures fix.patch is comprehensive and consistent with bug.patch
305
+ return True
306
+
307
+
308
+ def check_multi_file_requirement(
309
+ files: list[dict], min_files: int = 3, max_files: int = 10
310
+ ) -> tuple[bool, str, int]:
311
+ """Check if PR modifies sufficient source files for a good task.
312
+
313
+ Harbor tasks should require changes to 3+ source files (tests don't count).
314
+ Single-file and two-file changes are too easy - agents can pattern-match.
315
+ Large refactors (10+ files) are too complex and often not single bug fixes.
316
+
317
+ Args:
318
+ files: List of file dicts with 'filename' key (from GitHub API)
319
+ min_files: Minimum number of source files required (default: 3)
320
+ max_files: Maximum number of source files allowed (default: 10)
321
+
322
+ Returns:
323
+ Tuple of (passes, reason, source_count) where:
324
+ - passes: True if source files are within [min_files, max_files] range
325
+ - reason: Explanation if failed
326
+ - source_count: Number of source files found
327
+ """
328
+ source_files = []
329
+ for f in files:
330
+ filename = f.get("filename", "")
331
+ if _is_relevant_source(filename):
332
+ source_files.append(filename)
333
+
334
+ count = len(source_files)
335
+
336
+ if count < min_files:
337
+ return (
338
+ False,
339
+ f"Only {count} source file{'s' if count != 1 else ''} modified (need {min_files}+, tests excluded)",
340
+ count,
341
+ )
342
+
343
+ if count > max_files:
344
+ return (
345
+ False,
346
+ f"Too many source files modified ({count}, max {max_files}) - likely a large refactor (tests excluded)",
347
+ count,
348
+ )
349
+
350
+ return True, "", count
@@ -0,0 +1,13 @@
1
+ from .farm_hand import PRCandidate, TaskResult
2
+ from .fetcher import StreamingPRFetcher, load_skip_list
3
+ from .state import StreamState
4
+ from .stream_farm import StreamFarmer
5
+
6
+ __all__ = [
7
+ "StreamFarmer",
8
+ "StreamState",
9
+ "StreamingPRFetcher",
10
+ "PRCandidate",
11
+ "TaskResult",
12
+ "load_skip_list",
13
+ ]