You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(11-ai-adapters): sync capability matrix with pinned expectedCapabilities()
Matrix was stale across every runtime — the framework's AbstractAgentRuntime defaults (executeWithOuterRetry, assembleMessages, pipeline-level StructuredOutputCapturingSession) plus per-adapter tool bridges (EmbabelToolBridge, SemanticKernelToolBridge, AtmosphereToolBridge for Koog) make TOOL_CALLING, TOOL_APPROVAL, CONVERSATION_MEMORY, PER_REQUEST_RETRY, TOKEN_USAGE, and STRUCTURED_OUTPUT universal across all seven runtimes; PROMPT_CACHING and AUDIO now honest on ADK and Koog; Embabel and SK tool-calling now declared (bridges exist). Adds TOOL_CALL_DELTA row. Notes section rewritten — old "TOKEN_USAGE only SK", "PER_REQUEST_RETRY Built-in only", "Embabel has no tool calling" claims were wrong. Choosing a runtime section adds the missing Microsoft Semantic Kernel recommendation and samples table adds spring-boot-semantic-kernel-chat.
- ✅ Declared in `capabilities()` and honored at runtime
66
-
- — Not declared. Either the framework's native API does not expose the feature, or the bridge has not threaded it through yet
67
+
- — Not declared. Either the framework's native API does not expose the feature, or the bridge has not threaded it through yet.
67
68
68
-
**Notes on per-runtime gaps:**
69
+
### Why the baseline is so high
69
70
70
-
-**`CONVERSATION_MEMORY`** is declared only by runtimes whose native SPI threads a conversation ID into the provider call (ADK `Runner`, Koog `AIAgent`, SK `ChatHistory`). The other runtimes still accept `AgentExecutionContext.conversationId()` but rebuild history from `context.conversationHistory()` on every call — that's not the same contract, so declaring the capability would be dishonest.
71
-
-**`AGENT_ORCHESTRATION`** tracks whether the underlying framework owns a multi-agent dispatch loop (ADK `LlmAgent`, Embabel goal graph, Koog `chatAgentStrategy`). Built-in / Spring AI / LC4j / SK can participate in `@Coordinator` fleets but do not own the loop themselves.
72
-
-**`TOKEN_USAGE`** is currently declared only by Semantic Kernel. The other runtimes forward `ai.tokens.*` metadata through the pipeline whenever the provider returns it, but the capability flag is gated on a typed `TokenUsage` extraction path — Tier 1 follow-up work will graduate Spring AI / LC4j / ADK / Koog / Built-in as each bridge is updated.
73
-
-**`PROMPT_CACHING`** is honored by Built-in / Spring AI / LC4j through the `CacheHint` metadata attached to `AgentExecutionContext`. ADK's `ContextCacheConfig` is set at `App.Builder` construction time rather than per-request, so the hint has nowhere to go on the bridge path; the capability is not declared.
74
-
-**`AUDIO`** is not declared by Built-in because the OpenAI-compatible client does not yet emit an `input_audio` content block — `Content.Audio` parts are accepted at the API surface but silently skipped during message assembly. Tier 2 work will wire this up.
75
-
-**`PER_REQUEST_RETRY`** is Built-in only. Framework runtimes inherit their own retry layers (Spring Retry, LC4j `RetryUtils`, ADK's `HttpClient`, Koog `CallRetryPolicy`) and do not expose a per-request override through the bridge. Setting `context.retryPolicy(...)` on a framework runtime silently inherits the framework's default instead.
76
-
-**Embabel** has no tool-calling surface in its current release — the framework is goal-driven rather than tool-driven. `@AiTool` methods are ignored when Embabel is the active runtime.
77
-
-**Semantic Kernel** tool-calling is deferred in 4.0.36 — the Java SK's `KernelFunction` binding is on the Tier 2 roadmap. `TOOL_CALLING` and `TOOL_APPROVAL` will be added when the bridge routes through `ToolExecutionHelper.executeWithApproval` like the other runtimes.
78
-
-**Koog** 0.7.x does not expose a stable multi-modal or prompt-cache surface on the bridge path; both capabilities are omitted until Koog 0.8.x's `Message.Media` lands.
71
+
The first eight rows are universal because `AbstractAgentRuntime` and the pipeline layer implement them once and every runtime that extends or delegates to them inherits the behavior. The framework builds the default, the adapter only plugs in where it differs.
72
+
73
+
-**`STRUCTURED_OUTPUT`** is implemented at the pipeline layer — `AiPipeline.StructuredOutputCapturingSession` plus system-prompt schema injection — so any runtime that honors `SYSTEM_PROMPT` gets it for free (`BuiltInAgentRuntime` additionally enables native JSON mode).
74
+
-**`CONVERSATION_MEMORY`** is implemented by `AbstractAgentRuntime.assembleMessages`, which threads `context.history()` into the outgoing message list on every dispatch. Runtimes with a native conversation-id path (ADK `Runner`, Koog `AIAgent`, SK `ChatHistory`) use that; the rest honor the pipeline-managed history via the base class.
75
+
-**`PER_REQUEST_RETRY`** is implemented by `AbstractAgentRuntime.executeWithOuterRetry`, which wraps each runtime's dispatch in a retry loop respecting `context.retryPolicy(...)`. Each adapter stacks this on top of its own native retry layer (Spring Retry, LC4j `RetryUtils`, ADK `HttpClient`, Koog `CallRetryPolicy`, SK `OpenAIAsyncClient`), giving an "at least N retries" guarantee that is uniform across every runtime.
76
+
-**`TOOL_CALLING` / `TOOL_APPROVAL`** are implemented per-adapter (`SpringAiToolBridge`, `LangChain4jToolBridge`, `AdkFunctionToolBridge`, `EmbabelToolBridge`, `AtmosphereToolBridge` for Koog, `SemanticKernelToolBridge`) — each routes every invocation through `ToolExecutionHelper.executeWithApproval` so `@RequiresApproval` gates fire uniformly regardless of which runtime handles the call.
77
+
-**`TOKEN_USAGE`** is honest wherever each bridge threads a typed `TokenUsage` record through `session.usage(...)` — done per-runtime but declared universally, because the pipeline surfaces the record on every adapter today.
78
+
79
+
### Notes on the remaining gaps
80
+
81
+
-**`VISION` / `AUDIO` / `MULTI_MODAL`** — Semantic Kernel has no multi-modal surface yet. Embabel's Atmosphere-native dispatch path translates `Content.Image` into Embabel `AgentImage` (no audio); the deployed-agent path ignores multi-modal parts, matching Embabel's own semantics where a deployed `@Agent` owns its own handling. Koog 0.7.3 accepts vision, audio, and multi-modal natively via `ContentPart.Image` / `ContentPart.Audio`, but its tool-calling surface (`AIAgent.run(String)`) only accepts a plain text message — multi-modal + tools together degrade gracefully (the tool path wins with a WARN).
82
+
-**`PROMPT_CACHING`** — Built-in / Spring AI / LC4j / ADK / Koog all honor the portable `CacheHint` attached to `AgentExecutionContext`. Mechanism varies per provider (OpenAI `prompt_cache_key`, Anthropic / Bedrock `CacheControl.Bedrock.{FiveMinutes, OneHour}`, ADK's per-request `ContextCacheConfig` wired via `buildRequestRunner`). Embabel and Semantic Kernel do not expose a caching hook that maps to `CacheHint` today.
83
+
-**`AGENT_ORCHESTRATION`** tracks whether the underlying framework owns a multi-agent dispatch loop (ADK `LlmAgent`, Embabel goal graph, Koog `chatAgentStrategy`). Built-in / Spring AI / LC4j / SK can still participate in `@Coordinator` fleets — the coordinator dispatches from Atmosphere — but do not own the loop themselves.
84
+
-**`TOOL_CALL_DELTA`** is Built-in only. `OpenAiCompatibleClient` forwards every `delta.tool_calls[].function.arguments` fragment through `session.toolCallDelta(id, chunk)` on both the chat-completions and responses-API streaming paths. The six framework bridges cannot emit deltas without bypassing their high-level streaming APIs; they honor the default no-op contract instead.
79
85
80
86
When a framework adds a capability in a new release, update both the runtime's `capabilities()` method and the `expectedCapabilities()` override in its contract test in the same commit. The contract test will fail on either side of a drift, which is the intended safety net.
81
87
@@ -406,6 +412,8 @@ Tools are registered globally and selected per-endpoint:
406
412
407
413
**Koog** is the best fit when you want a concise Kotlin DSL for defining agents and tools and already live in a Kotlin codebase.
408
414
415
+
**Microsoft Semantic Kernel** is the best fit when you're already invested in the SK ecosystem — Plugins, Planners, shared `KernelFunction` definitions across .NET and Java — or want to bind server-side SK skills to Atmosphere's streaming transport. The bridge ships tool calling via `SemanticKernelToolBridge` (auto-invoke through `ToolExecutionHelper.executeWithApproval`), conversation memory via `ChatHistory`, and embeddings via `SemanticKernelEmbeddingRuntime`.
416
+
409
417
All runtimes produce the same wire protocol on the Atmosphere side: text-by-text JSON messages delivered over WebTransport, WebSocket, SSE, or long-polling to any connected client.
410
418
411
419
## Samples
@@ -419,6 +427,7 @@ Each runtime has a corresponding sample application in the repo:
|`spring-boot-semantic-kernel-chat`| Microsoft Semantic Kernel |[github](https://github.com/Atmosphere/atmosphere/tree/main/samples/spring-boot-semantic-kernel-chat)|
0 commit comments