Problem / Motivation
Part of #3572138: [META] Native Anthropic SDK integration for advanced API features. Phase 1 (#3572402: Add effort, adaptive thinking, and native token usage support) delivered the native SDK
integration with effort, thinking, and native token usage; Phase 2
(#3590963: Add prompt caching UI and PDF input support) added prompt caching and PDF input. This issue is
Phase 3 of the plan: the remaining three Anthropic-native
capabilities unused by the provider — token counting (pre-dispatch
cost/limit visibility), citations (source attribution for
document-grounded answers), and context management (server-side
compaction and context editing for long conversations).
Proposed resolution
- Token counting — public
AnthropicProvider::countTokens()backed by
$client->messages->countTokens(). No new
OperationType: drupal/ai 1.4 has no consumer for one; documented
provider-native method instead. - Citations — opt-in
CitationsConfigParamon PDF document blocks;
response citations (all 5 location variants) normalized by a new
CitationNormalizerinto
ChatOutput::getMetadata()['citations'], on both the
sync and streaming paths. - Context management — routes through
$client->beta->messages->create()with the
live-verified beta headers (compact-2026-01-12/
context-management-2025-06-27). Hybrid posture for
the beta-to-GA transition: admin-overridable header, and a
one-shot retry without context management when the API rejects
the header. Capability-gated per model via the typed
ModelCapabilities->contextManagementflags. - Admin form gains three opt-in sections following the existing
prompt-caching pattern; all features default off; config schema
updated with constraints.
Verification
101 unit tests / 208 assertions green (44 new tests). phpcs
(Drupal + DrupalPractice) clean, drupal-rector clean, composer
valid. Beta header slugs live-verified against
platform.claude.com docs on 2026-07-01. Multi-stage adversarial review (29 findings raised across two review passes, 22 vote-confirmed, all fixed) — details in the MR description. drupalci pipeline green (phpunit, phpstan, phpcs, cspell, composer). All three features additionally live-verified end to end against the Anthropic API; see comment #3.
Remaining tasks
- ✅ File the issue
- ✅ Implementation (MR !35)
- ✅ Automated unit testing coverage (44 new tests; 101 total / 208 assertions)
- ✅ drupalci pipeline green
- ✅ Live end-to-end verification against the Anthropic API
- ➖ Community / maintainer review
- ➖ Merge + release (1.3.0-beta3)
AI-Generated: Yes
Per the Drupal AI contribution policy. Built via multi-agent AI orchestration (Claude Opus 4.8 workers,
Claude Fable 5 orchestration) against a human-approved architecture:
research, implementation, and tests AI-generated; reviewed through
two adversarial review passes (code audit, 3-persona paper test,
fresh-context review, refutation voting) plus human maintainer
direction throughout. Dependencies, logic, security, and GPL
compatibility verified. Full contributor responsibility assumed.
Issue fork ai_provider_anthropic-3607960
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
camoa commentedMR !35 is up against 1.3.x and the full drupalci pipeline is green (phpunit, phpstan, phpcs, cspell, composer): https://git.drupalcode.org/project/ai_provider_anthropic/-/merge_requests/35
Beyond the unit suite (101 tests / 208 assertions, 44 new), all three features were live-verified end to end against the Anthropic API:
metadata['citations']with the correctpage_locationspan andcited_textquoting the source sentence.BetaMessageresponse), thecompact-2026-01-12header was accepted live, and the response carried thecontextManagementacknowledgment.#stateslevels behave, save round-trip persists all five new config keys.Review pointer: the beta-to-GA posture on compaction is the part most worth scrutiny — the header is admin-overridable and a rejected beta header triggers exactly one retry without context management (rationale and evidence in the MR description).
Setting to Needs review.
Comment #4
camoa commentedComment #5
codeitwisely commentedReviewed from the
ai_metering
perspective. Here is what the usage tracker sees for each feature:
countTokens()Citations and context management both route through the shared
buildChatOutput()path, soTokenUsageDtois populated correctly in both cases. ai_metering picks them up with no changes needed. Note: context management only routes to the beta endpoint on models that support compaction (thecompact_20260112capability flag returned by the Anthropic capabilities API). haiku-4-5 does not; sonnet-4-6 does.countTokens()is the one gap. It calls the SDK directly viaAnthropicNativeClient::countTokens()without firing any drupal/ai event, so ai_metering never records the request. Quota enforcement, rate limiting, and cost attribution all break silently for sites running pre-flight token checks before agent tasks.The fix belongs in drupal/ai core: a
PostCountTokensEventfired after a successfulcountTokens()call, carrying provider ID, model ID, and input token count. ai_metering would subscribe to it and record the usage. Any provider implementingcountTokens()benefits automatically.The MR is mergeable from the ai_metering side. The
countTokens()tracking gap is a follow-up concern, not a blocker here.Comment #6
camoa commentedThank you for testing this from the metering side. The gating confirmation is the part I appreciate most (haiku-4-5 staying on the GA path while sonnet-4-6 routing to beta is exactly what the capability flags should be doing, and now a second module has verified it).
You are right about the gap, and since the fix belongs in core I did some digging to hand you before you file it. Three findings that change the shape a bit:
1. An operation type gets you more than a new event, for less. ProviderProxy auto-detects any interface extending OperationTypeInterface whose method name matches the interface name, and fires the existing ai.pre_generate_response / ai.post_generate_response / exception events for it with operationType as the discriminator. Core has zero per-operation events today (no PostChatEvent, no PostEmbeddingsEvent), so a CountTokensInterface plus a small CountTokensOutput rides the whole pipeline for free: ai_metering filters on getOperationType() === 'count_tokens' using the subscription it already has, and the count inherits requestThreadId correlation and typed exception mapping.
2. The proposal needs to differentiate from the existing ai.tokenizer service. Core already ships countTokens() there, but it is local tiktoken estimation with OpenAI encodings. What is missing is the provider-native exact count (Anthropic's server tokenizer, which is also correct for PDFs, images and tool schemas). Getting ahead of "we already have countTokens" will save the issue a review round.
3. It survives the Symfony AI migration. Symfony AI Platform only carries post-response TokenUsage metadata, there is no pre-flight counting anywhere in it, so this is additive and the 2.0.x branch is still taking new operation types (ExtractiveQuestionAnswering landed there in June). Targeting 2.0.x with a 1.4.x backport offer seems like the right shape. Issue #3541284 is the friendly post-response counterpart worth aligning DTO naming with.
If you file it, link it here and I will implement the Anthropic side (the interface plus CountTokensOutput replacing the current int-returning method) as a follow-up on this project, and happily co-review the core MR.
Comment #7
codeitwisely commentedFiled the follow-up on drupal/ai core: https://git.drupalcode.org/project/ai/-/work_items/3586582