diff --git a/server/internal/handler/workspace.go b/server/internal/handler/workspace.go index fc09ef3b..10b2136d 100644 --- a/server/internal/handler/workspace.go +++ b/server/internal/handler/workspace.go @@ -20,6 +20,7 @@ type WorkspaceResponse struct { Description *string `json:"description"` Context *string `json:"context"` Settings any `json:"settings"` + Repos any `json:"repos"` CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` } @@ -32,6 +33,13 @@ func workspaceToResponse(w db.Workspace) WorkspaceResponse { if settings == nil { settings = map[string]any{} } + var repos any + if w.Repos != nil { + json.Unmarshal(w.Repos, &repos) + } + if repos == nil { + repos = []any{} + } return WorkspaceResponse{ ID: uuidToString(w.ID), Name: w.Name, @@ -39,6 +47,7 @@ func workspaceToResponse(w db.Workspace) WorkspaceResponse { Description: textToPtr(w.Description), Context: textToPtr(w.Context), Settings: settings, + Repos: repos, CreatedAt: timestampToString(w.CreatedAt), UpdatedAt: timestampToString(w.UpdatedAt), } @@ -169,6 +178,7 @@ type UpdateWorkspaceRequest struct { Description *string `json:"description"` Context *string `json:"context"` Settings any `json:"settings"` + Repos any `json:"repos"` } func (h *Handler) UpdateWorkspace(w http.ResponseWriter, r *http.Request) { @@ -204,6 +214,10 @@ func (h *Handler) UpdateWorkspace(w http.ResponseWriter, r *http.Request) { s, _ := json.Marshal(req.Settings) params.Settings = s } + if req.Repos != nil { + r, _ := json.Marshal(req.Repos) + params.Repos = r + } ws, err := h.Queries.UpdateWorkspace(r.Context(), params) if err != nil { diff --git a/server/migrations/014_workspace_repos.down.sql b/server/migrations/014_workspace_repos.down.sql new file mode 100644 index 00000000..2268bddf --- /dev/null +++ b/server/migrations/014_workspace_repos.down.sql @@ -0,0 +1 @@ +ALTER TABLE workspace DROP COLUMN repos; diff --git a/server/migrations/014_workspace_repos.up.sql b/server/migrations/014_workspace_repos.up.sql new file mode 100644 index 00000000..d37af6fe --- /dev/null +++ b/server/migrations/014_workspace_repos.up.sql @@ -0,0 +1 @@ +ALTER TABLE workspace ADD COLUMN repos JSONB NOT NULL DEFAULT '[]'; diff --git a/server/pkg/db/generated/models.go b/server/pkg/db/generated/models.go index dd71c7ea..ff125cdd 100644 --- a/server/pkg/db/generated/models.go +++ b/server/pkg/db/generated/models.go @@ -179,6 +179,18 @@ type Member struct { CreatedAt pgtype.Timestamptz `json:"created_at"` } +type PersonalAccessToken struct { + ID pgtype.UUID `json:"id"` + UserID pgtype.UUID `json:"user_id"` + Name string `json:"name"` + TokenHash string `json:"token_hash"` + TokenPrefix string `json:"token_prefix"` + ExpiresAt pgtype.Timestamptz `json:"expires_at"` + LastUsedAt pgtype.Timestamptz `json:"last_used_at"` + Revoked bool `json:"revoked"` + CreatedAt pgtype.Timestamptz `json:"created_at"` +} + type RuntimeUsage struct { ID pgtype.UUID `json:"id"` RuntimeID pgtype.UUID `json:"runtime_id"` @@ -193,18 +205,6 @@ type RuntimeUsage struct { UpdatedAt pgtype.Timestamptz `json:"updated_at"` } -type PersonalAccessToken struct { - ID pgtype.UUID `json:"id"` - UserID pgtype.UUID `json:"user_id"` - Name string `json:"name"` - TokenHash string `json:"token_hash"` - TokenPrefix string `json:"token_prefix"` - ExpiresAt pgtype.Timestamptz `json:"expires_at"` - LastUsedAt pgtype.Timestamptz `json:"last_used_at"` - Revoked bool `json:"revoked"` - CreatedAt pgtype.Timestamptz `json:"created_at"` -} - type Skill struct { ID pgtype.UUID `json:"id"` WorkspaceID pgtype.UUID `json:"workspace_id"` @@ -254,4 +254,5 @@ type Workspace struct { CreatedAt pgtype.Timestamptz `json:"created_at"` UpdatedAt pgtype.Timestamptz `json:"updated_at"` Context pgtype.Text `json:"context"` + Repos []byte `json:"repos"` } diff --git a/server/pkg/db/generated/runtime_usage.sql.go b/server/pkg/db/generated/runtime_usage.sql.go index 99ffeb21..f01354ce 100644 --- a/server/pkg/db/generated/runtime_usage.sql.go +++ b/server/pkg/db/generated/runtime_usage.sql.go @@ -11,41 +11,52 @@ import ( "github.com/jackc/pgx/v5/pgtype" ) -const upsertRuntimeUsage = `-- name: UpsertRuntimeUsage :exec -INSERT INTO runtime_usage (runtime_id, date, provider, model, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8) -ON CONFLICT (runtime_id, date, provider, model) -DO UPDATE SET - input_tokens = EXCLUDED.input_tokens, - output_tokens = EXCLUDED.output_tokens, - cache_read_tokens = EXCLUDED.cache_read_tokens, - cache_write_tokens = EXCLUDED.cache_write_tokens, - updated_at = now() +const getRuntimeUsageSummary = `-- name: GetRuntimeUsageSummary :many +SELECT provider, model, + SUM(input_tokens)::bigint AS total_input_tokens, + SUM(output_tokens)::bigint AS total_output_tokens, + SUM(cache_read_tokens)::bigint AS total_cache_read_tokens, + SUM(cache_write_tokens)::bigint AS total_cache_write_tokens +FROM runtime_usage +WHERE runtime_id = $1 +GROUP BY provider, model +ORDER BY provider, model ` -type UpsertRuntimeUsageParams struct { - RuntimeID pgtype.UUID `json:"runtime_id"` - Date pgtype.Date `json:"date"` - Provider string `json:"provider"` - Model string `json:"model"` - InputTokens int64 `json:"input_tokens"` - OutputTokens int64 `json:"output_tokens"` - CacheReadTokens int64 `json:"cache_read_tokens"` - CacheWriteTokens int64 `json:"cache_write_tokens"` +type GetRuntimeUsageSummaryRow struct { + Provider string `json:"provider"` + Model string `json:"model"` + TotalInputTokens int64 `json:"total_input_tokens"` + TotalOutputTokens int64 `json:"total_output_tokens"` + TotalCacheReadTokens int64 `json:"total_cache_read_tokens"` + TotalCacheWriteTokens int64 `json:"total_cache_write_tokens"` } -func (q *Queries) UpsertRuntimeUsage(ctx context.Context, arg UpsertRuntimeUsageParams) error { - _, err := q.db.Exec(ctx, upsertRuntimeUsage, - arg.RuntimeID, - arg.Date, - arg.Provider, - arg.Model, - arg.InputTokens, - arg.OutputTokens, - arg.CacheReadTokens, - arg.CacheWriteTokens, - ) - return err +func (q *Queries) GetRuntimeUsageSummary(ctx context.Context, runtimeID pgtype.UUID) ([]GetRuntimeUsageSummaryRow, error) { + rows, err := q.db.Query(ctx, getRuntimeUsageSummary, runtimeID) + if err != nil { + return nil, err + } + defer rows.Close() + items := []GetRuntimeUsageSummaryRow{} + for rows.Next() { + var i GetRuntimeUsageSummaryRow + if err := rows.Scan( + &i.Provider, + &i.Model, + &i.TotalInputTokens, + &i.TotalOutputTokens, + &i.TotalCacheReadTokens, + &i.TotalCacheWriteTokens, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil } const listRuntimeUsage = `-- name: ListRuntimeUsage :many @@ -92,50 +103,39 @@ func (q *Queries) ListRuntimeUsage(ctx context.Context, arg ListRuntimeUsagePara return items, nil } -const getRuntimeUsageSummary = `-- name: GetRuntimeUsageSummary :many -SELECT provider, model, - SUM(input_tokens)::bigint AS total_input_tokens, - SUM(output_tokens)::bigint AS total_output_tokens, - SUM(cache_read_tokens)::bigint AS total_cache_read_tokens, - SUM(cache_write_tokens)::bigint AS total_cache_write_tokens -FROM runtime_usage -WHERE runtime_id = $1 -GROUP BY provider, model -ORDER BY provider, model +const upsertRuntimeUsage = `-- name: UpsertRuntimeUsage :exec +INSERT INTO runtime_usage (runtime_id, date, provider, model, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8) +ON CONFLICT (runtime_id, date, provider, model) +DO UPDATE SET + input_tokens = EXCLUDED.input_tokens, + output_tokens = EXCLUDED.output_tokens, + cache_read_tokens = EXCLUDED.cache_read_tokens, + cache_write_tokens = EXCLUDED.cache_write_tokens, + updated_at = now() ` -type GetRuntimeUsageSummaryRow struct { - Provider string `json:"provider"` - Model string `json:"model"` - TotalInputTokens int64 `json:"total_input_tokens"` - TotalOutputTokens int64 `json:"total_output_tokens"` - TotalCacheReadTokens int64 `json:"total_cache_read_tokens"` - TotalCacheWriteTokens int64 `json:"total_cache_write_tokens"` +type UpsertRuntimeUsageParams struct { + RuntimeID pgtype.UUID `json:"runtime_id"` + Date pgtype.Date `json:"date"` + Provider string `json:"provider"` + Model string `json:"model"` + InputTokens int64 `json:"input_tokens"` + OutputTokens int64 `json:"output_tokens"` + CacheReadTokens int64 `json:"cache_read_tokens"` + CacheWriteTokens int64 `json:"cache_write_tokens"` } -func (q *Queries) GetRuntimeUsageSummary(ctx context.Context, runtimeID pgtype.UUID) ([]GetRuntimeUsageSummaryRow, error) { - rows, err := q.db.Query(ctx, getRuntimeUsageSummary, runtimeID) - if err != nil { - return nil, err - } - defer rows.Close() - items := []GetRuntimeUsageSummaryRow{} - for rows.Next() { - var i GetRuntimeUsageSummaryRow - if err := rows.Scan( - &i.Provider, - &i.Model, - &i.TotalInputTokens, - &i.TotalOutputTokens, - &i.TotalCacheReadTokens, - &i.TotalCacheWriteTokens, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil +func (q *Queries) UpsertRuntimeUsage(ctx context.Context, arg UpsertRuntimeUsageParams) error { + _, err := q.db.Exec(ctx, upsertRuntimeUsage, + arg.RuntimeID, + arg.Date, + arg.Provider, + arg.Model, + arg.InputTokens, + arg.OutputTokens, + arg.CacheReadTokens, + arg.CacheWriteTokens, + ) + return err } diff --git a/server/pkg/db/generated/workspace.sql.go b/server/pkg/db/generated/workspace.sql.go index b237493d..bd527a91 100644 --- a/server/pkg/db/generated/workspace.sql.go +++ b/server/pkg/db/generated/workspace.sql.go @@ -14,7 +14,7 @@ import ( const createWorkspace = `-- name: CreateWorkspace :one INSERT INTO workspace (name, slug, description, context) VALUES ($1, $2, $3, $4) -RETURNING id, name, slug, description, settings, created_at, updated_at, context +RETURNING id, name, slug, description, settings, created_at, updated_at, context, repos ` type CreateWorkspaceParams struct { @@ -41,6 +41,7 @@ func (q *Queries) CreateWorkspace(ctx context.Context, arg CreateWorkspaceParams &i.CreatedAt, &i.UpdatedAt, &i.Context, + &i.Repos, ) return i, err } @@ -55,7 +56,7 @@ func (q *Queries) DeleteWorkspace(ctx context.Context, id pgtype.UUID) error { } const getWorkspace = `-- name: GetWorkspace :one -SELECT id, name, slug, description, settings, created_at, updated_at, context FROM workspace +SELECT id, name, slug, description, settings, created_at, updated_at, context, repos FROM workspace WHERE id = $1 ` @@ -71,12 +72,13 @@ func (q *Queries) GetWorkspace(ctx context.Context, id pgtype.UUID) (Workspace, &i.CreatedAt, &i.UpdatedAt, &i.Context, + &i.Repos, ) return i, err } const getWorkspaceBySlug = `-- name: GetWorkspaceBySlug :one -SELECT id, name, slug, description, settings, created_at, updated_at, context FROM workspace +SELECT id, name, slug, description, settings, created_at, updated_at, context, repos FROM workspace WHERE slug = $1 ` @@ -92,12 +94,13 @@ func (q *Queries) GetWorkspaceBySlug(ctx context.Context, slug string) (Workspac &i.CreatedAt, &i.UpdatedAt, &i.Context, + &i.Repos, ) return i, err } const listWorkspaces = `-- name: ListWorkspaces :many -SELECT w.id, w.name, w.slug, w.description, w.settings, w.created_at, w.updated_at, w.context FROM workspace w +SELECT w.id, w.name, w.slug, w.description, w.settings, w.created_at, w.updated_at, w.context, w.repos FROM workspace w JOIN member m ON m.workspace_id = w.id WHERE m.user_id = $1 ORDER BY w.created_at ASC @@ -121,6 +124,7 @@ func (q *Queries) ListWorkspaces(ctx context.Context, userID pgtype.UUID) ([]Wor &i.CreatedAt, &i.UpdatedAt, &i.Context, + &i.Repos, ); err != nil { return nil, err } @@ -138,9 +142,10 @@ UPDATE workspace SET description = COALESCE($3, description), context = COALESCE($4, context), settings = COALESCE($5, settings), + repos = COALESCE($6, repos), updated_at = now() WHERE id = $1 -RETURNING id, name, slug, description, settings, created_at, updated_at, context +RETURNING id, name, slug, description, settings, created_at, updated_at, context, repos ` type UpdateWorkspaceParams struct { @@ -149,6 +154,7 @@ type UpdateWorkspaceParams struct { Description pgtype.Text `json:"description"` Context pgtype.Text `json:"context"` Settings []byte `json:"settings"` + Repos []byte `json:"repos"` } func (q *Queries) UpdateWorkspace(ctx context.Context, arg UpdateWorkspaceParams) (Workspace, error) { @@ -158,6 +164,7 @@ func (q *Queries) UpdateWorkspace(ctx context.Context, arg UpdateWorkspaceParams arg.Description, arg.Context, arg.Settings, + arg.Repos, ) var i Workspace err := row.Scan( @@ -169,6 +176,7 @@ func (q *Queries) UpdateWorkspace(ctx context.Context, arg UpdateWorkspaceParams &i.CreatedAt, &i.UpdatedAt, &i.Context, + &i.Repos, ) return i, err } diff --git a/server/pkg/db/queries/workspace.sql b/server/pkg/db/queries/workspace.sql index cafc57e9..73172ede 100644 --- a/server/pkg/db/queries/workspace.sql +++ b/server/pkg/db/queries/workspace.sql @@ -23,6 +23,7 @@ UPDATE workspace SET description = COALESCE(sqlc.narg('description'), description), context = COALESCE(sqlc.narg('context'), context), settings = COALESCE(sqlc.narg('settings'), settings), + repos = COALESCE(sqlc.narg('repos'), repos), updated_at = now() WHERE id = $1 RETURNING *;