queryglot 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- queryglot-0.1.0/LICENSE +21 -0
- queryglot-0.1.0/PKG-INFO +206 -0
- queryglot-0.1.0/README.md +182 -0
- queryglot-0.1.0/pyproject.toml +49 -0
- queryglot-0.1.0/src/queryglot/__init__.py +30 -0
- queryglot-0.1.0/src/queryglot/_static/.gitkeep +0 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-1lL6zxyT.js +9 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-8ImQIHea.js +9 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-BDeUwW1r.js +9 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-B_o_39XA.css +1 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-Bsk8qv60.js +9 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-BzbhFHkF.js +9 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-CQP-25bj.css +1 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-CV0K9dRj.css +1 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-CZbVKFtk.js +9 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-CtT8lmCb.js +9 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-D1uIJgrL.css +1 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-D9tF5GUl.js +9 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-DFNjME17.js +9 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-JkbG_W0h.css +1 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-L6MxI3WS.js +9 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-Uuctas8m.js +9 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-VxpsF3Fj.css +1 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-Xh50kjFZ.js +9 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-ZOPLw8dP.js +9 -0
- queryglot-0.1.0/src/queryglot/_static/assets/index-qzRFxYgo.js +9 -0
- queryglot-0.1.0/src/queryglot/_static/favicon.svg +1 -0
- queryglot-0.1.0/src/queryglot/_static/index.html +14 -0
- queryglot-0.1.0/src/queryglot/_static/widget.js +1 -0
- queryglot-0.1.0/src/queryglot/backends/__init__.py +41 -0
- queryglot-0.1.0/src/queryglot/backends/elastic.py +103 -0
- queryglot-0.1.0/src/queryglot/backends/http.py +46 -0
- queryglot-0.1.0/src/queryglot/backends/openapi.py +237 -0
- queryglot-0.1.0/src/queryglot/backends/prometheus.py +318 -0
- queryglot-0.1.0/src/queryglot/catalog.py +54 -0
- queryglot-0.1.0/src/queryglot/cli.py +59 -0
- queryglot-0.1.0/src/queryglot/dataset.py +266 -0
- queryglot-0.1.0/src/queryglot/engine.py +119 -0
- queryglot-0.1.0/src/queryglot/graph.py +162 -0
- queryglot-0.1.0/src/queryglot/llm.py +98 -0
- queryglot-0.1.0/src/queryglot/mcp_server.py +88 -0
- queryglot-0.1.0/src/queryglot/prompts.py +64 -0
- queryglot-0.1.0/src/queryglot/rerank.py +70 -0
- queryglot-0.1.0/src/queryglot/retrieve.py +115 -0
- queryglot-0.1.0/src/queryglot/server.py +340 -0
queryglot-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Divyanshu Sharma
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
queryglot-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: queryglot
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: One question, many query languages. Schema-aware natural-language search over your observability stack, as an MCP server.
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: mcp,prometheus,elasticsearch,promql,observability,rag,llm
|
|
7
|
+
Author: Divyanshu Sharma
|
|
8
|
+
Author-email: sharmadivyanshu24@gmail.com
|
|
9
|
+
Requires-Python: >=3.11,<4.0
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Provides-Extra: serve
|
|
16
|
+
Requires-Dist: fastapi (>=0.115,<0.116) ; extra == "serve"
|
|
17
|
+
Requires-Dist: langgraph (>=1.2,<2.0)
|
|
18
|
+
Requires-Dist: mcp (>=1.0,<2.0)
|
|
19
|
+
Requires-Dist: pydantic (>=2.9,<3.0)
|
|
20
|
+
Requires-Dist: uvicorn (>=0.32,<0.33) ; extra == "serve"
|
|
21
|
+
Project-URL: Repository, https://github.com/sharmadivyanshu/queryglot
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# queryglot
|
|
25
|
+
|
|
26
|
+
**One question, many query languages.** Schema-aware natural-language search
|
|
27
|
+
over your observability stack — Prometheus, Elasticsearch — and any
|
|
28
|
+
OpenAPI-described API, shipped as an MCP server any agent can plug into.
|
|
29
|
+
|
|
30
|
+
> "p95 latency by route" is easy. Knowing YOUR latency metric is called
|
|
31
|
+
> `http_server_request_duration_seconds` and carries a `route` label — that's
|
|
32
|
+
> the actual problem. Frontier models write fluent PromQL over metric names
|
|
33
|
+
> that don't exist.
|
|
34
|
+
|
|
35
|
+
## How it works
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
question ──> retrieve ──> compile ──> validate ──> execute
|
|
39
|
+
(BM25 + synonyms (LLM, schema (the backend's OWN (real data,
|
|
40
|
+
over YOUR schema, slice in the parser + unknown- query shown)
|
|
41
|
+
introspected live) prompt) metric check)
|
|
42
|
+
│ ▲ │ parse error
|
|
43
|
+
│ nothing matches └─── bounded repair ┘
|
|
44
|
+
▼
|
|
45
|
+
ABSTAIN — refuses to guess a metric name
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- **Retrieval owns facts** (your metric names, labels, index fields —
|
|
49
|
+
introspected from the live backend, never hallucinated). An LLM reranker
|
|
50
|
+
orders the candidates by intent — over a closed set it can never add to.
|
|
51
|
+
- **The model owns syntax** (PromQL / Query DSL — swap in your own fine-tune
|
|
52
|
+
via any OpenAI-compatible endpoint, including `mlx_lm.server` on a Mac).
|
|
53
|
+
- **The backend owns truth**: every query is validated by the server's own
|
|
54
|
+
parser (`format_query`, `_validate/query`) before execution, and parse
|
|
55
|
+
errors drive a bounded repair loop.
|
|
56
|
+
- **Abstention is a feature**: off-schema questions get a refusal, not an
|
|
57
|
+
invented metric. The eval scores this.
|
|
58
|
+
|
|
59
|
+
## Use it from any MCP client
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"mcpServers": {
|
|
64
|
+
"queryglot": {
|
|
65
|
+
"command": "queryglot-mcp",
|
|
66
|
+
"env": {
|
|
67
|
+
"QUERYGLOT_PROMETHEUS": "http://localhost:9090",
|
|
68
|
+
"QUERYGLOT_ELASTIC": "http://localhost:9200",
|
|
69
|
+
"QUERYGLOT_OPENAPI": "http://localhost:8081/api/v3",
|
|
70
|
+
"QUERYGLOT_LLM_URL": "http://localhost:11434/v1",
|
|
71
|
+
"QUERYGLOT_LLM_MODEL": "qwen3.5:4b"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Tools exposed: `search(question, backend?)`, `list_schema(query?)`,
|
|
79
|
+
`refresh_schema()`.
|
|
80
|
+
|
|
81
|
+
Or the CLI:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
queryglot "p95 http request duration" --prometheus http://localhost:9090
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Or run an HTTP server with the ask-widget and query playground:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pip install "queryglot[serve]" # or: poetry install --extras serve
|
|
91
|
+
queryglot-serve --prometheus http://localhost:9090
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`queryglot-serve` also serves the query playground at `/` and the embeddable
|
|
95
|
+
ask-widget bundle at `/widget.js` — see "Embed the ask-widget" below.
|
|
96
|
+
|
|
97
|
+
The playground is a small Discover-style console: your schema in a filterable
|
|
98
|
+
rail (type badges, prefix groups, and the items the last answer actually
|
|
99
|
+
used), a time-range picker whose window runs as a real `query_range` — the
|
|
100
|
+
window comes from the picker, never the model, so the compile prompt stays
|
|
101
|
+
byte-stable — and results as a bar chart or histogram with the raw rows one
|
|
102
|
+
toggle away. Answered questions also get a one-sentence conversational
|
|
103
|
+
summary, grounded strictly on the returned data (never computed, never
|
|
104
|
+
invented — an empty summary beats a wrong one), and repeat questions are
|
|
105
|
+
served from a short answer cache with an honest `cached Ns ago` tag and a
|
|
106
|
+
fresh re-run button.
|
|
107
|
+
|
|
108
|
+
Environment variables (serve only):
|
|
109
|
+
- `QUERYGLOT_SERVE_TOKEN` — bearer token for `/api/*` endpoints. Empty = open (intended for localhost/demo).
|
|
110
|
+
- `QUERYGLOT_CORS_ORIGINS` — comma-separated allowed origins for embedding.
|
|
111
|
+
|
|
112
|
+
Any OpenAI-compatible endpoint works as the model: OpenAI, Ollama, or your own
|
|
113
|
+
LoRA behind `mlx_lm.server` — that last one is the point of `finetune/`.
|
|
114
|
+
|
|
115
|
+
## Embed the ask-widget
|
|
116
|
+
|
|
117
|
+
`queryglot-serve` ships a self-contained widget: a floating "Ask" pill that
|
|
118
|
+
opens a search panel wired to the same schema-grounded engine as the CLI and
|
|
119
|
+
MCP server. Drop one script tag on any page:
|
|
120
|
+
|
|
121
|
+
```html
|
|
122
|
+
<script
|
|
123
|
+
src="https://your-queryglot-host/widget.js"
|
|
124
|
+
data-api="https://your-queryglot-host"
|
|
125
|
+
data-theme="auto"
|
|
126
|
+
data-token="optional-bearer-token"
|
|
127
|
+
data-backend="optional-backend-name"
|
|
128
|
+
></script>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
- `data-api` (required) — base URL of the queryglot HTTP API.
|
|
132
|
+
- `data-theme` — `light`, `dark`, or `auto` (default; follows the host page's `prefers-color-scheme`).
|
|
133
|
+
- `data-token` — bearer token, only needed when the server sets `QUERYGLOT_SERVE_TOKEN`.
|
|
134
|
+
- `data-backend` — pins searches to one backend instead of auto-routing.
|
|
135
|
+
|
|
136
|
+
See `frontend/README.md` for the build (`npm run build:all`) that packages
|
|
137
|
+
the widget and playground into the Python wheel.
|
|
138
|
+
|
|
139
|
+
## Evaluation — deterministic, no LLM judge
|
|
140
|
+
|
|
141
|
+
`eval/run_eval.py` scores golden questions against a live backend: the
|
|
142
|
+
outcome must match, required metrics must appear in the query, and the query
|
|
143
|
+
must actually execute. Abstention cases score correct only on refusal.
|
|
144
|
+
`eval/docker-compose.yml` brings up real backends; CI runs the full
|
|
145
|
+
integration suite against a real Prometheus and petstore on every push.
|
|
146
|
+
|
|
147
|
+
## Status
|
|
148
|
+
|
|
149
|
+
**v0.1.0** — the RAG arm, working end to end.
|
|
150
|
+
|
|
151
|
+
- [x] Prometheus + Elasticsearch backends (introspect / validate / execute)
|
|
152
|
+
- [x] BM25 + synonym schema retrieval, exact-name boosting
|
|
153
|
+
- [x] compile -> validate -> repair -> execute LangGraph with abstention
|
|
154
|
+
- [x] MCP server + CLI; 141 backend tests (live-Prometheus and
|
|
155
|
+
live-petstore integration included, always exercised in CI) plus 82
|
|
156
|
+
frontend tests; CI gates on all of it
|
|
157
|
+
- [x] Verified NL->PromQL dataset generator (parse+execute gated, metric-disjoint splits)
|
|
158
|
+
- [x] Bake-off complete — RAG 8/10, FT-only 3/10, FT+RAG 9/10 on the same
|
|
159
|
+
golden set; full analysis in `finetune/README.md`, build history and
|
|
160
|
+
bugs in `DESIGN_NOTES.md`
|
|
161
|
+
- [x] OpenAPI backend — read-only, GET-only by construction; validated
|
|
162
|
+
against the spec's own contract; petstore-verified in CI
|
|
163
|
+
- [x] HTTP serve layer: answer cache, grounded conversational summaries,
|
|
164
|
+
structured schema API, bearer auth — engine outcomes are always 200
|
|
165
|
+
payloads (an abstention is an answer, not an error)
|
|
166
|
+
- [x] Embeddable ask-widget (~15 KB gz, Shadow-DOM, one script tag) +
|
|
167
|
+
Discover-style playground: schema rail, time-range windows via
|
|
168
|
+
`query_range`, histogram/bar-chart results with rows one toggle away
|
|
169
|
+
- [ ] Loki (LogQL) backend; Datadog connector
|
|
170
|
+
|
|
171
|
+
## Where this is going: apps that agents can actually use
|
|
172
|
+
|
|
173
|
+
The `Backend` protocol (introspect / validate / execute) is not
|
|
174
|
+
observability-specific. The same loop pointed at a product's own OpenAPI spec
|
|
175
|
+
or database turns any app into something an AI can query *safely*:
|
|
176
|
+
|
|
177
|
+
- **OpenAPI backend — shipped.** Introspects a product's own spec into the
|
|
178
|
+
catalog; questions compile into validated, GET-only API calls. Existing
|
|
179
|
+
OpenAPI->MCP generators dump every endpoint as a tool, which measurably
|
|
180
|
+
degrades agents (arXiv 2411.15399) and executes whatever the model asks.
|
|
181
|
+
queryglot's contribution is the missing layer: schema-grounded retrieval,
|
|
182
|
+
server-side validation, and abstention.
|
|
183
|
+
- **Customer-facing ask widget** — an embeddable search box backed by the
|
|
184
|
+
same engine: visitors' questions become validated queries against the
|
|
185
|
+
app's data, never hallucinated ones.
|
|
186
|
+
- **`llms.txt` + MCP endpoint generation** — one schema catalog, two
|
|
187
|
+
audiences: humans get the widget, agents get a typed, validated interface
|
|
188
|
+
instead of scraping. Discoverability for the agentic web, with execution
|
|
189
|
+
semantics — not just markup.
|
|
190
|
+
|
|
191
|
+
## Known limits
|
|
192
|
+
|
|
193
|
+
- `metric_candidates` (unknown-metric detection) is regex-based and
|
|
194
|
+
best-effort; the backend parser owns syntax, this only improves error
|
|
195
|
+
messages. Complex PromQL may slip past it — never through the parser.
|
|
196
|
+
- Backend auto-routing is retrieval-strength-based; ambiguous questions
|
|
197
|
+
("errors in checkout") can route to the wrong store. Pass `backend=` to pin.
|
|
198
|
+
- Synonym table is small and English-only, grown from eval failures.
|
|
199
|
+
- When a backend's catalog is smaller than the retrieval k (8), retrieval
|
|
200
|
+
sends the whole catalog and the abstention gate rarely fires — abstention
|
|
201
|
+
then rests on the validation layer.
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
MIT
|
|
206
|
+
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# queryglot
|
|
2
|
+
|
|
3
|
+
**One question, many query languages.** Schema-aware natural-language search
|
|
4
|
+
over your observability stack — Prometheus, Elasticsearch — and any
|
|
5
|
+
OpenAPI-described API, shipped as an MCP server any agent can plug into.
|
|
6
|
+
|
|
7
|
+
> "p95 latency by route" is easy. Knowing YOUR latency metric is called
|
|
8
|
+
> `http_server_request_duration_seconds` and carries a `route` label — that's
|
|
9
|
+
> the actual problem. Frontier models write fluent PromQL over metric names
|
|
10
|
+
> that don't exist.
|
|
11
|
+
|
|
12
|
+
## How it works
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
question ──> retrieve ──> compile ──> validate ──> execute
|
|
16
|
+
(BM25 + synonyms (LLM, schema (the backend's OWN (real data,
|
|
17
|
+
over YOUR schema, slice in the parser + unknown- query shown)
|
|
18
|
+
introspected live) prompt) metric check)
|
|
19
|
+
│ ▲ │ parse error
|
|
20
|
+
│ nothing matches └─── bounded repair ┘
|
|
21
|
+
▼
|
|
22
|
+
ABSTAIN — refuses to guess a metric name
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- **Retrieval owns facts** (your metric names, labels, index fields —
|
|
26
|
+
introspected from the live backend, never hallucinated). An LLM reranker
|
|
27
|
+
orders the candidates by intent — over a closed set it can never add to.
|
|
28
|
+
- **The model owns syntax** (PromQL / Query DSL — swap in your own fine-tune
|
|
29
|
+
via any OpenAI-compatible endpoint, including `mlx_lm.server` on a Mac).
|
|
30
|
+
- **The backend owns truth**: every query is validated by the server's own
|
|
31
|
+
parser (`format_query`, `_validate/query`) before execution, and parse
|
|
32
|
+
errors drive a bounded repair loop.
|
|
33
|
+
- **Abstention is a feature**: off-schema questions get a refusal, not an
|
|
34
|
+
invented metric. The eval scores this.
|
|
35
|
+
|
|
36
|
+
## Use it from any MCP client
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"mcpServers": {
|
|
41
|
+
"queryglot": {
|
|
42
|
+
"command": "queryglot-mcp",
|
|
43
|
+
"env": {
|
|
44
|
+
"QUERYGLOT_PROMETHEUS": "http://localhost:9090",
|
|
45
|
+
"QUERYGLOT_ELASTIC": "http://localhost:9200",
|
|
46
|
+
"QUERYGLOT_OPENAPI": "http://localhost:8081/api/v3",
|
|
47
|
+
"QUERYGLOT_LLM_URL": "http://localhost:11434/v1",
|
|
48
|
+
"QUERYGLOT_LLM_MODEL": "qwen3.5:4b"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Tools exposed: `search(question, backend?)`, `list_schema(query?)`,
|
|
56
|
+
`refresh_schema()`.
|
|
57
|
+
|
|
58
|
+
Or the CLI:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
queryglot "p95 http request duration" --prometheus http://localhost:9090
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Or run an HTTP server with the ask-widget and query playground:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install "queryglot[serve]" # or: poetry install --extras serve
|
|
68
|
+
queryglot-serve --prometheus http://localhost:9090
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`queryglot-serve` also serves the query playground at `/` and the embeddable
|
|
72
|
+
ask-widget bundle at `/widget.js` — see "Embed the ask-widget" below.
|
|
73
|
+
|
|
74
|
+
The playground is a small Discover-style console: your schema in a filterable
|
|
75
|
+
rail (type badges, prefix groups, and the items the last answer actually
|
|
76
|
+
used), a time-range picker whose window runs as a real `query_range` — the
|
|
77
|
+
window comes from the picker, never the model, so the compile prompt stays
|
|
78
|
+
byte-stable — and results as a bar chart or histogram with the raw rows one
|
|
79
|
+
toggle away. Answered questions also get a one-sentence conversational
|
|
80
|
+
summary, grounded strictly on the returned data (never computed, never
|
|
81
|
+
invented — an empty summary beats a wrong one), and repeat questions are
|
|
82
|
+
served from a short answer cache with an honest `cached Ns ago` tag and a
|
|
83
|
+
fresh re-run button.
|
|
84
|
+
|
|
85
|
+
Environment variables (serve only):
|
|
86
|
+
- `QUERYGLOT_SERVE_TOKEN` — bearer token for `/api/*` endpoints. Empty = open (intended for localhost/demo).
|
|
87
|
+
- `QUERYGLOT_CORS_ORIGINS` — comma-separated allowed origins for embedding.
|
|
88
|
+
|
|
89
|
+
Any OpenAI-compatible endpoint works as the model: OpenAI, Ollama, or your own
|
|
90
|
+
LoRA behind `mlx_lm.server` — that last one is the point of `finetune/`.
|
|
91
|
+
|
|
92
|
+
## Embed the ask-widget
|
|
93
|
+
|
|
94
|
+
`queryglot-serve` ships a self-contained widget: a floating "Ask" pill that
|
|
95
|
+
opens a search panel wired to the same schema-grounded engine as the CLI and
|
|
96
|
+
MCP server. Drop one script tag on any page:
|
|
97
|
+
|
|
98
|
+
```html
|
|
99
|
+
<script
|
|
100
|
+
src="https://your-queryglot-host/widget.js"
|
|
101
|
+
data-api="https://your-queryglot-host"
|
|
102
|
+
data-theme="auto"
|
|
103
|
+
data-token="optional-bearer-token"
|
|
104
|
+
data-backend="optional-backend-name"
|
|
105
|
+
></script>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
- `data-api` (required) — base URL of the queryglot HTTP API.
|
|
109
|
+
- `data-theme` — `light`, `dark`, or `auto` (default; follows the host page's `prefers-color-scheme`).
|
|
110
|
+
- `data-token` — bearer token, only needed when the server sets `QUERYGLOT_SERVE_TOKEN`.
|
|
111
|
+
- `data-backend` — pins searches to one backend instead of auto-routing.
|
|
112
|
+
|
|
113
|
+
See `frontend/README.md` for the build (`npm run build:all`) that packages
|
|
114
|
+
the widget and playground into the Python wheel.
|
|
115
|
+
|
|
116
|
+
## Evaluation — deterministic, no LLM judge
|
|
117
|
+
|
|
118
|
+
`eval/run_eval.py` scores golden questions against a live backend: the
|
|
119
|
+
outcome must match, required metrics must appear in the query, and the query
|
|
120
|
+
must actually execute. Abstention cases score correct only on refusal.
|
|
121
|
+
`eval/docker-compose.yml` brings up real backends; CI runs the full
|
|
122
|
+
integration suite against a real Prometheus and petstore on every push.
|
|
123
|
+
|
|
124
|
+
## Status
|
|
125
|
+
|
|
126
|
+
**v0.1.0** — the RAG arm, working end to end.
|
|
127
|
+
|
|
128
|
+
- [x] Prometheus + Elasticsearch backends (introspect / validate / execute)
|
|
129
|
+
- [x] BM25 + synonym schema retrieval, exact-name boosting
|
|
130
|
+
- [x] compile -> validate -> repair -> execute LangGraph with abstention
|
|
131
|
+
- [x] MCP server + CLI; 141 backend tests (live-Prometheus and
|
|
132
|
+
live-petstore integration included, always exercised in CI) plus 82
|
|
133
|
+
frontend tests; CI gates on all of it
|
|
134
|
+
- [x] Verified NL->PromQL dataset generator (parse+execute gated, metric-disjoint splits)
|
|
135
|
+
- [x] Bake-off complete — RAG 8/10, FT-only 3/10, FT+RAG 9/10 on the same
|
|
136
|
+
golden set; full analysis in `finetune/README.md`, build history and
|
|
137
|
+
bugs in `DESIGN_NOTES.md`
|
|
138
|
+
- [x] OpenAPI backend — read-only, GET-only by construction; validated
|
|
139
|
+
against the spec's own contract; petstore-verified in CI
|
|
140
|
+
- [x] HTTP serve layer: answer cache, grounded conversational summaries,
|
|
141
|
+
structured schema API, bearer auth — engine outcomes are always 200
|
|
142
|
+
payloads (an abstention is an answer, not an error)
|
|
143
|
+
- [x] Embeddable ask-widget (~15 KB gz, Shadow-DOM, one script tag) +
|
|
144
|
+
Discover-style playground: schema rail, time-range windows via
|
|
145
|
+
`query_range`, histogram/bar-chart results with rows one toggle away
|
|
146
|
+
- [ ] Loki (LogQL) backend; Datadog connector
|
|
147
|
+
|
|
148
|
+
## Where this is going: apps that agents can actually use
|
|
149
|
+
|
|
150
|
+
The `Backend` protocol (introspect / validate / execute) is not
|
|
151
|
+
observability-specific. The same loop pointed at a product's own OpenAPI spec
|
|
152
|
+
or database turns any app into something an AI can query *safely*:
|
|
153
|
+
|
|
154
|
+
- **OpenAPI backend — shipped.** Introspects a product's own spec into the
|
|
155
|
+
catalog; questions compile into validated, GET-only API calls. Existing
|
|
156
|
+
OpenAPI->MCP generators dump every endpoint as a tool, which measurably
|
|
157
|
+
degrades agents (arXiv 2411.15399) and executes whatever the model asks.
|
|
158
|
+
queryglot's contribution is the missing layer: schema-grounded retrieval,
|
|
159
|
+
server-side validation, and abstention.
|
|
160
|
+
- **Customer-facing ask widget** — an embeddable search box backed by the
|
|
161
|
+
same engine: visitors' questions become validated queries against the
|
|
162
|
+
app's data, never hallucinated ones.
|
|
163
|
+
- **`llms.txt` + MCP endpoint generation** — one schema catalog, two
|
|
164
|
+
audiences: humans get the widget, agents get a typed, validated interface
|
|
165
|
+
instead of scraping. Discoverability for the agentic web, with execution
|
|
166
|
+
semantics — not just markup.
|
|
167
|
+
|
|
168
|
+
## Known limits
|
|
169
|
+
|
|
170
|
+
- `metric_candidates` (unknown-metric detection) is regex-based and
|
|
171
|
+
best-effort; the backend parser owns syntax, this only improves error
|
|
172
|
+
messages. Complex PromQL may slip past it — never through the parser.
|
|
173
|
+
- Backend auto-routing is retrieval-strength-based; ambiguous questions
|
|
174
|
+
("errors in checkout") can route to the wrong store. Pass `backend=` to pin.
|
|
175
|
+
- Synonym table is small and English-only, grown from eval failures.
|
|
176
|
+
- When a backend's catalog is smaller than the retrieval k (8), retrieval
|
|
177
|
+
sends the whole catalog and the abstention gate rarely fires — abstention
|
|
178
|
+
then rests on the validation layer.
|
|
179
|
+
|
|
180
|
+
## License
|
|
181
|
+
|
|
182
|
+
MIT
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "queryglot"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "One question, many query languages. Schema-aware natural-language search over your observability stack, as an MCP server."
|
|
5
|
+
authors = ["Divyanshu Sharma <sharmadivyanshu24@gmail.com>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
repository = "https://github.com/sharmadivyanshu/queryglot"
|
|
9
|
+
keywords = ["mcp", "prometheus", "elasticsearch", "promql", "observability", "rag", "llm"]
|
|
10
|
+
packages = [{ include = "queryglot", from = "src" }]
|
|
11
|
+
include = [{ path = "src/queryglot/_static/**/*", format = ["sdist", "wheel"] }]
|
|
12
|
+
|
|
13
|
+
[tool.poetry.scripts]
|
|
14
|
+
queryglot = "queryglot.cli:main"
|
|
15
|
+
queryglot-mcp = "queryglot.mcp_server:main"
|
|
16
|
+
queryglot-serve = "queryglot.server:main"
|
|
17
|
+
|
|
18
|
+
[tool.poetry.dependencies]
|
|
19
|
+
python = "^3.11"
|
|
20
|
+
langgraph = "^1.2"
|
|
21
|
+
pydantic = "^2.9"
|
|
22
|
+
mcp = "^1.0"
|
|
23
|
+
fastapi = { version = "^0.115", optional = true }
|
|
24
|
+
uvicorn = { version = "^0.32", optional = true }
|
|
25
|
+
|
|
26
|
+
[tool.poetry.extras]
|
|
27
|
+
serve = ["fastapi", "uvicorn"]
|
|
28
|
+
|
|
29
|
+
[tool.poetry.group.dev.dependencies]
|
|
30
|
+
pytest = "^8.3"
|
|
31
|
+
pytest-asyncio = "^0.24"
|
|
32
|
+
ruff = "^0.8"
|
|
33
|
+
mypy = "^1.13"
|
|
34
|
+
httpx = "^0.27"
|
|
35
|
+
|
|
36
|
+
[tool.ruff]
|
|
37
|
+
line-length = 100
|
|
38
|
+
target-version = "py311"
|
|
39
|
+
|
|
40
|
+
[tool.ruff.lint]
|
|
41
|
+
select = ["E", "F", "I", "UP", "B", "SIM"]
|
|
42
|
+
|
|
43
|
+
[tool.pytest.ini_options]
|
|
44
|
+
testpaths = ["tests"]
|
|
45
|
+
asyncio_mode = "auto"
|
|
46
|
+
|
|
47
|
+
[build-system]
|
|
48
|
+
requires = ["poetry-core"]
|
|
49
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""queryglot — one question, many query languages.
|
|
2
|
+
|
|
3
|
+
Schema-aware natural-language search over observability backends, shipped as
|
|
4
|
+
an MCP server. Retrieval supplies YOUR schema (the part no model can know);
|
|
5
|
+
the model supplies syntax (the part a small fine-tune learns); the backend's
|
|
6
|
+
own parser has the final word.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .backends.elastic import ElasticBackend
|
|
10
|
+
from .backends.openapi import OpenAPIBackend
|
|
11
|
+
from .backends.prometheus import PrometheusBackend
|
|
12
|
+
from .catalog import Catalog, SchemaItem
|
|
13
|
+
from .engine import Answer, Engine
|
|
14
|
+
from .llm import LLM, OpenAICompatibleLLM
|
|
15
|
+
from .retrieve import SchemaRetriever
|
|
16
|
+
|
|
17
|
+
__version__ = "0.1.0"
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"Answer",
|
|
21
|
+
"Catalog",
|
|
22
|
+
"ElasticBackend",
|
|
23
|
+
"Engine",
|
|
24
|
+
"LLM",
|
|
25
|
+
"OpenAICompatibleLLM",
|
|
26
|
+
"OpenAPIBackend",
|
|
27
|
+
"PrometheusBackend",
|
|
28
|
+
"SchemaItem",
|
|
29
|
+
"SchemaRetriever",
|
|
30
|
+
]
|
|
File without changes
|