Ajout de SEARXNG pour les recherches web
This commit is contained in:
1 parent
87eeb41364
commit
345129c7bf
30 files changed
+919
-166
No files matched your search
@@ -15,6 +15,7 @@ type Database interface {
|
||||
ProviderRepository() domain.ProviderRepository
|
||||
AgentRepository() domain.AgentRepository
|
||||
ChatRepository() domain.ChatRepository
|
||||
ToolRepository() domain.ToolRepository
|
||||
Migrate() error
|
||||
Close()
|
||||
}
|
||||
|
||||
@@ -79,3 +79,7 @@ func (db *TursoDB) AgentRepository() domain.AgentRepository {
|
||||
func (db *TursoDB) ChatRepository() domain.ChatRepository {
|
||||
return NewTursoChatRepository(db.DB)
|
||||
}
|
||||
|
||||
func (db *TursoDB) ToolRepository() domain.ToolRepository {
|
||||
return NewTursoToolRepository(db.DB)
|
||||
}
|
||||
@@ -95,9 +95,8 @@ create table tools (
|
||||
id text not null primary key,
|
||||
team_id text not null,
|
||||
name text not null,
|
||||
type text not null,
|
||||
enabled numeric not null default 1,
|
||||
configuration text not null default '{}',
|
||||
configuration text not null default '',
|
||||
_date_created numeric not null default current_timestamp,
|
||||
_date_updated numeric,
|
||||
_version text not null
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package turso
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/database/orm"
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
)
|
||||
|
||||
type TursoToolRepository struct {
|
||||
db *sql.DB
|
||||
ToolTable orm.Table[domain.Tool]
|
||||
}
|
||||
|
||||
func NewTursoToolRepository(db *sql.DB) *TursoToolRepository {
|
||||
dialect := orm.NewSqliteDialect()
|
||||
ToolTable, err := orm.NewTable[domain.Tool](db, dialect, "tools")
|
||||
if err != nil {
|
||||
log.Fatalf("error creating lasebuche Tool table")
|
||||
}
|
||||
|
||||
return &TursoToolRepository{
|
||||
db: db,
|
||||
ToolTable: ToolTable,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *TursoToolRepository) ListTools(userID string, teamID string) ([]*domain.Tool, error) {
|
||||
return r.ToolTable.Select(
|
||||
orm.WithWhere(
|
||||
"team_id=$1 and team_id in (select team_id from user_teams where user_id=$2)",
|
||||
teamID,
|
||||
userID,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func (r *TursoToolRepository) GetToolByName(userID string, teamID string, name string) (*domain.Tool, error) {
|
||||
return r.ToolTable.SelectOne(
|
||||
"name=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
|
||||
name,
|
||||
teamID,
|
||||
userID,
|
||||
)
|
||||
}
|
||||
|
||||
func (r *TursoToolRepository) GetToolByID(userID string, teamID string, id string) (*domain.Tool, error) {
|
||||
return r.ToolTable.SelectOne(
|
||||
"id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
|
||||
id,
|
||||
teamID,
|
||||
userID,
|
||||
)
|
||||
}
|
||||
|
||||
func (r *TursoToolRepository) CreateTool(userID string, Tool *domain.Tool) (*domain.Tool, error) {
|
||||
return r.ToolTable.Insert(Tool)
|
||||
}
|
||||
|
||||
func (r *TursoToolRepository) UpdateTool(userID string, Tool *domain.Tool) (*domain.Tool, error) {
|
||||
return r.ToolTable.Update(Tool)
|
||||
}
|
||||
|
||||
func (r *TursoToolRepository) DeleteTool(userID string, teamID string, name string) error {
|
||||
return r.ToolTable.DeleteWhere(
|
||||
"name=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
|
||||
name,
|
||||
teamID,
|
||||
userID,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package searxng
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
)
|
||||
|
||||
type SearxngConfig struct {
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
type SearxngTool struct {
|
||||
}
|
||||
|
||||
func (t *SearxngTool) Name() string {
|
||||
return "searxng"
|
||||
}
|
||||
|
||||
func (t *SearxngTool) Description() string {
|
||||
return "Search the web using a SearXNG instance. Returns a list of relevant results with title, URL, and snippet."
|
||||
}
|
||||
|
||||
func (t *SearxngTool) Params() []domain.ToolParam {
|
||||
return []domain.ToolParam{
|
||||
{JsonName: "url", Type: "string", Mandatory: true, Description: "Searxng URL"},
|
||||
}
|
||||
}
|
||||
|
||||
func (t *SearxngTool) Definition(ctx context.Context) *domain.ToolDefinition {
|
||||
return &domain.ToolDefinition{
|
||||
Type: "function",
|
||||
Function: &domain.ToolFunction{
|
||||
Name: t.Name(),
|
||||
Description: t.Description(),
|
||||
Parameters: map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"query": map[string]any{
|
||||
"type": "string",
|
||||
"description": "The search query",
|
||||
},
|
||||
"categories": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Comma-separated list of categories: general, news, images, science, it, social_media. Defaults to 'general'.",
|
||||
},
|
||||
"language": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Language code for results, e.g. 'fr', 'en'. Defaults to 'all'.",
|
||||
},
|
||||
},
|
||||
"required": []string{"query"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type searxngResponse struct {
|
||||
Results []searxngResult `json:"results"`
|
||||
}
|
||||
|
||||
type searxngResult struct {
|
||||
Title string `json:"title"`
|
||||
URL string `json:"url"`
|
||||
Content string `json:"content"`
|
||||
Engine string `json:"engine"`
|
||||
}
|
||||
|
||||
func (t *SearxngTool) Execute(ctx context.Context, input []byte, configuration string) ([]byte, error) {
|
||||
|
||||
slog.Debug("[searxng]", "input", string(input))
|
||||
|
||||
var params struct {
|
||||
Query string `json:"query"`
|
||||
Categories string `json:"categories"`
|
||||
Language string `json:"language"`
|
||||
}
|
||||
if err := json.Unmarshal(input, ¶ms); err != nil {
|
||||
return nil, fmt.Errorf("invalid parameters: %w", err)
|
||||
}
|
||||
|
||||
var toolCfg SearxngConfig
|
||||
err := json.Unmarshal([]byte(configuration), &toolCfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("searxng tool not configured: %w", err)
|
||||
}
|
||||
if toolCfg.URL == "" {
|
||||
return nil, fmt.Errorf("searxng tool config missing 'url' field")
|
||||
}
|
||||
baseURL := toolCfg.URL
|
||||
|
||||
categories := "general"
|
||||
if params.Categories != "" {
|
||||
categories = params.Categories
|
||||
}
|
||||
language := "all"
|
||||
if params.Language != "" {
|
||||
language = params.Language
|
||||
}
|
||||
|
||||
reqURL := fmt.Sprintf("%s/search?q=%s&format=json&categories=%s&language=%s",
|
||||
strings.TrimRight(baseURL, "/"),
|
||||
url.QueryEscape(params.Query),
|
||||
url.QueryEscape(categories),
|
||||
url.QueryEscape(language),
|
||||
)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build request: %w", err)
|
||||
}
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("searxng request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("searxng returned status %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var searchResp searxngResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&searchResp); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode response: %w", err)
|
||||
}
|
||||
|
||||
if len(searchResp.Results) == 0 {
|
||||
return []byte("No results found."), nil
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
fmt.Fprintf(&sb, "Search results for \"%s\":\n\n", params.Query)
|
||||
for i, r := range searchResp.Results {
|
||||
fmt.Fprintf(&sb, "%d. %s\n", i+1, r.Title)
|
||||
fmt.Fprintf(&sb, " URL: %s\n", r.URL)
|
||||
if r.Content != "" {
|
||||
fmt.Fprintf(&sb, " %s\n", r.Content)
|
||||
}
|
||||
fmt.Fprintf(&sb, "\n")
|
||||
}
|
||||
|
||||
return []byte(sb.String()), nil
|
||||
}
|
||||
@@ -25,6 +25,10 @@ func (t *WeatherTool) Description() string {
|
||||
return "Get the current weather for a given location"
|
||||
}
|
||||
|
||||
func (t *WeatherTool) Params() []domain.ToolParam {
|
||||
return []domain.ToolParam{}
|
||||
}
|
||||
|
||||
func (t *WeatherTool) Definition(ctx context.Context) *domain.ToolDefinition {
|
||||
return &domain.ToolDefinition{
|
||||
Type: "function",
|
||||
@@ -45,7 +49,7 @@ func (t *WeatherTool) Definition(ctx context.Context) *domain.ToolDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *WeatherTool) Execute(ctx context.Context, input []byte) ([]byte, error) {
|
||||
func (t *WeatherTool) Execute(ctx context.Context, input []byte, configuration string) ([]byte, error) {
|
||||
|
||||
var params struct {
|
||||
Location string `json:"location"`
|
||||
|
||||
@@ -29,6 +29,7 @@ type Config struct {
|
||||
StorageS3AccessKeySecret string // S3
|
||||
StorageS3UseSSL string // S3
|
||||
ServerID string
|
||||
SearXngUrl string
|
||||
}
|
||||
|
||||
var config *Config
|
||||
|
||||
@@ -66,11 +66,19 @@ type ChatParams struct {
|
||||
OnChunk StreamCallback
|
||||
}
|
||||
|
||||
type Tool interface {
|
||||
type ToolParam struct {
|
||||
JsonName string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
Mandatory bool `json:"mandatory"`
|
||||
}
|
||||
|
||||
type ITool interface {
|
||||
Name() string
|
||||
Description() string
|
||||
Params() []ToolParam
|
||||
Definition(ctx context.Context) *ToolDefinition
|
||||
Execute(ctx context.Context, input []byte) ([]byte, error)
|
||||
Execute(ctx context.Context, input []byte, configuration string) ([]byte, error)
|
||||
}
|
||||
|
||||
type ToolDefinition struct {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package domain
|
||||
|
||||
import "time"
|
||||
|
||||
type Tool struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
TeamID string `db:"team_id" json:"teamId"`
|
||||
Name string `db:"name" json:"name"`
|
||||
Enabled bool `db:"enabled" json:"enabled"`
|
||||
Configuration string `db:"configuration" json:"configuration"`
|
||||
DateCreated time.Time `db:"_date_created" json:"_date_created"`
|
||||
DateUpdated *time.Time `db:"_date_updated" json:"_date_updated"`
|
||||
VersionId string `db:"_version" json:"_version"`
|
||||
}
|
||||
|
||||
type ToolRepository interface {
|
||||
ListTools(userID string, teamID string) ([]*Tool, error)
|
||||
GetToolByName(userID string, teamID string, name string) (*Tool, error)
|
||||
GetToolByID(userID string, teamID string, id string) (*Tool, error)
|
||||
CreateTool(userID string, agent *Tool) (*Tool, error)
|
||||
UpdateTool(userID string, agent *Tool) (*Tool, error)
|
||||
DeleteTool(userID string, teamID string, name string) error
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package brain
|
||||
package gateway
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -1,4 +1,4 @@
|
||||
package brain
|
||||
package gateway
|
||||
|
||||
type Session struct {
|
||||
serverID string
|
||||
@@ -0,0 +1,123 @@
|
||||
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/tool"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/user"
|
||||
)
|
||||
|
||||
func NewToolGroup(
|
||||
prefix string,
|
||||
e *echo.Group,
|
||||
ToolService *tool.Service,
|
||||
userService *user.Service,
|
||||
middlewares ...echo.MiddlewareFunc) *echo.Group {
|
||||
|
||||
ToolHandler := &ToolHandler{
|
||||
ToolService: ToolService,
|
||||
userService: userService,
|
||||
}
|
||||
Tool := e.Group(prefix, middlewares...)
|
||||
|
||||
Tool.GET("/:space", ToolHandler.ListTools)
|
||||
Tool.GET("/:space/:tool", ToolHandler.GetTool)
|
||||
Tool.GET("/:space/:tool/params", ToolHandler.GetToolParams)
|
||||
Tool.PUT("/:space", ToolHandler.UpdateTool)
|
||||
Tool.POST("/:space", ToolHandler.CreateTool)
|
||||
//Tool.DELETE("/:space/:tool", ToolHandler.DeleteTool)
|
||||
|
||||
return Tool
|
||||
}
|
||||
|
||||
type ToolHandler struct {
|
||||
ToolService *tool.Service
|
||||
userService *user.Service
|
||||
}
|
||||
|
||||
func (h *ToolHandler) ListTools(c *echo.Context) error {
|
||||
userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
teamID := c.Param("space")
|
||||
Tools, err := h.ToolService.ListTools(userID, teamID)
|
||||
if err != nil {
|
||||
c.Logger().Error("error listing Tools", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error listing Tools")
|
||||
}
|
||||
return c.JSON(http.StatusOK, Tools)
|
||||
}
|
||||
|
||||
func (h *ToolHandler) GetTool(c *echo.Context) error {
|
||||
userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
teamID := c.Param("space")
|
||||
ToolID := c.Param("tool")
|
||||
Tool, err := h.ToolService.GetTool(userID, teamID, ToolID)
|
||||
if err != nil {
|
||||
c.Logger().Error("error getting Tool", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error getting Tool")
|
||||
}
|
||||
return c.JSON(http.StatusOK, Tool)
|
||||
}
|
||||
|
||||
func (h *ToolHandler) GetToolParams(c *echo.Context) error {
|
||||
userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
teamID := c.Param("space")
|
||||
toolID := c.Param("tool")
|
||||
tool, err := h.ToolService.GetTool(userID, teamID, toolID)
|
||||
if err != nil {
|
||||
c.Logger().Error("error getting Tool", "error", err, "teamid", teamID, "toolID", toolID)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error getting Tool")
|
||||
}
|
||||
toolImpl := h.ToolService.GetToolImpl(tool.Name)
|
||||
return c.JSON(http.StatusOK, toolImpl.Params())
|
||||
}
|
||||
|
||||
func (h *ToolHandler) CreateTool(c *echo.Context) error {
|
||||
userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
teamID := c.Param("space")
|
||||
var Tool domain.Tool
|
||||
err := c.Bind(&Tool)
|
||||
if err != nil {
|
||||
c.Logger().Error("error binding Tool", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error binding Tool")
|
||||
}
|
||||
Tool.TeamID = teamID
|
||||
updTool, err := h.ToolService.CreateTool(userID, &Tool)
|
||||
if err != nil {
|
||||
c.Logger().Error("error creating Tool", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error creating Tool")
|
||||
}
|
||||
return c.JSON(http.StatusOK, updTool)
|
||||
}
|
||||
|
||||
func (h *ToolHandler) UpdateTool(c *echo.Context) error {
|
||||
userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
teamID := c.Param("space")
|
||||
var Tool domain.Tool
|
||||
err := c.Bind(&Tool)
|
||||
if err != nil {
|
||||
c.Logger().Error("error binding Tool", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error binding Tool")
|
||||
}
|
||||
Tool.TeamID = teamID
|
||||
updTool, err := h.ToolService.UpdateTool(userID, &Tool)
|
||||
if err != nil {
|
||||
c.Logger().Error("error updating Tool", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error updating Tool")
|
||||
}
|
||||
return c.JSON(http.StatusOK, updTool)
|
||||
}
|
||||
|
||||
// func (h *ToolHandler) DeleteTool(c *echo.Context) error {
|
||||
// userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
// teamID := c.Param("space")
|
||||
// ToolID := c.Param("tool")
|
||||
// err := h.ToolService.DeleteTool(userID, teamID, ToolID)
|
||||
// if err != nil {
|
||||
// c.Logger().Error("error creating Tool", "error", err)
|
||||
// return echo.NewHTTPError(http.StatusBadRequest, "error creating Tool")
|
||||
// }
|
||||
// return c.JSON(http.StatusOK, ToolID)
|
||||
// }
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/chat"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/provider"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/storage"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/tool"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/user"
|
||||
)
|
||||
|
||||
@@ -42,6 +43,7 @@ type Dependencies struct {
|
||||
ProviderService *provider.Service
|
||||
AgentService *agent.Service
|
||||
ChatService *chat.Service
|
||||
ToolService *tool.Service
|
||||
TokenManager auth.TokenManager
|
||||
}
|
||||
|
||||
@@ -82,6 +84,7 @@ func NewRouter(deps Dependencies) *Router {
|
||||
_ = handlers.NewMiscGroup("/misc", api, deps.TokenManager.TokenMiddleware)
|
||||
_ = handlers.NewModelGroup("/provider", api, deps.ProviderService, deps.TokenManager.TokenMiddleware)
|
||||
_ = handlers.NewAgentGroup("/agent", api, deps.AgentService, deps.UserService, deps.TokenManager.TokenMiddleware)
|
||||
_ = handlers.NewToolGroup("/tool", api, deps.ToolService, deps.UserService, deps.TokenManager.TokenMiddleware)
|
||||
_ = handlers.NewTeamGroup("/team", api, deps.UserService, deps.TokenManager.TokenMiddleware)
|
||||
_ = handlers.NewChatGroup("/chat", api, deps.ChatService, deps.TokenManager.TokenMiddleware)
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/tool"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
@@ -17,6 +18,7 @@ type Service struct {
|
||||
repoChat domain.ChatRepository
|
||||
llmEngine domain.LLMEngine
|
||||
runningSessions map[string]*ChatSession
|
||||
toolService *tool.Service
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
@@ -26,6 +28,7 @@ func NewService(
|
||||
repoProvider domain.ProviderRepository,
|
||||
repoChat domain.ChatRepository,
|
||||
llmEngine domain.LLMEngine,
|
||||
toolService *tool.Service,
|
||||
) *Service {
|
||||
return &Service{
|
||||
repoUser: repoUser,
|
||||
@@ -33,6 +36,7 @@ func NewService(
|
||||
repoProvider: repoProvider,
|
||||
repoChat: repoChat,
|
||||
llmEngine: llmEngine,
|
||||
toolService: toolService,
|
||||
runningSessions: make(map[string]*ChatSession),
|
||||
}
|
||||
}
|
||||
@@ -194,6 +198,7 @@ func (s *Service) runQuery(
|
||||
modelID: modelID,
|
||||
messages: messages,
|
||||
llmEngine: s.llmEngine,
|
||||
toolService: s.toolService,
|
||||
subscribers: make([]domain.StreamCallback, 0),
|
||||
repoChat: s.repoChat,
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ package chat
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"log/slog"
|
||||
"sync"
|
||||
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/tool"
|
||||
)
|
||||
|
||||
//go:embed system_prompt.md
|
||||
@@ -20,6 +22,7 @@ type ChatSession struct {
|
||||
modelID string
|
||||
messages []*domain.Message
|
||||
llmEngine domain.LLMEngine
|
||||
toolService *tool.Service
|
||||
repoChat domain.ChatRepository
|
||||
subscribers []domain.StreamCallback
|
||||
mu sync.Mutex
|
||||
@@ -30,7 +33,7 @@ func (s *ChatSession) run(ctx context.Context) error {
|
||||
params := &domain.LLMParams{}
|
||||
|
||||
tools := make([]*domain.ToolDefinition, 0)
|
||||
for _, t := range allTools {
|
||||
for _, t := range s.toolService.GetAllToolImpl() {
|
||||
tools = append(tools, t.Definition(ctx))
|
||||
}
|
||||
params.Tools = tools
|
||||
@@ -76,8 +79,21 @@ func (s *ChatSession) run(ctx context.Context) error {
|
||||
continue_loop = true
|
||||
for _, tc := range msg.ToolCalls {
|
||||
|
||||
tool := GetTool(tc.Function.Name)
|
||||
output, err := tool.Execute(ctx, []byte(tc.Function.Arguments))
|
||||
toolImpl := s.toolService.GetToolImpl(tc.Function.Name)
|
||||
slog.Info("Execute tool", "name", tc.Function.Name, "impl", toolImpl)
|
||||
toolRecord, err := s.toolService.GetToolByName(s.userID, s.teamID, toolImpl.Name())
|
||||
if err != nil {
|
||||
slog.Info("Execute tool", "error", err)
|
||||
toolResponse := &domain.Message{
|
||||
ChatID: s.chatID,
|
||||
TeamID: s.teamID,
|
||||
ToolCallID: tc.ID,
|
||||
Role: string(domain.RoleTool),
|
||||
Content: "ERROR: " + err.Error(),
|
||||
}
|
||||
s.repoChat.CreateChatMessage(s.userID, toolResponse)
|
||||
}
|
||||
output, err := toolImpl.Execute(ctx, []byte(tc.Function.Arguments), toolRecord.Configuration)
|
||||
var toolResponse *domain.Message
|
||||
if err != nil {
|
||||
toolResponse = &domain.Message{
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package chat
|
||||
|
||||
import "trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
|
||||
var allTools = make(map[string]domain.Tool)
|
||||
|
||||
func RegisterTool(tool domain.Tool) error {
|
||||
allTools[tool.Name()] = tool
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetTool(name string) domain.Tool {
|
||||
return allTools[name]
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package tool
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
repo domain.ToolRepository
|
||||
repoUser domain.UserRepository
|
||||
allTools map[string]domain.ITool
|
||||
}
|
||||
|
||||
func NewService(
|
||||
repo domain.ToolRepository,
|
||||
repoUser domain.UserRepository,
|
||||
) *Service {
|
||||
return &Service{
|
||||
repo: repo,
|
||||
repoUser: repoUser,
|
||||
allTools: make(map[string]domain.ITool),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) ListTools(userID string, teamID string) ([]*domain.Tool, error) {
|
||||
|
||||
for _, tool := range s.allTools {
|
||||
_, err := s.repo.GetToolByName(userID, teamID, tool.Name())
|
||||
slog.Info("ListTools", "err1", err)
|
||||
if err != nil {
|
||||
_, err = s.repo.CreateTool(userID, &domain.Tool{
|
||||
TeamID: teamID,
|
||||
Name: tool.Name(),
|
||||
Enabled: true,
|
||||
})
|
||||
if err != nil {
|
||||
slog.Info("ListTools", "err2", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s.repo.ListTools(userID, teamID)
|
||||
}
|
||||
|
||||
func (s *Service) GetTool(userID string, teamID string, id string) (*domain.Tool, error) {
|
||||
return s.repo.GetToolByID(userID, teamID, id)
|
||||
}
|
||||
|
||||
func (s *Service) GetToolByName(userID string, teamID string, name string) (*domain.Tool, error) {
|
||||
return s.repo.GetToolByName(userID, teamID, name)
|
||||
}
|
||||
|
||||
func (s *Service) CreateTool(userID string, Tool *domain.Tool) (*domain.Tool, error) {
|
||||
|
||||
if _, err := s.repoUser.FindUserTeam(userID, Tool.TeamID); err != nil {
|
||||
return nil, fmt.Errorf("error finding user in team: %s", err)
|
||||
}
|
||||
|
||||
return s.repo.CreateTool(userID, Tool)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateTool(userID string, Tool *domain.Tool) (*domain.Tool, error) {
|
||||
if _, err := s.repoUser.FindUserTeam(userID, Tool.TeamID); err != nil {
|
||||
return nil, fmt.Errorf("error finding user in team: %s", err)
|
||||
}
|
||||
|
||||
if _, err := s.repo.GetToolByID(userID, Tool.TeamID, Tool.ID); err != nil {
|
||||
return nil, fmt.Errorf("error finding Tool: %s", err)
|
||||
}
|
||||
|
||||
return s.repo.UpdateTool(userID, Tool)
|
||||
}
|
||||
|
||||
func (s *Service) DeleteTool(userID string, teamID string, id string) error {
|
||||
if _, err := s.repoUser.FindUserTeam(userID, teamID); err != nil {
|
||||
return fmt.Errorf("error finding user in team: %s", err)
|
||||
}
|
||||
|
||||
if _, err := s.repo.GetToolByID(userID, teamID, id); err != nil {
|
||||
return fmt.Errorf("error finding Tool: %s", err)
|
||||
}
|
||||
|
||||
return s.repo.DeleteTool(userID, teamID, id)
|
||||
|
||||
}
|
||||
|
||||
func (s *Service) RegisterToolImpl(tool domain.ITool) error {
|
||||
s.allTools[tool.Name()] = tool
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) GetAllToolImpl() map[string]domain.ITool {
|
||||
return s.allTools
|
||||
}
|
||||
|
||||
func (s *Service) GetToolImpl(name string) domain.ITool {
|
||||
return s.allTools[name]
|
||||
}
|
||||
Reference in new issue
Block a user