Repository navigation
semantic_search: truncation warning misses SMW's server-side result cap #162
Description
Activity
The same gap exists in the service layer added by PR #133
#133 adds
osw.service.ops.search, whose search operations return atruncatedfield to CLI and MCP callers. Commit 5c26a76 makes that field fire when the result count reaches the limit that reached the wiki:osw-python/src/osw/service/ops/search.py
Lines 17 to 26 in bca9d1f
def _hit_limit(total: int, limit: Optional[int]) -> bool: """Whether a result set is as large as the limit that produced it. The wiki applies the limit itself, so ``cap_list`` never has to cut these results and its own flag stays False. A full result set is then the only signal left that the wiki may hold further matches. ``limit=0`` asks for no results, so meeting it says nothing about truncation. """ return bool(limit) and total >= limit That mirrors the
if limit and n >= limitcondition insemantic_search, so it inherits exactly the blind spot this issue describes. A server-side cap below the requested limit returns fewer rows than asked for,total >= limitis false, andtruncatedreadsFalsefor a result set that was cut.The service layer widens the gap to two more APIs:
Operation API Server cap search_entities,list_instances_of_categorySMW ask$smwgQMaxLimitsearch_titlesMediaWiki prefixsearchpslimitmaximumsearch_contentMediaWiki searchsrlimitmaximumAt the default
OSW_MAX_RESULTSof 100 none of these caps is reached. A caller passing a largerlimitcan reach all three.What this means for the proposed fix
Reading
query.meta.hasFurtherResults, the third option above, would fix the service layer too, but only ifsemantic_searchreturns the signal instead of only warning about it.search_entitiescalls it with the defaultreturn_json=Falseand receives a flat list of titles:osw-python/src/osw/service/ops/search.py
Lines 64 to 79 in bca9d1f
lim = ctx.limit(limit) titles = ctx.osw.site.semantic_search( WtSite.SearchParam(query=ask_query, limit=lim) ) # semantic_search lets a 'limit=' written into the query win over `lim`, # so the flag has to compare against the limit that reached the wiki. # `titles` excludes hits whose page does not exist, so a result set # thinned that way reads as not truncated. query_limit = get_query_limit(ask_query) effective_limit = lim if query_limit is None else query_limit capped, total, truncated = cap_list(titles, lim) return { "titles": capped, "count": total, "truncated": truncated or _hit_limit(total, effective_limit), } So the signal is not reachable from there today. Whichever option is chosen, returning the truncation state from
semantic_searchrather than only emitting a warning would let callers report it instead of re-deriving it from the result count.hasFurtherResultsdoes not exist,query-continue-offsetdoesMeasured with
mwclient, the client this library uses, against https://www.semantic-mediawiki.org.A truncated
askresponse carries a top-levelquery-continue-offsetholding the offset the remainder starts at. A complete one omits the key.query "[[Modification date::+]]|limit=3" top-level keys : ['query', 'query-continue-offset'] query-continue-offset: 3 query keys : ['meta', 'printrequests', 'results', 'serializer', 'version'] query "[[Category:NoSuchCat12345]]|limit=10" top-level keys : ['query'] query-continue-offset: <absent>hasFurtherResultsappears nowhere in the response, neither underquerynor underquery.meta, whose keys arehash,count,offset,sourceandtime. So option 3 as written is not implementable, but its idea is: the wiki does report completeness, under a different key and one level higher.query.metain the issue text is the right path forcountandoffset.The count comparison also invents truncation
Checking the case the issue does not mention, a complete result set whose size equals the limit.
[[Has type::Date]]has 18 results in total:query count returned query-continue-offset[[Has type::Date]]|limit=1818 absent [[Has type::Date]]|limit=1717 17 18 >= 18is true, so the old check warns about a result set that is complete. That is not a rare shape: at theOSW_MAX_RESULTSdefault of 100, any category with exactly 100 instances would settruncatedwrongly inosw.service.ops.search.Since the offset is present exactly when the set was cut short, it replaces the count comparison rather than supplementing it. Keeping the count as a fallback would only re-add the false positive.
A
limit=0count query returns no offset even when the query matches pages, so it stays silent as before.Fix
#171 changes
semantic_searchinwiki_toolson main:- truncation is read from
query-continue-offset SearchParam.return_meta=Truereturns oneSemanticSearchResultper query, withquery,titles,count,truncatedandnext_offset, so a caller can report the state instead of re-deriving it- the
return_json=Truepath is unchanged, since the raw response it returns already carries the offset
Wiring
osw.service.ops.searchtoreturn_metastays with #133, sincesrc/osw/service/does not exist on main yet.search_titlesandsearch_contentare not covered. They useprefixsearchandsearch, which report continuation differently, so they need separate handling in #133.- truncation is read from
Another confirmation of the false positive, measured on a second wiki
(arkeve.isc.fraunhofer.de, 2026-09-17).The query
[[Category:OSWc5d4829ed2744a219ba027171c75fa1d]]matches 408 tasks.limitrows returned top-level query-continue-offsetwiki_toolswarns3 3 present, value 3 yes, correctly 408 408 absent yes, wrongly 500 408 absent no The
limit=408row is the false positive. The result set is complete and the
server sends no continuation offset, but the count check at
https://github.com/OpenSemanticLab/osw-python/blob/9aa6322/src/osw/wiki_tools.py#L394
still emits theUserWarning:if limit and n >= limit:
This repeats what was already measured against semanticmediawiki.org and shows
the behaviour is not instance specific.A working implementation of the check now ships in this repository, in
_ask_rowsinsrc/osw/service/ops/tasks.pyon thefeat/mcp-serverbranch:response = raw[0] if raw else {} ... return rows, "query-continue-offset" in response
It reads the top-level key and nothing else.
semantic_searchcan adopt the
same test.
semantic_searchwarns when a query returns as many results as the limit it asked for:https://github.com/OpenSemanticLab/osw-python/blob/main/src/osw/wiki_tools.py#L318-L326
That catches truncation at the requested limit only. SMW also caps every request server-side at
$smwgQMaxLimit(default 10000, https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxLimit). A request above the cap is silently reduced, so a query asking for 50000 and truncated at the wiki's 10000 returns 10000 results,10000 >= 50000is false, and no warning is emitted. The caller sees a short result set with nothing to indicate it is incomplete.The same applies to
$smwgQUpperboundfor the printout limit.Possible approach
$smwgQMaxLimitis not exposed throughsiteinfo. Options, in rough order of preference:min(requested_limit, server_cap)where the cap is read once per wiki and cached, in the same shape asWtSite.get_allowed_file_extensionsfrom feat(file): report upload failures clearly and verify before storing #153. Needs a source for the cap.limit + 1and warn when more thanlimitcome back. Robust and needs no server config, but changes what is sent and costs one extra result per query.askresponse carriesquery.meta.countandquery.meta.offset, and SMW setsquery.meta.hasFurtherResultswhen more results exist beyond the returned set. Warning onhasFurtherResultswould cover both the requested limit and the server cap, and would replace the count comparison entirely.The third looks like the right fix: it asks SMW whether the result set is complete instead of inferring it.
Context
Noticed while reviewing #161, which fixes the related case where a caller-supplied
|limit=in the query string was silently overridden. The warning itself was added in #150.