Skip to content

revert: AuthenticationContext ambient primitive (#259)#260

Merged
Kamilbenkirane merged 1 commit intomainfrom
revert/authentication-context
Apr 15, 2026
Merged

revert: AuthenticationContext ambient primitive (#259)#260
Kamilbenkirane merged 1 commit intomainfrom
revert/authentication-context

Conversation

@Kamilbenkirane
Copy link
Copy Markdown
Member

Summary

Reverts #259 — the AuthenticationContext / authentication_scope / resolve_authentication / MissingAuthenticationError primitive — per YAGNI plus a design error surfaced during downstream integration.

Why

1. Per-Provider keying collapses real-world divergent auth

The shipped shape was AuthenticationContext.entries: Mapping[Provider, Authentication] — one authentication per provider, bound for the whole async scope. This assumed a single Authentication is sufficient per provider.

Multi-modal integration analysis showed that's not always true: a single provider can legitimately expose different authentication mechanisms for different (modality, operation) pairs. For example, a provider may use API-key auth for one operation and OAuth (bearer-token) auth for another — they're not the same Authentication class, not the same secret, and not interchangeable. A per-Provider scope can only hold one of them; the other is silently lost.

Moving to per-(modality, operation) keying would have been possible but leads to the next point.

2. The primitive isn't needed — create_client() already handles this

Primitive-tier peers (openai-python, anthropic-sdk-python, google-genai) have no ambient authentication state — not in ContextVar, not in threading.local, not anywhere. Their multi-tenant story is per-request client construction: OpenAI(api_key=...), Anthropic(api_key=...), genai.Client(api_key=...). Each request builds its own client; the api_key binds at the client level; subsequent calls on that client reuse it.

celeste-python already supports the identical pattern via create_client(), which returns a ModalityClient with model, auth, provider, protocol, and base_url bound at construction:

from celeste import create_client, Modality, Operation, AuthHeader

client = create_client(
    modality=Modality.IMAGES,
    operation=Operation.GENERATE,
    model="some-image-model",
    auth=AuthHeader(secret="sk-..."),
)
response = await client.generate("prompt")  # reuses bound auth + model

This is the primitive-tier "bind once, call many" pattern. It handles divergent per-(modality, operation) auth naturally — callers simply construct one client per slot, each with its own auth=.

Ambient ContextVar-backed authentication is a framework-tier convention (Flask g, structlog bind_contextvars, DSPy dspy.context, LangChain RunnableConfig). celeste-python positions itself as a primitive, and none of its primitive peers do this. AuthenticationContext imported a framework-tier idea into a layer whose peers deliberately reject it.

3. Zero downstream adoption

No consumer has actually wired authentication_scope(...) around any run. The primitive shipped but was never exercised in a downstream integration, so reverting has no external-breakage surface.

What this changes for users

Nothing. All pre-#259 APIs remain intact:

  • celeste.text.generate(prompt, model="...", auth=...) — per-call auth (unchanged)
  • celeste.text.generate(prompt, model="...", api_key="...") — per-call api_key (unchanged)
  • create_client(modality=..., operation=..., model=..., auth=...) — bound-auth client reuse (unchanged, and remains the recommended path)
  • Authentication / AuthHeader / NoAuth / APIKey / register_auth / get_auth_class — all kept
  • Env-based credential fallback via credentials.get_auth(...) — unchanged

What's removed (all introduced by #259, no other usage):

  • AuthenticationContext class
  • authentication_scope(...) context manager
  • resolve_authentication(provider) lookup
  • MissingAuthenticationError exception
  • Ambient lookup line in create_client() (~2 lines)
  • test_authentication_context.py test coverage folded back into test_auth.py

Test plan

  • uv sync --all-extras
  • uv run ruff check src tests — clean
  • uv run ruff format --check src tests — clean (394 files)
  • uv run mypy src/celeste — clean (331 files, 0 issues)
  • uv run pytest tests/unit_tests -q586 passed (0 failures)
  • Pre-commit hooks (format, lint, mypy src+tests, bandit, pytest-with-coverage) — all passed on push

This reverts commit ff60c19.

Reverting per YAGNI + design error found in downstream analysis.

The shipped AuthenticationContext was keyed per-Provider, assuming a
single Authentication is bound to a provider. Downstream analysis in a
multi-modal, multi-provider context revealed this collapses cases where
a single provider legitimately exposes DIFFERENT auth for different
(modality, operation) pairs — e.g. OAuth for one operation and API-key
for another. A per-Provider AuthenticationContext cannot represent both
in the same scope and silently drops one.

Independent of that correctness gap, the primitive was not actually
needed: primitive peer SDKs (openai-python, anthropic-sdk-python,
google-genai) have no ambient auth state and handle multi-tenancy via
per-request client construction. celeste-python's own create_client()
already supports this natively — it returns a ModalityClient with the
auth bound at construction, which is the primitive-tier "bind once,
call many" pattern. An ambient ContextVar-backed primitive is a
framework-tier idea grafted onto the primitive layer.

No downstream consumer actually adopted the primitive, so the revert
has zero external breakage. create_client(auth=...) remains the
supported path for reusable, auth-bound clients.
@Kamilbenkirane Kamilbenkirane merged commit bdacc15 into main Apr 15, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant