Skip to content

Commit 29fbe36

Browse files
committed
fix: upgrade to Spring Shell 4
Signed-off-by: Arnab Nandy <arnab_nandy7@yahoo.com>
1 parent d40d889 commit 29fbe36

5 files changed

Lines changed: 91 additions & 79 deletions

File tree

embabel-agent-docs/src/main/asciidoc/reference/rag/page.adoc

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,8 +1111,8 @@ Java::
11111111
+
11121112
[source,java]
11131113
----
1114-
@ShellMethod("Ingest URL or file path")
1115-
String ingest(@ShellOption(defaultValue = "./data/document.md") String location) {
1114+
@Command("Ingest URL or file path")
1115+
String ingest(@Option(defaultValue = "./data/document.md") String location) {
11161116
var uri = location.startsWith("http://") || location.startsWith("https://")
11171117
? location
11181118
: Path.of(location).toAbsolutePath().toUri().toString();
@@ -1132,8 +1132,8 @@ Kotlin::
11321132
+
11331133
[source,kotlin]
11341134
----
1135-
@ShellMethod("Ingest URL or file path")
1136-
fun ingest(@ShellOption(defaultValue = "./data/document.md") location: String): String {
1135+
@Command("Ingest URL or file path")
1136+
fun ingest(@Option(defaultValue = "./data/document.md") location: String): String {
11371137
val uri = if (location.startsWith("http://") || location.startsWith("https://")) {
11381138
location
11391139
} else {
@@ -1632,4 +1632,3 @@ See the https://github.com/embabel/rag-demo[rag-demo] project for a complete wor
16321632
- Chatbot with RAG-powered responses
16331633
- Jinja prompt templates for system prompts
16341634
- Spring Shell commands for interactive testing
1635-

embabel-agent-docs/src/main/asciidoc/shell/commands.adoc

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ The persistent context remains `{tenantId=acme, authToken=bearer-xyz123}` for fu
124124
==== Implementing Custom Shell Commands
125125

126126
During development you may want to add your own shell commands to invoke specific agents or flows directly, bypassing the natural-language routing of `execute`.
127-
Because the Embabel Shell is a standard Spring Shell application, any `@ShellComponent` bean is discovered and registered automatically by Spring.
127+
Because the Embabel Shell is a standard Spring Shell application, commands declared in any Spring `@Component` bean are discovered and registered automatically.
128128

129129
Inject `AgentPlatform` and use `AgentInvocation` to call agents with strong typing:
130130

@@ -134,15 +134,15 @@ Java::
134134
+
135135
[source,java]
136136
----
137-
@ShellComponent
137+
@Component
138138
public record SupportAgentShellCommands(
139139
AgentPlatform agentPlatform
140140
) {
141141
142-
@ShellMethod("Get bank support for a customer query")
142+
@Command("Get bank support for a customer query")
143143
public String bankSupport(
144-
@ShellOption(value = "id", help = "customer id", defaultValue = "123") Long id,
145-
@ShellOption(value = "query", help = "customer query", defaultValue = "What's my balance, including pending amounts?") String query
144+
@Option(longName = "id", description = "customer id", defaultValue = "123") Long id,
145+
@Option(longName = "query", description = "customer query", defaultValue = "What's my balance, including pending amounts?") String query
146146
) {
147147
var supportInput = new SupportInput(id, query);
148148
System.out.println("Support input: " + supportInput);
@@ -159,15 +159,15 @@ Kotlin::
159159
+
160160
[source,kotlin]
161161
----
162-
@ShellComponent
162+
@Component
163163
class SupportAgentShellCommands(
164164
private val agentPlatform: AgentPlatform
165165
) {
166166
167-
@ShellMethod("Get bank support for a customer query")
167+
@Command("Get bank support for a customer query")
168168
fun bankSupport(
169-
@ShellOption(value = ["id"], help = "customer id", defaultValue = "123") id: Long,
170-
@ShellOption(value = ["query"], help = "customer query", defaultValue = "What's my balance, including pending amounts?") query: String
169+
@Option(longName = "id", description = "customer id", defaultValue = "123") id: Long,
170+
@Option(longName = "query", description = "customer query", defaultValue = "What's my balance, including pending amounts?") query: String
171171
): String {
172172
val supportInput = SupportInput(id, query)
173173
println("Support input: $supportInput")
@@ -182,4 +182,4 @@ class SupportAgentShellCommands(
182182
====
183183

184184
TIP: Custom shell commands are particularly useful when you want to pre-populate inputs with test data or invoke a specific agent directly rather than relying on Autonomy's natural-language selection.
185-
For full `AgentInvocation` API details see <<reference.invoking>>.
185+
For full `AgentInvocation` API details see <<reference.invoking>>.

embabel-agent-shell/src/main/kotlin/com/embabel/agent/shell/ShellCommands.kt

Lines changed: 70 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,16 @@ import org.springframework.beans.factory.annotation.Autowired
3838
import org.springframework.boot.SpringApplication
3939
import org.springframework.context.ConfigurableApplicationContext
4040
import org.springframework.core.env.ConfigurableEnvironment
41-
import org.springframework.shell.standard.ShellComponent
42-
import org.springframework.shell.standard.ShellMethod
43-
import org.springframework.shell.standard.ShellOption
41+
import org.springframework.shell.core.command.annotation.Command
42+
import org.springframework.shell.core.command.annotation.Option
43+
import org.springframework.stereotype.Component
4444
import kotlin.system.exitProcess
4545

4646

4747
/**
4848
* Main shell entry point
4949
*/
50-
@ShellComponent
50+
@Component
5151
class ShellCommands(
5252
private val autonomy: Autonomy,
5353
private val asyncer: Asyncer,
@@ -93,20 +93,21 @@ class ShellCommands(
9393
)
9494
)
9595

96-
@ShellMethod(value = "Clear blackboard")
96+
@Command(description = "Clear blackboard")
9797
fun clear(): String {
9898
blackboard = null
9999
return "Blackboard cleared"
100100
}
101101

102-
@ShellMethod(
103-
value = "Set persistent tool call context as key=value pairs, passed to all tools during execution. " +
102+
@Command(
103+
description = "Set persistent tool call context as key=value pairs, passed to all tools during execution. " +
104104
"Example: set-context tenantId=acme,apiKey=secret123",
105-
key = ["set-context", "sc"],
105+
name = ["set-context"],
106+
alias = ["sc"],
106107
)
107108
fun setContext(
108-
@ShellOption(
109-
help = "Comma-separated key=value pairs (e.g. tenantId=acme,apiKey=secret). Use 'clear' to reset.",
109+
@Option(
110+
description = "Comma-separated key=value pairs (e.g. tenantId=acme,apiKey=secret). Use 'clear' to reset.",
110111
defaultValue = "",
111112
) context: String,
112113
): String {
@@ -118,9 +119,9 @@ class ShellCommands(
118119
return "Tool call context set: ${persistentToolCallContext.toMap()}".color(colorPalette.color2)
119120
}
120121

121-
@ShellMethod(
122-
value = "Show current tool call context",
123-
key = ["show-context"],
122+
@Command(
123+
description = "Show current tool call context",
124+
name = ["show-context"],
124125
)
125126
fun showContext(): String {
126127
val ctx = persistentToolCallContext.toMap()
@@ -131,7 +132,7 @@ class ShellCommands(
131132
}.color(colorPalette.color2)
132133
}
133134

134-
@ShellMethod(value = "Show recent agent process runs. This is what actually happened, not just what was planned.")
135+
@Command(description = "Show recent agent process runs. This is what actually happened, not just what was planned.")
135136
fun runs(): String {
136137
val plans = agentProcesses.map {
137138
"[${it.id}] Goal: ${it.agent.goals.map { g -> g.name }}; usage - ${it.costInfoString(verbose = false)}\n\t\t" +
@@ -140,7 +141,7 @@ class ShellCommands(
140141
return "Recent runs:\n\t${plans.joinToString("\n\t")}"
141142
}
142143

143-
@ShellMethod(value = "List all active Spring profiles")
144+
@Command(description = "List all active Spring profiles")
144145
fun profiles(): String {
145146
val profiles = environment.activeProfiles
146147
return "Active profiles: ${profiles.joinToString()}"
@@ -161,7 +162,7 @@ class ShellCommands(
161162
})
162163
}
163164

164-
@ShellMethod("Chat")
165+
@Command("Chat")
165166
fun chat(): String {
166167

167168
fun runChat(): String {
@@ -187,7 +188,7 @@ class ShellCommands(
187188
}
188189
}
189190

190-
@ShellMethod("List agents")
191+
@Command("List agents")
191192
fun agents(): String {
192193
val detail = "${"Agents:".bold()}\n${
193194
agentPlatform.agents()
@@ -198,7 +199,7 @@ class ShellCommands(
198199
return detail + "\n\nTL;DR\n${agentPlatform.agents().joinToString("\n") { "${it.name}: ${it.description}" }}"
199200
}
200201

201-
@ShellMethod("List actions")
202+
@Command("List actions")
202203
fun actions(): String {
203204
val detail = "${"Actions:".bold()}\n${
204205
agentPlatform.actions
@@ -207,25 +208,25 @@ class ShellCommands(
207208
return detail + "\n\nTL;DR\n${agentPlatform.actions.joinToString("\n") { "${it.name}: ${it.description}" }}"
208209
}
209210

210-
@ShellMethod("List conditions")
211+
@Command("List conditions")
211212
fun conditions(): String {
212213
return "${"Conditions:".bold()}\n${
213214
agentPlatform.conditions
214215
.joinToString(separator = "\n") { it.infoString(verbose = true, indent = 1) }
215216
}"
216217
}
217218

218-
@ShellMethod("List goals")
219+
@Command("List goals")
219220
fun goals(): String {
220221
return "${"Goals:".bold()}\n${
221222
agentPlatform.goals
222223
.joinToString(separator = "\n") { it.infoString(verbose = true, indent = 1) }
223224
}"
224225
}
225226

226-
@ShellMethod("Try to choose a goal for a given intent. Show all goal rankings")
227+
@Command("Try to choose a goal for a given intent. Show all goal rankings")
227228
fun chooseGoal(
228-
@ShellOption(help = "what the agent system should do") intent: String,
229+
@Option(description = "what the agent system should do") intent: String,
229230
): String {
230231
try {
231232
val goalSeeker = autonomy.createGoalSeeker(
@@ -245,21 +246,22 @@ class ShellCommands(
245246
}
246247
}
247248

248-
@ShellMethod("Information about the AgentPlatform")
249+
@Command("Information about the AgentPlatform")
249250
fun platform(): String = "AgentPlatform: ${agentPlatform.name}"
250251

251252

252-
@ShellMethod(
253+
@Command(
253254
"Show last blackboard: The final state of a previous operation",
254-
key = ["blackboard", "bb"],
255+
name = ["blackboard"],
256+
alias = ["bb"],
255257
)
256258
fun blackboard(): String {
257259
return if (blackboard == null) {
258260
"No blackboard available. Please run a command first."
259261
} else blackboard!!.infoString(verbose = true)
260262
}
261263

262-
@ShellMethod("List available tool groups")
264+
@Command("List available tool groups")
263265
fun tools(): String {
264266
val tgr = agentPlatform.toolGroupResolver
265267
return String.format(
@@ -275,16 +277,16 @@ class ShellCommands(
275277
)
276278
}
277279

278-
@ShellMethod("Show tool stats")
280+
@Command("Show tool stats")
279281
fun toolStats(): String {
280282
return toolsStats.infoString(verbose = true)
281283
}
282284

283-
@ShellMethod("List available models")
285+
@Command("List available models")
284286
fun models(): String =
285287
modelProvider.infoString(true)
286288

287-
@ShellMethod("Show options")
289+
@Command("Show options")
288290
fun showOptions(): String {
289291
// Don't show the blackboard as it's long
290292
return embabelObjectMapperHolder.get().writerWithDefaultPrettyPrinter().writeValueAsString(
@@ -298,24 +300,26 @@ class ShellCommands(
298300
.color(colorPalette.color2)
299301
}
300302

301-
@ShellMethod(
303+
@Command(
302304
"Set options",
303305
)
304306
fun setOptions(
305-
@ShellOption(
306-
value = ["-o", "--open"],
307-
help = "run in open mode, choosing a goal and using all actions that can help achieve it",
307+
@Option(
308+
shortName = 'o',
309+
longName = "open",
310+
description = "run in open mode, choosing a goal and using all actions that can help achieve it",
308311
) open: Boolean = false,
309-
@ShellOption(value = ["-t", "--test"], help = "run in help mode") test: Boolean = false,
310-
@ShellOption(value = ["-p", "--showPrompts"], help = "show prompts to LLMs") showPrompts: Boolean,
311-
@ShellOption(value = ["-r", "--showResponses"], help = "show LLM responses") showLlmResponses: Boolean = false,
312-
@ShellOption(value = ["-d", "--debug"], help = "show debug info") debug: Boolean = false,
313-
@ShellOption(value = ["-s", "--state"], help = "Use existing blackboard") state: Boolean = false,
314-
@ShellOption(value = ["-td", "--toolDelay"], help = "Tool delay") toolDelay: Boolean = false,
315-
@ShellOption(value = ["-od", "--operationDelay"], help = "Operation delay") operationDelay: Boolean = false,
316-
@ShellOption(
317-
value = ["-s", "--showPlanning"],
318-
help = "show detailed planning info",
312+
@Option(shortName = 't', longName = "test", description = "run in help mode") test: Boolean = false,
313+
@Option(shortName = 'p', longName = "showPrompts", description = "show prompts to LLMs") showPrompts: Boolean,
314+
@Option(shortName = 'r', longName = "showResponses", description = "show LLM responses") showLlmResponses: Boolean = false,
315+
@Option(shortName = 'd', longName = "debug", description = "show debug info") debug: Boolean = false,
316+
@Option(shortName = 's', longName = "state", description = "Use existing blackboard") state: Boolean = false,
317+
@Option(longName = "toolDelay", description = "Tool delay") toolDelay: Boolean = false,
318+
@Option(longName = "operationDelay", description = "Operation delay") operationDelay: Boolean = false,
319+
@Option(
320+
shortName = 'P',
321+
longName = "showPlanning",
322+
description = "show detailed planning info",
319323
defaultValue = "true",
320324
) showPlanning: Boolean = true,
321325
): String {
@@ -338,32 +342,35 @@ class ShellCommands(
338342
return "Options updated:\nOpen mode:$openMode\n${showOptions()}".color(colorPalette.color2)
339343
}
340344

341-
@ShellMethod(
345+
@Command(
342346
"Execute a task. Put the task in double quotes. For example:\n\tx \"Lynda is a scorpio. Find news for her\" -p",
343-
key = ["execute", "x"],
347+
name = ["execute"],
348+
alias = ["x"],
344349
)
345350
fun execute(
346-
@ShellOption(help = "what the agent system should do") intent: String,
347-
@ShellOption(
348-
value = ["-o", "--open"],
349-
help = "run in open mode, choosing a goal and using all actions that can help achieve it",
351+
@Option(description = "what the agent system should do") intent: String,
352+
@Option(
353+
shortName = 'o',
354+
longName = "open",
355+
description = "run in open mode, choosing a goal and using all actions that can help achieve it",
350356
) open: Boolean = false,
351-
@ShellOption(value = ["-p", "--showPrompts"], help = "show prompts to LLMs") showPrompts: Boolean,
352-
@ShellOption(value = ["-r", "--showResponses"], help = "show LLM responses") showLlmResponses: Boolean = false,
353-
@ShellOption(value = ["-d", "--debug"], help = "show debug info") debug: Boolean = false,
354-
@ShellOption(value = ["-s", "--state"], help = "Use existing blackboard") state: Boolean = false,
355-
@ShellOption(value = ["-td", "--toolDelay"], help = "Tool delay") toolDelay: Boolean = false,
356-
@ShellOption(value = ["-od", "--operationDelay"], help = "Operation delay") operationDelay: Boolean = false,
357-
@ShellOption(
358-
value = ["-P", "--showPlanning"],
359-
help = "show detailed planning info",
357+
@Option(shortName = 'p', longName = "showPrompts", description = "show prompts to LLMs") showPrompts: Boolean,
358+
@Option(shortName = 'r', longName = "showResponses", description = "show LLM responses") showLlmResponses: Boolean = false,
359+
@Option(shortName = 'd', longName = "debug", description = "show debug info") debug: Boolean = false,
360+
@Option(shortName = 's', longName = "state", description = "Use existing blackboard") state: Boolean = false,
361+
@Option(longName = "toolDelay", description = "Tool delay") toolDelay: Boolean = false,
362+
@Option(longName = "operationDelay", description = "Operation delay") operationDelay: Boolean = false,
363+
@Option(
364+
shortName = 'P',
365+
longName = "showPlanning",
366+
description = "show detailed planning info",
360367
defaultValue = "true",
361368
) showPlanning: Boolean = true,
362-
@ShellOption(
363-
value = ["-c", "--context"],
364-
help = "Tool call context as comma-separated key=value pairs (e.g. tenantId=acme,apiKey=secret). " +
369+
@Option(
370+
shortName = 'c',
371+
longName = "context",
372+
description = "Tool call context as comma-separated key=value pairs (e.g. tenantId=acme,apiKey=secret). " +
365373
"Merged with persistent context set via set-context; these values win on conflict.",
366-
defaultValue = ShellOption.NULL,
367374
) context: String? = null,
368375
): String {
369376
// Override any options
@@ -398,7 +405,7 @@ class ShellCommands(
398405
)
399406
}
400407

401-
@ShellMethod(value = "Exit the application", key = ["exit", "quit", "bye"])
408+
@Command(description = "Exit the application", name = ["exit"], alias = ["quit", "bye"])
402409
fun exit(): String {
403410
println("Exiting...".color(colorPalette.color2))
404411
logger.info("Shutting down application...")

embabel-agent-starters/embabel-agent-starter-shell/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ fun main(args: Array<String>) {
158158

159159
1. **Auto-Configuration**: `AgentShellAutoConfiguration` activates when the starter is present
160160
2. **Component Scanning**: Shell commands and services are discovered via `@ComponentScan`
161-
3. **Spring Shell Integration**: Commands are registered as `@ShellComponent` beans
161+
3. **Spring Shell Integration**: Commands are registered from Spring `@Component` beans
162162
4. **Agent Platform Access**: Shell commands interact with the `Autonomy` and `AgentPlatform` APIs
163163

164164
## Architecture

0 commit comments

Comments
 (0)