Research Query
0 tokensπ Session History
π§ Memory Context
Injected into every query as background context.
π§ Technical Reference
How the Research Lab works under the hood. Updated as the system evolves.
Architecture
Single Cloudflare Pages project. Static dashboard + a Pages Function (Workers runtime) for the API. No external state.
Browser β β GET / β static index.html (this page) β POST /api/research-lab β Pages Function β OpenRouter βΌ Cloudflare Pages: research-lab βββ index.html βββ _headers βββ functions/api/research-lab.ts
research-lab-9u3.pages.devresearch.accs.website (once CNAME lands)/api/research-lab (same origin β no CORS)OPENROUTER_API_KEY stays on the server.Request flow: prompt β output
- Type query β token counter (chars/4) live; buttons unlock at β₯5 chars + model catalogue loaded.
- Click β‘ Analyse β runs rule-based preflight in the browser. Zero network. Result populates Intent Β· Complexity Β· Tokens Β· Optimised Prompt Β· per-model cost chips.
- Edit optimised prompt (optional) β overrides the raw query on Run.
- Click βΆ Run β POST to
/api/research-labwith selected models + memory context. - Pages Function fans out β
Promise.allSettledhits OpenRouter once per model in parallel. Failures isolated. - Per-model parsing β regex captures
Step N:lines; split on## Final Answer; cost computed from local pricing table Γ provider's token counts. - Response renders β model cards with collapsible Steps + Chain of Thought, final answer, token counts, per-card cost.
- Session pushed to
localStorage['rl_h'](FIFO, cap 50). Click in History to replay.
latencyEdge round-trip ~10 ms. Run dominated by slowest model in the fan-out (typically 5β30 s).
Rule-based preflight heuristics
The β‘ Analyse path is 100% browser-side. No LLM call, no cost, instant.
Intent β first matching keyword group wins, in this order. All matches are whole-word, case-insensitive (regex word boundaries) so derivation doesn't trigger derive.
| Intent | Trigger words / phrases |
|---|---|
| mathematical | derive, calculate, prove, equation, theorem, solve for, integral, optimization, differentiate, integrate, gradient, formula, probability of, expected value, matrix, eigenvalue |
| comparative_analysis | compare, vs, versus, difference between, tradeoff, trade-off, pros and cons, rank, better than, contrast, side by side, head to head |
| literature_review | literature, papers on, studies on, research on, cite, publications, survey of, review of, meta-analysis, systematic review, peer-reviewed, recent work |
| experimental_design | experiment, study design, protocol, control group, ablation, a/b test, methodology, hypothesis, placebo, double blind, sample size, treatment group, controlled trial |
| technical_deep_dive | implement, build a, code for, design a, architecture, algorithm, how does, function that, write a script, engineer, framework, pipeline, refactor, develop a, construct a, optimise/optimize the |
| factual_lookup | what is, who is, when did, where is, define, meaning of, definition of, describe, tell me about |
| exploratory | (fallback β nothing matched) |
Complexity score (1β10) β additive heuristics:
- Base:
+2 - Word count
>30 / >60 / >120β+1each tier (max +3) - Multiple question marks β
+1 - Contains acronym (3+ caps) β
+1 - Contains numeric with unit (e.g.
15kg,5%) β+1 - Contains code block,
=>,function, orclassβ+2 - Intent in {mathematical, technical_deep_dive, experimental_design} β
+1 - Intent is
factual_lookupβ-2(with floor of 1)
Score maps to: β€3 simple4β6 mediumβ₯7 hard
Model suggestions by complexity:
- simple β
gpt-oss-120b:free - medium β
gpt-oss-120b:free,gemma-4-31b-it:free - hard β
gemma-4-31b-it:free,gpt-oss-120b:free,nemotron-nano-12b-v2-vl:free
Model catalogue
All three models are on OpenRouter's :free tier β $0 per query. Subject to free-tier rate limits.
| Model | Provider | Vision | Tier |
|---|---|---|---|
| GPT OSS 120B | OpenAI | β | free |
| Nemotron Nano 12B VL | NVIDIA | image, video | free |
| Gemma 4 31B | image, video | free |
Attached images are routed to vision-capable models as image_url content blocks; text-only models receive the prompt unchanged.
API endpoints
GET /api/research-lab β health check, returns service metadata.
POST /api/research-lab with one of two actions:
// list available models + pricing
{ "action": "list_models" }
// fan out a query to N models in parallel
{
"action": "run",
"query": "...",
"models": ["openai/gpt-oss-120b:free", "nvidia/nemotron-nano-12b-v2-vl:free"],
"sessionContext": "optional memory blob",
"image": "data:image/jpeg;base64,..." // optional; only sent to vision-capable models
}
Response shape for run:
{
"outputs": [
{
"modelId": "...",
"modelName": "...",
"provider": "...",
"answer": "...",
"reasoning": "...|null",
"steps": ["Step 1: ...", ...],
"sawImage": true,
"inputTokens": 0,
"outputTokens": 0,
"cost": 0,
"error": false
},
...
],
"totalCost": 0,
"query": "..."
}
State + persistence
sessionContext on every Run.Persistence is per-browser-per-device. Clearing site data wipes history + memory. There is currently no cross-device sync.
Cost model
| Component | Free tier | Steady cost |
|---|---|---|
| Cloudflare Pages requests + bandwidth | Unlimited static | $0 |
| Cloudflare Pages Functions invocations | 100,000 / day | $0 |
| Preflight (rule-based) | β | $0 |
OpenRouter (all 3 models on :free) | rate-limited free | $0 |
Domain accs.website | owned | $0 |
Infra + per-query cost: $0/mo. Free-tier rate limits apply at OpenRouter (currently ~20 req/min and a daily cap; check the OpenRouter dashboard for current values).
Limits + known gotchas
- Token counter is char/4 β approximate. Provider's
usage.prompt_tokensis authoritative and shown post-Run. - 4096 max output tokens per model call (set in API). Long answers may be truncated.
- No retry on provider error β failed models return inline error cards. Re-Run to retry that model.
- Free-tier rate limits β OpenRouter throttles
:freemodels per-minute and per-day. Heavy use will see 429 errors that surface as inline error cards. - Image size β dropped/pasted/uploaded images are base64'd inline in the request body. Stay under ~5 MB to avoid hitting OpenRouter request-size caps.
- Vision routing β when an image is attached, it's only sent to vision-capable models (Nemotron, Gemma). Text-only models still receive the prompt.
- History capped at 50 β oldest entries fall off automatically.
Migration history
- Origin β Base44 monolithic Deno function at
scientist-1-d28c6e14.base44.app/functions/researchLab. - Migrated to Cloudflare Pages + Pages Functions, single-account, no Supabase needed.
- Removed dead import
@base44/sdk+body.apiKeyfootgun before porting. - LLM preflight stripped β replaced with deterministic browser rules.