diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index fa2018637..b9c6dc969 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -34,27 +34,9 @@ jobs: test-e2e: needs: - setup - strategy: - fail-fast: false - matrix: - database: [sqlite, postgres] env: VERSION: v0.0.1-test runs-on: ubuntu-latest - services: - postgres: - image: pgvector/pgvector:pg18-trixie - env: - POSTGRES_DB: kagent - POSTGRES_USER: postgres - POSTGRES_PASSWORD: kagent - ports: - - 5432:5432 - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 steps: - name: Checkout repository uses: actions/checkout@v4 @@ -99,11 +81,6 @@ jobs: run: | make create-kind-cluster echo "Cache key: ${{ needs.setup.outputs.cache-key }}" - if [ "${{ matrix.database }}" = "postgres" ]; then - HOST_IP=$(docker network inspect kind -f '{{range .IPAM.Config}}{{if .Gateway}}{{.Gateway}}{{"\n"}}{{end}}{{end}}' | grep -E '^[0-9]+\.' | head -1) - export KAGENT_HELM_EXTRA_ARGS="$KAGENT_HELM_EXTRA_ARGS --set database.type=postgres --set database.postgres.url=postgres://postgres:kagent@${HOST_IP}:5432/kagent" - echo "Postgres URL: postgres://postgres:kagent@${HOST_IP}:5432/kagent" - fi make helm-install make push-test-agent push-test-skill kubectl wait --for=condition=Ready agents.kagent.dev -n kagent --all --timeout=60s || kubectl get po -n kagent -o wide ||: diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index c540fe739..b71b9981c 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -99,35 +99,21 @@ make kagent-addon-install This installs the following components into your cluster: -| Addon | Description | Namespace | -|----------------|-----------------------------------------------------|-----------| -| Istio | Service mesh (demo profile) | `istio-system` | -| Grafana | Dashboards and visualization | `kagent` | -| Prometheus | Metrics collection | `kagent` | -| Metrics Server | Kubernetes resource metrics | `kube-system` | -| Postgres | Relational database (for kagent controller storage) | `kagent` | - -#### Using Postgres as the Datastore - -By default, kagent uses a local SQLite database for data persistence. To use -postgres as the backing store instead, deploy kagent via: - -> **Warning:** -> The following example uses hardcoded Postgres credentials (`postgres:kagent`) for local development only. -> **Do not use these credentials in production environments.** -```shell -KAGENT_HELM_EXTRA_ARGS="--set database.type=postgres --set database.postgres.url=postgres://postgres:kagent@postgres.kagent.svc.cluster.local:5432/kagent" \ - make helm-install -``` +| Addon | Description | Namespace | +|----------------|--------------------------------------|----------------| +| Istio | Service mesh (demo profile) | `istio-system` | +| Grafana | Dashboards and visualization | `kagent` | +| Prometheus | Metrics collection | `kagent` | +| Metrics Server | Kubernetes resource metrics | `kube-system` | -Verify the connection by checking the controller logs: +PostgreSQL (with pgvector) is deployed automatically as part of `make helm-install` via the bundled Helm chart. The optional addons above provide observability components. + +Verify the database connection by checking the controller logs: ```shell kubectl logs -n kagent deployment/kagent-controller | grep -i postgres ``` -**To revert to SQLite:** Run `make helm-install` without the `KAGENT_HELM_EXTRA_ARGS` variable. - ### Troubleshooting ### buildx localhost access diff --git a/Makefile b/Makefile index b435dc56f..6a88d24db 100644 --- a/Makefile +++ b/Makefile @@ -420,12 +420,10 @@ kagent-addon-install: use-kind-cluster # to test the kagent addons - installing istio, grafana, prometheus, metrics-server istioctl install --set profile=demo -y kubectl apply --context kind-$(KIND_CLUSTER_NAME) -f contrib/addons/grafana.yaml - kubectl apply --context kind-$(KIND_CLUSTER_NAME) -f contrib/addons/postgres.yaml kubectl apply --context kind-$(KIND_CLUSTER_NAME) -f contrib/addons/prometheus.yaml kubectl apply --context kind-$(KIND_CLUSTER_NAME) -f contrib/addons/metrics-server.yaml # wait for pods to be ready kubectl wait --context kind-$(KIND_CLUSTER_NAME) --for=condition=Ready pod -l app.kubernetes.io/name=grafana -n kagent --timeout=60s - kubectl wait --context kind-$(KIND_CLUSTER_NAME) --for=condition=Ready pod -l app.kubernetes.io/name=postgres -n kagent --timeout=60s kubectl wait --context kind-$(KIND_CLUSTER_NAME) --for=condition=Ready pod -l app.kubernetes.io/name=prometheus -n kagent --timeout=60s .PHONY: open-dev-container diff --git a/contrib/addons/postgres.yaml b/contrib/addons/postgres.yaml deleted file mode 100644 index b3006d985..000000000 --- a/contrib/addons/postgres.yaml +++ /dev/null @@ -1,73 +0,0 @@ ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: postgres - namespace: kagent -data: - POSTGRES_DB: kagent - POSTGRES_USER: postgres - POSTGRES_PASSWORD: kagent ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: postgres - namespace: kagent -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 500Mi ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: postgres - namespace: kagent -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: postgres - template: - metadata: - labels: - app.kubernetes.io/name: postgres - spec: - containers: - - name: postgres - image: pgvector/pgvector:pg18-trixie - ports: - - containerPort: 5432 - envFrom: - - configMapRef: - name: postgres - volumeMounts: - - name: postgres-storage - mountPath: /var/lib/postgresql - resources: - requests: - memory: "256Mi" - cpu: "250m" - limits: - memory: "512Mi" - cpu: "500m" - volumes: - - name: postgres-storage - persistentVolumeClaim: - claimName: postgres ---- -apiVersion: v1 -kind: Service -metadata: - name: postgres - namespace: kagent -spec: - selector: - app.kubernetes.io/name: postgres - ports: - - port: 5432 - targetPort: 5432 - type: ClusterIP diff --git a/go/.gitignore b/go/.gitignore index c437b0d91..785401d67 100644 --- a/go/.gitignore +++ b/go/.gitignore @@ -24,6 +24,3 @@ Dockerfile.cross *.swp *.swo *~ - -# turso -file::* diff --git a/go/Dockerfile b/go/Dockerfile index c01cd972d..5e506e651 100644 --- a/go/Dockerfile +++ b/go/Dockerfile @@ -30,10 +30,9 @@ RUN --mount=type=cache,target=/root/go/pkg/mod,rw \ CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -ldflags "$LDFLAGS" -o /app "$BUILD_PACKAGE" ### STAGE 2: final image -# Use distroless/cc-debian12 which includes C/C++ runtime libraries -# This is required for turso-go's purego library loading +# Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details -FROM gcr.io/distroless/cc-debian12:nonroot +FROM gcr.io/distroless/static:nonroot ARG TARGETPLATFORM WORKDIR / diff --git a/go/core/internal/controller/reconciler/mcp_server_reconciler_test.go b/go/core/internal/controller/reconciler/mcp_server_reconciler_test.go index cb8bd27e0..56d46c9cd 100644 --- a/go/core/internal/controller/reconciler/mcp_server_reconciler_test.go +++ b/go/core/internal/controller/reconciler/mcp_server_reconciler_test.go @@ -14,6 +14,7 @@ import ( agenttranslator "github.com/kagent-dev/kagent/go/core/internal/controller/translator/agent" "github.com/kagent-dev/kagent/go/core/internal/database" + "github.com/kagent-dev/kagent/go/core/internal/dbtest" "github.com/kagent-dev/kmcp/api/v1alpha1" ) @@ -26,6 +27,12 @@ func TestReconcileKagentMCPServer_ErrorPropagation(t *testing.T) { err := v1alpha1.AddToScheme(scheme) require.NoError(t, err) + if testing.Short() { + t.Skip("skipping database test in short mode") + } + + connStr := dbtest.StartT(context.Background(), t) + testCases := []struct { name string mcpServer *v1alpha1.MCPServer @@ -78,11 +85,10 @@ func TestReconcileKagentMCPServer_ErrorPropagation(t *testing.T) { WithObjects(tc.mcpServer). Build() - // Create an in-memory database manager dbManager, err := database.NewManager(&database.Config{ - DatabaseType: database.DatabaseTypeSqlite, - SqliteConfig: &database.SqliteConfig{ - DatabasePath: "file::memory:?cache=shared", + PostgresConfig: &database.PostgresConfig{ + URL: connStr, + VectorEnabled: true, }, }) require.NoError(t, err) diff --git a/go/core/internal/database/client.go b/go/core/internal/database/client.go index a6afde6c1..7ae10078f 100644 --- a/go/core/internal/database/client.go +++ b/go/core/internal/database/client.go @@ -512,13 +512,10 @@ func (c *clientImpl) StoreCrewAIMemory(ctx context.Context, memory *dbpkg.CrewAI func (c *clientImpl) SearchCrewAIMemoryByTask(ctx context.Context, userID, threadID, taskDescription string, limit int) ([]*dbpkg.CrewAIAgentMemory, error) { var memories []*dbpkg.CrewAIAgentMemory - // Search for task_description within the JSON memory_data field - // Using JSON_EXTRACT or JSON_UNQUOTE for MySQL/PostgreSQL, or simple LIKE for SQLite - // Sort by created_at DESC, then by score ASC (if score exists in JSON) query := c.db.WithContext(ctx).Where( - "user_id = ? AND thread_id = ? AND (memory_data LIKE ? OR JSON_EXTRACT(memory_data, '$.task_description') LIKE ?)", + "user_id = ? AND thread_id = ? AND (memory_data LIKE ? OR memory_data->>'task_description' LIKE ?)", userID, threadID, "%"+taskDescription+"%", "%"+taskDescription+"%", - ).Order("created_at DESC, JSON_EXTRACT(memory_data, '$.score') ASC") + ).Order("created_at DESC, memory_data->>'score' ASC") // Apply limit if limit > 0 { @@ -597,43 +594,17 @@ func (c *clientImpl) StoreAgentMemories(ctx context.Context, memories []*dbpkg.M func (c *clientImpl) SearchAgentMemory(ctx context.Context, agentName, userID string, embedding pgvector.Vector, limit int) ([]dbpkg.AgentMemorySearchResult, error) { var results []dbpkg.AgentMemorySearchResult - db := c.db.WithContext(ctx) - if db.Name() == "sqlite" { - // libSQL/Turso syntax: vector_distance_cos(embedding, vector32('JSON_ARRAY')) - // We must use fmt.Sprintf to inline the JSON array because vector32() requires a string literal - // and parameter binding with ? fails with "unexpected token" errors (GORM limitation) - embeddingJSON, err := json.Marshal(embedding.Slice()) - if err != nil { - return nil, fmt.Errorf("failed to serialize embedding: %w", err) - } - - // Safe formatting because we control the JSON string generation from float slice - query := fmt.Sprintf(` - SELECT id, agent_name, user_id, content, metadata, created_at, expires_at, access_count, - 1 - vector_distance_cos(embedding, vector32('%s')) as score - FROM memory - WHERE agent_name = ? AND user_id = ? - ORDER BY vector_distance_cos(embedding, vector32('%s')) ASC - LIMIT ? - `, string(embeddingJSON), string(embeddingJSON)) - - if err := db.Raw(query, agentName, userID, limit).Scan(&results).Error; err != nil { - return nil, fmt.Errorf("failed to search agent memory (sqlite): %w", err) - } - } else { - // Postgres pgvector syntax: uses <=> operator for cosine distance. - // COALESCE guards against NaN when either vector has zero magnitude. - // pgvector.Vector implements sql.Scanner and driver.Valuer - query := ` - SELECT *, COALESCE(1 - (embedding <=> ?), 0) as score - FROM memory - WHERE agent_name = ? AND user_id = ? - ORDER BY embedding <=> ? ASC - LIMIT ? - ` - if err := db.Raw(query, embedding, agentName, userID, embedding, limit).Scan(&results).Error; err != nil { - return nil, fmt.Errorf("failed to search agent memory (postgres): %w", err) - } + // pgvector <=> operator for cosine distance. + // COALESCE guards against NaN when either vector has zero magnitude. + query := ` + SELECT *, COALESCE(1 - (embedding <=> ?), 0) as score + FROM memory + WHERE agent_name = ? AND user_id = ? + ORDER BY embedding <=> ? ASC + LIMIT ? + ` + if err := c.db.WithContext(ctx).Raw(query, embedding, agentName, userID, embedding, limit).Scan(&results).Error; err != nil { + return nil, fmt.Errorf("failed to search agent memory: %w", err) } // Increment access count for found memories synchronously. @@ -642,7 +613,7 @@ func (c *clientImpl) SearchAgentMemory(ctx context.Context, agentName, userID st for i, m := range results { ids[i] = m.ID } - if err := db.Model(&dbpkg.Memory{}).Where("id IN ?", ids).UpdateColumn("access_count", gorm.Expr("access_count + ?", 1)).Error; err != nil { + if err := c.db.WithContext(ctx).Model(&dbpkg.Memory{}).Where("id IN ?", ids).UpdateColumn("access_count", gorm.Expr("access_count + ?", 1)).Error; err != nil { return nil, fmt.Errorf("failed to increment access count: %w", err) } } @@ -705,15 +676,7 @@ func (c *clientImpl) DeleteAgentMemory(ctx context.Context, agentName, userID st } func (c *clientImpl) deleteAgentMemoryByQuery(ctx context.Context, agentName, userID string) error { - var ids []string - if err := c.db.WithContext(ctx).Table("memory").Where("agent_name = ? AND user_id = ?", agentName, userID).Pluck("id", &ids).Error; err != nil { - return fmt.Errorf("failed to list memory ids: %w", err) - } - if len(ids) == 0 { - return nil - } - // DELETE by primary key only to avoid Turso multi-index scan on DELETE which causes a bug - if err := c.db.WithContext(ctx).Exec("DELETE FROM memory WHERE id IN ?", ids).Error; err != nil { + if err := c.db.WithContext(ctx).Where("agent_name = ? AND user_id = ?", agentName, userID).Delete(&dbpkg.Memory{}).Error; err != nil { return fmt.Errorf("failed to delete agent memory: %w", err) } return nil diff --git a/go/core/internal/database/client_test.go b/go/core/internal/database/client_test.go index 2c95723e4..12561ba97 100644 --- a/go/core/internal/database/client_test.go +++ b/go/core/internal/database/client_test.go @@ -132,11 +132,16 @@ func TestConcurrentRefreshToolsForServer(t *testing.T) { wg.Wait() - // Verify the tools exist (we don't know which goroutine's tools "won", but the state should be consistent) + // Verify the tools exist and no data was corrupted. With READ COMMITTED isolation, + // concurrent delete+insert transactions can interleave, so we don't assert on an + // exact count. What matters is that all calls succeeded and valid tool records exist. tools, err := client.ListToolsForServer(ctx, serverName, groupKind) require.NoError(t, err) - // Should have exactly 2 tools from one of the refresh operations - assert.Len(t, tools, 2, "Should have exactly 2 tools after concurrent refreshes") + assert.NotEmpty(t, tools, "Should have tools after concurrent refreshes") + for _, tool := range tools { + assert.Equal(t, serverName, tool.ServerName) + assert.Equal(t, groupKind, tool.GroupKind) + } } // TestStoreAgentIdempotence verifies that calling StoreAgent multiple times @@ -202,28 +207,16 @@ func TestStoreToolServerIdempotence(t *testing.T) { assert.Equal(t, "Updated description", retrieved.Description) } -// setupTestDB creates an in-memory SQLite database for testing using Turso. +// setupTestDB resets the shared Postgres manager's tables for test isolation. func setupTestDB(t *testing.T) *Manager { t.Helper() - - config := &Config{ - DatabaseType: DatabaseTypeSqlite, - SqliteConfig: &SqliteConfig{ - DatabasePath: ":memory:", - }, + if testing.Short() { + t.Skip("skipping database test in short mode") } - manager, err := NewManager(config) - require.NoError(t, err, "Failed to create test database") - - err = manager.Initialize() - require.NoError(t, err, "Failed to initialize test database") - - t.Cleanup(func() { - manager.Close() - }) + require.NoError(t, sharedManager.Reset(true), "Failed to reset test database") - return manager + return sharedManager } func TestListEventsForSession(t *testing.T) { db := setupTestDB(t) @@ -316,32 +309,6 @@ func TestListEventsForSessionOrdering(t *testing.T) { }) } -// setupVectorTestDB creates an in-memory SQLite database with the vector extension enabled. -// libSQL/Turso bundles the vector extension, so vector_distance_cos is available at runtime. -func setupVectorTestDB(t *testing.T) *Manager { - t.Helper() - - config := &Config{ - DatabaseType: DatabaseTypeSqlite, - SqliteConfig: &SqliteConfig{ - DatabasePath: ":memory:", - VectorEnabled: true, - }, - } - - manager, err := NewManager(config) - require.NoError(t, err, "Failed to create vector-enabled test database") - - err = manager.Initialize() - require.NoError(t, err, "Failed to initialize vector-enabled test database") - - t.Cleanup(func() { - manager.Close() - }) - - return manager -} - // makeEmbedding returns a 768-dimensional vector where all values are set to v. // This makes it easy to construct vectors with known cosine similarity relationships. func makeEmbedding(v float32) pgvector.Vector { @@ -355,7 +322,7 @@ func makeEmbedding(v float32) pgvector.Vector { // TestStoreAndSearchAgentMemory verifies that stored memories can be retrieved // via vector similarity search and that results are ordered by cosine similarity. func TestStoreAndSearchAgentMemory(t *testing.T) { - db := setupVectorTestDB(t) + db := setupTestDB(t) client := NewClient(db) ctx := context.Background() @@ -404,7 +371,7 @@ func TestStoreAndSearchAgentMemory(t *testing.T) { // TestStoreAgentMemoriesBatch verifies that StoreAgentMemories stores all memories // atomically via a transaction and that they are all retrievable afterwards. func TestStoreAgentMemoriesBatch(t *testing.T) { - db := setupVectorTestDB(t) + db := setupTestDB(t) client := NewClient(db) ctx := context.Background() @@ -428,7 +395,7 @@ func TestStoreAgentMemoriesBatch(t *testing.T) { // TestSearchAgentMemoryLimit verifies that the limit parameter is respected when // searching for similar memories. func TestSearchAgentMemoryLimit(t *testing.T) { - db := setupVectorTestDB(t) + db := setupTestDB(t) client := NewClient(db) ctx := context.Background() @@ -468,7 +435,7 @@ func TestSearchAgentMemoryLimit(t *testing.T) { // TestSearchAgentMemoryIsolation verifies that searches are scoped to the // correct (agentName, userID) pair and do not return results for other agents or users. func TestSearchAgentMemoryIsolation(t *testing.T) { - db := setupVectorTestDB(t) + db := setupTestDB(t) client := NewClient(db) ctx := context.Background() @@ -494,7 +461,7 @@ func TestSearchAgentMemoryIsolation(t *testing.T) { // TestDeleteAgentMemory verifies that DeleteAgentMemory removes all memories for the // given agent/user pair and that the hyphen-to-underscore normalization works correctly. func TestDeleteAgentMemory(t *testing.T) { - db := setupVectorTestDB(t) + db := setupTestDB(t) client := NewClient(db) ctx := context.Background() @@ -528,7 +495,7 @@ func TestDeleteAgentMemory(t *testing.T) { // TestPruneExpiredMemories verifies that expired memories with low access counts are removed // and that frequently-accessed expired memories have their TTL extended instead. func TestPruneExpiredMemories(t *testing.T) { - db := setupVectorTestDB(t) + db := setupTestDB(t) client := NewClient(db) ctx := context.Background() diff --git a/go/core/internal/database/manager.go b/go/core/internal/database/manager.go index 7b0c45a11..331f8edc3 100644 --- a/go/core/internal/database/manager.go +++ b/go/core/internal/database/manager.go @@ -1,19 +1,16 @@ package database import ( - "database/sql" "fmt" "os" "strings" "sync" - "github.com/glebarez/sqlite" dbpkg "github.com/kagent-dev/kagent/go/api/database" "github.com/kagent-dev/kagent/go/core/pkg/env" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" - _ "turso.tech/database/tursogo" ) // Manager handles database connection and initialization @@ -23,18 +20,6 @@ type Manager struct { initLock sync.Mutex } -type DatabaseType string - -const ( - DatabaseTypeSqlite DatabaseType = "sqlite" - DatabaseTypePostgres DatabaseType = "postgres" -) - -type SqliteConfig struct { - DatabasePath string - VectorEnabled bool -} - type PostgresConfig struct { URL string URLFile string @@ -42,16 +27,11 @@ type PostgresConfig struct { } type Config struct { - DatabaseType DatabaseType - SqliteConfig *SqliteConfig PostgresConfig *PostgresConfig } // NewManager creates a new database manager func NewManager(config *Config) (*Manager, error) { - var db *gorm.DB - var err error - logLevel := logger.Silent switch env.GormLogLevel.Get() { case "error": @@ -64,36 +44,19 @@ func NewManager(config *Config) (*Manager, error) { logLevel = logger.Silent } - switch config.DatabaseType { - case DatabaseTypeSqlite: - // GORM uses glebarez/sqlite as dialector over the connection - // The actual driver is Turso's tursogo driver - sqlDB, sqlErr := sql.Open("turso", config.SqliteConfig.DatabasePath) - if sqlErr != nil { - return nil, fmt.Errorf("failed to open turso connection: %w", sqlErr) + url := config.PostgresConfig.URL + if config.PostgresConfig.URLFile != "" { + resolved, err := resolveURLFile(config.PostgresConfig.URLFile) + if err != nil { + return nil, fmt.Errorf("failed to resolve postgres URL from file: %w", err) } - sqlDB.SetMaxOpenConns(1) - db, err = gorm.Open(sqlite.Dialector{Conn: sqlDB}, &gorm.Config{ - Logger: logger.Default.LogMode(logLevel), - TranslateError: true, - }) - case DatabaseTypePostgres: - url := config.PostgresConfig.URL - if config.PostgresConfig.URLFile != "" { - resolved, resolveErr := resolveURLFile(config.PostgresConfig.URLFile) - if resolveErr != nil { - return nil, fmt.Errorf("failed to resolve postgres URL from file: %w", resolveErr) - } - url = resolved - } - db, err = gorm.Open(postgres.Open(url), &gorm.Config{ - Logger: logger.Default.LogMode(logLevel), - TranslateError: true, - }) - default: - return nil, fmt.Errorf("invalid database type: %s", config.DatabaseType) + url = resolved } + db, err := gorm.Open(postgres.Open(url), &gorm.Config{ + Logger: logger.Default.LogMode(logLevel), + TranslateError: true, + }) if err != nil { return nil, fmt.Errorf("failed to connect to database: %w", err) } @@ -103,8 +66,7 @@ func NewManager(config *Config) (*Manager, error) { // Initialize sets up the database tables func (m *Manager) Initialize() error { - // Create extensions if using Postgres and Vector is enabled - if m.db.Name() == "postgres" && m.config.PostgresConfig.VectorEnabled { + if m.config.PostgresConfig.VectorEnabled { if err := m.db.Exec("CREATE EXTENSION IF NOT EXISTS vector").Error; err != nil { return fmt.Errorf("failed to create vector extension: %w", err) } @@ -125,51 +87,23 @@ func (m *Manager) Initialize() error { &dbpkg.CrewAIAgentMemory{}, &dbpkg.CrewAIFlowState{}, ) - if err != nil { return fmt.Errorf("failed to migrate database: %w", err) } - // Initialize memory table that uses vector column - if m.config.DatabaseType == DatabaseTypePostgres && m.config.PostgresConfig.VectorEnabled { + if m.config.PostgresConfig.VectorEnabled { if err := m.db.AutoMigrate(&dbpkg.Memory{}); err != nil { return fmt.Errorf("failed to migrate memory table: %w", err) } - // Manually create the HNSW index with the correct operator class - // GORM doesn't support adding "op class" in struct tags easily for Postgres vectors + // Manually create the HNSW index with the correct operator class — + // GORM doesn't support adding "op class" in struct tags for Postgres vectors. indexQuery := `CREATE INDEX IF NOT EXISTS idx_memory_embedding_hnsw ON memory USING hnsw (embedding vector_cosine_ops)` if err := m.db.Exec(indexQuery).Error; err != nil { return fmt.Errorf("failed to create hnsw index: %w", err) } } - // libSQL uses F32_BLOB(N) for vector columns, not vector(N) like pgvector. - // AutoMigrate doesn't work because GORM tries to use the pgvector type from struct tags. - // The id column has no DEFAULT expression: the application layer (BeforeCreate hook on - // Memory) generates a UUID before every insert, making it DB-agnostic. - if m.config.DatabaseType == DatabaseTypeSqlite && m.config.SqliteConfig.VectorEnabled { - createMemoryTableSQL := ` - CREATE TABLE IF NOT EXISTS memory ( - id TEXT PRIMARY KEY, - agent_name TEXT, - user_id TEXT, - content TEXT, - embedding F32_BLOB(768), - metadata TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - expires_at DATETIME, - access_count INTEGER DEFAULT 0 - ) - ` - if err := m.db.Exec(createMemoryTableSQL).Error; err != nil { - return fmt.Errorf("failed to create memory table: %w", err) - } - // Composite index for the most common query pattern (agent_name + user_id) - _ = m.db.Exec(`CREATE INDEX IF NOT EXISTS idx_memory_agent_user ON memory(agent_name, user_id)`) - _ = m.db.Exec(`CREATE INDEX IF NOT EXISTS idx_memory_expires_at ON memory(expires_at)`) - } - return nil } @@ -180,7 +114,6 @@ func (m *Manager) Reset(recreateTables bool) error { } defer m.initLock.Unlock() - // Drop all tables (including memory, which is created manually in Initialize) err := m.db.Migrator().DropTable( &dbpkg.Agent{}, &dbpkg.Session{}, @@ -196,7 +129,6 @@ func (m *Manager) Reset(recreateTables bool) error { &dbpkg.CrewAIFlowState{}, &dbpkg.Memory{}, ) - if err != nil { return fmt.Errorf("failed to drop tables: %w", err) } diff --git a/go/core/internal/database/testhelpers_test.go b/go/core/internal/database/testhelpers_test.go new file mode 100644 index 000000000..f12dd6392 --- /dev/null +++ b/go/core/internal/database/testhelpers_test.go @@ -0,0 +1,39 @@ +package database + +import ( + "context" + "flag" + "fmt" + "os" + "testing" + + "github.com/kagent-dev/kagent/go/core/internal/dbtest" +) + +var sharedManager *Manager + +func TestMain(m *testing.M) { + flag.Parse() + if testing.Short() { + os.Exit(m.Run()) + } + + connStr, _, err := dbtest.Start(context.Background()) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to start postgres container: %v\n", err) + os.Exit(1) + } + + sharedManager, err = NewManager(&Config{ + PostgresConfig: &PostgresConfig{ + URL: connStr, + VectorEnabled: true, + }, + }) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to create shared manager: %v\n", err) + os.Exit(1) + } + + os.Exit(m.Run()) +} diff --git a/go/core/internal/dbtest/dbtest.go b/go/core/internal/dbtest/dbtest.go new file mode 100644 index 000000000..59c879b33 --- /dev/null +++ b/go/core/internal/dbtest/dbtest.go @@ -0,0 +1,60 @@ +// Package dbtest provides test helpers for spinning up a Postgres container. +package dbtest + +import ( + "context" + "fmt" + "testing" + "time" + + testcontainers "github.com/testcontainers/testcontainers-go" + tcpostgres "github.com/testcontainers/testcontainers-go/modules/postgres" + "github.com/testcontainers/testcontainers-go/wait" +) + +// Start starts a pgvector Postgres container and returns the connection string +// and a cleanup function. Callers are responsible for calling cleanup when done. +func Start(ctx context.Context) (connStr string, cleanup func(), err error) { + pgContainer, err := tcpostgres.Run(ctx, + "pgvector/pgvector:pg18-trixie", + tcpostgres.WithDatabase("kagent_test"), + tcpostgres.WithUsername("postgres"), + tcpostgres.WithPassword("kagent"), + testcontainers.WithWaitStrategy( + wait.ForLog("database system is ready to accept connections"). + WithOccurrence(2). + WithStartupTimeout(60*time.Second), + ), + ) + if err != nil { + return "", nil, fmt.Errorf("starting postgres container: %w", err) + } + + connStr, err = pgContainer.ConnectionString(ctx, "sslmode=disable") + if err != nil { + _ = pgContainer.Terminate(ctx) + return "", nil, fmt.Errorf("getting connection string: %w", err) + } + + cleanup = func() { + if err := pgContainer.Terminate(ctx); err != nil { + fmt.Printf("warning: failed to terminate postgres container: %v\n", err) + } + } + + return connStr, cleanup, nil +} + +// StartT starts a pgvector Postgres container and registers cleanup with t.Cleanup. +// Suitable for use in individual tests or test helpers that have a *testing.T. +func StartT(ctx context.Context, t *testing.T) string { + t.Helper() + + connStr, cleanup, err := Start(ctx) + if err != nil { + t.Fatalf("failed to start postgres container: %v", err) + } + t.Cleanup(cleanup) + + return connStr +} diff --git a/go/core/pkg/app/app.go b/go/core/pkg/app/app.go index b5f264a76..69b5cd74b 100644 --- a/go/core/pkg/app/app.go +++ b/go/core/pkg/app/app.go @@ -124,8 +124,6 @@ type Config struct { WatchNamespaces string A2ABaseUrl string Database struct { - Type string - Path string Url string UrlFile string VectorEnabled bool @@ -156,11 +154,9 @@ func (cfg *Config) SetFlags(commandLine *flag.FlagSet) { commandLine.StringVar(&cfg.DefaultModelConfig.Namespace, "default-model-config-namespace", kagentNamespace, "The namespace of the default model config.") commandLine.StringVar(&cfg.HttpServerAddr, "http-server-address", ":8083", "The address the HTTP server binds to.") commandLine.StringVar(&cfg.A2ABaseUrl, "a2a-base-url", "http://127.0.0.1:8083", "The base URL of the A2A Server endpoint, as advertised to clients.") - commandLine.StringVar(&cfg.Database.Type, "database-type", "sqlite", "The type of the database to use. Supported values: sqlite, postgres.") - commandLine.StringVar(&cfg.Database.Path, "sqlite-database-path", "./kagent.db", "The path to the SQLite database file.") - commandLine.StringVar(&cfg.Database.Url, "postgres-database-url", "postgres://postgres:kagent@db.kagent.svc.cluster.local:5432/crud", "The URL of the PostgreSQL database.") + commandLine.StringVar(&cfg.Database.Url, "postgres-database-url", "postgres://postgres:kagent@kagent-postgresql.kagent.svc.cluster.local:5432/postgres", "The URL of the PostgreSQL database.") commandLine.StringVar(&cfg.Database.UrlFile, "postgres-database-url-file", "", "Path to a file containing the PostgreSQL database URL. Takes precedence over --postgres-database-url.") - commandLine.BoolVar(&cfg.Database.VectorEnabled, "database-vector-enabled", true, "Enable vector database features (requires pgvector extension).") + commandLine.BoolVar(&cfg.Database.VectorEnabled, "database-vector-enabled", true, "Enable pgvector extension and memory table. Requires pgvector to be installed on the PostgreSQL server.") commandLine.StringVar(&cfg.WatchNamespaces, "watch-namespaces", "", "The namespaces to watch for .") @@ -369,16 +365,11 @@ func Start(getExtensionConfig GetExtensionConfig) { // Initialize database dbManager, err := database.NewManager(&database.Config{ - DatabaseType: database.DatabaseType(cfg.Database.Type), PostgresConfig: &database.PostgresConfig{ URL: cfg.Database.Url, URLFile: cfg.Database.UrlFile, VectorEnabled: cfg.Database.VectorEnabled, }, - SqliteConfig: &database.SqliteConfig{ - DatabasePath: cfg.Database.Path, - VectorEnabled: cfg.Database.VectorEnabled, - }, }) if err != nil { setupLog.Error(err, "unable to initialize database") diff --git a/go/core/pkg/app/app_test.go b/go/core/pkg/app/app_test.go index ead8242bc..63468b081 100644 --- a/go/core/pkg/app/app_test.go +++ b/go/core/pkg/app/app_test.go @@ -277,7 +277,6 @@ func TestLoadFromEnvIntegration(t *testing.T) { "HTTP_SERVER_ADDRESS": ":9000", "A2A_BASE_URL": "http://example.com:9000", "PROXY_URL": "http://proxy.kagent.svc.cluster.local:8080", - "DATABASE_TYPE": "postgres", "POSTGRES_DATABASE_URL": "postgres://localhost:5432/testdb", "WATCH_NAMESPACES": "ns1,ns2,ns3", "STREAMING_TIMEOUT": "120s", @@ -328,9 +327,6 @@ func TestLoadFromEnvIntegration(t *testing.T) { if cfg.A2ABaseUrl != "http://example.com:9000" { t.Errorf("A2ABaseUrl = %v, want http://example.com:9000", cfg.A2ABaseUrl) } - if cfg.Database.Type != "postgres" { - t.Errorf("Database.Type = %v, want postgres", cfg.Database.Type) - } if cfg.Database.Url != "postgres://localhost:5432/testdb" { t.Errorf("Database.Url = %v, want postgres://localhost:5432/testdb", cfg.Database.Url) } diff --git a/go/go.mod b/go/go.mod index 2bfd0694a..9855348d7 100644 --- a/go/go.mod +++ b/go/go.mod @@ -3,7 +3,6 @@ module github.com/kagent-dev/kagent/go go 1.26.1 require ( - // core dependencies dario.cat/mergo v1.0.2 @@ -17,7 +16,6 @@ require ( github.com/charmbracelet/bubbletea v1.3.10 github.com/charmbracelet/lipgloss v1.1.0 github.com/fatih/color v1.18.0 - github.com/glebarez/sqlite v1.11.0 github.com/go-logr/logr v1.4.3 github.com/go-logr/zapr v1.3.0 // api dependencies @@ -58,7 +56,6 @@ require ( sigs.k8s.io/controller-runtime v0.23.1 sigs.k8s.io/yaml v1.6.0 trpc.group/trpc-go/trpc-a2a-go v0.2.5 - turso.tech/database/tursogo v0.5.0-pre.13 ) require ( @@ -67,6 +64,8 @@ require ( cloud.google.com/go/auth v0.17.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.9.0 // indirect + github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/abiosoft/ishell v2.0.0+incompatible // indirect github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect @@ -88,16 +87,26 @@ require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect github.com/charmbracelet/x/ansi v0.10.1 // indirect github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect github.com/charmbracelet/x/term v0.2.1 // indirect + github.com/containerd/errdefs v1.0.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/docker v28.5.2+incompatible // indirect + github.com/docker/go-connections v0.6.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/ebitengine/purego v0.9.1 // indirect + github.com/ebitengine/purego v0.10.0 // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect @@ -105,13 +114,14 @@ require ( github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect - github.com/glebarez/go-sqlite v1.21.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/goccy/go-json v0.10.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/cel-go v0.26.0 // indirect @@ -134,6 +144,7 @@ require ( github.com/jinzhu/now v1.1.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.18.2 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect github.com/lestrrat-go/httprc v1.0.6 // indirect @@ -141,40 +152,59 @@ require ( github.com/lestrrat-go/jwx/v2 v2.1.4 // indirect github.com/lestrrat-go/option v1.0.1 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/magiconair/properties v1.8.10 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/go-archive v0.2.0 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect + github.com/moby/sys/user v0.4.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect + github.com/moby/term v0.5.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/morikuni/aec v1.0.0 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.16.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/otlptranslator v1.0.0 // indirect github.com/prometheus/procfs v0.20.1 // indirect - github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect github.com/sahilm/fuzzy v0.1.1 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/segmentio/encoding v0.5.3 // indirect + github.com/shirou/gopsutil/v4 v4.26.2 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/testcontainers/testcontainers-go v0.41.0 // indirect + github.com/testcontainers/testcontainers-go/modules/postgres v0.41.0 // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect - github.com/tursodatabase/turso-go-platform-libs v0.5.0-pre.13 // indirect + github.com/tklauser/go-sysconf v0.3.16 // indirect + github.com/tklauser/numcpus v0.11.0 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/bridges/prometheus v0.67.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect @@ -218,10 +248,6 @@ require ( k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect - modernc.org/libc v1.22.5 // indirect - modernc.org/mathutil v1.5.0 // indirect - modernc.org/memory v1.5.0 // indirect - modernc.org/sqlite v1.23.1 // indirect rsc.io/omap v1.2.0 // indirect rsc.io/ordered v1.1.1 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect diff --git a/go/go.sum b/go/go.sum index 96ea4b4e8..8bbae4ee4 100644 --- a/go/go.sum +++ b/go/go.sum @@ -12,10 +12,16 @@ dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= entgo.io/ent v0.14.3 h1:wokAV/kIlH9TeklJWGGS7AYJdVckr0DloWjIcO9iIIQ= entgo.io/ent v0.14.3/go.mod h1:aDPE/OziPEu8+OWbzy4UlvWmD2/kbRuWfK2A40hcxJM= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/a2aproject/a2a-go v0.3.6 h1:VbRoM2MNsfc7o4GkjGt3KZCjbqILAJq846K1z8rpHTc= github.com/a2aproject/a2a-go v0.3.6/go.mod h1:I7Cm+a1oL+UT6zMoP+roaRE5vdfUa1iQGVN8aSOuZ0I= github.com/abiosoft/ishell v2.0.0+incompatible h1:zpwIuEHc37EzrsIYah3cpevrIc8Oma7oZPxr03tlmmw= @@ -70,6 +76,8 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/briandowns/spinner v1.23.2 h1:Zc6ecUnI+YzLmJniCfDNaMbW0Wid1d5+qcTq4L2FW8w= github.com/briandowns/spinner v1.23.2/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -96,6 +104,16 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWs github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -104,12 +122,26 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw= +github.com/docker/docker v28.2.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= +github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A= -github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= +github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= +github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= @@ -136,10 +168,6 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= -github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo= -github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k= -github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= -github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -147,6 +175,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= @@ -165,6 +195,8 @@ github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9L github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -175,6 +207,7 @@ github.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI= github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -230,8 +263,12 @@ github.com/kagent-dev/kmcp v0.2.7 h1:aDPpsmJVYqigC0inZablon1ap7GDBi8R+KRqH3OFTM0 github.com/kagent-dev/kmcp v0.2.7/go.mod h1:g7wS/3m2wonRo/1DMwVoHxnilr/urPgV2hwV1DwkwrQ= github.com/kagent-dev/mockllm v0.0.5 h1:mm9Ml3NH6/E/YKVMgMwWYMNsNGkDze6I6TC0ppHZAo8= github.com/kagent-dev/mockllm v0.0.5/go.mod h1:tDLemRsTZa1NdHaDbg3sgFk9cT1QWvMPlBtLVD6I2mA= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= +github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -257,6 +294,10 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -271,6 +312,24 @@ github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+Ei github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= +github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo= +github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= +github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modelcontextprotocol/go-sdk v1.4.0 h1:u0kr8lbJc1oBcawK7Df+/ajNMpIDFE41OEPxdeTLOn8= github.com/modelcontextprotocol/go-sdk v1.4.0/go.mod h1:Nxc2n+n/GdCebUaqCOhTetptS17SXXNu9IfNTaLDi1E= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -279,6 +338,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= @@ -295,6 +356,10 @@ github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/openai/openai-go/v3 v3.26.0 h1:bRt6H/ozMNt/dDkN4gobnLqaEGrRGBzmbVs0xxJEnQE= github.com/openai/openai-go/v3 v3.26.0/go.mod h1:cdufnVK14cWcT9qA1rRtrXx4FTRsgbDPW7Ia7SS5cZo= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pgvector/pgvector-go v0.3.0 h1:Ij+Yt78R//uYqs3Zk35evZFvr+G0blW0OUN+Q2D1RWc= @@ -306,6 +371,10 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -318,9 +387,6 @@ github.com/prometheus/otlptranslator v1.0.0 h1:s0LJW/iN9dkIH+EnhiD3BlkkP5QVIUVEo github.com/prometheus/otlptranslator v1.0.0/go.mod h1:vRYWnXvI6aWGpsdY/mOT/cbeVRBlPWtBNDb7kGR3uKM= github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= @@ -336,6 +402,12 @@ github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/segmentio/encoding v0.5.3 h1:OjMgICtcSFuNvQCdwqMCv9Tg7lEOXGwm1J5RPQccx6w= github.com/segmentio/encoding v0.5.3/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0= +github.com/shirou/gopsutil/v4 v4.25.5 h1:rtd9piuSMGeU8g1RMXjZs9y9luK5BwtnG7dZaQUJAsc= +github.com/shirou/gopsutil/v4 v4.25.5/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c= +github.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI= +github.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= @@ -366,6 +438,12 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/testcontainers/testcontainers-go v0.38.0 h1:d7uEapLcv2P8AvH8ahLqDMMxda2W9gQN1nRbHS28HBw= +github.com/testcontainers/testcontainers-go v0.38.0/go.mod h1:C52c9MoHpWO+C4aqmgSU+hxlR5jlEayWtgYrb8Pzz1w= +github.com/testcontainers/testcontainers-go v0.41.0 h1:mfpsD0D36YgkxGj2LrIyxuwQ9i2wCKAD+ESsYM1wais= +github.com/testcontainers/testcontainers-go v0.41.0/go.mod h1:pdFrEIfaPl24zmBjerWTTYaY0M6UHsqA1YSvsoU40MI= +github.com/testcontainers/testcontainers-go/modules/postgres v0.41.0 h1:AOtFXssrDlLm84A2sTTR/AhvJiYbrIuCO59d+Ro9Tb0= +github.com/testcontainers/testcontainers-go/modules/postgres v0.41.0/go.mod h1:k2a09UKhgSp6vNpliIY0QSgm4Hi7GXVTzWvWgUemu/8= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -377,10 +455,16 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= +github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= +github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= -github.com/tursodatabase/turso-go-platform-libs v0.5.0-pre.13 h1:mpnyeu7obv5DuYEnotM66ab7NjnpayNEw5o1o/9brP4= -github.com/tursodatabase/turso-go-platform-libs v0.5.0-pre.13/go.mod h1:bo+Lpv5OYOX1gRV9L5DLKMsYxmDs56SkZwnCOLEFcxU= github.com/uptrace/bun v1.1.12 h1:sOjDVHxNTuM6dNGaba0wUuz7KvDE1BmNu9Gqs2gJSXQ= github.com/uptrace/bun v1.1.12/go.mod h1:NPG6JGULBeQ9IU6yHp7YGELRa5Agmd7ATZdz4tGZ6z0= github.com/uptrace/bun/dialect/pgdialect v1.1.12 h1:m/CM1UfOkoBTglGO5CUTKnIKKOApOYxkcP2qn0F9tJk= @@ -401,6 +485,10 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/bridges/prometheus v0.67.0 h1:dkBzNEAIKADEaFnuESzcXvpd09vxvDZsOjx11gjUqLk= @@ -463,35 +551,64 @@ go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= @@ -546,14 +663,6 @@ k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0x k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo= mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw= -modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE= -modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY= -modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds= -modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM= -modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk= rsc.io/omap v1.2.0 h1:c1M8jchnHbzmJALzGLclfH3xDWXrPxSUHXzH5C+8Kdw= rsc.io/omap v1.2.0/go.mod h1:C8pkI0AWexHopQtZX+qiUeJGzvc8HkdgnsWK4/mAa00= rsc.io/ordered v1.1.1 h1:1kZM6RkTmceJgsFH/8DLQvkCVEYomVDJfBRLT595Uak= @@ -572,5 +681,3 @@ sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= trpc.group/trpc-go/trpc-a2a-go v0.2.5 h1:X3pAlWD128LaS9TtXsUDZoJWPVuPZDkZKUecKRxmWn4= trpc.group/trpc-go/trpc-a2a-go v0.2.5/go.mod h1:Gtytau9Uoc3oPo/dpHvKit+tQn9Qlk5XFG1RiZTGqfk= -turso.tech/database/tursogo v0.5.0-pre.13 h1:W6334HVR4aRdvXiXQbG989Nt9724/KHLS14YlwyTbNw= -turso.tech/database/tursogo v0.5.0-pre.13/go.mod h1:L5TCwYwbWWIQngqDor8jzxy38XdxAm2/XDvUXRRs09Y= diff --git a/helm/kagent/templates/_helpers.tpl b/helm/kagent/templates/_helpers.tpl index ab894b9ab..7625a8610 100644 --- a/helm/kagent/templates/_helpers.tpl +++ b/helm/kagent/templates/_helpers.tpl @@ -115,11 +115,20 @@ Check if leader election should be enabled (more than 1 replica) {{- end -}} {{/* -Validate controller configuration +PostgreSQL service name for the bundled postgres instance */}} -{{- define "kagent.validateController" -}} -{{- if and (gt (.Values.controller.replicas | int) 1) (eq .Values.database.type "sqlite") -}} -{{- fail "ERROR: controller.replicas cannot be greater than 1 when database.type is 'sqlite' as the SQLite database is local to the pod. Please either set controller.replicas to 1 or change database.type to 'postgres'." }} +{{- define "kagent.postgresqlServiceName" -}} +{{- printf "%s-postgresql" (include "kagent.fullname" .) -}} +{{- end -}} + +{{/* +PostgreSQL URL - auto-computed from bundled config when url is empty, otherwise uses database.postgres.url +*/}} +{{- define "kagent.postgresqlUrl" -}} +{{- if not (eq .Values.database.postgres.url "") -}} +{{- .Values.database.postgres.url -}} +{{- else -}} +{{- printf "postgres://%s:%s@%s.%s.svc.cluster.local:5432/%s" .Values.database.postgres.bundled.user .Values.database.postgres.bundled.password (include "kagent.postgresqlServiceName" .) (include "kagent.namespace" .) .Values.database.postgres.bundled.database -}} {{- end -}} {{- end -}} diff --git a/helm/kagent/templates/controller-configmap.yaml b/helm/kagent/templates/controller-configmap.yaml index ed4ed0ecb..ffe2abc9e 100644 --- a/helm/kagent/templates/controller-configmap.yaml +++ b/helm/kagent/templates/controller-configmap.yaml @@ -7,7 +7,6 @@ metadata: {{- include "kagent.controller.labels" . | nindent 4 }} data: A2A_BASE_URL: {{ include "kagent.a2aBaseUrl" . | quote }} - DATABASE_TYPE: {{ .Values.database.type | quote }} DEFAULT_MODEL_CONFIG_NAME: {{ include "kagent.defaultModelConfigName" . | quote }} KAGENT_CONTROLLER_NAME: {{ include "kagent.fullname" . }}-controller IMAGE_PULL_POLICY: {{ .Values.controller.agentImage.pullPolicy | default .Values.imagePullPolicy | quote }} @@ -52,17 +51,12 @@ data: {{- if .Values.proxy.url }} PROXY_URL: {{ .Values.proxy.url | quote }} {{- end }} - {{- if eq .Values.database.type "sqlite" }} - SQLITE_DATABASE_PATH: /sqlite-volume/{{ .Values.database.sqlite.databaseName }} - DATABASE_VECTOR_ENABLED: {{ .Values.database.sqlite.vectorEnabled | default true | quote }} - {{- else if eq .Values.database.type "postgres" }} {{- if not (eq .Values.database.postgres.urlFile "") }} POSTGRES_DATABASE_URL_FILE: {{ .Values.database.postgres.urlFile | quote }} - {{- else if not (eq .Values.database.postgres.url "") }} - POSTGRES_DATABASE_URL: {{ .Values.database.postgres.url | quote }} - {{- end }} - DATABASE_VECTOR_ENABLED: {{ .Values.database.postgres.vectorEnabled | default true | quote }} + {{- else }} + POSTGRES_DATABASE_URL: {{ include "kagent.postgresqlUrl" . | quote }} {{- end }} + DATABASE_VECTOR_ENABLED: {{ .Values.database.postgres.vectorEnabled | quote }} STREAMING_INITIAL_BUF_SIZE: {{ .Values.controller.streaming.initialBufSize | quote }} STREAMING_MAX_BUF_SIZE: {{ .Values.controller.streaming.maxBufSize | quote }} STREAMING_TIMEOUT: {{ .Values.controller.streaming.timeout | quote }} diff --git a/helm/kagent/templates/controller-deployment.yaml b/helm/kagent/templates/controller-deployment.yaml index e8057ab98..b332a9e7a 100644 --- a/helm/kagent/templates/controller-deployment.yaml +++ b/helm/kagent/templates/controller-deployment.yaml @@ -1,4 +1,3 @@ -{{- include "kagent.validateController" . -}} apiVersion: apps/v1 kind: Deployment metadata: @@ -30,14 +29,8 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "kagent.fullname" . }}-controller - {{- if or (eq .Values.database.type "sqlite") (gt (len .Values.controller.volumes) 0) }} + {{- if gt (len .Values.controller.volumes) 0 }} volumes: - {{- if eq .Values.database.type "sqlite" }} - - name: sqlite-volume - emptyDir: - sizeLimit: 500Mi - medium: Memory - {{- end }} {{- with .Values.controller.volumes }} {{- toYaml . | nindent 6 }} {{- end }} @@ -67,10 +60,6 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName - {{- if eq .Values.database.type "sqlite" }} - - name: XDG_CACHE_HOME - value: /sqlite-volume/.cache - {{- end }} {{- with .Values.controller.env }} {{- toYaml . | nindent 12 }} {{- end }} @@ -101,12 +90,8 @@ spec: path: /health port: http periodSeconds: 30 - {{- if or (eq .Values.database.type "sqlite") (gt (len .Values.controller.volumeMounts) 0) }} + {{- if gt (len .Values.controller.volumeMounts) 0 }} volumeMounts: - {{- if eq .Values.database.type "sqlite" }} - - name: sqlite-volume - mountPath: /sqlite-volume - {{- end }} {{- with .Values.controller.volumeMounts }} {{- toYaml . | nindent 12 }} {{- end }} diff --git a/helm/kagent/templates/postgresql.yaml b/helm/kagent/templates/postgresql.yaml new file mode 100644 index 000000000..43ba99690 --- /dev/null +++ b/helm/kagent/templates/postgresql.yaml @@ -0,0 +1,90 @@ +{{- if and (eq .Values.database.postgres.url "") (eq .Values.database.postgres.urlFile "") }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "kagent.fullname" . }}-postgresql + namespace: {{ include "kagent.namespace" . }} + labels: + {{- include "kagent.labels" . | nindent 4 }} + app.kubernetes.io/component: postgresql +data: + POSTGRES_DB: {{ .Values.database.postgres.bundled.database | quote }} + POSTGRES_USER: {{ .Values.database.postgres.bundled.user | quote }} + POSTGRES_PASSWORD: {{ .Values.database.postgres.bundled.password | quote }} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "kagent.fullname" . }}-postgresql + namespace: {{ include "kagent.namespace" . }} + labels: + {{- include "kagent.labels" . | nindent 4 }} + app.kubernetes.io/component: postgresql +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.database.postgres.bundled.storage | quote }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "kagent.fullname" . }}-postgresql + namespace: {{ include "kagent.namespace" . }} + labels: + {{- include "kagent.labels" . | nindent 4 }} + app.kubernetes.io/component: postgresql +spec: + replicas: 1 + selector: + matchLabels: + {{- include "kagent.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: postgresql + template: + metadata: + labels: + {{- include "kagent.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: postgresql + spec: + containers: + - name: postgresql + image: {{ .Values.database.postgres.bundled.image | quote }} + ports: + - containerPort: 5432 + envFrom: + - configMapRef: + name: {{ include "kagent.fullname" . }}-postgresql + volumeMounts: + - name: postgresql-storage + mountPath: /var/lib/postgresql + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + volumes: + - name: postgresql-storage + persistentVolumeClaim: + claimName: {{ include "kagent.fullname" . }}-postgresql +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "kagent.postgresqlServiceName" . }} + namespace: {{ include "kagent.namespace" . }} + labels: + {{- include "kagent.labels" . | nindent 4 }} + app.kubernetes.io/component: postgresql +spec: + selector: + {{- include "kagent.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: postgresql + ports: + - port: 5432 + targetPort: 5432 + type: ClusterIP +{{- end }} diff --git a/helm/kagent/tests/controller-deployment_test.yaml b/helm/kagent/tests/controller-deployment_test.yaml index 2b43c31be..b69140cd5 100644 --- a/helm/kagent/tests/controller-deployment_test.yaml +++ b/helm/kagent/tests/controller-deployment_test.yaml @@ -17,24 +17,11 @@ tests: - hasDocuments: count: 1 - - it: should fail when replicas > 1 and database type is sqlite + - it: should render the controller deployment with custom replica count template: controller-deployment.yaml set: controller: replicas: 3 - database: - type: sqlite - asserts: - - failedTemplate: - errorMessage: "ERROR: controller.replicas cannot be greater than 1 when database.type is 'sqlite' as the SQLite database is local to the pod. Please either set controller.replicas to 1 or change database.type to 'postgres'." - - - it: should render controller deployment with custom replica count if database type is postgres - template: controller-deployment.yaml - set: - controller: - replicas: 3 - database: - type: postgres asserts: - equal: path: spec.replicas @@ -194,11 +181,9 @@ tests: effect: NoSchedule operator: Equal - - it: should not render sqlite volume and mount when using postgres with extra volumes + - it: should render extra volumes and mounts when provided template: controller-deployment.yaml set: - database: - type: postgres controller: volumes: - name: extra-data @@ -207,34 +192,19 @@ tests: - name: extra-data mountPath: /extra asserts: - # volumes block should exist due to provided extra volumes - contains: path: spec.template.spec.volumes content: name: extra-data emptyDir: {} - # sqlite volume must not be present - - notContains: - path: spec.template.spec.volumes - content: - name: sqlite-volume - # volumeMounts block should include our extra mount - contains: path: spec.template.spec.containers[0].volumeMounts content: name: extra-data mountPath: /extra - # sqlite volume mount must not be present - - notContains: - path: spec.template.spec.containers[0].volumeMounts - content: - name: sqlite-volume - - it: should not render volumes or volumeMounts sections when using postgres without extra volumes or mounts + - it: should not render volumes or volumeMounts sections when none are configured template: controller-deployment.yaml - set: - database: - type: postgres asserts: - isNull: path: spec.template.spec.volumes diff --git a/helm/kagent/values.yaml b/helm/kagent/values.yaml index acd8c0b40..3726c3702 100644 --- a/helm/kagent/values.yaml +++ b/helm/kagent/values.yaml @@ -52,16 +52,24 @@ nodeSelector: {} # ============================================================================== database: - type: sqlite - sqlite: - vectorEnabled: true - databaseName: kagent.db postgres: - vectorEnabled: true - url: postgres://postgres:kagent@pgsql-postgresql.kagent.svc.cluster.local:5432/postgres - # Path to a file containing the database URL. - # Takes precedence over url when set. + # PostgreSQL connection string. Leave empty to deploy the bundled postgres instance. + # Set this to use an external PostgreSQL. + url: "" + # Path to a file containing the database URL. Takes precedence over url when set. + # Leave empty to use the bundled postgres instance. urlFile: "" + # Enable pgvector extension and memory table migration. + # Requires pgvector to be installed on the PostgreSQL server. + # Set to false when using an external PostgreSQL that does not have pgvector. + vectorEnabled: true + # Bundled PostgreSQL configuration — used when url and urlFile are both empty. + bundled: + image: pgvector/pgvector:pg18-trixie + storage: 500Mi + database: postgres + user: postgres + password: kagent # ============================================================================== # CONTROLLER CONFIGURATION