Skip to content

Commit 26bf571

Browse files
authored
fix: entities list only show 100 entities (#142)
* fix: entities list only show 100 entities * fix: update Rust CLI for entities pagination API changes
1 parent 6232e69 commit 26bf571

16 files changed

Lines changed: 280 additions & 39 deletions

File tree

hindsight-api/hindsight_api/api/http.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -188,12 +188,18 @@ class EntityListResponse(BaseModel):
188188
"first_seen": "2024-01-15T10:30:00Z",
189189
"last_seen": "2024-02-01T14:00:00Z",
190190
}
191-
]
191+
],
192+
"total": 150,
193+
"limit": 100,
194+
"offset": 0,
192195
}
193196
}
194197
)
195198

196199
items: list[EntityListItem]
200+
total: int
201+
limit: int
202+
offset: int
197203

198204

199205
class EntityDetailResponse(BaseModel):
@@ -1516,19 +1522,27 @@ async def api_stats(
15161522
"/v1/default/banks/{bank_id}/entities",
15171523
response_model=EntityListResponse,
15181524
summary="List entities",
1519-
description="List all entities (people, organizations, etc.) known by the bank, ordered by mention count.",
1525+
description="List all entities (people, organizations, etc.) known by the bank, ordered by mention count. Supports pagination.",
15201526
operation_id="list_entities",
15211527
tags=["Entities"],
15221528
)
15231529
async def api_list_entities(
15241530
bank_id: str,
15251531
limit: int = Query(default=100, description="Maximum number of entities to return"),
1532+
offset: int = Query(default=0, description="Offset for pagination"),
15261533
request_context: RequestContext = Depends(get_request_context),
15271534
):
1528-
"""List entities for a memory bank."""
1535+
"""List entities for a memory bank with pagination."""
15291536
try:
1530-
entities = await app.state.memory.list_entities(bank_id, limit=limit, request_context=request_context)
1531-
return EntityListResponse(items=[EntityListItem(**e) for e in entities])
1537+
data = await app.state.memory.list_entities(
1538+
bank_id, limit=limit, offset=offset, request_context=request_context
1539+
)
1540+
return EntityListResponse(
1541+
items=[EntityListItem(**e) for e in data["items"]],
1542+
total=data["total"],
1543+
limit=data["limit"],
1544+
offset=data["offset"],
1545+
)
15321546
except (AuthenticationError, HTTPException):
15331547
raise
15341548
except Exception as e:

hindsight-api/hindsight_api/engine/interface.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -406,18 +406,20 @@ async def list_entities(
406406
bank_id: str,
407407
*,
408408
limit: int = 100,
409+
offset: int = 0,
409410
request_context: "RequestContext",
410-
) -> list[dict[str, Any]]:
411+
) -> dict[str, Any]:
411412
"""
412-
List entities for a bank.
413+
List entities for a bank with pagination.
413414
414415
Args:
415416
bank_id: The memory bank ID.
416417
limit: Maximum results.
418+
offset: Offset for pagination.
417419
request_context: Request context for authentication.
418420
419421
Returns:
420-
List of entity dicts.
422+
Dict with items, total, limit, offset.
421423
"""
422424
...
423425

hindsight-api/hindsight_api/engine/memory_engine.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3601,32 +3601,47 @@ async def list_entities(
36013601
bank_id: str,
36023602
*,
36033603
limit: int = 100,
3604+
offset: int = 0,
36043605
request_context: "RequestContext",
3605-
) -> list[dict[str, Any]]:
3606+
) -> dict[str, Any]:
36063607
"""
3607-
List all entities for a bank.
3608+
List all entities for a bank with pagination.
36083609
36093610
Args:
36103611
bank_id: bank IDentifier
36113612
limit: Maximum number of entities to return
3613+
offset: Offset for pagination
36123614
request_context: Request context for authentication.
36133615
36143616
Returns:
3615-
List of entity dicts with id, canonical_name, mention_count, first_seen, last_seen
3617+
Dict with items, total, limit, offset
36163618
"""
36173619
await self._authenticate_tenant(request_context)
36183620
pool = await self._get_pool()
36193621
async with acquire_with_retry(pool) as conn:
3622+
# Get total count
3623+
total_row = await conn.fetchrow(
3624+
f"""
3625+
SELECT COUNT(*) as total
3626+
FROM {fq_table("entities")}
3627+
WHERE bank_id = $1
3628+
""",
3629+
bank_id,
3630+
)
3631+
total = total_row["total"] if total_row else 0
3632+
3633+
# Get paginated entities
36203634
rows = await conn.fetch(
36213635
f"""
36223636
SELECT id, canonical_name, mention_count, first_seen, last_seen, metadata
36233637
FROM {fq_table("entities")}
36243638
WHERE bank_id = $1
36253639
ORDER BY mention_count DESC, last_seen DESC
3626-
LIMIT $2
3640+
LIMIT $2 OFFSET $3
36273641
""",
36283642
bank_id,
36293643
limit,
3644+
offset,
36303645
)
36313646

36323647
entities = []
@@ -3653,7 +3668,12 @@ async def list_entities(
36533668
"metadata": metadata,
36543669
}
36553670
)
3656-
return entities
3671+
return {
3672+
"items": entities,
3673+
"total": total,
3674+
"limit": limit,
3675+
"offset": offset,
3676+
}
36573677

36583678
async def get_entity_state(
36593679
self,

hindsight-api/tests/test_http_api_integration.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,11 +250,34 @@ async def test_full_api_workflow(api_client, test_bank_id):
250250
# 8. Test Entity Endpoints
251251
# ================================================================
252252

253-
# List entities
253+
# List entities with pagination
254254
response = await api_client.get(f"/v1/default/banks/{test_bank_id}/entities")
255255
assert response.status_code == 200
256256
entities_data = response.json()
257257
assert "items" in entities_data
258+
assert "total" in entities_data
259+
assert "limit" in entities_data
260+
assert "offset" in entities_data
261+
assert entities_data["offset"] == 0
262+
assert entities_data["limit"] == 100 # default limit
263+
264+
# Test pagination with custom limit and offset
265+
response = await api_client.get(f"/v1/default/banks/{test_bank_id}/entities?limit=5&offset=0")
266+
assert response.status_code == 200
267+
paginated_data = response.json()
268+
assert paginated_data["limit"] == 5
269+
assert paginated_data["offset"] == 0
270+
assert len(paginated_data["items"]) <= 5
271+
272+
# Test offset
273+
if entities_data["total"] > 1:
274+
response = await api_client.get(f"/v1/default/banks/{test_bank_id}/entities?limit=1&offset=1")
275+
assert response.status_code == 200
276+
offset_data = response.json()
277+
assert offset_data["offset"] == 1
278+
# With offset=1, we should get different entity than first one (if there are multiple)
279+
if len(offset_data["items"]) > 0 and len(entities_data["items"]) > 1:
280+
assert offset_data["items"][0]["id"] != entities_data["items"][0]["id"]
258281

259282
# Get specific entity if any exist
260283
if len(entities_data['items']) > 0:

hindsight-cli/src/api.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,9 @@ impl ApiClient {
241241
})
242242
}
243243

244-
pub fn list_entities(&self, bank_id: &str, limit: Option<i64>, _verbose: bool) -> Result<types::EntityListResponse> {
244+
pub fn list_entities(&self, bank_id: &str, limit: Option<i64>, offset: Option<i64>, _verbose: bool) -> Result<types::EntityListResponse> {
245245
self.runtime.block_on(async {
246-
let response = self.client.list_entities(bank_id, limit, None).await?;
246+
let response = self.client.list_entities(bank_id, limit, offset, None).await?;
247247
Ok(response.into_inner())
248248
})
249249
}

hindsight-cli/src/commands/entity.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub fn list(
1616
None
1717
};
1818

19-
let response = client.list_entities(bank_id, Some(limit), verbose)?;
19+
let response = client.list_entities(bank_id, Some(limit), None, verbose)?;
2020

2121
if let Some(mut sp) = spinner {
2222
sp.finish();

hindsight-cli/src/commands/explore.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ impl App {
283283
}
284284

285285
fn load_entities(&mut self, bank_id: &str) -> Result<()> {
286-
let response = self.client.list_entities(bank_id, Some(100), false)?;
286+
let response = self.client.list_entities(bank_id, Some(100), None, false)?;
287287
self.entities = response.items;
288288

289289
if !self.entities.is_empty() && self.entities_state.selected().is_none() {

hindsight-clients/python/hindsight_client_api/api/entities_api.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ async def list_entities(
338338
self,
339339
bank_id: StrictStr,
340340
limit: Annotated[Optional[StrictInt], Field(description="Maximum number of entities to return")] = None,
341+
offset: Annotated[Optional[StrictInt], Field(description="Offset for pagination")] = None,
341342
authorization: Optional[StrictStr] = None,
342343
_request_timeout: Union[
343344
None,
@@ -354,12 +355,14 @@ async def list_entities(
354355
) -> EntityListResponse:
355356
"""List entities
356357
357-
List all entities (people, organizations, etc.) known by the bank, ordered by mention count.
358+
List all entities (people, organizations, etc.) known by the bank, ordered by mention count. Supports pagination.
358359
359360
:param bank_id: (required)
360361
:type bank_id: str
361362
:param limit: Maximum number of entities to return
362363
:type limit: int
364+
:param offset: Offset for pagination
365+
:type offset: int
363366
:param authorization:
364367
:type authorization: str
365368
:param _request_timeout: timeout setting for this request. If one
@@ -387,6 +390,7 @@ async def list_entities(
387390
_param = self._list_entities_serialize(
388391
bank_id=bank_id,
389392
limit=limit,
393+
offset=offset,
390394
authorization=authorization,
391395
_request_auth=_request_auth,
392396
_content_type=_content_type,
@@ -414,6 +418,7 @@ async def list_entities_with_http_info(
414418
self,
415419
bank_id: StrictStr,
416420
limit: Annotated[Optional[StrictInt], Field(description="Maximum number of entities to return")] = None,
421+
offset: Annotated[Optional[StrictInt], Field(description="Offset for pagination")] = None,
417422
authorization: Optional[StrictStr] = None,
418423
_request_timeout: Union[
419424
None,
@@ -430,12 +435,14 @@ async def list_entities_with_http_info(
430435
) -> ApiResponse[EntityListResponse]:
431436
"""List entities
432437
433-
List all entities (people, organizations, etc.) known by the bank, ordered by mention count.
438+
List all entities (people, organizations, etc.) known by the bank, ordered by mention count. Supports pagination.
434439
435440
:param bank_id: (required)
436441
:type bank_id: str
437442
:param limit: Maximum number of entities to return
438443
:type limit: int
444+
:param offset: Offset for pagination
445+
:type offset: int
439446
:param authorization:
440447
:type authorization: str
441448
:param _request_timeout: timeout setting for this request. If one
@@ -463,6 +470,7 @@ async def list_entities_with_http_info(
463470
_param = self._list_entities_serialize(
464471
bank_id=bank_id,
465472
limit=limit,
473+
offset=offset,
466474
authorization=authorization,
467475
_request_auth=_request_auth,
468476
_content_type=_content_type,
@@ -490,6 +498,7 @@ async def list_entities_without_preload_content(
490498
self,
491499
bank_id: StrictStr,
492500
limit: Annotated[Optional[StrictInt], Field(description="Maximum number of entities to return")] = None,
501+
offset: Annotated[Optional[StrictInt], Field(description="Offset for pagination")] = None,
493502
authorization: Optional[StrictStr] = None,
494503
_request_timeout: Union[
495504
None,
@@ -506,12 +515,14 @@ async def list_entities_without_preload_content(
506515
) -> RESTResponseType:
507516
"""List entities
508517
509-
List all entities (people, organizations, etc.) known by the bank, ordered by mention count.
518+
List all entities (people, organizations, etc.) known by the bank, ordered by mention count. Supports pagination.
510519
511520
:param bank_id: (required)
512521
:type bank_id: str
513522
:param limit: Maximum number of entities to return
514523
:type limit: int
524+
:param offset: Offset for pagination
525+
:type offset: int
515526
:param authorization:
516527
:type authorization: str
517528
:param _request_timeout: timeout setting for this request. If one
@@ -539,6 +550,7 @@ async def list_entities_without_preload_content(
539550
_param = self._list_entities_serialize(
540551
bank_id=bank_id,
541552
limit=limit,
553+
offset=offset,
542554
authorization=authorization,
543555
_request_auth=_request_auth,
544556
_content_type=_content_type,
@@ -561,6 +573,7 @@ def _list_entities_serialize(
561573
self,
562574
bank_id,
563575
limit,
576+
offset,
564577
authorization,
565578
_request_auth,
566579
_content_type,
@@ -590,6 +603,10 @@ def _list_entities_serialize(
590603

591604
_query_params.append(('limit', limit))
592605

606+
if offset is not None:
607+
608+
_query_params.append(('offset', offset))
609+
593610
# process the header parameters
594611
if authorization is not None:
595612
_header_params['authorization'] = authorization

hindsight-clients/python/hindsight_client_api/models/entity_list_response.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import re # noqa: F401
1818
import json
1919

20-
from pydantic import BaseModel, ConfigDict
20+
from pydantic import BaseModel, ConfigDict, StrictInt
2121
from typing import Any, ClassVar, Dict, List
2222
from hindsight_client_api.models.entity_list_item import EntityListItem
2323
from typing import Optional, Set
@@ -28,7 +28,10 @@ class EntityListResponse(BaseModel):
2828
Response model for entity list endpoint.
2929
""" # noqa: E501
3030
items: List[EntityListItem]
31-
__properties: ClassVar[List[str]] = ["items"]
31+
total: StrictInt
32+
limit: StrictInt
33+
offset: StrictInt
34+
__properties: ClassVar[List[str]] = ["items", "total", "limit", "offset"]
3235

3336
model_config = ConfigDict(
3437
populate_by_name=True,
@@ -88,7 +91,10 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
8891
return cls.model_validate(obj)
8992

9093
_obj = cls.model_validate({
91-
"items": [EntityListItem.from_dict(_item) for _item in obj["items"]] if obj.get("items") is not None else None
94+
"items": [EntityListItem.from_dict(_item) for _item in obj["items"]] if obj.get("items") is not None else None,
95+
"total": obj.get("total"),
96+
"limit": obj.get("limit"),
97+
"offset": obj.get("offset")
9298
})
9399
return _obj
94100

hindsight-clients/python/tests/test_main_operations.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,38 @@ async def do_list():
449449
assert response is not None
450450
assert response.items is not None
451451
assert isinstance(response.items, list)
452+
# Verify pagination fields
453+
assert response.total is not None
454+
assert response.limit is not None
455+
assert response.offset is not None
456+
assert response.offset == 0
457+
assert response.limit == 100 # default limit
458+
459+
def test_list_entities_with_pagination(self, client, bank_id):
460+
"""Test listing entities with pagination parameters."""
461+
import asyncio
462+
from hindsight_client_api import ApiClient, Configuration
463+
from hindsight_client_api.api import EntitiesApi
464+
465+
async def do_list_paginated():
466+
config = Configuration(host=HINDSIGHT_API_URL)
467+
api_client = ApiClient(config)
468+
api = EntitiesApi(api_client)
469+
470+
# Test with custom limit
471+
response = await api.list_entities(bank_id=bank_id, limit=5, offset=0)
472+
assert response.limit == 5
473+
assert response.offset == 0
474+
assert len(response.items) <= 5
475+
476+
# Test with offset
477+
response_offset = await api.list_entities(bank_id=bank_id, limit=1, offset=1)
478+
assert response_offset.offset == 1
479+
assert response_offset.limit == 1
480+
481+
return response
482+
483+
asyncio.get_event_loop().run_until_complete(do_list_paginated())
452484

453485
def test_get_entity(self, client, bank_id):
454486
"""Test getting a specific entity."""

0 commit comments

Comments
 (0)