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
104 changes: 104 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# AGENTS Guidelines for This Repository

This repository is a Go project following the **microservice** architecture design. Services including:

- **API**: The API service handles HTTP requests and responses. It is the entry point for external clients to interact with the system.
- **User**: The User service handles user-related operations, such as user registration, login, and profile management. All requests are proxied to this service from the API service.
- **Accounting**: The Accounting service handles accounting-related operations, such as recording user token or hardware resource usage, or updating user balances. All requests are proxied to this service from the API service.
- **Moderation**: The Moderation service handles content moderation operations, such as flagging inappropriate text or images. All requests are proxied to this service from the API service.
- **DataViewer**: The DataViewer service handles dataset preview operations, such as fetching dataset metadata or previewing dataset files. All requests are proxied to this service from the API service.
- **Notification**: The Notification service handles sending notifications to users, such as email or push notifications. All requests are proxied to this service from the API service.
- **Payment**: The Payment service handles payment operations, such as processing payments or refunding payments. All requests are proxied to this service from the API service.
- **AIGateway**: The AIGateway service handles AI model inference operations, such as running AI models or generating AI outputs. It's another entry point for external clients to interact with the AI models.
- **Runner**: The Runner service is a bridge between api service and Kubernetes cluster. It handles deployment of models, spaces.
- **LogCollector**: The LogCollector service handles collecting logs from Kubernetes cluster. All logs are sent to this service from the Runner service and API service.

# Structure
Every service follows the layered architecture design: handler -> component -> builder (database, rpc, git, etc.).

- The handler layer handles HTTP requests and responses.
- The component layer handles business logic and coordinates between different layers.
- The builder layer handles low-level operations, such as database access, RPC calls, or Git operations.
- Every golang file should have a corresponding `*_test.go` file for unit tests.

Folders relative to the root of the repository for each service:

| Service | Folder |
|---------|--------|
| API | api |
| User | user |
| Accounting | accounting |
| Moderation | moderation |
| DataViewer | dataviewer |
| Notification | notification |
| Payment | payment |
| AIGateway | aigateway |
| Runner | runner |
| LogCollector | logcollector |

## Examples

### Router

- `api/router/api.go` is an example of a router that registers HTTP routes and their corresponding handlers for common functionality across services.
- `accounting/router/api.go` is an example of a router that registers HTTP routes and their corresponding handlers for the Accounting service.
- `runner/router/api.go` is an example of a router that registers HTTP routes and their corresponding handlers for the runner service.

### Handler Layer

- `api/handler/space.go` is an example of a handler that deals with space-related HTTP requests.
- `api/handler/evaluation.go` is an example of a handler that deals with evaluation-related HTTP requests.

### Component Layer

- `component/space.go` is an example of a component that deals with space-related business logic.
- `component/evaluation.go` is an example of a component that deals with evaluation-related business logic.

### Database Builder Layer

- `builder/store/database/space.go` is an example of a builder that deals with space-related database operations.

### Database Migration

- `builder/store/database/migrations/20240201061926_create_spaces.go` is an example of a database migration script that creates a space table.
- use `go run cmd/csghub-server/main.go migration create_go` to generate a go database migration script.
- use `go run cmd/csghub-server/main.go migration create_sql` to generate a sql database migration script.

### Space Deploy

- `builder/deploy/deployer.go` create build and deploy task in database, then create temporal workflow to run the task.
- `api/workflow/activity/deploy_activity.go` impletements temporal activities to run the build and deploy task by call runner api.
- `runner/handler/imagebuilder.go` implements runner api to trigger image builder process by call image builder component.
- `runner/component/imagebuilder.go` implements runner component to trigger deploy process by call knative api.
- `runner/handler/service.go` implements runner api to trigger deploy process by call deploy component.
- `runner/component/service.go` implements runner component to trigger deploy process by call knative api.
- `docker/spaces/builder/Dockerfile*` are Dockerfile that builds the space image.

## Code Style & Conventions:

- Each layer's interface should only expose data structures defined within its own layer or common type definitions from the common.types package. For example, interfaces in the Component layer (such as UserComponent) should not return data structures from the underlying database layer (such as database.User structure), as the database layer is considered lower-level than the component layer.
- Write unit tests for new code.
- Use struct data types instead of primitive types for function parameters and return values.
- All variables should be named in camelCase.
- Variables should be declared at the smallest possible scope under `common/types`.

### Do

### Do Not

## Testing

- Use `make mock_gen GO_TAGS={go.buildTags}` to generate mock implementations for the interfaces.
- Use `make test GO_TAGS={go.buildTags}` to run all tests in project.
- Mock dependencies (e.g., database, RPC clients) using tools like `mockery`.

## Tools

- Search `Makefile` for running, building, testing, and linting tools.
- Swagger doc is generated by `swag` tool, and it will be served by handler layer.

## Commit & Pull Request Guidelines:

- Each PR must include a clear description of the changes made and their impact, including root cause analysis if applicable, and solution details, and local test result.

## Specific Instructions

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

59 changes: 59 additions & 0 deletions _mocks/opencsg.com/csghub-server/component/mock_SpaceComponent.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 1 addition & 18 deletions api/workflow/activity/deploy_activity.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,8 +470,6 @@ func (a *DeployActivity) createBuildRequest(ctx context.Context, task *database.
return nil, fmt.Errorf("invalid repository path format: %s", repoInfo.Path)
}

sdkVersion := a.determineSDKVersion(repoInfo)

lastCommitReq := gitserver.GetRepoLastCommitReq{
RepoType: types.RepositoryType(repoInfo.RepoType),
Namespace: pathParts[0],
Expand All @@ -490,7 +488,7 @@ func (a *DeployActivity) createBuildRequest(ctx context.Context, task *database.
PythonVersion: "3.10",
Sdk: repoInfo.Sdk,
DriverVersion: repoInfo.DriverVersion,
Sdk_version: sdkVersion,
Sdk_version: repoInfo.SdkVersion,
SpaceURL: repoInfo.HTTPCloneURL,
GitRef: task.Deploy.GitBranch,
UserId: accessToken.User.Username,
Expand Down Expand Up @@ -588,21 +586,6 @@ func (a *DeployActivity) createDeployRequest(ctx context.Context, task *database
}, nil
}

func (a *DeployActivity) determineSDKVersion(repoInfo common.RepoInfo) string {
if repoInfo.SdkVersion != "" {
return repoInfo.SdkVersion
}

switch repoInfo.Sdk {
case types.GRADIO.Name:
return types.GRADIO.Version
case types.STREAMLIT.Name:
return types.STREAMLIT.Version
default:
return ""
}
}

func (a *DeployActivity) parseHardware(input string) string {
if strings.Contains(input, "GPU") || strings.Contains(input, "NVIDIA") {
return "gpu"
Expand Down
74 changes: 30 additions & 44 deletions api/workflow/activity/deploy_activity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"time"

"opencsg.com/csghub-server/builder/deploy/common"
"opencsg.com/csghub-server/builder/git/gitserver"
"opencsg.com/csghub-server/builder/store/database"
"opencsg.com/csghub-server/common/config"
"opencsg.com/csghub-server/common/types"
Expand Down Expand Up @@ -91,56 +92,41 @@ func setupTest(t *testing.T) *testEnv {
}
}

// TestActivities_determineSDKVersion tests the determineSDKVersion method
func TestActivities_determineSDKVersion(t *testing.T) {
// TestActivities_createBuildRequest tests the createBuildRequest method
func TestActivities_createBuildRequest(t *testing.T) {
tester := setupTest(t)

// Test cases
testCases := []struct {
name string
repoInfo common.RepoInfo
expectedSDK string
}{
{
name: "With explicit SDK version",
repoInfo: common.RepoInfo{
SdkVersion: "custom-version",
Sdk: "some-other-sdk",
},
expectedSDK: "custom-version",
},
{
name: "With GRADIO SDK",
repoInfo: common.RepoInfo{
SdkVersion: "",
Sdk: types.GRADIO.Name,
},
expectedSDK: types.GRADIO.Version,
},
{
name: "With STREAMLIT SDK",
repoInfo: common.RepoInfo{
SdkVersion: "",
Sdk: types.STREAMLIT.Name,
},
expectedSDK: types.STREAMLIT.Version,
},
{
name: "With unknown SDK",
repoInfo: common.RepoInfo{
SdkVersion: "",
Sdk: "unknown-sdk",
},
expectedSDK: "",
task := &database.DeployTask{
Deploy: &database.Deploy{
UserID: 1,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
sdkVersion := tester.activities.determineSDKVersion(tc.repoInfo)
require.Equal(t, tc.expectedSDK, sdkVersion)
})
repoInfo := common.RepoInfo{
Path: "org/space",
SdkVersion: "6.2.0",
}

tester.mockTokenStore.EXPECT().FindByUID(tester.ctx, task.Deploy.UserID).Return(&database.AccessToken{
Token: "test-token",
User: &database.User{
Username: "uname",
},
}, nil)

tester.mockGitServer.EXPECT().GetRepoLastCommit(tester.ctx, gitserver.GetRepoLastCommitReq{
RepoType: types.RepositoryType(repoInfo.RepoType),
Namespace: "org",
Name: "space",
Ref: task.Deploy.GitBranch,
}).Return(&types.Commit{
ID: "id",
}, nil)

r, err := tester.activities.createBuildRequest(tester.ctx, task, repoInfo)
require.Nil(t, err)
require.Equal(t, r.Sdk_version, repoInfo.SdkVersion)
require.Equal(t, r.LastCommitID, "id")
}

// TestActivities_handleDeployError tests the handleDeployError method
Expand Down
Loading
Loading