alpha-engine-lib 0.40.0__tar.gz → 0.41.0__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.
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/PKG-INFO +1 -1
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/pyproject.toml +1 -1
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/__init__.py +1 -1
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/anthropic_payload.py +92 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/cost.py +31 -6
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib.egg-info/PKG-INFO +1 -1
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_anthropic_payload.py +117 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_cost.py +41 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/README.md +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/setup.cfg +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/agent_schemas.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/alerts.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/arcticdb.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/artifact_freshness.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/collector_results.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/dates.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/decision_capture.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/ec2_spot.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/email_sender.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/eval_artifacts.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/locks.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/logging.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/model_pricing.yaml +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/pillars.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/pipeline_status/__init__.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/pipeline_status/read.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/pipeline_status/registry.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/pipeline_status/templates.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/preflight.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/rag/__init__.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/rag/db.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/rag/embeddings.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/rag/migrations/0001_content_tsv.sql +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/rag/rerank.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/rag/retrieval.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/rag/schema.sql +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/reconcile.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/secrets.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/sources/__init__.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/sources/protocols.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/ssm_dispatcher.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/ssm_log_capture.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/telegram.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/trading_calendar.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/transparency.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/transparency_inventory.yaml +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/universe.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib.egg-info/SOURCES.txt +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib.egg-info/dependency_links.txt +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib.egg-info/requires.txt +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib.egg-info/top_level.txt +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_agent_schemas.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_alerts.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_arcticdb.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_artifact_freshness.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_collector_results.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_dates.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_decision_capture.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_ec2_spot.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_email_sender.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_eval_artifacts.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_locks.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_logging.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_pillars.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_pipeline_status_read.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_pipeline_status_registry.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_pipeline_status_templates.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_preflight.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_rag.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_rag_rerank.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_rag_retrieval_hybrid.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_reconcile.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_secrets.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_sources_protocols.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_ssm_dispatcher.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_ssm_log_capture.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_telegram.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_trading_calendar.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_transparency.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_universe.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/tests/test_version_bump_workflow.py +0 -0
- {alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/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.
|
|
3
|
+
Version: 0.41.0
|
|
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.
|
|
7
|
+
version = "0.41.0"
|
|
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
|
{alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/anthropic_payload.py
RENAMED
|
@@ -215,3 +215,95 @@ def build_messages_payload(
|
|
|
215
215
|
|
|
216
216
|
validate_payload(payload)
|
|
217
217
|
return payload
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def build_batches_request_params(
|
|
221
|
+
*,
|
|
222
|
+
custom_id: str,
|
|
223
|
+
model: str,
|
|
224
|
+
max_tokens: int,
|
|
225
|
+
user_content: str,
|
|
226
|
+
tools: list[dict] | None = None,
|
|
227
|
+
tool_choice: dict[str, Any] | None = None,
|
|
228
|
+
system_prompt: str | None = None,
|
|
229
|
+
cache_system: bool = False,
|
|
230
|
+
extra: dict[str, Any] | None = None,
|
|
231
|
+
) -> dict[str, Any]:
|
|
232
|
+
"""Construct one entry of the ``messages.batches.create`` ``requests`` array.
|
|
233
|
+
|
|
234
|
+
The Anthropic Batches API takes a list of ``{"custom_id", "params"}``
|
|
235
|
+
dicts, where each ``params`` value is a kwargs dict for an underlying
|
|
236
|
+
``messages.create()`` call. This helper builds one such entry,
|
|
237
|
+
validating the embedded payload via :func:`validate_payload`.
|
|
238
|
+
|
|
239
|
+
Differs from :func:`build_messages_payload` along three axes the
|
|
240
|
+
judge-batch path requires:
|
|
241
|
+
|
|
242
|
+
1. **Optional system prompt.** Synchronous callers nearly always have
|
|
243
|
+
a static system prompt (the lib default caches it); judge batches
|
|
244
|
+
inject the entire rubric into the user message and have no system
|
|
245
|
+
block. Pass ``system_prompt=None`` (the default) to emit no
|
|
246
|
+
system block at all.
|
|
247
|
+
2. **No cache_control by default.** The Batches API discounts every
|
|
248
|
+
call 50% before prompt caching applies; the marginal value of
|
|
249
|
+
caching is small enough that the existing judge path opts out.
|
|
250
|
+
``cache_system=False`` is the default for this reason; pass
|
|
251
|
+
``cache_system=True`` explicitly if the system prompt is large
|
|
252
|
+
enough to benefit.
|
|
253
|
+
3. **Explicit tool_choice.** Forced tool calls (
|
|
254
|
+
``{"type": "tool", "name": ...}``) are the dominant Batches use
|
|
255
|
+
case (structured-output via a known schema). Pass ``tool_choice``
|
|
256
|
+
directly rather than smuggling through ``extra``.
|
|
257
|
+
|
|
258
|
+
All :func:`validate_payload` invariants run against the embedded
|
|
259
|
+
``params`` — including the server-tool ⊥ assistant-prefill check —
|
|
260
|
+
so a future Batches caller that mixes ``web_search`` with a
|
|
261
|
+
prefill won't reach Anthropic's HTTP 400.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
custom_id: Per-request identifier returned in the batch result.
|
|
265
|
+
Caller-owned; must be unique within a batch.
|
|
266
|
+
model: Anthropic model identifier (e.g. ``"claude-haiku-4-5"``).
|
|
267
|
+
max_tokens: ``max_tokens`` for the embedded call.
|
|
268
|
+
user_content: The user-message content (typically the full
|
|
269
|
+
rendered rubric / prompt body, since batch calls usually
|
|
270
|
+
omit the system block).
|
|
271
|
+
tools: Optional list of tool specs.
|
|
272
|
+
tool_choice: Optional tool-choice spec (e.g.
|
|
273
|
+
``{"type": "tool", "name": "RubricEvalLLMOutput"}`` to force
|
|
274
|
+
structured output via a specific tool).
|
|
275
|
+
system_prompt: Optional system-prompt text. When ``None`` (the
|
|
276
|
+
default), no ``system`` block is emitted.
|
|
277
|
+
cache_system: When ``True``, attach ``cache_control: ephemeral``
|
|
278
|
+
to the system block. Default ``False`` because Batches
|
|
279
|
+
already discounts 50% and the marginal cache value is small.
|
|
280
|
+
Ignored when ``system_prompt is None``.
|
|
281
|
+
extra: Optional dict merged into ``params`` after construction
|
|
282
|
+
(e.g. ``metadata``, ``stop_sequences``). Validation runs
|
|
283
|
+
AFTER the merge.
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
``{"custom_id": custom_id, "params": <validated kwargs dict>}``,
|
|
287
|
+
ready to splat into ``messages.batches.create(requests=[...])``.
|
|
288
|
+
|
|
289
|
+
Raises :exc:`PayloadInvariantError` on a known-incompatible shape.
|
|
290
|
+
"""
|
|
291
|
+
params: dict[str, Any] = {
|
|
292
|
+
"model": model,
|
|
293
|
+
"max_tokens": max_tokens,
|
|
294
|
+
"messages": [{"role": "user", "content": user_content}],
|
|
295
|
+
}
|
|
296
|
+
if system_prompt is not None:
|
|
297
|
+
system_block: dict[str, Any] = {"type": "text", "text": system_prompt}
|
|
298
|
+
if cache_system:
|
|
299
|
+
system_block["cache_control"] = {"type": "ephemeral"}
|
|
300
|
+
params["system"] = [system_block]
|
|
301
|
+
if tools:
|
|
302
|
+
params["tools"] = list(tools)
|
|
303
|
+
if tool_choice is not None:
|
|
304
|
+
params["tool_choice"] = tool_choice
|
|
305
|
+
if extra:
|
|
306
|
+
params.update(extra)
|
|
307
|
+
|
|
308
|
+
validate_payload(params)
|
|
309
|
+
return {"custom_id": custom_id, "params": params}
|
|
@@ -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
|
-
|
|
176
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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.
|
|
3
|
+
Version: 0.41.0
|
|
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
|
|
@@ -30,6 +30,7 @@ from alpha_engine_lib.anthropic_payload import (
|
|
|
30
30
|
DEFAULT_WEB_SEARCH_MAX_USES,
|
|
31
31
|
SERVER_TOOL_PREFIXES,
|
|
32
32
|
PayloadInvariantError,
|
|
33
|
+
build_batches_request_params,
|
|
33
34
|
build_messages_payload,
|
|
34
35
|
build_web_search_tool,
|
|
35
36
|
validate_payload,
|
|
@@ -271,3 +272,119 @@ def test_build_messages_payload_morning_signal_replication():
|
|
|
271
272
|
assert payload["tools"][0]["max_uses"] == 20
|
|
272
273
|
assert len(payload["messages"]) == 1 # no assistant prefill
|
|
273
274
|
assert opener in payload["messages"][0]["content"]
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
# ── build_batches_request_params ─────────────────────────────────────────────
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
_FORCE_TOOL_CHOICE = {"type": "tool", "name": "RubricEvalLLMOutput"}
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _custom_tool_spec():
|
|
284
|
+
"""A non-server-side tool — what the judge batch uses for structured output."""
|
|
285
|
+
return {
|
|
286
|
+
"name": "RubricEvalLLMOutput",
|
|
287
|
+
"description": "Emit the rubric eval payload as structured JSON.",
|
|
288
|
+
"input_schema": {
|
|
289
|
+
"type": "object",
|
|
290
|
+
"properties": {"score": {"type": "integer"}},
|
|
291
|
+
"required": ["score"],
|
|
292
|
+
},
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def test_build_batches_request_params_judge_shape():
|
|
297
|
+
"""Replicates the alpha-engine-research judge call shape: no system
|
|
298
|
+
prompt, custom tool, forced tool_choice, no caching. Locks the
|
|
299
|
+
minimal viable Batches request envelope the judge actually ships."""
|
|
300
|
+
req = build_batches_request_params(
|
|
301
|
+
custom_id="judge-abc-123",
|
|
302
|
+
model="claude-haiku-4-5",
|
|
303
|
+
max_tokens=2048,
|
|
304
|
+
user_content="Rubric prompt body here…",
|
|
305
|
+
tools=[_custom_tool_spec()],
|
|
306
|
+
tool_choice=_FORCE_TOOL_CHOICE,
|
|
307
|
+
)
|
|
308
|
+
assert req["custom_id"] == "judge-abc-123"
|
|
309
|
+
params = req["params"]
|
|
310
|
+
assert params["model"] == "claude-haiku-4-5"
|
|
311
|
+
assert params["max_tokens"] == 2048
|
|
312
|
+
assert params["messages"] == [{"role": "user", "content": "Rubric prompt body here…"}]
|
|
313
|
+
assert params["tools"] == [_custom_tool_spec()]
|
|
314
|
+
assert params["tool_choice"] == _FORCE_TOOL_CHOICE
|
|
315
|
+
# No system prompt by default — judge inlines rubric into user content.
|
|
316
|
+
assert "system" not in params
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def test_build_batches_request_params_with_system_prompt_no_cache_default():
|
|
320
|
+
"""When a system prompt IS provided, it lands as a one-element system
|
|
321
|
+
array. Caching is OFF by default for batches per the docstring rationale."""
|
|
322
|
+
req = build_batches_request_params(
|
|
323
|
+
custom_id="x",
|
|
324
|
+
model="claude-sonnet-4-6",
|
|
325
|
+
max_tokens=256,
|
|
326
|
+
user_content="u",
|
|
327
|
+
system_prompt="You are a helpful assistant.",
|
|
328
|
+
)
|
|
329
|
+
sys_blocks = req["params"]["system"]
|
|
330
|
+
assert sys_blocks == [{"type": "text", "text": "You are a helpful assistant."}]
|
|
331
|
+
assert "cache_control" not in sys_blocks[0]
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def test_build_batches_request_params_with_system_prompt_cache_opt_in():
|
|
335
|
+
"""``cache_system=True`` attaches ephemeral cache_control (the
|
|
336
|
+
opt-in path for batches with large repeated system prompts)."""
|
|
337
|
+
req = build_batches_request_params(
|
|
338
|
+
custom_id="x",
|
|
339
|
+
model="claude-sonnet-4-6",
|
|
340
|
+
max_tokens=256,
|
|
341
|
+
user_content="u",
|
|
342
|
+
system_prompt="Large repeated system prompt.",
|
|
343
|
+
cache_system=True,
|
|
344
|
+
)
|
|
345
|
+
assert req["params"]["system"][0]["cache_control"] == {"type": "ephemeral"}
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def test_build_batches_request_params_validates_server_tool_prefill_invariant():
|
|
349
|
+
"""The Batches builder honors the same server-tool ⊥ assistant-prefill
|
|
350
|
+
invariant as the sync builder — caught via ``extra`` smuggling."""
|
|
351
|
+
with pytest.raises(PayloadInvariantError):
|
|
352
|
+
build_batches_request_params(
|
|
353
|
+
custom_id="x",
|
|
354
|
+
model="claude-sonnet-4-6",
|
|
355
|
+
max_tokens=256,
|
|
356
|
+
user_content="u",
|
|
357
|
+
tools=[build_web_search_tool()],
|
|
358
|
+
extra={
|
|
359
|
+
"messages": [
|
|
360
|
+
{"role": "user", "content": "hi"},
|
|
361
|
+
{"role": "assistant", "content": "Y"},
|
|
362
|
+
]
|
|
363
|
+
},
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def test_build_batches_request_params_no_system_no_tools_minimal():
|
|
368
|
+
"""Minimal shape: only model + max_tokens + messages. Pins that
|
|
369
|
+
optional fields don't leak ``None`` keys into the payload."""
|
|
370
|
+
req = build_batches_request_params(
|
|
371
|
+
custom_id="x",
|
|
372
|
+
model="claude-haiku-4-5",
|
|
373
|
+
max_tokens=64,
|
|
374
|
+
user_content="ping",
|
|
375
|
+
)
|
|
376
|
+
params = req["params"]
|
|
377
|
+
assert set(params.keys()) == {"model", "max_tokens", "messages"}
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def test_build_batches_request_params_extra_merges_into_params():
|
|
381
|
+
"""``extra`` keys merge into ``params`` (e.g. metadata for batch-side
|
|
382
|
+
observability). Validation still runs."""
|
|
383
|
+
req = build_batches_request_params(
|
|
384
|
+
custom_id="x",
|
|
385
|
+
model="claude-haiku-4-5",
|
|
386
|
+
max_tokens=64,
|
|
387
|
+
user_content="u",
|
|
388
|
+
extra={"metadata": {"user_id": "judge-v3"}},
|
|
389
|
+
)
|
|
390
|
+
assert req["params"]["metadata"] == {"user_id": "judge-v3"}
|
|
@@ -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
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/artifact_freshness.py
RENAMED
|
File without changes
|
{alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/collector_results.py
RENAMED
|
File without changes
|
|
File without changes
|
{alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/decision_capture.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/pipeline_status/__init__.py
RENAMED
|
File without changes
|
{alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/pipeline_status/read.py
RENAMED
|
File without changes
|
{alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/pipeline_status/registry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/sources/__init__.py
RENAMED
|
File without changes
|
{alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/sources/protocols.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/trading_calendar.py
RENAMED
|
File without changes
|
|
File without changes
|
{alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/transparency_inventory.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib.egg-info/requires.txt
RENAMED
|
File without changes
|
{alpha_engine_lib-0.40.0 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|