alpha-engine-lib 0.40.0__tar.gz → 0.40.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. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/PKG-INFO +1 -1
  2. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/pyproject.toml +1 -1
  3. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/__init__.py +1 -1
  4. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/cost.py +31 -6
  5. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib.egg-info/PKG-INFO +1 -1
  6. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_cost.py +41 -0
  7. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/README.md +0 -0
  8. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/setup.cfg +0 -0
  9. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/agent_schemas.py +0 -0
  10. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/alerts.py +0 -0
  11. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/anthropic_payload.py +0 -0
  12. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/arcticdb.py +0 -0
  13. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/artifact_freshness.py +0 -0
  14. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/collector_results.py +0 -0
  15. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/dates.py +0 -0
  16. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/decision_capture.py +0 -0
  17. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/ec2_spot.py +0 -0
  18. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/email_sender.py +0 -0
  19. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/eval_artifacts.py +0 -0
  20. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/locks.py +0 -0
  21. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/logging.py +0 -0
  22. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/model_pricing.yaml +0 -0
  23. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/pillars.py +0 -0
  24. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/pipeline_status/__init__.py +0 -0
  25. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/pipeline_status/read.py +0 -0
  26. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/pipeline_status/registry.py +0 -0
  27. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/pipeline_status/templates.py +0 -0
  28. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/preflight.py +0 -0
  29. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/rag/__init__.py +0 -0
  30. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/rag/db.py +0 -0
  31. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/rag/embeddings.py +0 -0
  32. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/rag/migrations/0001_content_tsv.sql +0 -0
  33. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/rag/rerank.py +0 -0
  34. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/rag/retrieval.py +0 -0
  35. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/rag/schema.sql +0 -0
  36. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/reconcile.py +0 -0
  37. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/secrets.py +0 -0
  38. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/sources/__init__.py +0 -0
  39. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/sources/protocols.py +0 -0
  40. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/ssm_dispatcher.py +0 -0
  41. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/ssm_log_capture.py +0 -0
  42. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/telegram.py +0 -0
  43. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/trading_calendar.py +0 -0
  44. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/transparency.py +0 -0
  45. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/transparency_inventory.yaml +0 -0
  46. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib/universe.py +0 -0
  47. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib.egg-info/SOURCES.txt +0 -0
  48. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib.egg-info/dependency_links.txt +0 -0
  49. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib.egg-info/requires.txt +0 -0
  50. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/src/alpha_engine_lib.egg-info/top_level.txt +0 -0
  51. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_agent_schemas.py +0 -0
  52. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_alerts.py +0 -0
  53. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_anthropic_payload.py +0 -0
  54. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_arcticdb.py +0 -0
  55. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_artifact_freshness.py +0 -0
  56. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_collector_results.py +0 -0
  57. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_dates.py +0 -0
  58. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_decision_capture.py +0 -0
  59. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_ec2_spot.py +0 -0
  60. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_email_sender.py +0 -0
  61. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_eval_artifacts.py +0 -0
  62. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_locks.py +0 -0
  63. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_logging.py +0 -0
  64. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_pillars.py +0 -0
  65. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_pipeline_status_read.py +0 -0
  66. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_pipeline_status_registry.py +0 -0
  67. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_pipeline_status_templates.py +0 -0
  68. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_preflight.py +0 -0
  69. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_rag.py +0 -0
  70. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_rag_rerank.py +0 -0
  71. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_rag_retrieval_hybrid.py +0 -0
  72. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_reconcile.py +0 -0
  73. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_secrets.py +0 -0
  74. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_sources_protocols.py +0 -0
  75. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_ssm_dispatcher.py +0 -0
  76. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_ssm_log_capture.py +0 -0
  77. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_telegram.py +0 -0
  78. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_trading_calendar.py +0 -0
  79. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_transparency.py +0 -0
  80. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_universe.py +0 -0
  81. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_version_bump_workflow.py +0 -0
  82. {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.40.1}/tests/test_version_pin.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alpha-engine-lib
3
- Version: 0.40.0
3
+ Version: 0.40.1
4
4
  Summary: Shared utilities for the Alpha Engine modules: preflight, logging, ArcticDB, dates, decision capture, cost telemetry, Anthropic payload chokepoint, artifact freshness, RAG, agent schemas, SSM secrets, Telegram + SNS alerts, EC2 spot resilience, SSM log-capture, SSM dispatcher, Step-Functions execution-state projection, and S3-conditional-PUT writer locks. Full surface documented in README.
5
5
  Author: Brian McMahon
6
6
  License: Proprietary
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "alpha-engine-lib"
7
- version = "0.40.0"
7
+ version = "0.40.1"
8
8
  description = "Shared utilities for the Alpha Engine modules: preflight, logging, ArcticDB, dates, decision capture, cost telemetry, Anthropic payload chokepoint, artifact freshness, RAG, agent schemas, SSM secrets, Telegram + SNS alerts, EC2 spot resilience, SSM log-capture, SSM dispatcher, Step-Functions execution-state projection, and S3-conditional-PUT writer locks. Full surface documented in README."
9
9
  readme = "README.md"
10
10
  # EC2 still runs Python 3.9 on the always-on micro instance (boto3 drops
@@ -1,3 +1,3 @@
1
1
  """alpha-engine-lib — shared utilities for Alpha Engine modules."""
2
2
 
3
- __version__ = "0.40.0"
3
+ __version__ = "0.40.1"
@@ -58,6 +58,7 @@ Workstream design: ``alpha-engine-config/private-docs/ROADMAP.md`` line ~1708
58
58
 
59
59
  from __future__ import annotations
60
60
 
61
+ import re
61
62
  from datetime import date, datetime, timezone
62
63
  from importlib import resources
63
64
  from pathlib import Path
@@ -68,6 +69,17 @@ from pydantic import BaseModel, ConfigDict, Field, model_validator
68
69
 
69
70
  from alpha_engine_lib.decision_capture import ModelMetadata
70
71
 
72
+ # Anthropic SDK model IDs come in two forms: the family alias
73
+ # (e.g. ``claude-haiku-4-5``) and the dated snapshot form
74
+ # (e.g. ``claude-haiku-4-5-20251001``). ``Message.model`` returns the dated
75
+ # form even when the caller requested the alias, but our pricing YAML is
76
+ # keyed on the alias so a new snapshot date doesn't require a card refresh.
77
+ _DATED_SNAPSHOT_SUFFIX_RE = re.compile(r"-\d{8}$")
78
+
79
+
80
+ def _strip_dated_snapshot_suffix(model_name: str) -> str:
81
+ return _DATED_SNAPSHOT_SUFFIX_RE.sub("", model_name)
82
+
71
83
  if TYPE_CHECKING:
72
84
  # Structural Protocol below describes the only attributes we touch on
73
85
  # an Anthropic SDK ``Message`` — kept here so that ``anthropic`` does
@@ -172,14 +184,27 @@ class PriceTable(BaseModel):
172
184
  component is used for lookup) or a ``date``. The returned card is
173
185
  the one whose ``effective_from`` is the latest among cards ≤ ``at``.
174
186
 
175
- Raises :exc:`PriceCardLookupError` if the model has no cards or
176
- every card's ``effective_from`` is later than ``at``.
187
+ Lookup tries the model name as-given first; on miss, retries with
188
+ any trailing ``-YYYYMMDD`` snapshot suffix stripped. This lets the
189
+ YAML stay keyed on family aliases (``claude-haiku-4-5``) while
190
+ accepting the dated form (``claude-haiku-4-5-20251001``) that the
191
+ Anthropic SDK returns in ``Message.model``.
192
+
193
+ Raises :exc:`PriceCardLookupError` if neither form matches.
177
194
  """
178
195
  query_date = at.date() if isinstance(at, datetime) else at
179
- candidates = [
180
- c for c in self.cards
181
- if c.model_name == model_name and c.effective_from <= query_date
182
- ]
196
+
197
+ def _candidates_for(name: str) -> list[PriceCard]:
198
+ return [
199
+ c for c in self.cards
200
+ if c.model_name == name and c.effective_from <= query_date
201
+ ]
202
+
203
+ candidates = _candidates_for(model_name)
204
+ if not candidates:
205
+ alias = _strip_dated_snapshot_suffix(model_name)
206
+ if alias != model_name:
207
+ candidates = _candidates_for(alias)
183
208
  if not candidates:
184
209
  raise PriceCardLookupError(
185
210
  f"No price card for model {model_name!r} active on {query_date}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alpha-engine-lib
3
- Version: 0.40.0
3
+ Version: 0.40.1
4
4
  Summary: Shared utilities for the Alpha Engine modules: preflight, logging, ArcticDB, dates, decision capture, cost telemetry, Anthropic payload chokepoint, artifact freshness, RAG, agent schemas, SSM secrets, Telegram + SNS alerts, EC2 spot resilience, SSM log-capture, SSM dispatcher, Step-Functions execution-state projection, and S3-conditional-PUT writer locks. Full surface documented in README.
5
5
  Author: Brian McMahon
6
6
  License: Proprietary
@@ -155,6 +155,47 @@ class TestPriceTableLookup:
155
155
  self.table.get("haiku", date(2025, 12, 31))
156
156
 
157
157
 
158
+ class TestPriceTableLookupDatedSnapshotSuffix:
159
+ """Anthropic SDK returns ``Message.model`` in the dated snapshot form
160
+ (e.g. ``claude-haiku-4-5-20251001``) even when the caller requested
161
+ the alias; the YAML is keyed on the alias. Lookup must accept both.
162
+ """
163
+
164
+ def setup_method(self):
165
+ self.table = PriceTable(cards=[
166
+ _card("claude-haiku-4-5", 2026, 1, 1, in_p=1.0),
167
+ _card("claude-sonnet-4-6", 2026, 1, 1, in_p=3.0),
168
+ ])
169
+
170
+ def test_dated_suffix_falls_back_to_alias(self):
171
+ c = self.table.get("claude-haiku-4-5-20251001", date(2026, 5, 28))
172
+ assert c.input_per_1m == 1.0
173
+
174
+ def test_alias_lookup_unchanged(self):
175
+ c = self.table.get("claude-haiku-4-5", date(2026, 5, 28))
176
+ assert c.input_per_1m == 1.0
177
+
178
+ def test_exact_dated_match_wins_over_alias_fallback(self):
179
+ # If someone adds a dated card explicitly, it takes precedence.
180
+ table = PriceTable(cards=[
181
+ _card("claude-haiku-4-5", 2026, 1, 1, in_p=1.0),
182
+ _card("claude-haiku-4-5-20251001", 2026, 1, 1, in_p=9.99),
183
+ ])
184
+ c = table.get("claude-haiku-4-5-20251001", date(2026, 5, 28))
185
+ assert c.input_per_1m == 9.99
186
+
187
+ def test_unknown_alias_with_dated_suffix_still_hard_fails(self):
188
+ with pytest.raises(
189
+ PriceCardLookupError, match="claude-foo-9-9-20251001"
190
+ ):
191
+ self.table.get("claude-foo-9-9-20251001", date(2026, 5, 28))
192
+
193
+ def test_non_dated_suffix_is_not_stripped(self):
194
+ # Bare 8-digit substring without leading dash → no normalization.
195
+ with pytest.raises(PriceCardLookupError):
196
+ self.table.get("claude-haiku-4-5.20251001", date(2026, 5, 28))
197
+
198
+
158
199
  # ── compute_cost ──────────────────────────────────────────────────────────
159
200
 
160
201