Ship floating launcher, automatic discovery and bounded media search improvements

This commit is contained in:
karl committed 2026-10-14 11:44:15 +02:00
1 parent 01279a6b25
commit 521966a2ac
43 files changed
+3165 -267

No files matched your search

+5 -2
View File
@@ -22,6 +22,7 @@ then warms its retained CPU worker before listening on 127.0.0.1:1431.
```powershell
$env:SEARCHI_FORCE_CPU = '1'
$env:SEARCHI_EXTRACT_WORKER = (Resolve-Path target/release/searchi-extract-worker.exe).Path
cargo run --manifest-path tools/desktop-capture/backend/Cargo.toml -- --root .searchi/readme-capture/files --db .searchi/readme-capture/index.sqlite --models .searchi/models/embeddinggemma2 --worker .searchi/runtime/cpu/Release/searchi-gemma-worker.exe --dist apps/desktop/dist --ffmpeg .searchi/media-runtime/bin/ffmpeg.exe --ffprobe .searchi/media-runtime/bin/ffprobe.exe
```
@@ -46,12 +47,14 @@ loaded fonts, image pixels, and genuine worker inference counters in debug mode.
## Artifacts and limits
Outputs under docs/screenshots are search-light.png, search-dark.png,
Outputs under docs/screenshots are launcher-empty.png, search-light.png, search-dark.png,
media-search.png and performance-details.png, plus capture-manifest.json. The
manifest contains actual fixture queries, counts/selection, measured response
metrics, device/model, viewport, PNG SHA256, runner/browser versions, Git base
commit and dirty-state flag. The base commit does not identify uncommitted
source exactly. The ephemeral API token is never saved in the manifest.
source exactly. It also records the actual frontend package version and built
index SHA-256. The compact empty-bar capture has no manufactured results and
uses the genuine ready backend. The ephemeral API token is never saved in the manifest.
Captures are **browser-rendered production UI with the actual CPU engine**.
They omit native window chrome. They do not establish native WebView visual QA,
+16
View File
@@ -1129,11 +1129,16 @@ name = "searchi-core"
version = "0.1.0"
dependencies = [
"anyhow",
"libc",
"lopdf",
"quick-xml",
"rusqlite",
"searchi-media",
"serde",
"serde_json",
"sha2 0.10.9",
"walkdir",
"windows-sys",
"zip",
]
@@ -1192,11 +1197,22 @@ dependencies = [
"searchi-core",
"searchi-media",
"searchi-semantic",
"searchi-speech",
"serde",
"sha2 0.10.9",
"usearch",
]
[[package]]
name = "searchi-speech"
version = "0.1.0"
dependencies = [
"anyhow",
"serde",
"serde_json",
"sha2 0.10.9",
]
[[package]]
name = "serde"
version = "1.0.229"
+12 -2
View File
@@ -44,6 +44,8 @@ struct Filters {
max_duration_ms: Option<u32>,
modified_after_unix_ms: Option<i64>,
modified_before_unix_ms: Option<i64>,
media_artist: Option<String>,
media_title: Option<String>,
}
impl Filters {
fn options(self, root: &Path) -> Result<SearchOptions> {
@@ -63,6 +65,8 @@ impl Filters {
max_duration_ms: self.max_duration_ms,
modified_after_unix_ms: self.modified_after_unix_ms,
modified_before_unix_ms: self.modified_before_unix_ms,
media_artist: self.media_artist,
media_title: self.media_title,
})
}
}
@@ -87,6 +91,10 @@ fn main() -> Result<()> {
let mut lexical = SearchIndex::open(&args.db)?;
validate_database(&args.db, &root)?;
lexical.index_folder(&root)?;
ensure!(
lexical.extracted_source_hash(&root.join("Supplier Agreement.pdf").to_string_lossy())?.is_some(),
"Capture PDF was not genuinely extracted; set SEARCHI_EXTRACT_WORKER to the verified document helper"
);
lexical.prewarm()?;
let tools = FfmpegTools::new(args.ffmpeg, args.ffprobe)?;
let mut hybrid = HybridSearch::open(&args.db, &args.models, &args.worker, 1)?;
@@ -287,7 +295,7 @@ impl App {
json!({"model":"Google EmbeddingGemma 2","ready":true,"model_directory":self.models,"worker_ready":true,"downloading":false,"embedding":false,"device_policy":"cpu","error":null,"downloaded_bytes":864676480u64,"total_bytes":864676480u64,"generation":status.generation,"chunks":status.chunks,"stale":status.stale,"pending":status.pending,"warming":false,"worker_info":self.hybrid.worker_info(),"media_tools_ready":true,"media_setup_error":null,"media_failures":status.failures}),
)
}
"search_measured" | "hybrid_search_measured" => {
"search_measured" | "hybrid_search_measured" | "semantic_search_raw" => {
let query = args["query"].as_str().context("Query required")?;
ensure!(query.len() <= 4096, "Query too large");
let filters: Filters = if args["filters"].is_null() {
@@ -297,7 +305,9 @@ impl App {
};
let options = filters.options(&self.root)?;
let debug = args["debugPerformance"].as_bool().unwrap_or(false);
if name == "search_measured" {
if name == "semantic_search_raw" {
Ok(serde_json::to_value(self.hybrid.search_semantic(query, 50, &options)?)?)
} else if name == "search_measured" {
Ok(serde_json::to_value(
self.lexical
.search_with_options_measured(query, 50, &options, debug)?,
+41 -2
View File
@@ -92,6 +92,28 @@ try {
.evaluate((image) => image.complete && image.naturalWidth > 0),
"Brand asset failed to load",
);
// Capture the actual ready/empty production layout before typing. This is
// browser-rendered UI, not native window chrome or synthesized app results.
await page.waitForFunction(() =>
document.documentElement.dataset.launcherLayout === "compact" &&
document.querySelector("#query")?.value === "",
);
const compactPath = resolve(out, "launcher-empty.png");
const compactBounds = await page.locator("#app").boundingBox();
assert(compactBounds && compactBounds.width <= 720 && compactBounds.height <= 112,
"Ready empty launcher must remain a compact bar");
await page.locator("#app").screenshot({ path: compactPath, animations: "disabled" });
evidence.push({
file: "launcher-empty.png",
sha256: createHash("sha256").update(await readFile(compactPath)).digest("hex"),
query: "",
mode: "empty-ready",
result_count: 0,
selected_filename: null,
theme: "light",
capture_bounds: compactBounds,
state_assertions: { semantic_ready: true, compact_empty_layout: true, native_pixels: false },
});
async function query(text, debug = false) {
const awaited = page.waitForResponse(
(response) => {
@@ -151,6 +173,10 @@ try {
() => !document.querySelector("#preview")?.hidden,
);
if (text === "cancel the supplier partnership") {
assert(
!body.results.some((result) => ["Mountain Lake.jpg", "Night Pier.jpg"].includes(result.filename)),
"Contract results must not contain the unrelated fixture photos",
);
assert.equal(
latestMeasured.first_filename,
"Supplier Agreement.pdf",
@@ -225,9 +251,12 @@ try {
literal_query_marks: true,
actual_page_explanation: true,
why_disclosure_collapsed_by_default: true,
unrelated_photos_removed: true,
};
}
if (text === "mountains reflected in calm water") {
assert(body.results.every((result) => result.match_kind === "semantic_image"),
"Visual search shortlist must not contain the unrelated fixture documents");
assert.equal(
latestMeasured.first_filename,
"Mountain Lake.jpg",
@@ -238,7 +267,7 @@ try {
0,
"Meaning-only photo result must not invent word-level alignment",
);
latestMeasured.ui_checks = { meaning_only_image_has_no_text_marks: true };
latestMeasured.ui_checks = { meaning_only_image_has_no_text_marks: true, unrelated_documents_removed: true };
}
await page.evaluate(
() =>
@@ -249,8 +278,12 @@ try {
}
async function screenshot(name, queryText) {
assert.deepEqual(failures, [], "Frontend page errors occurred");
const pane = await page.locator(".results-pane").boundingBox();
const preview = await page.locator("#preview").boundingBox();
assert(pane && preview && preview.x >= pane.x + pane.width - 1,
"Wide result and preview panes must remain side by side");
const path = resolve(out, `${name}.png`);
await page.screenshot({ path, animations: "disabled", fullPage: false });
await page.locator("#app").screenshot({ path, animations: "disabled" });
const selected = await page.locator("#preview-title").innerText();
assert.equal(
selected,
@@ -272,7 +305,9 @@ try {
matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light",
),
summary: await page.locator("#result-summary").innerText(),
capture_bounds: await page.locator("#app").boundingBox(),
state_assertions: {
wide_split_preview: true,
semantic_ready: true,
hybrid_final: true,
image_visible: name === "media-search",
@@ -366,6 +401,10 @@ try {
{
kind: "browser-rendered-production-ui-real-cpu-engine",
source,
frontend_build: {
version: JSON.parse(await readFile(resolve(repository, "apps/desktop/package.json"), "utf8")).version,
index_sha256: createHash("sha256").update(await readFile(resolve(repository, "apps/desktop/dist/index.html"))).digest("hex"),
},
viewport: { width: 1440, height: 1000 },
runner: { playwright: "1.64.0", chromium: browser.version() },
backend: {
+2 -1
View File
@@ -203,7 +203,8 @@ def package(root, executable, output, cli=None):
"Windows needs WebView2. The Microsoft C++ runtime ships with these helpers; Visual Studio is not required.\n"
"First launch downloads approximately 865 MB of verified Google model files.\n"
"Local spoken-passage recognition adds approximately 149 MB of verified speech models.\n"
"Choose Add folder to index your own files; ordinary search stays local.\n"
"Use Ctrl+Shift+Space for the floating search bar. Local file discovery runs automatically.\n"
"Change automatic indexing or add a folder in Settings; ordinary search stays local.\n"
"This is an unsigned development build, not a certified production release.\n")
if cli is not None:
archive.writestr("AGENT-SETUP.txt",
@@ -0,0 +1,12 @@
{
"policy_id": "hybrid-semantic-tail-gap070-v1",
"scope": "hybrid semantic candidates before reciprocal-rank fusion",
"minimum_adjacent_gap_exclusive": 0.07,
"derivation": "Rounded upward from maximum adjacent gap 0.06504970788955688 among eight calibration All-files absence requests",
"calibration_report_sha256": "56f42c523527dd748918bb8f17be6bd7818158227e692fccaf387d915f9d0a94",
"boundary": "largest adjacent raw cosine gap in existing filtered file-deduplicated candidate list",
"equal_largest_gaps": "retain all; numeric tie tolerance is twice f32 EPSILON",
"preserve": ["strongest semantic candidate", "all independent lexical candidates", "exact filename and identifier priority", "raw semantic diagnostic API"],
"claims": ["prunes clearly separated weak semantic tails"],
"not_claimed": ["no-answer detection", "calibrated confidence", "universal embedding threshold", "new held-out or human quality certification"]
}