Skip to content

feat(agentic-ai): add native Anthropic provider for the v2 AI Agent connectors - #8069

Draft
maff wants to merge 35 commits into
agentic-ai/v2-config-typesfrom
agentic-ai/native-anthropic-provider
Draft

feat(agentic-ai): add native Anthropic provider for the v2 AI Agent connectors#8069
maff wants to merge 35 commits into
agentic-ai/v2-config-typesfrom
agentic-ai/native-anthropic-provider

Conversation

@maff

@maff maff commented Jul 24, 2026

Copy link
Copy Markdown
Member

Description

Adds a native (non-LangChain4J) Anthropic provider for the v2 AI Agent connectors, implementing
AnthropicChatModelConfiguration as a new ProviderConfiguration member and driving the Anthropic
Java SDK's Messages client directly through the ChatModel/ChatModelFactory provider SPI
introduced in #8043.

  • anthropic-api (direct, API key) and compatible (arbitrary Anthropic-compatible
    endpoint; API key or no auth; custom headers/query params/body parameters) backends.
  • Full request/response converter chain: content blocks (text, tool use/result, documents,
    multimodal user input), reasoning/extended thinking (enabled/adaptive/disabled, byte-identical
    round-trip of thinking/redacted-thinking blocks), effort, prompt caching (automatic, top-level
    cache_control), structured JSON output, and provider server-tool content preserved losslessly.
    Streams every call (createStreaming) and accumulates to the same Message shape a non-streaming
    call would produce.
  • Provider-neutral HTTP proxy support (HttpTransportSupport), shared with future OkHttp-based
    providers.
  • Normalizes Anthropic's stop reasons to the domain StopReason, treats pause_turn as a
    ChatResult.Continuation, and enforces the existing content-filter guard.
  • Element templates regenerated for both the v2 AI Agent Task and Sub-process connectors (including
    hybrid variants).

Related issues

closes #8055

Checklist

  • Tests/Integration tests for the changes have been added.
  • Backport labels — none added; this targets the unreleased v2 connectors.
  • Documentation — ai-agent.md updated for the provider SPI/native-provider sections touched by
    this change.

@maff
maff requested a review from a team as a code owner July 24, 2026 12:40
@maff maff added the e2e-tests label Jul 24, 2026
@maff
maff requested a review from Copilot July 24, 2026 12:42
@maff
maff marked this pull request as draft July 24, 2026 12:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a native Anthropic Messages provider to the v2 AI Agent connectors.

Changes:

  • Adds direct and compatible Anthropic backends, converters, streaming, reasoning, caching, and proxy support.
  • Registers the provider and regenerates standard/hybrid templates.
  • Adds unit, integration, wire-format, and real-provider tests.

Reviewed changes

Copilot reviewed 51 out of 51 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
AgentTestFixtures.java Adds v2 template fixtures.
AgentSubProcessAnthropicPromptCachingTests.java Tests prompt caching.
AgentSubProcessAnthropicReasoningEffortTests.java Tests reasoning and effort.
BaseAnthropicNativeSubProcessTest.java Adds native Anthropic E2E plumbing.
ProviderWireFormatSmokeTests.java Adds v2 Anthropic fixture coverage.
AbstractAnthropicMessagesWireFormatFixture.java Shares Anthropic fixture behavior.
AnthropicMessagesV1WireFormatFixture.java Retains v1 fixture configuration.
AnthropicMessagesV2WireFormatFixture.java Adds native v2 fixture configuration.
StreamingAnthropicMessagesSseChatModelStubs.java Adds Anthropic SSE stubs.
ProviderWireFormatFixture.java Supports fixture-specific templates.
AgentResponseAssert.java Corrects assertion helper naming.
AgentSubProcessResponseAssert.java Adds metrics and provider-content assertions.
RealProviderApiSmokeIT.java Adds real Anthropic acceptance scenarios.
native-provider-acceptance-agent.bpmn Adds native-provider acceptance process.
agenticai-ai-agent-subprocess.v2.json Exposes Anthropic subprocess settings.
agenticai-ai-agent-task.v2.json Exposes Anthropic task settings.
agenticai-ai-agent-subprocess.v2-hybrid.json Updates hybrid subprocess template.
agenticai-ai-agent-task.v2-hybrid.json Updates hybrid task template.
pom.xml Adds Anthropic SDK and OkHttp dependencies.
AgentTaskV2Function.java Adds the model-options property group.
AnthropicChatModelApi.java Executes streaming Anthropic calls.
AnthropicChatModelApiFactory.java Builds direct and compatible clients.
AnthropicContentConverter.java Converts domain content blocks.
AnthropicEffort.java Defines effort levels.
AnthropicMessageRequestConverter.java Builds Messages API requests.
AnthropicMessageResponseConverter.java Converts Anthropic responses.
AnthropicMessageStreamAssembler.java Accumulates streamed events.
AnthropicReasoningValidator.java Validates thinking configuration.
ThinkingMode.java Defines thinking modes.
AgenticAiAnthropicProviderConfiguration.java Registers the native factory.
anthropic/package-info.java Enables null-marked Anthropic code.
AnthropicChatModelConfiguration.java Defines v2 Anthropic configuration.
ProviderConfiguration.java Adds Anthropic to the provider union.
CompatibleAuthentication.java Defines compatible-backend authentication.
v2/shared/package-info.java Enables null-marked shared models.
HttpTransportSupport.java Adds provider-neutral proxy resolution.
transport/package-info.java Enables null-marked transport code.
JsonPayloadLogging.java Adds safe JSON logging.
AgenticAiConnectorsAutoConfiguration.java Imports Anthropic and transport beans.
AiAgentV2TemplateGenerationTest.java Verifies generated Anthropic fields.
AnthropicChatModelApiFactoryClientTest.java Tests client and proxy wiring.
AnthropicChatModelApiFactoryTest.java Tests factory selection and creation.
AnthropicChatModelApiTest.java Tests execution and lifecycle behavior.
AnthropicContentConverterTest.java Tests content conversion.
AnthropicMessageRequestConverterTest.java Tests request mapping.
AnthropicMessageResponseConverterTest.java Tests response mapping.
AnthropicMessageStreamAssemblerTest.java Tests stream accumulation and workaround.
AnthropicChatModelConfigurationTest.java Tests binding and validation.
HttpTransportSupportTest.java Tests proxy resolution.
AgenticAiConnectorsAutoConfigurationTest.java Tests factory registration.
ai-agent.md Documents the native provider implementation.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 51 out of 51 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

connectors/agentic-ai/connector-agentic-ai/src/main/java/io/camunda/connector/agenticai/aiagent/model/request/v2/AnthropicChatModelConfiguration.java:273

  • The maxTokens == null early return skips the cross-field check even though AnthropicMessageRequestConverter substitutes DEFAULT_MAX_TOKENS = 4096. An enabled-thinking configuration with a budget of 4096 or more therefore passes validation but is sent with max_tokens: 4096, which Anthropic rejects because the budget must be smaller. Validate against the effective default too, ideally by sharing the default between the model validation and request converter.

@maff
maff force-pushed the agentic-ai/v2-config-types branch from 486d0dd to cdd7ce7 Compare July 27, 2026 15:58
@maff
maff force-pushed the agentic-ai/native-anthropic-provider branch from e33d5ac to d9df976 Compare July 27, 2026 16:56
@maff
maff force-pushed the agentic-ai/v2-config-types branch 4 times, most recently from 0a8c50e to c3df2ca Compare July 28, 2026 20:13
maff added 14 commits July 28, 2026 22:14
…transport

HttpTransportSupport duplicated the pre-existing, LangChain4j-scoped
ChatModelHttpProxySupport (JDK/AWS/Azure proxy builders) even though the
native Anthropic OkHttp client factory only ever calls okHttpProxy().
Trim it down to that single method, built directly on ProxyConfiguration,
so it stays a small framework-neutral OkHttp-only proxy helper instead of
a second copy of logic that already lives elsewhere.

Also add real behavioral coverage for the previously-untested proxy
application branch in AnthropicOkHttpClientFactory (both the plain and
credentialed paths, via a real HttpTransportSupport and a minimal
in-process fake HTTP proxy), and document why compatible-backend
requestParameters() is intentionally not applied at client-construction
time (no client-level hook in the vendor SDK; belongs to the request
converter instead).
… template group

maxTokens/temperature/topP/topK belong in "model-options" alongside other
provider model-parameter groups, not the general "model" group.
…d of beta

The native Anthropic provider (content/request/response converters,
stream assembler, and chat model API) now targets
com.anthropic.models.messages.* and client.messages() instead of the
beta Messages client. Skills and server-tool support, the reason the
beta client was originally used, are out of scope for this PR, and the
stable client fully covers everything this provider ships (thinking,
effort, caching, tool calling, multimodal content, and pause_turn
handling all carry over unchanged).
Ports the native (v2, own-LLM-layer) Anthropic Messages provider's
WireMock-based e2e coverage into the target module, adapted to its
already-renamed subprocess/task package split and migrated from the
beta to the stable Anthropic SDK surface:

- Extend ProviderWireFormatFixture with elementTemplatePath/
  elementTemplateBaselineProperties default hooks so a fixture can
  redirect the shared smoke-test suite to a different element template.
- Factor AnthropicMessagesWireFormatFixture's shared wire-format
  plumbing into AbstractAnthropicMessagesWireFormatFixture so both the
  v1 (langchain4j-bridge) and new v2 (native) fixtures can reuse it.
- Add StreamingAnthropicMessagesSseChatModelStubs, stubbing the native
  provider's streamed SSE response shape using the stable
  com.anthropic.models.messages.* SDK types (mirroring the main-source
  converters' beta->stable migration), trimmed to the turns this PR
  ships (text/tool-call, thinking, redacted thinking) and dropping
  Skills/code-execution server-tool-use coverage.
- Add StreamingAnthropicMessagesWireFormatFixture and wire it into
  ProviderWireFormatSmokeTests as a fifth fixture row, plus the new
  v2 element-template constants in AgentTestFixtures.
- Port the reasoning/effort wire-mapping and round-trip tests
  (AgentSubProcessAnthropicReasoningEffortTests), dropping the
  capability-matrix-driven validation-failure test this worktree has
  no subsystem for.
- Add a new AgentSubProcessAnthropicPromptCachingTests asserting the
  enablePromptCaching template property adds/omits the wire-level
  cache_control field.
… explanation

Reword without naming ModelCapabilitiesResolver/AnthropicReasoningCapabilities,
which don't exist on this branch.
maff added 21 commits July 28, 2026 22:14
…tension example

Point §12/§18/§25.1 at AnthropicChatModelApiFactory/AnthropicChatModelConfiguration
as the reference implementation for a fully native (non-LangChain4J) ChatModel
provider, alongside the existing LangChain4J-bridge example.
…el options group

Drop the single-implementation AnthropicClientFactory interface and fold its
sole implementation's construction responsibility directly into
AnthropicOkHttpClientFactory. Trim planning-session references (task numbers,
capability-matrix history, LangChain4J asides) out of Anthropic provider
Javadoc, keeping only what's true of the current code. Clarify which document
formats the Anthropic Messages API accepts natively versus falling back to a
JSON reference.

Move the "Model options" property group to sit right after "Model" in the v2
element templates, with a title-cased label matching the other groups'
sentence-case convention, and regenerate the templates.
…trim stub docs

Rename AnthropicMessagesWireFormatFixture/StreamingAnthropicMessagesWireFormatFixture
to AnthropicMessagesV1WireFormatFixture/AnthropicMessagesV2WireFormatFixture and their
apiName() strings to match, since the two fixtures already correspond to the v1/v2
element templates and this makes that explicit instead of naming one "streaming".
Drop the "langchain4j-bridge" wording (no such bridge class exists) from the shared
abstract fixture's Javadoc.

In the SSE stub class, remove the out-of-scope-server-tools sentence and the
class-level coupling to the native provider class name (nothing about the stubs is
tied to a specific client), and replace a fabricated tracksToolInput() method
reference with the actual, verifiable reason a redacted-thinking block has no delta
accumulation: the SDK defines no delta type for it.
…y assertions

Extract the duplicated element-template/backend-wiring plumbing shared by
AgentSubProcessAnthropicReasoningEffortTests and AgentSubProcessAnthropicPromptCachingTests
into a new BaseAnthropicNativeSubProcessTest.

Replace the single-purpose hasReasoningTokensGreaterThanZero/hasCacheCreationTokensGreaterThanZero/
hasCacheReadTokensGreaterThanZero assertions with a generic metricsSatisfy(consumer) plus a
hasReasoningTokens() convenience built on it, and inline the cache-token checks at their one
call site. Fix the hasResponseTestSatisfying typo (-> hasResponseTextSatisfying) on both
response-assertion classes.

Clean up RealProviderApiSmokeIT: drop the unused LLM-judge SpringBootTest properties (never
invoked anywhere in this class), remove FQCN usages in favor of imports, reference the system
prompt as a FEEL variable instead of an escaped string literal, drop the unused
follow-up-user-prompt branch and the empty-documents default, hoist scattered constants to the
top of the class, and rewrite comments that described deferred/future work or assumed
knowledge of the porting process to instead describe the current behavior.
…roviderApiSmokeIT

RealProviderApiSmokeIT referenced this classpath resource but it was never added
to this branch (it only existed on the vertical-pilot branch it was ported from).
…icChatModelApiFactory

Drop the AnthropicOkHttpClientFactory class and move its buildClient logic
directly into AnthropicChatModelApiFactory as a package-private static method,
since it had exactly one caller and its own construction wasn't a meaningful
seam. The dedicated wire-level test moves with it (renamed to
AnthropicChatModelApiFactoryClientTest), still calling the method directly.

Stop sending a placeholder x-api-key for compatible backends configured with
no authentication: verified empirically that the SDK client builder neither
requires an api key to build nor to send a request without one, so the
placeholder was unnecessary; the header is now simply omitted.

Clarify why compatible-backend headers/queryParameters are applied at the
client level while requestParameters are applied per-request in the message
converter.
…e Anthropic converter

Match the LangChain4J-routed provider's behavior (DocumentToContentConverterImpl,
which throws DocumentConversionException): the native Anthropic converter now
throws a ConnectorException for a document content type that maps to none of
Anthropic's native blocks (image, PDF, text-family), instead of silently
degrading to a JSON reference of the document. Also clarifies that
base64Source(String) already sets both source.type and source.media_type for
PDF document blocks, matching Anthropic's documented wire shape.
…r request, not per client

Move header and query-parameter application for the compatible backend from
client construction (AnthropicChatModelApiFactory) to request building
(AnthropicMessageRequestConverter), alongside the existing request (body)
parameter merge. The client is short-lived (built fresh per job worker
execution, never reused across calls), so there's no benefit to setting them
at construction time, and MessageCreateParams.Builder already exposes
putAdditionalHeader/putAdditionalQueryParam/putAdditionalBodyProperty
uniformly -- no need to split the three across two places.
…e wording

Move enablePromptCaching out of AnthropicConnection into a new nested
AnthropicModelParameters.AnthropicPromptCaching record (a single enabled
flag for now), since prompt caching is a per-model-call concern like
temperature/effort/thinking, not a connection-level setting, and a record
keeps room for a future cache-type or TTL knob without changing the wire
shape. The element-template property moves from
provider.anthropic.enablePromptCaching to
provider.anthropic.model.parameters.promptCaching.enabled; its tooltip is
trimmed to state what it does rather than repeating TTL/wire-format detail
already covered by the linked docs.

Elevate effort and thinking to the first two fields of
AnthropicModelParameters, ahead of the raw numeric knobs.

Rename the backend discriminator's UI label from "Connection" to "Backend"
to match the AnthropicBackend type name, and merge the endpoint property's
separate tooltip about the appended /v1/messages suffix into its
description.

Rename the AnthropicChatModelApiFactory bean method to
aiAgentAnthropicChatModelApiFactory, and note in
AgenticAiAnthropicProviderConfiguration's Javadoc that it mirrors
AgenticAiLangChain4JChatModelConfiguration's dedicated-and-imported pattern
for the LangChain4J-routed providers.

Drop a comment explaining Java's lack of import-aliasing syntax from
AnthropicMessageResponseConverter, keeping only the actual reason for the
fully-qualified reference (the SDK's own StopReason name clash).
docs/superpowers/specs/2026-07-23-native-anthropic-task0-findings.md was a
Task 0 scratch note from planning; it doesn't belong in the shipped diff.
…der docs

- ai-agent.md no longer names AnthropicChatModelConfiguration as "the first
  built-in v2 member and the reference implementation"; the v2
  ProviderConfiguration extension point is described generically instead, so
  the list of native providers doesn't need updating as more are added.
- HttpTransportSupport's class javadoc dropped the comparison to the
  LangChain4J-side proxy support and just states what the class does.
- AnthropicMessageRequestConverter#applyOutputConfig dropped a comment
  referencing the LangChain4J-routed path.
pause_turn previously mapped to a null domain StopReason (reasoning being
that it's surfaced as a ChatResult.Continuation, not a finished turn), but
that also hid it from AssistantMessage.stopReason(), the one field meant to
carry the vendor's finish reason in normalized form. It now falls through to
the existing UnknownStopReason(raw) branch like any other value without a
dedicated domain mapping, so the assistant message always carries a stop
reason regardless of how the result is surfaced.
… model config

- Endpoint description drops the "e.g. Anthropic API" clause.
- Effort tooltip no longer explains every dropdown choice inline (the
  dropdown itself already lists them); shortened to what effort controls
  plus a link to the docs, with the link on its own visual line.
- Thinking-mode tooltip uses <code> for the option values and drops the
  semicolon-joined single-paragraph shape.
- Thinking budget tokens tooltip: "Max" -> "Maximum", and drops the
  "required and used only when enabled" clause -- the field's own
  @TemplateProperty.PropertyCondition already hides it unless thinking mode
  is 'enabled', so the constraint doesn't need restating in prose.
- Thinking display tooltip explains what happens when left unset.
- promptCaching moved to sit right after thinking in AnthropicModelParameters
  (was last, after the model-options group fields).

Element templates regenerated to match.
…t per create()

AnthropicChatModelApiFactory is a singleton Spring bean, but create() was
building fresh AnthropicContentConverter/AnthropicMessageRequestConverter/
AnthropicMessageResponseConverter instances on every call even though none
of them depend on per-request state (only on the injected ObjectMapper).
They're now built once in the constructor and reused across all create()
calls; the per-request AnthropicClient itself is unaffected (still built
fresh per job, since it depends on the request's backend/timeout).

Also drop buildClient's package-private visibility, which existed solely so
AnthropicChatModelApiFactoryClientTest could call it directly. buildClient is
now private, and the client test drives it through the public surface
instead (factory.create() + ChatModel#execute() against a real WireMock
SSE-streamed response) for everything reachable that way. The one thing that
isn't reachable through the public surface is the direct anthropic-api
backend's wire-level api-key header, since that backend always targets the
production Anthropic URL and there's no way to redirect it from outside the
class; AnthropicChatModelApiFactoryTest still covers that it builds a
working ChatModel.
AnthropicContentConverter classified any image/* document as an image
block, but Anthropic's Messages API only accepts JPEG, PNG, GIF, and
WebP; other image types (SVG, BMP, TIFF, ...) were forwarded and
rejected remotely with a 400. Restrict classification to the same
four MIME types already used by the LangChain4J-routed
DocumentToContentConverterImpl.
maxTokens and topK allowed zero, temperature and topP had no upper
bound, and an enabled thinking budget was never checked against
maxTokens -- all three passed connector-side validation and failed
only as a remote 400. Constrain maxTokens/topK to >= 1, bound
temperature/topP to [0, 1], and add a cross-field check that rejects
a thinking budget that isn't smaller than maxTokens when thinking is
enabled.

Also mark the thinking budget tokens field as required (rather than
optional) while its condition is active, matching the runtime
validator which already rejects a null budget when thinking mode is
enabled.
…gContent/ProviderContent envelope

ReasoningContent gains a provider discriminator and ProviderContent drops its
redundant blockType (every payload already self-describes its own shape via
an internal type field). Update the Anthropic response/request converters
and their tests to the new two/three-arg constructors, and drop the
now-unused blockType parameter (and its dead expectedServerToolBlockType
helper) from the e2e provider-content assertion.
…ntConverter

ReasoningContent/ProviderContent payload is no longer nullable (the compact
constructor rejects null), so the null-payload branches were unreachable.
Remove the guards and the two tests exercising the now-impossible case.
ProviderConfiguration.backend() was removed as unused; the
AnthropicChatModelConfiguration override and its assertions are now dead
code. The nested AnthropicConnection.backend() field accessor (used for
actual client construction) is untouched.
Relocate the native-Anthropic subprocess e2e tests into the subprocess.v2
package to extend BaseAgentSubProcessV2Test and configure the provider via
the providerConfigurer() hook, dropping the duplicated createProcessInstance
override and element-template accessor overrides it replaces. Follow the
AI_AGENT_SUB_PROCESS_V2_ELEMENT_TEMPLATE_* fixture rename through the
remaining wire-format fixture and smoke test.
@maff
maff force-pushed the agentic-ai/native-anthropic-provider branch from d9df976 to c23863b Compare July 28, 2026 20:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Agentic AI: Anthropic provider — Anthropic API (direct) & custom/compatible backends

2 participants