Releases: FoxNoseTech/foxnose-python
Release list
v0.7.0 — Flux Writes & Typed Write Errors
Release notes:
This release adds write support to the Flux client and aligns the error and usage types with the current delivery API.
Highlights
Flux writes
FluxClient.create_resource(folder_path, data, *, key=None)and the asyncAsyncFluxClient.create_resource(...)— create and publish a resource immediately.keyis an optional external identifier for deduplication.update_resource(folder_path, resource_key, data)(sync + async) — a full-document replace that publishes a new revision (not a partial merge).- Both accept nested collection paths, e.g.
users/usr_1/memories. - Returns
resource_key,revision_key,write_units,published.
created = client.create_resource("articles", {"title": "Hello"}, key="my-id")
client.update_resource("articles", created["resource_key"], {"title": "Hello (edited)"})Typed errors
New subclasses of FoxnoseAPIError (exported from the package root, still caught by except FoxnoseAPIError):
- CollectionNotWritable (403)
- ExternalIdConflict (409)
- ContentValidationFailed (422) — exposes .errors (each with a json_path) and .errors_truncated
- UpstreamError (502) — the write outcome is unknown
Reliability
- Writes are never retried automatically. A failed write's outcome is unknown, so on failure re-read with a GET before retrying. (Backed by a new per-request allow_retries flag on the transport, defaulting to True for reads.)
Misc
The default User-Agent now reports the real package version.
Breaking changes
- UsageBreakdown now exposes projects, resources, and users (previously also environments, folders, and components). Parsing stays forward-compatible with additional server fields.
Install
pip install --upgrade foxnose-sdkv0.6.0
foxnose-sdk 0.6.0
Warning
Breaking changes. This release renames the Folder API to Collection and
removes composite folder support. The old *_folder* methods still work as
deprecated aliases (removed in 1.0) — see Migration below.
Added
- Typed billing exceptions in
foxnose_sdk.errors, all subclasses ofFoxnoseAPIError
and exported from the package root (caught byexcept FoxnoseAPIError):SpendCapExceeded(402spend_cap_reached) —cap_usd,cycle_resets_at,raise_cap_urlPlanExhausted(402plan_exhausted) —axis,window_resets_at,upgrade_urlPlanLimitExceeded(403plan_limit_exceeded) —entity,limit,current,upgrade_urlRateLimitExceeded(429rate_limited) —retry_after(from theRetry-Afterheader)
- Components on Collections —
NestedFieldMetahelper for building nested-fieldmeta
(component,component_version,auto_update), andsync_collection_component()on
ManagementClient/AsyncManagementClientto advance pinned nested fields to a target
Component version. New modelsSyncComponentResponse,SyncComponentSkippedItem,
ComponentSyncConflictDetail.
Changed
- Renamed Folder → Collection across the Management API surface. New
*_collection*
methods (list_collections,create_collection,list_collection_versions,
list_collection_fields, …) and models (CollectionSummary,CollectionList,
APICollectionSummary,APICollectionList) are the preferred names. The old*_folder*
methods andFolderSummary/FolderListremain as deprecated aliases that emit a
one-shotDeprecationWarningon first use and will be removed in 1.0.
Removed
- Composite folder support.
Fixed
RolePermission.all_objectsis now optional (bool | None), so permissions for
non-object-based content types (returned by the API asall_objects: null) parse
without error.- Corrected the
roles_and_permissionsexample and docs to the real permission wire-shape
(content_type+actionslist,all_objectsonly on object-based content types), the
renamed content-type keys (collection-structure/collection-itemsin place of
folder-*), and the real API-key create shape (description+ a singlerole).
Migration
- Replace
*_folder*calls with their*_collection*equivalents; old names still work but
emit aDeprecationWarningand are removed in 1.0. - Content-type keys
folder-structure/folder-items→collection-structure/collection-items. - Remove any usage of composite folders.
Full changelog: v0.5.0...v0.6.0
v0.5.0 — Vector Search
What's New
Vector Search Support
The SDK now provides typed models and convenience methods for the Flux vector search API.
4 search modes:
vector_search()— semantic search with auto-generated embeddingsvector_field_search()— search with custom pre-computed embedding vectorshybrid_search()— blended text + vector search with configurable weightsboosted_search()— keyword search boosted by vector similarity
Typed models with validation:
SearchMode,VectorSearch,VectorFieldSearch,VectorBoostConfig,HybridConfig,SearchRequest- Cross-field validation catches invalid configurations before the API call
SearchRequestforwards extra fields (where,sort) viaextra="allow"
All methods available on both FluxClient and AsyncFluxClient.
Other Changes
- Fixed
examples/flux_client.pysearch body to use correctfind_text/resultskeys
Links
v0.4.2 - Fix SecureKeyAuth
Fixed
- Secure Management/Flux signing with query parameters:
SecureKeyAuthnow signs only the URL path (without query string)- aligns SDK signatures with server-side verification and Management auth docs - prevents
401 authentication_failed/Invalid signatureon requests with query params
v0.4.1 — Flux Permission Objects Reliability
Patch release focused on Flux role permission object stability and SDK/docs consistency.
Fixed
- Normalized Flux role permission object responses across legacy list and paginated payload shapes.
- Improved handling of empty/variant permission-object create responses.
- Aligned Flux permission-object behavior with production Management API contract.
Quality
- Added targeted tests for payload coercion helpers to close patch coverage gaps in
client.py
Docs
- Added documentation badge in README for faster docs discovery.
Version
- Python SDK bumped to
0.4.1and tagged asv0.4.1.
v0.4.0 — Flux Introspection & API Route Descriptions
What's New
get_router() — discover all available routes for a Flux API prefix at runtime via GET /{api_prefix}/_router.
router = client.get_router()
print(router["api"])
print(len(router["routes"]))get_schema(folder_path) — fetch live schema metadata for a connected folder route via GET /{api_prefix}/{folder_path}/_schema.
schema = client.get_schema("articles")
print(schema["json_schema"])
print(schema["searchable_fields"])
print(schema["non_searchable_fields"])Route descriptions for API folder connections — add_api_folder() and update_api_folder() now support per-route descriptions used by Flux introspection:
description_get_onedescription_get_manydescription_searchdescription_schema
connection = client.add_api_folder(
api_key="blog-api",
folder_key="articles",
allowed_methods=["get_many", "get_one"],
description_get_one="Get one article by key",
description_get_many="List published articles",
description_search="Search published articles",
description_schema="Read article schema",
)APIFolderSummary now includes route description fields — all description_* values are exposed on returned models.
Highlights
- Added Flux introspection support in both sync and async clients.
- Added Management API support for route-level descriptions on connected folders.
- Fully backward compatible: all new parameters are optional.
Full Changelog:
v0.3.0...v0.4.0
v0.3.0 — Upsert & Batch Upsert
What's New
upsert_resource() — create or update a resource by external_id in a single call. If no resource with the given external_id exists, a new one is created; otherwise a new revision is added.
resource = client.upsert_resource(
"blog-posts",
{"title": "My Article"},
external_id="cms-article-42",
)batch_upsert_resources() — upsert many resources concurrently. Uses threads (sync) or asyncio tasks (async) under the hood, with configurable parallelism.
from foxnose_sdk import BatchUpsertItem
items = [
BatchUpsertItem(external_id=f"article-{i}", payload={"title": f"Article {i}"})
for i in range(1000)
]
result = client.batch_upsert_resources("blog-posts", items, max_concurrency=10)
# result.success_count, result.failure_count, result.succeeded, result.failedmax_concurrency— limit parallel workers (default 5)fail_fast=True— stop and raise on first error;False(default) — collect all resultson_progress— optional callback(completed, total)for progress tracking
external_id on create_resource() — optionally assign an external identifier when creating a resource via POST:
resource = client.create_resource("blog-posts", payload, external_id="legacy-post-99")external_id on ResourceSummary — now exposed in all API responses.
New models: BatchUpsertItem, BatchItemError, BatchUpsertResult
v0.2.0 — Model Objects as Identifiers
What's New
Pass model objects directly as identifiers to any Management API client method — no need to extract .key manually.
# Before
folder = client.get_folder("blog-posts")
resources = client.list_resources(folder.key)
resource = client.get_resource(folder.key, resources.results[0].key)
# After
folder = client.get_folder("blog-posts")
resources = client.list_resources(folder)
resource = client.get_resource(folder, resources.results[0])Highlights
-
Flexible identifiers — all
ManagementClientandAsyncManagementClientmethods accept either a string key or the corresponding model object (FolderSummary,ResourceSummary, etc.). -
13 type aliases — available for use in your own type annotations:
-
FolderRef,ResourceRef,RevisionRef,ComponentRef,SchemaVersionRef,OrgRef,ProjectRef,EnvironmentRef,ManagementRoleRef,FluxRoleRef,ManagementAPIKeyRef,FluxAPIKeyRef,APIRef. -
Fully backward compatible — string keys continue to work everywhere.
Other
- Improved async client test coverage (60% → 77%).
- Overall test coverage increased to 84%.
Full Changelog:
v0.1.0...v0.2.0
Initial release
Initial release of the FoxNose Python SDK.
- ManagementClient and FluxClient (sync + async)
- JWT and API key authentication
- Python 3.9+