Skip to content

About

Local-first desktop app that turns a question into sourced results: tables of entities, images, relevant links, or a researched answer you can follow up on. Searches the web, reads pages and extracts data with LLMs via OpenRouter, Ollama or any OpenAI-compatible server. Built with Tauri Rust Svelte

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

Query2Table logo

Query2Table

English · Русский · Deutsch · Français · Español · Italiano · Português (Brasil) · Polski · Українська · Türkçe · 简体中文 · 日本語 · 한국어

GitHub release GitHub downloads License: MIT Built with Tauri v2 Rust backend Svelte 5 frontend
Query2Table views

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.

Comparison table of open-source vector databases with evidence and sources

Tour

Ask in plain language

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.

New query with the four result formats Proposed table schema ready for review

Every row keeps its evidence

Open a row to read complete values, copy them, and check the pages they were extracted from.

Row details with extraction confidence and sources

Research with follow-up questions

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.

Research answer with contents and suggested follow-up questions Research activity with numbered search and reading steps

Images and links

Image search ranks results by relevance with filters by site and size; link search reads each page and explains why it matches.

Image search results in a justified grid Relevant links with descriptions and match levels

History and settings

Search, filter and reopen past runs, run them again or export several at once. Settings check your LLM and search connections before you start.

Run history grouped by day with results, duration, cost and model Search settings with a successful connection test

Light and dark themes

Research answer in the dark theme Results table in the dark theme

Download

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.

Features

  • 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 (error steps, 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

Tech Stack

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

Prerequisites

Getting Started

# 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 build

Configuration

On 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, prices, and spending limits

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.

API Keys

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:11434 and 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 /v1 and the server's model ID. For llama.cpp, the default URL is http://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 rejects response_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.

Architecture

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

Pipeline Roles

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)

State Machine

Pending → SchemaReview → Running ⇄ Paused → Completed / Failed / Cancelled

Project Structure

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

Database

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

Development

# 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 lint

Android

Needs 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 device

Builds 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.

Export Formats

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

License

MIT

About

Local-first desktop app that turns a question into sourced results: tables of entities, images, relevant links, or a researched answer you can follow up on. Searches the web, reads pages and extracts data with LLMs via OpenRouter, Ollama or any OpenAI-compatible server. Built with Tauri Rust Svelte

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages