cotlab 0.8.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.
- cotlab/__init__.py +3 -0
- cotlab/analyse_experiments.py +392 -0
- cotlab/analysis/__init__.py +11 -0
- cotlab/analysis/cot_parser.py +243 -0
- cotlab/analysis/faithfulness_metrics.py +192 -0
- cotlab/backends/__init__.py +16 -0
- cotlab/backends/base.py +78 -0
- cotlab/backends/transformers_backend.py +335 -0
- cotlab/backends/vllm_backend.py +227 -0
- cotlab/cli.py +83 -0
- cotlab/core/__init__.py +34 -0
- cotlab/core/base.py +749 -0
- cotlab/core/config.py +90 -0
- cotlab/core/registry.py +68 -0
- cotlab/datasets/__init__.py +45 -0
- cotlab/datasets/loaders.py +1889 -0
- cotlab/experiment/__init__.py +315 -0
- cotlab/experiments/__init__.py +43 -0
- cotlab/experiments/activation_compare.py +290 -0
- cotlab/experiments/activation_patching.py +1050 -0
- cotlab/experiments/attention_analysis.py +885 -0
- cotlab/experiments/classification.py +235 -0
- cotlab/experiments/composite_shift_detector.py +524 -0
- cotlab/experiments/cot_ablation.py +277 -0
- cotlab/experiments/cot_faithfulness.py +187 -0
- cotlab/experiments/cot_heads.py +208 -0
- cotlab/experiments/full_layer_cot.py +232 -0
- cotlab/experiments/full_layer_patching.py +225 -0
- cotlab/experiments/h_neuron_analysis.py +712 -0
- cotlab/experiments/logit_lens.py +439 -0
- cotlab/experiments/multi_head_cot.py +220 -0
- cotlab/experiments/multi_head_patching.py +229 -0
- cotlab/experiments/probing_classifier.py +402 -0
- cotlab/experiments/residual_norm_ood.py +413 -0
- cotlab/experiments/sae_feature_analysis.py +673 -0
- cotlab/experiments/steering_vectors.py +223 -0
- cotlab/experiments/sycophancy_heads.py +224 -0
- cotlab/logging/__init__.py +5 -0
- cotlab/logging/json_logger.py +161 -0
- cotlab/main.py +317 -0
- cotlab/patching/__init__.py +24 -0
- cotlab/patching/cache.py +141 -0
- cotlab/patching/hooks.py +558 -0
- cotlab/patching/interventions.py +86 -0
- cotlab/patching/patcher.py +439 -0
- cotlab/patching/sae.py +181 -0
- cotlab/prompts/__init__.py +43 -0
- cotlab/prompts/cardiology.py +378 -0
- cotlab/prompts/histopathology.py +265 -0
- cotlab/prompts/length_matched_strategies.py +157 -0
- cotlab/prompts/mcq.py +193 -0
- cotlab/prompts/neurology.py +353 -0
- cotlab/prompts/oncology.py +367 -0
- cotlab/prompts/plab.py +162 -0
- cotlab/prompts/pubhealthbench.py +82 -0
- cotlab/prompts/pubmedqa.py +173 -0
- cotlab/prompts/radiology.py +414 -0
- cotlab/prompts/strategies.py +939 -0
- cotlab/prompts/tcga.py +168 -0
- cotlab/runner.py +204 -0
- cotlab-0.8.0.dist-info/METADATA +166 -0
- cotlab-0.8.0.dist-info/RECORD +65 -0
- cotlab-0.8.0.dist-info/WHEEL +4 -0
- cotlab-0.8.0.dist-info/entry_points.txt +3 -0
- cotlab-0.8.0.dist-info/licenses/LICENSE +21 -0
cotlab/prompts/tcga.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""TCGA Cancer Type Classification Prompt Strategy."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any, Dict, Optional
|
|
5
|
+
|
|
6
|
+
from ..core.base import BasePromptStrategy, StructuredOutputMixin
|
|
7
|
+
from ..core.registry import Registry
|
|
8
|
+
|
|
9
|
+
SYSTEM_ROLE = """You are an expert pathologist specializing in cancer classification.
|
|
10
|
+
Your goal is to identify the specific TCGA Cancer Type Code (e.g., BRCA, LUAD) from the pathology report.
|
|
11
|
+
Think rationally and explain your reasoning step-by-step."""
|
|
12
|
+
|
|
13
|
+
SYSTEM_ROLE_CONTRARIAN = """You are a skeptical pathologist reviewing a cancer classification.
|
|
14
|
+
Question obvious conclusions and consider alternate cancer type codes before deciding."""
|
|
15
|
+
|
|
16
|
+
PROMPT_TEMPLATE = """Follow this structured reasoning on the attached pathology report:
|
|
17
|
+
|
|
18
|
+
1. **Anatomic Site**: Identify the organ and specific site of the specimen.
|
|
19
|
+
2. **Histological Findings**: Identify the specific tumor type, morphology, and grade described.
|
|
20
|
+
3. **TCGA Code Selection**: Match the findings to one of the 32 valid TCGA codes.
|
|
21
|
+
|
|
22
|
+
Valid Codes:
|
|
23
|
+
ACC, BLCA, BRCA, CESC, CHOL, COAD, DLBC, ESCA, GBM, HNSC, KICH, KIRC, KIRP, LGG, LIHC, LUAD, LUSC, MESO, OV, PAAD, PCPG, PRAD, READ, SARC, SKCM, STAD, TGCT, THCA, THYM, UCEC, UCS, UVM
|
|
24
|
+
|
|
25
|
+
Follow the format of these examples and give the output strictly in the json format.
|
|
26
|
+
|
|
27
|
+
Example 1: Kidney Renal Clear Cell Carcinoma
|
|
28
|
+
```json
|
|
29
|
+
{{
|
|
30
|
+
"cancer_type": "KIRC",
|
|
31
|
+
"reasoning": "1. Site: Kidney (Left Upper Pole). 2. Findings: Renal cell carcinoma, conventional (clear cell) type, Fuhrman Grade II. 3. Code: Kidney Renal Clear Cell Carcinoma maps to KIRC."
|
|
32
|
+
}}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Example 2: Breast Invasive Carcinoma
|
|
36
|
+
```json
|
|
37
|
+
{{
|
|
38
|
+
"cancer_type": "BRCA",
|
|
39
|
+
"reasoning": "1. Site: Breast. 2. Findings: Invasive ductal carcinoma. 3. Code: Breast Invasive Carcinoma maps to BRCA."
|
|
40
|
+
}}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Pathology report:
|
|
44
|
+
\"\"\"
|
|
45
|
+
{report}
|
|
46
|
+
\"\"\"
|
|
47
|
+
|
|
48
|
+
Response:
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
PROMPT_TEMPLATE_ANSWER_FIRST = """Review the pathology report and provide an immediate TCGA code.
|
|
52
|
+
|
|
53
|
+
**Step 1 - Initial Code**: Based on your first read, state the single best TCGA code.
|
|
54
|
+
**Step 2 - Evidence**: Justify the code with site and histology evidence.
|
|
55
|
+
|
|
56
|
+
Valid Codes:
|
|
57
|
+
ACC, BLCA, BRCA, CESC, CHOL, COAD, DLBC, ESCA, GBM, HNSC, KICH, KIRC, KIRP, LGG, LIHC, LUAD, LUSC, MESO, OV, PAAD, PCPG, PRAD, READ, SARC, SKCM, STAD, TGCT, THCA, THYM, UCEC, UCS, UVM
|
|
58
|
+
|
|
59
|
+
Provide your response in JSON format. Follow the format of these two examples and give the output strictly in the json format.
|
|
60
|
+
|
|
61
|
+
Example 1: Initial BRCA, then justification
|
|
62
|
+
```json
|
|
63
|
+
{{
|
|
64
|
+
"cancer_type": "BRCA",
|
|
65
|
+
"reasoning": "Initial code: BRCA. Site: Breast. Histology: Invasive ductal carcinoma. This maps to TCGA BRCA."
|
|
66
|
+
}}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Example 2: Initial LUAD, then justification
|
|
70
|
+
```json
|
|
71
|
+
{{
|
|
72
|
+
"cancer_type": "LUAD",
|
|
73
|
+
"reasoning": "Initial code: LUAD. Site: Lung. Histology: Adenocarcinoma with glandular features. This maps to TCGA LUAD."
|
|
74
|
+
}}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Pathology report:
|
|
78
|
+
\"\"\"
|
|
79
|
+
{report}
|
|
80
|
+
\"\"\"
|
|
81
|
+
|
|
82
|
+
Response:
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@Registry.register_prompt("tcga")
|
|
87
|
+
class TCGAPromptStrategy(StructuredOutputMixin, BasePromptStrategy):
|
|
88
|
+
"""
|
|
89
|
+
Structured JSON output for TCGA cancer type classification.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
def __init__(
|
|
93
|
+
self,
|
|
94
|
+
name: str = "tcga",
|
|
95
|
+
system_role: Optional[str] = None,
|
|
96
|
+
few_shot: bool = True,
|
|
97
|
+
contrarian: bool = False,
|
|
98
|
+
answer_first: bool = False,
|
|
99
|
+
**kwargs,
|
|
100
|
+
):
|
|
101
|
+
self._name = name
|
|
102
|
+
self.few_shot = few_shot
|
|
103
|
+
self.contrarian = contrarian
|
|
104
|
+
self.answer_first = answer_first
|
|
105
|
+
if system_role:
|
|
106
|
+
self.system_role = system_role
|
|
107
|
+
else:
|
|
108
|
+
self.system_role = SYSTEM_ROLE_CONTRARIAN if contrarian else SYSTEM_ROLE
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def name(self) -> str:
|
|
112
|
+
return self._name
|
|
113
|
+
|
|
114
|
+
def build_prompt(self, input_data: Dict[str, Any]) -> str:
|
|
115
|
+
"""Build prompt with pathology report."""
|
|
116
|
+
report = input_data.get("text", input_data.get("report", ""))
|
|
117
|
+
|
|
118
|
+
template = PROMPT_TEMPLATE_ANSWER_FIRST if self.answer_first else PROMPT_TEMPLATE
|
|
119
|
+
|
|
120
|
+
if not self.few_shot:
|
|
121
|
+
template = self._remove_few_shot_examples(template)
|
|
122
|
+
|
|
123
|
+
prompt = template.format(report=report)
|
|
124
|
+
return prompt
|
|
125
|
+
|
|
126
|
+
def _remove_few_shot_examples(self, template: str) -> str:
|
|
127
|
+
import re
|
|
128
|
+
|
|
129
|
+
pattern = r"Example \d+:.*?```json.*?```"
|
|
130
|
+
cleaned = re.sub(pattern, "", template, flags=re.DOTALL)
|
|
131
|
+
return cleaned
|
|
132
|
+
|
|
133
|
+
def parse_response(self, response: str) -> Dict[str, Any]:
|
|
134
|
+
"""Parse JSON response."""
|
|
135
|
+
try:
|
|
136
|
+
# Try to find JSON block
|
|
137
|
+
import re
|
|
138
|
+
|
|
139
|
+
json_match = re.search(r"```json\s*(.*?)\s*```", response, re.DOTALL)
|
|
140
|
+
if json_match:
|
|
141
|
+
json_str = json_match.group(1)
|
|
142
|
+
else:
|
|
143
|
+
# Try raw brace match
|
|
144
|
+
json_match = re.search(r'\{[^{}]*"cancer_type"[^{}]*\}', response, re.DOTALL)
|
|
145
|
+
if json_match:
|
|
146
|
+
json_str = json_match.group(0)
|
|
147
|
+
else:
|
|
148
|
+
json_str = response
|
|
149
|
+
|
|
150
|
+
parsed = json.loads(json_str)
|
|
151
|
+
return {
|
|
152
|
+
"answer": parsed.get("cancer_type", "UNKNOWN"),
|
|
153
|
+
"cancer_type": parsed.get("cancer_type"),
|
|
154
|
+
"reasoning": parsed.get("reasoning", ""),
|
|
155
|
+
"raw": response,
|
|
156
|
+
"parsed_json": parsed,
|
|
157
|
+
}
|
|
158
|
+
except Exception:
|
|
159
|
+
return {"answer": "ERROR", "raw": response, "parse_error": True}
|
|
160
|
+
|
|
161
|
+
def get_system_message(self) -> Optional[str]:
|
|
162
|
+
return self.system_role
|
|
163
|
+
|
|
164
|
+
def get_compatible_datasets(self) -> list[str]:
|
|
165
|
+
return ["tcga"]
|
|
166
|
+
|
|
167
|
+
def get_prediction_field(self) -> str:
|
|
168
|
+
return "cancer_type"
|
cotlab/runner.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from hydra import compose, initialize
|
|
6
|
+
from omegaconf import OmegaConf
|
|
7
|
+
|
|
8
|
+
from cotlab.core import create_component
|
|
9
|
+
from cotlab.logging import ExperimentLogger
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _extract_backend_load_kwargs(cfg_backend) -> dict:
|
|
13
|
+
backend_cfg = OmegaConf.to_container(cfg_backend, resolve=True)
|
|
14
|
+
keys = [
|
|
15
|
+
"load_in_4bit",
|
|
16
|
+
"load_in_8bit",
|
|
17
|
+
"bnb_4bit_quant_type",
|
|
18
|
+
"bnb_4bit_compute_dtype",
|
|
19
|
+
"bnb_4bit_use_double_quant",
|
|
20
|
+
]
|
|
21
|
+
return {key: backend_cfg.get(key) for key in keys if backend_cfg.get(key) is not None}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def main():
|
|
25
|
+
# Parse CLI arguments
|
|
26
|
+
parser = argparse.ArgumentParser(description="Run CoTLab experiment batch")
|
|
27
|
+
parser.add_argument(
|
|
28
|
+
"--backend",
|
|
29
|
+
type=str,
|
|
30
|
+
default="vllm",
|
|
31
|
+
choices=["vllm", "transformers"],
|
|
32
|
+
help="Backend to use (default: vllm)",
|
|
33
|
+
)
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
"--model",
|
|
36
|
+
type=str,
|
|
37
|
+
default="medgemma_27b_text_it",
|
|
38
|
+
help="Model config name (default: medgemma_27b_text_it)",
|
|
39
|
+
)
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
"--format",
|
|
42
|
+
type=str,
|
|
43
|
+
default="plain",
|
|
44
|
+
choices=["plain", "json", "toon", "toml", "xml", "yaml", "markdown"],
|
|
45
|
+
help="Output format (default: plain)",
|
|
46
|
+
)
|
|
47
|
+
args = parser.parse_args()
|
|
48
|
+
|
|
49
|
+
backend_name = args.backend
|
|
50
|
+
model_name = args.model
|
|
51
|
+
output_format = args.format
|
|
52
|
+
|
|
53
|
+
# Setup base output directory with model name and format
|
|
54
|
+
timestamp = datetime.now().strftime("%Y-%m-%d/%H-%M-%S")
|
|
55
|
+
base_output_dir = Path(f"outputs/{timestamp}_{model_name}_{backend_name}_{output_format}")
|
|
56
|
+
base_output_dir.mkdir(parents=True, exist_ok=True)
|
|
57
|
+
print(f"Output directory: {base_output_dir}")
|
|
58
|
+
|
|
59
|
+
# Initialize Hydra
|
|
60
|
+
with initialize(version_base=None, config_path="../../conf"):
|
|
61
|
+
# 1. Load Backend & Model ONCE
|
|
62
|
+
print("=" * 60)
|
|
63
|
+
print(f"Initializing Backend ({backend_name}) and Model ({model_name})...")
|
|
64
|
+
print("=" * 60)
|
|
65
|
+
|
|
66
|
+
# Base config to load model
|
|
67
|
+
base_cfg = compose(
|
|
68
|
+
config_name="config",
|
|
69
|
+
overrides=[
|
|
70
|
+
f"backend={backend_name}",
|
|
71
|
+
f"model={model_name}",
|
|
72
|
+
"experiment=cot_faithfulness", # Dummy
|
|
73
|
+
"dataset=pediatrics", # Dummy
|
|
74
|
+
"prompt=chain_of_thought", # Dummy
|
|
75
|
+
],
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
backend = create_component(base_cfg.backend)
|
|
79
|
+
backend.load_model(base_cfg.model.name, **_extract_backend_load_kwargs(base_cfg.backend))
|
|
80
|
+
print("Backend initialized successfully.")
|
|
81
|
+
|
|
82
|
+
# Define Experiment Grid
|
|
83
|
+
grid = [
|
|
84
|
+
# 1. Main CoT Faithfulness Sweep
|
|
85
|
+
{
|
|
86
|
+
"experiment": "cot_faithfulness",
|
|
87
|
+
"datasets": ["pediatrics", "synthetic", "patching_pairs", "radiology"],
|
|
88
|
+
"prompts": [
|
|
89
|
+
"chain_of_thought",
|
|
90
|
+
"direct_answer",
|
|
91
|
+
"sycophantic",
|
|
92
|
+
"adversarial",
|
|
93
|
+
"uncertainty",
|
|
94
|
+
"expert_persona",
|
|
95
|
+
"few_shot",
|
|
96
|
+
"contrarian",
|
|
97
|
+
"arrogance",
|
|
98
|
+
"simple",
|
|
99
|
+
"socratic",
|
|
100
|
+
"no_instruction",
|
|
101
|
+
"radiology",
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
# 2. Classification Experiment (works for all specialties)
|
|
105
|
+
{"experiment": "classification", "datasets": ["radiology"], "prompts": ["radiology"]},
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
total_jobs = sum(len(g["datasets"]) * len(g["prompts"]) for g in grid)
|
|
109
|
+
print(f"Starting execution of {total_jobs} jobs...")
|
|
110
|
+
|
|
111
|
+
job_idx = 0
|
|
112
|
+
for group in grid:
|
|
113
|
+
exp_name = group["experiment"]
|
|
114
|
+
|
|
115
|
+
for dataset_name in group["datasets"]:
|
|
116
|
+
for prompt_name in group["prompts"]:
|
|
117
|
+
job_idx += 1
|
|
118
|
+
print(
|
|
119
|
+
f"\n[Job {job_idx}/{total_jobs}] exp={exp_name} dataset={dataset_name} prompt={prompt_name}"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Create specific config override
|
|
123
|
+
overrides = [
|
|
124
|
+
f"backend={backend_name}",
|
|
125
|
+
f"model={model_name}",
|
|
126
|
+
f"experiment={exp_name}",
|
|
127
|
+
f"dataset={dataset_name}",
|
|
128
|
+
f"prompt={prompt_name}",
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
# Add output format override if not plain
|
|
132
|
+
if output_format != "plain":
|
|
133
|
+
overrides.append(f"++prompt.output_format={output_format}")
|
|
134
|
+
|
|
135
|
+
cfg = compose(config_name="config", overrides=overrides)
|
|
136
|
+
|
|
137
|
+
# Instantiate Components for this run
|
|
138
|
+
try:
|
|
139
|
+
dataset = create_component(cfg.dataset)
|
|
140
|
+
prompt_strategy = create_component(cfg.prompt)
|
|
141
|
+
experiment = create_component(cfg.experiment)
|
|
142
|
+
|
|
143
|
+
# Check prompt-dataset compatibility (prompt side)
|
|
144
|
+
compatible_datasets = prompt_strategy.get_compatible_datasets()
|
|
145
|
+
if (
|
|
146
|
+
compatible_datasets is not None
|
|
147
|
+
and dataset_name not in compatible_datasets
|
|
148
|
+
):
|
|
149
|
+
print(f" SKIPPED: {prompt_name} is not compatible with {dataset_name}")
|
|
150
|
+
print(f" (prompt only works with: {compatible_datasets})")
|
|
151
|
+
continue
|
|
152
|
+
|
|
153
|
+
# Check prompt-dataset compatibility (dataset side)
|
|
154
|
+
compatible_prompts = dataset.get_compatible_prompts()
|
|
155
|
+
if compatible_prompts is not None and prompt_name not in compatible_prompts:
|
|
156
|
+
print(f" SKIPPED: {dataset_name} is not compatible with {prompt_name}")
|
|
157
|
+
print(f" (dataset only works with: {compatible_prompts})")
|
|
158
|
+
continue
|
|
159
|
+
|
|
160
|
+
# Setup individual run logger
|
|
161
|
+
run_name = f"{exp_name}_{dataset_name}_{prompt_name}"
|
|
162
|
+
run_dir = base_output_dir / run_name
|
|
163
|
+
run_dir.mkdir(exist_ok=True)
|
|
164
|
+
|
|
165
|
+
logger = ExperimentLogger(str(run_dir))
|
|
166
|
+
logger.log_config(cfg)
|
|
167
|
+
|
|
168
|
+
# Run Experiment
|
|
169
|
+
num_samples = OmegaConf.select(cfg, "experiment.num_samples", default=None)
|
|
170
|
+
result = experiment.run(
|
|
171
|
+
backend=backend,
|
|
172
|
+
dataset=dataset,
|
|
173
|
+
prompt_strategy=prompt_strategy,
|
|
174
|
+
logger=logger,
|
|
175
|
+
**(dict(num_samples=num_samples) if num_samples is not None else {}),
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# Save results
|
|
179
|
+
results_path = logger.save_results(result)
|
|
180
|
+
print(f"Results saved: {results_path}")
|
|
181
|
+
|
|
182
|
+
# Basic metrics print
|
|
183
|
+
metrics_str = ", ".join(
|
|
184
|
+
[
|
|
185
|
+
f"{k}={v:.2f}" if isinstance(v, float) else f"{k}={v}"
|
|
186
|
+
for k, v in result.metrics.items()
|
|
187
|
+
]
|
|
188
|
+
)
|
|
189
|
+
print(f"Metrics: {metrics_str}")
|
|
190
|
+
|
|
191
|
+
except Exception as e:
|
|
192
|
+
print(f"ERROR in job {job_idx}: {e}")
|
|
193
|
+
import traceback
|
|
194
|
+
|
|
195
|
+
traceback.print_exc()
|
|
196
|
+
|
|
197
|
+
print("\n" + "=" * 60)
|
|
198
|
+
print("All jobs completed.")
|
|
199
|
+
print(f"Results stored in: {base_output_dir}")
|
|
200
|
+
print("=" * 60)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
if __name__ == "__main__":
|
|
204
|
+
main()
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cotlab
|
|
3
|
+
Version: 0.8.0
|
|
4
|
+
Summary: CoTLab - Chain of Thought Research Toolkit for LLMs
|
|
5
|
+
Project-URL: Repository, https://github.com/huseyincavusbi/CoTLab
|
|
6
|
+
Project-URL: Documentation, https://huseyincavusbi.github.io/CoTLab
|
|
7
|
+
Project-URL: Issues, https://github.com/huseyincavusbi/CoTLab/issues
|
|
8
|
+
Author-email: Huseyin Cavus <huseyincavus@proton.me>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: chain-of-thought,interpretability,llm,mechanistic-interpretability,medical-ai,probing
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: accelerate>=0.27.0
|
|
22
|
+
Requires-Dist: bitsandbytes>=0.46.1
|
|
23
|
+
Requires-Dist: click>=8.1.0
|
|
24
|
+
Requires-Dist: huggingface-hub>=0.20.0
|
|
25
|
+
Requires-Dist: hydra-core>=1.3.0
|
|
26
|
+
Requires-Dist: numpy>=1.24.0
|
|
27
|
+
Requires-Dist: omegaconf>=2.3.0
|
|
28
|
+
Requires-Dist: pandas>=2.0.0
|
|
29
|
+
Requires-Dist: protobuf>=3.20.0
|
|
30
|
+
Requires-Dist: pyarrow>=14.0.0
|
|
31
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
32
|
+
Requires-Dist: safetensors>=0.4.0
|
|
33
|
+
Requires-Dist: scikit-learn>=1.3.0
|
|
34
|
+
Requires-Dist: torch>=2.1.0
|
|
35
|
+
Requires-Dist: tqdm>=4.65.0
|
|
36
|
+
Requires-Dist: transformers<5,>=4.56.0
|
|
37
|
+
Requires-Dist: xmltodict>=0.13.0
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: ipython>=8.0.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: jupyter>=1.0.0; extra == 'dev'
|
|
41
|
+
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
42
|
+
Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
|
|
43
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
44
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
45
|
+
Requires-Dist: ruff>=0.4.4; extra == 'dev'
|
|
46
|
+
Provides-Extra: vllm
|
|
47
|
+
Requires-Dist: vllm>=0.6.0; extra == 'vllm'
|
|
48
|
+
Description-Content-Type: text/markdown
|
|
49
|
+
|
|
50
|
+
# CoTLab
|
|
51
|
+
|
|
52
|
+
[](https://deepwiki.com/huseyincavusbi/CoTLab)
|
|
53
|
+
|
|
54
|
+
A research toolkit for investigating Chain of Thought (CoT) reasoning, faithfulness, and mechanistic interpretability in Large Language Models.
|
|
55
|
+
|
|
56
|
+
## Features
|
|
57
|
+
|
|
58
|
+
- Experiments for CoT faithfulness, patching, logit‑lens, steering, and probing
|
|
59
|
+
- Diverse prompt strategies (CoT, direct, adversarial, contrarian, few‑shot, etc.)
|
|
60
|
+
- Configurable models, datasets, and backends (vLLM + Transformers)
|
|
61
|
+
- Auto‑detect layers/heads at runtime
|
|
62
|
+
- Hydra config for easy composition and multiruns
|
|
63
|
+
----
|
|
64
|
+
- Project overview (DeepWiki): https://deepwiki.com/huseyincavusbi/CoTLab/1-overview
|
|
65
|
+
- Official docs: https://huseyincavusbi.github.io/CoTLab/
|
|
66
|
+
---
|
|
67
|
+
## Installation
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
git clone https://github.com/huseyincavusbi/CoTLab.git
|
|
71
|
+
cd CoTLab
|
|
72
|
+
uv venv cotlab --python 3.11
|
|
73
|
+
source cotlab/bin/activate
|
|
74
|
+
uv pip install -e ".[dev]"
|
|
75
|
+
|
|
76
|
+
# GPU Setup:
|
|
77
|
+
# NVIDIA: uv pip install vllm
|
|
78
|
+
# AMD ROCm: ./scripts/cotlab-rocm.sh (uses Docker)
|
|
79
|
+
|
|
80
|
+
# AMD ROCm (Transformers backend): install ROCm PyTorch wheels
|
|
81
|
+
# uv pip install --reinstall --index-url https://download.pytorch.org/whl/rocm6.4 torch torchvision torchaudio
|
|
82
|
+
|
|
83
|
+
# Apple Silicon: requires Python 3.12 and vllm-metal plugin
|
|
84
|
+
# See docs/getting-started/installation.md for Metal setup instructions
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
See [Installation Docs](docs/getting-started/installation.md) for detailed GPU setup.
|
|
88
|
+
|
|
89
|
+
## Backend Compatibility
|
|
90
|
+
|
|
91
|
+
CoTLab supports two inference backends with different strengths:
|
|
92
|
+
|
|
93
|
+
### 1. vLLM Backend (High Performance)
|
|
94
|
+
Best for large-scale generation experiments.
|
|
95
|
+
- **Supported Experiments**: `cot_faithfulness`, `radiology`
|
|
96
|
+
- **Supported Models**: All text-only models (e.g., `gemma_270m`, `medgemma_27b_text_it`)
|
|
97
|
+
- **Limitation**: Does NOT support activation patching or internal state access.
|
|
98
|
+
- **Note**: Gemma 3 multimodal models (e.g., `medgemma_4b_it`) are currently incompatible with vLLM 0.12.0 due to architecture detection issues. Use `transformers` backend for these.
|
|
99
|
+
|
|
100
|
+
### 2. Transformers Backend (Full Access)
|
|
101
|
+
Best for mechanistic interpretability and activation patching.
|
|
102
|
+
- **Supported Experiments**: ALL experiments.
|
|
103
|
+
- **Supported Models**: ALL models.
|
|
104
|
+
- **Limitation**: Slower.
|
|
105
|
+
|
|
106
|
+
To switch backends:
|
|
107
|
+
```bash
|
|
108
|
+
# Use vLLM (fast generation)
|
|
109
|
+
python -m cotlab.main backend=vllm ...
|
|
110
|
+
|
|
111
|
+
# Use Transformers (activation access)
|
|
112
|
+
python -m cotlab.main backend=transformers ...
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Quick Start
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# Run logit lens on MedGemma
|
|
119
|
+
python -m cotlab.main experiment=logit_lens model=medgemma_4b
|
|
120
|
+
|
|
121
|
+
# Find sycophancy heads
|
|
122
|
+
python -m cotlab.main experiment=sycophancy_heads model=medgemma_4b
|
|
123
|
+
|
|
124
|
+
# Test CoT ablation on pediatrics dataset
|
|
125
|
+
python -m cotlab.main experiment=cot_ablation dataset=pediatrics
|
|
126
|
+
|
|
127
|
+
# Compare prompt strategies
|
|
128
|
+
python -m cotlab.main -m prompt=chain_of_thought,direct_answer,sycophantic
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Supported Models
|
|
132
|
+
|
|
133
|
+
CoTLab ships config files for some models, but in principle it supports any model
|
|
134
|
+
that the selected backend can load. Mechanistic experiments can still fail for
|
|
135
|
+
models with unusual architectures.
|
|
136
|
+
|
|
137
|
+
You can add a model config file for more control over hyperparameters, but you
|
|
138
|
+
can also run any experiment by passing a Hugging Face model name directly.
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# Use a built-in model config
|
|
142
|
+
python -m cotlab.main model=medgemma_4b
|
|
143
|
+
|
|
144
|
+
# Or pass any HF model name directly
|
|
145
|
+
python -m cotlab.main model.name=google/gemma-3-270m
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
If you prefer, you can pre-create model configs with `cotlab-template`, but
|
|
149
|
+
this is optional because CoTLab can auto-generate model configs when running
|
|
150
|
+
with `model=org/repo-id`.
|
|
151
|
+
|
|
152
|
+
## Configuration
|
|
153
|
+
|
|
154
|
+
All configs auto-detect layers/heads at runtime. Override via CLI:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
python -m cotlab.main \
|
|
158
|
+
model=medgemma_4b \
|
|
159
|
+
dataset=pediatrics \
|
|
160
|
+
prompt=chain_of_thought \
|
|
161
|
+
experiment.top_k=10
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
cotlab/__init__.py,sha256=y--3rgqeSRBO5vMnx92B_HaS8QIzNlb_6sC5XDE29gs,73
|
|
2
|
+
cotlab/analyse_experiments.py,sha256=1fWqdBKRTgkJO1gREuXvQgO-tYErcEWqLS-TWdQzCO4,12790
|
|
3
|
+
cotlab/cli.py,sha256=b1jLJXmoAlrsz2jL5koMH4eIREXihDOROAitylc3REo,2431
|
|
4
|
+
cotlab/main.py,sha256=vXVHgwqccd0dy1M5upvT9oZciXHkK0kcg58hoinn0gY,11169
|
|
5
|
+
cotlab/runner.py,sha256=Dg5eT4xzsIYNqmJkZ4dY_307YbON_1gCGfF7lbCile4,7898
|
|
6
|
+
cotlab/analysis/__init__.py,sha256=tHoElnfh3Q4nw0J9pQltjhXuxpAfF1vkqW59WRotCY4,263
|
|
7
|
+
cotlab/analysis/cot_parser.py,sha256=ly4pmjWWZZATJeMwvX7XDagSEACA2qnPt8Y0LRQRHX0,7008
|
|
8
|
+
cotlab/analysis/faithfulness_metrics.py,sha256=MpdRBvP0UglQa6poyY4RTi0XtU98I7spbdD9yppvsek,6261
|
|
9
|
+
cotlab/backends/__init__.py,sha256=tPcnXB7TC6HWSgjmch9Dms_d1txe_dyithqv6C99Gy0,370
|
|
10
|
+
cotlab/backends/base.py,sha256=Issi9EJi0HeVfRhaPtQ4hbjOXtJKDAdxbeAp_e2ljXw,1973
|
|
11
|
+
cotlab/backends/transformers_backend.py,sha256=_rQzbhW2rmqHJVOSTm6uzjMtBm14qFDe1zt_UtTbkfU,11377
|
|
12
|
+
cotlab/backends/vllm_backend.py,sha256=PRlD_KrcFciU94fo0GcWgvZfWIj7OxZcoGwHaLmCHro,7374
|
|
13
|
+
cotlab/core/__init__.py,sha256=39XD_yL9RGoNAY6hn_jIXbU8hIhbsvT6P_tAcLl0fbg,669
|
|
14
|
+
cotlab/core/base.py,sha256=TpGy7DnQPTn4pwJYnF0_4mRdjxT3iaz3b4TM7hK6jFU,24254
|
|
15
|
+
cotlab/core/config.py,sha256=_GP5JOPDNQmCsTZc6ELA86PspxhrDRtqCUSFQKUHios,2149
|
|
16
|
+
cotlab/core/registry.py,sha256=Deg-O9cY5nsAfUZ8xxO8kH6tiSCK3TLEaZ4KOOdtXME,1618
|
|
17
|
+
cotlab/datasets/__init__.py,sha256=wcEZZDOYaPdqE0RRXfeaQIzRzMfC9iExmWn0jGkoNAE,932
|
|
18
|
+
cotlab/datasets/loaders.py,sha256=Wp854_UM52tXjYYU0ZTvBgNWQhdRp-Srcj46O5c8tlo,61236
|
|
19
|
+
cotlab/experiment/__init__.py,sha256=aPnVcJ8pkjwQ1m1ZOgqVnIPEun57W80O9-lU_tXioWU,11323
|
|
20
|
+
cotlab/experiments/__init__.py,sha256=72CoKW05xzr6NZhpDYVF6DWO7XPN3v7ARNEoOINZIBU,1737
|
|
21
|
+
cotlab/experiments/activation_compare.py,sha256=DjNOAySg5boOWsgfnvue_rmINq-VOGdVJfIYKSCRb2s,11736
|
|
22
|
+
cotlab/experiments/activation_patching.py,sha256=Pwv4nTVvtu8qCufWnJdSkexPR_LSWhdFLwwStcmMemQ,44682
|
|
23
|
+
cotlab/experiments/attention_analysis.py,sha256=kPj_MoVqnosR1jW-z32B2Ugu7vcP4GJ9w7dTd9t9GUM,40220
|
|
24
|
+
cotlab/experiments/classification.py,sha256=VzoS0ePmdwEJiU44lxm9LD7vwTgA0k2UABA-oJ1a2vc,8634
|
|
25
|
+
cotlab/experiments/composite_shift_detector.py,sha256=AM6Nb0pFvyE8wSn8INs0WmYTxMTJl0zAWJIEKM0dbTA,21326
|
|
26
|
+
cotlab/experiments/cot_ablation.py,sha256=GG5y4qSCSp4Kyoqvs9_UYkNLAIUwPQObGBkHa0PK7fI,10297
|
|
27
|
+
cotlab/experiments/cot_faithfulness.py,sha256=HXmE6vX4BYcDf6kzCRA0VhVwBbiupyOSDYkfmfrqxHk,7117
|
|
28
|
+
cotlab/experiments/cot_heads.py,sha256=9il1Ebll18_id30OGqBk76bxp4uzaEjo4wP62cN6UYs,7555
|
|
29
|
+
cotlab/experiments/full_layer_cot.py,sha256=Xoq13GubsUptIKXx6OmKkIw7NDt0h8bJGlvIXbuMG8c,8194
|
|
30
|
+
cotlab/experiments/full_layer_patching.py,sha256=H42W_z7k0ytTp-2MR1fieLT-YA7N-0543InVAtPu1Fs,8344
|
|
31
|
+
cotlab/experiments/h_neuron_analysis.py,sha256=kltUXGtgb42m_Ml-cm918Ae_AZJBLgRmidjofaGsqLQ,29465
|
|
32
|
+
cotlab/experiments/logit_lens.py,sha256=FE7-u3n1JPiNZONtFss2ZeTpOCio7Qe1yXIHQQ0GyZw,17654
|
|
33
|
+
cotlab/experiments/multi_head_cot.py,sha256=v5pPKS0c57Vk5tWp1wl5X_0Ghcrycedwb-k1A2TVRvY,7802
|
|
34
|
+
cotlab/experiments/multi_head_patching.py,sha256=-8_1lt64lUyNs74lvgZ7_4yabAl90akyg1ltJ5UKjVU,8478
|
|
35
|
+
cotlab/experiments/probing_classifier.py,sha256=TYqjVXpk9ZRJfLZ-R4BJnEPPM5mEA7f6APe0RjGt5Bk,14835
|
|
36
|
+
cotlab/experiments/residual_norm_ood.py,sha256=oWiElDgIG7TkYxzIo9etRkMHtTSwOI6gJ8eTsi00_po,16384
|
|
37
|
+
cotlab/experiments/sae_feature_analysis.py,sha256=maY6wQsMTy_E4JvFLfNr3LMX1DvT7cCtml4fvATm6I4,27976
|
|
38
|
+
cotlab/experiments/steering_vectors.py,sha256=lVRT0FyvzpeEMJg_4LlsFNQvNUNVW8AjANwDr7LiKqI,8800
|
|
39
|
+
cotlab/experiments/sycophancy_heads.py,sha256=5NmeAk3U3sr4tR5XsAjHZa_TyBL8sGmPTwwnqPk-NHI,8152
|
|
40
|
+
cotlab/logging/__init__.py,sha256=6d0OSqKkzpfHdKlfnKVQqRBkCziXqnX2PecNtzLlmZI,97
|
|
41
|
+
cotlab/logging/json_logger.py,sha256=ERDTa6oV-ArKKO26tQexnG077dgLH71uQjt_EjArHDg,5015
|
|
42
|
+
cotlab/patching/__init__.py,sha256=mXvfO_hJ6dygEeGW-xcouUDpc79DP48rBBDS25ETMrc,542
|
|
43
|
+
cotlab/patching/cache.py,sha256=o402BKEKuNoAz6NKxuo06H3iO65V7vz0HIl0-PozFxI,4547
|
|
44
|
+
cotlab/patching/hooks.py,sha256=Q_Pz78vwKf846adbzJTO1SBWIQQeSCdG7EbpmZOtB3k,20951
|
|
45
|
+
cotlab/patching/interventions.py,sha256=g4y1ni5MWTwxDqHOtT-awwCFSBNh85Laz1PczOBwV4o,2678
|
|
46
|
+
cotlab/patching/patcher.py,sha256=nJqkx8erhP4Npj4iZRsIxc42T8zwtekj4X8t5BbNADU,15697
|
|
47
|
+
cotlab/patching/sae.py,sha256=7XvR8LjQY9MYVUJkZLgGEn38_3qnmp0Y3uWCSQnXdwY,6847
|
|
48
|
+
cotlab/prompts/__init__.py,sha256=axogCx7Ce1upJC53MB5xms1zM7cPMIDIljsDVun6MjM,1278
|
|
49
|
+
cotlab/prompts/cardiology.py,sha256=-fGvhY56q2QXMdLW7QEPDymQUbpZJXjbfHuyWmAlufQ,15698
|
|
50
|
+
cotlab/prompts/histopathology.py,sha256=DfQmljd4Mig2G5loGqiwaGU0tHZw3NIaSn9zCcyPvck,9107
|
|
51
|
+
cotlab/prompts/length_matched_strategies.py,sha256=fnGmWy8uOkSlyWTakY0jJ_9nKg9GCCsSAiDZsibVwsY,4883
|
|
52
|
+
cotlab/prompts/mcq.py,sha256=tkLM8ac8L93mOBQKIfpmBXOSszrhzvvXkLZ4E33p4Mg,8279
|
|
53
|
+
cotlab/prompts/neurology.py,sha256=_h9cozOCHDLaG4n3RVLS3Re305zAV7rTPTtNXnONA5o,14782
|
|
54
|
+
cotlab/prompts/oncology.py,sha256=OmBWSOUJTwVXDPD998vV440EoYbeWCbtzDthiBXXR1s,14807
|
|
55
|
+
cotlab/prompts/plab.py,sha256=w1-xluqTmzjXHgnhW1XNU7MAhab8vQuiKichwIogwM0,5894
|
|
56
|
+
cotlab/prompts/pubhealthbench.py,sha256=XaGYMrjUfSMrfohK1xXegxcog1oeQwSS6O-3-0nSia4,3011
|
|
57
|
+
cotlab/prompts/pubmedqa.py,sha256=S7Ru94gAtl7dmSCzk5IwJxXcMemHahyPablRRFS0zIg,6983
|
|
58
|
+
cotlab/prompts/radiology.py,sha256=KLHeuD2ayWMOvNDJ2pUy_1iPLWjNBDDscCDPRVuS1W8,18007
|
|
59
|
+
cotlab/prompts/strategies.py,sha256=TdJNPbJqPeL9l9y2Q8wQMBgWAUKIJUokzyGcWoVKu7U,31295
|
|
60
|
+
cotlab/prompts/tcga.py,sha256=lieql237kCaSoa-hyvOfpVNmmoXYGfK1KnwYKNZhtPQ,5633
|
|
61
|
+
cotlab-0.8.0.dist-info/METADATA,sha256=NisTk2jRQ9wMO5d3DG_pOL0j7BqSThKPBFhCM-NDtL0,5751
|
|
62
|
+
cotlab-0.8.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
63
|
+
cotlab-0.8.0.dist-info/entry_points.txt,sha256=GHChdvu9KbueSjPgUwDWZOvFTmUxKJnhPY1SC0pWRQE,77
|
|
64
|
+
cotlab-0.8.0.dist-info/licenses/LICENSE,sha256=DKx8N2g6-am9PRrJHYm35-nlhNA-3tcdr5IHRj_1HyA,1078
|
|
65
|
+
cotlab-0.8.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Hüseyin Çavuş
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|