Don't Have to Read Docs Again, Ever?
I Built a smart doc reading buddy using Modus & Dgraph for a hackathon
Ever spent hours debating whether to dig through docs, jumping between pages, just to find that one specific detail you need? Been there, done that.
What if I told you there's a way this whole process can be done on autopilot?
Ahem, introducing Docsy - a smart extension that skims through the documentation for you, and delivers you the exact parts you were looking for. All you have to do is just ask!
👉 Here’s the Github Repo, go ahead and follow the Readme to set it up and use✨
In this blog, I'll walk you through how exactly I built Docsy using Modus and Dgraph by hypermode, and everything else that went into it.
But before that, let me tell you why I built this in the first place-
Why Docsy?
Okay, confession - I'm a dev and I'm not really a big fan of reading docs (please don't cancel me🫣).
The thing is, it's not just me. We've all been there:
Getting lost in documentation rabbit holes (and finding cool stuff you don't need right now)
"I swear I just read about this somewhere..."
Finally finding the answer and thinking "how could I miss this?"
I wanted to transform this repetitive and sometimes frustrating process into something faster, smarter, and actually enjoyable. That's where Docsy came in.
And when I stumbled upon ModusHack, I knew it was a perfect opportunity to create a fun solution while playing with some new tech ^-^
The Tech Stack⚙️
🦄 Frontend: React + TypeScript + Tailwind
🧠 Backend: Modus (LLaMA) + Dgraph
But wait... what's this Modus and Dgraph we are talking about? 🤔
What is Modus?
Modus is this cool serverless framework that lets you work with AI models (like meta-llama/Meta-Llama-3.1-8B-Instruct
in our case) without the typical headache of managing servers etc. that comes with it. It supports WebAssembly & Go i.e. its super fast.
And Dgraph?
Dgraph is the memory of our project. But it’s not just any database; it's a graph database with vector search capabilities! Yes, perfect for any AI project!
Next, how they work together?
How it works?
Here’s a brief architecture diagram of the application:
So, when you ask a question (probably something like "where the heck is this configuration option?"), all this is happening behind the scenes:
The crawler scans and indexes all content to Dgraph
Each documentation chunk gets converted into embeddings for semantic search
Dgraph performs semantic search to find relevant documentation
Modus processes your query and the result sent by Dgraph, through LLaMA
Everything gets synthesized into a comprehensive response
Alright, enough theory - let's get our hands dirty! I’ll walk you through how I built this thing piece by piece 🏗️!
Let’s Build!🧱
This is what we're gonna do, we will break the whole process into 5 simple steps👇
Step: 1 -> Setting up
Step: 2 -> Dgraph setup
Step: 3 -> Adding crawler code
Step: 4 -> Setting Up Modus
Step: 5 -> Bringing It All Together
So, without further ado, let's get coding🚀
Step 1: Setting up ⚙️
Let’s start with creating the UI for Docsy and setting up the foundation of the extension.
Creating the Extension
Set Up Your Project:
Create a new folder named
extension
.Initialize it with
npm init -y
.Add your dependencies:
npm install react react-dom typescript tailwindcss
Add Chrome-Specific Files:
Create a
manifest.json
file. This tells Chrome what the extension does. Add this basic setup:{ "manifest_version": 3, "name": "Docsy", "version": "1.0", "permissions": ["tabs", "activeTab", "storage", "windows"], "background": { "service_worker": "background.js" }, "action": { "default_popup": "index.html", "default_icon": "icon.png" } }
Write the Popup UI:
Create a
src/App.tsx
file and design the layout using TailwindCSS.Add an input for queries and a section to display responses.
Use Tailwind classes to make it visually appealing.
This is how my UI looks like 👇 here is the code if you wana copy it :)
Connect to Backend
Create a
background.js
file to handle backend communication.We will use
chrome.runtime.sendMessage
to communicate with our backend (Modus + Dgraph).chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.action === "processQuery") { fetch(GRAPHQL_ENDPOINT_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query: ` query { generateDocumentationResponse( question: "${message.query}", currentUrl: "${message.currentUrl}" ) { response links { title url } } } `, }), }) .then((res) => res.json()) .then((data) => sendResponse({ success: true, data })) .catch((error) => sendResponse({ success: false, error })); return true; } });
🤸 Let’s make it a lil fun?
If you want to trigger this extension just by pressing Alt + Space
in your browser, it can simply be done by adding this to manifest.json
👇
"commands": {
"_execute_action": {
"suggested_key": {
"default": "Alt+Space",
"mac": "Alt+Space"
},
"description": "Open the popup"
}
},
Step 2: Setting Up Dgraph🛢
Time to give Docsy a memory! Dgraph will act as the knowledge base for storing and searching documentation.
Create a Dgraph Account
Go to Dgraph Cloud and create an account.
Set up a new backend and note your API endpoint and admin key.
Adding the Schema
We’ll use this schema to represent documentation pages and their relationships:
type Page {
id: ID!
title: String! @search(by: [term])
url: String! @search(by: [term])
content: String @search(by: [fulltext])
embedding: [Float!]
headings: [Heading!] @hasInverse(field: "parentPage")
}
type Heading {
id: ID!
title: String! @search(by: [term])
embedding: [Float!]
parentPage: Page!
}
Deploy the Schema
Go to the Dgraph Cloud Console.
Under the
GraphQL Schema
tab, paste the schema and deploy it.
Why Graph Databases?
Graph databases like Dgraph allow us to represent relationships (e.g., between a page and its headings).
The vector search feature enables semantic search for more intelligent results.
Step 3: Adding Crawlers🕷️
Now, let’s index documentation pages into Dgraph for semantic search.
Setting Up Puppeteer for Crawling
Install Puppeteer:
npm install puppeteer
Writing the Crawler Script
Create a
crawl.js
file indgraph-backend/src/crawler
.Use Puppeteer to extract links, content, and headings:
const puppeteer = require("puppeteer"); async function crawlDocumentation(startUrl, maxPages = 100) { const browser = await puppeteer.launch(); const visitedUrls = new Set(); const queue = [startUrl]; const pages = []; while (queue.length && pages.length < maxPages) { const url = queue.shift(); if (visitedUrls.has(url)) continue; const page = await browser.newPage(); await page.goto(url); const content = await page.content(); const links = await page.$$eval("a", (anchors) => anchors.map((a) => a.href) ); pages.push({ url, content }); queue.push(...links); visitedUrls.add(url); } await browser.close(); return pages; } module.exports = crawlDocumentation;
Storing Crawled Data
Send the extracted data to Dgraph:
const { queryDgraph } = require("../dgraph/queryDgraph"); async function storeCrawledData(pages) { const mutation = ` mutation AddPages($pages: [AddPageInput!]!) { addPage(input: $pages) { page { id title url } } } `; await queryDgraph(mutation, { pages }); }
Step 4: Setting Up Modus👾
Now, let’s add the AI magic to Docsy with Modus.
Install Modus CLI
First, install the Modus CLI globally. This will help you create and manage your Modus project:
npm install -g @hypermode/modus-cli
Create a New Modus Project
modus new
When prompted, select AssemblyScript as the SDK (p.s. AssemblyScript is super similar to TypeScript).
Add the LLaMA Model
Docsy uses the meta-llama/Meta-Llama-3.1-8B-Instruct
model for query processing. It’s hosted by Modus, so adding it is straightforward:
Open the
modus.json
file in your Modus project.Add the following configuration under
models
:
"models": {
"text-generator": {
"sourceModel": "meta-llama/Meta-Llama-3.1-8B-Instruct",
"provider": "hugging-face",
"connection": "hypermode"
}
}
Create Query Handling Logic
Update the
assembly/index.ts
to handle queries:import { models } from "@hypermode/modus-sdk-as"; export function generateDocumentationResponse(question: string): string { const model = models.getModel("text-generator"); const input = model.createInput([{ role: "user", content: question }]); const response = model.invoke(input); return response.choices[0].message.content; }
Test Your Modus Backend
Once you run this command, your endpoint will be exposed on the url: https://localhost:8686/graphql
.
modus dev
Use tools like Postman or curl to make a request to the Modus endpoint and verify the responses.
Step 5: Bringing It All Together🦾
Finally, let’s connect the frontend, Dgraph backend, and Modus AI.
Connecting the Components
Update
background.js
to handle query requests:Query Dgraph for semantic search.
If Dgraph is rate-limited, use scraped data as a fallback❗
Send the query to Modus for processing.
And….There you have it🎉
I couldn’t cover everything. But here is the full source code 📦.
Do follow this readme to add the extension to your browser!
Conclusion👋
Well, you can now officially read docs like just a chill guy😎
Here are some resources for you👇
❤️ GitHub Repo
That's all folks!
I hope you had fun reading this blog. Don't forget to share your thoughts with me.
See you on the next one!!