Skip to main content

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

  1. Package version: 0.37.0
  2. Python baseline: >=3.13,<3.14
  3. Environment manager: uv only
  4. Segmenting dependency line: keble-segmenting >=0.38.0,<1
  5. Task dependency line: keble-task >=2.21.0,<3.0.0
  6. 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, and CLAUDE.md together.

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.py and agent/archetypes.py feed backend task-agent registration and must stay aligned with keble-helpers.ai.agent_descriptor.
  • agent/tools/*.py map registration config to pydantic-ai tool boundaries; the external pydantic-ai requires_approval flag is derived from the canonical AgentToolApprovalMode enum.
  • tests/test_package_contract.py pins 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 canonical aget_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:

  1. to_segmenting_prompt_context() returns the canonical context (only the nullable user_prompt falls back to positioning_context).
  2. require_marketplace() returns the concrete room/memory marketplace, raising a typed ServerSideInvalidParams if the context carries none.
  3. from_task_metadata_payload(raw) is the ONE storage-boundary adapter: it heals legacy stored docs (loose language/marketplace, no prompt_context) by rebuilding the typed context before validation, so already-created/completed tasks keep running. Use it at every parse site instead of model_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_DESCRIPTIONDEFAULT_MUTATE_SEGMENTING_TOOL_DESCRIPTION
  • POSITIONING_EDIT_GRID_SCHEMA_TOOL_DESCRIPTIONDEFAULT_POSITIONING_EDIT_GRID_SCHEMA_TOOL_DESCRIPTION
  • POSITIONING_REBUCKET_DIMENSION_TOOL_DESCRIPTIONDEFAULT_POSITIONING_REBUCKET_DIMENSION_TOOL_DESCRIPTION
  • POSITIONING_APPLY_CELL_ASSIGNMENTS_TOOL_DESCRIPTIONDEFAULT_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_by defaults to USER; it is part of the stored identity and is intentionally absent from PositioningViewUpdate (never patchable).
  • CRUDPositioningView exposes afirst_by_view_identity(view_owner, positioning_id, handled_by); aensure_indexes backfills legacy rows to USER, creates the unique compound index view_owner_positioning_handled_by_unique_idx, and drops the redundant (view_owner, positioning_id) index.
  • afirst_or_create_view / aget_view_detailed take handled_by (default USER). A brand-new AGENT row seeds its default display axes from the positioning owner's USER viewport.

Version 0.16.0 Positioning Study TASK Archetype

Program Phase 6 (subagent provider canonicalization):

  1. PositioningSubAgentProvider declares the positioning_study task archetype through the shared AgentProviderProtocol shape. Hosts should register this archetype and let agentic-chat generate delegate_positioning_study; do not add another backend wrapper tool for positioning creation.
  2. PositioningStudyInput is the typed parent-to-child contract: positioning_context, marketplace, optional name, and optional search_terms. Backend validates this model before creating the existing POSITIONING worker task.
  3. The archetype output is a host task result projection, not a second positioning workflow. keble-positioning continues 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):

  1. 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.
  2. register_query_tools is no longer exported from the package namespace — it is module-level in agent/query_registry.py for the owning package's internal use only.
  3. 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. Mutation requires_approval mirrors 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:

  1. construction captures positioning_client + optional tools_config;
  2. provider_id is the stable diagnostics id "positioning_query" (recorded in room diagnostics and mapped to a user-readable label in the frontend — do not change it);
  3. register(*, agent, context) ignores context and delegates to the canonical register_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:

  1. Language single-source. Bootstrap dimension discovery previously read the loose top-level PositioningTaskMetadata.language directly while every other segmenting path reads prompt_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.language is canonical and the top-level language is documented as a legacy fallback only.
  2. Segmenting 0.11.21 migration. Adopts keble-segmenting 0.11.21 (covariant Actions.build(Sequence[Action]), fixing the grid_tools.py variance lint). The 0.11.18+ "agent cohesion" refactor moved three free functions onto owning classes; positioning now calls SegmentingCoverageBase.build_source_signature, SegmentingCoverageBase.normalize_dimension_keys, and PresetMaskStageGroup.from_stage instead 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 into tests/.env.
  • Backend-style Azure provider env fields are converted by tests.config.Settings into the package text-model input shape, preserving one source of truth for live-test configuration.
  • The package requires pydantic-ai-slim >=1.102.0 to 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, and positioning_apply_cell_assignments.
  • The package consumes keble-helpers >=1.12.16 so 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-segmenting source 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 to PositioningClient.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

  • PositioningDependencies can 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 CellDisplayMetadataRow rows before they cross into keble-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-segmenting source declared in pyproject.toml.
  • Package contract tests now reject stale historical keble_segmenting-*.whl artifacts 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-bootstrap aauto_segment_and_mask(...) call.

Version 0.10.10 Progressive Bootstrap View Events

  • Positioning bootstrap emits the existing UPSERT_POSITIONING_VIEW event 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_segmenting an 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_MASK for 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_context rebuild the same typed context from positioning_context, language, and marketplace; no keyword-based off-scope guard is introduced in this release.

Version 0.10.7 Merged Context Metadata

  • This release supersedes 0.10.6 after the context branch merged with the already-published 0.10.5 item title fragment line.
  • PositionableItem.title_fragments and PositioningTaskMetadata.prompt_context are 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

  • PositioningTaskMetadata carries optional prompt_context typed as SegmentingPromptContext so 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, and marketplace fields only for legacy rows.
  • This release builds on 0.10.5 item title fragments rather than replacing that display metadata contract.

Version 0.10.5 Item Title Fragments

  • PositionableItem now carries provider-owned title_fragments, defaulting to an empty list for existing payload compatibility.
  • The field is serialized into PositionableItemRecord.item_payload so 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_context remains typed through SegmentingPromptContext so 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 run and uv sync can 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_types can 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 omitted AUTO_SEGMENT_AND_MASK actions, while explicit auto_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

  • PositioningView now stores display_source as AUTO or USER.
  • 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.3 pair-based coverage scopes.

Version 0.10.1 Update

  • PositioningViewDetailed now includes package-owned coverage_state, derived from exact segmenting coverage for the displayed dimension ids and current grouped item source.
  • Missing exact coverage derives NOT_LOADED instead 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.2 so 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, cell mask-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 socksio so HTTPX can run under developer SOCKS proxy environments during live/model-backed package tests.

Version 0.9.5 Update

  • PositioningTaskMetadata now uses the general mask_generation_enabled field that matches keble-segmenting 0.8.5.
  • Lightweight host-owned preview tasks can set the flag to False to 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

  • PositioningTaskMetadata now accepts optional preset_dimension_types and forwards them to segmenting bootstrap discovery.
  • FreshItemsRequest forwards persisted positioning_metadata to 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

  • PositioningTaskMetadata added a preview-facing mask override through the existing backend bridge.
  • Lightweight host-owned preview tasks can set the flag to False to generate dimensions and cell membership without generated masks.

Version 0.9.2 Update

  • PositioningTaskMetadata now accepts optional owner_type so 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.1 wheel 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 store option_keys and 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,<1 so 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,<1 so positioning tests exercise the same preset-role and grid-scoped mask contract consumed by backend 2.5.4.
  • Historical local keble_segmenting wheel artifacts older than 0.4.5 were 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.toml instead of pinning a stale historical release value.
  • mutate_positioning_task_workspace keeps 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_actions instead of backend execution modules.
  • PositioningTaskWorkspaceActionedResult reuses generic keble-task TaskActionCreatedTask and TaskActionCreatedRelation payloads, so created child tasks and relation metadata share one canonical DTO.
  • keble_positioning.agent.registry.register(...) registers mutate_positioning_task_workspace only 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_scopes and 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(...), and aget_positioning_items_data(...) are the canonical map-only read surface.
  • SEED_ONLY writes no child item records or relationships; SEED_AND_DISCOVER writes only newly discovered fresh child records.
  • Mapped viewport rows still use source_positioning_id; reduced children use item_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: PositioningAgentContext carries root_task_id and allowed_grid_ids, while segmenting mutations target Actions.grid_id.

Version 0.4.11 Update

  • Invalid-parameter diagnostics now pass typed primitive strings into keble-exceptions instead 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

  • PositioningAgentContext is now task-room scoped: owner, language, root_task_id, and allowed_grid_ids.
  • mutate_segmenting validates Actions.grid_id against allowed_grid_ids and delegates the whole canonical Actions payload.
  • PositioningClient.aapply_segmenting_actions(...) resolves the source positioning from payload.grid_id and owner; callers no longer pass positioning_id, grid_id, and actions separately.
  • Backend enrichment can use aget_positioning_by_task_id(...) and aget_source_positioning_by_grid_id(...).
  • aget_grid_for_agent(...) now requires compare_dimension_key and builds GridAgentContextRequest.

Version 0.4.9 Update

  • aapply_segmenting_actions(...) now requires explicit grid_id and forwards one canonical Actions(grid_id=...) payload into keble-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 nested SegmentingAgentDeps.segmenting.event_callbacks when 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(...) and PositionableItemsByRel.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.
  • CellDisplay is display-only (description / images); complete selected item identity comes from segmenting aggregate item_keys.

Design Direction

The current rewrite is bridge-first:

  1. keble-segmenting owns action execution, progress, and grid mutation.
  2. keble-positioning owns 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.
  3. 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:

  1. persisted positioning CRUD
  2. persisted view CRUD
  3. slim upstream bootstrap via PositioningTaskMetadata, PositioningBootstrapResult, and abootstrap_positioning(...)
  4. thin upstream task wrapper via atask_handler(...)
  5. fresh-item loading / next-page loading
  6. viewport-map creation via acreate_mapped_positioning(...)
  7. canonical aapply_segmenting_actions(...)
  8. explicit aget_segmenting_progress(...)
  9. explicit ainterrupt_segmenting(...)
  10. 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:

  1. one upstream task_id owns one positioning shell
  2. one positioning shell points at one segmenting grid_id
  3. bootstrap retries reuse that existing shell instead of creating duplicates
  4. resolved search_terms are persisted on task metadata and callback pagination state, while positioning rows do not persist search_terms
  5. mapped positioning rows use their own task_id and view, share the source grid_id, and point at source-owned items through source_positioning_id
  6. 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:

  • PositioningAgentDeps inherits keble_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:

  1. view
  2. positioning
  3. grid
  4. dimensions
  5. aggregated_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:

  1. uv run python -c "import keble_segmenting"
  2. uv run python -c "import keble_positioning"
  3. uv run pytest tests -q
  4. uv run pytest -m "not live" tests -q
  5. uv run pytest -m live tests -q
  6. uv run python tests/live/refresh_keepa_green_alien_plush_cache.py
  7. uv lock
  8. uv sync

The default pytest run is intentionally strict and comprehensive:

  1. it includes the live Keepa plus env-backed Azure/OpenAI tests under tests/live
  2. it fails fast when tests/.env, Mongo, Redis, Keepa, or text-model config is missing
  3. 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:

  1. positioning_edit_grid_schema accepts dimension, option, mask, and ordering schema edits, then converts directly to canonical keble-segmenting Actions.
  2. positioning_rebucket_dimension deletes retired option keys, creates fresh buckets, and finishes with terminal AUTO_SEGMENT_AND_MASK for the affected dimension.
  3. positioning_apply_cell_assignments converts 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

keble_positioning-0.37.0.tar.gz (961.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

keble_positioning-0.37.0-py3-none-any.whl (97.8 kB view details)

Uploaded Python 3

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

Hashes for keble_positioning-0.37.0.tar.gz
Algorithm Hash digest
SHA256 0de64351efa8a9f186431bea685032d76afc9cfae8b75d530c1c861bd60a2767
MD5 023b0bbe8d71a97384e7315a4a8da3d6
BLAKE2b-256 631cdb476e30b6f6fc210effe5b20e1003dd9321336fdaf10f33181c6faf03c7

See more details on using hashes here.

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

Hashes for keble_positioning-0.37.0-py3-none-any.whl
Algorithm Hash digest
SHA256 80c5f8a1a3960c4fd30c7f4170370eaab6bc90e5534d85a7c0053cacece4636a
MD5 bb52c9f719c3b92cbc164dd229495a66
BLAKE2b-256 e8a4234673bce8584188dd750a932be44565508afebcac7fd0da3b2cfe069b94

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page