diff --git a/DEPLOY.md b/DEPLOY.md index 9a84ba1..7031a34 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -118,8 +118,10 @@ shared secret is needed. https://floatchat.vercel.app,http://localhost:3000 ``` -5. Open the site and ask a question. The run log should fill in step by step and the answer should stream in. - If you later add a custom domain on Vercel, add it to `ALLOWED_ORIGINS` too. +5. In **Vercel → your project → Settings → Environment Variables**, add `NEXT_PUBLIC_SITE_URL` = that URL and + redeploy, so shared links show your site's preview image (the default is the original demo's address). +6. Open the site and ask a question. The run log should fill in step by step and the answer should stream in. + If you later add a custom domain on Vercel, add it to `ALLOWED_ORIGINS` and `NEXT_PUBLIC_SITE_URL` too. ## 6. Streamlit Community Cloud: the old interim UI (optional) diff --git a/docs/screenshots/web-chat-sql.png b/docs/screenshots/web-chat-sql.png index 4615cc1..2f74c62 100644 Binary files a/docs/screenshots/web-chat-sql.png and b/docs/screenshots/web-chat-sql.png differ diff --git a/eval/latest.json b/eval/latest.json index 0ce7896..2f5ff82 100644 --- a/eval/latest.json +++ b/eval/latest.json @@ -1,15 +1,15 @@ { "api": "https://floatchat-ai-svjw.onrender.com", - "ran_at": "2026-11-08 18:16 UTC", + "ran_at": "2026-11-09 05:16 UTC", "summary": { "cases": 30, - "passed": 29, - "routing_accuracy": 0.967, + "passed": 30, + "routing_accuracy": 1.0, "filter_accuracy": 1.0, "answer_pass_rate": 1.0, - "median_latency_ms": 2595, - "llm_routed": 28, - "llm_wrote_answer": 30, + "median_latency_ms": 1929, + "llm_routed": 30, + "llm_wrote_answer": 28, "failed_requests": 0 }, "results": [ @@ -31,7 +31,7 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 2701, + "latency_ms": 1249, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -61,7 +61,7 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 1498, + "latency_ms": 1783, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -95,7 +95,7 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 1712, + "latency_ms": 1839, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -121,7 +121,7 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 1957, + "latency_ms": 1772, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -147,7 +147,7 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 1836, + "latency_ms": 1976, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -173,7 +173,7 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 1807, + "latency_ms": 1572, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -203,7 +203,7 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 1931, + "latency_ms": 1764, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -229,7 +229,7 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 1818, + "latency_ms": 1782, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -259,7 +259,7 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 2003, + "latency_ms": 1883, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -285,7 +285,7 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 3129, + "latency_ms": 1999, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -315,7 +315,7 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 2463, + "latency_ms": 2010, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -343,7 +343,7 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 1996, + "latency_ms": 1492, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -374,8 +374,8 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 2165, - "router": "keywords", + "latency_ms": 1477, + "router": "llm", "llm_wrote_answer": true, "routing": true, "filters": true, @@ -402,7 +402,7 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 1672, + "latency_ms": 1625, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -432,7 +432,7 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 181, + "latency_ms": 2420, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -464,7 +464,7 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 2882, + "latency_ms": 2727, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -491,7 +491,7 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 2162, + "latency_ms": 1777, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -523,7 +523,7 @@ "answered_by": "semantic" }, "status": 200, - "latency_ms": 7575, + "latency_ms": 7085, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -553,7 +553,7 @@ "answered_by": "semantic" }, "status": 200, - "latency_ms": 3701, + "latency_ms": 16268, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -581,7 +581,7 @@ "answered_by": "semantic" }, "status": 200, - "latency_ms": 2894, + "latency_ms": 10319, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -611,7 +611,7 @@ "answered_by": "semantic" }, "status": 200, - "latency_ms": 10737, + "latency_ms": 12675, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -643,9 +643,9 @@ "answered_by": "semantic" }, "status": 200, - "latency_ms": 10217, - "router": "keywords", - "llm_wrote_answer": true, + "latency_ms": 5667, + "router": "llm", + "llm_wrote_answer": false, "routing": true, "filters": true, "answer": true, @@ -671,7 +671,7 @@ "answered_by": "semantic" }, "status": 200, - "latency_ms": 4217, + "latency_ms": 2374, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -701,7 +701,7 @@ "answered_by": "semantic" }, "status": 200, - "latency_ms": 11665, + "latency_ms": 8044, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -725,9 +725,9 @@ "answered_by": "semantic" }, "status": 200, - "latency_ms": 7732, + "latency_ms": 9042, "router": "llm", - "llm_wrote_answer": true, + "llm_wrote_answer": false, "routing": true, "filters": true, "answer": true, @@ -747,23 +747,23 @@ } }, "got": { - "path": "analytical", - "intent": "custom", + "path": "semantic", + "intent": null, "filters": { "region": "Bay of Bengal", "month_num": 9, "year": 2026 }, - "answered_by": "analytical" + "answered_by": "semantic" }, "status": 200, - "latency_ms": 4907, + "latency_ms": 193, "router": "llm", "llm_wrote_answer": true, - "routing": false, + "routing": true, "filters": true, "answer": true, - "note": "routed to analytical/custom" + "note": null }, { "id": "follow-region", @@ -787,7 +787,7 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 3397, + "latency_ms": 1843, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -819,7 +819,7 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 5795, + "latency_ms": 2524, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -849,7 +849,7 @@ "answered_by": "analytical" }, "status": 200, - "latency_ms": 2489, + "latency_ms": 1783, "router": "llm", "llm_wrote_answer": true, "routing": true, @@ -879,7 +879,7 @@ "answered_by": "semantic" }, "status": 200, - "latency_ms": 13054, + "latency_ms": 3235, "router": "llm", "llm_wrote_answer": true, "routing": true, diff --git a/eval/latest.md b/eval/latest.md index 8ae8df8..5d745d8 100644 --- a/eval/latest.md +++ b/eval/latest.md @@ -1,15 +1,15 @@ -## FloatChat evaluation — 29/30 questions fully correct +## FloatChat evaluation — 30/30 questions fully correct -Run against `https://floatchat-ai-svjw.onrender.com` on 2026-11-08 18:16 UTC. +Run against `https://floatchat-ai-svjw.onrender.com` on 2026-11-09 05:16 UTC. | Metric | Score | |---|---| -| Routing accuracy (SQL vs search, and which analysis) | 97% | +| Routing accuracy (SQL vs search, and which analysis) | 100% | | Filter accuracy (region, month, year, sensor) | 100% | | Answer checks passed | 100% | -| Median latency | 2595 ms | -| Understood by the LLM router | 28/30 | -| Answers written by the LLM | 30/30 | +| Median latency | 1929 ms | +| Understood by the LLM router | 30/30 | +| Answers written by the LLM | 28/30 | | Case | Question | Routing | Filters | Answer | Notes | |---|---|---|---|---|---| @@ -38,7 +38,7 @@ Run against `https://floatchat-ai-svjw.onrender.com` on 2026-11-08 18:16 UTC. | `no-data-red-sea` | Describe recent profiles from the Red Sea | ✅ | ✅ | ✅ | | | `find-nitrate-arabian` | Find nitrate measurements in the Arabian Sea | ✅ | ✅ | ✅ | | | `find-thermocline` | Show me profiles with a strong thermocline | ✅ | ✅ | ✅ | | -| `find-bengal-september` | Profiles in the Bay of Bengal in September 2026 | ❌ | ✅ | ✅ | routed to analytical/custom | +| `find-bengal-september` | Profiles in the Bay of Bengal in September 2026 | ✅ | ✅ | ✅ | | | `follow-region` | now only the Bay of Bengal *(follow-up to depth-avg)* | ✅ | ✅ | ✅ | | | `follow-month` | and just in March *(follow-up to depth-avg-arabian)* | ✅ | ✅ | ✅ | | | `follow-monthly-arabian` | now for the Arabian Sea *(follow-up to monthly)* | ✅ | ✅ | ✅ | | diff --git a/readme.md b/readme.md index daf63f0..6025560 100644 --- a/readme.md +++ b/readme.md @@ -3,10 +3,12 @@ **Ask questions about a year of real ocean data in plain English. FloatChat writes the SQL or searches the profiles, shows every step it took, and draws the charts.** -![FloatChat landing page](docs/screenshots/web-landing.png) +### ▶ Try it live: [float-chat-ai-teal.vercel.app](https://float-chat-ai-teal.vercel.app) + +[![FloatChat landing page](docs/screenshots/web-landing.png)](https://float-chat-ai-teal.vercel.app) **Live API:** [floatchat-ai-svjw.onrender.com/docs](https://floatchat-ai-svjw.onrender.com/docs) · -**Benchmark:** 29/30 questions fully correct ([details](eval/latest.md)) · **Deploy your own:** [DEPLOY.md](DEPLOY.md) +**Benchmark:** 30/30 questions fully correct ([details](eval/latest.md)) · **Deploy your own:** [DEPLOY.md](DEPLOY.md) **Contents:** [What FloatChat is](#-what-floatchat-is) · [How it's built](#-how-its-built) · [How well it works](#-how-well-it-works) · [Run it on your machine](#-run-it-on-your-machine) · @@ -68,7 +70,7 @@ Each float has its own page: trajectory, latest profile, a depth-time section an All data are **real Argo observations** from the **Indian Ocean**, downloaded from the [Ifremer ERDDAP](https://erddap.ifremer.fr/erddap/) server, keeping only values that passed Argo's quality control: -| | Live demo | Bundled snapshot (local setup) | +| | [Live demo](https://float-chat-ai-teal.vercel.app) | Bundled snapshot (local setup) | |---|---|---| | Period | rolling last 12 months, refreshed nightly | January to March 2024 | | Floats | about 660 (107 measuring oxygen) | 437 (52 measuring oxygen) | @@ -229,14 +231,15 @@ every Monday (`eval.yml`) and shows the table in the job summary. Without an LLM template questions and simple follow-ups; the free-phrasing and custom-SQL questions are where the LLM earns its keep. -**Latest score (live API, 8 Nov 2026): 29/30 questions fully correct.** +**Latest score (live API, 9 Nov 2026): 30/30 questions fully correct.** | Routing | Filters | Answer checks | Median latency | Answers written by the LLM | |---|---|---|---|---| -| 97% | 100% | 100% | 2.6 s | 30/30 | +| 100% | 100% | 100% | 1.9 s | 28/30 | -The one miss: *"Profiles in the Bay of Bengal in September 2026"* went to custom SQL instead of profile search. The -full per-question table is in [`eval/latest.md`](eval/latest.md). +The benchmark asks its questions back to back, so Groq's free tier sometimes hits its tokens-per-minute limit. When +that happens FloatChat still answers with a plain summary of the data, and the run log marks it *Answer built from +the data alone*. The full per-question table is in [`eval/latest.md`](eval/latest.md). --- diff --git a/web/.env.example b/web/.env.example index d24b712..a71c04d 100644 --- a/web/.env.example +++ b/web/.env.example @@ -1,2 +1,5 @@ # The FloatChat API the site talks to (the browser calls it directly) NEXT_PUBLIC_API_URL=https://floatchat-ai-svjw.onrender.com + +# This site's public address, for absolute links such as the social preview image +NEXT_PUBLIC_SITE_URL=https://float-chat-ai-teal.vercel.app diff --git a/web/app/layout.tsx b/web/app/layout.tsx index 79b0c9f..6c41565 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -2,10 +2,11 @@ import type { Metadata, Viewport } from "next"; import { GeistMono } from "geist/font/mono"; import { GeistSans } from "geist/font/sans"; import { SiteHeader } from "@/components/site-header"; +import { SITE_URL } from "@/lib/config"; import "./globals.css"; export const metadata: Metadata = { - metadataBase: new URL("https://floatchat.vercel.app"), + metadataBase: new URL(SITE_URL), title: { default: "FloatChat: ask the Indian Ocean", template: "%s · FloatChat" }, description: "Ask questions about a year of real Argo ocean-float data in plain English. FloatChat writes the SQL or searches " + diff --git a/web/lib/config.ts b/web/lib/config.ts index e3fc648..ea79937 100644 --- a/web/lib/config.ts +++ b/web/lib/config.ts @@ -7,6 +7,9 @@ export const API_URL = (process.env.NEXT_PUBLIC_API_URL || "https://floatchat-ai */ export const SERVER_API_URL = (process.env.API_URL_INTERNAL || API_URL).replace(/\/$/, ""); +/** The public address of this site, used for absolute links such as the social preview image. */ +export const SITE_URL = (process.env.NEXT_PUBLIC_SITE_URL || "https://float-chat-ai-teal.vercel.app").replace(/\/$/, ""); + export const REPO_URL = "https://github.com/CodexManvik/FloatChat-AI"; export const BENCHMARK_URL = "https://raw.githubusercontent.com/CodexManvik/FloatChat-AI/main/eval/latest.json"; export const BENCHMARK_TABLE_URL = `${REPO_URL}/blob/main/eval/latest.md`;