Commit 911dd02
temporal-spring-ai: pass provider-specific ChatOptions through the activity boundary (#2857)
* temporal-spring-ai: plan — provider-specific ChatOptions pass-through
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* temporal-spring-ai: amend plan — switch to serialized-blob pass-through
Option 2 (serialize the full ChatOptions subclass as JSON + class name,
rehydrate on the activity side) is strictly better than the opaque map.
Full fidelity, no per-provider allow-list, no builder gymnastics.
The earlier rationale for option 1 ("drags provider classes into the
workflow's classpath") was wrong: workflow and activity workers can be
separate JVMs, but the user's workflow can only construct
OpenAiChatOptions if spring-ai-openai is already on the workflow
worker's classpath, and the activity worker has the same class because
that's where the ChatModel bean runs. The precondition holds whenever
the feature is actually used.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* temporal-spring-ai: pass provider-specific ChatOptions through the activity boundary
Serialize the caller's ChatOptions as (class name, JSON) on the workflow
side and rehydrate the exact subclass on the activity side. Every field
survives — including provider-specific ones like OpenAI
reasoning_effort or Anthropic thinking-budget settings — without the
plugin needing per-provider allow-lists, reflection into builder types,
or compile deps on spring-ai-openai/anthropic/etc.
Changes:
- ChatModelTypes.ModelOptions: two new nullable fields,
chatOptionsClass and chatOptionsJson. Older callers that only set
the common scalar fields still work via a convenience constructor.
- ActivityChatModel: serialize the caller's ChatOptions with a plain
ObjectMapper when non-null; a Jackson mixin skips the tool-callback
bag (toolCallbacks/toolNames/toolContext) since those cross the
boundary via the separate `tools` field. Serialization failure is
logged at debug and leaves the blob fields null — activity side then
uses the common-field path.
- ChatModelActivityImpl: primary path rehydrates from the serialized
blob and, if the result is a ToolCallingChatOptions, re-attaches the
stub tool callbacks plus forces internalToolExecutionEnabled=false.
Fallback path (no blob, class-not-found, deser error) builds a plain
ToolCallingChatOptions from the common scalar fields, identical to
the prior behavior.
Tests:
- ProviderOptionsPassthroughTest.customChatOptionsSubclass... — a
test-local CustomChatOptions extending DefaultToolCallingChatOptions
with an extra reasoningEffort field round-trips, and common fields
(temperature/maxTokens) come through the same path.
- ProviderOptionsPassthroughTest.nullChatOptions... — workflow that
doesn't set any options still works via the fallback path.
The workflow calls ActivityChatModel.call(new Prompt(...)) directly
instead of going through ChatClient, because ChatClient.defaultOptions
coerces the caller's subclass into the client's internal options type
before ActivityChatModel ever sees it. Users of ChatClient who want
provider-specific options should set those on the ChatModel bean's
default options (Spring AI merges those into every call).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* temporal-spring-ai: drop PLAN.md
Planning scratchpad — not part of the shipped artifact. Removed before merge.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent 79c9fe5 commit 911dd02
4 files changed
Lines changed: 430 additions & 18 deletions
File tree
- temporal-spring-ai/src
- main/java/io/temporal/springai
- activity
- model
- test/java/io/temporal/springai
Lines changed: 102 additions & 14 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
| 11 | + | |
| 12 | + | |
10 | 13 | | |
11 | 14 | | |
12 | 15 | | |
| 16 | + | |
13 | 17 | | |
14 | 18 | | |
15 | 19 | | |
| |||
30 | 34 | | |
31 | 35 | | |
32 | 36 | | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
33 | 56 | | |
34 | 57 | | |
35 | 58 | | |
| |||
77 | 100 | | |
78 | 101 | | |
79 | 102 | | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
80 | 129 | | |
81 | 130 | | |
82 | 131 | | |
| |||
93 | 142 | | |
94 | 143 | | |
95 | 144 | | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
104 | | - | |
105 | | - | |
106 | | - | |
107 | | - | |
| 145 | + | |
108 | 146 | | |
109 | 147 | | |
110 | 148 | | |
111 | | - | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
112 | 165 | | |
113 | | - | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
114 | 202 | | |
115 | 203 | | |
116 | 204 | | |
| |||
Lines changed: 46 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
3 | 5 | | |
4 | 6 | | |
5 | 7 | | |
| |||
65 | 67 | | |
66 | 68 | | |
67 | 69 | | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
68 | 96 | | |
69 | 97 | | |
70 | 98 | | |
| |||
301 | 329 | | |
302 | 330 | | |
303 | 331 | | |
304 | | - | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
305 | 335 | | |
306 | 336 | | |
307 | 337 | | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
308 | 350 | | |
309 | 351 | | |
310 | 352 | | |
| |||
314 | 356 | | |
315 | 357 | | |
316 | 358 | | |
317 | | - | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
318 | 362 | | |
319 | 363 | | |
320 | 364 | | |
| |||
Lines changed: 42 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
233 | 233 | | |
234 | 234 | | |
235 | 235 | | |
236 | | - | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
237 | 248 | | |
238 | 249 | | |
239 | 250 | | |
| |||
244 | 255 | | |
245 | 256 | | |
246 | 257 | | |
247 | | - | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
248 | 288 | | |
0 commit comments