Skip to content

Commit c4d0bcc

Browse files
Merge pull request #107 from Poxel2/upstream/viewer-fixes
fix(web): conclusion knowledge search with target discovery and correct size pagination
2 parents 571623e + fc43695 commit c4d0bcc

6 files changed

Lines changed: 536 additions & 12 deletions

File tree

‎packages/web/src/api/compareQueries.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export function useScopedWorkspaces(instance: Instance, page = 1, pageSize = 20)
2727
queryFn: async () => {
2828
const client = createScopedClient(instance);
2929
const { data, error } = await client.POST("/v3/workspaces/list", {
30-
params: { query: { page, page_size: pageSize } },
30+
params: { query: { page, size: pageSize } },
3131
body: {},
3232
});
3333
return data ?? err(error);
@@ -41,7 +41,7 @@ export function useScopedPeers(instance: Instance, workspaceId: string, page = 1
4141
queryFn: async () => {
4242
const client = createScopedClient(instance);
4343
const { data, error } = await client.POST("/v3/workspaces/{workspace_id}/peers/list", {
44-
params: { path: { workspace_id: workspaceId }, query: { page, page_size: pageSize } },
44+
params: { path: { workspace_id: workspaceId }, query: { page, size: pageSize } },
4545
body: {},
4646
});
4747
return data ?? err(error);

‎packages/web/src/api/queries.ts‎

Lines changed: 115 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export function useWorkspaces(page = 1, pageSize = 20) {
2424
queryKey: QK.workspaces(page, pageSize),
2525
queryFn: async () => {
2626
const { data, error } = await client.current.POST("/v3/workspaces/list", {
27-
params: { query: { page, page_size: pageSize } },
27+
params: { query: { page, size: pageSize } },
2828
body: {},
2929
});
3030
return data ?? err(error);
@@ -149,7 +149,7 @@ export function usePeers(workspaceId: string, page = 1, pageSize = 20) {
149149
const { data, error } = await client.current.POST(
150150
"/v3/workspaces/{workspace_id}/peers/list",
151151
{
152-
params: { path: { workspace_id: workspaceId }, query: { page, page_size: pageSize } },
152+
params: { path: { workspace_id: workspaceId }, query: { page, size: pageSize } },
153153
body: {},
154154
},
155155
);
@@ -263,7 +263,7 @@ export function usePeerSessions(workspaceId: string, peerId: string, page = 1, p
263263
{
264264
params: {
265265
path: { workspace_id: workspaceId, peer_id: peerId },
266-
query: { page, page_size: pageSize },
266+
query: { page, size: pageSize },
267267
},
268268
body: {},
269269
},
@@ -338,7 +338,7 @@ export function useSessions(workspaceId: string, page = 1, pageSize = 20) {
338338
{
339339
params: {
340340
path: { workspace_id: workspaceId },
341-
query: { page, page_size: pageSize },
341+
query: { page, size: pageSize },
342342
},
343343
body: {},
344344
},
@@ -427,7 +427,7 @@ export function useSessionMessages(
427427
{
428428
params: {
429429
path: { workspace_id: workspaceId, session_id: sessionId },
430-
query: { page, page_size: pageSize },
430+
query: { page, size: pageSize },
431431
},
432432
body: {},
433433
},
@@ -642,6 +642,11 @@ export function useSessionContext(workspaceId: string, sessionId: string) {
642642

643643
// ─── Conclusions ──────────────────────────────────────────────────────────────
644644

645+
// Target-discovery walk bounds (see useConclusionTargetPeers): enough for
646+
// realistically sized per-observer stores; discovery is a read-only listing.
647+
const CONCLUSION_TARGET_PAGE_SIZE = 100;
648+
const CONCLUSION_TARGET_MAX_PAGES = 40;
649+
645650
export function useConclusions(
646651
workspaceId: string,
647652
filters: Record<string, unknown> = {},
@@ -657,7 +662,7 @@ export function useConclusions(
657662
{
658663
params: {
659664
path: { workspace_id: workspaceId },
660-
query: { page, page_size: pageSize, reverse },
665+
query: { page, size: pageSize, reverse },
661666
},
662667
body: filters,
663668
},
@@ -690,6 +695,109 @@ export function useQueryConclusions(
690695
});
691696
}
692697

698+
/**
699+
* Discover the actual conclusion target peers available for one observer peer.
700+
*
701+
* Walks `conclusions/list` (paginated, `page`/`size`) with `filters.observer_id`
702+
* and collects the distinct `observed_id` values from real stored conclusions.
703+
* This is what powers the target picker: the user picks among targets that
704+
* actually exist instead of guessing peer ids into an empty self-scope.
705+
*
706+
* Returns `{ targets, complete }`: `targets` is a sorted list of `{ id, count }`
707+
* (count = number of stored rows for that target, i.e. provenance strength),
708+
* `complete` is false when the walk hit the page cap before listing every page
709+
* (targets may then be incomplete). Empty list = this observer holds no
710+
* conclusions at all.
711+
*
712+
* The knowledge panel previously self-scoped `observed` to the peer, which is
713+
* always empty for knowledge-holder peers (a peer that holds knowledge *about*
714+
* other peers holds nothing about itself).
715+
*/
716+
export function useConclusionTargetPeers(workspaceId: string, observerPeerId: string) {
717+
return useQuery({
718+
queryKey: ["conclusions-target-peers", workspaceId, observerPeerId] as const,
719+
queryFn: async () => {
720+
const counters = new Map<string, number>();
721+
let page = 1;
722+
let pages = 1;
723+
let completed = false;
724+
while (page <= CONCLUSION_TARGET_MAX_PAGES) {
725+
const { data, error } = await client.current.POST(
726+
"/v3/workspaces/{workspace_id}/conclusions/list",
727+
{
728+
params: {
729+
path: { workspace_id: workspaceId },
730+
query: { page, size: CONCLUSION_TARGET_PAGE_SIZE, reverse: false },
731+
},
732+
body: { filters: { observer_id: observerPeerId } },
733+
},
734+
);
735+
if (error) err(error);
736+
const items = data.items ?? [];
737+
for (const item of items) {
738+
if (item.observed_id) {
739+
counters.set(item.observed_id, (counters.get(item.observed_id) ?? 0) + 1);
740+
}
741+
}
742+
pages = data.pages ?? 1;
743+
if (page >= pages || items.length === 0) {
744+
completed = true;
745+
break;
746+
}
747+
page += 1;
748+
}
749+
return {
750+
targets: Array.from(counters.entries())
751+
.map(([id, count]) => ({ id, count }))
752+
.sort((a, b) => b.count - a.count || a.id.localeCompare(b.id)),
753+
complete: completed,
754+
};
755+
},
756+
enabled: Boolean(workspaceId) && Boolean(observerPeerId),
757+
});
758+
}
759+
760+
/**
761+
* Semantic search over conclusions in the observer/observed scope of one peer.
762+
*
763+
* Unlike `useSearchPeer` (message search on the peer itself), this queries the
764+
* dedicated conclusions store with the peer as the *observer*, so it also finds
765+
* knowledge the peer holds *about other peers*.
766+
* The server requires both an observer and an observed peer; the observed target
767+
* MUST be resolved by the caller (target-picker / auto-selection in PeerDetail).
768+
* No implicit self-scope: self-scoping produced guaranteed-zero results.
769+
*/
770+
export function useQueryPeerConclusions(
771+
workspaceId: string,
772+
peerId: string,
773+
query: string,
774+
observedPeerId: string | null,
775+
enabled = false,
776+
) {
777+
const filters: Record<string, unknown> = { observer: peerId, observed: observedPeerId ?? "" };
778+
return useQuery({
779+
queryKey: QK.conclusionsQuery(workspaceId, query, { ...filters, scope: "peer" }),
780+
queryFn: async () => {
781+
const { data, error } = await client.current.POST(
782+
"/v3/workspaces/{workspace_id}/conclusions/query",
783+
{
784+
params: { path: { workspace_id: workspaceId } },
785+
body: { query, top_k: 25, filters },
786+
},
787+
);
788+
return data ?? err(error);
789+
},
790+
// Never fire with an empty target: the server requires both filters, and
791+
// an observed="" request is a guaranteed zero (or worse, an error).
792+
enabled:
793+
enabled &&
794+
Boolean(workspaceId) &&
795+
Boolean(peerId) &&
796+
Boolean(query) &&
797+
Boolean(observedPeerId),
798+
});
799+
}
800+
693801
export function useCreateConclusion(workspaceId: string) {
694802
const qc = useQueryClient();
695803
return useMutation({
@@ -761,7 +869,7 @@ export function useDreams(
761869
{
762870
params: {
763871
path: { workspace_id: workspaceId },
764-
query: { page, page_size: pageSize, reverse: false },
872+
query: { page, size: pageSize, reverse: false },
765873
},
766874
body: filters,
767875
},

0 commit comments

Comments
 (0)