Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions aigateway/component/openai.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"log/slog"
"strings"
"time"

"github.com/google/uuid"
Expand Down Expand Up @@ -54,16 +55,19 @@ func (m *openaiComponentImpl) GetAvailableModels(c context.Context, userName str
if err != nil {
slog.Error("get deploy hardware ")
}
// Check if engine_args contains tool-call-parser parameter
supportFunctionCall := strings.Contains(deploy.EngineArgs, "tool-call-parser")
m := types.Model{
Object: "model",
Created: deploy.CreatedAt.Unix(),
Task: string(deploy.Task),
CSGHubModelID: deploy.Repository.Path,
SvcName: deploy.SvcName,
SvcType: deploy.Type,
Hardware: hardwareInfo,
RuntimeFramework: deploy.RuntimeFramework,
ImageID: deploy.ImageID,
Object: "model",
Created: deploy.CreatedAt.Unix(),
Task: string(deploy.Task),
CSGHubModelID: deploy.Repository.Path,
SvcName: deploy.SvcName,
SvcType: deploy.Type,
Hardware: hardwareInfo,
RuntimeFramework: deploy.RuntimeFramework,
ImageID: deploy.ImageID,
SupportFunctionCall: supportFunctionCall,
}
modelName := ""
if deploy.Repository.HFPath != "" {
Expand Down
7 changes: 4 additions & 3 deletions aigateway/types/openai.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ type Model struct {
SvcName string `json:"-"` // the internal service name in CSGHub
SvcType int `json:"-"` // the internal service type like dedicated or serverless in CSGHub

Hardware types.HardWare `json:"-"` // the deployed hardware
RuntimeFramework string `json:"-"` // the deployed framework
ImageID string `json:"-"` // the deployed image
Hardware types.HardWare `json:"-"` // the deployed hardware
RuntimeFramework string `json:"-"` // the deployed framework
ImageID string `json:"-"` // the deployed image
SupportFunctionCall bool `json:"support_function_call,omitempty"` // whether the model supports function calling
}

// ModelList represents the model list response
Expand Down
57 changes: 55 additions & 2 deletions api/handler/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package handler

import (
"encoding/csv"
"fmt"
"log/slog"
"net/http"
"strconv"
"time"

"github.com/gin-gonic/gin"
"opencsg.com/csghub-server/api/httpbase"
Expand All @@ -29,6 +31,11 @@ type ClusterHandler struct {
c component.ClusterComponent
}

const (
deployTimeLayout = "2006-01-02 15:04:05"
deployDateOnlyLayout = "2006-01-02"
)

// Getclusters godoc
// @Security ApiKey
// @Summary Get cluster list
Expand Down Expand Up @@ -146,6 +153,8 @@ func (h *ClusterHandler) GetDeploys(ctx *gin.Context) {
// @Produce text/csv
// @Param status query string false "status" default(all) Enums(all, running, stopped, deployfailed)
// @Param search query string false "search" default("")
// @Param start_time query string false "filter deploys created after or at this time"
// @Param end_time query string false "filter deploys created before or at this time"
// @Success 200 {string} string "CSV file"
// @Failure 400 {object} types.APIBadRequest "Bad request"
// @Failure 500 {object} types.APIInternalServerError "Internal server error"
Expand All @@ -165,6 +174,11 @@ func (h *ClusterHandler) GetDeploysReport(ctx *gin.Context) {
req.Status = []int{code.DeployFailed}
}
req.Query = ctx.Query("search")
if err := bindDeployDateRange(ctx, &req); err != nil {
slog.Error("Invalid date range for deploy report", slog.Any("error", err))
httpbase.BadRequest(ctx, err.Error())
return
}

filename := "deploys_report.csv"
ctx.Header("Content-Type", "text/csv; charset=utf-8")
Expand All @@ -189,7 +203,6 @@ func (h *ClusterHandler) GetDeploysReport(ctx *gin.Context) {
})
writer.Flush()

const timeLayout = "2006-01-02 15:04:05"
totalProcessed := 0

for {
Expand All @@ -207,7 +220,7 @@ func (h *ClusterHandler) GetDeploysReport(ctx *gin.Context) {
d.DeployName,
d.User.Username,
d.Resource,
d.CreateTime.Local().Format(timeLayout),
d.CreateTime.Local().Format(deployTimeLayout),
d.Status,
strconv.Itoa(d.TotalTimeInMin),
strconv.Itoa(d.TotalFeeInCents),
Expand Down Expand Up @@ -246,3 +259,43 @@ func (h *ClusterHandler) Update(ctx *gin.Context) {
}
httpbase.OK(ctx, result)
}

func bindDeployDateRange(ctx *gin.Context, req *types.DeployReq) error {
startTime := ctx.Query("start_time")
endTime := ctx.Query("end_time")
if startTime == "" && endTime == "" {
return nil
}
if startTime == "" || endTime == "" {
return fmt.Errorf("start_time and end_time must be provided together")
}
parsedStart, err := parseDeployQueryTime(startTime, false)
if err != nil {
return err
}
parsedEnd, err := parseDeployQueryTime(endTime, true)
if err != nil {
return err
}
req.StartTime = &parsedStart
req.EndTime = &parsedEnd
return nil
}

func parseDeployQueryTime(value string, isEnd bool) (time.Time, error) {
layouts := []string{deployTimeLayout, deployDateOnlyLayout}
for _, layout := range layouts {
parsed, err := time.ParseInLocation(layout, value, time.UTC)
if err != nil {
continue
}
if layout == deployDateOnlyLayout {
if isEnd {
parsed = parsed.Add(24*time.Hour - time.Nanosecond)
}
return parsed, nil
}
return parsed, nil
}
return time.Time{}, fmt.Errorf("invalid datetime format, use '%s' or '%s'", deployTimeLayout, deployDateOnlyLayout)
}
34 changes: 33 additions & 1 deletion api/handler/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"net/http"
"testing"
"time"

"github.com/gin-gonic/gin"
"github.com/stretchr/testify/mock"
Expand Down Expand Up @@ -98,12 +99,25 @@ func Test_GetDeploysReport(t *testing.T) {
},
}

start := "2024-01-01 00:00:00"
end := "2024-01-31"
expectedStart, err := time.ParseInLocation(time.DateTime, start, time.UTC)
require.NoError(t, err)
endDate, err := time.ParseInLocation("2006-01-02", end, time.UTC)
require.NoError(t, err)
expectedEnd := endDate.Add(24*time.Hour - time.Nanosecond)
tester.mocks.clusterComponent.EXPECT().
GetDeploys(context.Background(), mock.Anything).
Run(func(_ context.Context, req types.DeployReq) {
require.NotNil(t, req.StartTime)
require.True(t, req.StartTime.Equal(expectedStart))
require.NotNil(t, req.EndTime)
require.True(t, req.EndTime.Equal(expectedEnd))
}).
Once().
Return(rows, len(rows), nil)

tester.Execute()
tester.WithQuery("start_time", start).WithQuery("end_time", end).Execute()

// assert response headers and body
resp := tester.Response()
Expand All @@ -117,3 +131,21 @@ func Test_GetDeploysReport(t *testing.T) {
require.Contains(t, body, "alice")
require.Contains(t, body, "bob")
}

func Test_GetDeploysReport_InvalidDateRange(t *testing.T) {
tester := newClusterTester(t).withHandlerFunc(func(clusterHandler *ClusterHandler) gin.HandlerFunc {
return clusterHandler.GetDeploysReport
})

tester.WithQuery("start_time", "2024-01-01").Execute()
tester.ResponseEqSimple(t, http.StatusBadRequest, httpbase.R{Msg: "start_time and end_time must be provided together"})
}

func Test_GetDeploysReport_InvalidFormat(t *testing.T) {
tester := newClusterTester(t).withHandlerFunc(func(clusterHandler *ClusterHandler) gin.HandlerFunc {
return clusterHandler.GetDeploysReport
})

tester.WithQuery("start_time", "invalid").WithQuery("end_time", "2024-01-01").Execute()
tester.ResponseEqSimple(t, http.StatusBadRequest, httpbase.R{Msg: "invalid datetime format, use '2006-01-02 15:04:05' or '2006-01-02'"})
}
9 changes: 8 additions & 1 deletion builder/store/database/deploy_task.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,14 @@ func (s *deployTaskStoreImpl) ListDeployByType(ctx context.Context, req types.De
query = query.Where("deploy_name LIKE ? OR \"user\".\"username\" LIKE ? OR cluster_id LIKE ?", "%"+req.Query+"%", "%"+req.Query+"%", "%"+req.Query+"%")
}

query = query.Order("created_at DESC").Limit(req.PageSize).Offset((req.Page - 1) * req.PageSize)
if req.StartTime != nil {
query = query.Where("deploy.created_at >= ?", req.StartTime)
}
if req.EndTime != nil {
query = query.Where("deploy.created_at <= ?", req.EndTime)
}

query = query.Order("deploy.created_at DESC").Limit(req.PageSize).Offset((req.Page - 1) * req.PageSize)
_, err := query.Exec(ctx, &result)
if err != nil {
err = errorx.HandleDBError(err, nil)
Expand Down
33 changes: 33 additions & 0 deletions builder/store/database/deploy_task_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package database_test
import (
"context"
"testing"
"time"

"github.com/stretchr/testify/require"
"opencsg.com/csghub-server/builder/deploy/common"
Expand Down Expand Up @@ -567,6 +568,19 @@ func TestDeployTaskStore_ListDeployBytype(t *testing.T) {
require.Nil(t, err)
}

now := time.Now().UTC().Truncate(time.Second)
older := now.Add(-72 * time.Hour)
middle := now.Add(-12 * time.Hour)
latest := now.Add(-1 * time.Hour)
_, err := db.BunDB.ExecContext(ctx, "UPDATE deploys SET created_at = ?, updated_at = ? WHERE deploy_name = ?", older, older, "running1")
require.NoError(t, err)
_, err = db.BunDB.ExecContext(ctx, "UPDATE deploys SET created_at = ?, updated_at = ? WHERE deploy_name = ?", older.Add(24*time.Hour), older.Add(24*time.Hour), "stopped1")
require.NoError(t, err)
_, err = db.BunDB.ExecContext(ctx, "UPDATE deploys SET created_at = ?, updated_at = ? WHERE deploy_name = ?", middle, middle, "running2")
require.NoError(t, err)
_, err = db.BunDB.ExecContext(ctx, "UPDATE deploys SET created_at = ?, updated_at = ? WHERE deploy_name = ?", latest, latest, "deploy1")
require.NoError(t, err)

// Only test running ones
var req types.DeployReq
req.Page = 1
Expand All @@ -578,6 +592,25 @@ func TestDeployTaskStore_ListDeployBytype(t *testing.T) {
result, _, err = store.ListDeployByType(ctx, req)
require.Nil(t, err)
require.Equal(t, 2, len(result))

startWindow := now.Add(-24 * time.Hour)
req.StartTime = &startWindow
endDate, err := time.ParseInLocation("2006-01-02", now.Format("2006-01-02"), time.UTC)
require.NoError(t, err)
endWindow := endDate.Add(24*time.Hour - time.Nanosecond)
req.EndTime = &endWindow
result, _, err = store.ListDeployByType(ctx, req)
require.Nil(t, err)
require.Equal(t, 1, len(result))
require.Equal(t, "running2", result[0].DeployName)

startWindow = now.Add(-5 * time.Hour)
req.StartTime = &startWindow
endWindow = now
req.EndTime = &endWindow
result, _, err = store.ListDeployByType(ctx, req)
require.Nil(t, err)
require.Equal(t, 0, len(result))
}
func TestDeployTaskStore_DeleteDeployByID(t *testing.T) {
db := tests.InitTestDB()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
SET statement_timeout = 0;

--bun:split

-- Rollback SQL statements for MS-SWIFT dataset tags
DELETE FROM tag_rules WHERE runtime_framework = 'ms-swift' AND repo_type = 'dataset' AND category = 'task' AND source = 'ms';
Loading
Loading