forked from molecule-ai/molecule-core
Merge pull request #2714 from Molecule-AI/feat/anthropic-direct-e2e-path
e2e: add direct-Anthropic LLM-key path alongside MiniMax + OpenAI
This commit is contained in:
commit
d360c34a30
21
.github/workflows/canary-staging.yml
vendored
21
.github/workflows/canary-staging.yml
vendored
@ -63,6 +63,11 @@ jobs:
|
|||||||
# full_saas.sh branches SECRETS_JSON on which key is present —
|
# full_saas.sh branches SECRETS_JSON on which key is present —
|
||||||
# MiniMax wins when set.
|
# MiniMax wins when set.
|
||||||
E2E_MINIMAX_API_KEY: ${{ secrets.MOLECULE_STAGING_MINIMAX_API_KEY }}
|
E2E_MINIMAX_API_KEY: ${{ secrets.MOLECULE_STAGING_MINIMAX_API_KEY }}
|
||||||
|
# Direct-Anthropic alternative for operators who don't want to
|
||||||
|
# set up a MiniMax account (priority below MiniMax — first
|
||||||
|
# non-empty wins in test_staging_full_saas.sh's secrets-injection
|
||||||
|
# block). See #2578 PR comment for the rationale.
|
||||||
|
E2E_ANTHROPIC_API_KEY: ${{ secrets.MOLECULE_STAGING_ANTHROPIC_API_KEY }}
|
||||||
# OpenAI fallback — kept wired so an operator-dispatched run with
|
# OpenAI fallback — kept wired so an operator-dispatched run with
|
||||||
# E2E_RUNTIME=hermes overridden via workflow_dispatch can still
|
# E2E_RUNTIME=hermes overridden via workflow_dispatch can still
|
||||||
# exercise the OpenAI path without re-editing the workflow.
|
# exercise the OpenAI path without re-editing the workflow.
|
||||||
@ -97,8 +102,20 @@ jobs:
|
|||||||
# missing" message at the top.
|
# missing" message at the top.
|
||||||
case "${E2E_RUNTIME}" in
|
case "${E2E_RUNTIME}" in
|
||||||
claude-code)
|
claude-code)
|
||||||
required_secret_name="MOLECULE_STAGING_MINIMAX_API_KEY"
|
# Either MiniMax OR direct-Anthropic works — first
|
||||||
required_secret_value="${E2E_MINIMAX_API_KEY:-}"
|
# non-empty wins in the test script's secrets-injection
|
||||||
|
# priority chain. Operators only need to set ONE of these
|
||||||
|
# secrets; we don't force a choice between them.
|
||||||
|
if [ -n "${E2E_MINIMAX_API_KEY:-}" ]; then
|
||||||
|
required_secret_name="MOLECULE_STAGING_MINIMAX_API_KEY"
|
||||||
|
required_secret_value="${E2E_MINIMAX_API_KEY}"
|
||||||
|
elif [ -n "${E2E_ANTHROPIC_API_KEY:-}" ]; then
|
||||||
|
required_secret_name="MOLECULE_STAGING_ANTHROPIC_API_KEY"
|
||||||
|
required_secret_value="${E2E_ANTHROPIC_API_KEY}"
|
||||||
|
else
|
||||||
|
required_secret_name="MOLECULE_STAGING_MINIMAX_API_KEY or MOLECULE_STAGING_ANTHROPIC_API_KEY"
|
||||||
|
required_secret_value=""
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
langgraph|hermes)
|
langgraph|hermes)
|
||||||
required_secret_name="MOLECULE_STAGING_OPENAI_KEY"
|
required_secret_name="MOLECULE_STAGING_OPENAI_KEY"
|
||||||
|
|||||||
23
.github/workflows/continuous-synth-e2e.yml
vendored
23
.github/workflows/continuous-synth-e2e.yml
vendored
@ -119,6 +119,11 @@ jobs:
|
|||||||
# tests/e2e/test_staging_full_saas.sh branches SECRETS_JSON on
|
# tests/e2e/test_staging_full_saas.sh branches SECRETS_JSON on
|
||||||
# which key is present — MiniMax wins when set.
|
# which key is present — MiniMax wins when set.
|
||||||
E2E_MINIMAX_API_KEY: ${{ secrets.MOLECULE_STAGING_MINIMAX_API_KEY }}
|
E2E_MINIMAX_API_KEY: ${{ secrets.MOLECULE_STAGING_MINIMAX_API_KEY }}
|
||||||
|
# Direct-Anthropic alternative for operators who don't want to
|
||||||
|
# set up a MiniMax account (priority below MiniMax — first
|
||||||
|
# non-empty wins in test_staging_full_saas.sh's secrets-injection
|
||||||
|
# block). See #2578 PR comment for the rationale.
|
||||||
|
E2E_ANTHROPIC_API_KEY: ${{ secrets.MOLECULE_STAGING_ANTHROPIC_API_KEY }}
|
||||||
# OpenAI fallback — kept wired so operators can dispatch with
|
# OpenAI fallback — kept wired so operators can dispatch with
|
||||||
# E2E_RUNTIME=langgraph or =hermes and still have a working
|
# E2E_RUNTIME=langgraph or =hermes and still have a working
|
||||||
# canary path. The script picks the right blob shape based on
|
# canary path. The script picks the right blob shape based on
|
||||||
@ -149,13 +154,21 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# LLM-key requirement is per-runtime: claude-code uses MiniMax
|
# LLM-key requirement is per-runtime: claude-code accepts
|
||||||
# (MOLECULE_STAGING_MINIMAX_API_KEY), langgraph + hermes use
|
# EITHER MiniMax OR direct-Anthropic (whichever is set first),
|
||||||
# OpenAI (MOLECULE_STAGING_OPENAI_KEY).
|
# langgraph + hermes use OpenAI (MOLECULE_STAGING_OPENAI_KEY).
|
||||||
case "${E2E_RUNTIME}" in
|
case "${E2E_RUNTIME}" in
|
||||||
claude-code)
|
claude-code)
|
||||||
required_secret_name="MOLECULE_STAGING_MINIMAX_API_KEY"
|
if [ -n "${E2E_MINIMAX_API_KEY:-}" ]; then
|
||||||
required_secret_value="${E2E_MINIMAX_API_KEY:-}"
|
required_secret_name="MOLECULE_STAGING_MINIMAX_API_KEY"
|
||||||
|
required_secret_value="${E2E_MINIMAX_API_KEY}"
|
||||||
|
elif [ -n "${E2E_ANTHROPIC_API_KEY:-}" ]; then
|
||||||
|
required_secret_name="MOLECULE_STAGING_ANTHROPIC_API_KEY"
|
||||||
|
required_secret_value="${E2E_ANTHROPIC_API_KEY}"
|
||||||
|
else
|
||||||
|
required_secret_name="MOLECULE_STAGING_MINIMAX_API_KEY or MOLECULE_STAGING_ANTHROPIC_API_KEY"
|
||||||
|
required_secret_value=""
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
langgraph|hermes)
|
langgraph|hermes)
|
||||||
required_secret_name="MOLECULE_STAGING_OPENAI_KEY"
|
required_secret_name="MOLECULE_STAGING_OPENAI_KEY"
|
||||||
|
|||||||
20
.github/workflows/e2e-staging-saas.yml
vendored
20
.github/workflows/e2e-staging-saas.yml
vendored
@ -93,6 +93,11 @@ jobs:
|
|||||||
# OpenAI quota collapse no longer wedges the gate. Mirrors the
|
# OpenAI quota collapse no longer wedges the gate. Mirrors the
|
||||||
# canary-staging.yml + continuous-synth-e2e.yml migrations.
|
# canary-staging.yml + continuous-synth-e2e.yml migrations.
|
||||||
E2E_MINIMAX_API_KEY: ${{ secrets.MOLECULE_STAGING_MINIMAX_API_KEY }}
|
E2E_MINIMAX_API_KEY: ${{ secrets.MOLECULE_STAGING_MINIMAX_API_KEY }}
|
||||||
|
# Direct-Anthropic alternative for operators who don't want to
|
||||||
|
# set up a MiniMax account (priority below MiniMax — first
|
||||||
|
# non-empty wins in test_staging_full_saas.sh's secrets-injection
|
||||||
|
# block). See #2578 PR comment for the rationale.
|
||||||
|
E2E_ANTHROPIC_API_KEY: ${{ secrets.MOLECULE_STAGING_ANTHROPIC_API_KEY }}
|
||||||
# OpenAI fallback — kept wired so an operator-dispatched run with
|
# OpenAI fallback — kept wired so an operator-dispatched run with
|
||||||
# E2E_RUNTIME=hermes or =langgraph via workflow_dispatch can still
|
# E2E_RUNTIME=hermes or =langgraph via workflow_dispatch can still
|
||||||
# exercise the OpenAI path.
|
# exercise the OpenAI path.
|
||||||
@ -128,8 +133,19 @@ jobs:
|
|||||||
# clean "secret missing" message at the top.
|
# clean "secret missing" message at the top.
|
||||||
case "${E2E_RUNTIME}" in
|
case "${E2E_RUNTIME}" in
|
||||||
claude-code)
|
claude-code)
|
||||||
required_secret_name="MOLECULE_STAGING_MINIMAX_API_KEY"
|
# Either MiniMax OR direct-Anthropic works — first
|
||||||
required_secret_value="${E2E_MINIMAX_API_KEY:-}"
|
# non-empty wins in the test script's secrets-injection
|
||||||
|
# priority chain.
|
||||||
|
if [ -n "${E2E_MINIMAX_API_KEY:-}" ]; then
|
||||||
|
required_secret_name="MOLECULE_STAGING_MINIMAX_API_KEY"
|
||||||
|
required_secret_value="${E2E_MINIMAX_API_KEY}"
|
||||||
|
elif [ -n "${E2E_ANTHROPIC_API_KEY:-}" ]; then
|
||||||
|
required_secret_name="MOLECULE_STAGING_ANTHROPIC_API_KEY"
|
||||||
|
required_secret_value="${E2E_ANTHROPIC_API_KEY}"
|
||||||
|
else
|
||||||
|
required_secret_name="MOLECULE_STAGING_MINIMAX_API_KEY or MOLECULE_STAGING_ANTHROPIC_API_KEY"
|
||||||
|
required_secret_value=""
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
langgraph|hermes)
|
langgraph|hermes)
|
||||||
required_secret_name="MOLECULE_STAGING_OPENAI_KEY"
|
required_secret_name="MOLECULE_STAGING_OPENAI_KEY"
|
||||||
|
|||||||
@ -321,8 +321,9 @@ tenant_call() {
|
|||||||
|
|
||||||
# ─── 5. Provision parent workspace ─────────────────────────────────────
|
# ─── 5. Provision parent workspace ─────────────────────────────────────
|
||||||
# Inject the LLM provider key so the runtime can authenticate at boot.
|
# Inject the LLM provider key so the runtime can authenticate at boot.
|
||||||
# Branch by which secret is set so the script supports both paths
|
# Branch by which secret is set so the script supports multiple paths
|
||||||
# without forcing every dispatch to ship both keys:
|
# without forcing every dispatch to ship them all. Priority order
|
||||||
|
# matters — first non-empty wins:
|
||||||
#
|
#
|
||||||
# E2E_MINIMAX_API_KEY → claude-code MiniMax path. Cheapest, default
|
# E2E_MINIMAX_API_KEY → claude-code MiniMax path. Cheapest, default
|
||||||
# for the cron canary post-2026-05-03. Routes via the claude-code
|
# for the cron canary post-2026-05-03. Routes via the claude-code
|
||||||
@ -334,6 +335,15 @@ tenant_call() {
|
|||||||
# collisions when a user runs MiniMax + Z.ai workspaces side-by-
|
# collisions when a user runs MiniMax + Z.ai workspaces side-by-
|
||||||
# side).
|
# side).
|
||||||
#
|
#
|
||||||
|
# E2E_ANTHROPIC_API_KEY → claude-code direct-Anthropic path (added
|
||||||
|
# 2026-05-04 after #2578 left the operator with an awkward choice
|
||||||
|
# between paying OpenAI's billing top-up and registering a new
|
||||||
|
# MiniMax account). Lower friction than MiniMax for operators
|
||||||
|
# who already have an Anthropic API key for their own Claude
|
||||||
|
# Code session. Pricier per-token than MiniMax but billing is
|
||||||
|
# still independent of MOLECULE_STAGING_OPENAI_KEY. Pinned to the
|
||||||
|
# claude-code runtime — hermes/langgraph use OpenAI-shaped envs.
|
||||||
|
#
|
||||||
# E2E_OPENAI_API_KEY → langgraph + hermes paths. Kept as fallback
|
# E2E_OPENAI_API_KEY → langgraph + hermes paths. Kept as fallback
|
||||||
# for operator dispatches that explicitly want to exercise the
|
# for operator dispatches that explicitly want to exercise the
|
||||||
# OpenAI path. The HERMES_* fields pin hermes-agent's bridge to
|
# OpenAI path. The HERMES_* fields pin hermes-agent's bridge to
|
||||||
@ -341,7 +351,7 @@ tenant_call() {
|
|||||||
# resolves openai/* → openrouter.ai and 401s). MODEL_PROVIDER
|
# resolves openai/* → openrouter.ai and 401s). MODEL_PROVIDER
|
||||||
# follows workspace/config.py:258's 'provider:model' format.
|
# follows workspace/config.py:258's 'provider:model' format.
|
||||||
#
|
#
|
||||||
# Both empty → '{}' (workspace will fail at first turn with an
|
# All empty → '{}' (workspace will fail at first turn with an
|
||||||
# expected, actionable auth error rather than masking the test).
|
# expected, actionable auth error rather than masking the test).
|
||||||
SECRETS_JSON='{}'
|
SECRETS_JSON='{}'
|
||||||
if [ -n "${E2E_MINIMAX_API_KEY:-}" ]; then
|
if [ -n "${E2E_MINIMAX_API_KEY:-}" ]; then
|
||||||
@ -352,6 +362,25 @@ print(json.dumps({
|
|||||||
'MINIMAX_API_KEY': k,
|
'MINIMAX_API_KEY': k,
|
||||||
}))
|
}))
|
||||||
")
|
")
|
||||||
|
elif [ -n "${E2E_ANTHROPIC_API_KEY:-}" ]; then
|
||||||
|
# Direct Anthropic path — claude-code adapter reads ANTHROPIC_API_KEY
|
||||||
|
# natively when ANTHROPIC_BASE_URL is unset. Useful for operators
|
||||||
|
# who already have an Anthropic API key (e.g. for their own Claude
|
||||||
|
# Code session) and want to avoid setting up a separate MiniMax
|
||||||
|
# account just for E2E. Pricier per-token than MiniMax but billing
|
||||||
|
# is still independent of MOLECULE_STAGING_OPENAI_KEY, so an OpenAI
|
||||||
|
# quota collapse doesn't wedge this path. Pinned to the claude-code
|
||||||
|
# runtime: hermes/langgraph use OpenAI-shaped envs and won't honour
|
||||||
|
# ANTHROPIC_API_KEY without further wiring (out of scope for this
|
||||||
|
# branch; if you need a hermes/Anthropic path, dispatch with
|
||||||
|
# E2E_RUNTIME=hermes + E2E_OPENAI_API_KEY pointing at a working key).
|
||||||
|
SECRETS_JSON=$(python3 -c "
|
||||||
|
import json, os
|
||||||
|
k = os.environ['E2E_ANTHROPIC_API_KEY']
|
||||||
|
print(json.dumps({
|
||||||
|
'ANTHROPIC_API_KEY': k,
|
||||||
|
}))
|
||||||
|
")
|
||||||
elif [ -n "${E2E_OPENAI_API_KEY:-}" ]; then
|
elif [ -n "${E2E_OPENAI_API_KEY:-}" ]; then
|
||||||
SECRETS_JSON=$(python3 -c "
|
SECRETS_JSON=$(python3 -c "
|
||||||
import json, os
|
import json, os
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user