Lynote commited on
Commit
7e99a66
Β·
verified Β·
1 Parent(s): 93feefa

feat: Lynote Notes open-source demo space (landing page + ready Gradio app)

Browse files
Files changed (4) hide show
  1. README.md +85 -5
  2. app.py +77 -0
  3. index.html +154 -18
  4. requirements.txt +2 -0
README.md CHANGED
@@ -1,10 +1,90 @@
1
  ---
2
  title: Lynote Notes
3
- emoji: πŸ“ˆ
4
- colorFrom: pink
5
- colorTo: gray
6
  sdk: static
7
- pinned: false
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: Lynote Notes
3
+ emoji: πŸ“
4
+ colorFrom: indigo
5
+ colorTo: green
6
  sdk: static
7
+ pinned: true
8
+ license: mit
9
+ short_description: "Source-grounded AI notes with citations (open source)."
10
+ tags:
11
+ - ai-notes
12
+ - note-taker
13
+ - source-grounded
14
+ - citations
15
+ - rag
16
+ - local-first
17
+ - bilingual
18
+ - open-source
19
+ - python
20
+ - cli
21
  ---
22
 
23
+ # πŸ“ Lynote Notes
24
+
25
+ **Source-grounded, bilingual, local-first AI note taker.** Turn documents, web pages, recordings, videos and YouTube links into structured notes where every bullet cites the exact source chunk it came from β€” so you can verify, edit and reuse notes instead of trusting a black box.
26
+
27
+ **Open source (MIT):** [github.com/lynote-ai/lynote-notes](https://github.com/lynote-ai/lynote-notes)
28
+
29
+ ## The problem
30
+
31
+ AI note tools usually give you a summary you cannot check. If a note says "BM25 is used for retrieval", you should be able to click through to the sentence that said it. Lynote Notes makes traceability the default contract: notes are generated from your sources, and every claim links back to a chunk β€” including a timestamp for audio and video.
32
+
33
+ ## Quickstart
34
+
35
+ ```bash
36
+ git clone https://github.com/lynote-ai/lynote-notes.git
37
+ cd lynote-notes
38
+ pip install -e .
39
+
40
+ lynote-notes add lecture.md
41
+ lynote-notes add https://example.com/post
42
+ lynote-notes note --title "Weekly reading"
43
+ lynote-notes ask "What did we decide about pricing?"
44
+ lynote-notes export --note note_xxx --format anki --out cards.tsv
45
+ ```
46
+
47
+ Optional extras add more source types:
48
+
49
+ ```bash
50
+ pip install -e ".[pdf]" # PDF (pypdf)
51
+ pip install -e ".[docx]" # Word documents
52
+ pip install -e ".[media]" # audio/video transcription (faster-whisper)
53
+ pip install -e ".[youtube]" # YouTube captions (yt-dlp)
54
+ ```
55
+
56
+ ## Methodology
57
+
58
+ Five small stages, one traceable pipeline:
59
+
60
+ 1. **Ingest** β€” text/Markdown, web pages, PDF, DOCX, YouTube captions, audio and video (optional backends behind extras, imported lazily).
61
+ 2. **Chunk** β€” paragraph-aware splitting with overlap; media keeps start/end timestamps.
62
+ 3. **Note** β€” a provider drafts sections from chunks (offline extractive by default, or any OpenAI-compatible LLM with automatic fallback). Providers only return chunk ids; the core resolves them into citations and drops ids that do not exist, so hallucinated citations cannot leak into a note.
63
+ 4. **Ask** β€” dependency-free BM25-lite retrieval (CJK unigrams + bigrams, numeric tokens) finds the evidence; answers cite the chunks they came from.
64
+ 5. **Export** β€” Markdown for reading, Anki TSV for memorising.
65
+
66
+ ## Status and quality
67
+
68
+ - 58 offline tests, 92% line coverage, CI on Python 3.9 / 3.11 / 3.12
69
+ - Zero third-party dependencies in the core install
70
+ - Bilingual: English and Chinese out of the box
71
+
72
+ ## Limitations
73
+
74
+ - The default provider is extractive β€” it selects and organises sentences from your sources rather than rewriting them. Use an LLM provider for more fluent notes.
75
+ - Retrieval is lexical (BM25): paraphrased questions can miss relevant passages; embeddings are on the roadmap.
76
+ - Transcription quality depends on the ASR model and audio quality.
77
+ - No live meeting capture by design: export the recording and upload it.
78
+ - Notes can still miss context or nuance β€” always check important facts, numbers and quotes against the cited source.
79
+
80
+ ## More free Lynote tools
81
+
82
+ - [πŸ“ Free AI Note Taker](https://huggingface.co/spaces/Lynote/free-ai-notes) β€” hosted product page (lynote.ai/ai-notes)
83
+ - [✍️ Free AI Humanizer](https://huggingface.co/spaces/Lynote/free-ai-humanizer) β€” rewrite AI text into natural human prose
84
+ - [πŸ” Free AI Detector](https://huggingface.co/spaces/Lynote/free-ai-detector) β€” sentence-level AI text detection
85
+ - [πŸ–ΌοΈ Free AI Image Detector](https://huggingface.co/spaces/Lynote/free-ai-image-detector) β€” check if an image was AI-generated
86
+ - [🧠 humanize-text-model](https://huggingface.co/Lynote/humanize-text-model) β€” MIT-licensed bilingual T5 model
87
+
88
+ ## License
89
+
90
+ MIT. See the [repository](https://github.com/lynote-ai/lynote-notes) for source and license notices.
app.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Interactive Lynote Notes demo (kept ready for PRO hosting).
2
+
3
+ Paste text, get a cited note, then ask questions grounded in the same text.
4
+ The demo uses the library's offline extractive provider, so it needs no model
5
+ and no API key β€” only Gradio and the lynote-notes package.
6
+ """
7
+
8
+ import tempfile
9
+
10
+ import gradio as gr
11
+
12
+ from lynote_notes import Workspace
13
+
14
+ MAX_CHARS = 5_000
15
+ UTM = (
16
+ "https://lynote.ai/ai-notes?utm_source=huggingface"
17
+ "&utm_medium=space&utm_campaign=hf_launch&utm_content=oss_notes"
18
+ )
19
+
20
+
21
+ def _workspace(text: str) -> Workspace:
22
+ ws = Workspace(tempfile.mkdtemp())
23
+ ws.add_text(text, "Pasted text")
24
+ return ws
25
+
26
+
27
+ def make_note(text: str, title: str):
28
+ text = (text or "").strip()
29
+ if len(text) < 80:
30
+ return "Add at least 80 characters of source text."
31
+ if len(text) > MAX_CHARS:
32
+ return f"Input is limited to {MAX_CHARS:,} characters in this demo."
33
+ ws = _workspace(text)
34
+ note = ws.make_note(title or "Your note")
35
+ return ws.export_note(note, "md")
36
+
37
+
38
+ def ask_question(text: str, question: str):
39
+ text = (text or "").strip()
40
+ question = (question or "").strip()
41
+ if not text or not question:
42
+ return "Paste some source text and ask a question."
43
+ ws = _workspace(text)
44
+ answer = ws.ask(question)
45
+ lines = [answer.text, ""]
46
+ for index, citation in enumerate(answer.citations, start=1):
47
+ lines.append(f"[{index}] {citation.source_title} Β· {citation.locator} β€” {citation.snippet}")
48
+ lines.append("")
49
+ lines.append(f"Try the full product: {UTM}")
50
+ return "\n".join(lines)
51
+
52
+
53
+ with gr.Blocks(title="Lynote Notes β€” Source-Grounded AI Note Taker") as demo:
54
+ gr.Markdown(
55
+ "# πŸ“ Lynote Notes\n"
56
+ "Source-grounded notes with citations β€” every bullet links back to the exact "
57
+ "source chunk. Open source: [github.com/lynote-ai/lynote-notes]"
58
+ "(https://github.com/lynote-ai/lynote-notes)"
59
+ )
60
+ source = gr.Textbox(
61
+ label="Source text (80–5,000 characters)",
62
+ placeholder="Paste a lecture transcript, article, or meeting notes…",
63
+ lines=10,
64
+ )
65
+ with gr.Tab("Generate note"):
66
+ title = gr.Textbox(label="Note title", value="Your note")
67
+ note_button = gr.Button("Generate cited note", variant="primary")
68
+ note_output = gr.Markdown()
69
+ note_button.click(make_note, inputs=[source, title], outputs=note_output)
70
+ with gr.Tab("Ask a question"):
71
+ question = gr.Textbox(label="Question", placeholder="What did we decide about pricing?")
72
+ ask_button = gr.Button("Ask", variant="primary")
73
+ answer_output = gr.Markdown()
74
+ ask_button.click(ask_question, inputs=[source, question], outputs=answer_output)
75
+
76
+ if __name__ == "__main__":
77
+ demo.launch()
index.html CHANGED
@@ -1,19 +1,155 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
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>Lynote Notes β€” Source-Grounded AI Note Taker (Open Source) | Lynote</title>
7
+ <meta name="description" content="Open-source, source-grounded AI note taker: turn documents, web pages, audio, video and YouTube links into notes where every bullet cites the exact source chunk. Local-first, bilingual, zero-dependency core." />
8
+ <meta name="keywords" content="open source ai note taker, source grounded notes, citations, rag notes, local first notes, ai notes cli, anki export, bilingual notes" />
9
+ <meta property="og:title" content="Lynote Notes β€” Source-Grounded AI Note Taker (Open Source)" />
10
+ <meta property="og:description" content="Notes where every bullet cites the exact source chunk. MIT licensed, local-first, bilingual." />
11
+ <style>
12
+ :root { --bg:#0f1420; --card:#171f31; --card2:#1b2540; --fg:#e8edf7; --muted:#9aa7c0; --accent:#7b6dff; --accent2:#34d399; --line:#23304a; }
13
+ * { margin:0; padding:0; box-sizing:border-box; }
14
+ body { background:var(--bg); color:var(--fg); font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif; line-height:1.7; }
15
+ .wrap { max-width:960px; margin:0 auto; padding:48px 24px 72px; }
16
+ .logo { font-size:15px; letter-spacing:3px; color:var(--muted); text-transform:uppercase; }
17
+ .badge { display:inline-block; font-size:13px; padding:4px 14px; border-radius:999px; border:1px solid #2c3a55; color:#b9c6e2; background:rgba(123,109,255,.08); margin:20px 0; }
18
+ h1 { font-size:clamp(30px,5.5vw,48px); line-height:1.2; margin:0 0 16px; background:linear-gradient(90deg,var(--accent),var(--accent2)); -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent; }
19
+ .sub { font-size:18px; color:var(--muted); max-width:720px; }
20
+ .stats { display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:14px; margin:34px 0 8px; }
21
+ .stat { background:var(--card); border:1px solid var(--line); border-radius:14px; padding:18px; text-align:center; }
22
+ .stat b { display:block; font-size:24px; background:linear-gradient(90deg,var(--accent),var(--accent2)); -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent; }
23
+ .stat span { font-size:13px; color:var(--muted); }
24
+ h2 { font-size:26px; margin:56px 0 8px; }
25
+ .section-note { color:var(--muted); margin-bottom:22px; }
26
+ .grid { list-style:none; display:grid; grid-template-columns:repeat(auto-fit,minmax(260px,1fr)); gap:14px; }
27
+ .grid li { background:var(--card); border:1px solid var(--line); border-radius:14px; padding:18px 20px; }
28
+ .grid strong { display:block; color:#fff; margin-bottom:6px; font-size:16px; }
29
+ .grid span { color:var(--muted); font-size:14.5px; }
30
+ .steps { list-style:none; display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:14px; margin-top:8px; }
31
+ .steps li { background:var(--card); border:1px solid var(--line); border-radius:14px; padding:20px; }
32
+ .steps em { display:block; font-style:normal; color:var(--accent2); font-size:13px; letter-spacing:1px; text-transform:uppercase; margin-bottom:6px; }
33
+ .steps strong { display:block; font-size:16px; margin-bottom:6px; }
34
+ .steps span { color:var(--muted); font-size:14.5px; }
35
+ pre { background:var(--card2); border:1px solid var(--line); border-radius:12px; padding:16px 18px; overflow-x:auto; font-size:13.5px; line-height:1.6; color:#d7e1f5; }
36
+ code { font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; }
37
+ details { background:var(--card); border:1px solid var(--line); border-radius:12px; padding:14px 18px; margin-bottom:10px; }
38
+ summary { cursor:pointer; font-weight:600; color:#fff; }
39
+ details p { color:var(--muted); font-size:14.5px; margin-top:8px; }
40
+ .cta { display:inline-block; background:linear-gradient(90deg,var(--accent),var(--accent2)); color:#fff; font-size:17px; font-weight:600; padding:14px 30px; border-radius:12px; text-decoration:none; margin:36px 0 8px; }
41
+ .cta:hover { opacity:.9; }
42
+ .cta.secondary { background:var(--card); border:1px solid var(--line); font-weight:500; margin-left:10px; }
43
+ .links-label { font-size:12px; color:var(--muted); letter-spacing:1.5px; text-transform:uppercase; margin-top:36px; }
44
+ .links { display:flex; flex-wrap:wrap; gap:10px; align-items:center; margin-top:10px; }
45
+ .chip { font-size:14px; padding:8px 18px; border-radius:999px; border:1px solid var(--line); color:var(--fg); text-decoration:none; background:var(--card); }
46
+ .chip b { color:var(--accent2); }
47
+ .chip:hover { border-color:var(--accent); color:#fff; }
48
+ .note { font-size:13.5px; color:var(--muted); margin-top:24px; max-width:780px; }
49
+ </style>
50
+ </head>
51
+ <body>
52
+ <div class="wrap">
53
+
54
+ <div class="logo">Lynote</div>
55
+ <span class="badge">OPEN SOURCE Β· MIT Β· ZERO-DEPENDENCY CORE</span>
56
+ <h1>Lynote Notes</h1>
57
+ <p class="sub">
58
+ Source-grounded, bilingual, local-first AI note taker. Turn documents, web pages,
59
+ recordings, videos and YouTube links into structured notes where every bullet cites
60
+ the exact source chunk it came from β€” so you can verify, edit and reuse notes
61
+ instead of trusting a black box.
62
+ </p>
63
+
64
+ <a class="cta" href="https://github.com/lynote-ai/lynote-notes" target="_blank" rel="noopener">
65
+ View on GitHub β†’
66
+ </a>
67
+ <a class="cta secondary" href="https://lynote.ai/ai-notes?utm_source=huggingface&utm_medium=space&utm_campaign=hf_launch&utm_content=oss_notes" target="_blank" rel="noopener">
68
+ Try Lynote AI Notes
69
+ </a>
70
+
71
+ <div class="links-label">Free Lynote tools Β· open source</div>
72
+ <div class="links">
73
+ <a class="chip" href="https://github.com/lynote-ai/lynote-notes" target="_blank" rel="noopener"><b>GitHub</b> β€” lynote-ai/lynote-notes</a>
74
+ <a class="chip" href="https://huggingface.co/spaces/Lynote/free-ai-notes" target="_blank" rel="noopener">Free AI Note Taker β†’</a>
75
+ <a class="chip" href="https://huggingface.co/spaces/Lynote/free-ai-humanizer" target="_blank" rel="noopener">Free AI Humanizer β†’</a>
76
+ <a class="chip" href="https://huggingface.co/spaces/Lynote/free-ai-detector" target="_blank" rel="noopener">Free AI Detector β†’</a>
77
+ <a class="chip" href="https://huggingface.co/spaces/Lynote/free-ai-image-detector" target="_blank" rel="noopener">Free AI Image Detector β†’</a>
78
+ </div>
79
+
80
+ <div class="stats">
81
+ <div class="stat"><b>58</b><span>Tests Passing</span></div>
82
+ <div class="stat"><b>92%</b><span>Test Coverage</span></div>
83
+ <div class="stat"><b>0</b><span>Core Dependencies</span></div>
84
+ <div class="stat"><b>MIT</b><span>License</span></div>
85
+ </div>
86
+
87
+ <h2>Why Source-Grounded?</h2>
88
+ <p class="section-note">AI note tools usually give you a summary you cannot check. Lynote Notes is built around traceability.</p>
89
+ <ul class="grid">
90
+ <li><strong>Citations as a Contract</strong><span>Every bullet carries a source citation. Providers return chunk ids; the core resolves them and drops anything that does not exist β€” a hallucinated citation cannot leak into a note.</span></li>
91
+ <li><strong>Offline by Default</strong><span>The default extractive provider needs no model and no network. Configure any OpenAI-compatible endpoint (Ollama, vLLM, OpenAI) for generative notes, with automatic fallback.</span></li>
92
+ <li><strong>Bilingual by Design</strong><span>CJK unigrams + bigrams, sentence splitting on γ€‚οΌοΌŸοΌ› β€” English and Chinese work out of the box.</span></li>
93
+ <li><strong>Local-First Workspace</strong><span>One workspace is one SQLite file. No server, no account, inspectable with standard tools.</span></li>
94
+ <li><strong>Media Timestamps</strong><span>Audio and video citations point to HH:MM:SS, not just "somewhere in the file".</span></li>
95
+ <li><strong>Reuse Built In</strong><span>Export Markdown for reading or Anki TSV for memorising β€” straight from the same cited note.</span></li>
96
+ </ul>
97
+
98
+ <h2>Quickstart</h2>
99
+ <p class="section-note">Python 3.9+, works offline.</p>
100
+ <pre><code>git clone https://github.com/lynote-ai/lynote-notes.git
101
+ cd lynote-notes
102
+ pip install -e .
103
+
104
+ lynote-notes add lecture.md
105
+ lynote-notes add https://example.com/post
106
+ lynote-notes note --title "Weekly reading"
107
+ lynote-notes ask "What did we decide about pricing?"
108
+ lynote-notes export --note note_xxx --format anki --out cards.tsv</code></pre>
109
+
110
+ <h2>How It Works</h2>
111
+ <p class="section-note">Five small stages, one traceable pipeline.</p>
112
+ <ol class="steps">
113
+ <li><em>Step 1</em><strong>Ingest</strong><span>Text/Markdown, web pages, PDF, DOCX, YouTube captions, audio and video β€” optional backends stay behind extras.</span></li>
114
+ <li><em>Step 2</em><strong>Chunk</strong><span>Paragraph-aware splitting with overlap; media keeps timestamps.</span></li>
115
+ <li><em>Step 3</em><strong>Note</strong><span>A provider drafts sections from chunks; the core resolves every bullet to a real citation.</span></li>
116
+ <li><em>Step 4</em><strong>Ask</strong><span>BM25-lite retrieval finds the evidence; answers cite the chunks they came from.</span></li>
117
+ <li><em>Step 5</em><strong>Export</strong><span>Markdown for reading, Anki TSV for memorising.</span></li>
118
+ </ol>
119
+
120
+ <h2>Limitations</h2>
121
+ <ul class="grid">
122
+ <li><strong>Extractive Default</strong><span>The default provider selects and organises sentences from your sources rather than rewriting them. Use an LLM provider for more fluent notes.</span></li>
123
+ <li><strong>Lexical Retrieval</strong><span>BM25 matches words, not meanings. Paraphrased questions can miss relevant passages; embeddings are on the roadmap.</span></li>
124
+ <li><strong>Transcription Quality</strong><span>Depends on the ASR model size and audio quality β€” check important details against the source.</span></li>
125
+ <li><strong>No Live Capture</strong><span>No meeting bots by design: export the recording and upload it.</span></li>
126
+ </ul>
127
+
128
+ <h2>FAQ</h2>
129
+ <details>
130
+ <summary>Is Lynote Notes the same as Lynote AI Notes?</summary>
131
+ <p>Lynote Notes is the open-source core (MIT). Lynote AI Notes is the hosted product at lynote.ai/ai-notes, which adds a workspace UI, more formats and managed AI. The CLI and library here work standalone.</p>
132
+ </details>
133
+ <details>
134
+ <summary>Do I need an API key?</summary>
135
+ <p>No. The default extractive provider runs offline. An OpenAI-compatible endpoint is optional for generative notes.</p>
136
+ </details>
137
+ <details>
138
+ <summary>Does it work with Chinese content?</summary>
139
+ <p>Yes β€” tokenization and sentence splitting are bilingual by design, and notes can be generated from mixed-language workspaces.</p>
140
+ </details>
141
+ <details>
142
+ <summary>How do I verify a citation?</summary>
143
+ <p>Every citation names the source, a locator ("chunk 4" or "00:12:30") and a snippet. Open the source at that locator and compare β€” that is the point of the design.</p>
144
+ </details>
145
+
146
+ <p class="note">
147
+ This Space is a static demo page for the open-source project. The interactive Gradio demo
148
+ (paste text β†’ cited note β†’ ask questions) is kept ready in this repository and will be
149
+ enabled once the account has PRO hosting. Full product:
150
+ <a href="https://lynote.ai/ai-notes" style="color:var(--accent)">lynote.ai/ai-notes</a>.
151
+ AI notes can miss context or nuance β€” always check important details against the cited source.
152
+ </p>
153
+ </div>
154
+ </body>
155
  </html>
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ gradio>=4.0
2
+ lynote-notes @ git+https://github.com/lynote-ai/lynote-notes.git@v0.1.0