From 5e42821bc613f611637337cad975f8eec1ace9ab Mon Sep 17 00:00:00 2001 From: Teodor Calin Date: Tue, 16 Jun 2026 12:06:04 +0300 Subject: [PATCH 01/11] feat(eval): add -embed-url and -chat-url flags to answer-eval Allows answer-eval to run against local inference endpoints (vLLM, Ollama-compatible) without an OpenAI key. -embed-url overrides the embedding client URL; -chat-url overrides both the synth and judge chat client URL. API-key guard relaxed: only errors when all of embed-url, chat-url, and OPENAI_API_KEY are absent. --- cmd/cosift/main.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/cmd/cosift/main.go b/cmd/cosift/main.go index 0a59a3c..3a2d5da 100644 --- a/cmd/cosift/main.go +++ b/cmd/cosift/main.go @@ -5484,6 +5484,8 @@ func runAnswerEval(ctx context.Context, args []string) error { embModel := fs.String("embed-model", "text-embedding-3-small", "embedding model name") embDim := fs.Int("embed-dim", 1536, "embedding dimensionality") embCacheDir := fs.String("embed-cache", "./eval-embed-cache", "embedding cache dir (set empty to disable)") + embURL := fs.String("embed-url", "", "embedding endpoint URL (leave empty for OpenAI default)") + chatURL := fs.String("chat-url", "", "chat endpoint URL for synth+judge models") answerChunkSize := fs.Int("chunk-size", 0, "passage chunker target words (0 = default 320)") answerChunkOverlap := fs.Int("chunk-overlap", 0, "passage chunker overlap words (0 = default 64)") dryRun := fs.Bool("dry-run", false, "build the harness + print the plan, but issue NO LLM calls") @@ -5498,8 +5500,11 @@ func runAnswerEval(ctx context.Context, args []string) error { if apiKey == "" { apiKey = os.Getenv("OPENAI") } - if apiKey == "" && !*dryRun { - return errors.New("OPENAI_API_KEY (or OPENAI) not set; pass -dry-run to inspect the plan without spending") + if apiKey == "" && *embURL == "" && *chatURL == "" && !*dryRun { + return errors.New("OPENAI_API_KEY (or OPENAI) not set; pass -embed-url/-chat-url for a local endpoint, or -dry-run to inspect") + } + if apiKey == "" { + apiKey = "local" } corpus, err := eval.LoadCorpus(*corpusPath) @@ -5532,7 +5537,7 @@ func runAnswerEval(ctx context.Context, args []string) error { }() bm := index.NewBM25(st) - oai := embed.NewOpenAIClient(apiKey, "", *embModel, *embDim) + oai := embed.NewOpenAIClient(apiKey, *embURL, *embModel, *embDim) var emb embed.Embedder = oai if *embCacheDir != "" { emb = embed.NewCachedEmbedder(oai, *embCacheDir) @@ -5572,8 +5577,8 @@ func runAnswerEval(ctx context.Context, args []string) error { } } - chat := embed.NewOpenAIChat(apiKey, "", *synthModel) - judge := embed.NewOpenAIChat(apiKey, "", *judgeModel) + chat := embed.NewOpenAIChat(apiKey, *chatURL, *synthModel) + judge := embed.NewOpenAIChat(apiKey, *chatURL, *judgeModel) srv := server.New(st). WithVector(vi, emb). From f9d3d0c132209316528116b31dec62cf938509bb Mon Sep 17 00:00:00 2001 From: Teodor Calin Date: Wed, 24 Jun 2026 17:08:52 +0300 Subject: [PATCH 02/11] feat: known-item /find, host-partition, observability + feedback, harvesters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Search quality: - per-host inverted-index partition (SearchInHost) + backfill — site= O(site_docs) - BM25 MaxScore pre-check; zero-overlap site boost; rerank cap for site= - dynamic runtime allowlist (/admin/allow-domain) for organic HN/Reddit growth New capabilities: - /find: live resource federation (HuggingFace + GitHub + PyPI) with LLM planner — fixes known-item lookup the indexed corpus is weak at - query logging (qlog middleware → JSONL, X-Cosift-Query-Id) + /admin/query-log - feedback loop: POST /feedback {query_id,rating,reason} + /admin/feedback?summary=1 (reward/penalize signal joined to the query log), per-client rate limited Ingest: - WET bulk import skips host-partition writes; crawler bulk-index fast path - testdata/eval/queries-realworld.json — representative intent-spanning eval set Tests: host_partition, dynamic_allowlist, querylog/tailLines, inject. Co-Authored-By: Claude Opus 4.8 (1M context) --- cmd/cosift/assets/chat.html | 44 +- cmd/cosift/assets/landing.html | 5 + cmd/cosift/backfill_host_postings.go | 66 ++ cmd/cosift/feedback.go | 211 ++++++ cmd/cosift/find.go | 270 ++++++++ cmd/cosift/main.go | 151 +++-- cmd/cosift/pebble_serve.go | 625 ++++++++++++++++-- cmd/cosift/querylog.go | 160 +++++ cmd/cosift/querylog_test.go | 49 ++ docs/DIAGRAMS-contrib-network.md | 734 +++++++++++++++++++++ docs/PLAN-contrib-network.md | 438 ++++++++++++ docs/cosift-diagrams.pdf | Bin 0 -> 593106 bytes internal/crawler/crawler.go | 92 ++- internal/crawler/dynamic_allowlist_test.go | 57 ++ internal/crawler/store_iface.go | 9 + internal/crawler/wet.go | 10 +- internal/embed/chat.go | 19 +- internal/index/host_partition_test.go | 159 +++++ internal/index/pebble_bm25.go | 187 +++++- internal/store/pebble.go | 453 +++++++++++++ testdata/eval/queries-realworld.json | 36 + 21 files changed, 3637 insertions(+), 138 deletions(-) create mode 100644 cmd/cosift/backfill_host_postings.go create mode 100644 cmd/cosift/feedback.go create mode 100644 cmd/cosift/find.go create mode 100644 cmd/cosift/querylog.go create mode 100644 cmd/cosift/querylog_test.go create mode 100644 docs/DIAGRAMS-contrib-network.md create mode 100644 docs/PLAN-contrib-network.md create mode 100644 docs/cosift-diagrams.pdf create mode 100644 internal/crawler/dynamic_allowlist_test.go create mode 100644 internal/index/host_partition_test.go create mode 100644 testdata/eval/queries-realworld.json diff --git a/cmd/cosift/assets/chat.html b/cmd/cosift/assets/chat.html index b8c1a0a..68a536a 100644 --- a/cmd/cosift/assets/chat.html +++ b/cmd/cosift/assets/chat.html @@ -213,6 +213,22 @@ font-family: var(--mono); font-size: 11px; padding: 6px 8px; cursor: pointer; } +.site-input-wrap { + display: flex; align-items: center; gap: 4px; + border: 1px solid var(--line); border-radius: 8px; + background: var(--bg-2); padding: 0 8px; +} +.site-input-label { + font-family: var(--mono); font-size: 10px; color: var(--ink-dim); + white-space: nowrap; user-select: none; flex-shrink: 0; +} +.site-input { + border: none; background: transparent; color: var(--ink); + font-family: var(--mono); font-size: 11px; + padding: 5px 0; width: 140px; min-width: 0; + outline: none; +} +.site-input::placeholder { color: var(--ink-dim); opacity: 0.6; } .send-btn { padding: 14px 22px; background: var(--accent); color: var(--accent-ink); @@ -324,6 +340,10 @@

Chat with the corpus.

+
+ site: + +