Skip to content

Feat/truncate text and cross parent reads - #19

Merged
loookashow merged 5 commits into
mainfrom
feat/truncate-text-and-cross-parent-reads
Aug 10, 2026
Merged

Feat/truncate text and cross parent reads#19
loookashow merged 5 commits into
mainfrom
feat/truncate-text-and-cross-parent-reads

Conversation

@loookashow

Copy link
Copy Markdown
Contributor

Types and plumbing for two Flux API features that shipped server-side: response truncation on reads, and cross-parent read addresses for strict-reference collections.

Flux

truncate_text is reachable from search(). FluxClient.search() / AsyncFluxClient.search() accept a keyword-only params mapping forwarded to the query string. The transport already supported query parameters; they were simply not plumbed through search(). With the existing passthrough on list_resources(), both List Resources and Search now support it.

truncate_text is an integer ≥ 1 that caps every text-typed field and is ignored when raw=true. Truncated fields are marked under _sys.truncated (field, localenull for non-localized — and original_length); fields within the limit get no entry. No client-side validation: an invalid value surfaces as a server 422 validation_error.

query_params on the four search wrappersvector_search(), vector_field_search(), hybrid_search(), boosted_search(), sync and async. Named query_params rather than params deliberately: params was already meaningful inside **extra_body (forwarded to the JSON body), so an explicit params keyword would have silently rerouted it to the query string for any caller already passing params={...} as a search-body field. _merge_extra now raises if handed truncate_text as a body field and names query_params as the right place for it.

Management

Cross-parent (flat) read addressing for strict-reference collections. add_api_collection / update_api_collection (and the deprecated add_api_folder / update_api_folder), sync and async, accept unscoped_levels: list[int] and unscoped_ancestors: list[str]. These expose additional read-only addresses that drop the ancestor chain entirely (level == 0) or partially (level >= 1, keeping the root-most keys).

The API requires both to be sent together. Not enforced client-side — the server owns that rule and may relax it.

Typed connection fields. APIFolderSummary (aliased APICollectionSummary) gains unscoped_levels, unscoped_ancestors, expose_owner — all defaulting to an empty/false value if the server omits them — plus flat_route and flat_routes, both optional and nullable, since null was observed on connections with no key-bearing ancestor. New FlatRoute and FlatRouteSummary models.

APIFolderSummary and both new models set model_config = ConfigDict(extra="allow"), so unrecognised fields — present or future — are preserved rather than silently dropped.

Not in scope: cross-parent writes. FluxClient has no create/update taking a folder path beyond the documented ones, and the server rejects writes on flat paths regardless. List, Get Resource and Schema already worked against these addresses, because the collection path is an opaque slash-trimmed string; this only adds typed support for the connection object and for configuring the addresses.

Fixed

DEFAULT_USER_AGENT was pinned to foxnose-sdk/0.1.0 regardless of the installed version — every request from every release reported 0.1.0. The version now lives in a single _version.py imported by both foxnose_sdk/__init__.py (so foxnose_sdk.__version__ keeps working) and config.py (so the User-Agent is real), which is what avoids the circular import that motivated the stale constant in the first place.

Repo hygiene

  • CI now gates the whole repo (ruff check . / ruff format --check .) instead of src/ only. Linting src/ alone let tests/ drift until it needed a repo-wide reformat to get back in line — that reformat is the style: commit here.
  • .claude/ is gitignored, as it already is in core, delivery-api, integration-tests and ts-sdk. Without it the repo-wide gate walks untracked tool directories and fails on code that is not in the repository.
  • An opt-in pre-push hook so the same gates can run before a push rather than after.

Review the formatting commit separately from the feature commits — it touches many files and changes nothing.

Known server-side discrepancies, typed as sent

Not worked around in the SDK, and raised with the API team:

  • flat_routes[].read_methods reports ["get_one", "get_many"] at level 0, though the docs describe Get Resource as available only at level >= 1.
  • read_methods never includes search at any level, though the docs list Search as available on flat addresses.

read_methods is typed as the server sends it rather than as documented.

Tests

Full suite green; ruff check . and ruff format --check . clean.

loookashow and others added 5 commits August 8, 2026 19:23
…release 0.8.0

- FluxClient/AsyncFluxClient.search() accept a keyword-only `params` mapping,
  forwarded to the query string, so `truncate_text` (and any other query
  param) works on Search as it already did on list_resources().
- vector_search/vector_field_search/hybrid_search/boosted_search (sync and
  async) gain a `query_params` keyword forwarded the same way. Named
  `query_params` rather than `params` because `params` was already a
  meaningful **extra_body key (forwarded to the JSON body); an explicit
  `params` keyword would have silently rerouted an existing caller's body
  field to the query string. `_merge_extra` now raises if `truncate_text` is
  passed as a body field, naming `query_params` as the fix.
- APIFolderSummary (APICollectionSummary) gains unscoped_levels,
  unscoped_ancestors, expose_owner, flat_route (new FlatRouteSummary model,
  undocumented) and flat_routes (new FlatRoute model per entry) to type the
  cross-parent (flat) read addressing already reachable via Flux's opaque
  folder-path strings. APIFolderSummary and both new models set
  extra="allow" so unrecognized fields are preserved, not dropped.
- add_api_collection/update_api_collection and the deprecated
  add_api_folder/update_api_folder aliases (sync and async) accept
  unscoped_levels/unscoped_ancestors to configure the addresses.
- Version bumped to 0.8.0 (additive).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…decisions

docs/flux-client.md wrongly claimed FluxClient has no method that can target
a flat address -- create_resource()/update_resource() accept the same opaque
folder_path as the read methods and will happily interpolate a flat path; it
is the server that rejects the write. Reworded to match the TS README.

Also pins two "the server validates, not us" decisions with regression tests
(sync + async): an out-of-range/non-integer truncate_text and a lone
unscoped_levels or unscoped_ancestors must keep forwarding to the request
unchanged, not raise, so client-side validation can't creep in unnoticed.
CI only runs 'ruff format --check src/', so tests/ has never been gated and
had drifted. Formatting only; no behavioural change. 378 tests still pass.
CI linted src/ only, so tests/ drifted unchecked until it needed a repo-wide
reformat. Both ruff commands now cover the repo. Two unused imports in tests/
are removed so the widened gate starts green.

.githooks/pre-push runs the same checks locally. Opt in per clone with
  git config core.hooksPath .githooks
It is skippable with --no-verify, so CI stays the real gate.
@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@loookashow
loookashow merged commit f5b1de8 into main Aug 10, 2026
6 checks passed
@loookashow
loookashow deleted the feat/truncate-text-and-cross-parent-reads branch August 10, 2026 09:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant