Positioning (axes/tags/records) for Keble v2.1 market exploration.
Project description
keble-positioning
keble-positioning is a thin bridge between datasource-backed positioning records and the canonical keble-segmenting runtime.
Package Line
- Package version:
0.37.0 - Python baseline:
>=3.13,<3.14 - Environment manager:
uvonly - Segmenting dependency line:
keble-segmenting >=0.38.0,<1 - Task dependency line:
keble-task >=2.21.0,<3.0.0 - Helpers dependency line:
keble-helpers >=1.41.0,<2.0.0
Testing
keble-positioning follows the root TESTING_GUIDELINE.md marker vocabulary.
Default tests must stay offline: unit-style schema/client/tool tests can run
without MongoDB, Redis, Keepa, or real LLM credentials. Tests that need MongoDB
or Redis are marked integration, db, mongo, and local_stack; live tests
add live plus keepa_live and/or llm as needed.
Default fast test command:
uv run pytest -m "not live and not slow and not eval and not local_stack"
Run real Mongo/Redis integration tests:
RUN_INTEGRATION=1 uv run pytest -m "integration and local_stack"
Run live Keepa tests:
RUN_INTEGRATION=1 RUN_KEEPA_LIVE=1 RUN_LLM_LIVE=1 uv run pytest -m keepa_live
Run live LLM wrapper tests:
RUN_INTEGRATION=1 RUN_LLM_LIVE=1 uv run pytest -m "live and llm"
Before completing a testing-infrastructure change, run:
uv run pytest -m "not live and not slow and not eval and not local_stack"
npx --yes pyright .
Do not add unguarded real-service tests to the default suite. Keep Mongo/Redis
tests isolated through tests/conftest.py, and put new live Keepa/LLM behavior
under explicit markers and environment guards.
Version 0.37.0 Cross-Repo Test Layers
Positioning now consumes the keble-segmenting 0.38.0 test-layer release and
registers the shared Keble marker vocabulary. Integration tests are marked
local_stack and require RUN_INTEGRATION=1; live Keepa tests require
RUN_KEEPA_LIVE=1; live LLM tests require RUN_LLM_LIVE=1.
Side effect if changes:
- Backend positioning workflows depend on package tests staying split between offline client/schema/tool coverage and explicit real-service integration coverage.
- Removing the opt-in guards can make cross-repo default CI depend on local MongoDB, Redis, Keepa credentials, or private LLM credentials.
- If new testing layers or commands are added, update
pyproject.toml,tests/pytest.ini,tests/conftest.py,README.md,AGENTS.md, andCLAUDE.mdtogether.
Version 0.35.0 No-Legacy Agentic Contract
Positioning now registers tools and agent descriptors through the canonical
AgentToolRegistrationConfig, AgentDescriptor, AgentId, and
KebleProviderId contracts. It does not import or expose the retired
tool-config aliases, chat-tool spec aliases, subagent alias models, or
deferred-tool form models.
The package depends on keble-segmenting >=0.36.0 and keble-task >=2.21.0
so the backend receives one aligned provider/action vocabulary.
Side effect if changes:
agent/chat_provider.pyandagent/archetypes.pyfeed backend task-agent registration and must stay aligned withkeble-helpers.ai.agent_descriptor.agent/tools/*.pymap registration config to pydantic-ai tool boundaries; the external pydantic-airequires_approvalflag is derived from the canonicalAgentToolApprovalModeenum.tests/test_package_contract.pypins the bundled segmenting wheel source so local package tests and backend wheel repins cannot drift.
Version 0.34.0 First-Class AgentDescriptor Projection
Positioning now exposes build_positioning_study_agent_descriptor() and
PositioningSubAgentProvider.agent_manifest beside the existing
AgentRegistration path. The new descriptor uses AgentId.POSITIONING_STUDY,
AgentRuntime.EXTERNAL_TASK, and AgentRunPlacement.BACKGROUND, while reusing
the same PositioningStudyInput / PositioningStudyOutput JSON schemas.
build_positioning_study_descriptor() returns the canonical descriptor consumed
by the keble-agentic-chat AgentRegistration runtime contract. This release
raises the helper floor to keble-helpers >=1.40.0 and uses
KebleProviderId.for_provider(...) so provider values and first-class provider
ids cannot drift.
Build packaging explicitly excludes local agent scratch directories
(.claude/, .worktrees/, .wt-discovery/) so private symlinks never enter
release artifacts.
Version 0.30.0 Single Coverage Mutate-Permission
client.aget_view_detailed no longer takes two can_queue_coverage /
can_retry_coverage (bool | None) inputs. It takes one required
can_mutate_coverage: bool — "may this viewer trigger/retry coverage work"
(owner-or-not), decided by the caller. The redundant two-bool input (every caller
set them equal, then the bridge re-derived owner-permission) is gone. The returned
coverage_state still exposes can_queue/can_retry, now derived inside
keble-segmenting by gating can_mutate on final_status. Requires
keble-segmenting >= 0.30.0. Frontend wire shape UNCHANGED.
0.31.0 Agentic ForAgent convention re-rebased onto 0.30.0
The ForAgent agentic-schema naming + placement refactor (originally 0.23.0)
re-applied on top of the 0.30.0 main line. The intervening main sweeps
(factory-helpers-onto-owning-types + identity-enums + dead client-API cleanup)
were preserved: the mutation registrar now delegates grid authorization to the
owning type via ctx.deps.positioning.ensure_grid_authorized(...) (no local
_ensure_grid_authorized helper), and the query registrar keeps main's
parse_object_id_or_model_retry / require_object_or_model_retry id-gating plus
the AGENT-viewport (handled_by=PositioningViewHandledBy.AGENT) read path — all
projected through the *ForAgent types in schemas/for_agent.py. No new agent
tool payload types appeared on 0.30.0, so the for_agent.py __all__ and the
tests/schemas/test_for_agent.py guard set are unchanged. See the convention
table below.
0.27.0 Dead Client-API Cleanup (clean-first)
Removed two confirmed-dead PositioningClient symbols (definition-only on
origin/main, no production consumer anywhere — backend/keble-core/frontend):
_apply_view_update_locally— private "read-only fallback" view-patch helper, zero references.aresolve_positioning_context_by_task— public bridge resolver with no production caller; a thin not-found wrapper over the heavily-used canonicalaget_positioning_by_task_id, which is retained. Its orphaned test and the README enrichment note were dropped/repointed.
No behavior change (both were unreachable). Persisted view updates continue to
flow only through aupdate_view (CRUD-backed).
0.23.0 Agentic schema convention (ForAgent + agent/tools/)
Every schema lands in exactly one bucket; the bucket dictates its NAME and its
LOCATION. Enforced by tests/schemas/test_for_agent.py.
| Bucket | What it is | Name | Lives in |
|---|---|---|---|
| Agent tool I/O | typed input to / return projection of a pydantic-ai @agent.tool |
*ForAgent suffix |
schemas/for_agent.py only — never in agent/ |
| Agent tool config/enum | tool-registration config or approval/mode enum for agent tools | *Agent…Config / *Agent…Policy infix |
schemas/for_agent.py |
| Domain "view" | a persisted/configured saved view of a grid (the noun) | *View* is legitimate |
schemas/view.py etc. (unchanged) |
| Persisted / CRUD / event | *Base/*Update/*MongoObject/*Event/*Upserted |
unchanged | schemas/ |
The registrars under agent/tools/ (mutation.py, query.py) are BEHAVIOR
ONLY — they import contracts from schemas/ and define zero BaseModel
classes. Do NOT reintroduce *View/*Display names for agent tool payloads:
in this package View is the domain saved-view noun, so an agent projection
named …View is ambiguous by construction. The query projections (formerly
PositioningSummaryView, PositioningViewDetailView, CellMaskResultView, …)
are now *ForAgent; the configs are PositioningAgent{Query,Mutation}ToolsConfig
and PositioningAgentMutationApprovalPolicy. Registrars are
register_mutation_tools / register_query_tools (the bare register alias is gone).
0.21.0 Single-Source Prompt Context (breaking)
PositioningTaskMetadata no longer carries loose top-level language /
marketplace fields. The typed prompt_context: SegmentingPromptContext is now
REQUIRED and is the single source of truth for locale and marketplace across the
whole positioning task tree:
to_segmenting_prompt_context()returns the canonical context (only the nullableuser_promptfalls back topositioning_context).require_marketplace()returns the concrete room/memory marketplace, raising a typedServerSideInvalidParamsif the context carries none.from_task_metadata_payload(raw)is the ONE storage-boundary adapter: it heals legacy stored docs (looselanguage/marketplace, noprompt_context) by rebuilding the typed context before validation, so already-created/completed tasks keep running. Use it at every parse site instead ofmodel_validate.
Version 0.33.0 One Action Per Tool Call (no list-wrapper payload)
The positioning task workspace mutation surface is now one action per tool
call. Each concrete action (CreateMappedViewportAction,
CreateReducedChildAction) inherits the single-action AgenticActionPayload
(its own defaulted message/warning_level); the batch wrapper
PositioningTaskWorkspaceActions and its batch result
PositioningTaskWorkspaceActionedResults are removed. The
mutate_positioning_task_workspace tool takes one
PositioningTaskWorkspaceAction and returns one
PositioningTaskWorkspaceActionedResult; multiplicity comes from the agent
emitting several tool calls (the deferred batch holds them and the frontend
resolves one card per click, in order). Consumes keble-segmenting 0.34.0,
where the ordered-atomic grid Actions batch is now a plain BaseModel
(no message/warning_level), so the grid-tool Actions.build(...) calls drop
those kwargs.
Version 0.20.0 DEFAULT_ Prefix For Overridable Tool Descriptions
The four agent mutation-tool description constants are renamed to carry a
DEFAULT_ prefix, marking them as overridable fallback defaults (they are
consumed in register(...) as config.description or DEFAULT_*). This is a
clean breaking rename — no aliases:
MUTATE_SEGMENTING_TOOL_DESCRIPTION→DEFAULT_MUTATE_SEGMENTING_TOOL_DESCRIPTIONPOSITIONING_EDIT_GRID_SCHEMA_TOOL_DESCRIPTION→DEFAULT_POSITIONING_EDIT_GRID_SCHEMA_TOOL_DESCRIPTIONPOSITIONING_REBUCKET_DIMENSION_TOOL_DESCRIPTION→DEFAULT_POSITIONING_REBUCKET_DIMENSION_TOOL_DESCRIPTIONPOSITIONING_APPLY_CELL_ASSIGNMENTS_TOOL_DESCRIPTION→DEFAULT_POSITIONING_APPLY_CELL_ASSIGNMENTS_TOOL_DESCRIPTION
Convention (see CODE_GUIDELINES.md): a module-level constant gets the
DEFAULT_ prefix only when it is an overridable fallback default. Fixed prompts
that are passed directly and never overridden keep their plain name.
tests/agent/test_default_constant_naming.py guards this.
Version 0.19.0 Handler-Owned Viewport (handled_by)
A PositioningView is now keyed by (view_owner, positioning_id, handled_by).
PositioningViewHandledBy separates the human USER viewport from the AI
AGENT viewport, so an agent re-projection (e.g. CHANGE_VIEW) writes its OWN
row and never clobbers the human's pinned/auto viewport. This is orthogonal to
PositioningViewDisplaySource (AUTO vs USER), which still describes whether
the dimensions inside one row are system-derived or pinned.
- New field
PositioningViewBase.handled_bydefaults toUSER; it is part of the stored identity and is intentionally absent fromPositioningViewUpdate(never patchable). CRUDPositioningViewexposesafirst_by_view_identity(view_owner, positioning_id, handled_by);aensure_indexesbackfills legacy rows toUSER, creates the unique compound indexview_owner_positioning_handled_by_unique_idx, and drops the redundant(view_owner, positioning_id)index.afirst_or_create_view/aget_view_detailedtakehandled_by(defaultUSER). A brand-newAGENTrow seeds its default display axes from the positioning owner'sUSERviewport.
Version 0.16.0 Positioning Study TASK Archetype
Program Phase 6 (subagent provider canonicalization):
PositioningSubAgentProviderdeclares thepositioning_studytask archetype through the sharedAgentProviderProtocolshape. Hosts should register this archetype and let agentic-chat generatedelegate_positioning_study; do not add another backend wrapper tool for positioning creation.PositioningStudyInputis the typed parent-to-child contract:positioning_context,marketplace, optionalname, and optionalsearch_terms. Backend validates this model before creating the existingPOSITIONINGworker task.- The archetype output is a host task result projection, not a second
positioning workflow.
keble-positioningcontinues to own positioning schemas and conversion; backend owns queueing, task metadata snapshots, and worker dispatch.
Version 0.15.0 Native Mutation Provider + Manifests
Program Phase 6 (provider canonicalization):
PositioningMutationChatToolProvider— the native provider for the mutation tools (provider_id="positioning_mutation", the id the backend wrapper used, so room diagnostics stay stable). Hosts must drop their generic wrapper adapters; provider classes are the only cross-repo seam.register_query_toolsis no longer exported from the package namespace — it is module-level inagent/query_registry.pyfor the owning package's internal use only.- Both providers expose a declarative
manifest(keble_helpers.ChatToolProviderManifest): the declared inventory powers registration assertions, generated Available-tools prompt blocks, and the frontend label-parity gate. Mutationrequires_approvalmirrors the runtime approval policy from the SAME source.
Version 0.13.0 Native Chat Tool Provider
keble_positioning.agent.PositioningQueryChatToolProvider ships the
keble_helpers.ChatToolProviderProtocol implementation natively, so hosts
(keble.backend today) can drop their generic wrapper adapter around
register_query_tools:
- construction captures
positioning_client+ optionaltools_config; provider_idis the stable diagnostics id"positioning_query"(recorded in room diagnostics and mapped to a user-readable label in the frontend — do not change it);register(*, agent, context)ignorescontextand delegates to the canonicalregister_query_tools(agent, ...)registrar, so the mounted tools are identical to a direct registrar call.
The keble-helpers floor moves to >=1.15.0 (first release exporting
ChatToolProviderProtocol).
Version 0.11.1 Prompt-Context user_prompt Single-Source
Bootstrap dimension discovery no longer passes user_prompt=metadata.positioning_context
separately. to_segmenting_prompt_context() already resolves the canonical
prompt_context.user_prompt (the user's typed intent, falling back to positioning_context only when
absent); passing it again let segmenting's with_user_prompt(...) overwrite that canonical value with
the loose top-level context. Discovery now passes only prompt_context, so the typed user intent is
the single source and cannot be clobbered. (Same single-source principle as the 0.11.0 language fix.)
Version 0.11.0 Language Single-Source And Segmenting 0.11.21 Migration
Two changes, no parallel architecture added:
- Language single-source. Bootstrap dimension discovery previously read the loose
top-level
PositioningTaskMetadata.languagedirectly while every other segmenting path readsprompt_context.language. When the two disagreed (a stale legacy field beside a refreshed typed context) discovery ran in the wrong language.to_segmenting_prompt_context()is now the single derivation every runtime path uses;prompt_context.languageis canonical and the top-levellanguageis documented as a legacy fallback only. - Segmenting 0.11.21 migration. Adopts
keble-segmenting0.11.21 (covariantActions.build(Sequence[Action]), fixing thegrid_tools.pyvariance lint). The 0.11.18+ "agent cohesion" refactor moved three free functions onto owning classes; positioning now callsSegmentingCoverageBase.build_source_signature,SegmentingCoverageBase.normalize_dimension_keys, andPresetMaskStageGroup.from_stageinstead of the removed module-level helpers.
Version 0.10.20 Live Env Discovery And Pydantic-AI Floor
- Live wrapper tests now load package-local env files first and then fall back
to the project backend
.env, so a clean project checkout can rerun the real LLM approval-loop proof without copying secrets intotests/.env. - Backend-style Azure provider env fields are converted by
tests.config.Settingsinto the package text-model input shape, preserving one source of truth for live-test configuration. - The package requires
pydantic-ai-slim >=1.102.0to match the current chat runtime streaming API used by the backend service line.
Version 0.10.19 Live Grid Wrapper Runtime Parity Follow-Up
- Wrapper-tool live coverage now verifies all three approved grid wrapper
tools through a real pydantic-ai tool approval loop:
positioning_edit_grid_schema,positioning_rebucket_dimension, andpositioning_apply_cell_assignments. - The package consumes
keble-helpers >=1.12.16so Python 3.13 clean installs keep the existing top-level helper imports working while preserving Aliyun OSS compatibility. - Local uv development keeps exactly one bundled segmenting wheel beside the
active
keble-segmentingsource path.
Version 0.10.17 Live Grid Wrapper Runtime Parity
- Positioning grid wrapper tools remain approval-only conversion helpers:
schema-owned payloads build canonical
keble-segmenting Actions, then delegate toPositioningClient.aapply_segmenting_actions(...). - Live coverage now verifies a real env-backed pydantic-ai agent requests
positioning_edit_grid_schema, receives approval, and persists the mutation through the same canonical segmenting client path. - Downstream services should consume this line when they need package tags, backend bundled wheels, and live grid-wrapper verification to represent the same repo state.
Version 0.10.16 Usage Accounting Bridge
PositioningDependenciescan carry a host-owned usage recorder.- Positioning bootstrap forwards that recorder into segmenting dimension
discovery, and
aapply_segmenting_actions(...)forwards it into explicit segmenting action batches. - Positioning remains a bridge package: it does not price or persist usage events.
Version 0.10.14 Domain-Neutral Segmenting Prompt Follow-Up
- This patch consumes
keble-segmenting 0.11.14, where generic segmenting prompt prose also follows the item/source contract instead of product/brand wording. - No positioning bridge schema was added. Commerce facts still convert through generic segmenting metadata rows before crossing the package boundary.
Version 0.10.13 Domain-Neutral Segmenting Contract
- This is a breaking dependency-line update for the segmenting item/source contract. Positioning remains the bridge between commerce/domain data and generic segmenting rows.
- Do not add commerce-specific fields to segmenting schemas from positioning.
Provider facts must be converted into generic
CellDisplayMetadataRowrows before they cross intokeble-segmenting. - Backend or provider-owned item classes may still know ASIN, brand, marketplace, price, rating, reviews, and monthly-sold semantics. Segmenting receives only generic row keys, labels, values, item references, and source labels.
Version 0.10.12 Segmenting Coverage Signature Bridge
- Local uv development now consumes
keble-segmenting 0.11.12, where coverage source signatures include mask semantics and completion events are envelope-idempotent. - Positioning now passes the current grid masks into segmenting coverage signature/view-state calls so visible coverage invalidates when mask definitions or preset semantics change.
- Publish metadata no longer carries the invalid private classifier string that PyPI rejects for public package uploads.
Version 0.10.11 Review Cleanup
- Local uv development keeps exactly one bundled segmenting wheel: the
keble-segmentingsource declared inpyproject.toml. - Package contract tests now reject stale historical
keble_segmenting-*.whlartifacts so future dependency bumps cannot leave multiple segmenting payloads in the same repo line. - Auto-mask metadata remains a bridge contract: positioning validates and
carries
PositioningTaskMetadata.mask_generation_enabled/auto_mask_types, while backend owns the post-bootstrapaauto_segment_and_mask(...)call.
Version 0.10.10 Progressive Bootstrap View Events
- Positioning bootstrap emits the existing
UPSERT_POSITIONING_VIEWevent once the positioning shell exists, then emits it again after initial dimensions are discovered. PositioningViewBase.display_dimension_ids=[]is now the valid shell-view state before any display dimensions exist. User view updates still require a meaningful non-empty display-dimension patch.- Shell view reads skip segmenting coverage aggregation until display dimensions exist, so task-room subscribers can render the room immediately without queueing coverage or leaking zero-axis coverage errors.
Version 0.10.9 Segmenting Mutation Tool Contract
- Positioning mutation tool registration now gives
mutate_segmentingan explicit rebucketing contract by default. - Price-band split, merge, and rebucketing requests must not relabel old option
keys with
UPDATE_DIMENSION_OPTIONS. - The required flow is to delete retired option keys, create fresh option
buckets, and finish with
AUTO_SEGMENT_AND_MASKfor the affected dimension so products move into the correct cells.
Version 0.10.8 Bootstrap Prompt Context Propagation
PositioningTaskMetadata.to_segmenting_prompt_context()is now the single task-owned conversion path from positioning metadata to segmenting prompt context.- Bootstrap dimension discovery forwards that typed context into
SegmentingClient.adiscover_dimensions(...), so the user's original prompt, language, and marketplace policy reach the initial root-axis discovery step. - Older queued tasks without
prompt_contextrebuild the same typed context frompositioning_context,language, andmarketplace; no keyword-based off-scope guard is introduced in this release.
Version 0.10.7 Merged Context Metadata
- This release supersedes
0.10.6after the context branch merged with the already-published0.10.5item title fragment line. PositionableItem.title_fragmentsandPositioningTaskMetadata.prompt_contextare both present in the same package line so backend consumers do not lose the item display contract while adopting prompt-context inheritance.
Version 0.10.6 Prompt Context Metadata
PositioningTaskMetadatacarries optionalprompt_contexttyped asSegmentingPromptContextso backend root tasks and reduced child tasks can preserve the user prompt, output language, and marketplace policy across queued segmenting work.- The field is optional for older queued tasks; callers should prefer it when
present and fall back to loose
positioning_context,language, andmarketplacefields only for legacy rows. - This release builds on
0.10.5item title fragments rather than replacing that display metadata contract.
Version 0.10.5 Item Title Fragments
PositionableItemnow carries provider-ownedtitle_fragments, defaulting to an empty list for existing payload compatibility.- The field is serialized into
PositionableItemRecord.item_payloadso hosts can build compact relationship-group titles without parsing commerce-specific nested payloads. - Positioning still treats the fragments as display data only; provider item classes own the meaning and ordering of those fragments.
PositioningTaskMetadata.prompt_contextremains typed throughSegmentingPromptContextso backend child queues can inherit prompt, language, and marketplace context without string parsing.
Version 0.10.4 Lock Refresh
- Local uv development still consumes
keble-segmenting 0.11.4. - The package lock now records the current bundled wheel hash so
uv runanduv synccan install the bridge without a stale same-version artifact mismatch. - No segmenting contract changed in this patch; the refresh only repairs the
positioning consumer artifact state before backend consumes
0.10.4.
Version 0.10.3 Auto-Mask Default Bridge
PositioningTaskMetadata.auto_mask_typescan carry a per-task preset mask allowlist for the backend-managed post-bootstrap segmenting run.PositioningClient(default_auto_mask_types=...)is bridge plumbing only: positioning applies the host default to omittedAUTO_SEGMENT_AND_MASKactions, while explicitauto_mask_types=[]still disables preset stages.- This package does not own the default allowlist. Backend hosts decide whether feasibility is part of their default positioning flow.
Version 0.10.2 Pair Default View Source
PositioningViewnow storesdisplay_sourceasAUTOorUSER.- AUTO views can follow the product default as preset dimensions arrive: pricing first, features/functionality second, scene/use-case as fallback, then grid order.
- USER views preserve explicit manual display choices, including
pricing + scene, and
aupdate_view(...)marks display-dimension updates as USER unless the caller already supplied a source. - Positioning coverage reads continue to delegate durable readiness to
keble-segmenting 0.11.3pair-based coverage scopes.
Version 0.10.1 Update
PositioningViewDetailednow includes package-ownedcoverage_state, derived from exact segmenting coverage for the displayed dimension ids and current grouped item source.- Missing exact coverage derives
NOT_LOADEDinstead of relying on aggregate cells to infer whether a selected dimension combination has ever been queued. - Added typed coverage claim helpers for backend owner view updates. Owners can queue/retry unloaded or failed coverage, while non-owner reads stay read-only.
- The package now consumes
keble-segmenting 0.11.2so coverage signatures, Mongo rows, and phase state enums come from the segmenting source of truth.
Version 0.10.0 Update
- The package now consumes
keble-segmenting 0.10.4, including typed OTHER dimension options, segmenting-owned Infos,cellmask-result payloads, and demand/sales metric summaries. - Reduced-child bootstrap now shares the source grid's Infos row when selected item scopes point to one unambiguous source context. Multi-source children still create a fresh Infos row because their reusable market context is not singular.
- Positioning view/detail payloads keep delegating cells, option roles, preset mask types, and metric summaries to segmenting-owned schemas instead of duplicating those contracts.
- Test dependencies include
socksioso HTTPX can run under developer SOCKS proxy environments during live/model-backed package tests.
Version 0.9.5 Update
PositioningTaskMetadatanow uses the generalmask_generation_enabledfield that matcheskeble-segmenting 0.8.5.- Lightweight host-owned preview tasks can set the flag to
Falseto generate dimensions and cell membership without any generated mask work. - The old preset-only mask field was removed rather than kept as an alias.
Version 0.9.4 Update
PositioningTaskMetadatanow accepts optionalpreset_dimension_typesand forwards them to segmenting bootstrap discovery.FreshItemsRequestforwards persistedpositioning_metadatato host callbacks so backend-owned preview filters can stay typed instead of being embedded in prompt text.- Lightweight AMZ setup previews can request pricing and feature axes while keeping full positioning tasks on normal open-ended discovery.
Version 0.9.3 Update
PositioningTaskMetadataadded a preview-facing mask override through the existing backend bridge.- Lightweight host-owned preview tasks can set the flag to
Falseto generate dimensions and cell membership without generated masks.
Version 0.9.2 Update
PositioningTaskMetadatanow accepts optionalowner_typeso backend task envelopes can carry seller-profile scope into segmenting progress and preset market reasoning without breaking already queued legacy tasks.
Version 0.9.1 Update
- The package now consumes
keble-segmenting 0.6.1, where stale direct-result test/API residue from the multi-select rewrite is removed. - Local uv development resolves the same
keble_segmenting-0.6.1wheel as the current backend line, so package-contract tests exercise the active cleanup release. - Runtime positioning behavior is unchanged from
0.9.0; this is a dependency consumption and repo-hygiene release.
Version 0.9.0 Update
- The package now consumes
keble-segmenting 0.6.0, where segmented results storeoption_keysand multi-select dimensions expand one item into multiple cell placements. - View-detail reads continue to delegate placement counts to segmenting aggregation; positioning does not reimplement cartesian cell expansion.
Version 0.8.1 Update
- View-detail display-axis resolution now preserves every valid saved view exactly, including user-selected pricing + scene/use-case pairs.
- Preset-priority defaults are still used when no saved view exists, preferring pricing, then features/functionality, then scene/use-case, then grid order.
- The stale first-N replacement heuristic was removed so valid user choices are never mistaken for old auto defaults.
Version 0.8.0 Update
- Local uv development now resolves
keble-segmenting 0.5.0, which adds durable terminal omitted segmentation rows and aggregate empty-vs-processing status semantics for lazy positioning axes. - Runtime view-detail aggregation still relies on the package-owned segmenting aggregate contract; no positioning workaround or copied omitted result payload is introduced.
Version 0.7.6 Update
- Local uv development now resolves
keble-segmenting 0.4.9, matching the backend current-line lazy auto-segment bootstrap fix. - The package dependency floor is raised to
keble-segmenting >=0.4.9,<1so positioning package-contract tests cannot drift behind the segmenting line consumed by backend workers. - Runtime positioning behavior is unchanged from
0.7.5; this patch keeps dependency metadata, local wheel sources, lockfile, and docs aligned.
Version 0.7.5 Update
- Local uv development now resolves
keble-segmenting 0.4.8, matching the display-critical auto-segment action contract consumed by backend and frontend lazy axis selection. - Runtime positioning behavior is unchanged from
0.7.4; this patch keeps the package contract tests and bundled segmenting wheel aligned with the current line.
Version 0.7.4 Update
- Positioning view-detail aggregation now explicitly opts into segmenting synthetic status cells after reducing the grid detail to display dimensions.
- Full-grid prompt/admin reads stay guarded by the segmenting default and do not build cartesian status cells for every grid dimension.
- Local uv development now resolves
keble-segmenting 0.4.7.
Version 0.7.3 Update
- Local uv development now resolves
keble-segmenting 0.4.5, matching the backend current-line preset-mask runtime. - The package dependency floor is raised to
keble-segmenting >=0.4.5,<1so positioning tests exercise the same preset-role and grid-scoped mask contract consumed by backend2.5.4. - Historical local
keble_segmentingwheel artifacts older than0.4.5were removed after the source, lockfile, and docs moved to the current wheel.
Version 0.7.2 Update
- Positioning view defaults now treat historical first-N grid-order views as replaceable auto defaults.
- When preset roles exist, default display axes resolve pricing first, then features/functionality, then scene/use-case, with grid order as the final fill.
- Non-default saved user views remain authoritative and keep their saved axis order.
Version 0.7.1 Update
- Package contract tests now derive the current version from
pyproject.tomlinstead of pinning a stale historical release value. mutate_positioning_task_workspacekeeps name/description configurable but always requires approval because it creates durable tasks and relations.
Version 0.7.0 Update
- Positioning workspace action/event contracts now live in
keble_positioning.schemas.workspace_actionsinstead of backend execution modules. PositioningTaskWorkspaceActionedResultreuses generickeble-taskTaskActionCreatedTaskandTaskActionCreatedRelationpayloads, so created child tasks and relation metadata share one canonical DTO.keble_positioning.agent.registry.register(...)registersmutate_positioning_task_workspaceonly when a backend execution handler is supplied; the package owns tool schema/approval metadata, while backend owns task spawning and SSE publishing.
Version 0.6.0 Update
- Reduced child positionings now persist typed
item_source_scopesand read source-owned item records on demand instead of copying selected records into the child row. PositioningClient.avalidate_item_source_scopes(...),alist_scoped_item_records(...),alist_scoped_item_relationships(...), andaget_positioning_items_data(...)are the canonical map-only read surface.SEED_ONLYwrites no child item records or relationships;SEED_AND_DISCOVERwrites only newly discovered fresh child records.- Mapped viewport rows still use
source_positioning_id; reduced children useitem_source_scopes, so the two source mechanisms stay separate.
Version 0.4.14 Update
- Scoped carryover now fails fast when a selected source item key is missing from the source positioning, rather than silently falling back to fresh items.
- Missing scope diagnostics report the source positioning and stale selected key so backend/chat callers can surface a refresh/retry boundary instead of creating a partial reduced child.
Version 0.4.12 Update
- Removed old positioning runtime-field cleanup and index-upgrade helpers after the task-root hard break; current CRUD only creates canonical indexes.
- Keeps the current task-room bridge boundary:
PositioningAgentContextcarriesroot_task_idandallowed_grid_ids, while segmenting mutations targetActions.grid_id.
Version 0.4.11 Update
- Invalid-parameter diagnostics now pass typed primitive strings into
keble-exceptionsinstead of dict payloads. - Mapped-row mutation rejection and relationship endpoint validation keep the same user-facing context while satisfying Pylance/Pyright argument typing.
Version 0.4.10 Update
PositioningAgentContextis now task-room scoped:owner,language,root_task_id, andallowed_grid_ids.mutate_segmentingvalidatesActions.grid_idagainstallowed_grid_idsand delegates the whole canonicalActionspayload.PositioningClient.aapply_segmenting_actions(...)resolves the source positioning frompayload.grid_idand owner; callers no longer passpositioning_id,grid_id, andactionsseparately.- Backend enrichment can use
aget_positioning_by_task_id(...)andaget_source_positioning_by_grid_id(...). aget_grid_for_agent(...)now requirescompare_dimension_keyand buildsGridAgentContextRequest.
Version 0.4.9 Update
aapply_segmenting_actions(...)now requires explicitgrid_idand forwards one canonicalActions(grid_id=...)payload intokeble-segmenting.- Positioning no longer builds or passes
SegmentingUxContext; workspace selection stays outside the bridge and segmenting execution targets only the explicit grid/action payload. aapply_segmenting_actions(...)now inherits nestedSegmentingAgentDeps.segmenting.event_callbackswhen callers do not provide explicit callbacks.- Backend composite deps can pass through positioning into segmenting without dropping room SSE callbacks.
- Grouped followers remain metadata-only; positioning still does not copy source/group segmenting rows to follower item keys.
PositionableItemLoaded.to_cell_display(...)andPositionableItemsByRel.to_cell_display(...)now delegate display projection to the provider item class. Positioning groups records and preserves item identity, but it does not parse commerce payloads into UI summaries.CellDisplayis display-only (description/images); complete selected item identity comes from segmenting aggregateitem_keys.
Design Direction
The current rewrite is bridge-first:
keble-segmentingowns action execution, progress, and grid mutation.keble-positioningowns only bridge-specific persistence and conversion:- positioning objects and views,
- positionable item records,
- positionable relationship records,
- fresh-item callback flows,
- grouped representative selection with metadata-only followers.
- This package no longer exposes positioning-owned agentic payloads, local runtime status models, or task/background orchestration helpers.
Queued auto-segment fanout stays backend-owned. Positioning forwards optional
backend scheduler/context into keble-segmenting; grouped followers remain
metadata-only and are not copied into segmenting result rows after workers finish.
Room/SSE transports should stream direct package AgenticActionEvent JSON.
Positioning should not create backend-specific room event wrappers. View events
use uppercase package-owned action_type values on the shared helper event
shape, and event source is the shared AgenticActionEventSource.KEBLE_POSITIONING
enum (not a free string). Positioning emits through the shared
AgenticEventEmitter held on the unified AgentDbDeps.event_emitter; it no
longer hand-rolls its own callback loop. The field accepts a callback sequence,
None, or a ready emitter (coerced via AgenticEventEmitter.build). Since
0.18.0, every emit site coerces through PositioningClient._ensure_event_emitter
before calling .aemit, so emission is robust even when a host builds the deps
with model_construct(...) (which bypasses the field validator and would otherwise
leave a raw callback list that crashed .aemit). Public client methods drain
the emitter at the runtime boundary before returning.
Positioning now emits direct UPSERT_ITEM_RECORDS and
UPSERT_ITEM_RELATIONSHIPS events for fresh-item CRUD batches, and forwards
request-scoped callbacks into segmenting action execution so segmenting
mutation events can reach the same task-root SSE room.
In 0.4.8, positioning also returns and emits PositioningViewDetailed with
the full grid dimensions / masks plus current-view aggregated_cells, so
production rooms can switch axes locally while reading only the active view
matrix. Room stores can patch the visible node without a backend wrapper.
Downstream TypeScript consumers should use keble-core 0.1.33+
TaskWorkspaceEvent.build(...) for these direct package payloads instead of
frontend-local unknown payload normalization.
Public Package Surface
The package root exports the slim bridge contract:
from keble_positioning import (
PositionableItemLoaded,
PositionableItemsByRel,
PositioningAgentContext,
PositioningAgentDeps,
PositioningBase,
PositioningBootstrapResult,
PositioningClient,
PositioningMutationToolsConfig,
PositioningMongoObject,
PositioningTaskMetadata,
PositioningUpdate,
PositioningViewBase,
PositioningViewDetailed,
PositioningViewMongoObject,
PositioningViewUpdate,
register_mutation_tools,
)
The main client surface is now:
- persisted positioning CRUD
- persisted view CRUD
- slim upstream bootstrap via
PositioningTaskMetadata,PositioningBootstrapResult, andabootstrap_positioning(...) - thin upstream task wrapper via
atask_handler(...) - fresh-item loading / next-page loading
- viewport-map creation via
acreate_mapped_positioning(...) - canonical
aapply_segmenting_actions(...) - explicit
aget_segmenting_progress(...) - explicit
ainterrupt_segmenting(...) - canonical
aget_view_detailed(...)
The package still does not own background orchestration or runtime status recovery.
The new bootstrap surface only covers the initial grid + positioning shell, the first
fresh-item load, and the first dimension-discovery pass.
Bootstrap is now retry-safe for one task id: if a previous attempt already created the
positioning shell, abootstrap_positioning(...) reloads that persisted positioning/grid
pair instead of creating a second shell.
Current bootstrap and segmenting bridge calls emit typed events through
AgentDbDeps.event_emitter and forward request-scoped callbacks into
keble-segmenting, so upstream runtime messages flow through the shared event
stream. The legacy task-tree progress_task path is retired for current
handlers.
Bootstrap Storage Contract
The persisted bridge contract is intentionally short:
- one upstream
task_idowns one positioning shell - one positioning shell points at one segmenting
grid_id - bootstrap retries reuse that existing shell instead of creating duplicates
- resolved
search_termsare persisted on task metadata and callback pagination state, while positioning rows do not persistsearch_terms - mapped positioning rows use their own
task_idand view, share the sourcegrid_id, and point at source-owned items throughsource_positioning_id - mapped positioning rows are viewport-only for mutations;
aapply_segmenting_actions(...)rejects them so source dimensions/results cannot diverge by accident
Mapped positioning rows are viewport-only in this line. They can read source items/relationships and shared segmenting results, but fresh item loading and next-page pagination must run against the source positioning row.
mapped_positioning = await positioning_client.acreate_mapped_positioning(
amongo,
owner=owner,
task_id=mapped_task.id,
source_positioning_id=source_positioning.id,
view_owner=owner,
display_dimension_ids=[dimension_id],
)
data = await positioning_client.aget_positioning_data(
amongo,
positioning_id=mapped_positioning.id,
positioning_owner=owner,
)
Upstream Task Wrapper
Use the upstream task metadata plus the thin task wrapper when the parent backend already owns task scheduling and completion:
from keble_positioning import PositioningTaskMetadata
task_handler_metadata = PositioningTaskMetadata(
positioning_context=payload.positioning_context,
search_terms=payload.search_terms,
language=language,
marketplace=payload.marketplace,
entity_type=payload.entity_type,
name=payload.name,
positioning_metadata={},
)
await positioning_client.atask_handler(request=request)
request.metadata should serialize the PositioningTaskMetadata payload. The
positioning package still does not own background orchestration, runtime status
recovery, or backend-specific Redis namespace setup.
When the parent backend resolves prompt-to-model search keywords, it should pass
those typed search_terms here so bootstrap can start with the intended
retrieval list. Later rounds may supply a different list, and next-page
continuity is carried by callback metadata rather than by the positioning row.
Canonical Write Bridge
Use the direct segmenting bridge instead of any positioning-owned action wrapper:
from keble_helpers import Language
from keble_helpers import AgenticActionWarningLevel
from keble_segmenting.schemas import Action, ActionedResults, Actions
async def apply_actions(
*,
positioning_client: PositioningClient,
owner: str,
db_deps,
grid_id,
actions: list[Action],
) -> ActionedResults:
return await positioning_client.aapply_segmenting_actions(
owner=owner,
db_deps=db_deps,
language=Language.ENGLISH,
payload=Actions.build(
message="Apply positioning segmenting actions.",
warning_level=AgenticActionWarningLevel.MUTATION,
grid_id=grid_id,
actions=actions,
),
)
PositioningClient prepares the grouped representative loader and delegates the canonical Actions payload to keble-segmenting. Grouped follower keys are kept in group metadata only; missing follower result rows are expected and should not be repaired.
Agent Tool Registration
Positioning owns the pydantic-ai registrar for positioning-scoped segmenting mutations. Backend should register this package tool instead of defining a manual duplicate in backend chat code.
from keble_positioning.agent import PositioningAgentDeps, register_mutation_tools
agent = Agent[PositioningAgentDeps, Any](...)
register_mutation_tools(
agent,
positioning_client=positioning_client,
enabled=True,
)
Deps shape:
PositioningAgentDepsinheritskeble_db.AgentDbDeps; Mongo/Redis are not separate tool args.- Positioning runtime state is under
ctx.deps.positioning. - The tool delegates to
PositioningClient.aapply_segmenting_actions(...), so source-positioning mutation guards and grouped-loader behavior stay package-owned. - Backend should omit mapped/view-only grids from
allowed_grid_ids; they must not mutate shared source dimensions/results.
Canonical Read Surface
PositioningViewDetailed is now a pure read model:
viewpositioninggriddimensionsaggregated_cells
Live segmenting progress is not embedded in the room payload anymore. Call aget_segmenting_progress(...) explicitly when the caller needs exact-key runtime progress.
Parent Repo Guidance
[tool.uv.sources] paths in this repo are parent-relative to this package directory. They are not inherited from a parent application repo:
[tool.uv.sources]
keble-segmenting = { path = "deps/keble_segmenting-0.6.1-py3-none-any.whl" }
keble-task = { path = "deps/keble_task-2.4.11-py3-none-any.whl" }
Validation Commands
Use uv commands only:
uv run python -c "import keble_segmenting"uv run python -c "import keble_positioning"uv run pytest tests -quv run pytest -m "not live" tests -quv run pytest -m live tests -quv run python tests/live/refresh_keepa_green_alien_plush_cache.pyuv lockuv sync
The default pytest run is intentionally strict and comprehensive:
- it includes the live Keepa plus env-backed Azure/OpenAI tests under
tests/live - it fails fast when
tests/.env, Mongo, Redis, Keepa, or text-model config is missing - it keeps the refresh script as the only write path for tracked live cache files under
tests/asset/
0.5.0 Preset Dimension Defaults And Child Discovery
keble-positioning 0.5.0 consumes keble-segmenting 0.2.0 and the new preset
dimension contract. View-detail defaults now prefer pricing as the row axis,
features/functionality as the column axis, scene/use-case as the next column
fallback, and then the existing grid order.
Reduced child positioning tasks default to SEED_ONLY; fresh product discovery
is an explicit SEED_AND_DISCOVER mode. This preserves copied selected item
records and in-scope relationships for child grids while preventing accidental
fresh expansion unless the caller requests it.
0.6.1 Chained Map-Only Source Scopes
keble-positioning 0.6.1 resolves nested reduced-child item scopes through the
original source positionings before reads or bootstrap persistence. A child made
from a map-only child therefore reads the root/source item records selected by
the parent scope, not the intermediate child positioning that intentionally owns
no copied item records.
Use PositioningClient.aresolve_item_source_scopes(...) before persisting
reduced-child metadata, and use aget_positioning_items_data(...) for explicit
item payload reads. The seed-copy workflow remains removed.
0.6.2 Mapped Viewport Scope Alias
keble-positioning 0.6.2 keeps map-only reduced children package-owned by
teaching aresolve_item_source_scopes(...) to treat mapped viewport rows as
aliases to their canonical source_positioning_id. Reducing from a mapped
viewport now reads the source-owned item records instead of rejecting the mapped
row as a non-source positioning.
0.8.0 Terminal Segmenting Consumption
keble-positioning 0.8.0 raises its keble-segmenting floor to >=0.5.0,<1
and consumes the terminal omission aggregate contract. View-detail reads should
continue to call aget_view_detailed(...) with display axes narrowed before
synthetic status cells are requested; omitted segmenting rows are handled by the
segmenting read model and require no positioning-side workaround.
0.9.0 Multi-Select Segmenting Consumption
keble-positioning 0.9.0 raises its keble-segmenting floor to
>=0.6.0,<1. The positioning view/detail API preserves the aggregate placement
counts returned by segmenting so multi-select axes can display one source item in
every selected option-cell combination without copying result rows.
0.9.1 Segmenting Cleanup Consumption
keble-positioning 0.9.1 raises its local wheel source and dependency floor to
keble-segmenting >=0.6.1,<1. This keeps current package tests and downstream
backend installs on the release where stale singular-result helper residue was
removed.
0.9.2 Owner-Type Metadata Mirror
keble-positioning 0.9.2 adds optional owner_type to
PositioningTaskMetadata. Backend queue envelopes can now carry the same owner
classification used by seller-profile context without adding a separate
positioning-side profile schema. The field is optional so already queued or
historical task metadata remains loadable.
0.10.15 Agent Grid Wrapper Tools
keble-positioning 0.10.15 keeps mutate_segmenting and
mutate_positioning_task_workspace as the canonical mutation paths while
adding approved agent-facing wrappers for common grid editing intent:
positioning_edit_grid_schemaaccepts dimension, option, mask, and ordering schema edits, then converts directly to canonical keble-segmentingActions.positioning_rebucket_dimensiondeletes retired option keys, creates fresh buckets, and finishes with terminalAUTO_SEGMENT_AND_MASKfor the affected dimension.positioning_apply_cell_assignmentsconverts approved explicit result or mask-result assignments into canonical upsert actions.
All wrappers require approval, validate the authorized grid id, and delegate to
PositioningClient.aapply_segmenting_actions(...). Do not add wrapper-specific
execution semantics outside the conversion schemas.
0.10.18 Usage Recorder Forwarding
Positioning task resources and segmenting action bridges forward the optional host usage recorder into downstream segmenting calls. Positioning does not price or persist task costs directly; backend task handlers attach the recorder with the task/root/owner context.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file keble_positioning-0.37.0.tar.gz.
File metadata
- Download URL: keble_positioning-0.37.0.tar.gz
- Upload date:
- Size: 961.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0de64351efa8a9f186431bea685032d76afc9cfae8b75d530c1c861bd60a2767
|
|
| MD5 |
023b0bbe8d71a97384e7315a4a8da3d6
|
|
| BLAKE2b-256 |
631cdb476e30b6f6fc210effe5b20e1003dd9321336fdaf10f33181c6faf03c7
|
File details
Details for the file keble_positioning-0.37.0-py3-none-any.whl.
File metadata
- Download URL: keble_positioning-0.37.0-py3-none-any.whl
- Upload date:
- Size: 97.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
80c5f8a1a3960c4fd30c7f4170370eaab6bc90e5534d85a7c0053cacece4636a
|
|
| MD5 |
bb52c9f719c3b92cbc164dd229495a66
|
|
| BLAKE2b-256 |
e8a4234673bce8584188dd750a932be44565508afebcac7fd0da3b2cfe069b94
|