-
Notifications
You must be signed in to change notification settings - Fork 1
feat: sensitive-data redaction, smarter grouping, OpenAI-compatible LLM, config hygiene #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
NotYuSheng
merged 15 commits into
main
from
feat/data-sanitization-and-pipeline-hardening
Jun 23, 2026
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
d263530
feat: sensitive-data redaction, smarter grouping, OpenAI-compatible Lβ¦
NotYuSheng a9367c7
chore: remove deprecated script shims; fix remote name in docs
NotYuSheng 33a5cd5
docs: resolve conflicting consent wording in top warnings
NotYuSheng 7c143fe
docs: add Roadmap section (training techniques + tracked issues)
NotYuSheng 4e9cb91
docs: expand Roadmap into exploration vision across AI techniques
NotYuSheng 4c207ea
docs: expand Roadmap to full exploration backlog (#16-#38)
NotYuSheng 23b12c5
docs: state roadmap techniques without issue links
NotYuSheng e5b8d9c
fix: address PR review (span overlap, speaker prefix, LLM failure aboβ¦
NotYuSheng 9619e7f
refactor: local-first LLM config (no hardcoded model default, drop clβ¦
NotYuSheng bf75479
docs: add local LLM (LM Studio) setup + model suggestions by hardware
NotYuSheng b395054
fix: SG postal detector no longer matches the leading digits of an NRIC
NotYuSheng 0eba0e8
feat: ASCII parrot+wordmark startup banner and demo GIF
NotYuSheng d0e93b0
fix: redact all occurrences of an LLM span; default missing Telegram β¦
NotYuSheng 874e305
fix: merge overlapping redaction spans; guard unmatched optional id gβ¦
NotYuSheng 301ce6c
fix: honour --redact when scan skipped; prioritize split repair; hardβ¦
NotYuSheng File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # demo/ | ||
|
|
||
| Assets and the (dev-only) scripts used to generate the README banner/GIF. None of | ||
| this is needed to run Doppelganger β it's tooling for regenerating the visuals. | ||
|
|
||
| | File | What it is | | ||
| |------|------------| | ||
| | `parrot-mirror.jpg` | Source image for the mascot | | ||
| | `mascot.txt` | The parrot converted to braille ASCII (committed art) | | ||
| | `sample_export.json` | **Synthetic** Telegram export used by the demo (safe, fake PII) | | ||
| | `demo.gif` | The README demo (ingest + sensitive-data scan) | | ||
| | `img2ascii.py` | Convert an image to ASCII (brightness ramp) | | ||
| | `build_final.py` | Rebuild `ingest/banner.py` and `demo/demo.gif` | | ||
|
|
||
| ## Regenerating | ||
|
|
||
| These scripts need extra dev dependencies that the app itself does **not** require: | ||
|
|
||
| ```bash | ||
| pip install pillow pyfiglet # img2ascii.py / build_final.py | ||
| # plus the asciinema 'agg' renderer (https://github.com/asciinema/agg): | ||
| # cargo install --git https://github.com/asciinema/agg | ||
| # or download a release binary and set: export AGG=/path/to/agg | ||
| ``` | ||
|
|
||
| Then: | ||
|
|
||
| ```bash | ||
| python demo/img2ascii.py parrot-mirror.jpg 72 # preview the mascot conversion | ||
| python demo/build_final.py # rewrite the banner + demo.gif | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| #!/usr/bin/env python3 | ||
| """Build the final banner (ingest/banner.py) and demo GIF (demo/demo.gif). | ||
|
|
||
| Layout: parrot (left) + ansi_shadow "Doppel"/"ganger" (right), amber wordmark, | ||
| tagline centered beneath. Renders the GIF with agg's dracula theme. | ||
| """ | ||
| import json | ||
| import os | ||
| import subprocess | ||
|
|
||
| import pyfiglet | ||
|
|
||
| ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | ||
| PY = os.path.join(ROOT, "venv", "bin", "python") | ||
| AGG = os.environ.get("AGG", "agg") # asciinema agg on PATH; override with $AGG | ||
| GAP = " " | ||
| TAG = "fine-tune an LLM to write like you" | ||
| CMD = "python -m ingest --source telegram --input demo/sample_export.json" | ||
| AMBER, RESET = "\x1b[1;38;2;242;176;76m", "\x1b[0m" | ||
|
|
||
| with open(os.path.join(ROOT, "demo/mascot.txt"), encoding="utf-8") as _f: | ||
| parrot = _f.read().rstrip("\n").split("\n") | ||
| PW = max(len(l) for l in parrot) | ||
|
|
||
|
|
||
| def _fig(t): | ||
| ls = [l.rstrip() for l in pyfiglet.figlet_format(t, font="ansi_shadow", width=200).rstrip("\n").split("\n")] | ||
| while ls and not ls[-1].strip(): ls.pop() | ||
| while ls and not ls[0].strip(): ls.pop(0) | ||
| return ls | ||
|
|
||
|
|
||
| word = _fig("Doppel") + _fig("ganger") | ||
| TOP = (len(parrot) - len(word)) // 2 | ||
| TOTAL_W = PW + len(GAP) + max(len(l) for l in word) | ||
|
|
||
|
|
||
| def rows(on, off): | ||
| r = [] | ||
| for i, pl in enumerate(parrot): | ||
| wl = word[i - TOP] if 0 <= i - TOP < len(word) else "" | ||
| wl = f"{on}{wl}{off}" if wl else "" | ||
| r.append((pl.ljust(PW) + GAP + wl).rstrip()) | ||
| r.append("") | ||
| r.append(TAG.center(TOTAL_W).rstrip()) # tagline centred under the whole logo | ||
| return r | ||
|
|
||
|
|
||
| def write_banner_module(): | ||
| body = "\n".join(rows("<C>", "<R>")) # sentinels; colourised at runtime | ||
| mod = ( | ||
| '"""ASCII startup banner: a parrot in a mirror (it mimics your voice; the\n' | ||
| 'mirror is the doppelganger) beside the wordmark. The wordmark is amber via\n' | ||
| 'truecolor ANSI. Regenerate via demo/build_final.py.\n' | ||
| 'Set DOPPELGANGER_NO_BANNER=1 to silence it."""\n\n' | ||
| 'import os\n\n' | ||
| '_AMBER = "\\x1b[1;38;2;242;176;76m" # truecolor amber\n' | ||
| '_RESET = "\\x1b[0m"\n\n' | ||
| '_BANNER = r"""\n' + body + '\n"""\n\n\n' | ||
| 'def print_banner() -> None:\n' | ||
| ' if os.environ.get("DOPPELGANGER_NO_BANNER"):\n' | ||
| ' return\n' | ||
| ' print(_BANNER.replace("<C>", _AMBER).replace("<R>", _RESET) + "\\n")\n' | ||
| ) | ||
| open(os.path.join(ROOT, "ingest/banner.py"), "w", encoding="utf-8").write(mod) | ||
|
|
||
|
|
||
| def render_gif(): | ||
| env = dict(os.environ, LLM_VALIDATE="false", DOPPELGANGER_NO_BANNER="1") | ||
| out = subprocess.run([PY, *CMD.split()[1:]], cwd=ROOT, env=env, | ||
| capture_output=True, text=True, check=True) | ||
| report = ((out.stdout or "") + (out.stderr or "")).split("\n") | ||
|
|
||
| events, t = [], 0.0 | ||
| def emit(d, dt): | ||
| nonlocal t | ||
| t += dt | ||
| events.append([round(t, 3), "o", d]) | ||
| emit("\x1b[32m$\x1b[0m ", 0.3) | ||
| for ch in CMD: | ||
| emit(ch, 0.026) | ||
| emit("\r\n", 0.5) | ||
| for line in rows(AMBER, RESET) + report: | ||
| emit(line + "\r\n", 0.05) | ||
| emit("\x1b[32m$\x1b[0m ", 1.6) | ||
|
|
||
| cast = os.path.join(ROOT, "demo/demo.cast") | ||
| with open(cast, "w", encoding="utf-8") as f: | ||
| f.write(json.dumps({"version": 2, "width": 94, "height": 34}) + "\n") | ||
| for ev in events: | ||
| f.write(json.dumps(ev, ensure_ascii=False) + "\n") | ||
| subprocess.run([AGG, "--font-size", "18", "--theme", "dracula", cast, | ||
| os.path.join(ROOT, "demo/demo.gif")], | ||
| stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, text=True, check=True) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| write_banner_module() | ||
| render_gif() | ||
| print("=== layout preview ===") | ||
| print("\n".join(rows("", ""))) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| #!/usr/bin/env python3 | ||
| """Convert an image to ASCII art via a brightness ramp. | ||
|
|
||
| Usage: python demo/img2ascii.py PATH [cols] [--invert] | ||
| Transparent images are composited onto white first, so a dark subject on a | ||
| transparent background (e.g. an OpenMoji black glyph) renders as the dense end | ||
| of the ramp. | ||
| """ | ||
| import sys | ||
| from PIL import Image | ||
|
|
||
| RAMP = " .:-=+*#%@" | ||
|
|
||
|
|
||
| def to_ascii(path: str, cols: int = 42, invert: bool = False) -> str: | ||
| img = Image.open(path) | ||
| if img.mode in ("RGBA", "LA") or (img.mode == "P" and "transparency" in img.info): | ||
| bg = Image.new("RGBA", img.size, (255, 255, 255, 255)) | ||
| img = Image.alpha_composite(bg, img.convert("RGBA")) | ||
| g = img.convert("L") | ||
| rows = max(1, int(cols * (g.height / g.width) * 0.50)) | ||
| g = g.resize((cols, rows)) | ||
| px = g.load() | ||
| ramp = RAMP[::-1] if invert else RAMP | ||
| lines = [] | ||
| for y in range(rows): | ||
| lines.append( | ||
| "".join( | ||
| ramp[int((255 - px[x, y]) / 255 * (len(ramp) - 1))] for x in range(cols) | ||
| ).rstrip() | ||
| ) | ||
| return "\n".join(lines) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| args = [a for a in sys.argv[1:] if a != "--invert"] | ||
| if not args: | ||
| print("Usage: python demo/img2ascii.py PATH [cols] [--invert]", file=sys.stderr) | ||
| raise SystemExit(2) | ||
| print(to_ascii(args[0], int(args[1]) if len(args) > 1 else 42, "--invert" in sys.argv)) | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| β β β β β β β β β β β β β β β β£β£β‘ | ||
| β β β β β β β β β£β£€β Άβ β β β β β£Ώβ£Ώβ£Ώβ£Ώβ£·β£Άβ£€β£β‘ | ||
| β β β β β β£ β‘Άβ β β β β β β β β β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£·β£β‘ | ||
| β β β β£ β‘Ύβ β β β β β β β β β β β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£ | ||
| β β ⣴β’⣀⣴⣢⣦⣀β‘β β β β β β β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ‘Ώβ β β β β β »β’·β‘ | ||
| β β£Έβ£Ώβ£Ώβ£Ώβ Ώβ ⠻⣿⣿⣦β β β’β£β£€β£β‘β »β’Ώβ£Ώβ β β£ β Άβ β β’Άβ£β β | ||
| β β£Ώβ£Ώβ£Ώβ β β Έβ Ώβ β£Ώβ£Ώβ ⣴β β β β£Ώβ£Ώβ£·β£β β β’Έβ β β β Ώβ β’Ήβ‘ | ||
| β’Έβ£Ώβ£Ώβ£Ώβ‘β β β β’β£Ώβ β£Όβ β Έβ β β£Ώβ£Ώβ£β£Ώβ£β β Έβ£β β β β β£Όβ | ||
| β’Έβ‘Ώβ’Ώβ£Ώβ£Ώβ£·β£Άβ£Ύβ£Ώβ£Ώβ’°β‘β β β β β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ‘β β β β Άβ Άβ β β β’ β‘ | ||
| β’Έβ‘β β β Ώβ£Ώβ£Ώβ£Ώβ‘Ώβ β’Έβ£β β β β β£Ώβ£Ώβ£Ώβ£Ώβ‘Ώβ β£β‘β β β β’⣠⣴⣿①| ||
| β’Έβ‘β β β β β β β β β’°β‘β’³β£β β β£Ώβ£Ώβ£Ώβ β£ β β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ‘ | ||
| β’Έβ‘β β β β β β β β β β’·β‘β’Ήβ‘β β£Ώβ£Ώβ β£Όβ β£°β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ‘ | ||
| β’Έβ‘β β β β β β β β β β β ³β£β’»β‘β£Ώβ‘β β’⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿①| ||
| β’Έβ‘β β β β β β β β β β β β β β’·β‘β’ β£Ύβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ‘ | ||
| β’Έβ‘β β β β β β β β β β β β β β ⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿①| ||
| β’Έβ£β£β£β£β£β£β£β£β£β£β£β£β£β£β£β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ‘ | ||
| β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| { | ||
| "personal_information": { "first_name": "Alex", "last_name": "Tan" }, | ||
| "chats": { | ||
| "list": [ | ||
| { | ||
| "id": 4815162342, | ||
| "messages": [ | ||
| { "type": "message", "id": 1, "from": "Jamie", "date_unixtime": "1700000000", "text": "yo did you book the airbnb for the trip?" }, | ||
| { "type": "message", "id": 2, "from": "Alex Tan", "date_unixtime": "1700000035", "text": "yeah just sorted it! i'll forward you the invoice" }, | ||
| { "type": "message", "id": 3, "from": "Jamie", "date_unixtime": "1700000070", "text": "nice send it to jamie.wong@gmail.com" }, | ||
| { "type": "message", "id": 4, "from": "Alex Tan", "date_unixtime": "1700000110", "text": "done. they need your details for check-in too" }, | ||
| { "type": "message", "id": 5, "from": "Jamie", "date_unixtime": "1700000150", "text": "ok my number is 8123 4567 and nric S1234567D" }, | ||
| { "type": "message", "id": 6, "from": "Alex Tan", "date_unixtime": "1700000190", "text": "got it. i already paid the deposit with card 4111 1111 1111 1111" }, | ||
| { "type": "message", "id": 7, "from": "Jamie", "date_unixtime": "1700000215", "text": "lmao did you just type your full card number" }, | ||
| { "type": "message", "id": 8, "from": "Alex Tan", "date_unixtime": "1700000245", "text": "haha oops. anyway i'll send the confirmation to alex.tan@example.com" }, | ||
| { "type": "message", "id": 9, "from": "Jamie", "date_unixtime": "1700000275", "text": "perfect, see you next week!" }, | ||
| { "type": "message", "id": 10, "from": "Alex Tan", "date_unixtime": "1700000300", "text": "see ya, can't wait" } | ||
| ] | ||
| } | ||
| ] | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| # Copy to .env and fill in. .env is gitignored β never commit your keys. | ||
| # Every value here is OPTIONAL; with none set, ingestion still runs (the LLM | ||
| # features just stay off). | ||
|
|
||
| # ββ Optional LLM features (quality auditor + LLM redaction) βββββββββββββββββββ | ||
| # The CORE pipeline (parse -> dataset + regex sensitive-data scan) needs NONE of | ||
| # this and runs with no setup. Uncomment below to ALSO enable the LLM auditor / | ||
| # redaction. | ||
| # | ||
| # Run a LOCAL OpenAI-compatible server so your chat data never leaves your machine | ||
| # (vLLM, LM Studio, llama.cpp). Serve an open model, then uncomment: | ||
| # | ||
| # LLM_VALIDATE=true | ||
| # LLM_API_BASE_URL=http://localhost:8000/v1 # vLLM (LM Studio uses :1234/v1) | ||
| # LLM_MODEL=Qwen/Qwen2.5-7B-Instruct # the model your server serves | ||
| # LLM_API_KEY=local # local servers accept any value | ||
|
|
||
| # ββ Optional: Hugging Face ββββββββββββββββββββββββββββββββββββββββββββββββββββ | ||
| # Only needed to download GATED models during training (e.g. Gemma). The default | ||
| # Qwen model in configs/train_lora.yaml is open and needs no token. Read by the | ||
| # training stack (huggingface_hub), not by this repo's ingestion code. | ||
| # HF_TOKEN= |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| """ASCII startup banner: a parrot in a mirror (it mimics your voice; the | ||
| mirror is the doppelganger) beside the wordmark. The wordmark is amber via | ||
| truecolor ANSI. Regenerate via demo/build_final.py. | ||
| Set DOPPELGANGER_NO_BANNER=1 to silence it.""" | ||
|
|
||
| import os | ||
|
|
||
| _AMBER = "\x1b[1;38;2;242;176;76m" # truecolor amber | ||
| _RESET = "\x1b[0m" | ||
|
|
||
| _BANNER = r""" | ||
| β β β β β β β β β β β β β β β β£β£β‘ | ||
| β β β β β β β β β£β£€β Άβ β β β β β£Ώβ£Ώβ£Ώβ£Ώβ£·β£Άβ£€β£β‘ | ||
| β β β β β β£ β‘Άβ β β β β β β β β β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£·β£β‘ <C>βββββββ βββββββ βββββββ βββββββ βββββββββββ<R> | ||
| β β β β£ β‘Ύβ β β β β β β β β β β β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£ <C>ββββββββββββββββββββββββββββββββββββββββββββ<R> | ||
| β β ⣴β’⣀⣴⣢⣦⣀β‘β β β β β β β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ‘Ώβ β β β β β »β’·β‘ <C>βββ ββββββ βββββββββββββββββββββββββ βββ<R> | ||
| β β£Έβ£Ώβ£Ώβ£Ώβ Ώβ ⠻⣿⣿⣦β β β’β£β£€β£β‘β »β’Ώβ£Ώβ β β£ β Άβ β β’Άβ£β β <C>βββ ββββββ ββββββββββ βββββββ ββββββ βββ<R> | ||
| β β£Ώβ£Ώβ£Ώβ β β Έβ Ώβ β£Ώβ£Ώβ ⣴β β β β£Ώβ£Ώβ£·β£β β β’Έβ β β β Ώβ β’Ήβ‘ <C>ββββββββββββββββββββ βββ ββββββββββββββββ<R> | ||
| β’Έβ£Ώβ£Ώβ£Ώβ‘β β β β’β£Ώβ β£Όβ β Έβ β β£Ώβ£Ώβ£β£Ώβ£β β Έβ£β β β β β£Όβ <C>βββββββ βββββββ βββ βββ ββββββββββββββββ<R> | ||
| β’Έβ‘Ώβ’Ώβ£Ώβ£Ώβ£·β£Άβ£Ύβ£Ώβ£Ώβ’°β‘β β β β β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ‘β β β β Άβ Άβ β β β’ β‘ <C> βββββββ ββββββ ββββ βββ βββββββ βββββββββββββββ<R> | ||
| β’Έβ‘β β β Ώβ£Ώβ£Ώβ£Ώβ‘Ώβ β’Έβ£β β β β β£Ώβ£Ώβ£Ώβ£Ώβ‘Ώβ β£β‘β β β β’⣠⣴⣿①<C>ββββββββ βββββββββββββ βββββββββββ ββββββββββββββββ<R> | ||
| β’Έβ‘β β β β β β β β β’°β‘β’³β£β β β£Ώβ£Ώβ£Ώβ β£ β β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ‘ <C>βββ ββββββββββββββββββ ββββββ ββββββββββ ββββββββ<R> | ||
| β’Έβ‘β β β β β β β β β β’·β‘β’Ήβ‘β β£Ώβ£Ώβ β£Όβ β£°β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ‘ <C>βββ ββββββββββββββββββββββββ βββββββββ ββββββββ<R> | ||
| β’Έβ‘β β β β β β β β β β β ³β£β’»β‘β£Ώβ‘β β’⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿①<C>ββββββββββββ ββββββ ββββββββββββββββββββββββββ βββ<R> | ||
| β’Έβ‘β β β β β β β β β β β β β β’·β‘β’ β£Ύβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ‘ <C> βββββββ βββ ββββββ βββββ βββββββ βββββββββββ βββ<R> | ||
| β’Έβ‘β β β β β β β β β β β β β β ⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿①| ||
| β’Έβ£β£β£β£β£β£β£β£β£β£β£β£β£β£β£β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ‘ | ||
| β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β | ||
|
|
||
| fine-tune an LLM to write like you | ||
| """ | ||
|
|
||
|
|
||
| def print_banner() -> None: | ||
| if os.environ.get("DOPPELGANGER_NO_BANNER"): | ||
| return | ||
| print(_BANNER.replace("<C>", _AMBER).replace("<R>", _RESET) + "\n") |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.