@@ -3,274 +3,69 @@ package main
33import (
44 "context"
55 "fmt"
6- "net"
7- "net/http"
8- "strings"
9- "time"
106
11- "github.com/gin-gonic/gin"
127 "github.com/insmtx/SingerOS/backend/config"
13- "github.com/insmtx/SingerOS/backend/internal/agent"
14- "github.com/insmtx/SingerOS/backend/internal/agent/externalcli"
15- "github.com/insmtx/SingerOS/backend/internal/eventengine"
16- "github.com/insmtx/SingerOS/backend/internal/infra/mq"
17- singerMCP "github.com/insmtx/SingerOS/backend/mcp"
18- "github.com/insmtx/SingerOS/backend/runtime/engines"
19- "github.com/insmtx/SingerOS/backend/runtime/engines/builtin"
20- "github.com/insmtx/SingerOS/backend/tools"
21- skilltools "github.com/insmtx/SingerOS/backend/tools/skill"
8+ "github.com/insmtx/SingerOS/backend/internal/worker/client"
229 "github.com/spf13/cobra"
23- "github.com/ygpkg/yg-go/lifecycle "
10+ ygconfig "github.com/ygpkg/yg-go/config "
2411 "github.com/ygpkg/yg-go/logs"
2512)
2613
2714var (
28- workerConfigPath string
29- workerServerAddr string
15+ workerConfigPath string
16+ workerServerAddr string
17+ workerAssistantCode string
3018)
3119
3220var workerCmd = & cobra.Command {
3321 Use : "worker" ,
3422 Short : "Start the SingerOS background worker" ,
3523 Long : `Start the background worker service for processing asynchronous tasks and events.` ,
3624 Run : func (cmd * cobra.Command , args []string ) {
37- mcpServer , err := startWorkerMCPServer (workerServerAddr )
38- if err != nil {
39- logs .Fatalf ("Failed to start worker MCP server: %v" , err )
40- return
41- }
25+ ctx := cmd .Context ()
4226
43- cfg , err := loadWorkerConfig ( workerConfigPath , workerServerAddr )
27+ worker , err := createWorker ( ctx )
4428 if err != nil {
45- logs .Fatalf ("Failed to load config : %v" , err )
29+ logs .Fatalf ("Failed to create worker : %v" , err )
4630 return
4731 }
4832
49- natsUrl := "nats://nats:4222"
50- if cfg .NATS != nil && cfg .NATS .URL != "" {
51- natsUrl = cfg .NATS .URL
52- }
53-
54- subscriber , err := mq .NewPublisher (natsUrl )
55- if err != nil {
56- logs .Fatalf ("Failed to create event subscriber: %v" , err )
57- return
33+ if err := worker .Start (ctx ); err != nil {
34+ logs .Fatalf ("Failed to start worker: %v" , err )
5835 }
59-
60- runtimeConfig , err := buildRuntimeConfig ()
61- if err != nil {
62- logs .Fatalf ("Failed to build runtime config: %v" , err )
63- return
64- }
65-
66- ctx , cancel := context .WithCancel (context .Background ())
67- runner , err := buildRuntimeRunner (ctx , cfg , runtimeConfig )
68- if err != nil {
69- cancel ()
70- logs .Fatalf ("Failed to create agent runtime: %v" , err )
71- return
72- }
73-
74- orchestratorInstance := eventengine .NewOrchestrator (subscriber , runner )
75- if err := orchestratorInstance .Start (ctx ); err != nil {
76- cancel ()
77- logs .Fatalf ("Failed to start orchestrator: %v" , err )
78- return
79- }
80- logs .Info ("Orchestrator started successfully" )
81-
82- lifecycle .Std ().AddCloseFunc (func () error {
83- cancel ()
84- return nil
85- })
86- lifecycle .Std ().AddCloseFunc (func () error {
87- shutdownCtx , shutdownCancel := context .WithTimeout (context .Background (), 5 * time .Second )
88- defer shutdownCancel ()
89- return mcpServer .Shutdown (shutdownCtx )
90- })
91- lifecycle .Std ().AddCloseFunc (subscriber .Close )
92-
93- logs .Info ("Worker runtime initialized successfully" )
94- logs .Info ("Worker service started" )
95-
96- lifecycle .Std ().WaitExit ()
97-
98- logs .Info ("Worker exited" )
9936 },
10037}
10138
10239func init () {
10340 workerCmd .Flags ().StringVar (& workerConfigPath , "config" , "" , "Configuration file path" )
104- workerCmd .Flags ().StringVar (& workerServerAddr , "server-addr" , ":8081" , "Worker MCP server listen address for runtime CLI bootstrap" )
41+ workerCmd .Flags ().StringVar (& workerServerAddr , "server-addr" , ":8080" , "Server address for WebSocket connection" )
42+ workerCmd .Flags ().StringVar (& workerAssistantCode , "assistant-code" , "" , "Assistant code for configuration retrieval" )
10543 rootCmd .AddCommand (workerCmd )
10644}
10745
108- func startWorkerMCPServer (addr string ) (* http.Server , error ) {
109- if strings .TrimSpace (addr ) == "" {
110- addr = ":8081"
111- }
112-
113- listener , err := net .Listen ("tcp" , addr )
46+ func createWorker (ctx context.Context ) (* client.Worker , error ) {
47+ _ , err := loadWorkerConfig ()
11448 if err != nil {
115- return nil , fmt .Errorf ("listen on %s: %w" , addr , err )
116- }
117-
118- r := gin .New ()
119- v1 := r .Group ("/v1" )
120- singerMCP .RegisterRoutes (v1 , singerMCP .NewServer ())
121-
122- server := & http.Server {
123- Addr : addr ,
124- Handler : r ,
125- }
126-
127- go func () {
128- logs .Infof ("Worker MCP server listening on %s" , listener .Addr ().String ())
129- if err := server .Serve (listener ); err != nil && err != http .ErrServerClosed {
130- logs .Errorf ("Worker MCP server stopped unexpectedly: %v" , err )
131- }
132- }()
133-
134- return server , nil
135- }
136-
137- func loadWorkerConfig (configPath string , bootstrapAddr string ) (* config.Config , error ) {
138- cfg , err := loadConfig (configPath )
139- if err != nil {
140- return nil , err
141- }
142-
143- bootstrapped , err := builtin .BootstrapCLIEngines (context .Background (), cfg .CLI , defaultCLIBootstrapOptions (bootstrapAddr ))
144- if err != nil {
145- logs .Warnf ("CLI bootstrap failed: %v" , err )
146- }
147- if bootstrapped != nil {
148- cfg .CLI = bootstrapped
49+ return nil , fmt .Errorf ("load config: %w" , err )
14950 }
15051
151- return cfg , nil
52+ return client .NewWorker (ctx , & client.WorkerConfig {
53+ ServerAddr : workerServerAddr ,
54+ AssistantCode : workerAssistantCode ,
55+ SkillsDir : "" ,
56+ ToolsEnabled : true ,
57+ })
15258}
15359
154- func defaultCLIBootstrapOptions (addr string ) builtin.BootstrapOptions {
155- return builtin.BootstrapOptions {
156- MCP : engines.MCPServerConfig {
157- Name : "singeros" ,
158- URL : mcpURLFromAddr (addr ),
159- BearerToken : singerMCP .DefaultAuthToken (),
160- },
161- }
162- }
163-
164- func mcpURLFromAddr (addr string ) string {
165- host := "localhost"
166- port := "8081"
167-
168- if strings .TrimSpace (addr ) != "" {
169- if splitHost , splitPort , err := net .SplitHostPort (addr ); err == nil {
170- if splitHost != "" && splitHost != "0.0.0.0" && splitHost != "::" && splitHost != "[::]" {
171- host = splitHost
172- }
173- if splitPort != "" {
174- port = splitPort
175- }
176- } else if strings .HasPrefix (addr , ":" ) {
177- port = strings .TrimPrefix (addr , ":" )
178- } else {
179- host = addr
180- }
181- }
182-
183- return fmt .Sprintf ("http://%s:%s/v1/mcp" , host , port )
184- }
185-
186- func buildRuntimeConfig () (agent.Config , error ) {
187- catalog , skillDir , err := skilltools .LoadDefaultCatalog ()
188- if err != nil {
189- return agent.Config {}, fmt .Errorf ("load skills: %w" , err )
190- }
191-
192- logs .Infof ("Loaded %d skills from %s for runtime" , len (catalog .List ()), skillDir )
193-
194- toolRegistry , err := buildTooling (catalog )
195- if err != nil {
196- return agent.Config {}, err
197- }
198-
199- return agent.Config {
200- SkillsCatalog : catalog ,
201- ToolRegistry : toolRegistry ,
202- }, nil
203- }
204-
205- func buildTooling (catalog * skilltools.Catalog ) (* tools.Registry , error ) {
206- registry := tools .NewRegistry ()
207-
208- if err := skilltools .Register (registry , catalog ); err != nil {
209- return nil , fmt .Errorf ("register skill use tool: %w" , err )
210- }
211-
212- logs .Infof ("Loaded %d tools for runtime" , len (registry .List ()))
213-
214- return registry , nil
215- }
216-
217- func buildRuntimeRunner (ctx context.Context , cfg * config.Config , runtimeConfig agent.Config ) (agent.Runner , error ) {
218- if cfg == nil {
219- return nil , fmt .Errorf ("config is required" )
220- }
221-
222- router := agent .NewRuntimeRouter (agent .RuntimeKindSingerOS )
223- registered := 0
224-
225- if cfg .LLM != nil && cfg .LLM .APIKey != "" {
226- switch cfg .LLM .Provider {
227- case "" , "openai" :
228- logs .Info ("Registering SingerOS agent runtime" )
229- singerRunner , err := agent .NewAgent (ctx , cfg .LLM , runtimeConfig )
230- if err != nil {
231- return nil , err
232- }
233- if err := router .Register (agent .RuntimeKindSingerOS , singerRunner ); err != nil {
234- return nil , err
235- }
236- registered ++
237- default :
238- logs .Warnf ("Skipping SingerOS agent runtime for unsupported Eino chat model provider: %s" , cfg .LLM .Provider )
239- }
240- }
241-
242- cliRegistry , err := builtin .NewRegistryFromConfig (cfg .CLI )
243- if err != nil {
244- return nil , fmt .Errorf ("create CLI engine registry: %w" , err )
245- }
246- cliNames := cliRegistry .Names ()
247- for _ , name := range cliNames {
248- engine , ok := cliRegistry .Get (name )
249- if ! ok {
250- continue
251- }
252- runner , err := externalcli .NewRunner (name , engine , cfg .LLM )
60+ func loadWorkerConfig () (* config.Config , error ) {
61+ if workerConfigPath != "" {
62+ cfg := & config.Config {}
63+ err := ygconfig .LoadYamlLocalFile (workerConfigPath , cfg )
25364 if err != nil {
254- return nil , err
65+ return nil , fmt . Errorf ( "failed to load config from %s: %w" , workerConfigPath , err )
25566 }
256- if err := router .Register (name , runner ); err != nil {
257- return nil , err
258- }
259- registered ++
260- logs .Infof ("Registering external agent CLI runtime: %s" , name )
67+ return cfg , nil
26168 }
26269
263- if registered == 0 {
264- return nil , fmt .Errorf ("no agent runtime is available" )
265- }
266- if cfg .LLM == nil || cfg .LLM .APIKey == "" {
267- if cfg .CLI != nil && cfg .CLI .Default != "" {
268- router .SetDefault (cfg .CLI .Default )
269- } else if len (cliNames ) > 0 {
270- router .SetDefault (cliNames [0 ])
271- }
272- } else {
273- router .SetDefault (agent .RuntimeKindSingerOS )
274- }
275- return router , nil
70+ return & config.Config {}, nil
27671}
0 commit comments