|
| 1 | +# Contributing to Qdrant Chrome Plugin |
| 2 | + |
| 3 | +Thank you for your interest in contributing! This guide will help you get started. |
| 4 | + |
| 5 | +## Development Setup |
| 6 | + |
| 7 | +1. Clone the repository: |
| 8 | + ```bash |
| 9 | + git clone https://github.com/qdrant/qdrant-chrome-plugin.git |
| 10 | + cd qdrant-chrome-plugin |
| 11 | + ``` |
| 12 | + |
| 13 | +2. Load the extension in Chrome: |
| 14 | + - Open `chrome://extensions` |
| 15 | + - Enable **Developer mode** |
| 16 | + - Click **Load unpacked** and select the project folder |
| 17 | + |
| 18 | +3. After making changes, click the **reload** button on the extension card in `chrome://extensions` |
| 19 | + |
| 20 | +No build tools or npm install required - the extension uses vanilla HTML/CSS/JS. |
| 21 | + |
| 22 | +## Architecture |
| 23 | + |
| 24 | +### Key Files |
| 25 | + |
| 26 | +- **`lib/qdrant-api.js`** - Qdrant REST API client. All API calls go through here. |
| 27 | +- **`lib/storage.js`** - Wrapper around `chrome.storage.local` for cluster config persistence. |
| 28 | +- **`popup/popup.js`** - Cluster connection management (add/edit/delete/test). |
| 29 | +- **`dashboard/dashboard.js`** - Main dashboard rendering logic. |
| 30 | +- **`dashboard/rules/rule-engine.js`** - Insight rule registry, executor, and renderer. |
| 31 | +- **`dashboard/rules/*.js`** - Individual rule files grouped by scope. |
| 32 | + |
| 33 | +### Data Flow |
| 34 | + |
| 35 | +``` |
| 36 | +User clicks cluster → dashboard.js loads |
| 37 | + → QdrantApi.getDashboardData() fetches from all endpoints |
| 38 | + → RuleEngine.run(dashboardData) executes all registered rules |
| 39 | + → RuleEngine.render(insights) shows insights panel |
| 40 | + → renderDashboard() renders all tabs |
| 41 | +``` |
| 42 | + |
| 43 | +## Adding New Rules |
| 44 | + |
| 45 | +The rule engine is designed to make adding new rules as simple as possible. Each rule is a self-contained function that receives the full dashboard context and returns zero or more insights. |
| 46 | + |
| 47 | +### Step 1: Choose the Right File |
| 48 | + |
| 49 | +| File | Scope | |
| 50 | +|-----------------------------|---------------------------------------------------------| |
| 51 | +| `rules/cluster-rules.js` | Cluster-wide: memory, raft, consensus, nodes | |
| 52 | +| `rules/collection-rules.js` | Per-collection: config, performance, indexing | |
| 53 | +| `rules/segment-rules.js` | Per-shard/segment: replicas, optimizer, deleted vectors | |
| 54 | + |
| 55 | +Or create a new file (e.g., `rules/my-rules.js`) and add a `<script>` tag in `dashboard.html` before `dashboard.js`. |
| 56 | + |
| 57 | +### Step 2: Write the Rule |
| 58 | + |
| 59 | +```js |
| 60 | +RuleEngine.register('my-rule-name', function(ctx) { |
| 61 | + const insights = []; |
| 62 | + |
| 63 | + // ctx contains: |
| 64 | + // ctx.cluster - GET /cluster result |
| 65 | + // ctx.collections - array of collection names |
| 66 | + // ctx.collectionDetails - { [name]: { info, cluster } } |
| 67 | + // ctx.telemetry - GET /telemetry result (connected node) |
| 68 | + // ctx.nodeTelemetry - { [peerId]: telemetry } (all reachable nodes) |
| 69 | + |
| 70 | + for (const name of ctx.collections) { |
| 71 | + const info = ctx.collectionDetails[name]?.info; |
| 72 | + if (!info) continue; |
| 73 | + |
| 74 | + if (/* your condition */) { |
| 75 | + insights.push({ |
| 76 | + level: 'warning', // critical | warning | performance | info |
| 77 | + category: 'config', // memory | optimizer | replication | indexing | config | cluster |
| 78 | + collection: name, // or null for cluster-wide insights |
| 79 | + title: 'Short title', |
| 80 | + detail: 'Explanation with actionable advice.', |
| 81 | + }); |
| 82 | + } |
| 83 | + } |
| 84 | + |
| 85 | + return insights; |
| 86 | +}); |
| 87 | +``` |
| 88 | +
|
| 89 | +### Step 3: Done |
| 90 | +
|
| 91 | +No other files need to change. The rule automatically registers on script load, executes when data loads, and appears in the insights panel. |
| 92 | +
|
| 93 | +### Insight Levels |
| 94 | +
|
| 95 | +| Level | When to Use | Display | |
| 96 | +|---------------|--------------------------------------------------------|------------------------| |
| 97 | +| `critical` | Immediate action required (data loss risk, errors) | Always visible, red | |
| 98 | +| `warning` | Should be addressed soon (degraded performance, no HA) | Always visible, yellow | |
| 99 | +| `performance` | Optimization opportunity (quantization, indexes) | Collapsible, purple | |
| 100 | +| `info` | Informational (current state, no action needed) | Collapsible, blue | |
| 101 | +
|
| 102 | +### Guidelines for Rules |
| 103 | +
|
| 104 | +- One rule should check one thing |
| 105 | +- Use descriptive rule names: `no-quantization`, `high-segment-count`, `replica-not-active` |
| 106 | +- Include actionable advice in `detail` - don't just report the problem, suggest a fix |
| 107 | +- Use `formatNumber()` and `formatBytes()` for readable values (available globally) |
| 108 | +- Set appropriate thresholds - avoid noisy rules that trigger on tiny collections |
| 109 | +- Each rule is wrapped in try/catch by the engine, so a broken rule won't crash the dashboard |
| 110 | +
|
| 111 | +### Iterating Over Multiple Nodes |
| 112 | +
|
| 113 | +For segment-level rules that need telemetry from all nodes, use the helper pattern from `segment-rules.js`: |
| 114 | +
|
| 115 | +```js |
| 116 | +function _allNodeTelemetries(ctx) { |
| 117 | + if (ctx.nodeTelemetry && Object.keys(ctx.nodeTelemetry).length > 0) { |
| 118 | + return Object.entries(ctx.nodeTelemetry); |
| 119 | + } |
| 120 | + return [[ctx.cluster?.peer_id?.toString() || 'local', ctx.telemetry]]; |
| 121 | +} |
| 122 | +``` |
| 123 | +
|
| 124 | +## Submitting Changes |
| 125 | +
|
| 126 | +1. Fork the repository |
| 127 | +2. Create a feature branch: `git checkout -b feature/my-feature` |
| 128 | +3. Make your changes |
| 129 | +4. Test with at least one Qdrant instance (local Docker is fine) |
| 130 | +5. Submit a pull request |
| 131 | +
|
| 132 | +### PR Guidelines |
| 133 | +
|
| 134 | +- Keep PRs focused - one feature or fix per PR |
| 135 | +- Test the extension by loading it unpacked in Chrome |
| 136 | +- For new rules, include a brief description of what it detects and why it matters |
| 137 | +- Update README.md if adding new features visible to users |
| 138 | +
|
| 139 | +## Reporting Issues |
| 140 | +
|
| 141 | +- Use [GitHub Issues](../../issues) |
| 142 | +- Include your Qdrant version and Chrome version |
| 143 | +- Screenshots of the dashboard are helpful |
| 144 | +- For rule suggestions, describe the scenario and what insight should be shown |
0 commit comments