sigx 0.1.1__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.
- sigx-0.1.1/LICENSE +21 -0
- sigx-0.1.1/PKG-INFO +549 -0
- sigx-0.1.1/README.md +510 -0
- sigx-0.1.1/pyproject.toml +112 -0
- sigx-0.1.1/setup.cfg +4 -0
- sigx-0.1.1/sigx/__init__.py +94 -0
- sigx-0.1.1/sigx/converters/__init__.py +17 -0
- sigx-0.1.1/sigx/converters/preference.py +282 -0
- sigx-0.1.1/sigx/exceptions.py +60 -0
- sigx-0.1.1/sigx/extractors/__init__.py +13 -0
- sigx-0.1.1/sigx/extractors/abandon.py +144 -0
- sigx-0.1.1/sigx/extractors/base.py +128 -0
- sigx-0.1.1/sigx/extractors/llm.py +336 -0
- sigx-0.1.1/sigx/extractors/rephrase.py +172 -0
- sigx-0.1.1/sigx/extractors/sentiment.py +436 -0
- sigx-0.1.1/sigx/filters/__init__.py +3 -0
- sigx-0.1.1/sigx/filters/quality.py +179 -0
- sigx-0.1.1/sigx/io/__init__.py +3 -0
- sigx-0.1.1/sigx/io/loader.py +213 -0
- sigx-0.1.1/sigx/pipeline.py +404 -0
- sigx-0.1.1/sigx/py.typed +0 -0
- sigx-0.1.1/sigx/types.py +246 -0
- sigx-0.1.1/sigx.egg-info/PKG-INFO +549 -0
- sigx-0.1.1/sigx.egg-info/SOURCES.txt +32 -0
- sigx-0.1.1/sigx.egg-info/dependency_links.txt +1 -0
- sigx-0.1.1/sigx.egg-info/requires.txt +15 -0
- sigx-0.1.1/sigx.egg-info/top_level.txt +1 -0
- sigx-0.1.1/tests/test_converters.py +362 -0
- sigx-0.1.1/tests/test_exceptions.py +56 -0
- sigx-0.1.1/tests/test_extractors.py +373 -0
- sigx-0.1.1/tests/test_filters.py +93 -0
- sigx-0.1.1/tests/test_performance.py +120 -0
- sigx-0.1.1/tests/test_pipeline.py +291 -0
- sigx-0.1.1/tests/test_regressions.py +287 -0
sigx-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 fengrru
|
|
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.
|
sigx-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sigx
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Implicit Feedback Signal Extraction for LLM Alignment
|
|
5
|
+
Author: fengrru
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/fengrru/sigx
|
|
8
|
+
Project-URL: Repository, https://github.com/fengrru/sigx
|
|
9
|
+
Project-URL: Issues, https://github.com/fengrru/sigx/issues
|
|
10
|
+
Keywords: llm,alignment,dpo,kto,rlhf,preference-learning
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: numpy>=1.21.0
|
|
27
|
+
Requires-Dist: scikit-learn>=1.0.0
|
|
28
|
+
Provides-Extra: wildchat
|
|
29
|
+
Requires-Dist: datasets>=2.0.0; extra == "wildchat"
|
|
30
|
+
Provides-Extra: llm
|
|
31
|
+
Requires-Dist: openai>=1.0.0; extra == "llm"
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
36
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
37
|
+
Requires-Dist: pyright>=1.0.0; extra == "dev"
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
<div align="center">
|
|
41
|
+
|
|
42
|
+
[](https://github.com/Fengrru/sigx/actions/workflows/ci.yml)
|
|
43
|
+
[](https://codecov.io/gh/Fengrru/sigx)
|
|
44
|
+

|
|
45
|
+

|
|
46
|
+

|
|
47
|
+
|
|
48
|
+
# SigX
|
|
49
|
+
|
|
50
|
+
### Implicit Feedback Signal Extraction for LLM Alignment
|
|
51
|
+
|
|
52
|
+
**Extract preference signals from conversation logs — no human annotation required.**
|
|
53
|
+
|
|
54
|
+
[SigX Documentation](docs/index.md) | [Quick Start](#quick-start) | [API Reference](docs/api/pipeline.md) | [Contributing](CONTRIBUTING.md)
|
|
55
|
+
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Why SigX?
|
|
61
|
+
|
|
62
|
+
Traditional LLM alignment relies on **expensive human annotations** or **synthetic preferences from GPT-4**. But every production chat log already contains rich preference signals:
|
|
63
|
+
|
|
64
|
+
| What Users Do | What It Means | SigX Signal |
|
|
65
|
+
|:---|:---|:---|
|
|
66
|
+
| Re-ask the same question | Previous answer was unhelpful | `rephrase` |
|
|
67
|
+
| Say "Actually I meant..." | Model misunderstood | `correction` |
|
|
68
|
+
| Say "That's wrong" | Explicit dissatisfaction | `negative` |
|
|
69
|
+
| Say "Thanks! Perfect!" | Explicit satisfaction | `positive` |
|
|
70
|
+
| Say "Never mind" and leave | User gave up | `abandon` |
|
|
71
|
+
|
|
72
|
+
**SigX mines these signals at zero marginal cost** — turning your existing chat logs into DPO/KTO training data.
|
|
73
|
+
|
|
74
|
+
> Inspired by [WildFeedback](https://arxiv.org/abs/2408.15549) (Microsoft Research, 2024) and the broader literature on learning from implicit preferences.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Comparison with Related Tools
|
|
79
|
+
|
|
80
|
+
| Feature | **SigX** | TRL (HuggingFace) | VeRL |
|
|
81
|
+
|:---|:---:|:---:|:---:|
|
|
82
|
+
| **Extract signals from raw logs** | **Yes** | No (expects labeled pairs) | No |
|
|
83
|
+
| **Implicit feedback detection** | **Yes** | No | No |
|
|
84
|
+
| **DPO/KTO output format** | **Yes** | Yes (training only) | Yes (training only) |
|
|
85
|
+
| **Auto-infer chosen responses** | **Yes** | No | No |
|
|
86
|
+
| **Pluggable extractors** | **Yes** | No | No |
|
|
87
|
+
| **Built-in evaluation metrics** | **Yes** | No | No |
|
|
88
|
+
| **Zero GPU required** | **Yes** | No | No |
|
|
89
|
+
|
|
90
|
+
> SigX is not a training framework — it sits **upstream** of TRL/VeRL, producing the preference data they consume.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Highlights
|
|
95
|
+
|
|
96
|
+
<table>
|
|
97
|
+
<tr>
|
|
98
|
+
<td width="50%">
|
|
99
|
+
|
|
100
|
+
### Core Capabilities
|
|
101
|
+
|
|
102
|
+
- **Pluggable Extractors** — Mix regex, TF-IDF, ML, and LLM detectors
|
|
103
|
+
- **Smart DPO Conversion** — Auto-infer `chosen` from subsequent positive turns
|
|
104
|
+
- **Multi-Format Output** — DPO, KTO, rejection sampling for TRL/VeRL
|
|
105
|
+
- **Built-in Evaluation** — Per-type precision/recall/F1 against benchmarks
|
|
106
|
+
|
|
107
|
+
</td>
|
|
108
|
+
<td width="50%">
|
|
109
|
+
|
|
110
|
+
### Design Principles
|
|
111
|
+
|
|
112
|
+
- **Lightweight** — Only `numpy` + `scikit-learn`. No GPU needed.
|
|
113
|
+
- **Extensible** — Subclass `BaseExtractor` for custom detectors
|
|
114
|
+
- **Production-ready** — Type annotations, 92 tests, CI/CD
|
|
115
|
+
- **Framework-agnostic** — Output compatible with TRL, VeRL, and more
|
|
116
|
+
|
|
117
|
+
</td>
|
|
118
|
+
</tr>
|
|
119
|
+
</table>
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Quick Start
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
pip install git+https://github.com/Fengrru/sigx.git
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 1. Extract Signals from Conversations
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from sigx import Pipeline, RephraseDetector, SentimentDetector
|
|
133
|
+
|
|
134
|
+
pipeline = Pipeline([
|
|
135
|
+
RephraseDetector(similarity_threshold=0.6),
|
|
136
|
+
SentimentDetector(min_confidence=0.6),
|
|
137
|
+
])
|
|
138
|
+
|
|
139
|
+
conversations = [
|
|
140
|
+
{
|
|
141
|
+
"conversation_id": "1",
|
|
142
|
+
"conversation": [
|
|
143
|
+
{"role": "user", "content": "What is Python?"},
|
|
144
|
+
{"role": "assistant", "content": "Python is a type of snake."},
|
|
145
|
+
{"role": "user", "content": "That's not what I asked. I meant the programming language."},
|
|
146
|
+
{"role": "assistant", "content": "Python is a high-level programming language created by Guido van Rossum."},
|
|
147
|
+
{"role": "user", "content": "Thanks! That's exactly what I needed."},
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
signals = pipeline.run(conversations)
|
|
153
|
+
for s in signals:
|
|
154
|
+
print(f"[{s.signal_type}] confidence={s.confidence:.2f} | {s.evidence[:60]}...")
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Output:**
|
|
158
|
+
```
|
|
159
|
+
[negative] confidence=0.95 | That's not what I asked. I meant the programming language....
|
|
160
|
+
[positive] confidence=0.80 | Thanks! That's exactly what I needed....
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### 2. Convert to DPO Training Pairs
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
pairs = pipeline.to_dpo(conversations)
|
|
167
|
+
for p in pairs:
|
|
168
|
+
print(f"Prompt: {p.prompt[:80]}...")
|
|
169
|
+
print(f"Rejected: {p.rejected[:80]}...")
|
|
170
|
+
print(f"Chosen: {p.chosen[:80] if p.chosen else 'None'}...")
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Output:**
|
|
174
|
+
```
|
|
175
|
+
Prompt: User: What is Python?...
|
|
176
|
+
Rejected: Python is a type of snake....
|
|
177
|
+
Chosen: Python is a high-level programming language created by Guido van Rossum....
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### 3. Feed to TRL for Training
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from trl import DPOTrainer
|
|
184
|
+
|
|
185
|
+
trainer = DPOTrainer(
|
|
186
|
+
model=model,
|
|
187
|
+
train_dataset=pairs, # SigX output works directly with TRL
|
|
188
|
+
)
|
|
189
|
+
trainer.train()
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Installation
|
|
195
|
+
|
|
196
|
+
> PyPI release coming soon. For now, install from source:
|
|
197
|
+
|
|
198
|
+
| Install Command | Includes |
|
|
199
|
+
|:---|:---|
|
|
200
|
+
| `pip install git+https://github.com/Fengrru/sigx.git` | Core (numpy, scikit-learn) |
|
|
201
|
+
| `pip install "sigx[llm] @ git+https://github.com/Fengrru/sigx.git"` | + OpenAI API for LLMExtractor |
|
|
202
|
+
| `pip install "sigx[wildchat] @ git+https://github.com/Fengrru/sigx.git"` | + HuggingFace datasets for WildChat |
|
|
203
|
+
| `git clone https://github.com/Fengrru/sigx.git && pip install -e ".[dev]"` | + pytest, ruff, mypy, pyright |
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Signal Types
|
|
208
|
+
|
|
209
|
+
SigX detects five categories of implicit feedback:
|
|
210
|
+
|
|
211
|
+
| Signal | Trigger Pattern | Confidence Range | Training Implication |
|
|
212
|
+
|:---|:---|:---:|:---|
|
|
213
|
+
| `rephrase` | User re-asks similar question (TF-IDF cosine sim) | 0.60 – 1.00 | Previous response → `rejected` |
|
|
214
|
+
| `correction` | "Actually I meant...", "No, that's not..." | 0.60 – 0.95 | Model output → `rejected` |
|
|
215
|
+
| `negative` | "That's wrong", "Not helpful" | 0.65 – 0.95 | Explicit dissatisfaction → `rejected` |
|
|
216
|
+
| `positive` | "Thanks!", "Exactly what I needed" | 0.60 – 0.90 | Explicit satisfaction → `chosen` / `label=True` |
|
|
217
|
+
| `abandon` | "Never mind", "I give up", trailing assistant | 0.35 – 0.90 | User gave up → `rejected` |
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Extractors
|
|
222
|
+
|
|
223
|
+
### RephraseDetector
|
|
224
|
+
|
|
225
|
+
Detects when users rephrase or repeat a question using TF-IDF cosine similarity.
|
|
226
|
+
For CJK text (Chinese/Japanese/Korean) it automatically switches to character
|
|
227
|
+
n-grams, since whitespace tokenization does not apply to those languages.
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
from sigx import RephraseDetector
|
|
231
|
+
|
|
232
|
+
detector = RephraseDetector(
|
|
233
|
+
similarity_threshold=0.6, # Cosine similarity threshold
|
|
234
|
+
min_turn_length=20, # Skip user turns shorter than this (chars)
|
|
235
|
+
skip_acknowledgments=True, # Skip "thanks", "ok", etc.
|
|
236
|
+
)
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### SentimentDetector
|
|
240
|
+
|
|
241
|
+
Hybrid regex + ML detection with 41 built-in patterns for corrections, negatives, positives, and sarcasm.
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
from sigx import SentimentDetector
|
|
245
|
+
|
|
246
|
+
# Rule-only mode (default, no extra deps)
|
|
247
|
+
detector = SentimentDetector(min_confidence=0.6)
|
|
248
|
+
|
|
249
|
+
# ML-enhanced mode (broader coverage)
|
|
250
|
+
detector = SentimentDetector(use_ml=True, min_confidence=0.5)
|
|
251
|
+
detector.fit(
|
|
252
|
+
texts=["that's wrong", "thanks!", "actually I meant..."],
|
|
253
|
+
labels=["negative", "positive", "correction"],
|
|
254
|
+
)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### AbandonDetector
|
|
258
|
+
|
|
259
|
+
Detects when users give up via explicit patterns or conversation structure analysis.
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
from sigx import AbandonDetector
|
|
263
|
+
|
|
264
|
+
detector = AbandonDetector(
|
|
265
|
+
min_assistant_length=300, # Min chars for trailing-assistant
|
|
266
|
+
min_turns=3, # Min conversation length
|
|
267
|
+
require_unanswered_question=True, # Only flag if user asked a question
|
|
268
|
+
)
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### LLMExtractor
|
|
272
|
+
|
|
273
|
+
High-accuracy classification using any OpenAI-compatible LLM.
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
from sigx import LLMExtractor
|
|
277
|
+
|
|
278
|
+
detector = LLMExtractor(
|
|
279
|
+
model="gpt-4o-mini", # Or "qwen2.5-7b-instruct" via vLLM
|
|
280
|
+
base_url=None, # Defaults to OpenAI; set for Ollama/vLLM
|
|
281
|
+
api_key="sk-...", # Or set OPENAI_API_KEY env var
|
|
282
|
+
min_confidence=0.6,
|
|
283
|
+
)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Custom Extractors
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
from sigx.extractors import BaseExtractor
|
|
290
|
+
from sigx.types import Signal
|
|
291
|
+
|
|
292
|
+
class ToxicityExtractor(BaseExtractor):
|
|
293
|
+
name = "toxicity"
|
|
294
|
+
|
|
295
|
+
def extract(self, conversation: dict) -> list[Signal]:
|
|
296
|
+
signals = []
|
|
297
|
+
# Your detection logic here
|
|
298
|
+
return signals
|
|
299
|
+
|
|
300
|
+
pipeline = Pipeline([ToxicityExtractor()])
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## DPO Chosen Strategies
|
|
306
|
+
|
|
307
|
+
A key innovation: SigX automatically infers the `chosen` response from subsequent conversation turns.
|
|
308
|
+
|
|
309
|
+
| Strategy | Behavior | Use Case |
|
|
310
|
+
|:---|:---|:---|
|
|
311
|
+
| `subsequent` *(default)* | Find positive turn after negative signal; use assistant response before it as `chosen` | Best quality |
|
|
312
|
+
| `last_assistant` | Use the final assistant response as `chosen` | Simple fallback |
|
|
313
|
+
| `none` | `chosen=None` | Backward compatible |
|
|
314
|
+
|
|
315
|
+
```python
|
|
316
|
+
from sigx import Pipeline, CHOSEN_SUBSEQUENT, CHOSEN_NONE
|
|
317
|
+
|
|
318
|
+
# Default: smart chosen inference
|
|
319
|
+
pipeline = Pipeline(extractors=[...], chosen_strategy=CHOSEN_SUBSEQUENT)
|
|
320
|
+
|
|
321
|
+
# Backward compatible: no chosen inference
|
|
322
|
+
pipeline = Pipeline(extractors=[...], chosen_strategy=CHOSEN_NONE)
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Data Loading
|
|
328
|
+
|
|
329
|
+
SigX supports multiple conversation formats:
|
|
330
|
+
|
|
331
|
+
```python
|
|
332
|
+
from sigx import load_conversations, load_wildchat, stream_wildchat
|
|
333
|
+
|
|
334
|
+
# ShareGPT format
|
|
335
|
+
convos = load_conversations("sharegpt_data.json", format="sharegpt")
|
|
336
|
+
|
|
337
|
+
# OpenAI chat format
|
|
338
|
+
convos = load_conversations("openai_data.json", format="openai")
|
|
339
|
+
|
|
340
|
+
# Generic JSONL
|
|
341
|
+
convos = load_conversations("logs.jsonl", format="jsonl")
|
|
342
|
+
|
|
343
|
+
# WildChat from HuggingFace (1M+ conversations)
|
|
344
|
+
convos = load_wildchat(n=1000)
|
|
345
|
+
|
|
346
|
+
# Stream large datasets (memory efficient)
|
|
347
|
+
for convo in stream_wildchat(n=10000):
|
|
348
|
+
signals = pipeline.run([convo])
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## Output Formats
|
|
354
|
+
|
|
355
|
+
### DPO (Direct Preference Optimization)
|
|
356
|
+
|
|
357
|
+
```python
|
|
358
|
+
pairs = pipeline.to_dpo(conversations)
|
|
359
|
+
# List[PreferencePair] — compatible with TRL's DPOTrainer
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### KTO (Kahneman-Tversky Optimization)
|
|
363
|
+
|
|
364
|
+
```python
|
|
365
|
+
examples = pipeline.to_kto(conversations)
|
|
366
|
+
# List[KTOExample] — binary labels: True (desirable), False (undesirable)
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Rejection Sampling
|
|
370
|
+
|
|
371
|
+
```python
|
|
372
|
+
pairs = pipeline.to_rejection(conversations)
|
|
373
|
+
# List[dict] with keys: prompt, rejected, signal_type, confidence
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## End-to-End Training Pipeline
|
|
379
|
+
|
|
380
|
+
```mermaid
|
|
381
|
+
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#e3f2fd', 'primaryTextColor': '#1565c0', 'primaryBorderColor': '#1565c0', 'lineColor': '#90a4ae', 'secondaryColor': '#fff3e0', 'tertiaryColor': '#e8f5e9'}}}%%
|
|
382
|
+
|
|
383
|
+
graph LR
|
|
384
|
+
subgraph INPUT [" "]
|
|
385
|
+
direction TB
|
|
386
|
+
I1["JSONL"]
|
|
387
|
+
I2["ShareGPT"]
|
|
388
|
+
I3["WildChat"]
|
|
389
|
+
I4["OpenAI"]
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
subgraph SIGX ["SigX"]
|
|
393
|
+
direction LR
|
|
394
|
+
S1["Extract"] --> S2["Filter"] --> S3["Convert"]
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
subgraph OUTPUT [" "]
|
|
398
|
+
direction TB
|
|
399
|
+
O1["DPO Pairs"]
|
|
400
|
+
O2["KTO Examples"]
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
INPUT --> SIGX --> OUTPUT
|
|
404
|
+
|
|
405
|
+
style INPUT fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#0d47a1
|
|
406
|
+
style SIGX fill:#fff3e0,stroke:#e65100,stroke-width:2px,color:#bf360c
|
|
407
|
+
style OUTPUT fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#1b5e20
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
```python
|
|
411
|
+
from sigx import Pipeline, SentimentDetector, RephraseDetector, AbandonDetector
|
|
412
|
+
from sigx.io import load_wildchat
|
|
413
|
+
|
|
414
|
+
# 1. Configure pipeline
|
|
415
|
+
pipeline = Pipeline([
|
|
416
|
+
SentimentDetector(min_confidence=0.6),
|
|
417
|
+
RephraseDetector(similarity_threshold=0.6),
|
|
418
|
+
AbandonDetector(min_turns=3),
|
|
419
|
+
])
|
|
420
|
+
|
|
421
|
+
# 2. Load conversation data
|
|
422
|
+
convos = load_wildchat(n=10000)
|
|
423
|
+
|
|
424
|
+
# 3. Convert to DPO training pairs
|
|
425
|
+
dpo_pairs = pipeline.to_dpo(convos)
|
|
426
|
+
|
|
427
|
+
# 4. Feed directly to TRL
|
|
428
|
+
from trl import DPOTrainer
|
|
429
|
+
trainer = DPOTrainer(model=model, train_dataset=dpo_pairs)
|
|
430
|
+
trainer.train()
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## Evaluation
|
|
436
|
+
|
|
437
|
+
Evaluate extraction quality against a labeled benchmark:
|
|
438
|
+
|
|
439
|
+
```python
|
|
440
|
+
metrics = pipeline.evaluate("benchmark.json")
|
|
441
|
+
|
|
442
|
+
print(f"Overall F1: {metrics['overall']['f1']:.4f}")
|
|
443
|
+
# Per-type breakdown
|
|
444
|
+
for signal_type, m in metrics["per_type"].items():
|
|
445
|
+
print(f" {signal_type}: P={m['precision']:.4f} R={m['recall']:.4f} F1={m['f1']:.4f}")
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Benchmark Results
|
|
449
|
+
|
|
450
|
+
Evaluated on a 20-conversation benchmark with regex-based extractors (default settings, no LLM):
|
|
451
|
+
|
|
452
|
+

|
|
453
|
+
|
|
454
|
+
> Reproduce with `pipeline.evaluate("tests/benchmark.json")`; regenerate the chart with `python scripts/generate_assets.py`. Adding `LLMExtractor` significantly improves recall on `negative` and `rephrase` types.
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
## Architecture
|
|
459
|
+
|
|
460
|
+
```
|
|
461
|
+
sigx/
|
|
462
|
+
├── pipeline.py # Orchestration: extract → filter → convert
|
|
463
|
+
├── types.py # Core dataclasses: Signal, PreferencePair, KTOExample
|
|
464
|
+
├── exceptions.py # Custom exception hierarchy
|
|
465
|
+
├── extractors/
|
|
466
|
+
│ ├── base.py # Abstract BaseExtractor
|
|
467
|
+
│ ├── rephrase.py # TF-IDF cosine similarity rephrase detection
|
|
468
|
+
│ ├── sentiment.py # Regex + optional ML sentiment classifier
|
|
469
|
+
│ ├── abandon.py # Frustration pattern + trailing-assistant detection
|
|
470
|
+
│ └── llm.py # OpenAI-compatible LLM classifier
|
|
471
|
+
├── filters/
|
|
472
|
+
│ └── quality.py # Confidence threshold, dedup, per-conv limits
|
|
473
|
+
├── converters/
|
|
474
|
+
│ └── preference.py # DPO / KTO / rejection-sampling converters
|
|
475
|
+
└── io/
|
|
476
|
+
└── loader.py # ShareGPT, OpenAI, WildChat, JSONL loaders
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## Research Background
|
|
482
|
+
|
|
483
|
+
SigX is built on growing evidence that **implicit feedback from real user interactions produces better alignment data** than synthetic alternatives:
|
|
484
|
+
|
|
485
|
+
| Paper | Institution | Year | Key Finding |
|
|
486
|
+
|:---|:---|:---:|:---|
|
|
487
|
+
| [WildFeedback](https://arxiv.org/abs/2408.15549) | Microsoft Research | 2024 | 20K preference pairs from 148K ChatGPT conversations outperformed UltraFeedback on AlpacaEval 2, Arena-Hard, MT-Bench |
|
|
488
|
+
|
|
489
|
+
**The key insight**: Real user feedback captures nuance that synthetic data misses, and the signals are already present in existing chat logs — they just need to be extracted.
|
|
490
|
+
|
|
491
|
+
### How SigX Relates
|
|
492
|
+
|
|
493
|
+
| Project | Focus | SigX Connection |
|
|
494
|
+
|:---|:---|:---|
|
|
495
|
+
| [WildFeedback](https://arxiv.org/abs/2408.15549) | Extracting preference pairs from in-situ feedback | SigX implements the extraction pipeline in Python |
|
|
496
|
+
| [TRL](https://github.com/huggingface/trl) | Post-training (DPO/KTO/PPO) | SigX output is TRL-compatible |
|
|
497
|
+
| [VeRL](https://github.com/volcengine/verl) | RLHF framework | SigX output works with VeRL |
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## Development
|
|
502
|
+
|
|
503
|
+
```bash
|
|
504
|
+
git clone https://github.com/fengrru/sigx.git
|
|
505
|
+
cd sigx
|
|
506
|
+
pip install -e ".[dev]"
|
|
507
|
+
|
|
508
|
+
# Run tests
|
|
509
|
+
pytest # 92 tests
|
|
510
|
+
pytest --cov=sigx # With coverage
|
|
511
|
+
|
|
512
|
+
# Lint
|
|
513
|
+
ruff check . # Lint
|
|
514
|
+
ruff format . # Format
|
|
515
|
+
|
|
516
|
+
# Type check
|
|
517
|
+
mypy sigx/ # mypy
|
|
518
|
+
pyright # pyright
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
---
|
|
522
|
+
|
|
523
|
+
## Citation
|
|
524
|
+
|
|
525
|
+
```bibtex
|
|
526
|
+
@software{sigx2026,
|
|
527
|
+
title = {{SigX: Implicit Feedback Signal Extraction for LLM Alignment}},
|
|
528
|
+
author = {fengrru},
|
|
529
|
+
license = {MIT},
|
|
530
|
+
url = {https://github.com/fengrru/sigx},
|
|
531
|
+
year = {2026},
|
|
532
|
+
}
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
---
|
|
536
|
+
|
|
537
|
+
## License
|
|
538
|
+
|
|
539
|
+
MIT License — see [LICENSE](LICENSE) for details.
|
|
540
|
+
|
|
541
|
+
---
|
|
542
|
+
|
|
543
|
+
<div align="center">
|
|
544
|
+
|
|
545
|
+
**Built with care for the LLM alignment community.**
|
|
546
|
+
|
|
547
|
+
[Report Bug](https://github.com/fengrru/sigx/issues) · [Request Feature](https://github.com/fengrru/sigx/issues) · [Contributing Guide](CONTRIBUTING.md)
|
|
548
|
+
|
|
549
|
+
</div>
|