Skip to content
This repository was archived by the owner on Sep 11, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .trunk/configs/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"getsentry",
"gitinfo",
"gjson",
"goakt",
"goarch",
"GOBIN",
"goccy",
Expand Down Expand Up @@ -139,6 +140,7 @@
"openai",
"openspeech",
"operationreport",
"passivation",
"PEMS",
"pgconn",
"pgxpool",
Expand Down Expand Up @@ -179,6 +181,7 @@
"textgeneration",
"tidwall",
"tinygo",
"tochemey",
"toolcalling",
"tseslint",
"tsrv",
Expand Down
4 changes: 4 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@
"type": "pickString",
"description": "Choose a Modus app",
"options": [
{
"label": "Agents Example",
"value": "agents"
},
{
"label": "Anthropic Model Example",
"value": "anthropic-functions"
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

# Change Log

## UNRELEASED

- feat: initial implementation of Modus Agents [#840](https://github.com/hypermodeinc/modus/pull/840)

## 2025-05-06 - CLI 0.17.4

- fix: fully install go sdk in dev mode [#837](https://github.com/hypermodeinc/modus/pull/837)
Expand Down
1 change: 1 addition & 0 deletions go.work
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use (
./runtime
./runtime/languages/golang/testdata
./sdk/go
./sdk/go/examples/agents
./sdk/go/examples/anthropic-functions
./sdk/go/examples/auth
./sdk/go/examples/classification
Expand Down
116 changes: 116 additions & 0 deletions runtime/actors/actorlogger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* Copyright 2025 Hypermode Inc.
* Licensed under the terms of the Apache License, Version 2.0
* See the LICENSE file that accompanied this code for further details.
*
* SPDX-FileCopyrightText: 2025 Hypermode Inc. <[email protected]>
* SPDX-License-Identifier: Apache-2.0
*/

package actors

import (
"fmt"
"io"
"log"

"github.com/hypermodeinc/modus/runtime/logger"
"github.com/hypermodeinc/modus/runtime/utils"
"github.com/rs/zerolog"
actorLog "github.com/tochemey/goakt/v3/log"
)

func newActorLogger(logger *zerolog.Logger) *actorLogger {

var minLevel zerolog.Level
if utils.EnvVarFlagEnabled("MODUS_DEBUG_ACTORS") {
minLevel = zerolog.DebugLevel
} else {
// goakt info level is too noisy, so default to show only errors
minLevel = zerolog.ErrorLevel
}

l := logger.Level(minLevel).With().Str("component", "actors").Logger()
return &actorLogger{&l}
}

type actorLogger struct {
logger *zerolog.Logger
}

func (al *actorLogger) Debug(v ...any) {
al.logger.Debug().Msg(fmt.Sprint(v...))
}

func (al *actorLogger) Debugf(format string, v ...any) {
al.logger.Debug().Msgf(format, v...)
}

func (al *actorLogger) Info(v ...any) {
al.logger.Info().Msg(fmt.Sprint(v...))
}

func (al *actorLogger) Infof(format string, v ...any) {
al.logger.Info().Msgf(format, v...)
}

func (al *actorLogger) Warn(v ...any) {
al.logger.Warn().Msg(fmt.Sprint(v...))
}

func (al *actorLogger) Warnf(format string, v ...any) {
al.logger.Warn().Msgf(format, v...)
}

func (al *actorLogger) Error(v ...any) {
al.logger.Error().Msg(fmt.Sprint(v...))
}

func (al *actorLogger) Errorf(format string, v ...any) {
al.logger.Error().Msgf(format, v...)
}

func (al *actorLogger) Fatal(v ...any) {
al.logger.Fatal().Msg(fmt.Sprint(v...))
}

func (al *actorLogger) Fatalf(format string, v ...any) {
al.logger.Fatal().Msgf(format, v...)
}

func (al *actorLogger) Panic(v ...any) {
al.logger.Panic().Msg(fmt.Sprint(v...))
}

func (al *actorLogger) Panicf(format string, v ...any) {
al.logger.Panic().Msgf(format, v...)
}

func (al *actorLogger) LogLevel() actorLog.Level {
switch al.logger.GetLevel() {
case zerolog.DebugLevel:
return actorLog.DebugLevel
case zerolog.InfoLevel:
return actorLog.InfoLevel
case zerolog.WarnLevel:
return actorLog.WarningLevel
case zerolog.ErrorLevel:
return actorLog.ErrorLevel
case zerolog.FatalLevel:
return actorLog.FatalLevel
case zerolog.PanicLevel:
return actorLog.PanicLevel
default:
return actorLog.InvalidLevel
}
}

func (al *actorLogger) LogOutput() []io.Writer {
w := logger.NewLogWriter(al.logger, al.logger.GetLevel())
return []io.Writer{w}
}

func (al *actorLogger) StdLogger() *log.Logger {
w := logger.NewLogWriter(al.logger, al.logger.GetLevel())
return log.New(w, "", 0)
}
78 changes: 78 additions & 0 deletions runtime/actors/actorsystem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2025 Hypermode Inc.
* Licensed under the terms of the Apache License, Version 2.0
* See the LICENSE file that accompanied this code for further details.
*
* SPDX-FileCopyrightText: 2025 Hypermode Inc. <[email protected]>
* SPDX-License-Identifier: Apache-2.0
*/

package actors

import (
"context"
"time"

"github.com/hypermodeinc/modus/runtime/logger"
"github.com/hypermodeinc/modus/runtime/pluginmanager"
"github.com/hypermodeinc/modus/runtime/plugins"

goakt "github.com/tochemey/goakt/v3/actor"
)

var _actorSystem goakt.ActorSystem

func Initialize(ctx context.Context) {

actorLogger := newActorLogger(logger.Get(ctx))

actorSystem, err := goakt.NewActorSystem("modus",
goakt.WithLogger(actorLogger),
goakt.WithCoordinatedShutdown(beforeShutdown),
goakt.WithPassivationDisabled(), // TODO: enable passivation. Requires a persistence store in production for agent state.
goakt.WithActorInitTimeout(10*time.Second), // TODO: adjust this value, or make it configurable
goakt.WithActorInitMaxRetries(1)) // TODO: adjust this value, or make it configurable

if err != nil {
logger.Fatal(ctx).Err(err).Msg("Failed to create actor system.")
}

if err := actorSystem.Start(ctx); err != nil {
logger.Fatal(ctx).Err(err).Msg("Failed to start actor system.")
}

_actorSystem = actorSystem

logger.Info(ctx).Msg("Actor system started.")

pluginmanager.RegisterPluginLoadedCallback(reloadAgentActors)
}

func reloadAgentActors(ctx context.Context, plugin *plugins.Plugin) error {
for _, pid := range _actorSystem.Actors() {
if actor, ok := pid.Actor().(*WasmAgentActor); ok {
if err := actor.reloadModule(ctx, plugin); err != nil {
return err
}
}
}

return nil
}

func beforeShutdown(ctx context.Context) error {
logger.Info(ctx).Msg("Actor system shutting down...")
return nil
}

func Shutdown(ctx context.Context) {
if _actorSystem == nil {
return
}

if err := _actorSystem.Stop(ctx); err != nil {
logger.Err(ctx, err).Msg("Failed to shutdown actor system.")
}

logger.Info(ctx).Msg("Actor system shutdown complete.")
}
Loading
Loading