English · Русский · Deutsch · Français · Español · Italiano · Português (Brasil) · Polski · Українська · Türkçe · 简体中文 · 日本語 · 한국어
Query2Table — local-first research tool for desktop (Linux, Windows, macOS) and Android. You describe what data you need; the app autonomously searches the internet across multiple sources and languages, fetches pages, extracts structured entities with LLMs, deduplicates them, and presents the result as a live-updating table with row-level source links — all running on your machine without a cloud backend.
Beyond tables, it offers three more dedicated modes: image search, which finds and LLM-ranks relevant images by visual relevance; link search, which reads the full content of candidate pages and returns only the most relevant links with LLM-generated descriptions and relevance scores; and research, an agentic mode where the model autonomously searches, reads pages, reasons, and writes a sourced Markdown answer.
The app is also available for Android phones: the same app with the same modes, settings and history, built from the same code and adapted to a phone screen with a bottom tab bar.
Ask something like "Build a comparison table of open-source vector search databases and libraries with their language, license, deployment type, use case and website" and watch the table fill up in real time.
Pick a result format, describe what you need, and set stop conditions. For tables, the app proposes columns that you can edit before the search starts.
![]() |
![]() |
Open a row to read complete values, copy them, and check the pages they were extracted from.
The agent searches, reads pages and writes a sourced answer with a table of contents. Every step is visible in Activity, and follow-up questions continue the conversation with its history.
![]() |
![]() |
Image search ranks results by relevance with filters by site and size; link search reads each page and explains why it matches.
![]() |
![]() |
Search, filter and reopen past runs, run them again or export several at once. Settings check your LLM and search connections before you start.
![]() |
![]() |
![]() |
![]() |
Get the latest build from Releases:
| Platform | File |
|---|---|
| Linux | .AppImage, .deb, .rpm |
| Windows | .msi, .exe |
| macOS (Apple Silicon) | .dmg |
| Android 7.0+ | Query2Table_<version>_android.apk |
To move your setup from a computer to the phone, use Settings → Export settings…, tick Include API keys and proxies, and import the file on the phone. Such a file contains your keys: keep it private and delete it after importing.
On Android, open the downloaded APK and allow installing apps from your browser or file manager when asked. The Android app keeps its data on the phone; it has no tray, notifications appear as regular Android notifications. Local model servers (Ollama, llama.cpp) can be used over your network by their address, e.g. http://192.168.1.10:11434.
- Natural language queries — describe what you want in plain English
- Automatic schema inference — the app proposes table columns; you confirm or edit before execution
- Multi-provider search — Brave Search + Serper with automatic fallback
- Multilingual query expansion — searches across languages and geographies
- LLM-powered extraction — OpenRouter, Anthropic Claude (prompt caching, optional whole-PDF reading), Ollama (local or Cloud), DeepSeek, Groq, Mistral, Gemini, xAI, OpenAI, LM Studio, llama.cpp, or any OpenAI-compatible address
- Streaming results — rows appear in a live table as they're found
- PDF extraction — automatically detects and extracts text from PDF documents found in search results
- Image search mode — a dedicated pipeline that searches images instead of text: generates 6–12 diverse query variations (LLM or static fallbacks), executes image searches via Brave / Serper, then ranks every result with an LLM using a strict relevance rubric (0.0–1.0); images scoring below 0.7 are dropped, the rest are stored sorted by score with real-time per-image UI updates; budget, cancellation, and stop conditions (count / cost / time) are respected throughout
- Link search mode — a dedicated pipeline that returns the most relevant pages instead of a table: generates search query variations from your request, executes web searches via Brave / Serper (deduplicated by URL), fetches and parses each page, then has an LLM read the full content and score its relevance (0.0–1.0) to your query while generating a short description; pages below the confidence threshold are dropped, the rest are stored sorted by score with real-time per-link UI updates; budget, cancellation, and stop conditions are respected throughout
- Research mode — an agentic mode that answers open-ended questions in Markdown instead of a table: the LLM drives a tool-calling loop (search the web, fetch a page as Markdown, think, answer) for up to a fixed number of steps, streaming each step to a live timeline; reasoning (
think) steps and any failures (errorsteps, shown in red) are surfaced so the process stays transparent, and the final sourced Markdown answer is rendered (sanitized) and exportable as.md; budget, cancellation, and stop conditions are respected throughout - Formulas and pictures in research answers — LaTeX formulas are drawn (MathJax, offline); the agent can search for images, look at them, and place fitting ones in its answer, kept with the run
- Diagrams and charts in research answers — Mermaid diagrams and bar, line, area, pie, scatter, radar and heatmap charts, drawn offline in the app theme; saved as SVG or PNG, opened full screen
- Usage statistics — money, tokens, model calls, Brave and Serper searches and data volume by month, year and all time, with breakdowns by model and mode, the most expensive runs, disk use, CSV export and a monthly budget with warnings
- 13 interface languages — English, Russian, German, French, Spanish, Italian, Portuguese (Brazil), Polish, Ukrainian, Turkish, Chinese (Simplified), Japanese and Korean; the system language is picked by default
- Row-level sources — every row links back to the pages it was extracted from
- Entity deduplication — fuzzy matching + LLM-assisted disambiguation
- Configurable stop conditions — target row count, max cost, max duration
- Run history — browse, view, and re-export past research runs
- Export — CSV, JSON, XLSX with full source metadata
- Dark / Light theme — toggle in the sidebar
- System tray — completion notifications (desktop)
- Android app — the same app on phones, with a layout for small screens
- In-app updates — new versions are announced with their changes and installed from the app after their signature is checked (desktop and Android)
- Local-first — all data in SQLite, no cloud backend required
| Layer | Technology |
|---|---|
| App shell | Tauri v2 (desktop and Android) |
| Backend | Rust (Tokio async runtime) |
| Frontend | Svelte 5 (SvelteKit SPA) |
| UI styling | Plain CSS with design tokens |
| Database | SQLite (sqlx, WAL mode) |
| LLM | OpenRouter / Ollama (local) / Ollama Cloud / OpenAI-compatible API (e.g. llama.cpp) |
| Search | Brave Search API / Serper API |
| Icons | Lucide |
- Node.js >= 18
- Rust (stable toolchain)
- Tauri v2 prerequisites for your OS
# Clone the repository
git clone https://github.com/hightemp/query2table.git
cd query2table
# Install frontend dependencies
npm install
# Run in development mode (starts both Vite dev server and Tauri)
npm run tauri dev
# Build for production
npm run tauri buildOn first launch the app creates a local SQLite database with default settings. Open Settings from the sidebar to configure:
| Group | Settings |
|---|---|
| LLM Provider | Provider (OpenRouter / Ollama / Ollama Cloud / OpenAI-compatible), URL, model, API key, JSON mode, temperature, max tokens |
| Search Provider | Provider (Brave / Serper), API key, fallback toggle, results per query |
| Execution | Parallel fetches (default 8), parallel extractions (default 3), fetch timeout, rate limiting, robots.txt |
| Quality | Precision/recall balance, evidence strictness, confidence threshold, dedup similarity |
Stop conditions (target rows, max cost, max duration) are set per-query on the Query page.
Use backup search provider controls automatic Brave/Serper fallback for both web and image searches. Disable it to use only the selected primary provider, even if both API keys are saved. The choice applies to new runs after Save; retries stay with the selected provider. When enabled, the other provider is used only after a failure and requires its saved key.
Set Settings → Search → Results per Query to choose how many results each search query requests in all four modes, including Research, then Save for new runs. Brave requests clamp this value to the documented endpoint range: 1–20 for web search and up to 200 for images. This also covers previously saved oversized values. Larger values do not trigger automatic extra pages.
Usage & cost distinguishes provider-reported charges, model-rate estimates, partial totals, and unknown costs. Every provider attempt is counted separately, including retries, fallback searches, failed completions with usage data, and cancelled requests whose final charge could not be obtained. Input/output usage comes from provider responses; thinking tokens are not charged a second time on top of output tokens.
- OpenRouter: prefer the reported request cost. If it is unavailable, estimate from reported tokens and the actual returned model's public catalog rates.
- Ollama Cloud: estimate usage-credit consumption from published model rates, including cached input and recognized peak pricing periods. Included credits, subscription payments, and cash charges can differ from this estimate. Unrecognized or unavailable tariffs remain unknown.
- Custom model rates: configure input/output USD per million tokens in Settings. Rates belong to a specific provider, endpoint, and model. Explicit zero rates mean free usage; missing rates never silently become zero. Published rates are fetched without changing saved manual rates.
- Search: Brave and Serper default to USD 0 per 1,000 requests, excluding search charges from estimates. Set your plan's effective rate to include them, or leave the price blank if unknown. Failed attempts without billing data remain unpriced. Settings changes apply to new runs; saved history is not recalculated. On upgrade, former empty defaults become zero once, preserving existing numeric rates and subsequent deliberate blanks.
The run's Max Cost prevents new provider requests once accounted spending reaches its cap. It is not a guaranteed invoice ceiling: estimates can differ from billing, unknown charges cannot be included, and requests already in flight may still incur charges. Use provider-side limits when a strict cap is required. Accounting survives completion, failure, cancellation, and empty-result runs. Earlier history entries are explicitly labeled as historical estimates.
Deduplication preserves the first member's row ID, merges the group's values and source records transactionally, and reconciles the live table with history and export. Previously overwritten historical results cannot be reconstructed automatically; rerun those queries to obtain fresh results.
Settings keep Save and Discard visible while scrolling. Section links jump directly to the relevant controls, and leaving with unsaved changes offers Save / Discard / Stay. Edits made during a save remain available for the next save.
Results use a continuous, virtualized table with local search and keyboard sorting. Open a row's Details to read or copy complete values and inspect its saved sources. Research puts the answer before expandable activity. Images support grid/list views and keyboard previews; export and deletion show explicit progress and outcomes. Clearing the diagnostic Logs panel does not reset run activity.
Browser regression checks use synthetic data and mocked Tauri commands: install the test browsers with npx playwright install chromium webkit, then run npm run test:ui. Existing frontend tests run with npm test.
You need at least one search API key and a configured LLM provider. Local Ollama and unauthenticated OpenAI-compatible servers do not require an LLM API key:
| Service | Get a key at |
|---|---|
| Brave Search | https://brave.com/search/api/ |
| Serper | https://serper.dev/ |
| OpenRouter | https://openrouter.ai/ |
| Ollama (local) | https://ollama.com/ — no key needed |
| Ollama Cloud | https://ollama.com/settings/keys |
| OpenAI-compatible | Optional key, depending on your server |
Select the provider in Settings → LLM Provider, fill in its fields, and click Save. Each provider keeps its own model and credentials when you switch.
Settings migrations run automatically at startup: newly added options receive defaults and existing values are preserved. Renamed settings are migrated explicitly in the backend before defaults are inserted; changes to stored formats also require migration code when implementing a new version.
At the bottom of Settings, Application files shows the actual data folder, data.db database, and log folder. Use Copy path to copy a location to the system clipboard or Open folder to open it in your file manager. Opening the database location opens its containing folder. Settings, history, results, and sources live in data.db; diagnostic logs are stored separately.
- OpenRouter: enter your API key and select a model from the searchable OpenRouter catalog. Type to filter model IDs, choose with the mouse or arrow keys and Enter, and use Refresh to reload the list. The catalog can be browsed before entering a key; a key is required for inference.
- Ollama (Local): URL
http://localhost:11434and the name of an installed model. - Ollama Cloud: URL
https://ollama.com, an Ollama API key, and a model selected from the searchable server catalog. Click the model field and type to filter, then choose with the mouse or arrow keys and Enter; Refresh reloads the catalog. Filtering does not change the selected model until you choose a result. Direct cloud access uses the native Ollama API. Cloud currently lacks structured output support, so JSON is requested through prompts and parsed by the existing pipeline. - OpenAI-compatible: enter the API base URL including
/v1and the server's model ID. For llama.cpp, the default URL ishttp://localhost:8080/v1; use the loaded model name or configured alias. Leave the API key empty unless your server requires one. Disable JSON Mode if the server rejectsresponse_format.
Thinking / reasoning effort controls the model's reasoning before its answer. Auto preserves the safe defaults: structured Ollama requests disable thinking (GPT-OSS uses Low), while other providers keep their defaults. Provider default leaves reasoning to the server. Off, On, Low, Medium, High, and Max request an explicit mode or level; support depends on the model and provider. GPT-OSS on Ollama accepts Low/Medium/High and cannot use Off or Max. Changes apply to new runs after Save.
Max output tokens is a per-request allowance, potentially shared by thinking and answer tokens. It is separate from the run's cost/time limits and the provider account quota. Higher reasoning effort can leave fewer tokens for the answer.
Model request issues show the provider, model, stage, requested output cap, any reported input/output/thinking token counts, and whether a retry was scheduled. Output limits, context limits, provider quotas, rate limits, authentication, and network failures have distinct explanations and corrective actions. Issues remain visible when individual pages are skipped and are available in Run History. Other errors show a short explanation, an action to take, and collapsible technical details.
Pause suspends processing at the current stage, including schema review, and Resume continues without resubmitting the request. A request already sent to a remote model may still finish there while paused. Cancel stops local work and closes pending requests. Pipeline failures are shown and saved as failed, with the underlying error available in the run log.
The backend uses a pipeline state machine with fixed roles orchestrated in sequence:
Query → Interpret → Plan Schema → [User Confirms] → Plan Searches
→ Expand Queries → Execute Search → Fetch Pages → Parse Documents
→ Extract Entities → Validate → Deduplicate → [Stop Check] → Done
| Role | LLM | Purpose |
|---|---|---|
| QueryInterpreter | Yes | Parse query into structured intent |
| SchemaPlanner | Yes | Propose table columns and types |
| SearchPlanner | Yes | Generate search queries for multiple languages/geos |
| QueryExpander | Yes | Translate queries into target languages |
| SearchExecutor | No | Call search APIs, collect candidate URLs |
| Fetcher | No | HTTP fetch with rate limiting and robots.txt |
| DocumentParser | No | HTML → clean text (boilerplate removal) |
| PdfParser | No | PDF → clean text (via pdf-extract) |
| Extractor | Yes | Text + schema → structured rows |
| Validator | Partial | Schema conformance + semantic checks |
| Deduplicator | Partial | Fuzzy matching (strsim) + LLM for edge cases |
| LinkRanker | Yes | Read page content and score relevance (0.0–1.0) + generate description (link search mode) |
| ResearchAgent | Yes | Drive the agentic search/fetch/think/answer loop and produce a Markdown answer (research mode) |
| StoppingController | No | Evaluate stop conditions (rows, budget, time, saturation) |
Pending → SchemaReview → Running ⇄ Paused → Completed / Failed / Cancelled
query2table/
├── src-tauri/ # Rust backend
│ ├── src/
│ │ ├── main.rs # Tauri entry point
│ │ ├── commands/ # IPC command handlers
│ │ ├── orchestrator/ # Pipeline state machine, budget tracker
│ │ ├── roles/ # Pipeline & research-agent roles
│ │ ├── providers/ # External API clients (LLM, search, HTTP)
│ │ ├── storage/ # SQLite models & repository
│ │ └── export/ # CSV, JSON, XLSX export
│ └── migrations/ # SQLite schema migrations
├── src/ # Svelte frontend
│ ├── lib/
│ │ ├── components/ # UI components
│ │ ├── stores/ # Svelte stores (run, settings, ui, logs)
│ │ ├── types/ # TypeScript type definitions
│ │ └── api/ # Tauri invoke/listen wrappers
│ └── routes/ # Pages (query, history, settings)
├── package.json
└── src-tauri/Cargo.toml
SQLite in WAL mode with the following tables:
| Table | Purpose |
|---|---|
settings |
User configuration (API keys, models, preferences) |
runs |
Research run records with status and stats |
run_schemas |
Confirmed table schema per run |
search_queries |
Generated search queries per run |
search_results |
URLs collected from search APIs |
fetched_pages |
Downloaded page content |
entity_rows |
Extracted structured rows (JSON data) |
row_sources |
Row-level evidence (URL, title, snippet) |
image_results |
Ranked images per run (image search mode) |
link_results |
Ranked relevant links with descriptions and scores (link search mode) |
research_steps |
Agent tool-call steps per run — search/fetch/think/error (research mode) |
research_results |
Final Markdown answer per run (research mode) |
run_logs |
Execution logs per run |
# Frontend dev server only
npm run dev
# Type checking
npm run check
# Run tests
npm test
# Run Rust tests
cd src-tauri && cargo test
# Lint
npm run lintNeeds the Tauri Android prerequisites: Android SDK and NDK (ANDROID_HOME, NDK_HOME), Java 17 (JAVA_HOME), and the Rust targets (rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android). Connect a phone with USB debugging on, or start an emulator.
make android-devices # check the phone or emulator is connected
make android-emulator # start an emulator (AVD=<name> to pick one)
make android-apk # build dist/Query2Table_<version>_android.apk
make android-run # install it on the device and open the app
make android-logs # follow the app's log
make android-dev # live reload on the deviceBuilds are signed with the debug key unless src-tauri/gen/android/keystore.properties exists. make android-keystore creates a release key and that file, and prints the GitHub secrets (ANDROID_KEYSTORE_BASE64, ANDROID_KEY_PASSWORD, ANDROID_KEY_ALIAS) the release workflow uses to sign the APK it adds to each release. Keep the key: updates install over an existing app only when signed with the same key.
| Format | Contents |
|---|---|
| CSV | All columns + sources as JSON column |
| JSON | Full rows with nested sources array and run metadata |
| XLSX | Formatted workbook with data sheet, sources sheet, and metadata sheet |
MIT












