Agent par défaut
This commit is contained in:
1 parent
cf10b9073b
commit
f74e4d1043
24 files changed
+385
-37
No files matched your search
+43
-2
@@ -1,7 +1,13 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
nethttp "net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -11,6 +17,7 @@ import (
|
|||||||
"trankilou.fr/lassistanoque/backend/internal/adapter/llm"
|
"trankilou.fr/lassistanoque/backend/internal/adapter/llm"
|
||||||
"trankilou.fr/lassistanoque/backend/internal/adapter/security"
|
"trankilou.fr/lassistanoque/backend/internal/adapter/security"
|
||||||
"trankilou.fr/lassistanoque/backend/internal/http"
|
"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/agent"
|
||||||
"trankilou.fr/lassistanoque/backend/internal/service/auth"
|
"trankilou.fr/lassistanoque/backend/internal/service/auth"
|
||||||
"trankilou.fr/lassistanoque/backend/internal/service/provider"
|
"trankilou.fr/lassistanoque/backend/internal/service/provider"
|
||||||
@@ -84,7 +91,41 @@ func runServe() {
|
|||||||
AgentService: agentService,
|
AgentService: agentService,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err := router.Start(); err != nil {
|
llms := llmserver.NewLLMServer(llmserver.Dependencies{})
|
||||||
slog.Error("failed to start server", "error", err)
|
|
||||||
|
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")
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -63,7 +63,7 @@ create table user_addresses (
|
|||||||
create table teams (
|
create table teams (
|
||||||
id text not null primary key,
|
id text not null primary key,
|
||||||
label text not null,
|
label text not null,
|
||||||
default_model_id text,
|
default_agent_id text,
|
||||||
_date_created numeric not null default current_timestamp,
|
_date_created numeric not null default current_timestamp,
|
||||||
_date_updated numeric ,
|
_date_updated numeric ,
|
||||||
_version text not null
|
_version text not null
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ func (ur *TursoUserRepository) DeleteUser(id string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ur *TursoUserRepository) FindTeam(userid string, teamid string) (*domain.Team, 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) {
|
func (ur *TursoUserRepository) ListTeams(userid string) ([]*domain.Team, error) {
|
||||||
|
|||||||
@@ -74,3 +74,48 @@ func (e *AnyLLMEngine) ListModelsFromProvider(ctx context.Context, provider *dom
|
|||||||
}
|
}
|
||||||
return models, nil
|
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
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ const (
|
|||||||
DEFAULT_DB_URL = "lassistanoque.db"
|
DEFAULT_DB_URL = "lassistanoque.db"
|
||||||
DEFAULT_HTTP_PORT = 3000
|
DEFAULT_HTTP_PORT = 3000
|
||||||
DEFAULT_STORAGE_TYPE = "database"
|
DEFAULT_STORAGE_TYPE = "database"
|
||||||
|
DEFAULT_SERVER_ID = "default"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@@ -27,6 +28,7 @@ type Config struct {
|
|||||||
StorageS3AccessKeyID string // S3
|
StorageS3AccessKeyID string // S3
|
||||||
StorageS3AccessKeySecret string // S3
|
StorageS3AccessKeySecret string // S3
|
||||||
StorageS3UseSSL string // S3
|
StorageS3UseSSL string // S3
|
||||||
|
ServerID string
|
||||||
}
|
}
|
||||||
|
|
||||||
var config *Config
|
var config *Config
|
||||||
@@ -56,6 +58,10 @@ func GetConfig() *Config {
|
|||||||
if storageType == "" {
|
if storageType == "" {
|
||||||
storageType = DEFAULT_STORAGE_TYPE
|
storageType = DEFAULT_STORAGE_TYPE
|
||||||
}
|
}
|
||||||
|
serverID := os.Getenv("LASSISTANOQUE_SERVER_ID")
|
||||||
|
if serverID == "" {
|
||||||
|
serverID = DEFAULT_SERVER_ID
|
||||||
|
}
|
||||||
|
|
||||||
config = &Config{
|
config = &Config{
|
||||||
DatabaseType: databaseType,
|
DatabaseType: databaseType,
|
||||||
@@ -63,6 +69,7 @@ func GetConfig() *Config {
|
|||||||
HttpPort: httpPort,
|
HttpPort: httpPort,
|
||||||
JWTSecret: jwtsecret,
|
JWTSecret: jwtsecret,
|
||||||
StorageType: storageType,
|
StorageType: storageType,
|
||||||
|
ServerID: serverID,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,48 @@ type LLMModel struct {
|
|||||||
Detail *LLMModelDetail `json:"detail"`
|
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 {
|
type LLMEngine interface {
|
||||||
ListProviderTypes() []Item
|
ListProviderTypes() []Item
|
||||||
ListModelsFromProvider(ctx context.Context, provider *Provider) ([]string, error)
|
ListModelsFromProvider(ctx context.Context, provider *Provider) ([]string, error)
|
||||||
|
Stream(
|
||||||
|
ctx context.Context,
|
||||||
|
provider *Provider,
|
||||||
|
modelID string,
|
||||||
|
params *StreamParams,
|
||||||
|
messages []*Message,
|
||||||
|
) error
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,7 @@ type User struct {
|
|||||||
type Team struct {
|
type Team struct {
|
||||||
ID string `db:"id" json:"id"`
|
ID string `db:"id" json:"id"`
|
||||||
Label string `db:"label" json:"label"`
|
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"`
|
DateCreated time.Time `db:"_date_created" json:"_dateCreated"`
|
||||||
DateUpdated *time.Time `db:"_date_updated" json:"_dateUpdated"`
|
DateUpdated *time.Time `db:"_date_updated" json:"_dateUpdated"`
|
||||||
VersionId string `db:"_version" json:"_version"`
|
VersionId string `db:"_version" json:"_version"`
|
||||||
|
|||||||
@@ -7,11 +7,19 @@ import (
|
|||||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||||
"trankilou.fr/lassistanoque/backend/internal/service/agent"
|
"trankilou.fr/lassistanoque/backend/internal/service/agent"
|
||||||
"trankilou.fr/lassistanoque/backend/internal/service/auth"
|
"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{
|
agentHandler := &AgentHandler{
|
||||||
agentService: service,
|
agentService: agentService,
|
||||||
|
userService: userService,
|
||||||
}
|
}
|
||||||
agent := e.Group(prefix, middlewares...)
|
agent := e.Group(prefix, middlewares...)
|
||||||
|
|
||||||
@@ -26,6 +34,7 @@ func NewAgentGroup(prefix string, e *echo.Group, service *agent.Service, middlew
|
|||||||
|
|
||||||
type AgentHandler struct {
|
type AgentHandler struct {
|
||||||
agentService *agent.Service
|
agentService *agent.Service
|
||||||
|
userService *user.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *AgentHandler) ListAgents(c *echo.Context) error {
|
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)
|
return c.JSON(http.StatusOK, agentID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DefaulAgentRequest struct {
|
||||||
|
AgentID string `json:"AgentId"`
|
||||||
|
}
|
||||||
|
type DefaulAgentResponse struct {
|
||||||
|
NewDefaultAgentID *string `json:"newDefaultAgentId"`
|
||||||
|
OldDefaultAgentID *string `json:"oldDefaultAgentId"`
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"embed"
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
@@ -30,7 +31,7 @@ var indexhtml []byte
|
|||||||
var robotstxt []byte
|
var robotstxt []byte
|
||||||
|
|
||||||
type Router struct {
|
type Router struct {
|
||||||
echo *echo.Echo
|
server *http.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
type Dependencies struct {
|
type Dependencies struct {
|
||||||
@@ -44,6 +45,8 @@ type Dependencies struct {
|
|||||||
|
|
||||||
func NewRouter(deps Dependencies) *Router {
|
func NewRouter(deps Dependencies) *Router {
|
||||||
|
|
||||||
|
cfg := config.GetConfig()
|
||||||
|
|
||||||
e := echo.New()
|
e := echo.New()
|
||||||
e.Use(middleware.RequestLogger())
|
e.Use(middleware.RequestLogger())
|
||||||
e.Use(middleware.CORS("http://localhost:5173"))
|
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.NewUserGroup("/user", api, deps.UserService, deps.TokenManager.TokenMiddleware)
|
||||||
_ = handlers.NewMiscGroup("/misc", api, deps.TokenManager.TokenMiddleware)
|
_ = handlers.NewMiscGroup("/misc", api, deps.TokenManager.TokenMiddleware)
|
||||||
_ = handlers.NewModelGroup("/provider", api, deps.ProviderService, 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{
|
return &Router{
|
||||||
echo: e,
|
&server,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) Start() error {
|
func (r *Router) Start() error {
|
||||||
cfg := config.GetConfig()
|
return r.server.ListenAndServe()
|
||||||
return r.echo.Start(fmt.Sprintf(":%d", cfg.HttpPort))
|
}
|
||||||
|
|
||||||
|
func (r *Router) Stop(ctx context.Context) error {
|
||||||
|
return r.server.Shutdown(ctx)
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package llmserver
|
||||||
|
|
||||||
|
type Session struct {
|
||||||
|
server string
|
||||||
|
}
|
||||||
@@ -105,3 +105,24 @@ func (s *Service) DeleteAddress(userId string, addressId string) error {
|
|||||||
}
|
}
|
||||||
return s.repo.DeleteUserAddress(addressId)
|
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)
|
||||||
|
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -6,5 +6,5 @@ export const agentApi = {
|
|||||||
getAgent: (space: string, id: string) => api.get<Agent>(`/agent/${space}/${id}`),
|
getAgent: (space: string, id: string) => api.get<Agent>(`/agent/${space}/${id}`),
|
||||||
createAgent: (space: string,data: Partial<Agent>) => api.post<Agent>(`/agent/${space}`, data),
|
createAgent: (space: string,data: Partial<Agent>) => api.post<Agent>(`/agent/${space}`, data),
|
||||||
updateAgent: (space: string,data: Partial<Agent>) => api.put<Agent>(`/agent/${space}`, data),
|
updateAgent: (space: string,data: Partial<Agent>) => api.put<Agent>(`/agent/${space}`, data),
|
||||||
deleteAgent: (space: string,id: string) => api.delete(`/agent/${space}/${id}`),
|
deleteAgent: (space: string, id: string) => api.delete(`/agent/${space}/${id}`),
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { api } from '../client';
|
||||||
|
import type { Team } from "$lib/types/api"
|
||||||
|
|
||||||
|
export const teamApi = {
|
||||||
|
getSpace: (space: string) => api.get<Team>(`/team/${space}`),
|
||||||
|
updateSpace: (space: string,data: Partial<Team>) => api.put<Team>(`/team/${space}`, data),
|
||||||
|
};
|
||||||
@@ -1,10 +1,18 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { goto } from "$app/navigation";
|
||||||
import {uiState} from "$lib/state/ui.svelte"
|
import {uiState} from "$lib/state/ui.svelte"
|
||||||
let {label='', icon='', link=''} = $props();
|
let {label='', icon='', link=''} = $props();
|
||||||
|
|
||||||
|
const closeDrawer = (e: Event) => {
|
||||||
|
e.preventDefault()
|
||||||
|
uiState.sidebarMobileOpen=false
|
||||||
|
uiState.menuContentVisible=false
|
||||||
|
goto(link)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="my-2 block">
|
<div class="my-2 block">
|
||||||
<a class="p-2 w-full text-left menuitem block" href={link}>
|
<a class="p-2 w-full text-left menuitem block" href={link} onclick={closeDrawer}>
|
||||||
<i class={[icon]}></i>
|
<i class={[icon]}></i>
|
||||||
{#if uiState.menuContentVisible}
|
{#if uiState.menuContentVisible}
|
||||||
<span class="ml-4">{label}</span>
|
<span class="ml-4">{label}</span>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div id="menu" class="flex-1 overflow-y-scroll">
|
<div id="menu" class="flex-1 overflow-y-scroll">
|
||||||
<Menu label="Nouveau chat" icon="icon-plus" link={`/${spaceid}`}/>
|
<Menu label="Nouveau chat" icon="icon-plus" link={`/${spaceid}`} />
|
||||||
<Menu label="Conversations" icon="icon-chat" link={`/${spaceid}/history`}/>
|
<Menu label="Conversations" icon="icon-chat" link={`/${spaceid}/history`}/>
|
||||||
<Menu label="Planification" icon="icon-tasks" link={`/${spaceid}/tasks`}/>
|
<Menu label="Planification" icon="icon-tasks" link={`/${spaceid}/tasks`}/>
|
||||||
<Menu label="Connaissances" icon="icon-graduation-cap" link={`/${spaceid}/kb`}/>
|
<Menu label="Connaissances" icon="icon-graduation-cap" link={`/${spaceid}/kb`}/>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export interface UserTeam {
|
|||||||
export interface Team {
|
export interface Team {
|
||||||
id: string
|
id: string
|
||||||
label: string
|
label: string
|
||||||
defaultModelId: string
|
defaultAgentId: string
|
||||||
_version: string
|
_version: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,13 +5,17 @@
|
|||||||
import type { Agent } from '$lib/types/api'
|
import type { Agent } from '$lib/types/api'
|
||||||
import type { PageProps } from './$types';
|
import type { PageProps } from './$types';
|
||||||
import { agentApi } from "$lib/api/endpoints/agent"
|
import { agentApi } from "$lib/api/endpoints/agent"
|
||||||
|
import { teamApi } from '$lib/api/endpoints/team';
|
||||||
|
|
||||||
//import { validateUrl } from '$lib/utilities/validators'
|
//import { validateUrl } from '$lib/utilities/validators'
|
||||||
import AgentForm from './AgentForm.svelte'
|
import AgentForm from './AgentForm.svelte'
|
||||||
|
import { untrack } from 'svelte';
|
||||||
|
import { space as spaceState } from "$lib/state/space.svelte"
|
||||||
|
|
||||||
let { data }: PageProps = $props();
|
let { data }: PageProps = $props();
|
||||||
|
|
||||||
let agents = $state(data.agents)
|
let space = $state(untrack(()=>data.space))
|
||||||
|
let agents = $state(untrack(()=>data.agents))
|
||||||
let addingAgent = $state(false)
|
let addingAgent = $state(false)
|
||||||
let editingAgent = $state(null as string|null)
|
let editingAgent = $state(null as string|null)
|
||||||
let agentForm = $state({} as Agent)
|
let agentForm = $state({} as Agent)
|
||||||
@@ -38,7 +42,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (errors.size === 0) {
|
if (errors.size === 0) {
|
||||||
agentApi.createAgent(data.space, a)
|
agentApi.createAgent(data.space.id, a)
|
||||||
.then((p)=>{
|
.then((p)=>{
|
||||||
agents.push(p)
|
agents.push(p)
|
||||||
addingAgent= false
|
addingAgent= false
|
||||||
@@ -61,7 +65,7 @@
|
|||||||
errors.set("name","Le nom est obligatoire !")
|
errors.set("name","Le nom est obligatoire !")
|
||||||
}
|
}
|
||||||
if (errors.size === 0) {
|
if (errors.size === 0) {
|
||||||
agentApi.updateAgent(data.space,a)
|
agentApi.updateAgent(data.space.id,a)
|
||||||
.then((p)=>{
|
.then((p)=>{
|
||||||
agents = agents.map((it: Agent)=>{return p.id===it.id ? p : it})
|
agents = agents.map((it: Agent)=>{return p.id===it.id ? p : it})
|
||||||
editingAgent = null
|
editingAgent = null
|
||||||
@@ -76,7 +80,7 @@
|
|||||||
const handleEditAgent = async (id: string) => {
|
const handleEditAgent = async (id: string) => {
|
||||||
errors.clear()
|
errors.clear()
|
||||||
if (addingAgent) return
|
if (addingAgent) return
|
||||||
agentApi.getAgent(data.space,id)
|
agentApi.getAgent(data.space.id,id)
|
||||||
.then((a)=>{
|
.then((a)=>{
|
||||||
agents = agents.filter((it)=>it.id===a.id ? a : it)
|
agents = agents.filter((it)=>it.id===a.id ? a : it)
|
||||||
editingAgent = id
|
editingAgent = id
|
||||||
@@ -85,13 +89,25 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleDeleteAgent = (id: string) => {
|
const handleDeleteAgent = (id: string) => {
|
||||||
agentApi.deleteAgent(data.space,id)
|
agentApi.deleteAgent(data.space.id,id)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
agents = agents.filter((it: Agent)=>it.id!==id)
|
agents = agents.filter((it: Agent)=>it.id!==id)
|
||||||
})
|
})
|
||||||
.catch((err) => {console.log(err)})
|
.catch((err) => {console.log(err)})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleDefaultAgent = (agentId: string) => {
|
||||||
|
let updSpace = space
|
||||||
|
updSpace.defaultAgentId = agentId
|
||||||
|
teamApi.updateSpace(updSpace.id, updSpace).then((newdata)=>{
|
||||||
|
console.log(newdata)
|
||||||
|
spaceState.spaces.map((s)=>s.id===spaceState.current?newdata:s)
|
||||||
|
space=newdata
|
||||||
|
}).catch((err)=>{
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -129,10 +145,13 @@
|
|||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<span>{ agent.name }</span>
|
<span>{ agent.name }</span>
|
||||||
</div>
|
</div>
|
||||||
|
<button class="bt small mx-1" class:highlight={agent.id===space.defaultAgentId} onclick={()=>handleDefaultAgent(agent.id)} title="Modifier">
|
||||||
|
Agent par défaut
|
||||||
|
</button>
|
||||||
<button class="bt small mx-1" onclick={()=>handleEditAgent(agent.id)} title="Modifier">
|
<button class="bt small mx-1" onclick={()=>handleEditAgent(agent.id)} title="Modifier">
|
||||||
<i class="icon-edit"></i>
|
<i class="icon-edit"></i>
|
||||||
</button>
|
</button>
|
||||||
<button class="bt small mx-1" onclick={()=>handleDeleteAgent(agent.id)} title="Supprimer">
|
<button class="bt small mx-1" onclick={()=>handleDeleteAgent(agent.id)} title="Supprimer">
|
||||||
<i class="icon-trash-empty"></i>
|
<i class="icon-trash-empty"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import { agentApi } from "$lib/api/endpoints/agent"
|
import { agentApi } from "$lib/api/endpoints/agent"
|
||||||
import {providerApi} from "$lib/api/endpoints/provider"
|
import { providerApi } from "$lib/api/endpoints/provider"
|
||||||
|
import { teamApi } from "$lib/api/endpoints/team"
|
||||||
import type { PageLoad } from './$types';
|
import type { PageLoad } from './$types';
|
||||||
|
|
||||||
export const load: PageLoad = async ({ params }) => {
|
export const load: PageLoad = async ({ params }) => {
|
||||||
return {
|
return {
|
||||||
title: 'Paramétrage : Agents',
|
title: 'Paramétrage : Agents',
|
||||||
back: "../settings",
|
back: "../settings",
|
||||||
space: params.space,
|
//space: params.space,
|
||||||
|
space: await teamApi.getSpace(params.space),
|
||||||
agents: await agentApi.getAgents(params.space),
|
agents: await agentApi.getAgents(params.space),
|
||||||
providersModels: await providerApi.getProvidersModels(params.space),
|
providersModels: await providerApi.getProvidersModels(params.space),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,28 +1,23 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
||||||
import { SvelteMap } from 'svelte/reactivity';
|
import { SvelteMap } from 'svelte/reactivity';
|
||||||
|
import { untrack } from 'svelte';
|
||||||
|
|
||||||
import type { Provider} from '$lib/types/api'
|
import type { Provider} from '$lib/types/api'
|
||||||
import type { PageProps } from './$types';
|
import type { PageProps } from './$types';
|
||||||
import { providerApi } from "$lib/api/endpoints/provider"
|
import { providerApi } from "$lib/api/endpoints/provider"
|
||||||
import { onMount } from 'svelte';
|
|
||||||
|
|
||||||
import { validateUrl } from '$lib/utilities/validators'
|
import { validateUrl } from '$lib/utilities/validators'
|
||||||
import ProviderForm from './ProviderForm.svelte'
|
import ProviderForm from './ProviderForm.svelte'
|
||||||
|
|
||||||
let { data }: PageProps = $props();
|
let { data }: PageProps = $props();
|
||||||
|
|
||||||
let providers = $state([] as Provider[])
|
let providers = $state(untrack(()=>data.providers))
|
||||||
let addingProvider = $state(false)
|
let addingProvider = $state(false)
|
||||||
let editingProvider = $state(null as string|null)
|
let editingProvider = $state(null as string|null)
|
||||||
let providerForm = $state({} as Provider)
|
let providerForm = $state({} as Provider)
|
||||||
let errors = new SvelteMap<string,string>()
|
let errors = new SvelteMap<string,string>()
|
||||||
|
|
||||||
|
|
||||||
onMount(async ()=>{
|
|
||||||
providers = await providerApi.getProviders(data.space)
|
|
||||||
})
|
|
||||||
|
|
||||||
const enterAddProviderHandler = () => {
|
const enterAddProviderHandler = () => {
|
||||||
if (editingProvider) return
|
if (editingProvider) return
|
||||||
errors.clear()
|
errors.clear()
|
||||||
@@ -121,10 +116,13 @@
|
|||||||
/>
|
/>
|
||||||
{/key}
|
{/key}
|
||||||
{:else}
|
{:else}
|
||||||
<div class="rounded border border-dashed p-2 border-stone-500 cursor-pointer" onclick={enterAddProviderHandler} tabindex="0">
|
<button class="rounded w-full border border-dashed p-2 border-stone-500 cursor-pointer"
|
||||||
|
onclick={enterAddProviderHandler}
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
<i class="icon-plus"></i>
|
<i class="icon-plus"></i>
|
||||||
Ajouter un fournisseur
|
Ajouter un fournisseur
|
||||||
</div>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#each providers as p (p.id) }
|
{#each providers as p (p.id) }
|
||||||
@@ -144,10 +142,18 @@
|
|||||||
<span>{ p.name }</span>
|
<span>{ p.name }</span>
|
||||||
<span class="text-stone-500">({ p.type })</span>
|
<span class="text-stone-500">({ p.type })</span>
|
||||||
</div>
|
</div>
|
||||||
<button class="bt small mx-1" onclick={()=>handleEditProvider(p.id)}>
|
<button
|
||||||
|
class="bt small mx-1"
|
||||||
|
onclick={()=>handleEditProvider(p.id)}
|
||||||
|
aria-label="Modifier"
|
||||||
|
>
|
||||||
<i class="icon-edit"></i>
|
<i class="icon-edit"></i>
|
||||||
</button>
|
</button>
|
||||||
<button class="bt small mx-1" onclick={()=>handleDeleteProvider(p.id)}>
|
<button
|
||||||
|
class="bt small mx-1"
|
||||||
|
onclick={()=>handleDeleteProvider(p.id)}
|
||||||
|
aria-label="Supprimer"
|
||||||
|
>
|
||||||
<i class="icon-trash-empty"></i>
|
<i class="icon-trash-empty"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,5 +7,6 @@ export const load: PageLoad = async ({params}) => {
|
|||||||
back: "../settings",
|
back: "../settings",
|
||||||
space: params.space,
|
space: params.space,
|
||||||
providerTypes: await providerApi.providerTypes(),
|
providerTypes: await providerApi.providerTypes(),
|
||||||
|
providers: await providerApi.getProviders(params.space),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -30,13 +30,13 @@ h2 {
|
|||||||
}
|
}
|
||||||
.bt {
|
.bt {
|
||||||
appearance: button;
|
appearance: button;
|
||||||
@apply min-w-40 cursor-pointer rounded bg-stone-300 px-4 py-1 font-bold transition-all duration-300 hover:bg-stone-200 dark:bg-stone-800 dark:hover:bg-stone-700;
|
@apply min-w-40 cursor-pointer rounded bg-stone-300 px-4 py-1 transition-all duration-300 hover:bg-stone-200 dark:bg-stone-800 dark:hover:bg-stone-700;
|
||||||
&.small {
|
&.small {
|
||||||
@apply min-w-0 px-2 py-1;
|
@apply min-w-0 px-2 py-1;
|
||||||
}
|
}
|
||||||
&.changed,
|
&.changed,
|
||||||
&.saving {
|
&.saving {
|
||||||
@apply bg-sky-300 dark:bg-sky-700;
|
@apply bg-cyan-400 dark:bg-cyan-600;
|
||||||
}
|
}
|
||||||
&.saved {
|
&.saved {
|
||||||
@apply bg-green-300 dark:bg-green-700;
|
@apply bg-green-300 dark:bg-green-700;
|
||||||
@@ -47,6 +47,9 @@ h2 {
|
|||||||
&.disabled {
|
&.disabled {
|
||||||
@apply text-stone-400 hover:bg-stone-200 dark:hover:bg-stone-800;
|
@apply text-stone-400 hover:bg-stone-200 dark:hover:bg-stone-800;
|
||||||
}
|
}
|
||||||
|
&.highlight {
|
||||||
|
@apply text-sky-700 dark:text-sky-300;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.menuitem {
|
.menuitem {
|
||||||
@apply text-stone-950 dark:text-stone-300 hover:dark:text-stone-50;
|
@apply text-stone-950 dark:text-stone-300 hover:dark:text-stone-50;
|
||||||
|
|||||||
Reference in new issue
Block a user