mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-05-17 08:07:28 +08:00
refactor api server around rpc services
This commit is contained in:
63
pkg/rpc/admin.go
Normal file
63
pkg/rpc/admin.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package rpc
|
||||
|
||||
import "context"
|
||||
|
||||
type ConfigService interface {
|
||||
View(context.Context, ConfigViewRequest) (*ConfigViewResponse, *Error)
|
||||
Save(context.Context, ConfigSaveRequest) (*ConfigSaveResponse, *Error)
|
||||
}
|
||||
|
||||
type CronService interface {
|
||||
List(context.Context, ListCronJobsRequest) (*ListCronJobsResponse, *Error)
|
||||
Get(context.Context, GetCronJobRequest) (*GetCronJobResponse, *Error)
|
||||
Mutate(context.Context, MutateCronJobRequest) (*MutateCronJobResponse, *Error)
|
||||
}
|
||||
|
||||
type ConfigViewRequest struct {
|
||||
Mode string `json:"mode,omitempty"`
|
||||
IncludeHotReloadInfo bool `json:"include_hot_reload_info,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigViewResponse struct {
|
||||
Config interface{} `json:"config,omitempty"`
|
||||
RawConfig interface{} `json:"raw_config,omitempty"`
|
||||
PrettyText string `json:"pretty_text,omitempty"`
|
||||
HotReloadFields []string `json:"hot_reload_fields,omitempty"`
|
||||
HotReloadFieldDetails []map[string]interface{} `json:"hot_reload_field_details,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigSaveRequest struct {
|
||||
Mode string `json:"mode,omitempty"`
|
||||
ConfirmRisky bool `json:"confirm_risky,omitempty"`
|
||||
Config map[string]interface{} `json:"config"`
|
||||
}
|
||||
|
||||
type ConfigSaveResponse struct {
|
||||
Saved bool `json:"saved"`
|
||||
RequiresConfirm bool `json:"requires_confirm,omitempty"`
|
||||
ChangedFields []string `json:"changed_fields,omitempty"`
|
||||
Details interface{} `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
type ListCronJobsRequest struct{}
|
||||
|
||||
type ListCronJobsResponse struct {
|
||||
Jobs []interface{} `json:"jobs"`
|
||||
}
|
||||
|
||||
type GetCronJobRequest struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type GetCronJobResponse struct {
|
||||
Job interface{} `json:"job,omitempty"`
|
||||
}
|
||||
|
||||
type MutateCronJobRequest struct {
|
||||
Action string `json:"action"`
|
||||
Args map[string]interface{} `json:"args,omitempty"`
|
||||
}
|
||||
|
||||
type MutateCronJobResponse struct {
|
||||
Result interface{} `json:"result,omitempty"`
|
||||
}
|
||||
37
pkg/rpc/envelope.go
Normal file
37
pkg/rpc/envelope.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
Method string `json:"method"`
|
||||
Params json.RawMessage `json:"params,omitempty"`
|
||||
RequestID string `json:"request_id,omitempty"`
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
OK bool `json:"ok"`
|
||||
Result interface{} `json:"result,omitempty"`
|
||||
Error *Error `json:"error,omitempty"`
|
||||
RequestID string `json:"request_id,omitempty"`
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Details interface{} `json:"details,omitempty"`
|
||||
Retryable bool `json:"retryable,omitempty"`
|
||||
}
|
||||
|
||||
func (r Request) NormalizedMethod() string {
|
||||
return strings.ToLower(strings.TrimSpace(r.Method))
|
||||
}
|
||||
|
||||
func DecodeParams(raw json.RawMessage, target interface{}) error {
|
||||
if len(raw) == 0 || string(raw) == "null" {
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(raw, target)
|
||||
}
|
||||
192
pkg/rpc/node.go
Normal file
192
pkg/rpc/node.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/YspCoder/clawgo/pkg/nodes"
|
||||
)
|
||||
|
||||
type NodeService interface {
|
||||
Register(context.Context, RegisterNodeRequest) (*RegisterNodeResponse, *Error)
|
||||
Heartbeat(context.Context, HeartbeatNodeRequest) (*HeartbeatNodeResponse, *Error)
|
||||
Dispatch(context.Context, DispatchNodeRequest) (*DispatchNodeResponse, *Error)
|
||||
ListArtifacts(context.Context, ListNodeArtifactsRequest) (*ListNodeArtifactsResponse, *Error)
|
||||
GetArtifact(context.Context, GetNodeArtifactRequest) (*GetNodeArtifactResponse, *Error)
|
||||
DeleteArtifact(context.Context, DeleteNodeArtifactRequest) (*DeleteNodeArtifactResponse, *Error)
|
||||
PruneArtifacts(context.Context, PruneNodeArtifactsRequest) (*PruneNodeArtifactsResponse, *Error)
|
||||
}
|
||||
|
||||
type RegisterNodeRequest struct {
|
||||
Node nodes.NodeInfo `json:"node"`
|
||||
}
|
||||
|
||||
type RegisterNodeResponse struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type HeartbeatNodeRequest struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type HeartbeatNodeResponse struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type DispatchNodeRequest struct {
|
||||
Node string `json:"node"`
|
||||
Action string `json:"action"`
|
||||
Mode string `json:"mode,omitempty"`
|
||||
Task string `json:"task,omitempty"`
|
||||
Model string `json:"model,omitempty"`
|
||||
Args map[string]interface{} `json:"args,omitempty"`
|
||||
}
|
||||
|
||||
type DispatchNodeResponse struct {
|
||||
Result nodes.Response `json:"result"`
|
||||
}
|
||||
|
||||
type ArtifactSummary struct {
|
||||
Data map[string]interface{} `json:"data"`
|
||||
}
|
||||
|
||||
type ArtifactContentRef struct {
|
||||
Data map[string]interface{} `json:"data"`
|
||||
}
|
||||
|
||||
type ArtifactDeleteResult struct {
|
||||
ID string `json:"id"`
|
||||
DeletedFile bool `json:"deleted_file"`
|
||||
DeletedAudit bool `json:"deleted_audit"`
|
||||
}
|
||||
|
||||
type ArtifactPruneResult struct {
|
||||
Pruned int `json:"pruned"`
|
||||
DeletedFiles int `json:"deleted_files"`
|
||||
Kept int `json:"kept"`
|
||||
}
|
||||
|
||||
type ListNodeArtifactsRequest struct {
|
||||
Node string `json:"node,omitempty"`
|
||||
Action string `json:"action,omitempty"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Limit int `json:"limit,omitempty"`
|
||||
}
|
||||
|
||||
type ListNodeArtifactsResponse struct {
|
||||
Items []map[string]interface{} `json:"items"`
|
||||
ArtifactRetention map[string]interface{} `json:"artifact_retention,omitempty"`
|
||||
}
|
||||
|
||||
type GetNodeArtifactRequest struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type GetNodeArtifactResponse struct {
|
||||
Found bool `json:"found"`
|
||||
Artifact map[string]interface{} `json:"artifact,omitempty"`
|
||||
}
|
||||
|
||||
type DeleteNodeArtifactRequest struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type DeleteNodeArtifactResponse struct {
|
||||
ArtifactDeleteResult
|
||||
}
|
||||
|
||||
type PruneNodeArtifactsRequest struct {
|
||||
Node string `json:"node,omitempty"`
|
||||
Action string `json:"action,omitempty"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
KeepLatest int `json:"keep_latest,omitempty"`
|
||||
Limit int `json:"limit,omitempty"`
|
||||
}
|
||||
|
||||
type PruneNodeArtifactsResponse struct {
|
||||
ArtifactPruneResult
|
||||
}
|
||||
|
||||
type ProviderService interface {
|
||||
ListModels(context.Context, ListProviderModelsRequest) (*ListProviderModelsResponse, *Error)
|
||||
UpdateModels(context.Context, UpdateProviderModelsRequest) (*UpdateProviderModelsResponse, *Error)
|
||||
Chat(context.Context, ProviderChatRequest) (*ProviderChatResponse, *Error)
|
||||
CountTokens(context.Context, ProviderCountTokensRequest) (*ProviderCountTokensResponse, *Error)
|
||||
RuntimeView(context.Context, ProviderRuntimeViewRequest) (*ProviderRuntimeViewResponse, *Error)
|
||||
RuntimeAction(context.Context, ProviderRuntimeActionRequest) (*ProviderRuntimeActionResponse, *Error)
|
||||
}
|
||||
|
||||
type ListProviderModelsRequest struct {
|
||||
Provider string `json:"provider"`
|
||||
}
|
||||
|
||||
type ListProviderModelsResponse struct {
|
||||
Provider string `json:"provider"`
|
||||
Models []string `json:"models,omitempty"`
|
||||
Default string `json:"default_model,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateProviderModelsRequest struct {
|
||||
Provider string `json:"provider"`
|
||||
Model string `json:"model,omitempty"`
|
||||
Models []string `json:"models,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateProviderModelsResponse struct {
|
||||
Provider string `json:"provider"`
|
||||
Models []string `json:"models,omitempty"`
|
||||
}
|
||||
|
||||
type ProviderChatRequest struct {
|
||||
Provider string `json:"provider"`
|
||||
Model string `json:"model,omitempty"`
|
||||
Messages []map[string]interface{} `json:"messages"`
|
||||
Tools []map[string]interface{} `json:"tools,omitempty"`
|
||||
Options map[string]interface{} `json:"options,omitempty"`
|
||||
}
|
||||
|
||||
type ProviderChatResponse struct {
|
||||
Content string `json:"content,omitempty"`
|
||||
ToolCalls []map[string]interface{} `json:"tool_calls,omitempty"`
|
||||
FinishReason string `json:"finish_reason,omitempty"`
|
||||
Usage map[string]interface{} `json:"usage,omitempty"`
|
||||
}
|
||||
|
||||
type ProviderCountTokensRequest struct {
|
||||
Provider string `json:"provider"`
|
||||
Model string `json:"model,omitempty"`
|
||||
Messages []map[string]interface{} `json:"messages"`
|
||||
Tools []map[string]interface{} `json:"tools,omitempty"`
|
||||
Options map[string]interface{} `json:"options,omitempty"`
|
||||
}
|
||||
|
||||
type ProviderCountTokensResponse struct {
|
||||
Usage map[string]interface{} `json:"usage,omitempty"`
|
||||
}
|
||||
|
||||
type ProviderRuntimeViewRequest struct {
|
||||
Provider string `json:"provider,omitempty"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
Target string `json:"target,omitempty"`
|
||||
Sort string `json:"sort,omitempty"`
|
||||
ChangesOnly bool `json:"changes_only,omitempty"`
|
||||
WindowSec int `json:"window_sec,omitempty"`
|
||||
Limit int `json:"limit,omitempty"`
|
||||
Cursor int `json:"cursor,omitempty"`
|
||||
HealthBelow int `json:"health_below,omitempty"`
|
||||
CooldownUntilBeforeSec int `json:"cooldown_until_before_sec,omitempty"`
|
||||
}
|
||||
|
||||
type ProviderRuntimeViewResponse struct {
|
||||
View map[string]interface{} `json:"view"`
|
||||
}
|
||||
|
||||
type ProviderRuntimeActionRequest struct {
|
||||
Provider string `json:"provider,omitempty"`
|
||||
Action string `json:"action"`
|
||||
OnlyExpiring bool `json:"only_expiring,omitempty"`
|
||||
}
|
||||
|
||||
type ProviderRuntimeActionResponse struct {
|
||||
Result map[string]interface{} `json:"result"`
|
||||
}
|
||||
56
pkg/rpc/registry.go
Normal file
56
pkg/rpc/registry.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type MethodHandler func(context.Context, json.RawMessage) (interface{}, *Error)
|
||||
|
||||
type Registry struct {
|
||||
methods map[string]MethodHandler
|
||||
}
|
||||
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{methods: map[string]MethodHandler{}}
|
||||
}
|
||||
|
||||
func (r *Registry) Register(method string, handler MethodHandler) {
|
||||
if r == nil || handler == nil {
|
||||
return
|
||||
}
|
||||
method = strings.ToLower(strings.TrimSpace(method))
|
||||
if method == "" {
|
||||
return
|
||||
}
|
||||
r.methods[method] = handler
|
||||
}
|
||||
|
||||
func (r *Registry) Handle(ctx context.Context, req Request) (interface{}, *Error) {
|
||||
if r == nil {
|
||||
return nil, &Error{Code: "internal", Message: "rpc registry unavailable"}
|
||||
}
|
||||
handler := r.methods[req.NormalizedMethod()]
|
||||
if handler == nil {
|
||||
return nil, &Error{
|
||||
Code: "invalid_argument",
|
||||
Message: "unknown method",
|
||||
Details: map[string]interface{}{"method": strings.TrimSpace(req.Method)},
|
||||
}
|
||||
}
|
||||
return handler(ctx, req.Params)
|
||||
}
|
||||
|
||||
func RegisterJSON[T any](r *Registry, method string, handler func(context.Context, T) (interface{}, *Error)) {
|
||||
if r == nil || handler == nil {
|
||||
return
|
||||
}
|
||||
r.Register(method, func(ctx context.Context, raw json.RawMessage) (interface{}, *Error) {
|
||||
var params T
|
||||
if err := DecodeParams(raw, ¶ms); err != nil {
|
||||
return nil, &Error{Code: "invalid_argument", Message: err.Error()}
|
||||
}
|
||||
return handler(ctx, params)
|
||||
})
|
||||
}
|
||||
87
pkg/rpc/subagent.go
Normal file
87
pkg/rpc/subagent.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/YspCoder/clawgo/pkg/tools"
|
||||
)
|
||||
|
||||
type SubagentService interface {
|
||||
List(context.Context, ListSubagentsRequest) (*ListSubagentsResponse, *Error)
|
||||
Snapshot(context.Context, SnapshotRequest) (*SnapshotResponse, *Error)
|
||||
Get(context.Context, GetSubagentRequest) (*GetSubagentResponse, *Error)
|
||||
Spawn(context.Context, SpawnSubagentRequest) (*SpawnSubagentResponse, *Error)
|
||||
DispatchAndWait(context.Context, DispatchAndWaitRequest) (*DispatchAndWaitResponse, *Error)
|
||||
Registry(context.Context, RegistryRequest) (*RegistryResponse, *Error)
|
||||
}
|
||||
|
||||
type ListSubagentsRequest struct{}
|
||||
|
||||
type ListSubagentsResponse struct {
|
||||
Items []*tools.SubagentTask `json:"items"`
|
||||
}
|
||||
|
||||
type SnapshotRequest struct {
|
||||
Limit int `json:"limit,omitempty"`
|
||||
}
|
||||
|
||||
type SnapshotResponse struct {
|
||||
Snapshot tools.RuntimeSnapshot `json:"snapshot"`
|
||||
}
|
||||
|
||||
type GetSubagentRequest struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type GetSubagentResponse struct {
|
||||
Found bool `json:"found"`
|
||||
Task *tools.SubagentTask `json:"task,omitempty"`
|
||||
}
|
||||
|
||||
type SpawnSubagentRequest struct {
|
||||
Task string `json:"task"`
|
||||
Label string `json:"label,omitempty"`
|
||||
Role string `json:"role,omitempty"`
|
||||
AgentID string `json:"agent_id,omitempty"`
|
||||
MaxRetries int `json:"max_retries,omitempty"`
|
||||
RetryBackoffMS int `json:"retry_backoff_ms,omitempty"`
|
||||
TimeoutSec int `json:"timeout_sec,omitempty"`
|
||||
MaxTaskChars int `json:"max_task_chars,omitempty"`
|
||||
MaxResultChars int `json:"max_result_chars,omitempty"`
|
||||
Channel string `json:"channel,omitempty"`
|
||||
ChatID string `json:"chat_id,omitempty"`
|
||||
}
|
||||
|
||||
type SpawnSubagentResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type DispatchAndWaitRequest struct {
|
||||
Task string `json:"task"`
|
||||
Label string `json:"label,omitempty"`
|
||||
Role string `json:"role,omitempty"`
|
||||
AgentID string `json:"agent_id,omitempty"`
|
||||
ThreadID string `json:"thread_id,omitempty"`
|
||||
CorrelationID string `json:"correlation_id,omitempty"`
|
||||
ParentRunID string `json:"parent_run_id,omitempty"`
|
||||
Channel string `json:"channel,omitempty"`
|
||||
ChatID string `json:"chat_id,omitempty"`
|
||||
MaxRetries int `json:"max_retries,omitempty"`
|
||||
RetryBackoffMS int `json:"retry_backoff_ms,omitempty"`
|
||||
TimeoutSec int `json:"timeout_sec,omitempty"`
|
||||
MaxTaskChars int `json:"max_task_chars,omitempty"`
|
||||
MaxResultChars int `json:"max_result_chars,omitempty"`
|
||||
WaitTimeoutSec int `json:"wait_timeout_sec,omitempty"`
|
||||
}
|
||||
|
||||
type DispatchAndWaitResponse struct {
|
||||
Task *tools.SubagentTask `json:"task,omitempty"`
|
||||
Reply *tools.RouterReply `json:"reply,omitempty"`
|
||||
Merged string `json:"merged,omitempty"`
|
||||
}
|
||||
|
||||
type RegistryRequest struct{}
|
||||
|
||||
type RegistryResponse struct {
|
||||
Items []map[string]interface{} `json:"items"`
|
||||
}
|
||||
50
pkg/rpc/workspace.go
Normal file
50
pkg/rpc/workspace.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package rpc
|
||||
|
||||
import "context"
|
||||
|
||||
type WorkspaceService interface {
|
||||
ListFiles(context.Context, ListWorkspaceFilesRequest) (*ListWorkspaceFilesResponse, *Error)
|
||||
ReadFile(context.Context, ReadWorkspaceFileRequest) (*ReadWorkspaceFileResponse, *Error)
|
||||
WriteFile(context.Context, WriteWorkspaceFileRequest) (*WriteWorkspaceFileResponse, *Error)
|
||||
DeleteFile(context.Context, DeleteWorkspaceFileRequest) (*DeleteWorkspaceFileResponse, *Error)
|
||||
}
|
||||
|
||||
type ListWorkspaceFilesRequest struct {
|
||||
Scope string `json:"scope,omitempty"`
|
||||
}
|
||||
|
||||
type ListWorkspaceFilesResponse struct {
|
||||
Files []string `json:"files"`
|
||||
}
|
||||
|
||||
type ReadWorkspaceFileRequest struct {
|
||||
Scope string `json:"scope,omitempty"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
type ReadWorkspaceFileResponse struct {
|
||||
Path string `json:"path,omitempty"`
|
||||
Found bool `json:"found,omitempty"`
|
||||
Content string `json:"content,omitempty"`
|
||||
}
|
||||
|
||||
type WriteWorkspaceFileRequest struct {
|
||||
Scope string `json:"scope,omitempty"`
|
||||
Path string `json:"path"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type WriteWorkspaceFileResponse struct {
|
||||
Path string `json:"path,omitempty"`
|
||||
Saved bool `json:"saved,omitempty"`
|
||||
}
|
||||
|
||||
type DeleteWorkspaceFileRequest struct {
|
||||
Scope string `json:"scope,omitempty"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
type DeleteWorkspaceFileResponse struct {
|
||||
Path string `json:"path,omitempty"`
|
||||
Deleted bool `json:"deleted"`
|
||||
}
|
||||
Reference in New Issue
Block a user