NEVER TRAINED ON · PRIVATE BY DEFAULT · BUILT BY MOZILLA

GITHUB

Integrate with your model stack

Giving a local model the live web with Tabstack and Open WebUI

Local models can't see anything after their training cutoff, and they can't open a link. I built a single-file Tabstack tool for Open WebUI that gives them cited live research and clean page reads, and the live runs changed the design more than I expected.

If you run models locally, you’ll hit two gaps pretty quickly. They don’t know anything that happened after their training data was collected, and they can’t open a link you paste into the chat. Ask llama3.1:8b what the Python Software Foundation announced this week and it’ll tell you, very politely, that it doesn’t have that information.

I built a Tabstack tool for Open WebUI to close both of those gaps. It’s a single Python file that an admin pastes into Open WebUI, and it gives the model two new things it can do:

  • research(question) sends the question to Tabstack Research and gets back a cited answer from live sources, with numbered [n] markers and a source list. While it runs, the chat shows what’s actually happening: planning searches, searching, writing the answer.
  • read_url(url, focus) sends a page to Tabstack Extract and gets back clean Markdown for the model to read. On a long page the model can pass a few words of focus, and the tool keeps the parts of the page that mention them rather than just the start.

There’s no extra service to run either. Open WebUI installs the Tabstack SDK from the requirements line at the top of the file when you save it. The repository has the file, a recording of it answering a question, and an install guide with screenshots.

Why you’d want it

Most people running models locally are doing it for privacy, cost or control, and have accepted that the model is going to be out of date as part of that deal. This tool lets you keep the model local and still answer current questions, with sources your users can actually check. The answer comes back with numbered markers, and Open WebUI turns those into source chips next to each point.

Every Research call costs money, so I’ve been careful about spending. The tool never retries a call on its own, and it caps how many calls the model can make in one reply. Each user can also bring their own Tabstack key, so a shared Open WebUI instance doesn’t end up billing everything to the admin.

Getting it running

  1. In Open WebUI, open Workspace > Tools, click Create, paste the whole of tabstack_tool.py over the sample, and click Save & Create.
  2. Click the gear icon on the Tabstack row and paste your Tabstack API key into Tabstack Api Key.
  3. In Admin Panel > Settings > Models, open your model, add Tabstack under Tools, and click Save & Update.

Then ask it something current.

One setting mattered far more than I expected. Open WebUI offers models around three dozen built-in tools of its own: memory, notes, knowledge bases, calendar, automations and more. With all of those switched on, llama3.1:8b and qwen3:8b didn’t call the Tabstack tool once in eight runs. With only the Memory, Knowledge Base and Files categories left on, both models called it in all four runs, and with built-in tools switched off entirely they called it in all 24.

That’s a small number of runs on two models, but the pattern was consistent. When I added categories back a group at a time, no single one was to blame. The model just stopped calling the tool once roughly 15 extra tools were on offer. So keep only the built-in tools you actually use.

How it works

Workspace tools in Open WebUI are plain Python. The tool is a class called Tools, every public method on it becomes a function the model can call, and the method’s docstring is the description the model sees. Open WebUI also passes in some special arguments that the model never sees: an event emitter for status updates, the current message id, the request, and the user along with their settings.

Research

Research is a streamed call. The tool reads Tabstack’s event stream as it arrives and turns each event into a short status line in the chat. When the final event lands, it takes Tabstack’s report and the list of pages it cited, and does a little bit of tidying before handing the answer to the model.

Duplicate sources get merged first. Tabstack can cite the same page twice under slightly different addresses: http and https, with and without www., a trailing slash, tracking parameters. The tool merges those, renumbers the [n] markers to match, and tells the model which address was folded into which number.

Then each source becomes a citation event, sent in the same order as the numbers so Open WebUI’s chips point at the right page. And the answer ends with an instruction to the model: answer the user from this, keep the markers, and don’t call the tool again. That last part came straight out of testing, and so did the call cap, which I’ll get to.

Read URL

This one’s simpler. It asks Tabstack Extract for the page as Markdown and passes up to 20,000 characters to the model, along with a note saying how much was kept.

If the model passes focus, the tool splits the page at headings and paragraph breaks, picks the parts that best match the focus words, puts them back in page order, and marks the gaps with [...]. The “What’s new in Python 3.14” page is nearly 150,000 characters, and a focus of “Zstandard” kept 3 of its 69 parts, including every mention of the new compression.zstd module.

Keys and failures

Keys come from valves, which is Open WebUI’s settings mechanism for tools. There’s an admin key, and each user can set their own in a chat under Controls > Valves > Tools > Tabstack. If a user has set a key, their calls use it. An admin can also require personal keys, so the shared key is never used at all.

Failures come back as plain messages starting with Error:, whether that’s a missing or rejected key, a timeout, a stream that ended early, or a page Tabstack couldn’t read. Each one is written for the user, and tells the model to pass it on as it is, not to add its own ideas about how to fix it, and not to call the tool again. No stack traces reach the chat, and the key is never echoed back.

How I built it

The tool is about 1,500 lines of Python. Most of the effort went into finding out how Open WebUI and Tabstack actually behave, and then writing that down.

Verify first

Before I wrote any of the tool, every behaviour it was going to rely on went into docs/VERIFIED.md. How Open WebUI loads tools, how status and citation events work, how valves are stored, what the Tabstack SDK retries, what its stream events look like. Each entry records where it was checked (the docs, the source, or a live run) and a status: confirmed, source only, observed, or unconfirmed.

Some of this you can only find in the code. Open WebUI numbers citation chips by the order sources arrive on a message, and that’s only visible in its front-end source. It ended up shaping how the tool numbers sources across several calls in one reply.

Pure functions and recorded fixtures

The file has two halves. The first is pure functions with no Open WebUI dependency at all: citation handling, status lines, trimming, error messages. The second is the small Tools class that Open WebUI loads.

There are just over 200 tests, and they all run with the network blocked. They use real Research and Extract responses recorded from the live API, plus hand-built ones for the cases a live call can’t produce on demand, like a stream that closes early. There’s also a test that checks black leaves the file unchanged, because Open WebUI reformats tool code when you save it.

The live runs changed the design

The first runs with a working key, in a local Open WebUI with real models, had to be stopped partway through. I asked llama3.1:8b which UK bank holidays were left in 2026. It got a good answer, and then called Research again with the same question, 24 times in one reply. Open WebUI allows up to 256 tool-calling turns per reply by default, so nothing on that side was going to stop it.

That’s where the per-reply cap came from. By default the model gets one Research call and three page reads per reply, and anything over that is refused without contacting Tabstack. Re-run with the cap in place, each question made exactly one call.

Other runs turned up smaller problems. One model answered the first of two questions and quietly dropped the one the cap had refused. The refusal message now names the question and asks the model to say it wasn’t looked up, and on the next run the reply ended with “I was unable to look up the most recent Formula 1 Grand Prix winner.”

The gov.uk bank holidays page was a stranger one. The Markdown came back without the table’s date column, and the model just invented dates to fill the gap. A run at maximum Extract effort lost the same column. The page marks each date as a row-header cell, and those cells get dropped. I’ve written that up for Tabstack, but the tool can’t recover something that isn’t in the page text.

Reviews caught a mistake

At one point the per-reply cap had a fallback for calls that arrive without a message id. A live check in Native mode, Open WebUI’s default, showed those requests never reach the tool, so I took the fallback out.

A code review then pointed out that I’d only checked Native mode. In the older prompt-based mode, now labelled Legacy and previously called Default, an API request without an id does run tools, with no message id, so the cap would have been switched off. A live run in that mode confirmed it. The fallback came back, rebuilt so each request gets its own counter that disappears when the request ends. A second review then caught that my first version of that could let old requests crowd out live ones.

Every run with a real key is logged in docs/EVIDENCE.md, with the prompts, what the model did, how long it took, and how many billed calls it made.

What it can’t do

Whether the model calls the tool at all is still up to the model. The tool can describe itself well, but it can’t make the model use it.

Small models often drop the [n] markers too. llama3.1:8b left them out of three of its four cited replies in one session. The sources still show in the panel under the reply, just not as chips next to each point.

The model also only sees what read_url passes on, which is either the start of a long page or the parts matching focus. If the extracted text is missing something, like those gov.uk dates, the model can fill the gap with something wrong.

And sources from other tools can shift the numbers. Citation numbers are per message, and the tool only knows about the sources it sent itself.

It’s one file, tabstack_tool.py, MIT licensed and tested on Open WebUI v0.12.3. You’ll need a model that supports tool calling and a Tabstack API key, and the install guide in the repository walks through every step, including that built-in tools setting.

Paste it in, switch off the built-in tools you don’t use, and ask your local model what happened this week.

Make a research call

One question in, a cited answer out. 10,000 free credits and no card.

START FREE

Read the guide, then make the call.

Start with 10,000 free credits. No credit card required.

curl -fsSL https://tabstack.ai/install.sh | sh