@@ -2,20 +2,16 @@ package application
22
33import (
44 "context"
5+ "math/rand/v2"
56 "sync"
67 "sync/atomic"
78 "time"
89
910 "github.com/mudler/LocalAI/core/config"
1011 mcpTools "github.com/mudler/LocalAI/core/http/endpoints/mcp"
11- "github.com/mudler/LocalAI/core/services/agents"
1212 "github.com/mudler/LocalAI/core/services/agentpool"
1313 "github.com/mudler/LocalAI/core/services/galleryop"
14- "github.com/mudler/LocalAI/core/services/distributed"
15- "github.com/mudler/LocalAI/core/services/jobs"
16- "github.com/mudler/LocalAI/core/services/messaging"
1714 "github.com/mudler/LocalAI/core/services/nodes"
18- "github.com/mudler/LocalAI/core/services/storage"
1915 "github.com/mudler/LocalAI/core/templates"
2016 "github.com/mudler/LocalAI/pkg/model"
2117 "github.com/mudler/xlog"
@@ -40,17 +36,7 @@ type Application struct {
4036 agentJobMutex sync.Mutex
4137
4238 // Distributed mode services (nil when not in distributed mode)
43- natsClient * messaging.Client
44- objectStore storage.ObjectStore
45- nodeRegistry * nodes.NodeRegistry
46- smartRouter * nodes.SmartRouter
47- healthMon * nodes.HealthMonitor
48- jobStore * jobs.JobStore
49- jobDispatcher * jobs.Dispatcher
50- agentStore * agents.AgentStore
51- agentBridge * agents.EventBridge
52- distStores * distributed.Stores
53- fileManager * storage.FileManager
39+ distributed * DistributedServices
5440}
5541
5642func newApplication (appConfig * config.ApplicationConfig ) * Application {
@@ -107,77 +93,27 @@ func (a *Application) StartupConfig() *config.ApplicationConfig {
10793 return a .startupConfig
10894}
10995
110- // NatsClient returns the NATS messaging client, or nil if not in distributed mode.
111- func (a * Application ) NatsClient () * messaging.Client {
112- return a .natsClient
113- }
114-
115- // ObjectStore returns the object storage backend, or nil if not in distributed mode.
116- func (a * Application ) ObjectStore () storage.ObjectStore {
117- return a .objectStore
118- }
119-
120- // NodeRegistry returns the node registry, or nil if not in distributed mode.
121- func (a * Application ) NodeRegistry () * nodes.NodeRegistry {
122- return a .nodeRegistry
123- }
124-
125- // SmartRouter returns the smart router, or nil if not in distributed mode.
126- func (a * Application ) SmartRouter () * nodes.SmartRouter {
127- return a .smartRouter
128- }
129-
130- // JobStore returns the distributed job store, or nil if not in distributed mode.
131- func (a * Application ) JobStore () * jobs.JobStore {
132- return a .jobStore
133- }
134-
135- // JobDispatcher returns the distributed job dispatcher, or nil if not in distributed mode.
136- func (a * Application ) JobDispatcher () * jobs.Dispatcher {
137- return a .jobDispatcher
138- }
139-
140- // AgentStore returns the distributed agent store, or nil if not in distributed mode.
141- func (a * Application ) AgentStore () * agents.AgentStore {
142- return a .agentStore
143- }
144-
145- // AgentEventBridge returns the agent event bridge, or nil if not in distributed mode.
146- func (a * Application ) AgentEventBridge () * agents.EventBridge {
147- return a .agentBridge
148- }
149-
150- // DistributedStores returns the Phase 4 distributed stores, or nil if not in distributed mode.
151- func (a * Application ) DistributedStores () * distributed.Stores {
152- return a .distStores
153- }
154-
155- // FileManager returns the file manager for object storage, or nil if not in distributed mode.
156- func (a * Application ) FileManager () * storage.FileManager {
157- return a .fileManager
158- }
159-
160- // HealthMonitor returns the health monitor, or nil if not in distributed mode.
161- func (a * Application ) HealthMonitor () * nodes.HealthMonitor {
162- return a .healthMon
96+ // Distributed returns the distributed services, or nil if not in distributed mode.
97+ func (a * Application ) Distributed () * DistributedServices {
98+ return a .distributed
16399}
164100
165101// IsDistributed returns true if the application is running in distributed mode.
166102func (a * Application ) IsDistributed () bool {
167- return a .applicationConfig . Distributed . Enabled
103+ return a .distributed != nil
168104}
169105
170106// waitForHealthyWorker blocks until at least one healthy backend worker is registered.
171107// This prevents the agent pool from failing during startup when workers haven't connected yet.
172108func (a * Application ) waitForHealthyWorker () {
173109 maxWait := a .applicationConfig .Distributed .WorkerWaitTimeoutOrDefault ()
174- const pollInterval = 2 * time .Second
110+ const basePoll = 2 * time .Second
175111
176112 xlog .Info ("Waiting for at least one healthy backend worker before starting agent pool" )
177113 deadline := time .Now ().Add (maxWait )
178114
179115 for time .Now ().Before (deadline ) {
180- registered , err := a .nodeRegistry .List ()
116+ registered , err := a .distributed . Registry .List ()
181117 if err == nil {
182118 for _ , n := range registered {
183119 if n .NodeType == nodes .NodeTypeBackend && n .Status == nodes .StatusHealthy {
@@ -186,10 +122,12 @@ func (a *Application) waitForHealthyWorker() {
186122 }
187123 }
188124 }
125+ // Add 0-1s jitter to prevent thundering-herd on the node registry
126+ jitter := time .Duration (rand .Int64N (int64 (time .Second )))
189127 select {
190128 case <- a .applicationConfig .Context .Done ():
191129 return
192- case <- time .After (pollInterval ):
130+ case <- time .After (basePoll + jitter ):
193131 }
194132 }
195133 xlog .Warn ("No healthy backend worker found after waiting, proceeding anyway" )
@@ -237,24 +175,23 @@ func (a *Application) StartAgentPool() {
237175 if a .authDB != nil {
238176 aps .SetAuthDB (a .authDB )
239177 }
240- if a .distStores != nil && a .distStores .Skills != nil {
241- aps .SetSkillStore (a .distStores .Skills )
242- }
243178 // Wire distributed mode components
244- if a .natsClient != nil {
245- aps .SetNATSClient (a .natsClient )
246- }
247- if a .agentBridge != nil {
248- aps .SetEventBridge (a .agentBridge )
249- }
250- if a .agentStore != nil {
251- aps .SetAgentStore (a .agentStore )
252- }
253- // In distributed mode, wait for at least one healthy backend worker before
254- // starting the agent pool. Collections initialization calls embeddings which
255- // require a worker to be registered and subscribed to NATS.
256- if a .IsDistributed () && a .nodeRegistry != nil {
257- a .waitForHealthyWorker ()
179+ if d := a .Distributed (); d != nil {
180+ if d .DistStores != nil && d .DistStores .Skills != nil {
181+ aps .SetSkillStore (d .DistStores .Skills )
182+ }
183+ aps .SetNATSClient (d .Nats )
184+ if d .AgentBridge != nil {
185+ aps .SetEventBridge (d .AgentBridge )
186+ }
187+ if d .AgentStore != nil {
188+ aps .SetAgentStore (d .AgentStore )
189+ }
190+ // Wait for at least one healthy backend worker before starting the agent pool.
191+ // Collections initialization calls embeddings which require a worker.
192+ if d .Registry != nil {
193+ a .waitForHealthyWorker ()
194+ }
258195 }
259196
260197 if err := aps .Start (a .applicationConfig .Context ); err != nil {
0 commit comments