alpha-engine-lib 0.40.1__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.
Files changed (82) hide show
  1. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/PKG-INFO +1 -1
  2. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/pyproject.toml +1 -1
  3. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/__init__.py +1 -1
  4. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/anthropic_payload.py +92 -0
  5. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib.egg-info/PKG-INFO +1 -1
  6. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_anthropic_payload.py +117 -0
  7. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/README.md +0 -0
  8. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/setup.cfg +0 -0
  9. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/agent_schemas.py +0 -0
  10. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/alerts.py +0 -0
  11. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/arcticdb.py +0 -0
  12. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/artifact_freshness.py +0 -0
  13. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/collector_results.py +0 -0
  14. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/cost.py +0 -0
  15. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/dates.py +0 -0
  16. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/decision_capture.py +0 -0
  17. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/ec2_spot.py +0 -0
  18. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/email_sender.py +0 -0
  19. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/eval_artifacts.py +0 -0
  20. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/locks.py +0 -0
  21. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/logging.py +0 -0
  22. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/model_pricing.yaml +0 -0
  23. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/pillars.py +0 -0
  24. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/pipeline_status/__init__.py +0 -0
  25. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/pipeline_status/read.py +0 -0
  26. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/pipeline_status/registry.py +0 -0
  27. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/pipeline_status/templates.py +0 -0
  28. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/preflight.py +0 -0
  29. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/rag/__init__.py +0 -0
  30. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/rag/db.py +0 -0
  31. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/rag/embeddings.py +0 -0
  32. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/rag/migrations/0001_content_tsv.sql +0 -0
  33. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/rag/rerank.py +0 -0
  34. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/rag/retrieval.py +0 -0
  35. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/rag/schema.sql +0 -0
  36. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/reconcile.py +0 -0
  37. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/secrets.py +0 -0
  38. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/sources/__init__.py +0 -0
  39. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/sources/protocols.py +0 -0
  40. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/ssm_dispatcher.py +0 -0
  41. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/ssm_log_capture.py +0 -0
  42. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/telegram.py +0 -0
  43. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/trading_calendar.py +0 -0
  44. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/transparency.py +0 -0
  45. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/transparency_inventory.yaml +0 -0
  46. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib/universe.py +0 -0
  47. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib.egg-info/SOURCES.txt +0 -0
  48. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib.egg-info/dependency_links.txt +0 -0
  49. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib.egg-info/requires.txt +0 -0
  50. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/src/alpha_engine_lib.egg-info/top_level.txt +0 -0
  51. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_agent_schemas.py +0 -0
  52. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_alerts.py +0 -0
  53. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_arcticdb.py +0 -0
  54. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_artifact_freshness.py +0 -0
  55. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_collector_results.py +0 -0
  56. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_cost.py +0 -0
  57. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_dates.py +0 -0
  58. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_decision_capture.py +0 -0
  59. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_ec2_spot.py +0 -0
  60. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_email_sender.py +0 -0
  61. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_eval_artifacts.py +0 -0
  62. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_locks.py +0 -0
  63. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_logging.py +0 -0
  64. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_pillars.py +0 -0
  65. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_pipeline_status_read.py +0 -0
  66. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_pipeline_status_registry.py +0 -0
  67. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_pipeline_status_templates.py +0 -0
  68. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_preflight.py +0 -0
  69. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_rag.py +0 -0
  70. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_rag_rerank.py +0 -0
  71. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_rag_retrieval_hybrid.py +0 -0
  72. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_reconcile.py +0 -0
  73. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_secrets.py +0 -0
  74. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_sources_protocols.py +0 -0
  75. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_ssm_dispatcher.py +0 -0
  76. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_ssm_log_capture.py +0 -0
  77. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_telegram.py +0 -0
  78. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_trading_calendar.py +0 -0
  79. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_transparency.py +0 -0
  80. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_universe.py +0 -0
  81. {alpha_engine_lib-0.40.1 → alpha_engine_lib-0.41.0}/tests/test_version_bump_workflow.py +0 -0
  82. {alpha_engine_lib-0.40.1 → 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.40.1
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.40.1"
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
@@ -1,3 +1,3 @@
1
1
  """alpha-engine-lib — shared utilities for Alpha Engine modules."""
2
2
 
3
- __version__ = "0.40.1"
3
+ __version__ = "0.41.0"
@@ -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}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alpha-engine-lib
3
- Version: 0.40.1
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"}