package configuration import ( "context" "fmt" "go.maximumdirect.net/promptkit/internal/backend" "go.maximumdirect.net/promptkit/internal/domain" "go.maximumdirect.net/promptkit/internal/profile" ) // ProfileResource is the resolved profile before execution completion. // Its fields borrow immutable configuration captured by the adapter. type ProfileResource struct { ID string Backend, Endpoint, Model *string Temperature, TopP *float64 MaxTokens, TimeoutSeconds *int ServiceTier, ReasoningEffort *string APIKeyEnv *string APIKeyRequired *bool ExtraParams map[string]any } // BackendResource is the resolved backend and its configured diagnostics. // Its fields borrow immutable configuration captured by the adapter. type BackendResource struct { ID, Endpoint string APIKeyEnv *string ConcurrencyLimit, QueueCapacity *int ExtraParams map[string]any SmokeTests *domain.BackendSmokeTestConfig } // PromptResource is the resolved prompt with already captured message text. type PromptResource struct { ID, Hash string DefaultProfile, Description, SessionID *string Inputs *[]PromptInputResource Messages []PromptMessageResource Output PromptOutputResource } type PromptInputResource struct { Name string Required *bool ContentType, Description *string } type PromptMessageResource struct { Role, Content string CacheControl *PromptCacheControlResource } type PromptCacheControlResource struct { Type domain.CacheControlType TTL *string } type PromptOutputResource struct { Format domain.OutputFormat ValidationMode domain.ValidationMode SchemaPath *string RepairAttempts *int } func (state *loaded) InspectPrompt(ctx context.Context, id string) (*PromptResource, error) { if err := ctx.Err(); err != nil { return nil, err } entry, err := state.lookupPromptEntry(id) if err != nil { return nil, promptLookupError(err) } raw := entry.resource result := &PromptResource{ID: id, Hash: entry.hash, DefaultProfile: raw.DefaultProfile, Description: raw.Description, SessionID: raw.SessionID} if raw.Inputs != nil { inputs := make([]PromptInputResource, len(raw.Inputs)) presence := state.promptMetadata[id] for i, input := range raw.Inputs { inputs[i] = PromptInputResource{Name: input.Name, ContentType: input.ContentType, Description: input.Description} if presence[i].required == fieldValue { required := input.Required inputs[i].Required = &required } } result.Inputs = &inputs } result.Messages = make([]PromptMessageResource, len(entry.definition.Templates)) for i, message := range entry.definition.Templates { result.Messages[i] = PromptMessageResource{Role: message.Role, Content: message.Content} if message.CacheControl != nil { result.Messages[i].CacheControl = &PromptCacheControlResource{Type: message.CacheControl.Type, TTL: raw.Messages[i].CacheControl.TTL} } } result.Output = PromptOutputResource{Format: entry.definition.Validation.Format, ValidationMode: entry.definition.Validation.ValidationMode, SchemaPath: raw.Output.SchemaPath} if state.promptRepair[id] { value := raw.Output.RepairAttempts result.Output.RepairAttempts = &value } return result, nil } func (state *loaded) InspectProfile(ctx context.Context, id string) (*ProfileResource, error) { if err := ctx.Err(); err != nil { return nil, err } if state == nil { return nil, fmt.Errorf("%w: %q", profile.ErrProfileNotFound, id) } config, ok := state.profileRecords[id] if !ok { return nil, fmt.Errorf("%w: %q", profile.ErrProfileNotFound, id) } return &ProfileResource{ ID: id, Backend: config.Backend, Endpoint: config.Endpoint, Model: config.Model, Temperature: config.Temperature, TopP: config.TopP, MaxTokens: config.MaxTokens, TimeoutSeconds: config.TimeoutSeconds, ServiceTier: config.ServiceTier, ReasoningEffort: config.ReasoningEffort, APIKeyEnv: config.APIKeyEnv, APIKeyRequired: config.APIKeyRequired, ExtraParams: config.ExtraParams, }, nil } func (state *loaded) InspectBackend(ctx context.Context, id string) (*BackendResource, error) { if err := ctx.Err(); err != nil { return nil, err } if state == nil { return nil, fmt.Errorf("%w: %q", backend.ErrBackendNotFound, id) } config, ok := state.backendRecords[id] if !ok { return nil, fmt.Errorf("%w: %q", backend.ErrBackendNotFound, id) } return &BackendResource{ ID: id, Endpoint: valueOrZero(config.Endpoint), APIKeyEnv: config.APIKeyEnv, ConcurrencyLimit: config.ConcurrencyLimit, QueueCapacity: config.QueueCapacity, ExtraParams: config.ExtraParams, SmokeTests: captureBackend(id, config).SmokeTests, }, nil }