Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
290 lines
12 KiB
Go
290 lines
12 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
|
|
tuttigenerated "github.com/tutti-os/tutti/services/tuttid/api/generated"
|
|
"github.com/tutti-os/tutti/services/tuttid/apierrors"
|
|
workspaceagentbiz "github.com/tutti-os/tutti/services/tuttid/biz/workspaceagent"
|
|
workspacedata "github.com/tutti-os/tutti/services/tuttid/data/workspace"
|
|
workspaceagentservice "github.com/tutti-os/tutti/services/tuttid/service/workspaceagent"
|
|
)
|
|
|
|
type WorkspaceAgentService interface {
|
|
List(context.Context, string) ([]workspaceagentbiz.View, error)
|
|
Get(context.Context, string, string) (workspaceagentbiz.View, error)
|
|
Create(context.Context, workspaceagentservice.PutInput) (workspaceagentbiz.View, error)
|
|
Update(context.Context, workspaceagentservice.PutInput) (workspaceagentbiz.View, error)
|
|
Delete(context.Context, string, string) error
|
|
}
|
|
|
|
func (api DaemonAPI) ListWorkspaceAgents(ctx context.Context, request tuttigenerated.ListWorkspaceAgentsRequestObject) (tuttigenerated.ListWorkspaceAgentsResponseObject, error) {
|
|
if api.WorkspaceAgentService == nil {
|
|
return tuttigenerated.ListWorkspaceAgents503JSONResponse{
|
|
ServiceUnavailableErrorJSONResponse: workspaceAgentServiceUnavailable(),
|
|
}, nil
|
|
}
|
|
views, err := api.WorkspaceAgentService.List(ctx, request.WorkspaceID)
|
|
if err != nil {
|
|
if errors.Is(err, workspaceagentservice.ErrInvalidInput) {
|
|
return tuttigenerated.ListWorkspaceAgents400JSONResponse{
|
|
InvalidRequestErrorJSONResponse: invalidWorkspaceAgentRequest(err),
|
|
}, nil
|
|
}
|
|
return tuttigenerated.ListWorkspaceAgents502JSONResponse{
|
|
WorkspaceOperationErrorJSONResponse: workspaceOperationError(apierrors.WorkspaceOperationFailed(apierrors.WithCause(err))),
|
|
}, nil
|
|
}
|
|
response := tuttigenerated.ListWorkspaceAgentsResponse{Agents: make([]tuttigenerated.WorkspaceAgent, 0, len(views))}
|
|
for _, view := range views {
|
|
response.Agents = append(response.Agents, generatedWorkspaceAgent(view))
|
|
}
|
|
return tuttigenerated.ListWorkspaceAgents200JSONResponse(response), nil
|
|
}
|
|
|
|
func (api DaemonAPI) GetWorkspaceAgent(ctx context.Context, request tuttigenerated.GetWorkspaceAgentRequestObject) (tuttigenerated.GetWorkspaceAgentResponseObject, error) {
|
|
if api.WorkspaceAgentService == nil {
|
|
return tuttigenerated.GetWorkspaceAgent503JSONResponse{
|
|
ServiceUnavailableErrorJSONResponse: workspaceAgentServiceUnavailable(),
|
|
}, nil
|
|
}
|
|
view, err := api.WorkspaceAgentService.Get(ctx, request.WorkspaceID, request.WorkspaceAgentID)
|
|
if err != nil {
|
|
switch {
|
|
case errors.Is(err, workspacedata.ErrWorkspaceAgentNotFound):
|
|
return tuttigenerated.GetWorkspaceAgent404JSONResponse{
|
|
WorkspaceNotFoundErrorJSONResponse: workspaceAgentNotFoundError(),
|
|
}, nil
|
|
case errors.Is(err, workspaceagentservice.ErrInvalidInput):
|
|
return tuttigenerated.GetWorkspaceAgent400JSONResponse{
|
|
InvalidRequestErrorJSONResponse: invalidWorkspaceAgentRequest(err),
|
|
}, nil
|
|
}
|
|
return tuttigenerated.GetWorkspaceAgent502JSONResponse{
|
|
WorkspaceOperationErrorJSONResponse: workspaceOperationError(apierrors.WorkspaceOperationFailed(apierrors.WithCause(err))),
|
|
}, nil
|
|
}
|
|
return tuttigenerated.GetWorkspaceAgent200JSONResponse(generatedWorkspaceAgent(view)), nil
|
|
}
|
|
|
|
func (api DaemonAPI) CreateWorkspaceAgent(ctx context.Context, request tuttigenerated.CreateWorkspaceAgentRequestObject) (tuttigenerated.CreateWorkspaceAgentResponseObject, error) {
|
|
if api.WorkspaceAgentService == nil {
|
|
return tuttigenerated.CreateWorkspaceAgent503JSONResponse{
|
|
ServiceUnavailableErrorJSONResponse: workspaceAgentServiceUnavailable(),
|
|
}, nil
|
|
}
|
|
if request.Body == nil {
|
|
return tuttigenerated.CreateWorkspaceAgent400JSONResponse{
|
|
InvalidRequestErrorJSONResponse: invalidRequestError(apierrors.EmptyBody()),
|
|
}, nil
|
|
}
|
|
view, err := api.WorkspaceAgentService.Create(ctx, workspaceAgentPutInput(request.WorkspaceID, "", *request.Body))
|
|
if err != nil {
|
|
switch {
|
|
case errors.Is(err, workspacedata.ErrWorkspaceNotFound),
|
|
errors.Is(err, workspacedata.ErrAgentTargetNotFound),
|
|
errors.Is(err, workspacedata.ErrModelPlanNotFound):
|
|
return tuttigenerated.CreateWorkspaceAgent404JSONResponse{
|
|
WorkspaceNotFoundErrorJSONResponse: workspaceAgentDependencyNotFoundError(err),
|
|
}, nil
|
|
case isInvalidWorkspaceAgentError(err):
|
|
return tuttigenerated.CreateWorkspaceAgent400JSONResponse{
|
|
InvalidRequestErrorJSONResponse: invalidWorkspaceAgentRequest(err),
|
|
}, nil
|
|
}
|
|
return tuttigenerated.CreateWorkspaceAgent502JSONResponse{
|
|
WorkspaceOperationErrorJSONResponse: workspaceOperationError(apierrors.WorkspaceOperationFailed(apierrors.WithCause(err))),
|
|
}, nil
|
|
}
|
|
return tuttigenerated.CreateWorkspaceAgent201JSONResponse(generatedWorkspaceAgent(view)), nil
|
|
}
|
|
|
|
func (api DaemonAPI) UpdateWorkspaceAgent(ctx context.Context, request tuttigenerated.UpdateWorkspaceAgentRequestObject) (tuttigenerated.UpdateWorkspaceAgentResponseObject, error) {
|
|
if api.WorkspaceAgentService == nil {
|
|
return tuttigenerated.UpdateWorkspaceAgent503JSONResponse{
|
|
ServiceUnavailableErrorJSONResponse: workspaceAgentServiceUnavailable(),
|
|
}, nil
|
|
}
|
|
if request.Body == nil {
|
|
return tuttigenerated.UpdateWorkspaceAgent400JSONResponse{
|
|
InvalidRequestErrorJSONResponse: invalidRequestError(apierrors.EmptyBody()),
|
|
}, nil
|
|
}
|
|
view, err := api.WorkspaceAgentService.Update(ctx, workspaceAgentPutInput(request.WorkspaceID, request.WorkspaceAgentID, *request.Body))
|
|
if err != nil {
|
|
switch {
|
|
case errors.Is(err, workspacedata.ErrWorkspaceAgentNotFound):
|
|
return tuttigenerated.UpdateWorkspaceAgent404JSONResponse{
|
|
WorkspaceNotFoundErrorJSONResponse: workspaceAgentNotFoundError(),
|
|
}, nil
|
|
case errors.Is(err, workspacedata.ErrWorkspaceNotFound),
|
|
errors.Is(err, workspacedata.ErrAgentTargetNotFound),
|
|
errors.Is(err, workspacedata.ErrModelPlanNotFound):
|
|
return tuttigenerated.UpdateWorkspaceAgent404JSONResponse{
|
|
WorkspaceNotFoundErrorJSONResponse: workspaceAgentDependencyNotFoundError(err),
|
|
}, nil
|
|
case isInvalidWorkspaceAgentError(err):
|
|
return tuttigenerated.UpdateWorkspaceAgent400JSONResponse{
|
|
InvalidRequestErrorJSONResponse: invalidWorkspaceAgentRequest(err),
|
|
}, nil
|
|
}
|
|
return tuttigenerated.UpdateWorkspaceAgent502JSONResponse{
|
|
WorkspaceOperationErrorJSONResponse: workspaceOperationError(apierrors.WorkspaceOperationFailed(apierrors.WithCause(err))),
|
|
}, nil
|
|
}
|
|
return tuttigenerated.UpdateWorkspaceAgent200JSONResponse(generatedWorkspaceAgent(view)), nil
|
|
}
|
|
|
|
func (api DaemonAPI) DeleteWorkspaceAgent(ctx context.Context, request tuttigenerated.DeleteWorkspaceAgentRequestObject) (tuttigenerated.DeleteWorkspaceAgentResponseObject, error) {
|
|
if api.WorkspaceAgentService == nil {
|
|
return tuttigenerated.DeleteWorkspaceAgent503JSONResponse{
|
|
ServiceUnavailableErrorJSONResponse: workspaceAgentServiceUnavailable(),
|
|
}, nil
|
|
}
|
|
if err := api.WorkspaceAgentService.Delete(ctx, request.WorkspaceID, request.WorkspaceAgentID); err != nil {
|
|
switch {
|
|
case errors.Is(err, workspacedata.ErrWorkspaceAgentNotFound):
|
|
return tuttigenerated.DeleteWorkspaceAgent404JSONResponse{
|
|
WorkspaceNotFoundErrorJSONResponse: workspaceAgentNotFoundError(),
|
|
}, nil
|
|
case errors.Is(err, workspaceagentservice.ErrInvalidInput):
|
|
return tuttigenerated.DeleteWorkspaceAgent400JSONResponse{
|
|
InvalidRequestErrorJSONResponse: invalidWorkspaceAgentRequest(err),
|
|
}, nil
|
|
}
|
|
return tuttigenerated.DeleteWorkspaceAgent502JSONResponse{
|
|
WorkspaceOperationErrorJSONResponse: workspaceOperationError(apierrors.WorkspaceOperationFailed(apierrors.WithCause(err))),
|
|
}, nil
|
|
}
|
|
return tuttigenerated.DeleteWorkspaceAgent200JSONResponse{
|
|
WorkspaceAgentId: request.WorkspaceAgentID,
|
|
}, nil
|
|
}
|
|
|
|
func workspaceAgentPutInput(workspaceID string, agentID string, body tuttigenerated.PutWorkspaceAgentRequest) workspaceagentservice.PutInput {
|
|
return workspaceagentservice.PutInput{
|
|
WorkspaceID: workspaceID,
|
|
AgentID: agentID,
|
|
Name: body.Name,
|
|
Description: body.Description,
|
|
HarnessAgentTargetID: body.HarnessAgentTargetId,
|
|
ModelPlanID: stringValue(body.ModelPlanId),
|
|
DefaultModel: stringValue(body.DefaultModel),
|
|
ModelFallbacks: bizWorkspaceAgentModelRefs(body.ModelFallbacks),
|
|
Instructions: body.Instructions,
|
|
CallConditions: append([]string(nil), body.CallConditions...),
|
|
CapabilitiesExplicit: body.CapabilitiesExplicit,
|
|
Skills: append([]string(nil), body.Skills...),
|
|
Tools: append([]string(nil), body.Tools...),
|
|
}
|
|
}
|
|
|
|
func generatedWorkspaceAgent(view workspaceagentbiz.View) tuttigenerated.WorkspaceAgent {
|
|
agent := view.Agent
|
|
result := tuttigenerated.WorkspaceAgent{
|
|
Id: agent.ID,
|
|
AgentTargetId: agent.ID,
|
|
WorkspaceId: agent.WorkspaceID,
|
|
Name: agent.Name,
|
|
Description: agent.Description,
|
|
Harness: tuttigenerated.WorkspaceAgentHarness{
|
|
AgentTargetId: view.Harness.AgentTargetID,
|
|
Available: view.Harness.Available,
|
|
},
|
|
Instructions: agent.Instructions,
|
|
CallConditions: append([]string{}, agent.CallConditions...),
|
|
CapabilitiesExplicit: agent.CapabilitiesExplicit,
|
|
Skills: append([]string{}, agent.Skills...),
|
|
Tools: append([]string{}, agent.Tools...),
|
|
ModelFallbacks: generatedWorkspaceAgentModelRefs(agent.ModelFallbacks),
|
|
Source: tuttigenerated.WorkspaceAgentSource(agent.Source),
|
|
Revision: agent.Revision,
|
|
CreatedAt: agent.CreatedAt,
|
|
UpdatedAt: agent.UpdatedAt,
|
|
}
|
|
if agent.ModelPlanID != "" {
|
|
result.ModelPlanId = stringPointer(agent.ModelPlanID)
|
|
}
|
|
if agent.DefaultModel != "" {
|
|
result.DefaultModel = stringPointer(agent.DefaultModel)
|
|
}
|
|
if view.Harness.Available {
|
|
provider := tuttigenerated.AgentTargetProvider(view.Harness.Provider)
|
|
result.Harness.Provider = &provider
|
|
result.Harness.Name = stringPointer(view.Harness.Name)
|
|
result.Harness.IconKey = stringPointer(view.Harness.IconKey)
|
|
result.Harness.Enabled = boolPointer(view.Harness.Enabled)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func bizWorkspaceAgentModelRefs(values *[]tuttigenerated.WorkspaceAgentModelRef) []workspaceagentbiz.ModelRef {
|
|
if values == nil {
|
|
return []workspaceagentbiz.ModelRef{}
|
|
}
|
|
result := make([]workspaceagentbiz.ModelRef, 0, len(*values))
|
|
for _, value := range *values {
|
|
result = append(result, workspaceagentbiz.ModelRef{
|
|
ModelPlanID: value.ModelPlanId,
|
|
Model: stringValue(value.Model),
|
|
})
|
|
}
|
|
return result
|
|
}
|
|
|
|
func generatedWorkspaceAgentModelRefs(values []workspaceagentbiz.ModelRef) []tuttigenerated.WorkspaceAgentModelRef {
|
|
result := make([]tuttigenerated.WorkspaceAgentModelRef, 0, len(values))
|
|
for _, value := range values {
|
|
generated := tuttigenerated.WorkspaceAgentModelRef{ModelPlanId: value.ModelPlanID}
|
|
if value.Model != "" {
|
|
generated.Model = stringPointer(value.Model)
|
|
}
|
|
result = append(result, generated)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func isInvalidWorkspaceAgentError(err error) bool {
|
|
return errors.Is(err, workspaceagentservice.ErrInvalidInput) ||
|
|
errors.Is(err, workspaceagentservice.ErrModelNotInPlan) ||
|
|
errors.Is(err, workspaceagentservice.ErrHarnessPlanProtocolMismatch)
|
|
}
|
|
|
|
func invalidWorkspaceAgentRequest(err error) tuttigenerated.InvalidRequestErrorJSONResponse {
|
|
return invalidRequestError(apierrors.InvalidRequest(
|
|
"invalid_workspace_agent",
|
|
apierrors.WithDeveloperMessage(err.Error()),
|
|
))
|
|
}
|
|
|
|
func workspaceAgentServiceUnavailable() tuttigenerated.ServiceUnavailableErrorJSONResponse {
|
|
return serviceUnavailableError(apierrors.ServiceUnavailable(
|
|
"workspace_agent_service_unavailable",
|
|
apierrors.WithDeveloperMessage("workspace agent service is unavailable"),
|
|
))
|
|
}
|
|
|
|
func workspaceAgentNotFoundError() tuttigenerated.WorkspaceNotFoundErrorJSONResponse {
|
|
return tuttigenerated.WorkspaceNotFoundErrorJSONResponse(protocolErrorResponse(
|
|
apierrors.New(
|
|
http.StatusNotFound,
|
|
tuttigenerated.WorkspaceAgentNotFound,
|
|
"workspace_agent_not_found",
|
|
apierrors.WithDeveloperMessage("workspace agent not found"),
|
|
),
|
|
))
|
|
}
|
|
|
|
func workspaceAgentDependencyNotFoundError(err error) tuttigenerated.WorkspaceNotFoundErrorJSONResponse {
|
|
return tuttigenerated.WorkspaceNotFoundErrorJSONResponse(protocolErrorResponse(
|
|
apierrors.New(
|
|
http.StatusNotFound,
|
|
tuttigenerated.WorkspaceAgentNotFound,
|
|
"workspace_agent_dependency_not_found",
|
|
apierrors.WithCause(err),
|
|
),
|
|
))
|
|
}
|