searchsocket 0.3.2 → 0.4.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.
package/dist/client.cjs CHANGED
@@ -1,5 +1,59 @@
1
1
  'use strict';
2
2
 
3
+ // src/merge.ts
4
+ function mergeSearchResults(initial, reranked, options) {
5
+ const maxDisplacement = options?.maxDisplacement ?? 3;
6
+ const initialUrls = initial.results.map((r) => r.url);
7
+ const rerankedUrls = reranked.results.map((r) => r.url);
8
+ const initialPos = /* @__PURE__ */ new Map();
9
+ for (let i = 0; i < initialUrls.length; i++) {
10
+ initialPos.set(initialUrls[i], i);
11
+ }
12
+ const rerankedPos = /* @__PURE__ */ new Map();
13
+ for (let i = 0; i < rerankedUrls.length; i++) {
14
+ rerankedPos.set(rerankedUrls[i], i);
15
+ }
16
+ const displacements = [];
17
+ for (const url of initialUrls) {
18
+ const iPos = initialPos.get(url);
19
+ const rPos = rerankedPos.get(url);
20
+ const displacement = rPos !== void 0 ? Math.abs(iPos - rPos) : 0;
21
+ displacements.push({ url, displacement });
22
+ }
23
+ const totalResults = displacements.length;
24
+ if (totalResults === 0) {
25
+ return {
26
+ response: reranked,
27
+ usedRerankedOrder: true,
28
+ displacements
29
+ };
30
+ }
31
+ const hasLargeDisplacement = displacements.some((d) => d.displacement > maxDisplacement);
32
+ if (hasLargeDisplacement) {
33
+ return {
34
+ response: reranked,
35
+ usedRerankedOrder: true,
36
+ displacements
37
+ };
38
+ }
39
+ const rerankedScoreMap = /* @__PURE__ */ new Map();
40
+ for (const result of reranked.results) {
41
+ rerankedScoreMap.set(result.url, result.score);
42
+ }
43
+ const mergedResults = initial.results.map((result) => ({
44
+ ...result,
45
+ score: rerankedScoreMap.get(result.url) ?? result.score
46
+ }));
47
+ return {
48
+ response: {
49
+ ...reranked,
50
+ results: mergedResults
51
+ },
52
+ usedRerankedOrder: false,
53
+ displacements
54
+ };
55
+ }
56
+
3
57
  // src/client.ts
4
58
  function createSearchClient(options = {}) {
5
59
  const endpoint = options.endpoint ?? "/api/search";
@@ -27,10 +81,77 @@ function createSearchClient(options = {}) {
27
81
  throw new Error(message);
28
82
  }
29
83
  return payload;
84
+ },
85
+ async streamSearch(request, onPhase) {
86
+ const response = await fetchImpl(endpoint, {
87
+ method: "POST",
88
+ headers: {
89
+ "content-type": "application/json"
90
+ },
91
+ body: JSON.stringify(request)
92
+ });
93
+ if (!response.ok) {
94
+ let payload;
95
+ try {
96
+ payload = await response.json();
97
+ } catch {
98
+ throw new Error("Search failed");
99
+ }
100
+ const message = payload.error?.message ?? "Search failed";
101
+ throw new Error(message);
102
+ }
103
+ const contentType = response.headers.get("content-type") ?? "";
104
+ if (contentType.includes("application/json")) {
105
+ const data = await response.json();
106
+ onPhase({ phase: "initial", data });
107
+ return data;
108
+ }
109
+ if (!response.body) {
110
+ throw new Error("Response body is not readable");
111
+ }
112
+ const reader = response.body.getReader();
113
+ const decoder = new TextDecoder();
114
+ let buffer = "";
115
+ let lastResponse = null;
116
+ for (; ; ) {
117
+ const { done, value } = await reader.read();
118
+ if (done) break;
119
+ buffer += decoder.decode(value, { stream: true });
120
+ let newlineIdx;
121
+ while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
122
+ const line = buffer.slice(0, newlineIdx).trim();
123
+ buffer = buffer.slice(newlineIdx + 1);
124
+ if (line.length === 0) continue;
125
+ const event = JSON.parse(line);
126
+ if (event.phase === "error") {
127
+ const errData = event.data;
128
+ throw new Error(errData.error.message ?? "Streaming search error");
129
+ }
130
+ const searchEvent = event;
131
+ onPhase(searchEvent);
132
+ lastResponse = searchEvent.data;
133
+ }
134
+ }
135
+ const remaining = buffer.trim();
136
+ if (remaining.length > 0) {
137
+ const event = JSON.parse(remaining);
138
+ if (event.phase === "error") {
139
+ const errData = event.data;
140
+ throw new Error(errData.error.message ?? "Streaming search error");
141
+ }
142
+ const searchEvent = event;
143
+ onPhase(searchEvent);
144
+ lastResponse = searchEvent.data;
145
+ }
146
+ if (!lastResponse) {
147
+ throw new Error("No search results received");
148
+ }
149
+ return lastResponse;
30
150
  }
31
151
  };
32
152
  }
33
153
 
34
154
  exports.createSearchClient = createSearchClient;
155
+ exports.mergeSearchResults = mergeSearchResults;
35
156
  //# sourceMappingURL=client.cjs.map
36
157
  //# sourceMappingURL=client.cjs.map
package/dist/client.d.cts CHANGED
@@ -1,4 +1,15 @@
1
- import { S as SearchRequest, a as SearchResponse } from './types-BrG6XTUU.cjs';
1
+ import { S as SearchResponse, M as MergeSearchOptions, a as MergeSearchResult, b as SearchRequest, c as StreamSearchEvent } from './types-z2dw3H6E.cjs';
2
+
3
+ /**
4
+ * Smart merge of initial (pre-rerank) and reranked search results.
5
+ *
6
+ * If the reranker barely changed the ordering, we keep the initial order
7
+ * (which the user already saw) and just update scores from the reranked response.
8
+ * If the reranker moved any single result by more than `maxDisplacement`
9
+ * positions, we adopt the reranked order — the reranker is semantic and
10
+ * expensive, so if it strongly disagrees on even one result, trust it.
11
+ */
12
+ declare function mergeSearchResults(initial: SearchResponse, reranked: SearchResponse, options?: MergeSearchOptions): MergeSearchResult;
2
13
 
3
14
  interface SearchClientOptions {
4
15
  endpoint?: string;
@@ -6,6 +17,10 @@ interface SearchClientOptions {
6
17
  }
7
18
  declare function createSearchClient(options?: SearchClientOptions): {
8
19
  search(request: SearchRequest): Promise<SearchResponse>;
20
+ streamSearch(request: SearchRequest & {
21
+ stream: true;
22
+ rerank: true;
23
+ }, onPhase: (event: StreamSearchEvent) => void): Promise<SearchResponse>;
9
24
  };
10
25
 
11
- export { type SearchClientOptions, createSearchClient };
26
+ export { type SearchClientOptions, createSearchClient, mergeSearchResults };
package/dist/client.d.ts CHANGED
@@ -1,4 +1,15 @@
1
- import { S as SearchRequest, a as SearchResponse } from './types-BrG6XTUU.js';
1
+ import { S as SearchResponse, M as MergeSearchOptions, a as MergeSearchResult, b as SearchRequest, c as StreamSearchEvent } from './types-z2dw3H6E.js';
2
+
3
+ /**
4
+ * Smart merge of initial (pre-rerank) and reranked search results.
5
+ *
6
+ * If the reranker barely changed the ordering, we keep the initial order
7
+ * (which the user already saw) and just update scores from the reranked response.
8
+ * If the reranker moved any single result by more than `maxDisplacement`
9
+ * positions, we adopt the reranked order — the reranker is semantic and
10
+ * expensive, so if it strongly disagrees on even one result, trust it.
11
+ */
12
+ declare function mergeSearchResults(initial: SearchResponse, reranked: SearchResponse, options?: MergeSearchOptions): MergeSearchResult;
2
13
 
3
14
  interface SearchClientOptions {
4
15
  endpoint?: string;
@@ -6,6 +17,10 @@ interface SearchClientOptions {
6
17
  }
7
18
  declare function createSearchClient(options?: SearchClientOptions): {
8
19
  search(request: SearchRequest): Promise<SearchResponse>;
20
+ streamSearch(request: SearchRequest & {
21
+ stream: true;
22
+ rerank: true;
23
+ }, onPhase: (event: StreamSearchEvent) => void): Promise<SearchResponse>;
9
24
  };
10
25
 
11
- export { type SearchClientOptions, createSearchClient };
26
+ export { type SearchClientOptions, createSearchClient, mergeSearchResults };
package/dist/client.js CHANGED
@@ -1,3 +1,57 @@
1
+ // src/merge.ts
2
+ function mergeSearchResults(initial, reranked, options) {
3
+ const maxDisplacement = options?.maxDisplacement ?? 3;
4
+ const initialUrls = initial.results.map((r) => r.url);
5
+ const rerankedUrls = reranked.results.map((r) => r.url);
6
+ const initialPos = /* @__PURE__ */ new Map();
7
+ for (let i = 0; i < initialUrls.length; i++) {
8
+ initialPos.set(initialUrls[i], i);
9
+ }
10
+ const rerankedPos = /* @__PURE__ */ new Map();
11
+ for (let i = 0; i < rerankedUrls.length; i++) {
12
+ rerankedPos.set(rerankedUrls[i], i);
13
+ }
14
+ const displacements = [];
15
+ for (const url of initialUrls) {
16
+ const iPos = initialPos.get(url);
17
+ const rPos = rerankedPos.get(url);
18
+ const displacement = rPos !== void 0 ? Math.abs(iPos - rPos) : 0;
19
+ displacements.push({ url, displacement });
20
+ }
21
+ const totalResults = displacements.length;
22
+ if (totalResults === 0) {
23
+ return {
24
+ response: reranked,
25
+ usedRerankedOrder: true,
26
+ displacements
27
+ };
28
+ }
29
+ const hasLargeDisplacement = displacements.some((d) => d.displacement > maxDisplacement);
30
+ if (hasLargeDisplacement) {
31
+ return {
32
+ response: reranked,
33
+ usedRerankedOrder: true,
34
+ displacements
35
+ };
36
+ }
37
+ const rerankedScoreMap = /* @__PURE__ */ new Map();
38
+ for (const result of reranked.results) {
39
+ rerankedScoreMap.set(result.url, result.score);
40
+ }
41
+ const mergedResults = initial.results.map((result) => ({
42
+ ...result,
43
+ score: rerankedScoreMap.get(result.url) ?? result.score
44
+ }));
45
+ return {
46
+ response: {
47
+ ...reranked,
48
+ results: mergedResults
49
+ },
50
+ usedRerankedOrder: false,
51
+ displacements
52
+ };
53
+ }
54
+
1
55
  // src/client.ts
2
56
  function createSearchClient(options = {}) {
3
57
  const endpoint = options.endpoint ?? "/api/search";
@@ -25,10 +79,76 @@ function createSearchClient(options = {}) {
25
79
  throw new Error(message);
26
80
  }
27
81
  return payload;
82
+ },
83
+ async streamSearch(request, onPhase) {
84
+ const response = await fetchImpl(endpoint, {
85
+ method: "POST",
86
+ headers: {
87
+ "content-type": "application/json"
88
+ },
89
+ body: JSON.stringify(request)
90
+ });
91
+ if (!response.ok) {
92
+ let payload;
93
+ try {
94
+ payload = await response.json();
95
+ } catch {
96
+ throw new Error("Search failed");
97
+ }
98
+ const message = payload.error?.message ?? "Search failed";
99
+ throw new Error(message);
100
+ }
101
+ const contentType = response.headers.get("content-type") ?? "";
102
+ if (contentType.includes("application/json")) {
103
+ const data = await response.json();
104
+ onPhase({ phase: "initial", data });
105
+ return data;
106
+ }
107
+ if (!response.body) {
108
+ throw new Error("Response body is not readable");
109
+ }
110
+ const reader = response.body.getReader();
111
+ const decoder = new TextDecoder();
112
+ let buffer = "";
113
+ let lastResponse = null;
114
+ for (; ; ) {
115
+ const { done, value } = await reader.read();
116
+ if (done) break;
117
+ buffer += decoder.decode(value, { stream: true });
118
+ let newlineIdx;
119
+ while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
120
+ const line = buffer.slice(0, newlineIdx).trim();
121
+ buffer = buffer.slice(newlineIdx + 1);
122
+ if (line.length === 0) continue;
123
+ const event = JSON.parse(line);
124
+ if (event.phase === "error") {
125
+ const errData = event.data;
126
+ throw new Error(errData.error.message ?? "Streaming search error");
127
+ }
128
+ const searchEvent = event;
129
+ onPhase(searchEvent);
130
+ lastResponse = searchEvent.data;
131
+ }
132
+ }
133
+ const remaining = buffer.trim();
134
+ if (remaining.length > 0) {
135
+ const event = JSON.parse(remaining);
136
+ if (event.phase === "error") {
137
+ const errData = event.data;
138
+ throw new Error(errData.error.message ?? "Streaming search error");
139
+ }
140
+ const searchEvent = event;
141
+ onPhase(searchEvent);
142
+ lastResponse = searchEvent.data;
143
+ }
144
+ if (!lastResponse) {
145
+ throw new Error("No search results received");
146
+ }
147
+ return lastResponse;
28
148
  }
29
149
  };
30
150
  }
31
151
 
32
- export { createSearchClient };
152
+ export { createSearchClient, mergeSearchResults };
33
153
  //# sourceMappingURL=client.js.map
34
154
  //# sourceMappingURL=client.js.map