Skip to content

temporal-spring-ai: per-model ActivityOptions registry#2855

Open
donald-pinckney wants to merge 3 commits intospring-ai/retry-and-optionsfrom
spring-ai/per-model-timeouts
Open

temporal-spring-ai: per-model ActivityOptions registry#2855
donald-pinckney wants to merge 3 commits intospring-ai/retry-and-optionsfrom
spring-ai/per-model-timeouts

Conversation

@donald-pinckney
Copy link
Copy Markdown
Contributor

@donald-pinckney donald-pinckney commented Apr 21, 2026

Based on

Merge those in order first; this PR will retarget each time an ancestor lands.


What was changed

  • New SpringAiPluginOptions static registry (package io.temporal.springai.plugin). SpringAiPlugin publishes a Map<String, ActivityOptions> to it on construction; ActivityChatModel.forModel(String) / forDefault() consult it before falling back to the plugin defaults.
  • SpringAiPlugin gains a third constructor that accepts the per-model map; the two existing constructors delegate with an empty map. No constructor changes to existing SpringAiPlugin(ChatModel) / SpringAiPlugin(Map, ChatModel) call sites.
  • New ChatModelActivityOptions record (package io.temporal.springai.autoconfigure) — a thin wrapper around Map<String, ActivityOptions> — exists so SpringAiTemporalAutoConfiguration can inject user options by type rather than by bean name. Raw Map<String, ActivityOptions> injection would trigger Spring's collection-of-beans autowiring and sweep in any unrelated ActivityOptions bean in the context. Using a dedicated wrapper type avoids that collision and keeps the design consistent with temporal-spring-ai: discover MCP clients by type, not by bean name #2859 (MCP bean lookup by type, not by name).
  • SpringAiTemporalAutoConfiguration injects ObjectProvider<ChatModelActivityOptions> and forwards the wrapped map to the plugin. No magic bean name, no @Qualifier.
  • Callers who pass explicit ActivityOptions via forModel(name, options) / forDefault(options) bypass the registry entirely. The (timeout, maxAttempts) convenience factory is unaffected — it still builds options from its arguments.
  • Tests: PerModelActivityOptionsTest covers the three cases in the plan — registry hit uses the registered startToCloseTimeout, registry miss falls back to the 2-minute default, explicit options bypass a populated registry entry.

Why?

A single default (2 min start-to-close, 3 attempts) doesn't fit every model. Reasoning and thinking-mode models need more time; fast models want shorter timeouts so retries recover quickly. The Temporal AI integration guide specifically calls this out as a capability partners should expose. With this change, users register one bean and never have to hand-build activity stubs again.

Example user config:

@Bean
ChatModelActivityOptions chatModelActivityOptions() {
    return new ChatModelActivityOptions(Map.of(
        "reasoning", ActivityOptions.newBuilder(ActivityChatModel.defaultActivityOptions())
            .setStartToCloseTimeout(Duration.ofMinutes(15))
            .build()));
}

@donald-pinckney donald-pinckney force-pushed the spring-ai/per-model-timeouts branch from 44777c8 to 316d52b Compare April 22, 2026 15:44
@donald-pinckney donald-pinckney force-pushed the spring-ai/retry-and-options branch from f967316 to 1310ba4 Compare April 22, 2026 16:07
@donald-pinckney donald-pinckney force-pushed the spring-ai/per-model-timeouts branch from 316d52b to ba6a88a Compare April 22, 2026 16:08
@donald-pinckney donald-pinckney force-pushed the spring-ai/retry-and-options branch from 1310ba4 to 9bb3866 Compare April 22, 2026 16:57
@donald-pinckney donald-pinckney force-pushed the spring-ai/per-model-timeouts branch 2 times, most recently from bad7fd6 to e8405cc Compare April 22, 2026 17:54
@donald-pinckney donald-pinckney force-pushed the spring-ai/retry-and-options branch from f038d46 to 9528294 Compare April 22, 2026 19:10
donald-pinckney and others added 3 commits April 22, 2026 15:11
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SpringAiPlugin now accepts an optional Map<String, ActivityOptions>
keyed by chat-model bean name. On construction the plugin publishes
the map to a new package-public static registry SpringAiPluginOptions,
which ActivityChatModel.forModel(name) and forDefault() consult when
building the activity stub. Entries resolve by bean name; the reserved
key SpringAiPlugin.DEFAULT_MODEL_NAME ("default") covers forDefault().

Callers who pass explicit ActivityOptions via forModel(name, options)
or forDefault(options) bypass the registry entirely — explicit options
always win. The registry has no effect on the (timeout, maxAttempts)
convenience factory either; that still builds options from its args.

Auto-configuration picks up a user bean named
"chatModelActivityOptions" (constant
SpringAiTemporalAutoConfiguration.CHAT_MODEL_ACTIVITY_OPTIONS_BEAN) of
type Map<String, ActivityOptions>. The explicit bean-name qualifier
avoids Spring's collection-of-beans auto-wiring for Map<String, T>.

Tests: PerModelActivityOptionsTest covers the three cases called out
in the plan — registry hit, registry miss (falls back to default
2-minute timeout), and explicit options bypass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Planning scratchpad — not part of the shipped artifact. Removed before merge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@donald-pinckney donald-pinckney force-pushed the spring-ai/per-model-timeouts branch from e8405cc to 1a2adb2 Compare April 22, 2026 19:11
@donald-pinckney donald-pinckney marked this pull request as ready for review April 22, 2026 19:50
@donald-pinckney donald-pinckney requested a review from a team as a code owner April 22, 2026 19:50
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