@gmickel/gno 0.16.0 → 0.18.0

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.
Files changed (43) hide show
  1. package/README.md +55 -2
  2. package/package.json +4 -1
  3. package/src/cli/commands/ask.ts +13 -0
  4. package/src/cli/commands/models/use.ts +1 -0
  5. package/src/cli/commands/query.ts +3 -2
  6. package/src/cli/pager.ts +1 -1
  7. package/src/cli/program.ts +107 -0
  8. package/src/config/types.ts +2 -0
  9. package/src/core/links.ts +92 -20
  10. package/src/ingestion/sync.ts +267 -23
  11. package/src/ingestion/types.ts +2 -0
  12. package/src/ingestion/walker.ts +2 -1
  13. package/src/llm/nodeLlamaCpp/generation.ts +3 -1
  14. package/src/llm/registry.ts +1 -0
  15. package/src/llm/types.ts +2 -0
  16. package/src/mcp/tools/index.ts +34 -1
  17. package/src/mcp/tools/query.ts +26 -2
  18. package/src/mcp/tools/search.ts +10 -0
  19. package/src/mcp/tools/vsearch.ts +10 -0
  20. package/src/pipeline/answer.ts +324 -7
  21. package/src/pipeline/expansion.ts +282 -11
  22. package/src/pipeline/explain.ts +93 -5
  23. package/src/pipeline/hybrid.ts +273 -70
  24. package/src/pipeline/intent.ts +152 -0
  25. package/src/pipeline/query-modes.ts +125 -0
  26. package/src/pipeline/rerank.ts +109 -51
  27. package/src/pipeline/search.ts +58 -4
  28. package/src/pipeline/temporal.ts +257 -0
  29. package/src/pipeline/types.ts +67 -0
  30. package/src/pipeline/vsearch.ts +121 -10
  31. package/src/serve/public/app.tsx +1 -3
  32. package/src/serve/public/globals.built.css +2 -2
  33. package/src/serve/public/lib/retrieval-filters.ts +174 -0
  34. package/src/serve/public/pages/Ask.tsx +378 -109
  35. package/src/serve/public/pages/Browse.tsx +71 -5
  36. package/src/serve/public/pages/DocView.tsx +2 -21
  37. package/src/serve/public/pages/Search.tsx +561 -120
  38. package/src/serve/routes/api.ts +247 -2
  39. package/src/store/migrations/006-document-metadata.ts +104 -0
  40. package/src/store/migrations/007-document-date-fields.ts +24 -0
  41. package/src/store/migrations/index.ts +3 -1
  42. package/src/store/sqlite/adapter.ts +218 -5
  43. package/src/store/types.ts +46 -0
@@ -0,0 +1,174 @@
1
+ export type TagMode = "any" | "all";
2
+
3
+ export type QueryModeType = "term" | "intent" | "hyde";
4
+
5
+ export interface QueryModeEntry {
6
+ mode: QueryModeType;
7
+ text: string;
8
+ }
9
+
10
+ export interface RetrievalFiltersState {
11
+ collection: string;
12
+ intent: string;
13
+ candidateLimit: string;
14
+ since: string;
15
+ until: string;
16
+ category: string;
17
+ author: string;
18
+ tagMode: TagMode;
19
+ tags: string[];
20
+ queryModes: QueryModeEntry[];
21
+ }
22
+
23
+ const TAG_SEGMENT_REGEX = /^[\p{Ll}\p{Lo}\p{N}][\p{Ll}\p{Lo}\p{N}\-.]*$/u;
24
+
25
+ export function normalizeTag(tag: string): string {
26
+ return tag.trim().normalize("NFC").toLowerCase();
27
+ }
28
+
29
+ export function isValidTag(tag: string): boolean {
30
+ if (tag.length === 0) {
31
+ return false;
32
+ }
33
+ if (tag.startsWith("/") || tag.endsWith("/")) {
34
+ return false;
35
+ }
36
+ const segments = tag.split("/");
37
+ for (const segment of segments) {
38
+ if (segment.length === 0 || !TAG_SEGMENT_REGEX.test(segment)) {
39
+ return false;
40
+ }
41
+ }
42
+ return true;
43
+ }
44
+
45
+ export function parseTagsCsv(csv: string): string[] {
46
+ const out: string[] = [];
47
+ const seen = new Set<string>();
48
+ for (const rawTag of csv.split(",")) {
49
+ const tag = normalizeTag(rawTag);
50
+ if (!isValidTag(tag) || seen.has(tag)) {
51
+ continue;
52
+ }
53
+ seen.add(tag);
54
+ out.push(tag);
55
+ }
56
+ return out;
57
+ }
58
+
59
+ export function parseQueryModeSpec(spec: string): QueryModeEntry | null {
60
+ const delimiter = spec.indexOf(":");
61
+ if (delimiter <= 0) {
62
+ return null;
63
+ }
64
+ const rawMode = spec.slice(0, delimiter).trim().toLowerCase();
65
+ const mode =
66
+ rawMode === "term" || rawMode === "intent" || rawMode === "hyde"
67
+ ? rawMode
68
+ : null;
69
+ if (!mode) {
70
+ return null;
71
+ }
72
+ const text = spec.slice(delimiter + 1).trim();
73
+ if (text.length === 0) {
74
+ return null;
75
+ }
76
+ return { mode, text };
77
+ }
78
+
79
+ export function serializeQueryModeSpec(entry: QueryModeEntry): string {
80
+ return `${entry.mode}:${entry.text}`;
81
+ }
82
+
83
+ export function parseQueryModes(values: string[]): QueryModeEntry[] {
84
+ const out: QueryModeEntry[] = [];
85
+ const seen = new Set<string>();
86
+ let hasHyde = false;
87
+
88
+ for (const value of values) {
89
+ const parsed = parseQueryModeSpec(value);
90
+ if (!parsed) {
91
+ continue;
92
+ }
93
+
94
+ const dedupeKey = `${parsed.mode}:${parsed.text}`;
95
+ if (seen.has(dedupeKey)) {
96
+ continue;
97
+ }
98
+
99
+ if (parsed.mode === "hyde") {
100
+ if (hasHyde) {
101
+ continue;
102
+ }
103
+ hasHyde = true;
104
+ }
105
+
106
+ seen.add(dedupeKey);
107
+ out.push(parsed);
108
+ }
109
+
110
+ return out;
111
+ }
112
+
113
+ export function parseFiltersFromSearch(
114
+ search: string,
115
+ defaults: Partial<RetrievalFiltersState> = {}
116
+ ): RetrievalFiltersState {
117
+ const params = new URLSearchParams(search);
118
+
119
+ const tagsAll = params.get("tagsAll");
120
+ const tagsAny = params.get("tagsAny");
121
+ const tagMode: TagMode = tagsAll ? "all" : "any";
122
+ const tags = parseTagsCsv(tagsAll ?? tagsAny ?? "");
123
+
124
+ return {
125
+ collection: params.get("collection") ?? defaults.collection ?? "",
126
+ intent: params.get("intent") ?? defaults.intent ?? "",
127
+ candidateLimit:
128
+ params.get("candidateLimit") ?? defaults.candidateLimit ?? "",
129
+ since: params.get("since") ?? defaults.since ?? "",
130
+ until: params.get("until") ?? defaults.until ?? "",
131
+ category: params.get("category") ?? defaults.category ?? "",
132
+ author: params.get("author") ?? defaults.author ?? "",
133
+ tagMode,
134
+ tags,
135
+ queryModes:
136
+ parseQueryModes(params.getAll("qm")) ?? defaults.queryModes ?? [],
137
+ };
138
+ }
139
+
140
+ export function applyFiltersToUrl(
141
+ url: URL,
142
+ filters: RetrievalFiltersState
143
+ ): void {
144
+ const setOrDelete = (key: string, value: string) => {
145
+ if (value.trim().length > 0) {
146
+ url.searchParams.set(key, value.trim());
147
+ } else {
148
+ url.searchParams.delete(key);
149
+ }
150
+ };
151
+
152
+ setOrDelete("collection", filters.collection);
153
+ setOrDelete("intent", filters.intent);
154
+ setOrDelete("candidateLimit", filters.candidateLimit);
155
+ setOrDelete("since", filters.since);
156
+ setOrDelete("until", filters.until);
157
+ setOrDelete("category", filters.category);
158
+ setOrDelete("author", filters.author);
159
+
160
+ url.searchParams.delete("tagsAll");
161
+ url.searchParams.delete("tagsAny");
162
+ if (filters.tags.length > 0) {
163
+ if (filters.tagMode === "all") {
164
+ url.searchParams.set("tagsAll", filters.tags.join(","));
165
+ } else {
166
+ url.searchParams.set("tagsAny", filters.tags.join(","));
167
+ }
168
+ }
169
+
170
+ url.searchParams.delete("qm");
171
+ for (const queryMode of filters.queryModes) {
172
+ url.searchParams.append("qm", serializeQueryModeSpec(queryMode));
173
+ }
174
+ }