keble-keepa 1.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. keble_keepa-1.2.1/.gitattributes +2 -0
  2. keble_keepa-1.2.1/.gitignore +155 -0
  3. keble_keepa-1.2.1/.python-version +1 -0
  4. keble_keepa-1.2.1/AGENTS.md +18 -0
  5. keble_keepa-1.2.1/AiAgentChangesLogs.md +272 -0
  6. keble_keepa-1.2.1/CLAUDE.md +15 -0
  7. keble_keepa-1.2.1/PKG-INFO +533 -0
  8. keble_keepa-1.2.1/README.md +514 -0
  9. keble_keepa-1.2.1/deps/keble_typing-0.3.7-py3-none-any.whl +0 -0
  10. keble_keepa-1.2.1/keble_keepa/__init__.py +9 -0
  11. keble_keepa-1.2.1/keble_keepa/api/__init__.py +1 -0
  12. keble_keepa-1.2.1/keble_keepa/api/main.py +963 -0
  13. keble_keepa-1.2.1/keble_keepa/api/memory_cache.py +58 -0
  14. keble_keepa-1.2.1/keble_keepa/batch_loader.py +657 -0
  15. keble_keepa-1.2.1/keble_keepa/crud/__init__.py +1 -0
  16. keble_keepa-1.2.1/keble_keepa/crud/keepa_cache.py +167 -0
  17. keble_keepa-1.2.1/keble_keepa/schemas/__init__.py +13 -0
  18. keble_keepa-1.2.1/keble_keepa/schemas/api_response/__init__.py +34 -0
  19. keble_keepa-1.2.1/keble_keepa/schemas/api_response/browsing_deal.py +179 -0
  20. keble_keepa-1.2.1/keble_keepa/schemas/api_response/category_lookup.py +30 -0
  21. keble_keepa-1.2.1/keble_keepa/schemas/api_response/category_search.py +22 -0
  22. keble_keepa-1.2.1/keble_keepa/schemas/api_response/lightning_deal.py +25 -0
  23. keble_keepa-1.2.1/keble_keepa/schemas/api_response/most_rated_user.py +16 -0
  24. keble_keepa-1.2.1/keble_keepa/schemas/api_response/product_finder.py +841 -0
  25. keble_keepa-1.2.1/keble_keepa/schemas/api_response/product_search.py +64 -0
  26. keble_keepa-1.2.1/keble_keepa/schemas/api_response/request_best_seller.py +80 -0
  27. keble_keepa-1.2.1/keble_keepa/schemas/api_response/request_products.py +154 -0
  28. keble_keepa-1.2.1/keble_keepa/schemas/api_response/request_seller_info.py +50 -0
  29. keble_keepa-1.2.1/keble_keepa/schemas/api_response/retrieve_token_status.py +44 -0
  30. keble_keepa-1.2.1/keble_keepa/schemas/base.py +167 -0
  31. keble_keepa-1.2.1/keble_keepa/schemas/db.py +19 -0
  32. keble_keepa-1.2.1/keble_keepa/schemas/non_keepa/__init__.py +2 -0
  33. keble_keepa-1.2.1/keble_keepa/schemas/non_keepa/bsr.py +100 -0
  34. keble_keepa-1.2.1/keble_keepa/schemas/non_keepa/unit_data.py +676 -0
  35. keble_keepa-1.2.1/keble_keepa/schemas/objects/README.md +3243 -0
  36. keble_keepa-1.2.1/keble_keepa/schemas/objects/__init__.py +29 -0
  37. keble_keepa-1.2.1/keble_keepa/schemas/objects/best_seller.py +31 -0
  38. keble_keepa-1.2.1/keble_keepa/schemas/objects/category.py +65 -0
  39. keble_keepa-1.2.1/keble_keepa/schemas/objects/deal.py +94 -0
  40. keble_keepa-1.2.1/keble_keepa/schemas/objects/lightning_deal.py +121 -0
  41. keble_keepa-1.2.1/keble_keepa/schemas/objects/marketplace_offer.py +134 -0
  42. keble_keepa-1.2.1/keble_keepa/schemas/objects/product.py +2822 -0
  43. keble_keepa-1.2.1/keble_keepa/schemas/objects/seller.py +214 -0
  44. keble_keepa-1.2.1/keble_keepa/schemas/objects/statistics.py +285 -0
  45. keble_keepa-1.2.1/keble_keepa/testing/__init__.py +38 -0
  46. keble_keepa-1.2.1/keble_keepa/testing/assertions.py +28 -0
  47. keble_keepa-1.2.1/keble_keepa/testing/cassettes.py +42 -0
  48. keble_keepa-1.2.1/keble_keepa/testing/factories.py +152 -0
  49. keble_keepa-1.2.1/keble_keepa/testing/fake_gateway.py +129 -0
  50. keble_keepa-1.2.1/keble_keepa/testing/fixtures/product_no_buybox.json +34 -0
  51. keble_keepa-1.2.1/keble_keepa/testing/fixtures/product_normal.json +34 -0
  52. keble_keepa-1.2.1/keble_keepa/testing/markers.py +35 -0
  53. keble_keepa-1.2.1/keble_keepa/testing/namespace.py +14 -0
  54. keble_keepa-1.2.1/keble_keepa/testing/pytest_plugin.py +21 -0
  55. keble_keepa-1.2.1/keble_keepa/utils/__init__.py +6 -0
  56. keble_keepa-1.2.1/keble_keepa/utils/keepa.py +98 -0
  57. keble_keepa-1.2.1/keble_keepa/utils/keyword.py +119 -0
  58. keble_keepa-1.2.1/poetry.lock +3214 -0
  59. keble_keepa-1.2.1/pyproject.poetry.toml +27 -0
  60. keble_keepa-1.2.1/pyproject.toml +53 -0
  61. keble_keepa-1.2.1/pyrightconfig.json +12 -0
  62. keble_keepa-1.2.1/tests/__init__.py +0 -0
  63. keble_keepa-1.2.1/tests/config.py +83 -0
  64. keble_keepa-1.2.1/tests/conftest.py +123 -0
  65. keble_keepa-1.2.1/tests/test_api/__init__.py +0 -0
  66. keble_keepa-1.2.1/tests/test_api/test_batch_loader.py +1013 -0
  67. keble_keepa-1.2.1/tests/test_api/test_cache_newest_first.py +240 -0
  68. keble_keepa-1.2.1/tests/test_api/test_keepa_api_timeouts.py +238 -0
  69. keble_keepa-1.2.1/tests/test_api/test_main.py +385 -0
  70. keble_keepa-1.2.1/tests/test_api/test_objects/test_product.py +73 -0
  71. keble_keepa-1.2.1/tests/test_api/test_objects/test_product_prompt_snapshot_irl.py +94 -0
  72. keble_keepa-1.2.1/tests/test_api/test_request_products_cache_parts.py +179 -0
  73. keble_keepa-1.2.1/tests/test_api/test_usage_accounting.py +122 -0
  74. keble_keepa-1.2.1/tests/test_objects/__init__.py +0 -0
  75. keble_keepa-1.2.1/tests/test_objects/test_keyword_inflection.py +55 -0
  76. keble_keepa-1.2.1/tests/test_objects/test_product.py +0 -0
  77. keble_keepa-1.2.1/tests/test_objects/test_product_average_price_int.py +77 -0
  78. keble_keepa-1.2.1/tests/test_objects/test_product_guessed_has_offer_since_datetime.py +255 -0
  79. keble_keepa-1.2.1/tests/test_objects/test_product_prompt_snapshot.py +287 -0
  80. keble_keepa-1.2.1/tests/test_objects/test_unit_data_time_weighted_mean.py +103 -0
  81. keble_keepa-1.2.1/tests/test_testing/test_keepa_testing_toolkit.py +137 -0
  82. keble_keepa-1.2.1/uv.lock +1515 -0
@@ -0,0 +1,2 @@
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
@@ -0,0 +1,155 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
105
+ __pypackages__/
106
+
107
+ # Celery stuff
108
+ celerybeat-schedule
109
+ celerybeat.pid
110
+
111
+ # SageMath parsed files
112
+ *.sage.py
113
+
114
+ # Environments
115
+ .env
116
+ .venv
117
+ env/
118
+ venv/
119
+ ENV/
120
+ env.bak/
121
+ venv.bak/
122
+
123
+ # Spyder project settings
124
+ .spyderproject
125
+ .spyproject
126
+
127
+ # Rope project settings
128
+ .ropeproject
129
+
130
+ # mkdocs documentation
131
+ /site
132
+
133
+ # mypy
134
+ .mypy_cache/
135
+ .dmypy.json
136
+ dmypy.json
137
+
138
+ # Pyre type checker
139
+ .pyre/
140
+
141
+ # pytype static type analyzer
142
+ .pytype/
143
+
144
+ # Cython debug symbols
145
+ cython_debug/
146
+
147
+ # PyCharm
148
+ # JetBrains specific template is maintainted in a separate JetBrains.gitignore that can
149
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
150
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
151
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
152
+ .idea/
153
+ .vscode/
154
+ .lprof/
155
+ .history/
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,18 @@
1
+ # Keble Keepa Agent Instructions
2
+
3
+ Follow the root `TESTING_GUIDELINE.md` when changing tests in this repo.
4
+
5
+ ## Testing Rules
6
+
7
+ - Use `keble_keepa.testing` for Keepa-dependent tests before creating new mocks.
8
+ - Keep default tests offline: no real Keepa, no real LLMs, no live HTTP calls.
9
+ - Mark recorded payload tests with `contract` / `keepa_contract`.
10
+ - Mark Mongo-backed cache tests with `integration`, `db`, `mongo`, and `keepa`.
11
+ - Mark live Keepa tests with `live`, `keepa_live`, and `requires_keepa_live`.
12
+ - Do not add session-wide DB cleanup. Use isolated cache collections or exact
13
+ query-key cleanup per test.
14
+ - Run `uv run pytest -m "not live and not slow and not eval and not local_stack"`
15
+ and `npx --yes pyright .` after test-infrastructure changes.
16
+ - Update `README.md`, `AGENTS.md`, `CLAUDE.md`, and `AiAgentChangesLogs.md`
17
+ whenever testing behavior, commands, markers, fixtures, or dependencies
18
+ change.
@@ -0,0 +1,272 @@
1
+ ## 2026-06-30 09:34 CST
2
+ State: branch `fix/keepa-aiohttp-proxy-env`; package line `1.1.15` -> `1.1.16`.
3
+
4
+ zma:
5
+ - Continue the live E2E red-suite follow-up after local Docker proxy wiring.
6
+ - Fix the remaining Keepa-backed positioning failures by making async Keepa aiohttp calls honor Docker proxy env, then rebuild and repin backend.
7
+
8
+ Ai:
9
+ - Added a typed `KeepaAioHttpSessionConfig` owner for async Keepa sessions and routed `_aget` / `_apost` through it.
10
+ - Set aiohttp `trust_env=True` for async Keepa GET/POST requests so local Docker `HTTP_PROXY` / `HTTPS_PROXY` env is honored without adding package-level proxy values or production config.
11
+ - Updated timeout/session tests to assert both configured timeout values and proxy-env honoring.
12
+
13
+ ## 2026-06-22 (fix: null contextFreeName breaks category lookup) — 1.1.15
14
+ State: branch `fix/keepa-null-context-free-name` (from 1.1.14 @ 45b2e99). Fixes v3 Shuffle "Failed to load categories".
15
+
16
+ zma: v3 Shuffle category dropdown shows "Failed to load categories"; prod curl revealed `CategoryLookupResponse.categories.<id>.contextFreeName → Input should be a valid string, input_value=None`.
17
+
18
+ Ai: Keepa occasionally returns a null `contextFreeName` for some category nodes. `Category.context_free_name` was a required `str`, so the entire `CategoryLookupResponse` failed to parse and the (public) root/children category endpoints 500'd. Made `context_free_name: Optional[str] = None` + a `model_validator(mode="after")` that falls back to `name` when null, so downstream consumers (keble_amz_shuffle) still receive a real string. Bumped 1.1.14 -> 1.1.15. Verified: a category payload with `contextFreeName=None` now parses with `context_free_name == name`; normal values unchanged.
19
+
20
+ ## 2026-06-20 (opt-in singular/plural search expansion) — 1.1.14
21
+ State: branch `fix/keyword-search-coverage` (from `main` @ 1.1.13); package line 1.1.13 -> 1.1.14. Track 3 of the keyword-coverage plan.
22
+
23
+ zma: report-gen's Keepa title search sends RAW keywords with no singular/plural variant (esp. `*ies`), under-covering markets that split across inflections (e.g. "berry" misses "berries").
24
+
25
+ Ai:
26
+ - New dependency-free `keble_keepa/utils/keyword.py`: `singular_plural_variants(token)` (real BIDIRECTIONAL inflection — classifies by surface and emits the OPPOSITE number: `berry<->berries` ies-rule, `box<->boxes` es-rule, `dog<->dogs` plain-s; guards `glass`/`ss` and vowel+y; never both directions at once so no `dogses`) and `expand_singular_plural(keywords)` (inflects only the trailing head noun of each phrase, order-preserving dedup). Exported from `keble_keepa`. This is a deliberate minimal duplicate of `keble_amz_product_report.keyword_text` (keepa is the lower layer and must not import amz).
27
+ - `batch_loader.asearch_asins_from_keepa` gained OPT-IN `expand_keyword_variants: bool = False`: when True, expands the search terms via the helper, deduplicates, and caps at the Keepa 50-keyword title-search limit before fan-out. The leaf is the single cap-enforcement site. Threaded the flag through `aload_similar_products` and `aload_similar_products_with_limit` (both internal leaf calls). Default False keeps every other caller (keble-keepa-analysis, keble-products, internal) byte-for-byte identical.
28
+ - Tests: `tests/test_objects/test_keyword_inflection.py` (pure-unit inflection rules) + `tests/test_api/test_batch_loader.py` new cases (expansion fires only when opted-in; default OFF leaves keywords unchanged; 50-keyword cap; wrapper forwards the flag). Full `test_api`: 57 passed, 1 skipped.
29
+
30
+ ## 2026-06-30 13:22 CST
31
+ State: branch `fix/keepa-publish-classifier`; package line moving from `1.2.0` to `1.2.1`.
32
+
33
+ zma:
34
+ - Complete the `keble-keepa` testing-toolkit release and publish it after verification.
35
+
36
+ Ai:
37
+ - Publish of `1.2.0` was blocked by PyPI rejecting the pre-existing custom classifier `Private :: Core keepa warpper therefore keep private`.
38
+ - Removed the invalid classifier from Hatch and Poetry metadata.
39
+ - Bumped to `1.2.1` instead of mutating the already-pushed `v1.2.0` tag.
40
+
41
+ ## 2026-06-30 13:10 CST
42
+ State: branch `refactor/testing-layers-keble-keepa`; package line moving from `1.1.16` to `1.2.0`.
43
+
44
+ zma:
45
+ - Continue the cross-repo testing refactor after `keble-db`.
46
+ - Enrich `keble-keepa` with shared testing fixtures and guards.
47
+ - Keep default tests fast/offline, align markers, run pytest and `npx --yes pyright .`.
48
+
49
+ Ai:
50
+ - Added `keble_keepa.testing` with fake gateway, typed product factories, variation/raw payload factories, cassette secret filters, live Keepa guard, namespace helper, and schema assertions.
51
+ - Registered the cross-repo pytest markers and pytest plugin entry point.
52
+ - Reworked test bootstrap so default pytest no longer requires `.env`, Mongo, Redis, or Keepa token.
53
+ - Replaced session-wide Keepa cache wiping with per-test cache collection isolation plus exact query-key cleanup in Mongo-backed tests.
54
+ - Added toolkit unit coverage and `pyrightconfig.json`.
55
+ - Validation: `uv run pytest -q -m "not live and not slow and not eval and not local_stack"` passed (`64 passed, 34 skipped, 16 deselected`), `uv run pytest -q` passed (`64 passed, 50 skipped`), `npx --yes pyright .` passed, and `uv build` produced `keble_keepa-1.2.0` sdist/wheel.
56
+
57
+ ## 2026-06-10 (cache poisoning fix, v1.1.12)
58
+ State: branch `fix/keepa-cache-newest-first-and-refetch` (from `main` @ v1.1.11); package line moving from `1.1.11` to `1.1.12`.
59
+
60
+ zma:
61
+ - Live bootstrap report task `6a291e80cf834afeeb632e39` failed with 相关产品不足 / NO_SUFFICIENT_DATA while the core ASIN `B0DCF53PCH` was missing from a 10-ASIN cache read. Suspected NOT a genuine lack-of-products case; asked for investigation and the agreed fix policy: newest-first cache reads + a forced-refetch escape hatch.
62
+
63
+ Ai:
64
+ - Root cause: `to_response_cache` writes a negative `cache.data=None` row for every requested ASIN missing from a (partial) Keepa response, and all per-key cache readers used unsorted `first/afirst` (Mongo natural order) so the OLDEST row won. A negative row written 2026-06-05 shadowed a GOOD row written 2026-06-08 for the same key until TTL expiry.
65
+ - `crud/keepa_cache.py`: new `_newest_first_cache_query` helper — all per-key readers (`get_from_cache`, `aget_from_cache`, `aget_request_products_cache_parts`) now order by `created` DESC with `_id` DESC tiebreak (same-millisecond writes stay deterministic).
66
+ - `api/main.py`: `arequest_products` / `_aget` gained `skip_cache_read: bool = False` — bypasses Redis+Mongo cache READ, still WRITES the fresh response, so a forced fetch repairs a poisoned key for all later callers. `ready_mongo` now also creates the `{"key": 1, "created": -1}` index backing the per-key read (was a collection scan).
67
+ - Tests: `tests/test_api/test_cache_newest_first.py` — newer-good-supersedes-older-negative for both readers + skip_cache_read bypass/repair (fake aiohttp live response, real pytest Mongo). Full suite: 90 passed, 1 skipped (pre-existing).
68
+ ## 2026-06-10 14:39 CST
69
+ State: branch `fix/average-price-time-weighted-mean` (from `main` @ v1.1.10); package line moving from `1.1.10` to `1.1.11`.
70
+
71
+ AndrewChan:
72
+ - Production report `6a23b711148a3de3da0055ac` (ASIN `B08SSWY8SH`, amazon.com) showed price $97.95 while the real price is $19.95. Root-caused to `Product.average_price_int`; requested the time-weighted-mean fix (option 3 of the analysis).
73
+
74
+ Ai:
75
+ - Root cause: Keepa csv NEW (index 1) only records change points. The price has been stable at $19.95 since 2026-02-02, so the "recent 3 months" `filter_list(...)` window was empty and the code fell back to the all-time arithmetic mean of change points — dominated by years-old $90-$110 oscillations — which is exactly 9795 ($97.95).
76
+ - Added `UnitDataList.time_weighted_mean(gte=..., lte=...)` in `keble_keepa/schemas/non_keepa/unit_data.py`: duration-weighted average over the change-point series; forward-fills the last change point before the window start; clamps each value's effective span to the window; rounds away 1-ulp float noise so constant series return their exact value; returns `None` when no value covers the window. `UnitDataList.mean` is unchanged (other consumers keep arithmetic-mean semantics).
77
+ - `Product.average_price_int` now uses `time_weighted_mean(gte=now-3months)` with an all-time `time_weighted_mean()` fallback; the `stats` fallback chain is unchanged. Updated docstrings.
78
+ - Known limitation documented in README: `-1` (offer unavailable) entries are still dropped at `UnitDataList.build`, so out-of-stock gaps count as the previous price's duration (same as before this fix).
79
+ - Tests: `tests/test_objects/test_unit_data_time_weighted_mean.py` (forward-fill, duration weighting, change-frequency bias, empty/edge windows) and `tests/test_objects/test_product_average_price_int.py` (production regression scenario expecting 1995, mid-window change, single stable point, empty history). Full suite: `pytest tests/ -q` → 87 passed, 1 skipped (pre-existing skip).
80
+ - End-to-end verification with live Keepa data for `B08SSWY8SH`: `average_price_int` now returns `1995` ($19.95) instead of `9795` ($97.95).
81
+ - Bumped `1.1.10 -> 1.1.11`, regenerated `uv.lock` (`uv lock --check` passes), built the wheel and shipped it to `keble.backend/dependencies/` with the `requirements.keble.txt` bump (user-approved continuation). Compatibility checked: backend's pinned `keble_helpers-1.13.0` exports `UsageAccountingEvent` required by this package line. Production mirror intentionally not touched (user instruction).
82
+
83
+ ## 2026-06-06 13:00 CST
84
+ State: branch `fix/keepa-prompt-cleanup`; package line moving from `1.1.9` to `1.1.10` (release-hygiene follow-up).
85
+
86
+ zma:
87
+ - Double-check a follow-up audit of the 1.1.9 prompt fixes and apply the valid cleanup items.
88
+
89
+ Ai:
90
+ - pyright: typed `Product._is_valid_keepa_timestamp` as `TypeGuard[int | float]` so `_extract_first_keepa_timestamp_from_series` narrows before `int(first)` (clears the last pre-existing `product.py:2425` error) — chose the TypeGuard over the audit's duplicate-`isinstance` suggestion to avoid duplicating validation logic.
91
+ - tests: `pytest_sessionfinish` now also wipes the Keepa pytest Mongo cache collection, symmetric with `pytest_sessionstart`.
92
+ - lock: regenerated `uv.lock` (was stale at `1.1.8`; `uv lock --check` now passes at `1.1.10`).
93
+ - Bumped metadata `1.1.9 -> 1.1.10`, rebuilt the wheel. Deferred (per zma) the prompt-builder ownership refactor into schema classmethods; no backport to `0.0.x`/`1.0.x` (no `KeepaProductPrompt*` there).
94
+
95
+ ## 2026-06-06 12:00 CST
96
+ State: branch `fix/keepa-prompt-typing-and-coverage`; package line moving from `1.1.8` to `1.1.9`.
97
+
98
+ zma:
99
+ - Fix Pylance `reportOptionalMemberAccess` on `retry_state.outcome.exception()` in `KeepaApiBatchLoader`.
100
+ - Fix Pylance `reportArgumentType` where `KeepaProductPromptPhysicalFacts` dimension tuples (`tuple[float | None, ...]`) did not narrow to `tuple[float, float, float] | None`.
101
+ - Confirm `int(round(latest_rating_unit.value))` does not corrupt ratings.
102
+ - Address the silent-null risk of string-keyed field reads (`getattr`-style) and weak real-world test coverage of the `KeepaProductPrompt*` builders.
103
+
104
+ Ai:
105
+ - `batch_loader.py`: added `KeepaApiBatchLoader._retry_exception(retry_state)` with an explicit `outcome is None` guard (no getattr/try-except) and reused it in both `before_sleep` log callbacks, clearing the optional-access diagnostic.
106
+ - `schemas/objects/product.py`: added `_dimension_triple_cm(*, length, width, height) -> tuple[float, float, float] | None` whose explicit per-axis None guard narrows the type; `KeepaProductPromptPhysicalFacts.from_product` now builds both item/package triples through it (behavior unchanged: all three axes required).
107
+ - Rating fix: `_prompt_metric_value_text` now always divides the Keepa 0..50 rating unit by 10, so a genuine sub-0.5-star product (Keepa 1..5) renders correctly (e.g. Keepa `5` -> `0.5/5`, previously the `value > 5` heuristic wrongly produced `5.0/5`). The 0..50 rounding question is a no-op: the value rounded is the raw integer Keepa unit, the star `/10` conversion happens at display time.
108
+ - Hardening: `_prompt_model_value` now raises `KeyError` when `field_name` is not a declared field of the model (typo/removed field fails loudly instead of nulling forever); legitimately-absent declared fields on partial `model_construct` fixtures still return `None`. Verified all 33 field names currently read are valid `Product` attributes.
109
+ - Tests: added a full-scale rating parametrize (incl. the `(5, "0.5/5")` regression row) and an unknown-field `KeyError` test to `tests/test_objects/test_product_prompt_snapshot.py`; added live coverage `tests/test_api/test_objects/test_product_prompt_snapshot_irl.py` asserting physical facts/metrics/ranks (heavy ASIN) and rating/reviews (with `rating=1`) populate non-null from real Keepa products — guarding upstream Keepa field drift the field-name check cannot catch.
110
+ - Bumped metadata `1.1.8 -> 1.1.9`, rebuilt the wheel, copied it into `keble.backend/dependencies/`, and updated `requirements.keble.txt`. All mock + live object/batch-loader tests pass (54 + 2); both originally-reported Pylance errors are gone. Note: one pre-existing unrelated `int(object)` pyright error at `product.py:2425` (`_collect_keepa_timestamp_series`) is left out of scope.
111
+
112
+ ## 2026-05-20 19:16 CST
113
+ State: branch `fix/product-prompt-snapshot`; package line moving from `1.1.5` to `1.1.6`.
114
+
115
+ zma:
116
+ - Implement compact Keepa product prompt snapshots for LLM/RAG prompts.
117
+ - Keep raw Keepa payload storage lossless, default historical windows to 7 days with explicit kwargs, exclude raw histories/reviews/offers, export public types, and add tests.
118
+
119
+ Ai:
120
+ - Added `KeepaProductPromptSnapshot`, bounded series/point/rank/variation/physical/latest metric schemas, plus `Product.to_prompt_snapshot(...)` and `Product.to_prompt_markdown(...)`.
121
+ - Snapshot output keeps identity, category, image, features, dimensions, variations, latest metrics, and capped recent series while excluding raw `csv`, `salesRanks`, monthly-sold history, offers, and review payloads.
122
+ - Bumped package metadata to `1.1.6`, built `dist/keble_keepa-1.1.6-py3-none-any.whl`, and verified focused prompt snapshot/release-date tests.
123
+
124
+ ## 2026-04-29 19:23 CST
125
+ State: `keble-keepa` branch `fix/keepa-batch-concurrency` prepared for version `1.1.4`.
126
+
127
+ zma:
128
+ - Investigate review feedback on the Keepa hardening follow-up.
129
+ - Confirm whether `max_concurrent_tasks` is enforced and update the codebase for confirmed issues.
130
+
131
+ Ai:
132
+ - Confirmed `max_concurrent_tasks` was not enforced for product batch requests.
133
+ - Added validated positive `max_concurrent_tasks` handling and wrapped product requests in an async semaphore.
134
+ - Added regression coverage proving five one-ASIN batches never exceed two concurrent Keepa requests when configured that way.
135
+ - Bumped package metadata to `1.1.4` for backend consumption.
136
+
137
+ ## 2026-04-29 14:41 CST
138
+ State: `keble-keepa` branch `fix/keepa-request-timeouts` prepared for version `1.1.3`.
139
+
140
+ zma:
141
+ - Implement the hardened Keepa 50-ASIN request plan from `main`.
142
+ - Add HTTP timeout controls, partial product cache reuse, smaller product batches, and deterministic tests.
143
+ - Build and prepare the package for backend consumption.
144
+
145
+ Ai:
146
+ - Added validated `KeepaApi` sync/async timeout configuration and applied it to all requests calls.
147
+ - Added partial product cache loading that preserves exact `RequestProductsQuery` params and treats `data=None` rows as known cache hits.
148
+ - Updated `KeepaApiBatchLoader` with configurable `product_batch_size=20`, cache preloading, ordered merge output, and richer retry warning logs.
149
+ - Added timeout, partial-cache, batch-loader, split-fallback, and warning-log tests; verified the targeted Keepa suite passed.
150
+ - Bumped package metadata to `1.1.3` and built `dist/keble_keepa-1.1.3-py3-none-any.whl`.
151
+
152
+ ## 2026-04-22 15:29 CST
153
+ State: `keble-keepa` current branch `fix/keepa-images-field` prepared for version `1.1.2`.
154
+
155
+ zma:
156
+ - Investigate production report thumbnails rendering as grey boxes.
157
+ - Fix the Keepa image contract drift for all affected maintained lines.
158
+ - Use full patch-release flow with branches, tags, and downstream backend updates.
159
+
160
+ Ai:
161
+ - Added typed `ProductImage` support for Keepa's structured `images` field.
162
+ - Updated `Product.image_filenames` / `image_filename` / `image_urls` / `image_url` to prefer structured images and preserve `imagesCSV` fallback.
163
+ - Added current-line regression coverage for structured large image, structured medium fallback, legacy CSV fallback, and URL generation.
164
+ - Bumped current package metadata to `1.1.2`.
165
+
166
+ ## 2026-04-21 16:52 CST
167
+ State: `keble-keepa` current branch `fix/new-price-is-map-optional` prepared for version `1.1.1`.
168
+
169
+ zma:
170
+ - Found bug: `new_price_is_map` is actually optional in Keepa payloads.
171
+ - Fix Keepa in all major versions.
172
+ - Copy the latest `1.*` fix into `keble.backend` `2.1.*` main and update `requirements.keble.txt`.
173
+
174
+ Ai:
175
+ - Bumped package version to `1.1.1`.
176
+ - Updated `Product.new_price_is_map` to accept missing `newPriceIsMAP` as `None` while preserving explicit boolean values.
177
+ - Added regression tests for missing, `true`, and `false` `newPriceIsMAP` payloads.
178
+ - Updated README release notes for the current bugfix.
179
+
180
+ ## 2026-02-27 19:25
181
+ State: Backported guessed release-date extraction logic to backend-compatible keepa version line.
182
+
183
+ zhenhao-ma:
184
+ - Investigate release-date empty/new-old misclassification issue.
185
+ - Ensure guessed release-date extraction has parametrized coverage for CSV/timestamp pair data.
186
+ - Prepare package updates consumable by keble backend runtime.
187
+
188
+ Ai:
189
+ - Bumped package version to `1.0.2` on branch `1.0.2`.
190
+ - Upgraded `Product.guessed_has_offer_since_datetime` extraction to scan valid Keepa timestamp series recursively (CSV + keepaTime-described lists), with invalid-series filtering.
191
+ - Added `tests/test_objects/test_product_guessed_has_offer_since_datetime.py` with parametrized timestamp scenarios and invalid-data guard cases.
192
+ - Updated `README.md` release notes for the backport.
193
+ - Validation: `pytest tests/test_objects/test_product_guessed_has_offer_since_datetime.py -q --confcutdir=tests/test_objects` passed (8 tests).
194
+
195
+ ## 2026-03-06 10:02
196
+ State: Keepa batch loading no longer fails the whole pipeline when one batch exhausts retries.
197
+
198
+ zhenhao-ma:
199
+ - Investigate positioning task failure caused by `KeepaApiBatchLoader._keepa_request_products` retry exhaustion.
200
+ - Fix runtime behavior so task can continue with partial data.
201
+ - Add regression coverage and release a new keepa package version.
202
+
203
+ Ai:
204
+ - Bumped `keble-keepa` to `1.0.3` on branch `1.0.3`.
205
+ - Updated `keble_keepa/batch_loader.py`:
206
+ - keepa retry loop now avoids last-attempt sleep.
207
+ - `aload_asins_from_keepa` now applies split-and-continue fallback when a batch fails.
208
+ - single-ASIN hard failures are skipped with warning logs instead of aborting the full load.
209
+ - Added tests in `tests/test_api/test_batch_loader.py`:
210
+ - split failed large batch into smaller chunks and recover.
211
+ - skip irrecoverable single-ASIN failures without raising.
212
+ - Updated `README.md` with the `1.0.3` behavior notes.
213
+ - Validation: `pytest -q` passed (`36 passed, 1 skipped`).
214
+
215
+ ## 2026-03-13 15:55 CST
216
+ State: Synced future-timestamp inference fix from `0.0.19` commit `e062e5d` into branch `1.0.4`.
217
+
218
+ AndrewChan:
219
+ - Requested to sync only `./keble_keepa` code changes from commit `e062e5d9352b5e4afbfa7cf04e103e9ca4de3f85` into branch `1.0.4`.
220
+ - Then requested syncing related `md` files, `tests/` code, and `AiAgentChangesLogs.md`.
221
+
222
+ Ai:
223
+ - Synced `keble_keepa/schemas/objects/product.py` logic to ignore future `listedSince` and non-future-only guessing from Keepa time series.
224
+ - Synced regression tests in `tests/test_objects/test_product_guessed_has_offer_since_datetime.py` for:
225
+ - future `listedSince` fallback,
226
+ - all-future series returns `None`.
227
+ - Updated `README.md` with a new `Version 1.0.4 update` section aligned to the synced behavior.
228
+ - Added this `AiAgentChangesLogs.md` entry to document the branch-level synchronization work.
229
+ ## 2026-05-13 22:09 CST
230
+ State: Preparing current-line Keepa release-date fallback fix on `fix/keepa-release-date-fallback`.
231
+
232
+ zma:
233
+ - Investigate ASINs whose release dates cannot be detected in AMZ reports.
234
+ - Use Keepa API data when needed and fix the latest Keepa package line without regressing structured image handling.
235
+
236
+ Ai:
237
+ - Added `Product.tracking_since_datetime` as a typed fallback for products where Keepa reports `listedSince=0` but has a valid `trackingSince`.
238
+ - Updated `Product.guessed_has_offer_since_datetime` to use valid `trackingSince` before historical series scanning.
239
+ - Added schema coverage for the missing-listing-date case and bumped package metadata from `1.1.4` to `1.1.5`.
240
+ ## 2026-05-25 12:12
241
+ State: branch `fix/task-cost-accounting-audit-hardening`; package `1.1.7`.
242
+
243
+ zma:
244
+ - Wire Keepa provider usage accounting at the provider boundary and use tenacity for retry ownership.
245
+
246
+ Ai:
247
+ - Added optional usage-recorder forwarding to async product finder and product request calls; cache hits do not emit usage.
248
+ - Replaced Keepa batch retry loops with tenacity while preserving retry diagnostics.
249
+ - Added tests that enforce async GET/POST usage context signatures and counted Keepa usage events.
250
+
251
+ ## 2026-05-26 09:46 CST
252
+ State: branch `fix/task-cost-accounting-runtime-hardening`; package bumped to `1.1.8`.
253
+
254
+ zma:
255
+ - Harden Keepa retry ownership after audit found tenacity predicates retrying every `Exception`.
256
+ - Keep provider-boundary usage accounting and avoid duplicate higher-level Keepa cost rows.
257
+
258
+ Ai:
259
+ - Narrowed batch-loader tenacity retry predicates to HTTP transport and timeout exceptions.
260
+ - Added a regression test proving programming errors are not retried, refreshed helper dependency metadata, and rebuilt the package wheel.
261
+
262
+ ## 2026-06-13 20:05 CST
263
+ State: branch `fix/keepa-finder-perpage`; package bumped to `1.1.13`.
264
+
265
+ zma:
266
+ - Continue Round-4.4 and fix the live `general_research` crash caused by Keepa Product Finder rejecting small `perPage` values.
267
+ - Keep the fix at the source package boundary with typed validation.
268
+
269
+ Ai:
270
+ - Added typed Product Finder page bounds and `ProductFinderQuery.resolve_valid_finder_page(...)`.
271
+ - Updated the batch loader to clamp requested `per_page` into Keepa-valid bounds and slice returned ASINs locally.
272
+ - Added tests for small page-size requests and impossible-window typed rejection.
@@ -0,0 +1,15 @@
1
+ # Keble Keepa Claude Instructions
2
+
3
+ Follow the root `TESTING_GUIDELINE.md` for all Keepa test work.
4
+
5
+ Use `keble_keepa.testing` (`FakeKeepaGateway`, factories, cassette filters, and
6
+ `requires_keepa_live`) instead of ad-hoc raw Keepa mocks. Never hit the real
7
+ Keepa API from default tests. Put each test in the lowest useful layer and keep
8
+ the standard marker vocabulary aligned with the other Keble repos.
9
+
10
+ Required checks after test-infrastructure changes:
11
+
12
+ ```bash
13
+ uv run pytest -m "not live and not slow and not eval and not local_stack"
14
+ npx --yes pyright .
15
+ ```