mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
feat: refactor logging system to slog with DI and component tagging
This commit is contained in:
+65
-47
@@ -3,13 +3,14 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/memohai/memoh/internal/chat"
|
||||
"github.com/memohai/memoh/internal/config"
|
||||
"github.com/memohai/memoh/internal/logger"
|
||||
ctr "github.com/memohai/memoh/internal/containerd"
|
||||
"github.com/memohai/memoh/internal/db"
|
||||
dbsqlc "github.com/memohai/memoh/internal/db/sqlc"
|
||||
@@ -34,15 +35,20 @@ func main() {
|
||||
cfgPath := os.Getenv("CONFIG_PATH")
|
||||
cfg, err := config.Load(cfgPath)
|
||||
if err != nil {
|
||||
log.Fatalf("load config: %v", err)
|
||||
fmt.Fprintf(os.Stderr, "load config: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
logger.Init(cfg.Log.Level, cfg.Log.Format)
|
||||
|
||||
if strings.TrimSpace(cfg.Auth.JWTSecret) == "" {
|
||||
log.Fatalf("jwt secret is required")
|
||||
logger.Error("jwt secret is required")
|
||||
os.Exit(1)
|
||||
}
|
||||
jwtExpiresIn, err := time.ParseDuration(cfg.Auth.JWTExpiresIn)
|
||||
if err != nil {
|
||||
log.Fatalf("invalid jwt expires in: %v", err)
|
||||
logger.Error("invalid jwt expires in", slog.Any("error", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
addr := cfg.Server.Addr
|
||||
@@ -57,30 +63,33 @@ func main() {
|
||||
factory := ctr.DefaultClientFactory{SocketPath: socketPath}
|
||||
client, err := factory.New(ctx)
|
||||
if err != nil {
|
||||
log.Fatalf("connect containerd: %v", err)
|
||||
logger.Error("connect containerd", slog.Any("error", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
service := ctr.NewDefaultService(client, cfg.Containerd.Namespace)
|
||||
manager := mcp.NewManager(service, cfg.MCP)
|
||||
service := ctr.NewDefaultService(logger.L, client, cfg.Containerd.Namespace)
|
||||
manager := mcp.NewManager(logger.L, service, cfg.MCP)
|
||||
|
||||
pingHandler := handlers.NewPingHandler()
|
||||
containerdHandler := handlers.NewContainerdHandler(service, cfg.MCP, cfg.Containerd.Namespace)
|
||||
pingHandler := handlers.NewPingHandler(logger.L)
|
||||
containerdHandler := handlers.NewContainerdHandler(logger.L, service, cfg.MCP, cfg.Containerd.Namespace)
|
||||
|
||||
conn, err := db.Open(ctx, cfg.Postgres)
|
||||
if err != nil {
|
||||
log.Fatalf("db connect: %v", err)
|
||||
logger.Error("db connect", slog.Any("error", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
defer conn.Close()
|
||||
manager.WithDB(conn)
|
||||
queries := dbsqlc.New(conn)
|
||||
modelsService := models.NewService(queries)
|
||||
modelsService := models.NewService(logger.L, queries)
|
||||
|
||||
if err := ensureAdminUser(ctx, queries, cfg); err != nil {
|
||||
log.Fatalf("ensure admin user: %v", err)
|
||||
if err := ensureAdminUser(ctx, logger.L, queries, cfg); err != nil {
|
||||
logger.Error("ensure admin user", slog.Any("error", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
authHandler := handlers.NewAuthHandler(conn, cfg.Auth.JWTSecret, jwtExpiresIn)
|
||||
authHandler := handlers.NewAuthHandler(logger.L, conn, cfg.Auth.JWTSecret, jwtExpiresIn)
|
||||
|
||||
// Initialize chat resolver after memory service is configured.
|
||||
var chatResolver *chat.Resolver
|
||||
@@ -90,27 +99,29 @@ func main() {
|
||||
modelsService: modelsService,
|
||||
queries: queries,
|
||||
timeout: 30 * time.Second,
|
||||
logger: logger.L,
|
||||
}
|
||||
|
||||
resolver := embeddings.NewResolver(modelsService, queries, 10*time.Second)
|
||||
resolver := embeddings.NewResolver(logger.L, modelsService, queries, 10*time.Second)
|
||||
vectors, textModel, multimodalModel, hasModels, err := embeddings.CollectEmbeddingVectors(ctx, modelsService)
|
||||
if err != nil {
|
||||
log.Fatalf("embedding models: %v", err)
|
||||
logger.Error("embedding models", slog.Any("error", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var memoryService *memory.Service
|
||||
var memoryHandler *handlers.MemoryHandler
|
||||
|
||||
if !hasModels {
|
||||
log.Println("WARNING: No embedding models configured. Memory service will not be available.")
|
||||
log.Println("You can add embedding models via the /models API endpoint.")
|
||||
memoryHandler = handlers.NewMemoryHandler(nil)
|
||||
logger.Warn("No embedding models configured. Memory service will not be available.")
|
||||
logger.Warn("You can add embedding models via the /models API endpoint.")
|
||||
memoryHandler = handlers.NewMemoryHandler(logger.L, nil)
|
||||
} else {
|
||||
if textModel.ModelID == "" {
|
||||
log.Println("WARNING: No text embedding model configured. Text embedding features will be limited.")
|
||||
logger.Warn("No text embedding model configured. Text embedding features will be limited.")
|
||||
}
|
||||
if multimodalModel.ModelID == "" {
|
||||
log.Println("WARNING: No multimodal embedding model configured. Multimodal embedding features will be limited.")
|
||||
logger.Warn("No multimodal embedding model configured. Multimodal embedding features will be limited.")
|
||||
}
|
||||
|
||||
var textEmbedder embeddings.Embedder
|
||||
@@ -125,6 +136,7 @@ func main() {
|
||||
|
||||
if len(vectors) > 0 {
|
||||
store, err = memory.NewQdrantStoreWithVectors(
|
||||
logger.L,
|
||||
cfg.Qdrant.BaseURL,
|
||||
cfg.Qdrant.APIKey,
|
||||
cfg.Qdrant.Collection,
|
||||
@@ -132,10 +144,12 @@ func main() {
|
||||
time.Duration(cfg.Qdrant.TimeoutSeconds)*time.Second,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("qdrant named vectors init: %v", err)
|
||||
logger.Error("qdrant named vectors init", slog.Any("error", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
store, err = memory.NewQdrantStore(
|
||||
logger.L,
|
||||
cfg.Qdrant.BaseURL,
|
||||
cfg.Qdrant.APIKey,
|
||||
cfg.Qdrant.Collection,
|
||||
@@ -143,42 +157,45 @@ func main() {
|
||||
time.Duration(cfg.Qdrant.TimeoutSeconds)*time.Second,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("qdrant init: %v", err)
|
||||
logger.Error("qdrant init", slog.Any("error", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memoryService = memory.NewService(llmClient, textEmbedder, store, resolver, textModel.ModelID, multimodalModel.ModelID)
|
||||
memoryHandler = handlers.NewMemoryHandler(memoryService)
|
||||
memoryService = memory.NewService(logger.L, llmClient, textEmbedder, store, resolver, textModel.ModelID, multimodalModel.ModelID)
|
||||
memoryHandler = handlers.NewMemoryHandler(logger.L, memoryService)
|
||||
}
|
||||
chatResolver = chat.NewResolver(modelsService, queries, memoryService, cfg.AgentGateway.BaseURL(), 30*time.Second)
|
||||
embeddingsHandler := handlers.NewEmbeddingsHandler(modelsService, queries)
|
||||
swaggerHandler := handlers.NewSwaggerHandler()
|
||||
chatHandler := handlers.NewChatHandler(chatResolver)
|
||||
chatResolver = chat.NewResolver(logger.L, modelsService, queries, memoryService, cfg.AgentGateway.BaseURL(), 30*time.Second)
|
||||
embeddingsHandler := handlers.NewEmbeddingsHandler(logger.L, modelsService, queries)
|
||||
swaggerHandler := handlers.NewSwaggerHandler(logger.L)
|
||||
chatHandler := handlers.NewChatHandler(logger.L, chatResolver)
|
||||
|
||||
// Initialize providers and models handlers
|
||||
providersService := providers.NewService(queries)
|
||||
providersHandler := handlers.NewProvidersHandler(providersService)
|
||||
modelsHandler := handlers.NewModelsHandler(modelsService)
|
||||
settingsService := settings.NewService(queries)
|
||||
settingsHandler := handlers.NewSettingsHandler(settingsService)
|
||||
historyService := history.NewService(queries)
|
||||
historyHandler := handlers.NewHistoryHandler(historyService)
|
||||
scheduleService := schedule.NewService(queries, chatResolver, cfg.Auth.JWTSecret)
|
||||
providersService := providers.NewService(logger.L, queries)
|
||||
providersHandler := handlers.NewProvidersHandler(logger.L, providersService)
|
||||
modelsHandler := handlers.NewModelsHandler(logger.L, modelsService)
|
||||
settingsService := settings.NewService(logger.L, queries)
|
||||
settingsHandler := handlers.NewSettingsHandler(logger.L, settingsService)
|
||||
historyService := history.NewService(logger.L, queries)
|
||||
historyHandler := handlers.NewHistoryHandler(logger.L, historyService)
|
||||
scheduleService := schedule.NewService(logger.L, queries, chatResolver, cfg.Auth.JWTSecret)
|
||||
if err := scheduleService.Bootstrap(ctx); err != nil {
|
||||
log.Fatalf("schedule bootstrap: %v", err)
|
||||
logger.Error("schedule bootstrap", slog.Any("error", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
scheduleHandler := handlers.NewScheduleHandler(scheduleService)
|
||||
subagentService := subagent.NewService(queries)
|
||||
subagentHandler := handlers.NewSubagentHandler(subagentService)
|
||||
srv := server.NewServer(addr, cfg.Auth.JWTSecret, pingHandler, authHandler, memoryHandler, embeddingsHandler, chatHandler, swaggerHandler, providersHandler, modelsHandler, settingsHandler, historyHandler, scheduleHandler, subagentHandler, containerdHandler)
|
||||
scheduleHandler := handlers.NewScheduleHandler(logger.L, scheduleService)
|
||||
subagentService := subagent.NewService(logger.L, queries)
|
||||
subagentHandler := handlers.NewSubagentHandler(logger.L, subagentService)
|
||||
srv := server.NewServer(logger.L, addr, cfg.Auth.JWTSecret, pingHandler, authHandler, memoryHandler, embeddingsHandler, chatHandler, swaggerHandler, providersHandler, modelsHandler, settingsHandler, historyHandler, scheduleHandler, subagentHandler, containerdHandler)
|
||||
|
||||
if err := srv.Start(); err != nil {
|
||||
log.Fatalf("server failed: %v", err)
|
||||
logger.Error("server failed", slog.Any("error", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func ensureAdminUser(ctx context.Context, queries *dbsqlc.Queries, cfg config.Config) error {
|
||||
func ensureAdminUser(ctx context.Context, log *slog.Logger, queries *dbsqlc.Queries, cfg config.Config) error {
|
||||
if queries == nil {
|
||||
return fmt.Errorf("db queries not configured")
|
||||
}
|
||||
@@ -197,7 +214,7 @@ func ensureAdminUser(ctx context.Context, queries *dbsqlc.Queries, cfg config.Co
|
||||
return fmt.Errorf("admin username/password required in config.toml")
|
||||
}
|
||||
if password == "change-your-password-here" {
|
||||
log.Printf("WARNING: admin password uses default placeholder; please update config.toml")
|
||||
log.Warn("admin password uses default placeholder; please update config.toml")
|
||||
}
|
||||
|
||||
hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
@@ -225,7 +242,7 @@ func ensureAdminUser(ctx context.Context, queries *dbsqlc.Queries, cfg config.Co
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("Admin user created: %s", username)
|
||||
log.Info("Admin user created", slog.String("username", username))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -233,6 +250,7 @@ type lazyLLMClient struct {
|
||||
modelsService *models.Service
|
||||
queries *dbsqlc.Queries
|
||||
timeout time.Duration
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func (c *lazyLLMClient) Extract(ctx context.Context, req memory.ExtractRequest) (memory.ExtractResponse, error) {
|
||||
@@ -263,5 +281,5 @@ func (c *lazyLLMClient) resolve(ctx context.Context) (memory.LLM, error) {
|
||||
if clientType != "openai" && clientType != "openai-compat" {
|
||||
return nil, fmt.Errorf("memory provider client type not supported: %s", memoryProvider.ClientType)
|
||||
}
|
||||
return memory.NewLLMClient(memoryProvider.BaseUrl, memoryProvider.ApiKey, memoryModel.ModelID, c.timeout), nil
|
||||
return memory.NewLLMClient(c.logger, memoryProvider.BaseUrl, memoryProvider.ApiKey, memoryModel.ModelID, c.timeout), nil
|
||||
}
|
||||
|
||||
+17
-7
@@ -8,7 +8,7 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
"github.com/memohai/memoh/internal/chat"
|
||||
"github.com/memohai/memoh/internal/config"
|
||||
"github.com/memohai/memoh/internal/logger"
|
||||
)
|
||||
|
||||
type cliOptions struct {
|
||||
@@ -33,13 +34,18 @@ func main() {
|
||||
|
||||
cfg, err := config.Load(opts.configPath)
|
||||
if err != nil {
|
||||
log.Fatalf("load config: %v", err)
|
||||
fmt.Fprintf(os.Stderr, "load config: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
logger.Init(cfg.Log.Level, cfg.Log.Format)
|
||||
|
||||
if strings.TrimSpace(opts.apiBaseURL) == "" {
|
||||
opts.apiBaseURL = defaultAPIBaseURL(cfg.Server.Addr)
|
||||
}
|
||||
if strings.TrimSpace(opts.apiBaseURL) == "" {
|
||||
log.Fatalf("api url is required")
|
||||
logger.Error("api url is required")
|
||||
os.Exit(1)
|
||||
}
|
||||
opts.apiBaseURL = normalizeBaseURL(opts.apiBaseURL)
|
||||
|
||||
@@ -48,7 +54,8 @@ func main() {
|
||||
if jwtToken == "" {
|
||||
username, password, err := resolveLoginCredentials(opts, cfg)
|
||||
if err != nil {
|
||||
log.Fatalf("resolve login: %v", err)
|
||||
logger.Error("resolve login", slog.Any("error", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
loginCtx := ctx
|
||||
if opts.timeout > 0 {
|
||||
@@ -58,19 +65,22 @@ func main() {
|
||||
}
|
||||
jwtToken, err = resolveJWTToken(loginCtx, client, opts.apiBaseURL, username, password)
|
||||
if err != nil {
|
||||
log.Fatalf("resolve jwt: %v", err)
|
||||
logger.Error("resolve jwt", slog.Any("error", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
query := strings.TrimSpace(strings.Join(flag.Args(), " "))
|
||||
if query != "" {
|
||||
if err := sendChat(ctx, client, opts.apiBaseURL, jwtToken, query); err != nil {
|
||||
log.Fatalf("chat failed: %v", err)
|
||||
logger.Error("chat failed", slog.Any("error", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err := runInteractive(ctx, client, opts.apiBaseURL, jwtToken); err != nil {
|
||||
log.Fatalf("chat failed: %v", err)
|
||||
logger.Error("chat failed", slog.Any("error", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-2
@@ -2,8 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"github.com/memohai/memoh/internal/logger"
|
||||
"github.com/memohai/memoh/internal/mcp"
|
||||
gomcp "github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
)
|
||||
@@ -17,12 +19,24 @@ func main() {
|
||||
if version == "unknown" {
|
||||
version = "v0.0.0-dev+" + commitHash
|
||||
}
|
||||
|
||||
logLevel := os.Getenv("LOG_LEVEL")
|
||||
if logLevel == "" {
|
||||
logLevel = "info"
|
||||
}
|
||||
logFormat := os.Getenv("LOG_FORMAT")
|
||||
if logFormat == "" {
|
||||
logFormat = "text"
|
||||
}
|
||||
logger.Init(logLevel, logFormat)
|
||||
|
||||
server := gomcp.NewServer(
|
||||
&gomcp.Implementation{Name: "memoh-mcp", Version: version},
|
||||
nil,
|
||||
)
|
||||
mcp.RegisterTools(server)
|
||||
if err := server.Run(context.Background(), &gomcp.StdioTransport{}); err != nil {
|
||||
log.Fatal(err)
|
||||
logger.Error("mcp server failed", slog.Any("error", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
## Service configuration
|
||||
[log]
|
||||
level = "info"
|
||||
format = "text"
|
||||
|
||||
[server]
|
||||
# HTTP listen address
|
||||
addr = ":8080"
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -30,11 +31,12 @@ type Resolver struct {
|
||||
memoryService *memory.Service
|
||||
gatewayBaseURL string
|
||||
timeout time.Duration
|
||||
logger *slog.Logger
|
||||
httpClient *http.Client
|
||||
streamingClient *http.Client
|
||||
}
|
||||
|
||||
func NewResolver(modelsService *models.Service, queries *sqlc.Queries, memoryService *memory.Service, gatewayBaseURL string, timeout time.Duration) *Resolver {
|
||||
func NewResolver(log *slog.Logger, modelsService *models.Service, queries *sqlc.Queries, memoryService *memory.Service, gatewayBaseURL string, timeout time.Duration) *Resolver {
|
||||
if strings.TrimSpace(gatewayBaseURL) == "" {
|
||||
gatewayBaseURL = "http://127.0.0.1:8081"
|
||||
}
|
||||
@@ -48,6 +50,7 @@ func NewResolver(modelsService *models.Service, queries *sqlc.Queries, memorySer
|
||||
memoryService: memoryService,
|
||||
gatewayBaseURL: gatewayBaseURL,
|
||||
timeout: timeout,
|
||||
logger: log.With(slog.String("service", "chat")),
|
||||
httpClient: &http.Client{
|
||||
Timeout: timeout,
|
||||
},
|
||||
|
||||
@@ -26,6 +26,7 @@ const (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Log LogConfig `toml:"log"`
|
||||
Server ServerConfig `toml:"server"`
|
||||
Admin AdminConfig `toml:"admin"`
|
||||
Auth AuthConfig `toml:"auth"`
|
||||
@@ -36,6 +37,11 @@ type Config struct {
|
||||
AgentGateway AgentGatewayConfig `toml:"agent_gateway"`
|
||||
}
|
||||
|
||||
type LogConfig struct {
|
||||
Level string `toml:"level"`
|
||||
Format string `toml:"format"`
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
Addr string `toml:"addr"`
|
||||
}
|
||||
@@ -98,6 +104,10 @@ func (c AgentGatewayConfig) BaseURL() string {
|
||||
|
||||
func Load(path string) (Config, error) {
|
||||
cfg := Config{
|
||||
Log: LogConfig{
|
||||
Level: "info",
|
||||
Format: "text",
|
||||
},
|
||||
Server: ServerConfig{
|
||||
Addr: DefaultHTTPAddr,
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -144,15 +145,17 @@ type Service interface {
|
||||
type DefaultService struct {
|
||||
client *containerd.Client
|
||||
namespace string
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewDefaultService(client *containerd.Client, namespace string) *DefaultService {
|
||||
func NewDefaultService(log *slog.Logger, client *containerd.Client, namespace string) *DefaultService {
|
||||
if namespace == "" {
|
||||
namespace = DefaultNamespace
|
||||
}
|
||||
return &DefaultService{
|
||||
client: client,
|
||||
namespace: namespace,
|
||||
logger: log.With(slog.String("service", "containerd")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -20,6 +21,7 @@ type DashScopeEmbedder struct {
|
||||
apiKey string
|
||||
baseURL string
|
||||
model string
|
||||
logger *slog.Logger
|
||||
http *http.Client
|
||||
}
|
||||
|
||||
@@ -53,7 +55,7 @@ type dashScopeResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func NewDashScopeEmbedder(apiKey, baseURL, model string, timeout time.Duration) *DashScopeEmbedder {
|
||||
func NewDashScopeEmbedder(log *slog.Logger, apiKey, baseURL, model string, timeout time.Duration) *DashScopeEmbedder {
|
||||
if baseURL == "" {
|
||||
baseURL = DefaultDashScopeBaseURL
|
||||
}
|
||||
@@ -64,6 +66,7 @@ func NewDashScopeEmbedder(apiKey, baseURL, model string, timeout time.Duration)
|
||||
apiKey: apiKey,
|
||||
baseURL: strings.TrimRight(baseURL, "/"),
|
||||
model: model,
|
||||
logger: log.With(slog.String("embedder", "dashscope")),
|
||||
http: &http.Client{
|
||||
Timeout: timeout,
|
||||
},
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -21,6 +22,7 @@ type OpenAIEmbedder struct {
|
||||
baseURL string
|
||||
model string
|
||||
dims int
|
||||
logger *slog.Logger
|
||||
http *http.Client
|
||||
}
|
||||
|
||||
@@ -35,7 +37,7 @@ type openAIEmbeddingResponse struct {
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
func NewOpenAIEmbedder(apiKey, baseURL, model string, dims int, timeout time.Duration) *OpenAIEmbedder {
|
||||
func NewOpenAIEmbedder(log *slog.Logger, apiKey, baseURL, model string, dims int, timeout time.Duration) *OpenAIEmbedder {
|
||||
if baseURL == "" {
|
||||
baseURL = "https://api.openai.com"
|
||||
}
|
||||
@@ -53,6 +55,7 @@ func NewOpenAIEmbedder(apiKey, baseURL, model string, dims int, timeout time.Dur
|
||||
baseURL: strings.TrimRight(baseURL, "/"),
|
||||
model: model,
|
||||
dims: dims,
|
||||
logger: log.With(slog.String("embedder", "openai")),
|
||||
http: &http.Client{
|
||||
Timeout: timeout,
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@ package embeddings
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -55,13 +56,15 @@ type Resolver struct {
|
||||
modelsService *models.Service
|
||||
queries *sqlc.Queries
|
||||
timeout time.Duration
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewResolver(modelsService *models.Service, queries *sqlc.Queries, timeout time.Duration) *Resolver {
|
||||
func NewResolver(log *slog.Logger, modelsService *models.Service, queries *sqlc.Queries, timeout time.Duration) *Resolver {
|
||||
return &Resolver{
|
||||
modelsService: modelsService,
|
||||
queries: queries,
|
||||
timeout: timeout,
|
||||
logger: log.With(slog.String("service", "embeddings")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +130,7 @@ func (r *Resolver) Embed(ctx context.Context, req Request) (Result, error) {
|
||||
if strings.TrimSpace(provider.ApiKey) == "" {
|
||||
return Result{}, errors.New("openai api key is required")
|
||||
}
|
||||
embedder := NewOpenAIEmbedder(provider.ApiKey, provider.BaseUrl, req.Model, req.Dimensions, timeout)
|
||||
embedder := NewOpenAIEmbedder(r.logger, provider.ApiKey, provider.BaseUrl, req.Model, req.Dimensions, timeout)
|
||||
vector, err := embedder.Embed(ctx, req.Input.Text)
|
||||
if err != nil {
|
||||
return Result{}, err
|
||||
@@ -144,7 +147,7 @@ func (r *Resolver) Embed(ctx context.Context, req Request) (Result, error) {
|
||||
if strings.TrimSpace(provider.ApiKey) == "" {
|
||||
return Result{}, errors.New("dashscope api key is required")
|
||||
}
|
||||
dashscope := NewDashScopeEmbedder(provider.ApiKey, provider.BaseUrl, req.Model, timeout)
|
||||
dashscope := NewDashScopeEmbedder(r.logger, provider.ApiKey, provider.BaseUrl, req.Model, timeout)
|
||||
vector, usage, err := dashscope.Embed(ctx, req.Input.Text, req.Input.ImageURL, req.Input.VideoURL)
|
||||
if err != nil {
|
||||
return Result{}, err
|
||||
|
||||
@@ -3,6 +3,7 @@ package handlers
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -22,6 +23,7 @@ type AuthHandler struct {
|
||||
db *pgxpool.Pool
|
||||
jwtSecret string
|
||||
expiresIn time.Duration
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
type LoginRequest struct {
|
||||
@@ -39,11 +41,12 @@ type LoginResponse struct {
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
func NewAuthHandler(db *pgxpool.Pool, jwtSecret string, expiresIn time.Duration) *AuthHandler {
|
||||
func NewAuthHandler(log *slog.Logger, db *pgxpool.Pool, jwtSecret string, expiresIn time.Duration) *AuthHandler {
|
||||
return &AuthHandler{
|
||||
db: db,
|
||||
jwtSecret: jwtSecret,
|
||||
expiresIn: expiresIn,
|
||||
logger: log.With(slog.String("handler", "auth")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
@@ -15,10 +16,14 @@ import (
|
||||
|
||||
type ChatHandler struct {
|
||||
resolver *chat.Resolver
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewChatHandler(resolver *chat.Resolver) *ChatHandler {
|
||||
return &ChatHandler{resolver: resolver}
|
||||
func NewChatHandler(log *slog.Logger, resolver *chat.Resolver) *ChatHandler {
|
||||
return &ChatHandler{
|
||||
resolver: resolver,
|
||||
logger: log.With(slog.String("handler", "chat")),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ChatHandler) Register(e *echo.Echo) {
|
||||
|
||||
@@ -2,7 +2,7 @@ package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -28,6 +28,7 @@ type ContainerdHandler struct {
|
||||
service ctr.Service
|
||||
cfg config.MCPConfig
|
||||
namespace string
|
||||
logger *slog.Logger
|
||||
mcpMu sync.Mutex
|
||||
mcpSess map[string]*mcpSession
|
||||
}
|
||||
@@ -85,11 +86,12 @@ type ListSnapshotsResponse struct {
|
||||
Snapshots []SnapshotInfo `json:"snapshots"`
|
||||
}
|
||||
|
||||
func NewContainerdHandler(service ctr.Service, cfg config.MCPConfig, namespace string) *ContainerdHandler {
|
||||
func NewContainerdHandler(log *slog.Logger, service ctr.Service, cfg config.MCPConfig, namespace string) *ContainerdHandler {
|
||||
return &ContainerdHandler{
|
||||
service: service,
|
||||
cfg: cfg,
|
||||
namespace: namespace,
|
||||
logger: log.With(slog.String("handler", "containerd")),
|
||||
mcpSess: make(map[string]*mcpSession),
|
||||
}
|
||||
}
|
||||
@@ -196,7 +198,10 @@ func (h *ContainerdHandler) CreateContainer(c echo.Context) error {
|
||||
}); err == nil {
|
||||
started = true
|
||||
} else {
|
||||
log.Printf("mcp container start failed: id=%s err=%v", req.ContainerID, err)
|
||||
h.logger.Error("mcp container start failed",
|
||||
slog.String("container_id", req.ContainerID),
|
||||
slog.Any("error", err),
|
||||
)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, CreateContainerResponse{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -16,6 +17,7 @@ const DefaultEmbeddingTimeout = 10 * time.Second
|
||||
|
||||
type EmbeddingsHandler struct {
|
||||
resolver *embeddings.Resolver
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
type EmbeddingsRequest struct {
|
||||
@@ -48,9 +50,10 @@ type EmbeddingsUsage struct {
|
||||
VideoTokens int `json:"video_tokens,omitempty"`
|
||||
}
|
||||
|
||||
func NewEmbeddingsHandler(modelsService *models.Service, queries *sqlc.Queries) *EmbeddingsHandler {
|
||||
func NewEmbeddingsHandler(log *slog.Logger, modelsService *models.Service, queries *sqlc.Queries) *EmbeddingsHandler {
|
||||
return &EmbeddingsHandler{
|
||||
resolver: embeddings.NewResolver(modelsService, queries, DefaultEmbeddingTimeout),
|
||||
resolver: embeddings.NewResolver(log, modelsService, queries, DefaultEmbeddingTimeout),
|
||||
logger: log.With(slog.String("handler", "embeddings")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
@@ -13,10 +14,14 @@ import (
|
||||
|
||||
type HistoryHandler struct {
|
||||
service *history.Service
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewHistoryHandler(service *history.Service) *HistoryHandler {
|
||||
return &HistoryHandler{service: service}
|
||||
func NewHistoryHandler(log *slog.Logger, service *history.Service) *HistoryHandler {
|
||||
return &HistoryHandler{
|
||||
service: service,
|
||||
logger: log.With(slog.String("handler", "history")),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HistoryHandler) Register(e *echo.Echo) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
@@ -13,10 +14,14 @@ import (
|
||||
|
||||
type MemoryHandler struct {
|
||||
service *memory.Service
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewMemoryHandler(service *memory.Service) *MemoryHandler {
|
||||
return &MemoryHandler{service: service}
|
||||
func NewMemoryHandler(log *slog.Logger, service *memory.Service) *MemoryHandler {
|
||||
return &MemoryHandler{
|
||||
service: service,
|
||||
logger: log.With(slog.String("handler", "memory")),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *MemoryHandler) Register(e *echo.Echo) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
@@ -11,10 +12,14 @@ import (
|
||||
|
||||
type ModelsHandler struct {
|
||||
service *models.Service
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewModelsHandler(service *models.Service) *ModelsHandler {
|
||||
return &ModelsHandler{service: service}
|
||||
func NewModelsHandler(log *slog.Logger, service *models.Service) *ModelsHandler {
|
||||
return &ModelsHandler{
|
||||
service: service,
|
||||
logger: log.With(slog.String("handler", "models")),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ModelsHandler) Register(e *echo.Echo) {
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type PingHandler struct{}
|
||||
type PingHandler struct {
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewPingHandler() *PingHandler {
|
||||
return &PingHandler{}
|
||||
func NewPingHandler(log *slog.Logger) *PingHandler {
|
||||
return &PingHandler{logger: log.With(slog.String("handler", "ping"))}
|
||||
}
|
||||
|
||||
func (h *PingHandler) Register(e *echo.Echo) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
@@ -10,10 +11,14 @@ import (
|
||||
|
||||
type ProvidersHandler struct {
|
||||
service *providers.Service
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewProvidersHandler(service *providers.Service) *ProvidersHandler {
|
||||
return &ProvidersHandler{service: service}
|
||||
func NewProvidersHandler(log *slog.Logger, service *providers.Service) *ProvidersHandler {
|
||||
return &ProvidersHandler{
|
||||
service: service,
|
||||
logger: log.With(slog.String("handler", "providers")),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ProvidersHandler) Register(e *echo.Echo) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
@@ -12,10 +13,14 @@ import (
|
||||
|
||||
type ScheduleHandler struct {
|
||||
service *schedule.Service
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewScheduleHandler(service *schedule.Service) *ScheduleHandler {
|
||||
return &ScheduleHandler{service: service}
|
||||
func NewScheduleHandler(log *slog.Logger, service *schedule.Service) *ScheduleHandler {
|
||||
return &ScheduleHandler{
|
||||
service: service,
|
||||
logger: log.With(slog.String("handler", "schedule")),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ScheduleHandler) Register(e *echo.Echo) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
@@ -12,10 +13,14 @@ import (
|
||||
|
||||
type SettingsHandler struct {
|
||||
service *settings.Service
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewSettingsHandler(service *settings.Service) *SettingsHandler {
|
||||
return &SettingsHandler{service: service}
|
||||
func NewSettingsHandler(log *slog.Logger, service *settings.Service) *SettingsHandler {
|
||||
return &SettingsHandler{
|
||||
service: service,
|
||||
logger: log.With(slog.String("handler", "settings")),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *SettingsHandler) Register(e *echo.Echo) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
@@ -12,10 +13,14 @@ import (
|
||||
|
||||
type SubagentHandler struct {
|
||||
service *subagent.Service
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewSubagentHandler(service *subagent.Service) *SubagentHandler {
|
||||
return &SubagentHandler{service: service}
|
||||
func NewSubagentHandler(log *slog.Logger, service *subagent.Service) *SubagentHandler {
|
||||
return &SubagentHandler{
|
||||
service: service,
|
||||
logger: log.With(slog.String("handler", "subagent")),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *SubagentHandler) Register(e *echo.Echo) {
|
||||
|
||||
@@ -4,6 +4,7 @@ package handlers
|
||||
// @version 1.0.0
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
@@ -19,10 +20,12 @@ var (
|
||||
swaggerErr error
|
||||
)
|
||||
|
||||
type SwaggerHandler struct{}
|
||||
type SwaggerHandler struct {
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewSwaggerHandler() *SwaggerHandler {
|
||||
return &SwaggerHandler{}
|
||||
func NewSwaggerHandler(log *slog.Logger) *SwaggerHandler {
|
||||
return &SwaggerHandler{logger: log.With(slog.String("handler", "swagger"))}
|
||||
}
|
||||
|
||||
func (h *SwaggerHandler) Register(e *echo.Echo) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -19,10 +20,14 @@ const defaultListLimit = 50
|
||||
|
||||
type Service struct {
|
||||
queries *sqlc.Queries
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewService(queries *sqlc.Queries) *Service {
|
||||
return &Service{queries: queries}
|
||||
func NewService(log *slog.Logger, queries *sqlc.Queries) *Service {
|
||||
return &Service{
|
||||
queries: queries,
|
||||
logger: log.With(slog.String("service", "history")),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) Create(ctx context.Context, userID string, req CreateRequest) (Record, error) {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ctxKey struct{}
|
||||
|
||||
var (
|
||||
L *slog.Logger = slog.Default()
|
||||
logKey = ctxKey{}
|
||||
)
|
||||
|
||||
// Init 初始化全局日志
|
||||
func Init(level, format string) {
|
||||
var handler slog.Handler
|
||||
opts := &slog.HandlerOptions{
|
||||
Level: parseLevel(level),
|
||||
}
|
||||
|
||||
if strings.ToLower(format) == "json" {
|
||||
handler = slog.NewJSONHandler(os.Stdout, opts)
|
||||
} else {
|
||||
handler = slog.NewTextHandler(os.Stdout, opts)
|
||||
}
|
||||
|
||||
L = slog.New(handler)
|
||||
slog.SetDefault(L)
|
||||
}
|
||||
|
||||
// FromContext 从 context 中获取 logger,如果不存在则返回全局 logger
|
||||
func FromContext(ctx context.Context) *slog.Logger {
|
||||
if l, ok := ctx.Value(logKey).(*slog.Logger); ok {
|
||||
return l
|
||||
}
|
||||
return L
|
||||
}
|
||||
|
||||
// WithContext 将 logger 注入 context
|
||||
func WithContext(ctx context.Context, l *slog.Logger) context.Context {
|
||||
return context.WithValue(ctx, logKey, l)
|
||||
}
|
||||
|
||||
func parseLevel(level string) slog.Level {
|
||||
switch strings.ToLower(level) {
|
||||
case "debug":
|
||||
return slog.LevelDebug
|
||||
case "info":
|
||||
return slog.LevelInfo
|
||||
case "warn":
|
||||
return slog.LevelWarn
|
||||
case "error":
|
||||
return slog.LevelError
|
||||
default:
|
||||
return slog.LevelInfo
|
||||
}
|
||||
}
|
||||
|
||||
// 快捷方法,支持强类型 slog.Attr 或松散的 key-value 对
|
||||
func Debug(msg string, args ...any) { L.Debug(msg, args...) }
|
||||
func Info(msg string, args ...any) { L.Info(msg, args...) }
|
||||
func Warn(msg string, args ...any) { L.Warn(msg, args...) }
|
||||
func Error(msg string, args ...any) { L.Error(msg, args...) }
|
||||
@@ -0,0 +1,55 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInitAndLogging(t *testing.T) {
|
||||
// 测试 JSON 格式
|
||||
Init("debug", "json")
|
||||
|
||||
if L.Enabled(context.Background(), slog.LevelDebug) != true {
|
||||
t.Error("expected debug level to be enabled")
|
||||
}
|
||||
|
||||
// 验证是否能正常输出(不崩溃)
|
||||
Info("test info message", "key", "value")
|
||||
}
|
||||
|
||||
func TestContextLogger(t *testing.T) {
|
||||
Init("info", "text")
|
||||
|
||||
// 创建一个带特定属性的 logger
|
||||
expectedKey := "request_id"
|
||||
expectedValue := "12345"
|
||||
customLogger := L.With(expectedKey, expectedValue)
|
||||
|
||||
ctx := WithContext(context.Background(), customLogger)
|
||||
extracted := FromContext(ctx)
|
||||
|
||||
// 这里简单验证提取出来的是否是同一个(或者功能一致)
|
||||
if extracted == nil {
|
||||
t.Fatal("extracted logger should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseLevel(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected slog.Level
|
||||
}{
|
||||
{"debug", slog.LevelDebug},
|
||||
{"INFO", slog.LevelInfo},
|
||||
{"Warn", slog.LevelWarn},
|
||||
{"error", slog.LevelError},
|
||||
{"unknown", slog.LevelInfo},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
if got := parseLevel(tt.input); got != tt.expected {
|
||||
t.Errorf("parseLevel(%s) = %v, want %v", tt.input, got, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package mcp
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -43,12 +44,14 @@ type Manager struct {
|
||||
containerID func(string) string
|
||||
db *pgxpool.Pool
|
||||
queries *dbsqlc.Queries
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewManager(service ctr.Service, cfg config.MCPConfig) *Manager {
|
||||
func NewManager(log *slog.Logger, service ctr.Service, cfg config.MCPConfig) *Manager {
|
||||
return &Manager{
|
||||
service: service,
|
||||
cfg: cfg,
|
||||
logger: log.With(slog.String("manager", "mcp")),
|
||||
containerID: func(userID string) string {
|
||||
return ContainerPrefix + userID
|
||||
},
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -15,10 +16,11 @@ type LLMClient struct {
|
||||
baseURL string
|
||||
apiKey string
|
||||
model string
|
||||
logger *slog.Logger
|
||||
http *http.Client
|
||||
}
|
||||
|
||||
func NewLLMClient(baseURL, apiKey, model string, timeout time.Duration) *LLMClient {
|
||||
func NewLLMClient(log *slog.Logger, baseURL, apiKey, model string, timeout time.Duration) *LLMClient {
|
||||
if baseURL == "" {
|
||||
baseURL = "https://api.openai.com/v1"
|
||||
}
|
||||
@@ -33,6 +35,7 @@ func NewLLMClient(baseURL, apiKey, model string, timeout time.Duration) *LLMClie
|
||||
baseURL: baseURL,
|
||||
apiKey: apiKey,
|
||||
model: model,
|
||||
logger: log.With(slog.String("client", "llm")),
|
||||
http: &http.Client{
|
||||
Timeout: timeout,
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@ package memory
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -18,6 +19,7 @@ type QdrantStore struct {
|
||||
baseURL string
|
||||
apiKey string
|
||||
timeout time.Duration
|
||||
logger *slog.Logger
|
||||
vectorNames map[string]int
|
||||
usesNamedVectors bool
|
||||
}
|
||||
@@ -29,7 +31,7 @@ type qdrantPoint struct {
|
||||
Payload map[string]interface{} `json:"payload,omitempty"`
|
||||
}
|
||||
|
||||
func NewQdrantStore(baseURL, apiKey, collection string, dimension int, timeout time.Duration) (*QdrantStore, error) {
|
||||
func NewQdrantStore(log *slog.Logger, baseURL, apiKey, collection string, dimension int, timeout time.Duration) (*QdrantStore, error) {
|
||||
host, port, useTLS, err := parseQdrantEndpoint(baseURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -59,6 +61,7 @@ func NewQdrantStore(baseURL, apiKey, collection string, dimension int, timeout t
|
||||
baseURL: baseURL,
|
||||
apiKey: apiKey,
|
||||
timeout: timeoutOrDefault(timeout),
|
||||
logger: log.With(slog.String("store", "qdrant")),
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeoutOrDefault(timeout))
|
||||
@@ -70,10 +73,10 @@ func NewQdrantStore(baseURL, apiKey, collection string, dimension int, timeout t
|
||||
}
|
||||
|
||||
func (s *QdrantStore) NewSibling(collection string, dimension int) (*QdrantStore, error) {
|
||||
return NewQdrantStore(s.baseURL, s.apiKey, collection, dimension, s.timeout)
|
||||
return NewQdrantStore(s.logger, s.baseURL, s.apiKey, collection, dimension, s.timeout)
|
||||
}
|
||||
|
||||
func NewQdrantStoreWithVectors(baseURL, apiKey, collection string, vectors map[string]int, timeout time.Duration) (*QdrantStore, error) {
|
||||
func NewQdrantStoreWithVectors(log *slog.Logger, baseURL, apiKey, collection string, vectors map[string]int, timeout time.Duration) (*QdrantStore, error) {
|
||||
host, port, useTLS, err := parseQdrantEndpoint(baseURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -102,6 +105,7 @@ func NewQdrantStoreWithVectors(baseURL, apiKey, collection string, vectors map[s
|
||||
baseURL: baseURL,
|
||||
apiKey: apiKey,
|
||||
timeout: timeoutOrDefault(timeout),
|
||||
logger: log.With(slog.String("store", "qdrant")),
|
||||
vectorNames: vectors,
|
||||
usesNamedVectors: true,
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -20,16 +21,18 @@ type Service struct {
|
||||
embedder embeddings.Embedder
|
||||
store *QdrantStore
|
||||
resolver *embeddings.Resolver
|
||||
logger *slog.Logger
|
||||
defaultTextModelID string
|
||||
defaultMultimodalModelID string
|
||||
}
|
||||
|
||||
func NewService(llm LLM, embedder embeddings.Embedder, store *QdrantStore, resolver *embeddings.Resolver, defaultTextModelID, defaultMultimodalModelID string) *Service {
|
||||
func NewService(log *slog.Logger, llm LLM, embedder embeddings.Embedder, store *QdrantStore, resolver *embeddings.Resolver, defaultTextModelID, defaultMultimodalModelID string) *Service {
|
||||
return &Service{
|
||||
llm: llm,
|
||||
embedder: embedder,
|
||||
store: store,
|
||||
resolver: resolver,
|
||||
logger: log.With(slog.String("service", "memory")),
|
||||
defaultTextModelID: defaultTextModelID,
|
||||
defaultMultimodalModelID: defaultMultimodalModelID,
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package models
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
@@ -12,12 +13,14 @@ import (
|
||||
// Service provides CRUD operations for models
|
||||
type Service struct {
|
||||
queries *sqlc.Queries
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
// NewService creates a new models service
|
||||
func NewService(queries *sqlc.Queries) *Service {
|
||||
func NewService(log *slog.Logger, queries *sqlc.Queries) *Service {
|
||||
return &Service{
|
||||
queries: queries,
|
||||
logger: log.With(slog.String("service", "models")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -15,11 +16,15 @@ import (
|
||||
// Service handles provider operations
|
||||
type Service struct {
|
||||
queries *sqlc.Queries
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
// NewService creates a new provider service
|
||||
func NewService(queries *sqlc.Queries) *Service {
|
||||
return &Service{queries: queries}
|
||||
func NewService(log *slog.Logger, queries *sqlc.Queries) *Service {
|
||||
return &Service{
|
||||
queries: queries,
|
||||
logger: log.With(slog.String("service", "providers")),
|
||||
}
|
||||
}
|
||||
|
||||
// Create creates a new LLM provider
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -24,11 +25,12 @@ type Service struct {
|
||||
parser cron.Parser
|
||||
chat *chat.Resolver
|
||||
jwtSecret string
|
||||
logger *slog.Logger
|
||||
mu sync.Mutex
|
||||
jobs map[string]cron.EntryID
|
||||
}
|
||||
|
||||
func NewService(queries *sqlc.Queries, chatResolver *chat.Resolver, jwtSecret string) *Service {
|
||||
func NewService(log *slog.Logger, queries *sqlc.Queries, chatResolver *chat.Resolver, jwtSecret string) *Service {
|
||||
parser := cron.NewParser(cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)
|
||||
c := cron.New(cron.WithParser(parser))
|
||||
service := &Service{
|
||||
@@ -37,6 +39,7 @@ func NewService(queries *sqlc.Queries, chatResolver *chat.Resolver, jwtSecret st
|
||||
parser: parser,
|
||||
chat: chatResolver,
|
||||
jwtSecret: jwtSecret,
|
||||
logger: log.With(slog.String("service", "schedule")),
|
||||
jobs: map[string]cron.EntryID{},
|
||||
}
|
||||
c.Start()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
@@ -13,9 +14,10 @@ import (
|
||||
type Server struct {
|
||||
echo *echo.Echo
|
||||
addr string
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewServer(addr string, jwtSecret string, pingHandler *handlers.PingHandler, authHandler *handlers.AuthHandler, memoryHandler *handlers.MemoryHandler, embeddingsHandler *handlers.EmbeddingsHandler, chatHandler *handlers.ChatHandler, swaggerHandler *handlers.SwaggerHandler, providersHandler *handlers.ProvidersHandler, modelsHandler *handlers.ModelsHandler, settingsHandler *handlers.SettingsHandler, historyHandler *handlers.HistoryHandler, scheduleHandler *handlers.ScheduleHandler, subagentHandler *handlers.SubagentHandler, containerdHandler *handlers.ContainerdHandler) *Server {
|
||||
func NewServer(log *slog.Logger, addr string, jwtSecret string, pingHandler *handlers.PingHandler, authHandler *handlers.AuthHandler, memoryHandler *handlers.MemoryHandler, embeddingsHandler *handlers.EmbeddingsHandler, chatHandler *handlers.ChatHandler, swaggerHandler *handlers.SwaggerHandler, providersHandler *handlers.ProvidersHandler, modelsHandler *handlers.ModelsHandler, settingsHandler *handlers.SettingsHandler, historyHandler *handlers.HistoryHandler, scheduleHandler *handlers.ScheduleHandler, subagentHandler *handlers.SubagentHandler, containerdHandler *handlers.ContainerdHandler) *Server {
|
||||
if addr == "" {
|
||||
addr = ":8080"
|
||||
}
|
||||
@@ -23,7 +25,21 @@ func NewServer(addr string, jwtSecret string, pingHandler *handlers.PingHandler,
|
||||
e := echo.New()
|
||||
e.HideBanner = true
|
||||
e.Use(middleware.Recover())
|
||||
e.Use(middleware.RequestLogger())
|
||||
e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
|
||||
LogStatus: true,
|
||||
LogURI: true,
|
||||
LogMethod: true,
|
||||
LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error {
|
||||
log.Info("request",
|
||||
slog.String("method", v.Method),
|
||||
slog.String("uri", v.URI),
|
||||
slog.Int("status", v.Status),
|
||||
slog.Duration("latency", v.Latency),
|
||||
slog.String("remote_ip", c.RealIP()),
|
||||
)
|
||||
return nil
|
||||
},
|
||||
}))
|
||||
e.Use(auth.JWTMiddleware(jwtSecret, func(c echo.Context) bool {
|
||||
path := c.Request().URL.Path
|
||||
if path == "/ping" || path == "/api/swagger.json" || path == "/auth/login" {
|
||||
@@ -78,6 +94,7 @@ func NewServer(addr string, jwtSecret string, pingHandler *handlers.PingHandler,
|
||||
return &Server{
|
||||
echo: e,
|
||||
addr: addr,
|
||||
logger: log.With(slog.String("component", "server")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -15,10 +16,14 @@ import (
|
||||
|
||||
type Service struct {
|
||||
queries *sqlc.Queries
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewService(queries *sqlc.Queries) *Service {
|
||||
return &Service{queries: queries}
|
||||
func NewService(log *slog.Logger, queries *sqlc.Queries) *Service {
|
||||
return &Service{
|
||||
queries: queries,
|
||||
logger: log.With(slog.String("service", "settings")),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) Get(ctx context.Context, userID string) (Settings, error) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -16,10 +17,14 @@ import (
|
||||
|
||||
type Service struct {
|
||||
queries *sqlc.Queries
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewService(queries *sqlc.Queries) *Service {
|
||||
return &Service{queries: queries}
|
||||
func NewService(log *slog.Logger, queries *sqlc.Queries) *Service {
|
||||
return &Service{
|
||||
queries: queries,
|
||||
logger: log.With(slog.String("service", "subagent")),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) Create(ctx context.Context, userID string, req CreateRequest) (Subagent, error) {
|
||||
|
||||
Reference in New Issue
Block a user