@gmickel/gno 0.9.3 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -332,14 +332,14 @@ Models auto-download on first use to `~/.cache/gno/models/`.
332
332
 
333
333
  ### Model Presets
334
334
 
335
- | Preset | Disk | Best For |
336
- | :--------- | :----- | :--------------------- |
337
- | `slim` | ~1GB | Fast, lower quality |
338
- | `balanced` | ~2GB | Good balance (default) |
339
- | `quality` | ~2.5GB | Best answers |
335
+ | Preset | Disk | Best For |
336
+ | :--------- | :----- | :--------------------------- |
337
+ | `slim` | ~1GB | Fast, good quality (default) |
338
+ | `balanced` | ~2GB | Slightly larger model |
339
+ | `quality` | ~2.5GB | Best answers |
340
340
 
341
341
  ```bash
342
- gno models use balanced
342
+ gno models use slim
343
343
  gno models pull --all # Optional: pre-download models (auto-downloads on first use)
344
344
  ```
345
345
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gmickel/gno",
3
- "version": "0.9.3",
3
+ "version": "0.9.4",
4
4
  "description": "Local semantic search for your documents. Index Markdown, PDF, and Office files with hybrid BM25 + vector search.",
5
5
  "keywords": [
6
6
  "embeddings",
@@ -48,6 +48,9 @@
48
48
  "test:coverage": "bun test --coverage",
49
49
  "test:coverage:html": "bun test --coverage --html",
50
50
  "test:fixtures": "bun scripts/generate-test-fixtures.ts",
51
+ "evals": "bun scripts/update-eval-scores.ts",
52
+ "eval": "bun --bun evalite",
53
+ "eval:watch": "bun --bun evalite watch",
51
54
  "reset": "bun run src/index.ts reset --confirm",
52
55
  "docs:verify": "bun run scripts/docs-verify.ts",
53
56
  "website:install": "cd website && bundle install",
@@ -110,6 +113,7 @@
110
113
  "zod": "^4.3.4"
111
114
  },
112
115
  "devDependencies": {
116
+ "@ai-sdk/openai": "^3.0.2",
113
117
  "@biomejs/biome": "2.3.10",
114
118
  "@tailwindcss/cli": "^4.1.18",
115
119
  "@types/bun": "latest",
@@ -126,7 +130,8 @@
126
130
  "oxlint-tsgolint": "^0.10.1",
127
131
  "pdf-lib": "^1.17.1",
128
132
  "pptxgenjs": "^4.0.1",
129
- "ultracite": "7.0.4"
133
+ "ultracite": "7.0.4",
134
+ "vitest": "^4.0.16"
130
135
  },
131
136
  "peerDependencies": {
132
137
  "typescript": "^5"
@@ -176,7 +176,7 @@ export type ModelPreset = z.infer<typeof ModelPresetSchema>;
176
176
  export const DEFAULT_MODEL_PRESETS: ModelPreset[] = [
177
177
  {
178
178
  id: "slim",
179
- name: "Slim (Fast, ~1GB)",
179
+ name: "Slim (Default, ~1GB)",
180
180
  embed: "hf:gpustack/bge-m3-GGUF/bge-m3-Q4_K_M.gguf",
181
181
  rerank:
182
182
  "hf:ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF/qwen3-reranker-0.6b-q8_0.gguf",
@@ -184,11 +184,11 @@ export const DEFAULT_MODEL_PRESETS: ModelPreset[] = [
184
184
  },
185
185
  {
186
186
  id: "balanced",
187
- name: "Balanced (Default, ~2GB)",
187
+ name: "Balanced (~2GB)",
188
188
  embed: "hf:gpustack/bge-m3-GGUF/bge-m3-Q4_K_M.gguf",
189
189
  rerank:
190
190
  "hf:ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF/qwen3-reranker-0.6b-q8_0.gguf",
191
- gen: "hf:ggml-org/SmolLM3-3B-GGUF/SmolLM3-Q4_K_M.gguf",
191
+ gen: "hf:bartowski/Qwen2.5-3B-Instruct-GGUF/Qwen2.5-3B-Instruct-Q4_K_M.gguf",
192
192
  },
193
193
  {
194
194
  id: "quality",
@@ -202,7 +202,7 @@ export const DEFAULT_MODEL_PRESETS: ModelPreset[] = [
202
202
 
203
203
  export const ModelConfigSchema = z.object({
204
204
  /** Active preset ID */
205
- activePreset: z.string().default("balanced"),
205
+ activePreset: z.string().default("slim"),
206
206
  /** Model presets */
207
207
  presets: z.array(ModelPresetSchema).default(DEFAULT_MODEL_PRESETS),
208
208
  /** Model load timeout in ms */
@@ -19,7 +19,7 @@ import { DEFAULT_MODEL_PRESETS } from "../config/types";
19
19
  */
20
20
  export function getModelConfig(config: Config): ModelConfig {
21
21
  return {
22
- activePreset: config.models?.activePreset ?? "balanced",
22
+ activePreset: config.models?.activePreset ?? "slim",
23
23
  presets: config.models?.presets?.length
24
24
  ? config.models.presets
25
25
  : DEFAULT_MODEL_PRESETS,
@@ -13,21 +13,24 @@ import type { Citation, SearchResult } from "./types";
13
13
  // Constants
14
14
  // ─────────────────────────────────────────────────────────────────────────────
15
15
 
16
- const ANSWER_PROMPT = `You are answering a question using ONLY the provided context blocks.
16
+ const ANSWER_PROMPT = `Answer the question using ONLY the context blocks below. Cite sources with [1], [2], etc.
17
17
 
18
- Rules you MUST follow:
19
- 1) Use ONLY facts stated in the context blocks. Do NOT use outside knowledge.
20
- 2) Every factual statement must include an inline citation like [1] or [2] referring to a context block.
21
- 3) If the context does not contain enough information to answer, reply EXACTLY:
22
- "I don't have enough information in the provided sources to answer this question."
23
- 4) Do not cite sources you did not use. Do not invent citation numbers.
18
+ Example:
19
+ Q: What is the capital of France?
20
+ Context:
21
+ [1] France is a country in Western Europe. Paris is the capital and largest city.
22
+ [2] The Eiffel Tower, built in 1889, is located in Paris.
24
23
 
25
- Question: {query}
24
+ Answer: Paris is the capital of France [1]. It is home to the Eiffel Tower [2].
26
25
 
27
- Context blocks:
26
+ ---
27
+
28
+ Q: {query}
29
+
30
+ Context:
28
31
  {context}
29
32
 
30
- Write a concise answer (1-3 paragraphs).`;
33
+ Answer:`;
31
34
 
32
35
  /** Abstention message when LLM cannot ground answer */
33
36
  export const ABSTENTION_MESSAGE =
@@ -223,7 +223,11 @@ export async function searchBm25(
223
223
 
224
224
  // For --full, fetch full content and build results
225
225
  if (options.full) {
226
- for (const { fts, chunk } of bestByDocid.values()) {
226
+ // Sort by raw BM25 score (smaller = better) before building results
227
+ const sortedEntries = [...bestByDocid.values()].sort(
228
+ (a, b) => a.score - b.score
229
+ );
230
+ for (const { fts, chunk } of sortedEntries) {
227
231
  let fullContent: string | undefined;
228
232
  if (fts.mirrorHash) {
229
233
  const contentResult = await store.getContent(fts.mirrorHash);