diff --git a/aigateway/router/aigateway.go b/aigateway/router/aigateway.go index f8ee22eee..a658c99d5 100644 --- a/aigateway/router/aigateway.go +++ b/aigateway/router/aigateway.go @@ -3,22 +3,28 @@ package router import ( "fmt" + "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" "opencsg.com/csghub-server/aigateway/handler" "opencsg.com/csghub-server/api/middleware" + "opencsg.com/csghub-server/builder/instrumentation" "opencsg.com/csghub-server/common/config" - "opencsg.com/csghub-server/common/i18n" ) func NewRouter(config *config.Config) (*gin.Engine, error) { r := gin.New() - r.Use(gin.Recovery()) - r.Use(middleware.Log()) + middleware.SetInfraMiddleware(r, config, instrumentation.Aigateway) + r.Use(cors.New(cors.Config{ + AllowCredentials: true, + AllowHeaders: []string{"*"}, + AllowMethods: []string{"*"}, + AllowAllOrigins: true, + })) //to access model,fintune with any kind of tokens in auth header - i18n.InitLocalizersFromEmbedFile() - r.Use(middleware.ModifyAcceptLanguageMiddleware(), middleware.LocalizedErrorMiddleware()) r.Use(middleware.Authenticator(config)) - mustLogin := middleware.MustLogin() + middlewareCollection := middleware.MiddlewareCollection{} + middlewareCollection.Auth.NeedLogin = middleware.MustLogin() + middlewareCollection.Auth.NeedPhoneVerified = middleware.NeedPhoneVerified(config) v1Group := r.Group("/v1") @@ -26,16 +32,19 @@ func NewRouter(config *config.Config) (*gin.Engine, error) { if err != nil { return nil, fmt.Errorf("error creating openai handler :%w", err) } - v1Group.GET("/models", mustLogin, openAIhandler.ListModels) - v1Group.GET("/models/:model", mustLogin, openAIhandler.GetModel) - v1Group.POST("/chat/completions", mustLogin, openAIhandler.Chat) - v1Group.POST("/embeddings", mustLogin, openAIhandler.Embedding) + v1Group.GET("/models", middlewareCollection.Auth.NeedLogin, openAIhandler.ListModels) + v1Group.GET("/models/:model", middlewareCollection.Auth.NeedLogin, openAIhandler.GetModel) + v1Group.POST("/chat/completions", middlewareCollection.Auth.NeedLogin, openAIhandler.Chat) + v1Group.POST("/embeddings", middlewareCollection.Auth.NeedLogin, openAIhandler.Embedding) mcpProxy, err := handler.NewMCPProxyHandler(config) if err != nil { return nil, fmt.Errorf("error creating mcp proxy handler :%w", err) } CreateMCPRoute(v1Group, mcpProxy) + if err := extendRoutes(v1Group, middlewareCollection, config); err != nil { + return nil, fmt.Errorf("error creating extended routes :%w", err) + } return r, nil } diff --git a/aigateway/router/api_ce.go b/aigateway/router/api_ce.go new file mode 100644 index 000000000..561537181 --- /dev/null +++ b/aigateway/router/api_ce.go @@ -0,0 +1,13 @@ +//go:build !ee && !saas + +package router + +import ( + "github.com/gin-gonic/gin" + "opencsg.com/csghub-server/api/middleware" + "opencsg.com/csghub-server/common/config" +) + +func extendRoutes(_ *gin.RouterGroup, _ middleware.MiddlewareCollection, _ *config.Config) error { + return nil +} diff --git a/api/handler/finetune.go b/api/handler/finetune.go index 00e1b9f74..de262f476 100644 --- a/api/handler/finetune.go +++ b/api/handler/finetune.go @@ -11,31 +11,9 @@ import ( "github.com/gin-gonic/gin" "opencsg.com/csghub-server/api/httpbase" - "opencsg.com/csghub-server/common/config" "opencsg.com/csghub-server/common/types" - "opencsg.com/csghub-server/component" ) -func NewFinetuneHandler(config *config.Config) (*FinetuneHandler, error) { - ftComp, err := component.NewFinetuneComponent(config) - if err != nil { - return nil, err - } - sc, err := component.NewSensitiveComponent(config) - if err != nil { - return nil, fmt.Errorf("error creating sensitive component:%w", err) - } - return &FinetuneHandler{ - ftComp: ftComp, - sensitive: sc, - }, nil -} - -type FinetuneHandler struct { - ftComp component.FinetuneComponent - sensitive component.SensitiveComponent -} - // create finetune godoc // @Security ApiKey // @Summary run fineune with model and dataset @@ -72,6 +50,9 @@ func (h *FinetuneHandler) RunFinetuneJob(ctx *gin.Context) { httpbase.ServerError(ctx, err) return } + + h.createAgentInstanceTask(ctx.Request.Context(), req.Agent, finetune.TaskId, currentUser) + httpbase.OK(ctx, finetune) } diff --git a/api/handler/finetune_ce.go b/api/handler/finetune_ce.go new file mode 100644 index 000000000..a514f9efa --- /dev/null +++ b/api/handler/finetune_ce.go @@ -0,0 +1,34 @@ +//go:build !ee && !saas + +package handler + +import ( + "context" + "fmt" + + "opencsg.com/csghub-server/common/config" + "opencsg.com/csghub-server/component" +) + +func NewFinetuneHandler(config *config.Config) (*FinetuneHandler, error) { + ftComp, err := component.NewFinetuneComponent(config) + if err != nil { + return nil, err + } + sc, err := component.NewSensitiveComponent(config) + if err != nil { + return nil, fmt.Errorf("error creating sensitive component:%w", err) + } + return &FinetuneHandler{ + ftComp: ftComp, + sensitive: sc, + }, nil +} + +type FinetuneHandler struct { + ftComp component.FinetuneComponent + sensitive component.SensitiveComponent +} + +func (h *FinetuneHandler) createAgentInstanceTask(_ context.Context, _ string, _ string, _ string) { +} diff --git a/api/handler/finetune_test.go b/api/handler/finetune_ce_test.go similarity index 99% rename from api/handler/finetune_test.go rename to api/handler/finetune_ce_test.go index b33e5246d..6c424d4c4 100644 --- a/api/handler/finetune_test.go +++ b/api/handler/finetune_ce_test.go @@ -1,3 +1,5 @@ +//go:build !ee && !saas + package handler import ( diff --git a/api/handler/model.go b/api/handler/model.go index 5c2ba18ef..21402ced8 100644 --- a/api/handler/model.go +++ b/api/handler/model.go @@ -11,39 +11,11 @@ import ( "github.com/gin-gonic/gin" "opencsg.com/csghub-server/api/httpbase" - "opencsg.com/csghub-server/common/config" "opencsg.com/csghub-server/common/errorx" "opencsg.com/csghub-server/common/types" "opencsg.com/csghub-server/common/utils/common" - "opencsg.com/csghub-server/component" ) -func NewModelHandler(config *config.Config) (*ModelHandler, error) { - uc, err := component.NewModelComponent(config) - if err != nil { - return nil, err - } - sc, err := component.NewSensitiveComponent(config) - if err != nil { - return nil, fmt.Errorf("error creating sensitive component:%w", err) - } - repo, err := component.NewRepoComponent(config) - if err != nil { - return nil, fmt.Errorf("error creating repo component:%w", err) - } - return &ModelHandler{ - model: uc, - sensitive: sc, - repo: repo, - }, nil -} - -type ModelHandler struct { - model component.ModelComponent - repo component.RepoComponent - sensitive component.SensitiveComponent -} - // GetVisiableModels godoc // @Security ApiKey // @Summary Get Visiable models for current user @@ -737,6 +709,7 @@ func (h *ModelHandler) DeployDedicated(ctx *gin.Context) { slog.Debug("deploy model as inference created", slog.String("namespace", namespace), slog.String("name", name), slog.Int64("deploy_id", deployID)) + h.createAgentInstanceTask(ctx.Request.Context(), req.Agent, fmt.Sprintf("%d", deployID), types.AgentTaskTypeInference, currentUser) // return deploy_id response := types.DeployRepo{DeployID: deployID} diff --git a/api/handler/model_ce.go b/api/handler/model_ce.go new file mode 100644 index 000000000..411546771 --- /dev/null +++ b/api/handler/model_ce.go @@ -0,0 +1,41 @@ +//go:build !ee && !saas + +package handler + +import ( + "context" + "fmt" + + "opencsg.com/csghub-server/common/config" + "opencsg.com/csghub-server/common/types" + "opencsg.com/csghub-server/component" +) + +func NewModelHandler(config *config.Config) (*ModelHandler, error) { + uc, err := component.NewModelComponent(config) + if err != nil { + return nil, err + } + sc, err := component.NewSensitiveComponent(config) + if err != nil { + return nil, fmt.Errorf("error creating sensitive component:%w", err) + } + repo, err := component.NewRepoComponent(config) + if err != nil { + return nil, fmt.Errorf("error creating repo component:%w", err) + } + return &ModelHandler{ + model: uc, + sensitive: sc, + repo: repo, + }, nil +} + +type ModelHandler struct { + model component.ModelComponent + repo component.RepoComponent + sensitive component.SensitiveComponent +} + +func (h *ModelHandler) createAgentInstanceTask(_ context.Context, _ string, _ string, _ types.AgentTaskType, _ string) { +} diff --git a/api/handler/model_test.go b/api/handler/model_ce_test.go similarity index 99% rename from api/handler/model_test.go rename to api/handler/model_ce_test.go index 9d0cffbe1..420173fd9 100644 --- a/api/handler/model_test.go +++ b/api/handler/model_ce_test.go @@ -1,3 +1,5 @@ +//go:build !ee && !saas + package handler import ( diff --git a/common/types/agent.go b/common/types/agent.go new file mode 100644 index 000000000..3c2e4fc84 --- /dev/null +++ b/common/types/agent.go @@ -0,0 +1,460 @@ +package types + +import ( + "encoding/json" + "time" +) + +// AgentTemplate represents the template for an agent +type AgentTemplate struct { + ID int64 `json:"id"` + Type *string `json:"type" binding:"required"` // Possible values: langflow, agno, code, etc. + UserUUID *string `json:"-"` // Will be set from HTTP header using httpbase.GetCurrentUserUUID + Name *string `json:"name" binding:"required,max=255"` // Agent template name + Description *string `json:"description" binding:"omitempty,max=500"` // Agent template description + Content *string `json:"content,omitempty"` // Used to store the complete content of the template + Public *bool `json:"public,omitempty"` // Whether the template is public + Metadata *map[string]any `json:"metadata,omitempty"` // Template metadata + CreatedAt time.Time `json:"created_at"` // When the template was created + UpdatedAt time.Time `json:"updated_at"` // When the template was last updated +} + +type AgentTemplateFilter struct { + Search string + Type string +} + +// AgentInstance represents an instance created from an agent template +type AgentInstance struct { + ID int64 `json:"id"` + TemplateID *int64 `json:"template_id" binding:"omitempty,gte=1"` // Associated with the id in the template table + UserUUID *string `json:"-"` // Will be set from HTTP header using httpbase.GetCurrentUserUUID + Name *string `json:"name"` // Instance name + Description *string `json:"description" binding:"omitempty"` // Instance description + Type *string `json:"type"` // Possible values: langflow, agno, code, etc. + ContentID *string `json:"content_id" binding:"omitempty"` // Used to specify the unique id of the instance resource + Public *bool `json:"public"` // Whether the instance is public + Editable bool `json:"editable"` // Whether the instance is editable + IsRunning bool `json:"is_running"` // Whether the instance is running + BuiltIn bool `json:"built_in"` // Whether the instance is built-in + Metadata *map[string]any `json:"metadata,omitempty"` // Instance metadata + CreatedAt time.Time `json:"created_at"` // When the instance was created + UpdatedAt time.Time `json:"updated_at"` // When the instance was last updated +} + +type AgentType string + +const ( + AgentTypeLangflow AgentType = "langflow" + AgentTypeCode AgentType = "code" +) + +func (t AgentType) String() string { + return string(t) +} + +type AgentInstanceFilter struct { + Search string + Type string + TemplateID *int64 `json:"template_id,omitempty"` + BuiltIn *bool `json:"built_in"` + Public *bool `json:"public"` +} + +type UpdateAgentInstanceRequest struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + Metadata *map[string]any `json:"metadata,omitempty"` +} + +type AgentInstanceCreationResult struct { + ID string + Name string + Description string + Metadata map[string]any // Additional metadata for the agent instance +} + +// LangFlowChatRequest represents a chat request to an agent instance +type LangflowChatRequest struct { + SessionID *string `json:"session_id,omitempty"` // Optional session ID (client-provided) + InputValue string `json:"input_value" binding:"required"` // Input value for the agent + InputType string `json:"input_type" binding:"required"` // Type of input (e.g., "chat") + OutputType string `json:"output_type" binding:"required"` // Type of output (e.g., "chat") + Tweaks json.RawMessage `json:"tweaks,omitempty"` // Optional parameter tweaks +} + +// AgentChatResponse represents the response from an agent chat +type AgentChatResponse struct { + SessionID string `json:"session_id"` // Session ID used for this conversation + OutputType string `json:"output_type"` // Output type from the request + Message string `json:"message"` // Agent's response message + InstanceID int64 `json:"instance_id"` // Agent instance ID + ContentID string `json:"content_id"` // Agent instance content ID + Type string `json:"type"` // Agent instance type + Timestamp string `json:"timestamp"` // When the response was generated + Sender string `json:"sender"` // Which Agent sent the message +} + +// AgentChatSession represents a chat session +type AgentInstanceSession struct { + ID int64 `json:"id"` + SessionUUID string `json:"session_uuid"` + Name string `json:"name"` + Type string `json:"type"` // Possible values: langflow, agno, code, etc. + InstanceID int64 `json:"instance_id"` + UserUUID string `json:"user_uuid"` + LastTurn int64 `json:"last_turn"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type AgentInstanceSessionFilter struct { + InstanceID *int64 + Search string +} + +type CreateAgentInstanceSessionRequest struct { + SessionUUID *string `json:"session_uuid,omitempty"` + Name *string `json:"name,omitempty" binding:"omitempty,max=255"` + Type string `json:"-"` // Possible values: langflow, agno, code, etc. + InstanceID *int64 `json:"-"` + ContentID *string `json:"-"` +} + +type CreateAgentInstanceSessionResponse struct { + SessionUUID string `json:"session_uuid"` +} + +type UpdateAgentInstanceSessionRequest struct { + Name string `json:"name" binding:"required,max=255"` +} + +type RecordAgentInstanceSessionHistoryRequest struct { + SessionUUID string `json:"session_uuid"` + Request bool `json:"request"` + Content string `json:"content"` +} + +type CreateSessionHistoryRequest struct { + SessionUUID string `json:"-"` + Messages []SessionHistoryMessage `json:"messages" binding:"required"` +} + +type SessionHistoryMessage struct { + Request bool `json:"request"` // true: request, false: response + Content string `json:"content,omitempty"` // message content +} + +type CreateSessionHistoryResponse struct { + MsgUUIDs []string `json:"msg_uuids"` +} + +type SessionHistoryMessageType string + +const ( + SessionHistoryMessageTypeCreate SessionHistoryMessageType = "create" + SessionHistoryMessageTypeUpdateFeedback SessionHistoryMessageType = "update_feedback" + SessionHistoryMessageTypeRewrite SessionHistoryMessageType = "rewrite" +) + +// SessionHistoryMessageEnvelope is a unified message structure for all session history operations +type SessionHistoryMessageEnvelope struct { + // Common fields + MessageType SessionHistoryMessageType `json:"message_type"` + MsgUUID string `json:"msg_uuid"` + SessionID int64 `json:"session_id"` + SessionUUID string `json:"session_uuid"` + Request bool `json:"request"` // true: request, false: response + + // Create/Rewrite fields + Content string `json:"content,omitempty"` // message content + IsRewritten *bool `json:"is_rewritten,omitempty"` // true: rewritten by user's request + + // UpdateFeedback field + Feedback *AgentSessionHistoryFeedback `json:"feedback,omitempty"` // feedback: none, like, dislike + + // Rewrite field + OriginalMsgUUID string `json:"original_msg_uuid,omitempty"` // original message UUID when rewriting +} + +// AgentInstanceSessionHistory represents a session history +type AgentInstanceSessionHistory struct { + ID int64 `json:"id"` + MsgUUID string `json:"msg_uuid"` + SessionID int64 `json:"session_id"` + SessionUUID string `json:"session_uuid"` + Request bool `json:"request"` + Content string `json:"content"` + Feedback AgentSessionHistoryFeedback `json:"feedback"` + IsRewritten bool `json:"is_rewritten"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type AgentInstanceSessionResponse struct { + SessionUUID string `json:"session_uuid"` + Histories []string `json:"histories"` // list of history contents +} + +type AgentSessionHistoryFeedback string + +const ( + AgentSessionHistoryFeedbackNone AgentSessionHistoryFeedback = "none" + AgentSessionHistoryFeedbackLike AgentSessionHistoryFeedback = "like" + AgentSessionHistoryFeedbackDislike AgentSessionHistoryFeedback = "dislike" +) + +type FeedbackSessionHistoryRequest struct { + MsgUUID string `json:"-"` + Feedback AgentSessionHistoryFeedback `json:"feedback" binding:"required,oneof=none like dislike"` +} + +type RewriteSessionHistoryRequest struct { + OriginalMsgUUID string `json:"-"` + Content string `json:"content" binding:"required"` +} + +type RewriteSessionHistoryResponse struct { + MsgUUID string `json:"msg_uuid"` +} + +type AgentTaskType string + +const ( + AgentTaskTypeFinetuneJob AgentTaskType = "finetuneJob" + AgentTaskTypeInference AgentTaskType = "inference" +) + +func (t AgentTaskType) String() string { + return string(t) +} + +// AgentInfo represents the agent information parsed from JSON string +// e.g. {"type": "code", "id": "123", "request_id": "123"} +type AgentInfo struct { + Type string `json:"type"` // Agent type (e.g., "code", "langflow") + ID string `json:"id"` // Agent content ID + RequestID string `json:"request_id"` // request ID (session uuid) +} + +type AgentInstanceTaskReq struct { + TaskID string `json:"task_id"` // Task ID from argo_workflows + Agent string `json:"agent"` // Agent JSON string, e.g. {"type": "code", "id": "123"} + Type AgentTaskType `json:"type"` // Agent task type (e.g., "finetune", "inference") + Username string `json:"username"` // Username +} + +type AgentStreamEvent struct { + Event string `json:"event"` + Data json.RawMessage `json:"data"` +} + +type CodeAgentRequest struct { + RequestID string `json:"request_id,omitempty"` // Session ID (client-provided) + Query string `json:"query" binding:"required"` // The user's query/question + MaxLoop int `json:"max_loop" binding:"omitempty,min=1"` // Maximum number of execution loops (default: 1) + SearchEngines []string `json:"search_engines"` // List of search engines to use + Stream bool `json:"stream"` // Whether to stream the response + AgentName string `json:"agent_name" binding:"required"` // Name of the agent to use + StreamMode *StreamMode `json:"stream_mode,omitempty"` // Stream configuration + History []CodeAgentRequestMessage `json:"history,omitempty"` // Conversation history +} + +type StreamMode struct { + Mode string `json:"mode" binding:"required"` // Stream mode (e.g., "general") + Token int `json:"token" binding:"min=1"` // Token-based streaming interval + Time int `json:"time" binding:"min=1"` // Time-based streaming interval +} + +type CodeAgentRequestMessage struct { + Role string `json:"role"` + Content string `json:"content"` +} + +type CodeAgentSyncOperation string + +const ( + CodeAgentSyncOperationUpdate CodeAgentSyncOperation = "update" + CodeAgentSyncOperationDelete CodeAgentSyncOperation = "delete" +) + +func (o CodeAgentSyncOperation) String() string { + return string(o) +} + +// AgentInstanceStatusRequest represents a request to get status for an agent instance +type AgentInstanceStatusRequest struct { + Type string `json:"type" binding:"required"` // Agent type (e.g., "code", "langflow") + ContentID string `json:"content_id" binding:"required"` // Content ID of the instance +} + +// AgentInstanceStatusResponse represents the status response for an agent instance +type AgentInstanceStatusResponse struct { + ID int64 `json:"id"` // Instance ID + Type string `json:"type"` // Agent type + ContentID string `json:"content_id"` // Content ID of the instance + Status string `json:"status,omitempty"` // Full status string (e.g., "Running", "Stopped", "Building", etc.) + Error string `json:"error,omitempty"` // Error message if status check failed +} + +// AgentInstanceStatusResult represents the status result for a single instance from adapter +type AgentInstanceStatusResult struct { + Status string + Error error +} + +// AgentTaskStatus represents the unified task status +type AgentTaskStatus string + +const ( + AgentTaskStatusInProgress AgentTaskStatus = "in_progress" + AgentTaskStatusCompleted AgentTaskStatus = "completed" + AgentTaskStatusFailed AgentTaskStatus = "failed" +) + +// AgentTaskFilter represents the filter for listing agent tasks +type AgentTaskFilter struct { + Search string `json:"search,omitempty"` // Search by task name + TaskType AgentTaskType `json:"task_type,omitempty"` // Filter by task type (finetuneJob, inference) + Status AgentTaskStatus `json:"status,omitempty"` // Filter by status (in_progress, completed, failed) + InstanceID *int64 `json:"instance_id,omitempty"` // Filter by instance ID + SessionUUID string `json:"session_uuid,omitempty"` // Filter by session UUID +} + +// AgentTaskListItem represents a task item in the list +type AgentTaskListItem struct { + ID int64 `json:"id"` + TaskID string `json:"task_id"` + TaskName string `json:"task_name"` + TaskType AgentTaskType `json:"task_type"` + TaskStatus AgentTaskStatus `json:"task_status"` + InstanceID int64 `json:"instance_id"` + SessionUUID string `json:"session_uuid"` + UpdatedAt time.Time `json:"updated_at"` + CreatedAt time.Time `json:"created_at"` +} + +// AgentTaskDetail represents detailed task information +type AgentTaskDetail struct { + ID int64 `json:"id"` + TaskID string `json:"task_id"` + TaskName string `json:"task_name"` + TaskDesc string `json:"task_desc"` + TaskType AgentTaskType `json:"task_type"` + Status AgentTaskStatus `json:"status"` + InstanceID int64 `json:"instance_id"` + InstanceType string `json:"instance_type"` + InstanceName string `json:"instance_name"` + SessionUUID string `json:"session_uuid"` + SessionName string `json:"session_name"` + Username string `json:"username"` + Backend string `json:"backend"` // Backend system of the task (argo_workflow, deploy) + Metadata map[string]any `json:"metadata,omitempty"` // Backend-specific fields (argo_workflow or deploy) + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type AgentMonitorRequest struct { + MonitorID string `json:"monitor_id" binding:"required"` + InstanceIDs []int64 `json:"instance_ids" binding:"required"` +} + +// AgentMCPServerConnectionType represents the connection type for MCP servers +type AgentMCPServerConnectionType string + +const ( + AgentMCPServerConnectionTypeJSON AgentMCPServerConnectionType = "json" + AgentMCPServerConnectionTypeSSE AgentMCPServerConnectionType = "sse" +) + +func (t AgentMCPServerConnectionType) String() string { + return string(t) +} + +// AgentMCPServer represents an MCP server configuration for an agent (API layer) +type AgentMCPServer struct { + ID int64 `json:"id"` + UserUUID string `json:"user_uuid"` + Name string `json:"name"` + Description *string `json:"description,omitempty"` + Protocol string `json:"protocol"` // streamable or sse + URL string `json:"url"` + Headers *map[string]any `json:"headers,omitempty"` + Env *map[string]any `json:"env,omitempty"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +// AgentMCPServerFilter represents the filter for listing agent MCP servers +type AgentMCPServerFilter struct { + Search string `json:"search,omitempty"` // Search term for name field + UserUUID string `json:"user_uuid"` // Filter by user UUID (always set, includes built-in servers) + BuiltIn *bool `json:"built_in,omitempty"` // Filter by built-in status + Protocol *string `json:"protocol,omitempty"` // Filter by protocol (streamable, sse) + NeedInstall *bool `json:"need_install,omitempty"` // Filter by need_install status +} + +// CreateAgentMCPServerRequest represents a request to create an agent MCP server +type CreateAgentMCPServerRequest struct { + Name string `json:"name" binding:"required,max=255"` + Description string `json:"description,omitempty"` + Protocol string `json:"protocol" binding:"required,oneof=streamable sse"` + URL string `json:"url" binding:"required"` + Headers map[string]any `json:"headers,omitempty"` + Env map[string]any `json:"env,omitempty"` + UserUUID string `json:"-"` // Will be set from HTTP header +} + +// UpdateAgentMCPServerRequest represents a request to update an agent MCP server +type UpdateAgentMCPServerRequest struct { + Name *string `json:"name,omitempty" binding:"omitempty,max=255"` + Description *string `json:"description,omitempty"` + Protocol *string `json:"protocol,omitempty" binding:"omitempty,oneof=streamable sse"` + URL *string `json:"url,omitempty"` + Headers *map[string]any `json:"headers,omitempty"` + Env *map[string]any `json:"env,omitempty"` +} + +// AgentMCPServerListItem represents an MCP server in list responses +type AgentMCPServerListItem struct { + ID string `json:"id"` // String ID (format: "builtin:{id}" or "user:{id}") + Name string `json:"name"` + Description string `json:"description"` + URL string `json:"url"` + Owner string `json:"owner"` + Avatar string `json:"avatar"` + Protocol string `json:"protocol,omitempty"` + BuiltIn bool `json:"built_in"` + NeedInstall bool `json:"need_install"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +// AgentMCPServerDetail represents a complete MCP server with all configuration details +type AgentMCPServerDetail struct { + ID string `json:"id"` // String ID (format: "builtin:{id}" or "user:{id}") + Name string `json:"name"` + Description string `json:"description"` + UserUUID string `json:"user_uuid"` + Owner string `json:"owner"` + Avatar string `json:"avatar"` + Protocol string `json:"protocol,omitempty"` + URL string `json:"url,omitempty"` + Headers map[string]any `json:"headers,omitempty"` + Env map[string]any `json:"env,omitempty"` + BuiltIn bool `json:"built_in"` + NeedInstall bool `json:"need_install"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type AgentMCPServerIDPrefix string + +const ( + AgentMCPServerIDPrefixBuiltin AgentMCPServerIDPrefix = "builtin:" + AgentMCPServerIDPrefixUser AgentMCPServerIDPrefix = "user:" +) + +func (p AgentMCPServerIDPrefix) String() string { + return string(p) +} diff --git a/common/types/finetune.go b/common/types/finetune.go index a9888f5a7..012b5e909 100644 --- a/common/types/finetune.go +++ b/common/types/finetune.go @@ -45,6 +45,7 @@ type FinetuneReq struct { ShareMode bool `json:"share_mode"` LearningRate float64 `json:"learning_rate"` CustomeArgs string `json:"custom_args"` + Agent string `json:"agent"` } type FinetineGetReq struct { diff --git a/common/types/model.go b/common/types/model.go index 04565a1cb..363cedd68 100644 --- a/common/types/model.go +++ b/common/types/model.go @@ -268,6 +268,7 @@ type ModelRunReq struct { OrderDetailID int64 `json:"order_detail_id"` Entrypoint string `json:"entrypoint"` // model file name for gguf model EngineArgs string `json:"engine_args"` + Agent string `json:"agent"` } var _ SensitiveRequestV2 = (*ModelRunReq)(nil) diff --git a/component/space.go b/component/space.go index d58395a01..c45620c75 100644 --- a/component/space.go +++ b/component/space.go @@ -795,6 +795,8 @@ func (c *spaceComponentImpl) Delete(ctx context.Context, namespace, name, curren } }() + c.syncCodeAgentIfExists(repo.User.UUID, repo.User.Username, repo.Path, types.CodeAgentSyncOperationDelete) + return nil } @@ -887,7 +889,14 @@ func (c *spaceComponentImpl) Deploy(ctx context.Context, namespace, name, curren ClusterID: space.ClusterID, } dr = c.updateDeployRepoBySpace(dr, space) - return c.deployer.Deploy(ctx, dr) + + deployID, err := c.deployer.Deploy(ctx, dr) + if err != nil { + return -1, err + } + + c.syncCodeAgentIfExists(user.UUID, user.Username, space.Repository.Path, types.CodeAgentSyncOperationUpdate) + return deployID, nil } func (c *spaceComponentImpl) Wakeup(ctx context.Context, namespace, name string) error { diff --git a/component/space_ce.go b/component/space_ce.go index a2754cd97..6cd8e8812 100644 --- a/component/space_ce.go +++ b/component/space_ce.go @@ -92,3 +92,6 @@ func (c *spaceComponentImpl) updateDeployRepoByDeploy(repo types.DeployRepo, dep func (c *spaceComponentImpl) addOpWeightToSpaces(ctx context.Context, repoIDs []int64, spaces []*types.Space) { } + +func (c *spaceComponentImpl) syncCodeAgentIfExists(_ string, _ string, _ string, _ types.CodeAgentSyncOperation) { +}