package app import ( "context" "errors" "time" ) // GenerationProvider is the port the domain uses to call the underlying model executor. type GenerationProvider interface { ListModels(ctx context.Context) ([]ModelDescriptor, error) Generate(ctx context.Context, req GenerationRequest) (GenerationResponse, error) } // Service orchestrates requests across providers and enforces domain rules. type Service struct { provider GenerationProvider } func NewService(provider GenerationProvider) *Service { return &Service{provider: provider} } func (s *Service) Models(ctx context.Context) ([]ModelDescriptor, error) { ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() return s.provider.ListModels(ctx) } func (s *Service) Generate(ctx context.Context, req GenerationRequest) (GenerationResponse, error) { if req.ModelID == "" { return GenerationResponse{}, errors.New("model id is required") } if req.Prompt == "" && len(req.References) == 0 { return GenerationResponse{}, errors.New("prompt or references must be provided") } ctx, cancel := context.WithTimeout(ctx, 30*time.Second) defer cancel() return s.provider.Generate(ctx, req) } // ModelDescriptor describes a generative model exposed via the API. type ModelDescriptor struct { ID string `json:"id"` Label string `json:"label"` Provider string `json:"provider"` Modalities []string `json:"modalities"` Capabilities []string `json:"capabilities"` } // GenerationRequest carries a generation task requested by the UI. type GenerationRequest struct { ModelID string `json:"modelId"` Prompt string `json:"prompt"` Negative string `json:"negativePrompt,omitempty"` Aspect string `json:"aspect,omitempty"` References []string `json:"references,omitempty"` UserLocale string `json:"userLocale,omitempty"` } // GenerationResponse summarises the async job triggered by the provider. type GenerationResponse struct { ID string `json:"id"` ModelID string `json:"modelId"` Status string `json:"status"` QueuedAt time.Time `json:"queuedAt"` ExpiresAt time.Time `json:"expiresAt"` }