Skip to content

Commit 231c688

Browse files
docs: appealing README with badges and AI-papers demo familiar to peer users
- hero, badges, workflow mapping from LLM Wiki/Reor/Khoj, providers, comparison - demo.svg now shows reading AI papers (Transformer, BERT, RAG) from the real core - data/demo/papers sample set; make start opens it by default Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1 parent 1563085 commit 231c688

10 files changed

Lines changed: 192 additions & 37 deletions

File tree

‎README.md‎

Lines changed: 93 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,103 @@
1-
# gknowledge
1+
<p align="center">
2+
<img src="data/icons/hicolor/scalable/apps/io.gknowledge.Gknowledge.svg" alt="" width="96">
3+
</p>
24

3-
**A native GNOME knowledge workstation that turns raw material into an evolving knowledge base.**
5+
<h1 align="center">gknowledge</h1>
46

5-
`gknowledge` is a desktop application for GNOME that helps users collect documents, notes, links, and research material, then incrementally transform that corpus into a structured knowledge space with the support of LLMs.
7+
<p align="center">
8+
<b>Drop in your documents. Get a linked wiki that an LLM keeps up to date.</b><br>
9+
A native GNOME app. Offline by default, plain Markdown on disk, Obsidian-compatible.<br>
10+
Bring your own brain: a local model, any OpenAI-compatible endpoint, or your own ChatGPT plan.
11+
</p>
612

7-
The goal is not to build yet another chat wrapper.
8-
The goal is to build a serious native app for Linux desktops that makes knowledge curation, synthesis, and retrieval feel like a first-class operating system capability.
13+
<p align="center">
14+
<a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/github/license/openapi/gknowledge?style=for-the-badge&color=3584e4"></a>
15+
<img alt="GTK 4" src="https://img.shields.io/badge/GTK-4-4a86cf?style=for-the-badge&logo=gtk&logoColor=white">
16+
<img alt="Libadwaita" src="https://img.shields.io/badge/Libadwaita-1.5%2B-4a86cf?style=for-the-badge&logo=gnome&logoColor=white">
17+
<img alt="Ubuntu 24.04+" src="https://img.shields.io/badge/Ubuntu-24.04%2B-E95420?style=for-the-badge&logo=ubuntu&logoColor=white">
18+
</p>
919

1020
<p align="center">
11-
<img src="docs/demo.svg" alt="gknowledge in action: drop documents, get a linked wiki, ask questions with citations" width="860">
21+
<a href="https://github.com/openapi/gknowledge/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/openapi/gknowledge/ci.yml?branch=main&style=flat-square&label=CI"></a>
22+
<a href="https://github.com/openapi/gknowledge/stargazers"><img alt="Stars" src="https://img.shields.io/github/stars/openapi/gknowledge?style=flat-square&logo=github"></a>
23+
<a href="https://github.com/openapi/gknowledge/issues"><img alt="Issues" src="https://img.shields.io/github/issues/openapi/gknowledge?style=flat-square"></a>
24+
<img alt="Last commit" src="https://img.shields.io/github/last-commit/openapi/gknowledge?style=flat-square">
25+
<img alt="Python 3.10+" src="https://img.shields.io/badge/python-3.10%2B-3776AB?style=flat-square&logo=python&logoColor=white">
26+
<img alt="Offline first" src="https://img.shields.io/badge/offline-first-2ea043?style=flat-square">
27+
<img alt="No API key needed" src="https://img.shields.io/badge/API%20key-not%20required-2ea043?style=flat-square">
28+
<img alt="Obsidian compatible" src="https://img.shields.io/badge/Obsidian-compatible-7C3AED?style=flat-square&logo=obsidian&logoColor=white">
29+
<img alt="Karpathy LLM Wiki pattern" src="https://img.shields.io/badge/pattern-Karpathy%20LLM%20Wiki-f59e0b?style=flat-square">
30+
<img alt="Sign in with ChatGPT" src="https://img.shields.io/badge/Sign%20in%20with-ChatGPT-10a37f?style=flat-square&logo=openai&logoColor=white">
31+
<img alt="Ollama" src="https://img.shields.io/badge/Ollama-supported-000000?style=flat-square">
32+
<img alt="Flatpak planned" src="https://img.shields.io/badge/Flatpak-planned-4a86cf?style=flat-square&logo=flatpak&logoColor=white">
33+
<img alt="PRs welcome" src="https://img.shields.io/badge/PRs-welcome-ff69b4?style=flat-square">
1234
</p>
1335

14-
<sub>The animation is generated from the real core on the chess sample documents in `data/demo/chess` (offline engine, no API key). Regenerate it with `make demo-svg`.</sub>
36+
<p align="center">
37+
<img src="docs/demo.svg" alt="gknowledge in action: drop in AI research papers, get a linked wiki, ask a question and get an answer with citations" width="880">
38+
</p>
39+
40+
<p align="center"><sub>
41+
Real output of the offline engine on six AI papers (<code>data/demo/papers</code>), no API key.
42+
Regenerate with <code>make demo-svg</code>.
43+
</sub></p>
44+
45+
## Why gknowledge
46+
47+
If you have used [LLM Wiki](https://github.com/nashsu/llm_wiki), [Tolaria](https://github.com/refactoringhq/tolaria), [OpenKnowledge](https://github.com/inkeep/open-knowledge), [Reor](https://github.com/reorproject/reor) or [Khoj](https://github.com/khoj-ai/khoj), you already know the loop: **ingest sources, let the model build and maintain a wiki, then ask questions against it.** gknowledge does the same thing as a real GNOME application.
48+
49+
- **Native, not a web view.** GTK 4 and Libadwaita, built to follow the GNOME HIG. No Electron, no Tauri, no browser engine.
50+
- **Works with no account.** A built-in heuristic engine ingests and answers offline. Add a model when you want better synthesis.
51+
- **Your data stays yours.** Markdown with YAML frontmatter plus a readable SQLite index. Open the folder in Obsidian or any editor.
52+
- **Cited, reviewable, incremental.** Every generated page records its sources, answers cite pages, and a review queue keeps you in control.
53+
54+
## The workflow you already know
55+
56+
| You know this from… | What it is | In gknowledge |
57+
|---|---|---|
58+
| Karpathy's LLM Wiki, LLM Wiki | **Ingest**: read a new source, update the pages it touches | Done: two-step extraction with provenance |
59+
| Reor, Khoj | **Ask**: questions answered from your notes, with citations | Done: lexical search plus graph expansion, citations, saved answers |
60+
| LLM Wiki, OpenKnowledge | `index.md` and `log.md`, `[[wikilinks]]`, Obsidian-compatible vault | Done |
61+
| LLM Wiki, Tolaria | **Review** AI changes before they stick | Review queue done, Git diff planned |
62+
| Karpathy's LLM Wiki, OpenKnowledge | **Lint**: orphans, contradictions, stale pages | Planned |
63+
| LLM Wiki | Knowledge graph, clusters, gaps | Planned |
64+
| Tolaria, OpenKnowledge, LLM Wiki | MCP server so agents can read and write the wiki | Planned |
65+
| LLM Wiki | Web clipper, folder watch, deep research | Planned |
66+
67+
See [`ROADMAP.md`](ROADMAP.md) for dates and [`TODO.md`](TODO.md) for the backlog.
68+
69+
## Quick start
70+
71+
```bash
72+
sudo apt install python3-gi gir1.2-gtk-4.0 gir1.2-adw-1 # Ubuntu / Debian
73+
git clone https://github.com/openapi/gknowledge && cd gknowledge
74+
make start # opens a demo project with six AI papers already ingested
75+
```
76+
77+
`make start` also restarts the app whenever you edit the source, which makes it the fastest way to hack on gknowledge. Install it for daily use with `make install`.
78+
79+
## Bring your own brain
80+
81+
| Provider | Needs | Notes |
82+
|---|---|---|
83+
| **Offline** (default) | nothing | Heuristic extraction and extractive answers. Fully private. |
84+
| **ChatGPT plan** | A Plus or Pro account | Preferences, Provider, *Sign in with ChatGPT*. Usage is charged to your own plan, no API key. |
85+
| **Ollama / LM Studio / vLLM** | A local server | Any OpenAI-compatible endpoint on your machine. |
86+
| **OpenAI-compatible cloud** | An API key | OpenAI, OpenRouter, Together, and similar. |
87+
88+
Nothing is sent anywhere unless you pick a provider that is not offline.
89+
90+
## How it compares
91+
92+
| | gknowledge | LLM Wiki | Tolaria | OpenKnowledge | Reor (archived) | Khoj |
93+
|---|---|---|---|---|---|---|
94+
| Native GNOME / GTK 4 | **yes** | Tauri | Tauri | Electron | Electron | web + Electron |
95+
| Works offline, no key | **yes** | needs a model | AI optional | agent-driven | local model | self-host |
96+
| Plain Markdown vault | **yes** | yes | yes | yes | yes | reads yours |
97+
| Linux packaging | `.deb`, Flatpak planned | `.deb`, AppImage | AppImage, `.deb` | `.deb`, `.rpm` | AppImage | pip, Docker |
98+
| License | MIT | GPL-3.0 | AGPL-3.0 | GPL-3.0 | AGPL-3.0 | AGPL-3.0 |
99+
100+
Details and history of each project are in [`DISTRIBUTING.md`](DISTRIBUTING.md).
15101

16102
## gknowledge is an alternative to…
17103

‎build-aux/dev/make_demo_svg.py‎

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"""Generate docs/demo.svg: an animated (SMIL) walkthrough of gknowledge.
22
3-
Everything shown comes from the real core: the chess sample documents are
3+
The theme is "reading AI papers", the scenario users of LLM-wiki tools know
4+
best (Karpathy's gist, LLM Wiki's Research template, Reor, Khoj). Everything
5+
shown comes from the real core: the sample documents are
46
ingested with the offline engine and the page titles, summaries, counts and
57
answer text are read back from the resulting project. Regenerate with:
68
@@ -9,6 +11,7 @@
911
from __future__ import annotations
1012

1113
import html
14+
import os
1215
import sys
1316
import tempfile
1417
import textwrap
@@ -24,7 +27,21 @@
2427
from gknowledge.providers import build_provider # noqa: E402
2528

2629
OUT = Path(sys.argv[1]) if len(sys.argv) > 1 else ROOT / "docs" / "demo.svg"
27-
QUESTION = "What is the Ruy Lopez?"
30+
TOPIC = os.environ.get("TOPIC", "papers")
31+
CONFIG = {
32+
"papers": dict(name="Papers demo", question="What is retrieval-augmented generation?",
33+
page="Attention Is All You Need",
34+
chosen=["Attention Is All You Need", "BERT", "GPT-3",
35+
"Retrieval-Augmented Generation", "Chain-of-Thought Prompting",
36+
"LLM Wiki Pattern", "Ashish Vaswani", "Andrej Karpathy"],
37+
chips=["Transformer", "BERT", "Ashish Vaswani"]),
38+
"chess": dict(name="Chess demo", question="What is the Ruy Lopez?", page="Ruy Lopez",
39+
chosen=["Ruy Lopez", "Deep Blue versus Kasparov", "Deep Blue",
40+
"Garry Kasparov", "Vladimir Kramnik", "Bobby Fischer",
41+
"Sicilian Defence", "Wilhelm Steinitz"],
42+
chips=["Garry Kasparov", "Vladimir Kramnik", "Sicilian Defence"]),
43+
}[TOPIC]
44+
QUESTION = CONFIG["question"]
2845
T = 24.0 # loop length in seconds
2946
import os
3047
FRAME = float(os.environ["FRAME"]) if os.environ.get("FRAME") else None # debug: static frame
@@ -35,6 +52,10 @@
3552
FONT = "Cantarell, 'Noto Sans', 'Ubuntu', 'DejaVu Sans', sans-serif"
3653

3754

55+
def short(s: str, n: int = 27) -> str:
56+
return s if len(s) <= n else s[: n - 1].rstrip() + "…"
57+
58+
3859
def esc(s: str) -> str:
3960
return html.escape(s, quote=True)
4061

@@ -49,16 +70,16 @@ def clean(s: str, title: str = "") -> str:
4970
# -- real data ----------------------------------------------------------------
5071
def collect():
5172
tmp = Path(tempfile.mkdtemp())
52-
proj = Project.create(tmp / "p", name="Chess demo")
73+
proj = Project.create(tmp / "p", name=CONFIG["name"])
5374
proj.save_settings(Settings(provider="offline"))
5475
mgr = IngestionManager(proj)
55-
files = sorted((ROOT / "data" / "demo" / "chess").glob("*.md"))
76+
files = sorted((ROOT / "data" / "demo" / TOPIC).glob("*.md"))
5677
for f in files:
5778
mgr.import_file(f)
5879
mgr.process_pending()
5980
pages = {p.title: p for p in proj.store.list_pages()}
6081
w = KnowledgeWriter(proj)
61-
page = pages["Ruy Lopez"]
82+
page = pages[CONFIG["page"]]
6283
body = w.read_body(page)
6384
summary, points = "", []
6485
section = ""
@@ -68,28 +89,34 @@ def collect():
6889
elif section == "Summary" and line.strip():
6990
summary += " " + line.strip()
7091
elif section == "Key points" and line.startswith("- "):
71-
points.append(clean(line[2:], "Ruy Lopez"))
92+
points.append(clean(line[2:], CONFIG["page"]))
7293
engine = QueryEngine(proj, build_provider(proj.settings.as_dict()))
7394
res = engine.query(QUESTION)
7495
first = next(l for l in res.answer.splitlines() if l.startswith("- #"))
75-
answer = clean(first[2:], "Ruy Lopez")
96+
answer = clean(first[2:], res.citations[0].title)
7697
answer = answer.rsplit(" [", 1)[0]
98+
if "." in answer: # the engine truncates snippets mid-word
99+
answer = answer[: answer.rfind(". ") + 1] if ". " in answer else answer
77100
cites, seen = [], set()
78101
for c in res.citations:
79102
if c.title not in seen:
80103
seen.add(c.title)
81104
cites.append((c.number, c.title, c.kind))
82-
chosen = [t for t in ("Ruy Lopez", "Deep Blue versus Kasparov", "Deep Blue",
83-
"Garry Kasparov", "Vladimir Kramnik", "Bobby Fischer",
84-
"Sicilian Defence", "Wilhelm Steinitz") if t in pages]
105+
chosen = [x for x in CONFIG["chosen"] if x in pages]
106+
link_titles = {pg.slug: pg.title for pg in pages.values()}
107+
import re
108+
linked = [link_titles[s] for s in re.findall(r"\[\[([^\]|]+)", body) if s in link_titles]
109+
chips = [c for c in CONFIG["chips"] if c in pages]
85110
titles = [s.title for s in sorted(proj.store.list_sources(), key=lambda s: s.title)]
86111
return {
87112
"files": titles,
88113
"n_pages": len(pages),
89114
"pages": [(t, pages[t].kind.value) for t in chosen],
90-
"summary": clean(summary, "Ruy Lopez"),
115+
"summary": clean(summary, CONFIG["page"]),
91116
"points": points[:3],
92-
"answer": answer, "cites": cites[:3],
117+
"answer": answer, "cites": cites[:3], "chips": chips,
118+
"page_title": CONFIG["page"], "page_file": page.slug + ".md",
119+
"project": CONFIG["name"],
93120
}
94121

95122

@@ -152,7 +179,7 @@ def build(d) -> str:
152179
o.append(f'<rect x="{SB}" y="0" width="1" height="{H - 44}" fill="{BORDER}"/>')
153180
# sidebar header
154181
o.append(text(26, 31, "+", 22, ICON, "middle"))
155-
o.append(text(SB / 2, 22, "Chess demo", 13, TEXT, "middle", "700"))
182+
o.append(text(SB / 2, 22, d["project"], 13, TEXT, "middle", "700"))
156183
o.append(text(SB / 2, 38, "Offline (local heuristics)", 10, DIM, "middle"))
157184
o.append(text(SB - 24, 30, "≡", 20, ICON, "middle"))
158185
# tabs
@@ -173,7 +200,7 @@ def build(d) -> str:
173200
y = 72 + i * 46
174201
t0 = 1.2 + 0.45 * i
175202
row = (f'<circle cx="30" cy="{y - 4}" r="9" fill="none" stroke="{ICON}" stroke-width="1.5"/>'
176-
+ text(50, y, name, 13)
203+
+ text(50, y, short(name), 13)
177204
+ g(text(50, y + 16, "queued", 10, DIM), 0, 7.0)
178205
+ g(text(50, y + 16, "ingested", 10, DIM), 7.0, 8.5))
179206
rows.append(g(row, t0, 8.5))
@@ -199,19 +226,19 @@ def build(d) -> str:
199226
if i == 0:
200227
row += f'<rect x="8" y="{y - 24}" width="{SB - 16}" height="40" rx="8" fill="{SELECT}"/>'
201228
row += (f'<circle cx="30" cy="{y - 4}" r="9" fill="none" stroke="{ICON}" stroke-width="1.5"/>'
202-
+ text(50, y, title, 13) + text(50, y + 16, kind, 10, DIM)
229+
+ text(50, y, short(title), 13) + text(50, y + 16, kind, 10, DIM)
203230
+ text(SB - 24, y, "AI", 10, DIM, "middle"))
204231
kr.append(g(row, 8.8 + 0.3 * i, 15.0))
205232
o.append("".join(kr))
206-
page = [text(310, 100, "Ruy Lopez", 24, TEXT, weight="700"),
207-
text(310, 124, "type: source · generated · linked to ruy-lopez.md", 11, DIM),
233+
page = [text(310, 100, d["page_title"], 24, TEXT, weight="700"),
234+
text(310, 124, f"type: source · generated · from {d['page_file']}", 11, DIM),
208235
text(310, 160, "Summary", 14, TEXT, weight="700"),
209236
wrapped(310, 182, d["summary"], 78, 12.5, TEXT, 18, 5),
210237
text(310, 292, "Key points", 14, TEXT, weight="700")]
211238
for i, pt in enumerate(d["points"]):
212239
page.append(wrapped(322, 314 + i * 40, "• " + pt, 74, 12.5, TEXT, 18, 2))
213-
page.append(text(310, 440, "Linked pages", 14, TEXT, weight="700"))
214-
for i, nm in enumerate(["Garry Kasparov", "Vladimir Kramnik", "Sicilian Defence"]):
240+
page.append(text(310, 440, "Related pages", 14, TEXT, weight="700"))
241+
for i, nm in enumerate(d["chips"]):
215242
x = 310 + i * 160
216243
page.append(f'<rect x="{x}" y="452" width="148" height="26" rx="13" fill="#2f4a73"/>'
217244
+ text(x + 74, 470, "[[" + nm + "]]", 11, "#cfe0ff", "middle"))

‎build-aux/dev/seed_demo.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Create a demo project, import data/demo/*.md and ingest it offline.
22
3-
Usage: python3 build-aux/dev/seed_demo.py <project-dir> [chess|energy]
3+
Usage: python3 build-aux/dev/seed_demo.py <project-dir> [papers|chess|energy]
44
"""
55
import sys
66
from pathlib import Path
@@ -12,10 +12,10 @@
1212
from gknowledge.core.project import Project, Settings # noqa: E402
1313

1414
target = Path(sys.argv[1])
15-
proj = Project.create(target, name="Chess demo" if len(sys.argv) < 3 or sys.argv[2] == "chess" else "Demo")
15+
proj = Project.create(target, name={"papers": "Papers demo", "chess": "Chess demo"}.get(sys.argv[2] if len(sys.argv) > 2 else "papers", "Demo"))
1616
proj.save_settings(Settings(provider="offline"))
1717
mgr = IngestionManager(proj)
18-
topic = sys.argv[2] if len(sys.argv) > 2 else "chess"
18+
topic = sys.argv[2] if len(sys.argv) > 2 else "papers"
1919
for md in sorted((ROOT / "data" / "demo" / topic).glob("*.md")):
2020
mgr.import_file(md)
2121
print(f"Ingested {mgr.process_pending()} source(s) into {target}")
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Attention Is All You Need
2+
3+
Attention Is All You Need is a 2017 paper by Ashish Vaswani and seven co-authors at Google Brain and Google Research. It introduced the Transformer, a neural network architecture based entirely on self-attention that dispenses with recurrence and convolutions.
4+
5+
The Transformer uses multi-head attention, so the model can look at different positions of a sequence in parallel, and adds positional encodings to keep word order. Because it avoids sequential computation, it trains much faster than recurrent networks.
6+
7+
The model reached 28.4 BLEU on the WMT 2014 English-to-German translation task, a new state of the art, after training for 3.5 days on eight P100 GPUs. The paper was presented at NeurIPS 2017 and became the foundation of BERT, GPT-3 and almost every modern large language model.

‎data/demo/papers/bert.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# BERT
2+
3+
BERT, short for Bidirectional Encoder Representations from Transformers, was introduced in 2018 by Jacob Devlin, Ming-Wei Chang, Kenton Lee and Kristina Toutanova at Google AI Language. The paper was published at NAACL 2019.
4+
5+
BERT pre-trains a Transformer encoder on unlabelled text with two objectives: a masked language model, which hides some words and asks the model to predict them, and next sentence prediction. Because it reads context from both directions, it builds richer representations than left-to-right models.
6+
7+
BERT-base has 110 million parameters and BERT-large has 340 million. After fine-tuning it set a new state of the art on eleven natural language processing tasks, including the GLUE benchmark and the SQuAD question answering dataset.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Chain-of-Thought Prompting
2+
3+
Chain-of-Thought Prompting Elicits Reasoning in Large Language Models is a 2022 paper by Jason Wei and colleagues at Google Brain, presented at NeurIPS 2022.
4+
5+
Instead of showing only question and answer pairs, the prompt includes worked examples with the intermediate reasoning steps written out. Large models then produce their own step-by-step reasoning before giving the final answer. This sharply improves arithmetic, commonsense and symbolic reasoning.
6+
7+
With eight chain-of-thought examples, PaLM 540B reached state-of-the-art accuracy on GSM8K, a benchmark of grade-school math word problems, surpassing fine-tuned GPT-3 with a verifier. The ability emerges only at large scale, roughly around one hundred billion parameters, and does not help small models.

‎data/demo/papers/gpt-3.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# GPT-3
2+
3+
Language Models are Few-Shot Learners is the 2020 paper by Tom Brown and colleagues at OpenAI that introduced GPT-3. The model has 175 billion parameters, more than ten times larger than any earlier non-sparse language model.
4+
5+
The key finding is in-context learning. Given only a few examples written in the prompt, GPT-3 performs translation, question answering and arithmetic without any gradient update or fine-tuning. Performance improves smoothly with model size, and few-shot results approached those of fine-tuned systems on many benchmarks.
6+
7+
GPT-3 was trained on roughly 300 billion tokens of filtered Common Crawl, books and Wikipedia. The paper also discussed risks such as bias and misuse for generating misleading text.

0 commit comments

Comments
 (0)