This commit is contained in:
2026-09-23 17:26:13 +05:30
parent 8e1549764b
commit 697b77f8c1
54 changed files with 5750 additions and 69 deletions

View File

@@ -1,6 +1,8 @@
package facade
import (
"log"
"nearle/controllers"
"nearle/repositories"
"nearle/services"
@@ -26,6 +28,7 @@ type Facade struct {
CatalogueUploadController *controllers.CatalogueUploadController
ScanController *controllers.ScanController
AssistantController *controllers.AssistantController
MCPController *controllers.MCPController
// Tools is what Nearle Buddy is allowed to do.
//
@@ -44,7 +47,7 @@ type Facade struct {
// it may be nil if catalogue env vars are not configured, in which case
// catalogue endpoints will error at query time rather than at startup.
// embedder may be nil too: scan-to-order then matches on words alone.
func NewFacade(db *gorm.DB, catalogueDB *gorm.DB, embedder utils.Embedder, chat utils.Chat) *Facade {
func NewFacade(db *gorm.DB, catalogueDB *gorm.DB, embedder utils.Embedder, chat utils.Chat, agentsDir string) *Facade {
// User Module
userRepo := repositories.NewUserRepository(db)
@@ -135,9 +138,27 @@ func NewFacade(db *gorm.DB, catalogueDB *gorm.DB, embedder utils.Embedder, chat
// a tool with no description is a programming mistake, and a server that
// starts with a tool silently absent answers real questions with "I cannot
// do that" for a reason nobody can see from the outside.
toolRegistry := tools.New(tools.LogAudit{})
// The help corpus, checked before it is registered. A passage carrying one
// shop's figures stops the server rather than reaching another shop's screen.
helpCorpus, err := tools.LoadHelp()
if err != nil {
panic("assistant help: " + err.Error())
}
// The audit trail goes to the database and to the log. See
// services/assistantAudit.go for why both.
auditRepo := repositories.NewAssistantAuditRepository(db)
toolRegistry := tools.New(services.NewDBAudit(auditRepo))
for _, tool := range []tools.Tool{
tools.StuckOrders(deliveriesService, nil),
tools.DeliveryProgress(deliveriesService),
tools.BranchPerformance(orderService),
tools.PendingApprovals(stockRequestService, nil),
tools.LowStock(productService),
tools.TillsNotSyncing(posService),
tools.SalesByChannel(orderService, posService, nil),
tools.Help(helpCorpus),
tools.ApproveStockRequest(stockRequestService, stockRequestService),
} {
if err := toolRegistry.Register(tool); err != nil {
panic("assistant tools: " + err.Error())
@@ -149,9 +170,24 @@ func NewFacade(db *gorm.DB, catalogueDB *gorm.DB, embedder utils.Embedder, chat
// switched on here" rather than a 500. The tools themselves are ordinary
// Go functions and work either way; only turning a sentence into a tool
// call needs a model.
assistantService := services.NewAssistantService(toolRegistry, chat, nil)
// Agent definitions, validated against the registry above. A typo in a tool
// name stops the server rather than producing an agent that quietly cannot
// do one of the things it claims — which is invisible at runtime, because the
// model simply reports it could not look something up.
agents, err := services.LoadAgents(agentsDir, toolRegistry.Has)
if err != nil {
panic("assistant agents: " + err.Error())
}
log.Printf("assistant: %d agents loaded %v", len(agents), services.AgentNames(agents))
assistantService := services.NewAssistantService(toolRegistry, chat, agents)
assistantController := controllers.NewAssistantController(assistantService)
// The second door. Same registry, same agents, same session — see
// controllers/mcpController.go for why it is a door rather than a service.
mcpController := controllers.NewMCPController(toolRegistry, agents)
return &Facade{
UserController: userController,
ProductController: productController,
@@ -168,6 +204,7 @@ func NewFacade(db *gorm.DB, catalogueDB *gorm.DB, embedder utils.Embedder, chat
CatalogueUploadController: catalogueUploadController,
ScanController: scanController,
AssistantController: assistantController,
MCPController: mcpController,
Tools: toolRegistry,
posService: posService,
}