|
| 1 | +package main |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "fmt" |
| 6 | + "log/slog" |
| 7 | + |
| 8 | + "github.com/xortexai/xmem-go/internal/agents" |
| 9 | + "github.com/xortexai/xmem-go/internal/config" |
| 10 | + "github.com/xortexai/xmem-go/internal/database" |
| 11 | + "github.com/xortexai/xmem-go/internal/graph" |
| 12 | + "github.com/xortexai/xmem-go/internal/jobs" |
| 13 | + "github.com/xortexai/xmem-go/internal/models" |
| 14 | + "github.com/xortexai/xmem-go/internal/pipelines" |
| 15 | + "github.com/xortexai/xmem-go/internal/storage" |
| 16 | + "github.com/xortexai/xmem-go/internal/weaver" |
| 17 | +) |
| 18 | + |
| 19 | +type runtimeDeps struct { |
| 20 | + ingest *pipelines.IngestPipeline |
| 21 | + retrieval *pipelines.RetrievalPipeline |
| 22 | + keyStore database.APIKeyStore |
| 23 | + jobStore jobs.Store |
| 24 | +} |
| 25 | + |
| 26 | +func buildRuntime(ctx context.Context, settings config.Settings, logger *slog.Logger) (runtimeDeps, error) { |
| 27 | + embedder, err := buildEmbedder(settings, logger) |
| 28 | + if err != nil { |
| 29 | + return runtimeDeps{}, err |
| 30 | + } |
| 31 | + |
| 32 | + vectorStore, snippetStore, err := buildVectorStores(ctx, settings, embedder, logger) |
| 33 | + if err != nil { |
| 34 | + return runtimeDeps{}, err |
| 35 | + } |
| 36 | + |
| 37 | + temporalStore, err := buildTemporalStore(ctx, settings, logger) |
| 38 | + if err != nil { |
| 39 | + return runtimeDeps{}, err |
| 40 | + } |
| 41 | + |
| 42 | + model := models.NewRegistry(settings) |
| 43 | + ingest, retrieval := buildPipelines(model, vectorStore, snippetStore, temporalStore, embedder) |
| 44 | + |
| 45 | + keyStore, jobStore, err := buildAppStores(ctx, settings, logger) |
| 46 | + if err != nil { |
| 47 | + return runtimeDeps{}, err |
| 48 | + } |
| 49 | + |
| 50 | + return runtimeDeps{ |
| 51 | + ingest: ingest, |
| 52 | + retrieval: retrieval, |
| 53 | + keyStore: keyStore, |
| 54 | + jobStore: jobStore, |
| 55 | + }, nil |
| 56 | +} |
| 57 | + |
| 58 | +func buildEmbedder(settings config.Settings, logger *slog.Logger) (storage.Embedder, error) { |
| 59 | + fallback := storage.HashEmbedder{Dimension: settings.PineconeDimension} |
| 60 | + if settings.EmbeddingProvider != "openai" { |
| 61 | + return fallback, nil |
| 62 | + } |
| 63 | + |
| 64 | + openAIEmbedder, err := storage.NewOpenAIEmbedder(settings) |
| 65 | + if err == nil { |
| 66 | + logger.Info("using OpenAI embedder", "model", settings.OpenAIEmbeddingModel, "dimension", settings.PineconeDimension) |
| 67 | + return openAIEmbedder, nil |
| 68 | + } |
| 69 | + if production(settings) { |
| 70 | + return nil, fmt.Errorf("openai embedder initialization failed: %w", err) |
| 71 | + } |
| 72 | + logger.Warn("openai embedder unavailable, using hash embedder", "error", err) |
| 73 | + return fallback, nil |
| 74 | +} |
| 75 | + |
| 76 | +func buildVectorStores(ctx context.Context, settings config.Settings, embedder storage.Embedder, logger *slog.Logger) (storage.VectorStore, storage.VectorStore, error) { |
| 77 | + vectorStore := storage.VectorStore(storage.NewMemoryVectorStore()) |
| 78 | + snippetStore := storage.VectorStore(storage.NewMemoryVectorStore()) |
| 79 | + if settings.VectorStoreProvider != "pinecone" { |
| 80 | + return vectorStore, snippetStore, nil |
| 81 | + } |
| 82 | + |
| 83 | + pineconeStore, err := storage.NewPineconeVectorStore(ctx, settings, embedder, settings.PineconeNamespace) |
| 84 | + if err != nil { |
| 85 | + if production(settings) { |
| 86 | + return nil, nil, fmt.Errorf("pinecone vector store initialization failed: %w", err) |
| 87 | + } |
| 88 | + logger.Warn("pinecone unavailable, using memory vector store", "error", err) |
| 89 | + } else { |
| 90 | + vectorStore = pineconeStore |
| 91 | + logger.Info("using Pinecone vector store", "namespace", settings.PineconeNamespace) |
| 92 | + } |
| 93 | + |
| 94 | + snippetNamespace := settings.PineconeNamespace + "-snippets" |
| 95 | + pineconeSnippets, err := storage.NewPineconeVectorStore(ctx, settings, embedder, snippetNamespace) |
| 96 | + if err != nil { |
| 97 | + if production(settings) { |
| 98 | + return nil, nil, fmt.Errorf("pinecone snippet store initialization failed: %w", err) |
| 99 | + } |
| 100 | + logger.Warn("pinecone snippet store unavailable, using memory vector store", "error", err) |
| 101 | + } else { |
| 102 | + snippetStore = pineconeSnippets |
| 103 | + logger.Info("using Pinecone snippet vector store", "namespace", snippetNamespace) |
| 104 | + } |
| 105 | + |
| 106 | + return vectorStore, snippetStore, nil |
| 107 | +} |
| 108 | + |
| 109 | +func buildTemporalStore(ctx context.Context, settings config.Settings, logger *slog.Logger) (graph.TemporalStore, error) { |
| 110 | + fallback := graph.NewMemoryTemporalStore() |
| 111 | + if settings.Neo4jPassword == "" { |
| 112 | + return fallback, nil |
| 113 | + } |
| 114 | + |
| 115 | + neoStore, err := graph.NewNeo4jTemporalStore(ctx, settings) |
| 116 | + if err == nil { |
| 117 | + logger.Info("using Neo4j temporal store") |
| 118 | + return neoStore, nil |
| 119 | + } |
| 120 | + if production(settings) { |
| 121 | + return nil, fmt.Errorf("neo4j initialization failed: %w", err) |
| 122 | + } |
| 123 | + logger.Warn("neo4j unavailable, using memory temporal store", "error", err) |
| 124 | + return fallback, nil |
| 125 | +} |
| 126 | + |
| 127 | +func buildPipelines(model models.ChatModel, vectorStore storage.VectorStore, snippetStore storage.VectorStore, temporalStore graph.TemporalStore, embedder storage.Embedder) (*pipelines.IngestPipeline, *pipelines.RetrievalPipeline) { |
| 128 | + w := &weaver.Weaver{ |
| 129 | + VectorStore: vectorStore, |
| 130 | + SnippetVectorStore: snippetStore, |
| 131 | + Embedder: embedder, |
| 132 | + TemporalStore: temporalStore, |
| 133 | + } |
| 134 | + |
| 135 | + ingest := &pipelines.IngestPipeline{ |
| 136 | + ModelName: model.Name(), |
| 137 | + Weaver: w, |
| 138 | + Classifier: agents.ClassifierAgent{Model: model}, |
| 139 | + Profiler: agents.ProfilerAgent{Model: model}, |
| 140 | + Temporal: agents.TemporalAgent{Model: model}, |
| 141 | + Summarizer: agents.SummarizerAgent{Model: model}, |
| 142 | + Image: agents.ImageAgent{Model: model}, |
| 143 | + Snippet: agents.SnippetAgent{Model: model}, |
| 144 | + Judge: agents.JudgeAgent{Model: model, VectorStore: vectorStore, TopK: 3}, |
| 145 | + } |
| 146 | + retrieval := &pipelines.RetrievalPipeline{ |
| 147 | + Model: model, |
| 148 | + VectorStore: vectorStore, |
| 149 | + SnippetStore: snippetStore, |
| 150 | + TemporalStore: temporalStore, |
| 151 | + } |
| 152 | + return ingest, retrieval |
| 153 | +} |
| 154 | + |
| 155 | +func buildAppStores(ctx context.Context, settings config.Settings, logger *slog.Logger) (database.APIKeyStore, jobs.Store, error) { |
| 156 | + keyStore := database.APIKeyStore(database.NewMemoryAPIKeyStore()) |
| 157 | + jobStore := jobs.Store(jobs.NewMemoryStore()) |
| 158 | + if settings.AppStoreProvider != "mongo" { |
| 159 | + return keyStore, jobStore, nil |
| 160 | + } |
| 161 | + |
| 162 | + mongoStore, err := database.NewMongoAPIKeyStore(ctx, settings) |
| 163 | + if err != nil { |
| 164 | + if production(settings) { |
| 165 | + return nil, nil, fmt.Errorf("mongodb api key store initialization failed: %w", err) |
| 166 | + } |
| 167 | + logger.Warn("mongodb unavailable, using memory API key store", "error", err) |
| 168 | + } else { |
| 169 | + keyStore = mongoStore |
| 170 | + logger.Info("using MongoDB API key store") |
| 171 | + } |
| 172 | + |
| 173 | + durableStore, err := database.NewMongoDurableJobStore(ctx, settings) |
| 174 | + if err != nil { |
| 175 | + if production(settings) { |
| 176 | + return nil, nil, fmt.Errorf("mongodb durable job store initialization failed: %w", err) |
| 177 | + } |
| 178 | + logger.Warn("mongodb durable job store unavailable, using memory job store", "error", err) |
| 179 | + } else { |
| 180 | + jobStore = durableStore |
| 181 | + logger.Info("using MongoDB durable job store") |
| 182 | + } |
| 183 | + |
| 184 | + return keyStore, jobStore, nil |
| 185 | +} |
| 186 | + |
| 187 | +func production(settings config.Settings) bool { |
| 188 | + return settings.Environment == "production" |
| 189 | +} |
0 commit comments