Skip to content
This repository was archived by the owner on Sep 11, 2025. It is now read-only.

Commit 6aaedc0

Browse files
fix: start agents synchronously, and add examples setting data on start (#890)
1 parent 004bb58 commit 6aaedc0

File tree

5 files changed

+44
-20
lines changed

5 files changed

+44
-20
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
- feat: add kubernetes secrets provider and API to read secrets [#885](https://github.com/hypermodeinc/modus/pull/885)
88
- feat: "start" and "stop" are now mutation prefixes [#889](https://github.com/hypermodeinc/modus/pull/889)
9+
- fix: start agents synchronously, and add examples setting data on start [#890](https://github.com/hypermodeinc/modus/pull/890)
910

1011
## 2025-06-10 - Runtime 0.18.0-alpha.6
1112

runtime/actors/actorsystem.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,15 @@ func loadAgentActors(ctx context.Context, plugin *plugins.Plugin) error {
9292
actors := _actorSystem.Actors()
9393
runningAgents := make(map[string]bool, len(actors))
9494
for _, pid := range actors {
95-
if actor, ok := pid.Actor().(*wasmAgentActor); ok {
96-
runningAgents[actor.agentId] = true
97-
actor.plugin = plugin
98-
if err := pid.Restart(ctx); err != nil {
99-
logger.Err(ctx, err).Msgf("Failed to restart actor for agent %s.", actor.agentId)
95+
go func(f_ctx context.Context, f_pid *goakt.PID) {
96+
if actor, ok := f_pid.Actor().(*wasmAgentActor); ok {
97+
runningAgents[actor.agentId] = true
98+
actor.plugin = plugin
99+
if err := f_pid.Restart(f_ctx); err != nil {
100+
logger.Err(f_ctx, err).Msgf("Failed to restart actor for agent %s.", actor.agentId)
101+
}
100102
}
101-
}
103+
}(ctx, pid)
102104
}
103105

104106
// spawn actors for agents with state in the database, that are not already running
@@ -112,7 +114,11 @@ func loadAgentActors(ctx context.Context, plugin *plugins.Plugin) error {
112114
host := wasmhost.GetWasmHost(ctx)
113115
for _, agent := range agents {
114116
if !runningAgents[agent.Id] {
115-
spawnActorForAgentAsync(host, plugin, agent.Id, agent.Name, false)
117+
go func(f_ctx context.Context, agentId string, agentName string) {
118+
if _, err := spawnActorForAgent(host, plugin, agentId, agentName, false); err != nil {
119+
logger.Err(f_ctx, err).Msgf("Failed to spawn actor for agent %s.", agentId)
120+
}
121+
}(ctx, agent.Id, agent.Name)
116122
}
117123
}
118124

runtime/actors/agents.go

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,26 +67,21 @@ func StartAgent(ctx context.Context, agentName string) (*AgentInfo, error) {
6767

6868
agentId := xid.New().String()
6969
host := wasmhost.GetWasmHost(ctx)
70-
spawnActorForAgentAsync(host, plugin, agentId, agentName, true)
70+
pid, err := spawnActorForAgent(host, plugin, agentId, agentName, true)
71+
if err != nil {
72+
return nil, fmt.Errorf("error spawning actor for agent %s: %w", agentId, err)
73+
}
7174

75+
actor := pid.Actor().(*wasmAgentActor)
7276
info := &AgentInfo{
73-
Id: agentId,
74-
Name: agentName,
75-
Status: AgentStatusStarting,
77+
Id: actor.agentId,
78+
Name: actor.agentName,
79+
Status: actor.status,
7680
}
7781

7882
return info, nil
7983
}
8084

81-
func spawnActorForAgentAsync(host wasmhost.WasmHost, plugin *plugins.Plugin, agentId, agentName string, initializing bool) {
82-
// We spawn the actor in a goroutine to avoid blocking while the actor is being spawned.
83-
// This allows many agents to be spawned in parallel, if needed.
84-
// Errors are logged but not returned, as the actor system will handle them.
85-
go func() {
86-
_, _ = spawnActorForAgent(host, plugin, agentId, agentName, initializing)
87-
}()
88-
}
89-
9085
func spawnActorForAgent(host wasmhost.WasmHost, plugin *plugins.Plugin, agentId, agentName string, initializing bool) (*goakt.PID, error) {
9186
// The actor needs to spawn in its own context, so we don't pass one in to this function.
9287
// If we did, then when the original context was cancelled or completed, the actor initialization would be cancelled too.

sdk/assemblyscript/examples/agents/assembly/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ export function startCounterAgent(): AgentInfo {
2525
return agents.start("Counter");
2626
}
2727

28+
/**
29+
* Starts a counter agent with an initial count value,
30+
* and returns info including its ID and status.
31+
*/
32+
export function startCounterAgentWithData(initialCount: i32): AgentInfo {
33+
const info = agents.start("Counter");
34+
updateCountAsync(info.id, initialCount);
35+
return info;
36+
}
37+
2838
/**
2939
* Stops the specified agent by ID, returning its status info.
3040
* This will terminate the agent, and it cannot be resumed or restarted.

sdk/go/examples/agents/main.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,18 @@ func StartCounterAgent() (agents.AgentInfo, error) {
3030
return agents.Start("Counter")
3131
}
3232

33+
// Starts a counter agent with an initial count value,
34+
// and returns info including its ID and status.
35+
func StartCounterAgentWithData(initialCount int) (agents.AgentInfo, error) {
36+
info, err := agents.Start("Counter")
37+
if err != nil {
38+
return agents.AgentInfo{}, err
39+
}
40+
41+
err = UpdateCountAsync(info.Id, initialCount)
42+
return info, err
43+
}
44+
3345
// Stops the specified agent by ID, returning its status info.
3446
// This will terminate the agent, and it cannot be resumed or restarted.
3547
// However, a new agent with the same name can be started at any time.

0 commit comments

Comments
 (0)