From f74e4d1043d6c53a7db7196559ccf231e8ab182e Mon Sep 17 00:00:00 2001 From: Fabien Masson Date: Mon, 7 Sep 2026 21:04:48 +0200 Subject: [PATCH] =?UTF-8?q?Agent=20par=20d=C3=A9faut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/cmd/serve.go | 45 +++++++++++++- .../migrations/000001_create_tables.up.sql | 2 +- .../adapter/database/turso/user_repository.go | 2 +- .../adapter/llm/{llm.go => anyllm.go} | 45 ++++++++++++++ backend/internal/config/config.go | 7 +++ backend/internal/domain/llm.go | 41 +++++++++++++ backend/internal/domain/user.go | 2 +- backend/internal/http/handlers/agent.go | 21 ++++++- backend/internal/http/handlers/team.go | 55 +++++++++++++++++ backend/internal/http/router.go | 22 +++++-- backend/internal/llmserver/server.go | 58 ++++++++++++++++++ backend/internal/llmserver/session.go | 5 ++ backend/internal/service/user/service.go | 21 +++++++ backend/lassistanoque.db-wal | Bin 412032 -> 12392 bytes backend/web/src/lib/api/endpoints/agent.ts | 2 +- backend/web/src/lib/api/endpoints/team.ts | 7 +++ backend/web/src/lib/components/Menu.svelte | 10 ++- backend/web/src/lib/components/Sidebar.svelte | 2 +- backend/web/src/lib/types/api.ts | 2 +- .../[space]/settings/agents/+page.svelte | 31 ++++++++-- .../[space]/settings/agents/+page.ts | 6 +- .../[space]/settings/models/+page.svelte | 28 +++++---- .../[space]/settings/models/+page.ts | 1 + backend/web/src/routes/layout.css | 7 ++- 24 files changed, 385 insertions(+), 37 deletions(-) rename backend/internal/adapter/llm/{llm.go => anyllm.go} (67%) create mode 100644 backend/internal/http/handlers/team.go create mode 100644 backend/internal/llmserver/server.go create mode 100644 backend/internal/llmserver/session.go create mode 100644 backend/web/src/lib/api/endpoints/team.ts diff --git a/backend/cmd/serve.go b/backend/cmd/serve.go index 0ad6614..159ce21 100644 --- a/backend/cmd/serve.go +++ b/backend/cmd/serve.go @@ -1,7 +1,13 @@ package cmd import ( + "context" + "errors" "log/slog" + nethttp "net/http" + "os" + "os/signal" + "syscall" "time" "github.com/spf13/cobra" @@ -11,6 +17,7 @@ import ( "trankilou.fr/lassistanoque/backend/internal/adapter/llm" "trankilou.fr/lassistanoque/backend/internal/adapter/security" "trankilou.fr/lassistanoque/backend/internal/http" + "trankilou.fr/lassistanoque/backend/internal/llmserver" "trankilou.fr/lassistanoque/backend/internal/service/agent" "trankilou.fr/lassistanoque/backend/internal/service/auth" "trankilou.fr/lassistanoque/backend/internal/service/provider" @@ -84,7 +91,41 @@ func runServe() { AgentService: agentService, }) - if err := router.Start(); err != nil { - slog.Error("failed to start server", "error", err) + llms := llmserver.NewLLMServer(llmserver.Dependencies{}) + + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer stop() + + // start LLM service + go func() { + if err := llms.Start(); err != nil && !errors.Is(err, nethttp.ErrServerClosed) { + slog.Error("failed to start llm server", "error", err) + } + }() + + // start HTTP service + go func() { + if err := router.Start(); err != nil && !errors.Is(err, nethttp.ErrServerClosed) { + slog.Error("failed to start http server", "error", err) + } + }() + + <-ctx.Done() + + // stop HTTP service + ctx, cancel1 := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel1() + if err := router.Stop(ctx); err != nil { + slog.Error("failed to stop http server", "error", err) } + slog.Info("HTTP server stopped gracefully") + + // stop LLM service + ctx, cancel2 := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel2() + if err := llms.Stop(ctx); err != nil { + slog.Error("failed to stop llm server", "error", err) + } + slog.Info("LLM server stopped gracefully") + } diff --git a/backend/internal/adapter/database/turso/migrations/000001_create_tables.up.sql b/backend/internal/adapter/database/turso/migrations/000001_create_tables.up.sql index 73f1605..9abab00 100644 --- a/backend/internal/adapter/database/turso/migrations/000001_create_tables.up.sql +++ b/backend/internal/adapter/database/turso/migrations/000001_create_tables.up.sql @@ -63,7 +63,7 @@ create table user_addresses ( create table teams ( id text not null primary key, label text not null, - default_model_id text, + default_agent_id text, _date_created numeric not null default current_timestamp, _date_updated numeric , _version text not null diff --git a/backend/internal/adapter/database/turso/user_repository.go b/backend/internal/adapter/database/turso/user_repository.go index c5a8149..bda158f 100644 --- a/backend/internal/adapter/database/turso/user_repository.go +++ b/backend/internal/adapter/database/turso/user_repository.go @@ -68,7 +68,7 @@ func (ur *TursoUserRepository) DeleteUser(id string) error { } func (ur *TursoUserRepository) FindTeam(userid string, teamid string) (*domain.Team, error) { - return ur.TeamTable.SelectOne("id=$1 and user_id=$2", teamid, userid) + return ur.TeamTable.SelectOne("id=$1 and id in (select team_id from user_teams where user_id=$2)", teamid, userid) } func (ur *TursoUserRepository) ListTeams(userid string) ([]*domain.Team, error) { diff --git a/backend/internal/adapter/llm/llm.go b/backend/internal/adapter/llm/anyllm.go similarity index 67% rename from backend/internal/adapter/llm/llm.go rename to backend/internal/adapter/llm/anyllm.go index 031244d..e1624a2 100644 --- a/backend/internal/adapter/llm/llm.go +++ b/backend/internal/adapter/llm/anyllm.go @@ -74,3 +74,48 @@ func (e *AnyLLMEngine) ListModelsFromProvider(ctx context.Context, provider *dom } return models, nil } + +func (e *AnyLLMEngine) Stream( + ctx context.Context, + provider *domain.Provider, + modelID string, + params *domain.StreamParams, + messages []*domain.Message, +) error { + + p, err := providerFactory(provider) + if err != nil { + return err + } + + anyllmMessages := make([]anyllm.Message, 0) + for _, m := range messages { + anyllmMessages = append(anyllmMessages, anyllm.Message{ + Role: m.Role, + Content: m.Content, + }) + } + + chunkChan, errChan := p.CompletionStream(ctx, anyllm.CompletionParams{ + Model: modelID, + Messages: anyllmMessages, + Stream: true, + }) + + for chunk := range chunkChan { + if len(chunk.Choices) > 0 { + content := chunk.Choices[0].Delta.Content + if params.OnChunk != nil { + params.OnChunk(&domain.Chunk{ + Content: content, + }) + } + } + } + + if err := <-errChan; err != nil { + return err + } + + return nil +} diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index cfd8a74..3360cf2 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -13,6 +13,7 @@ const ( DEFAULT_DB_URL = "lassistanoque.db" DEFAULT_HTTP_PORT = 3000 DEFAULT_STORAGE_TYPE = "database" + DEFAULT_SERVER_ID = "default" ) type Config struct { @@ -27,6 +28,7 @@ type Config struct { StorageS3AccessKeyID string // S3 StorageS3AccessKeySecret string // S3 StorageS3UseSSL string // S3 + ServerID string } var config *Config @@ -56,6 +58,10 @@ func GetConfig() *Config { if storageType == "" { storageType = DEFAULT_STORAGE_TYPE } + serverID := os.Getenv("LASSISTANOQUE_SERVER_ID") + if serverID == "" { + serverID = DEFAULT_SERVER_ID + } config = &Config{ DatabaseType: databaseType, @@ -63,6 +69,7 @@ func GetConfig() *Config { HttpPort: httpPort, JWTSecret: jwtsecret, StorageType: storageType, + ServerID: serverID, } } diff --git a/backend/internal/domain/llm.go b/backend/internal/domain/llm.go index 8561108..09587ed 100644 --- a/backend/internal/domain/llm.go +++ b/backend/internal/domain/llm.go @@ -20,7 +20,48 @@ type LLMModel struct { Detail *LLMModelDetail `json:"detail"` } +type Message struct { + Role string + Content string + ToolCalls []ToolCall + ToolCallID string + + //Reasoning *Reasoning +} + +type Tool struct { +} + +type ToolCall struct { +} + +type Chunk struct { + Content string +} + +type StreamParams struct { + Tools []*Tool + OnChunk func(chunk *Chunk) + OnDebug func() +} + +type Role string + +const ( + RoleSystem Role = "system" + RoleUser Role = "user" + RoleAssistant Role = "assistant" + RoleTool Role = "tool" +) + type LLMEngine interface { ListProviderTypes() []Item ListModelsFromProvider(ctx context.Context, provider *Provider) ([]string, error) + Stream( + ctx context.Context, + provider *Provider, + modelID string, + params *StreamParams, + messages []*Message, + ) error } diff --git a/backend/internal/domain/user.go b/backend/internal/domain/user.go index 2cb7933..3b94dd5 100644 --- a/backend/internal/domain/user.go +++ b/backend/internal/domain/user.go @@ -23,7 +23,7 @@ type User struct { type Team struct { ID string `db:"id" json:"id"` Label string `db:"label" json:"label"` - DefaultModelID *string `db:"default_model_id" json:"defaultModelId"` + DefaultAgentID *string `db:"default_agent_id" json:"defaultAgentId"` DateCreated time.Time `db:"_date_created" json:"_dateCreated"` DateUpdated *time.Time `db:"_date_updated" json:"_dateUpdated"` VersionId string `db:"_version" json:"_version"` diff --git a/backend/internal/http/handlers/agent.go b/backend/internal/http/handlers/agent.go index 3f8da44..e0fc9ab 100644 --- a/backend/internal/http/handlers/agent.go +++ b/backend/internal/http/handlers/agent.go @@ -7,11 +7,19 @@ import ( "trankilou.fr/lassistanoque/backend/internal/domain" "trankilou.fr/lassistanoque/backend/internal/service/agent" "trankilou.fr/lassistanoque/backend/internal/service/auth" + "trankilou.fr/lassistanoque/backend/internal/service/user" ) -func NewAgentGroup(prefix string, e *echo.Group, service *agent.Service, middlewares ...echo.MiddlewareFunc) *echo.Group { +func NewAgentGroup( + prefix string, + e *echo.Group, + agentService *agent.Service, + userService *user.Service, + middlewares ...echo.MiddlewareFunc) *echo.Group { + agentHandler := &AgentHandler{ - agentService: service, + agentService: agentService, + userService: userService, } agent := e.Group(prefix, middlewares...) @@ -26,6 +34,7 @@ func NewAgentGroup(prefix string, e *echo.Group, service *agent.Service, middlew type AgentHandler struct { agentService *agent.Service + userService *user.Service } func (h *AgentHandler) ListAgents(c *echo.Context) error { @@ -98,3 +107,11 @@ func (h *AgentHandler) DeleteAgent(c *echo.Context) error { } return c.JSON(http.StatusOK, agentID) } + +type DefaulAgentRequest struct { + AgentID string `json:"AgentId"` +} +type DefaulAgentResponse struct { + NewDefaultAgentID *string `json:"newDefaultAgentId"` + OldDefaultAgentID *string `json:"oldDefaultAgentId"` +} diff --git a/backend/internal/http/handlers/team.go b/backend/internal/http/handlers/team.go new file mode 100644 index 0000000..57fe2a7 --- /dev/null +++ b/backend/internal/http/handlers/team.go @@ -0,0 +1,55 @@ +package handlers + +import ( + "net/http" + + "github.com/labstack/echo/v5" + "trankilou.fr/lassistanoque/backend/internal/domain" + "trankilou.fr/lassistanoque/backend/internal/service/auth" + "trankilou.fr/lassistanoque/backend/internal/service/user" +) + +func NewTeamGroup(prefix string, e *echo.Group, service *user.Service, middlewares ...echo.MiddlewareFunc) *echo.Group { + teamHandler := &TeamHandler{ + userService: service, + } + h := e.Group(prefix, middlewares...) + h.GET("/:team", teamHandler.Get) + h.PUT("/:team", teamHandler.Update) + return h +} + +type TeamHandler struct { + userService *user.Service +} + +func (h TeamHandler) Get(c *echo.Context) error { + userID := c.Get(auth.ContextUserIDKey).(string) + teamID := c.Param("team") + user, err := h.userService.GetTeam(userID, teamID) + if err != nil { + return echo.NewHTTPError(http.StatusUnauthorized, err.Error()) + } + return c.JSON(http.StatusOK, user) +} + +func (h TeamHandler) Update(c *echo.Context) error { + userID := c.Get(auth.ContextUserIDKey).(string) + teamID := c.Param("team") + + var updTeam domain.Team + if err := c.Bind(&updTeam); err != nil { + return c.String(http.StatusBadRequest, "bad request") + } + + if teamID != updTeam.ID { + return c.String(http.StatusBadRequest, "bad request") + } + + user, err := h.userService.UpdateTeam(userID, &updTeam) + if err != nil { + return c.String(http.StatusBadRequest, err.Error()) + } + + return c.JSON(http.StatusOK, user) +} diff --git a/backend/internal/http/router.go b/backend/internal/http/router.go index 768bdae..e6c70e7 100644 --- a/backend/internal/http/router.go +++ b/backend/internal/http/router.go @@ -1,6 +1,7 @@ package http import ( + "context" "embed" "fmt" "io/fs" @@ -30,7 +31,7 @@ var indexhtml []byte var robotstxt []byte type Router struct { - echo *echo.Echo + server *http.Server } type Dependencies struct { @@ -44,6 +45,8 @@ type Dependencies struct { func NewRouter(deps Dependencies) *Router { + cfg := config.GetConfig() + e := echo.New() e.Use(middleware.RequestLogger()) e.Use(middleware.CORS("http://localhost:5173")) @@ -76,14 +79,23 @@ func NewRouter(deps Dependencies) *Router { _ = handlers.NewUserGroup("/user", api, deps.UserService, deps.TokenManager.TokenMiddleware) _ = handlers.NewMiscGroup("/misc", api, deps.TokenManager.TokenMiddleware) _ = handlers.NewModelGroup("/provider", api, deps.ProviderService, deps.TokenManager.TokenMiddleware) - _ = handlers.NewAgentGroup("/agent", api, deps.AgentService, deps.TokenManager.TokenMiddleware) + _ = handlers.NewAgentGroup("/agent", api, deps.AgentService, deps.UserService, deps.TokenManager.TokenMiddleware) + _ = handlers.NewTeamGroup("/team", api, deps.UserService, deps.TokenManager.TokenMiddleware) + + server := http.Server{ + Addr: fmt.Sprintf(":%d", cfg.HttpPort), + Handler: e, + } return &Router{ - echo: e, + &server, } } func (r *Router) Start() error { - cfg := config.GetConfig() - return r.echo.Start(fmt.Sprintf(":%d", cfg.HttpPort)) + return r.server.ListenAndServe() +} + +func (r *Router) Stop(ctx context.Context) error { + return r.server.Shutdown(ctx) } diff --git a/backend/internal/llmserver/server.go b/backend/internal/llmserver/server.go new file mode 100644 index 0000000..45ed046 --- /dev/null +++ b/backend/internal/llmserver/server.go @@ -0,0 +1,58 @@ +package llmserver + +import ( + "context" + "fmt" + "sync" + + "trankilou.fr/lassistanoque/backend/internal/utility" +) + +type Dependencies struct { +} + +type LLMServer struct { + started bool + mu sync.Mutex + sessions map[string]*Session // running sessions + newSessionChan chan string + closeChan chan bool +} + +func NewLLMServer(deps Dependencies) *LLMServer { + return &LLMServer{ + started: true, + sessions: make(map[string]*Session), + newSessionChan: make(chan string), + closeChan: make(chan bool), + } +} + +func (s *LLMServer) Start() error { + + for { + select { + case _ = <-s.closeChan: + fmt.Println("Closing LLM Server") + return nil + } + } + +} + +func (s *LLMServer) Stop(ctx context.Context) error { + s.closeChan <- true + s.started = false + return nil +} + +func (s *LLMServer) StartSession() (*Session, error) { + sid := utility.GenID() + session := &Session{} + + s.mu.Lock() + s.sessions[sid] = session + s.mu.Unlock() + + return session, nil +} diff --git a/backend/internal/llmserver/session.go b/backend/internal/llmserver/session.go new file mode 100644 index 0000000..4e9f363 --- /dev/null +++ b/backend/internal/llmserver/session.go @@ -0,0 +1,5 @@ +package llmserver + +type Session struct { + server string +} diff --git a/backend/internal/service/user/service.go b/backend/internal/service/user/service.go index 67cfb5c..e8d797b 100644 --- a/backend/internal/service/user/service.go +++ b/backend/internal/service/user/service.go @@ -105,3 +105,24 @@ func (s *Service) DeleteAddress(userId string, addressId string) error { } return s.repo.DeleteUserAddress(addressId) } + +func (s *Service) GetTeam(userid, id string) (*domain.Team, error) { + team, err := s.repo.FindTeam(userid, id) + if err != nil { + return nil, err + } + return team, nil +} + +func (s *Service) UpdateTeam(userid string, team *domain.Team) (*domain.Team, error) { + updTeam, err := s.repo.FindTeam(userid, team.ID) + if err != nil { + return nil, err + } + + updTeam.Label = team.Label + updTeam.DefaultAgentID = team.DefaultAgentID + + return s.repo.UpdateTeam(userid, updTeam) + +} diff --git a/backend/lassistanoque.db-wal b/backend/lassistanoque.db-wal index bba7ca41ec0211a14aee9e7e277eee7709b1511f..57f39fcbb0445197c72c502ac3b764584391a82e 100644 GIT binary patch delta 339 zcmZpeEcqhA!n~fXi9z>~1OtNr0|-Ps6#Kg2;U9*Rf1hMGJ(_y}D8z*2sbLp^g#LvsUj6H5!Ib74w=J~Fg2FtIW= n(z7(TG%>L-GcbcF$xks!H1qJS$`0|3$}b4Y4NDDhXLJPsn`UbP literal 412032 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 api.get(`/agent/${space}/${id}`), createAgent: (space: string,data: Partial) => api.post(`/agent/${space}`, data), updateAgent: (space: string,data: Partial) => api.put(`/agent/${space}`, data), - deleteAgent: (space: string,id: string) => api.delete(`/agent/${space}/${id}`), + deleteAgent: (space: string, id: string) => api.delete(`/agent/${space}/${id}`), }; diff --git a/backend/web/src/lib/api/endpoints/team.ts b/backend/web/src/lib/api/endpoints/team.ts new file mode 100644 index 0000000..4bf87a3 --- /dev/null +++ b/backend/web/src/lib/api/endpoints/team.ts @@ -0,0 +1,7 @@ +import { api } from '../client'; +import type { Team } from "$lib/types/api" + +export const teamApi = { + getSpace: (space: string) => api.get(`/team/${space}`), + updateSpace: (space: string,data: Partial) => api.put(`/team/${space}`, data), +}; diff --git a/backend/web/src/lib/components/Menu.svelte b/backend/web/src/lib/components/Menu.svelte index 80e208b..e9876ee 100644 --- a/backend/web/src/lib/components/Menu.svelte +++ b/backend/web/src/lib/components/Menu.svelte @@ -1,10 +1,18 @@ {/if}