feedloop 0.1.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 (42) hide show
  1. feedloop-0.1.0/.claude/settings.local.json +9 -0
  2. feedloop-0.1.0/.gitignore +19 -0
  3. feedloop-0.1.0/LICENSE +21 -0
  4. feedloop-0.1.0/Makefile +18 -0
  5. feedloop-0.1.0/PKG-INFO +41 -0
  6. feedloop-0.1.0/PLAN.md +265 -0
  7. feedloop-0.1.0/README.md +17 -0
  8. feedloop-0.1.0/demo.py +40 -0
  9. feedloop-0.1.0/frontend/index.html +12 -0
  10. feedloop-0.1.0/frontend/package-lock.json +2990 -0
  11. feedloop-0.1.0/frontend/package.json +23 -0
  12. feedloop-0.1.0/frontend/src/App.tsx +55 -0
  13. feedloop-0.1.0/frontend/src/api.ts +49 -0
  14. feedloop-0.1.0/frontend/src/components/ComparisonView.tsx +62 -0
  15. feedloop-0.1.0/frontend/src/components/EmptyState.tsx +19 -0
  16. feedloop-0.1.0/frontend/src/components/ExportPanel.tsx +35 -0
  17. feedloop-0.1.0/frontend/src/components/FeedbackBar.tsx +32 -0
  18. feedloop-0.1.0/frontend/src/components/ProgressBar.tsx +19 -0
  19. feedloop-0.1.0/frontend/src/components/PromptHeader.tsx +12 -0
  20. feedloop-0.1.0/frontend/src/components/ResponseCard.tsx +17 -0
  21. feedloop-0.1.0/frontend/src/hooks/useComparison.ts +31 -0
  22. feedloop-0.1.0/frontend/src/main.tsx +10 -0
  23. feedloop-0.1.0/frontend/src/styles.css +351 -0
  24. feedloop-0.1.0/frontend/src/vite-env.d.ts +1 -0
  25. feedloop-0.1.0/frontend/tsconfig.json +21 -0
  26. feedloop-0.1.0/frontend/tsconfig.tsbuildinfo +1 -0
  27. feedloop-0.1.0/frontend/vite.config.ts +15 -0
  28. feedloop-0.1.0/preferences.jsonl +3 -0
  29. feedloop-0.1.0/pyproject.toml +43 -0
  30. feedloop-0.1.0/src/feedloop/__init__.py +155 -0
  31. feedloop-0.1.0/src/feedloop/__main__.py +44 -0
  32. feedloop-0.1.0/src/feedloop/_config.py +6 -0
  33. feedloop-0.1.0/src/feedloop/_db.py +172 -0
  34. feedloop-0.1.0/src/feedloop/_export.py +30 -0
  35. feedloop-0.1.0/src/feedloop/_models.py +30 -0
  36. feedloop-0.1.0/src/feedloop/_routes.py +99 -0
  37. feedloop-0.1.0/src/feedloop/_server.py +97 -0
  38. feedloop-0.1.0/src/feedloop/py.typed +0 -0
  39. feedloop-0.1.0/tests/__init__.py +0 -0
  40. feedloop-0.1.0/tests/test_db.py +106 -0
  41. feedloop-0.1.0/tests/test_export.py +64 -0
  42. feedloop-0.1.0/tests/test_routes.py +111 -0
@@ -0,0 +1,9 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(pip3 install:*)",
5
+ "Bash(python3 -c \":*)",
6
+ "Bash(source /Users/rammuthiah/Documents/Coding/hitl/.venv/bin/activate)"
7
+ ]
8
+ }
9
+ }
@@ -0,0 +1,19 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .eggs/
8
+ *.egg
9
+ .venv/
10
+ venv/
11
+ .env
12
+ *.db
13
+ *.db-wal
14
+ *.db-shm
15
+ node_modules/
16
+ src/feedloop/_static/*
17
+ !src/feedloop/_static/.gitkeep
18
+ .pytest_cache/
19
+ .mypy_cache/
feedloop-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ram Muthiah
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.
@@ -0,0 +1,18 @@
1
+ .PHONY: dev-frontend dev-backend build test clean
2
+
3
+ dev-frontend:
4
+ cd frontend && npm run dev
5
+
6
+ dev-backend:
7
+ uvicorn feedloop._server:app --reload
8
+
9
+ build:
10
+ cd frontend && npm run build
11
+ python -m build
12
+
13
+ test:
14
+ pytest tests/ -v
15
+
16
+ clean:
17
+ rm -rf dist/ build/ *.egg-info
18
+ find . -type d -name __pycache__ -exec rm -rf {} +
@@ -0,0 +1,41 @@
1
+ Metadata-Version: 2.4
2
+ Name: feedloop
3
+ Version: 0.1.0
4
+ Summary: The fastest way to collect human preference data for LLMs
5
+ Project-URL: Homepage, https://github.com/rammuthiah/feedloop
6
+ Project-URL: Repository, https://github.com/rammuthiah/feedloop
7
+ Author: Ram Muthiah
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: dpo,human-feedback,llm,preference-data,rlhf
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Requires-Python: >=3.10
20
+ Requires-Dist: fastapi>=0.100
21
+ Requires-Dist: pydantic>=2.0
22
+ Requires-Dist: uvicorn[standard]>=0.20
23
+ Description-Content-Type: text/markdown
24
+
25
+ # feedloop
26
+
27
+ The fastest way to collect human preference data for LLMs.
28
+
29
+ ```python
30
+ import feedloop
31
+
32
+ feedloop.start()
33
+
34
+ feedloop.compare(
35
+ prompt="Explain quantum computing",
36
+ outputs=["Response A", "Response B"],
37
+ )
38
+
39
+ # Rate in the browser, then export
40
+ feedloop.export("preferences.jsonl")
41
+ ```
feedloop-0.1.0/PLAN.md ADDED
@@ -0,0 +1,265 @@
1
+ # HITL Microservice — Implementation Plan
2
+
3
+ ## Context
4
+
5
+ AI developers lack a frictionless way to collect human preference data during model iteration. Existing tools are either cloud-heavy (LangSmith, Humanloop), enterprise-scale (Labelbox, Scale AI), or platform-y (Argilla). This project fills the gap: a local-first, pip-installable tool that lets a solo developer go from "bad model output" to "DPO training example" in under a minute.
6
+
7
+ **Positioning:** "The SQLite of human feedback" — zero config, instant value.
8
+
9
+ ---
10
+
11
+ ## Distribution Strategy
12
+
13
+ **Both pip package + open-source GitHub repo.** pip for frictionless install, GitHub for trust and contributions.
14
+
15
+ - Package name: `feedloop` (appears available on PyPI — register early)
16
+ - Import name: `feedloop` regardless of pip name
17
+ - License: MIT
18
+ - Versioning: SemVer, start at `0.1.0`
19
+
20
+ ---
21
+
22
+ ## Architecture
23
+
24
+ ```
25
+ Developer's Python script
26
+
27
+ │ feedloop.start() → spawns FastAPI in a daemon thread
28
+ │ feedloop.compare(...) → inserts row into SQLite, returns immediately
29
+ │ feedloop.export(...) → reads SQLite, writes JSONL
30
+
31
+ ┌────▼──────────────────────────────────┐
32
+ │ FastAPI Server (background thread) │
33
+ │ │
34
+ │ GET /api/pending → next comparison │
35
+ │ POST /api/feedback → record choice │
36
+ │ GET /api/stats → progress │
37
+ │ GET /api/export → download JSONL │
38
+ │ GET /* → React static │
39
+ │ │
40
+ │ SQLite (~/.feedloop/feedloop.db, WAL mode) │
41
+ └────┬──────────────────────────────────┘
42
+
43
+ ┌────▼──────────────────────────────────┐
44
+ │ React UI (static files) │
45
+ │ Polls /api/pending every 1s │
46
+ │ Side-by-side comparison + shortcuts │
47
+ └────────────────────────────────────────┘
48
+ ```
49
+
50
+ **Key design decisions:**
51
+ - Background daemon thread (not subprocess) — shares SQLite, dies when script exits
52
+ - SQLite WAL mode for concurrent reads across threads
53
+ - `atexit` handler for graceful shutdown
54
+ - Position randomization (shuffle A/B) to reduce bias — stored per row for correct export
55
+
56
+ ---
57
+
58
+ ## Project Structure
59
+
60
+ ```
61
+ feedloop/
62
+ ├── pyproject.toml
63
+ ├── LICENSE (MIT)
64
+ ├── README.md
65
+ ├── Makefile
66
+ ├── src/
67
+ │ └── feedloop/
68
+ │ ├── __init__.py # Public API: start(), compare(), export(), wait(), stop(), status()
69
+ │ ├── __main__.py # python -m feedloop CLI entry
70
+ │ ├── _server.py # FastAPI app + uvicorn background thread
71
+ │ ├── _db.py # SQLite schema, CRUD, WAL mode
72
+ │ ├── _models.py # Pydantic models
73
+ │ ├── _routes.py # API endpoints
74
+ │ ├── _export.py # SQLite → JSONL
75
+ │ ├── _config.py # Defaults (port, db path)
76
+ │ ├── _static/ # React build output (bundled in wheel)
77
+ │ └── py.typed
78
+ ├── frontend/
79
+ │ ├── package.json
80
+ │ ├── vite.config.ts # outputs to ../src/feedloop/_static/
81
+ │ ├── tsconfig.json
82
+ │ ├── index.html
83
+ │ └── src/
84
+ │ ├── main.tsx
85
+ │ ├── App.tsx
86
+ │ ├── api.ts
87
+ │ ├── components/
88
+ │ │ ├── ComparisonView.tsx
89
+ │ │ ├── ResponseCard.tsx
90
+ │ │ ├── PromptHeader.tsx
91
+ │ │ ├── FeedbackBar.tsx
92
+ │ │ ├── ProgressBar.tsx
93
+ │ │ ├── ExportPanel.tsx
94
+ │ │ └── EmptyState.tsx
95
+ │ └── hooks/
96
+ │ └── useComparison.ts
97
+ └── tests/
98
+ ├── test_db.py
99
+ ├── test_routes.py
100
+ ├── test_sdk.py
101
+ └── test_export.py
102
+ ```
103
+
104
+ ---
105
+
106
+ ## SDK API Design
107
+
108
+ ```python
109
+ import feedloop
110
+
111
+ # Launch server + open browser
112
+ feedloop.start(port=7856, db_path=None, open_browser=True)
113
+
114
+ # Submit comparisons (non-blocking)
115
+ cid = feedloop.compare(
116
+ prompt="Explain quantum computing",
117
+ outputs=["Response A text", "Response B text"],
118
+ metadata={"model_a": "gpt-4", "model_b": "claude-3"}
119
+ )
120
+
121
+ # Optionally block until rated
122
+ result = feedloop.wait(cid, timeout=60)
123
+
124
+ # Check progress
125
+ feedloop.status() # {"pending": 5, "completed": 10, "total": 15}
126
+
127
+ # Export DPO-formatted data
128
+ feedloop.export("preferences.jsonl", session_id=None, format="dpo")
129
+
130
+ # Cleanup
131
+ feedloop.stop()
132
+ ```
133
+
134
+ **Example end-to-end usage:**
135
+ ```python
136
+ import feedloop
137
+
138
+ feedloop.start()
139
+
140
+ for prompt in test_prompts:
141
+ a = model_a.generate(prompt)
142
+ b = model_b.generate(prompt)
143
+ feedloop.compare(prompt=prompt, outputs=[a, b])
144
+
145
+ input("Rate all comparisons in the browser, then press Enter...")
146
+ feedloop.export("my_preferences.jsonl")
147
+ ```
148
+
149
+ ---
150
+
151
+ ## Data Model (SQLite)
152
+
153
+ ```sql
154
+ CREATE TABLE comparisons (
155
+ id TEXT PRIMARY KEY, -- UUID
156
+ session_id TEXT NOT NULL,
157
+ prompt TEXT NOT NULL,
158
+ output_a TEXT NOT NULL,
159
+ output_b TEXT NOT NULL,
160
+ display_order TEXT NOT NULL, -- 'ab' or 'ba' (position randomization)
161
+ status TEXT NOT NULL DEFAULT 'pending', -- pending | completed | skipped
162
+ chosen TEXT, -- 'a' | 'b' | NULL
163
+ created_at TEXT NOT NULL,
164
+ completed_at TEXT,
165
+ metadata TEXT -- JSON blob
166
+ );
167
+ ```
168
+
169
+ **DPO export format:**
170
+ ```json
171
+ {"prompt": "...", "chosen": "...", "rejected": "..."}
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Frontend Design
177
+
178
+ Three states:
179
+ 1. **Empty** — "Waiting for comparisons... Run `feedloop.compare()` in your script." (polls every 1s)
180
+ 2. **Comparison** — Side-by-side A/B with keyboard shortcuts (`1` = A, `2` = B, `S` = skip), progress bar, markdown rendering
181
+ 3. **Done** — Summary stats + download JSONL button
182
+
183
+ Tech: Vite + React + TypeScript, minimal CSS (no Tailwind), `react-markdown` for output rendering.
184
+
185
+ ---
186
+
187
+ ## Build & Packaging
188
+
189
+ - **Build backend:** Hatchling (handles `src/` layout, includes `_static/` in wheel)
190
+ - **Frontend build:** `cd frontend && npm run build` → outputs to `src/feedloop/_static/`
191
+ - **Python build:** `python -m build` → wheel includes static files
192
+ - **Dependencies:** `fastapi>=0.100`, `uvicorn[standard]>=0.20`, `pydantic>=2.0`
193
+ - **Python target:** `>=3.10` (for `X | Y` union syntax)
194
+
195
+ ---
196
+
197
+ ## Phased Build Order
198
+
199
+ ### Phase 1: Foundation
200
+ - [x] `pyproject.toml` + directory structure
201
+ - [x] `_db.py` — SQLite schema, WAL mode, CRUD functions
202
+ - [x] `_models.py` — Pydantic models
203
+ - [x] `tests/test_db.py`
204
+
205
+ ### Phase 2: Backend API
206
+ - [x] `_routes.py` — API endpoints
207
+ - [x] `_server.py` — FastAPI app, CORS, static mount
208
+ - [x] `_export.py` — JSONL export
209
+ - [x] `tests/test_routes.py`
210
+
211
+ ### Phase 3: SDK
212
+ - [x] `__init__.py` — public API (start, compare, export, wait, stop, status)
213
+ - [x] Background thread server launch
214
+ - [x] `__main__.py` — CLI entry
215
+ - [ ] `tests/test_sdk.py`
216
+
217
+ ### Phase 4: Frontend
218
+ - [x] Scaffold Vite + React + TS, configure output path
219
+ - [x] `api.ts` + `useComparison` polling hook
220
+ - [x] ComparisonView, ResponseCard, FeedbackBar components
221
+ - [x] ProgressBar, EmptyState, ExportPanel
222
+ - [x] Keyboard shortcuts + transitions
223
+ - [x] Styling
224
+
225
+ ### Phase 5: Integration & Polish
226
+ - [x] Build frontend into `_static/`, verify in wheel
227
+ - [x] End-to-end test: start → compare → rate → export
228
+ - [x] Position randomization (A/B shuffle)
229
+ - [x] README with quickstart
230
+
231
+ ### Phase 6: Distribution
232
+ - [ ] TestPyPI upload + fresh venv validation
233
+ - [ ] GitHub repo + MIT license + CI
234
+ - [ ] PyPI publish
235
+
236
+ ---
237
+
238
+ ## Future Roadmap
239
+
240
+ ### v1.1 — Uncertainty-based triggering
241
+ - SDK accepts an `uncertainty` score per comparison
242
+ - Only surfaces comparisons to the human when score exceeds a threshold
243
+ - Aligns with active learning / cost-efficient RLHF
244
+
245
+ ### v1.5 — Training loop helpers
246
+ - "Generate training script" button in UI
247
+ - "Re-run prompts with new model" helper
248
+ - Light guidance toward DPO fine-tuning without owning compute
249
+
250
+ ### v2.0 — Extended formats
251
+ - Support for >2 outputs (ranking, not just pairwise)
252
+ - Single-output Likert-scale rating (reward model data)
253
+ - Export formats: `anthropic`, `openai`, `trl`
254
+ - WebSocket for real-time updates (replace polling)
255
+
256
+ ---
257
+
258
+ ## Verification
259
+
260
+ 1. `pip install -e .` in a fresh venv
261
+ 2. Run `python demo.py` — browser opens, comparisons appear
262
+ 3. Rate all comparisons via keyboard shortcuts (`1`, `2`, `S`)
263
+ 4. Press Enter — exports to `preferences.jsonl` in DPO format
264
+ 5. `python -m build` — verify wheel contains `_static/index.html`
265
+ 6. `pip install dist/feedloop-0.1.0-py3-none-any.whl` in clean venv — verify it works
@@ -0,0 +1,17 @@
1
+ # feedloop
2
+
3
+ The fastest way to collect human preference data for LLMs.
4
+
5
+ ```python
6
+ import feedloop
7
+
8
+ feedloop.start()
9
+
10
+ feedloop.compare(
11
+ prompt="Explain quantum computing",
12
+ outputs=["Response A", "Response B"],
13
+ )
14
+
15
+ # Rate in the browser, then export
16
+ feedloop.export("preferences.jsonl")
17
+ ```
feedloop-0.1.0/demo.py ADDED
@@ -0,0 +1,40 @@
1
+ """Demo script — run this to try feedloop."""
2
+
3
+ import feedloop
4
+
5
+ feedloop.start()
6
+
7
+ # Add some sample comparisons
8
+ feedloop.compare(
9
+ prompt="Explain quantum computing to a 5-year-old",
10
+ outputs=[
11
+ "Quantum computing uses qubits that can be in superposition, meaning they can represent both 0 and 1 simultaneously. This allows quantum computers to process many possibilities at once, making them powerful for certain types of calculations.",
12
+ "Imagine you have a magic coin. A normal coin is either heads or tails. But a magic coin can be both at the same time! A quantum computer uses millions of these magic coins to solve really hard puzzles super fast.",
13
+ ],
14
+ metadata={"model_a": "gpt-4", "model_b": "claude-3"},
15
+ )
16
+
17
+ feedloop.compare(
18
+ prompt="Write a haiku about programming",
19
+ outputs=[
20
+ "Lines of code cascade\nSilicon dreams come alive\nBugs hide in the night",
21
+ "Semicolons lost\nThe compiler screams in pain\nStack overflow saves",
22
+ ],
23
+ )
24
+
25
+ feedloop.compare(
26
+ prompt="What is the meaning of life?",
27
+ outputs=[
28
+ "The meaning of life is a deeply philosophical question that has been debated for centuries. Different traditions offer different answers — from religious purpose to existentialist self-creation.",
29
+ "42. But seriously, the meaning of life is whatever you decide to make it. Find what brings you joy, help others when you can, and try to leave things better than you found them.",
30
+ ],
31
+ )
32
+
33
+ print("\n3 comparisons ready. Rate them in the browser!")
34
+ print("When done, press Enter to export.\n")
35
+
36
+ input()
37
+
38
+ count = feedloop.export("preferences.jsonl")
39
+ print(f"\nExported {count} preferences to preferences.jsonl")
40
+ feedloop.stop()
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>feedloop</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>