package promptkit import ( "reflect" "gitea.maximumdirect.net/eric/promptkit/internal/domain" "gitea.maximumdirect.net/eric/promptkit/internal/jsonvalue" ) func toDomainRunRequest(req RunRequest) (domain.RunRequest, error) { execution, err := toDomainExecutionTargetOverride(req.Execution) if err != nil { return domain.RunRequest{}, err } return domain.RunRequest{ PromptID: req.PromptID, PromptVersion: req.PromptVersion, ProfileID: req.ProfileID, SessionID: req.SessionID, APIKey: req.APIKey, Inputs: toDomainArtifactRefMap(req.Inputs), Vars: copyStringMap(req.Vars), Execution: execution, Validation: toDomainOutputContractPtr(req.Validation), }, nil } func fromDomainPreparedRun(prepared *domain.PreparedRun) *PreparedRun { if prepared == nil { return nil } return &PreparedRun{ PromptID: prepared.PromptID, PromptVersion: prepared.PromptVersion, PromptHash: prepared.PromptHash, SelectedProfileID: prepared.SelectedProfileID, SelectedBackendID: prepared.SelectedBackendID, EffectiveModelParams: fromDomainExecutionTarget(prepared.EffectiveModelParams), OutputContract: fromDomainOutputContract(prepared.OutputContract), StructuredOutput: fromDomainStructuredOutputSpec(prepared.StructuredOutput), InputHashes: copyStringMap(prepared.InputHashes), SessionID: prepared.SessionID, RenderedPromptHash: prepared.RenderedPromptHash, Messages: fromDomainRenderedMessages(prepared.Messages), StartTime: prepared.StartTime, EndTime: prepared.EndTime, DurationMS: prepared.DurationMS, } } func fromDomainRunResult(result *domain.RunResult) *RunResult { if result == nil { return nil } return &RunResult{ RunID: result.RunID, Artifact: fromDomainArtifact(result.Artifact), RawOutput: result.RawOutput, Validation: fromDomainValidationResult(result.Validation), PromptID: result.PromptID, PromptVersion: result.PromptVersion, PromptHash: result.PromptHash, SessionID: result.SessionID, RenderedPromptHash: result.RenderedPromptHash, SelectedProfileID: result.SelectedProfileID, SelectedBackendID: result.SelectedBackendID, ModelName: result.ModelName, Endpoint: result.Endpoint, EffectiveModelParams: fromDomainExecutionTarget(result.EffectiveModelParams), InputHashes: copyStringMap(result.InputHashes), Usage: fromDomainTokenUsage(result.Usage), StartTime: result.StartTime, EndTime: result.EndTime, Duration: result.Duration, } } func fromDomainGenerateRequest(req domain.GenerateRequest) GenerateRequest { return GenerateRequest{ Prompt: fromDomainRenderedPrompt(req.Prompt), Target: fromDomainExecutionTarget(req.Target), TargetPresence: fromDomainExecutionTargetPresence(req.TargetPresence), StructuredOutput: fromDomainStructuredOutputSpec(req.StructuredOutput), APIKey: req.Target.APIKey, } } func toDomainGenerateResponse(resp *GenerateResponse) *domain.GenerateResponse { if resp == nil { return nil } return &domain.GenerateResponse{ Content: resp.Content, Usage: toDomainTokenUsage(resp.Usage), } } func fromDomainRenderedPrompt(prompt domain.RenderedPrompt) RenderedPrompt { return RenderedPrompt{ SessionID: prompt.SessionID, Messages: fromDomainRenderedMessages(prompt.Messages), } } func toDomainArtifactRefMap(src map[string]ArtifactRef) map[string]domain.ArtifactRef { if src == nil { return nil } out := make(map[string]domain.ArtifactRef, len(src)) for k, v := range src { out[k] = toDomainArtifactRef(v) } return out } func toDomainArtifactRef(ref ArtifactRef) domain.ArtifactRef { return domain.ArtifactRef{ Type: domain.ArtifactRefType(ref.Type), URI: ref.URI, Body: ref.Body, } } func fromDomainArtifact(artifact domain.Artifact) Artifact { return Artifact{ Name: artifact.Name, ContentType: artifact.ContentType, Body: copyBytes(artifact.Body), URI: artifact.URI, Size: artifact.Size, Hash: artifact.Hash, } } func toDomainExecutionTargetOverride(override *ExecutionTargetOverride) (*domain.ExecutionTargetOverride, error) { if override == nil { return nil, nil } extraParams, err := jsonvalue.CopyMap(override.ExtraParams) if err != nil { return nil, err } return &domain.ExecutionTargetOverride{ Endpoint: override.Endpoint, Model: override.Model, Temperature: copyFloat64Ptr(override.Temperature), MaxTokens: copyIntPtr(override.MaxTokens), TopP: copyFloat64Ptr(override.TopP), TimeoutSeconds: copyIntPtr(override.TimeoutSeconds), ServiceTier: override.ServiceTier, ReasoningEffort: copyStringPtr(override.ReasoningEffort), APIKeyEnv: override.APIKeyEnv, ExtraParams: extraParams, }, nil } func fromDomainExecutionTarget(target domain.ExecutionTarget) ExecutionTarget { return ExecutionTarget{ BackendID: target.BackendID, Endpoint: target.Endpoint, Model: target.Model, Temperature: target.Temperature, MaxTokens: target.MaxTokens, TopP: target.TopP, TimeoutSeconds: target.TimeoutSeconds, ServiceTier: target.ServiceTier, ReasoningEffort: target.ReasoningEffort, APIKeyEnv: target.APIKeyEnv, ExtraParams: copyAnyMap(target.ExtraParams), } } func fromDomainProfileInspection(inspection *domain.ProfileInspection) *ProfileInspection { if inspection == nil { return nil } return &ProfileInspection{ ProfileID: inspection.ProfileID, EffectiveModelParams: fromDomainExecutionTarget(inspection.EffectiveModelParams), APIKeyRequired: inspection.APIKeyRequired, } } func fromDomainPromptInspection(inspection *domain.PromptInspection) *PromptInspection { if inspection == nil { return nil } inputs := make([]PromptInputDefinition, len(inspection.Inputs)) for i, input := range inspection.Inputs { inputs[i] = PromptInputDefinition{ Name: input.Name, Required: input.Required, ContentType: input.ContentType, Description: input.Description, } } return &PromptInspection{ PromptID: inspection.PromptID, PromptVersion: inspection.PromptVersion, PromptHash: inspection.PromptHash, DefaultProfileID: inspection.DefaultProfileID, Inputs: inputs, OutputContract: fromDomainOutputContract(inspection.OutputContract), } } func fromDomainExecutionTargetPresence(presence domain.ExecutionTargetPresence) ExecutionTargetPresence { return ExecutionTargetPresence{ Temperature: presence.Temperature, MaxTokens: presence.MaxTokens, TopP: presence.TopP, TimeoutSeconds: presence.TimeoutSeconds, } } func toDomainOutputContractPtr(contract *OutputContract) *domain.OutputContract { if contract == nil { return nil } out := toDomainOutputContract(*contract) return &out } func toDomainOutputContract(contract OutputContract) domain.OutputContract { return domain.OutputContract{ Format: domain.OutputFormat(contract.Format), ValidationMode: domain.ValidationMode(contract.ValidationMode), SchemaPath: contract.SchemaPath, RepairAttempts: contract.RepairAttempts, } } func fromDomainOutputContract(contract domain.OutputContract) OutputContract { return OutputContract{ Format: OutputFormat(contract.Format), ValidationMode: ValidationMode(contract.ValidationMode), SchemaPath: contract.SchemaPath, RepairAttempts: contract.RepairAttempts, } } func fromDomainValidationResult(result domain.ValidationResult) ValidationResult { return ValidationResult{ Status: ValidationStatus(result.Status), Mode: ValidationMode(result.Mode), Errors: copyStringSlice(result.Errors), SchemaPath: result.SchemaPath, RepairAttempts: result.RepairAttempts, IsValid: result.IsValid, } } func fromDomainTokenUsage(usage domain.TokenUsage) TokenUsage { return TokenUsage{ PromptTokens: usage.PromptTokens, CompletionTokens: usage.CompletionTokens, TotalTokens: usage.TotalTokens, CachedTokens: usage.CachedTokens, CacheWriteTokens: usage.CacheWriteTokens, } } func toDomainTokenUsage(usage TokenUsage) domain.TokenUsage { return domain.TokenUsage{ PromptTokens: usage.PromptTokens, CompletionTokens: usage.CompletionTokens, TotalTokens: usage.TotalTokens, CachedTokens: usage.CachedTokens, CacheWriteTokens: usage.CacheWriteTokens, } } func fromDomainRenderedMessages(messages []domain.RenderedMessage) []RenderedMessage { if messages == nil { return nil } out := make([]RenderedMessage, len(messages)) for i, msg := range messages { out[i] = RenderedMessage{ Role: msg.Role, Content: msg.Content, CacheControl: fromDomainCacheControl(msg.CacheControl), } } return out } func fromDomainCacheControl(cacheControl *domain.CacheControl) *CacheControl { if cacheControl == nil { return nil } return &CacheControl{ Type: CacheControlType(cacheControl.Type), TTL: cacheControl.TTL, } } func fromDomainStructuredOutputSpec(spec *domain.StructuredOutputSpec) *StructuredOutputSpec { if spec == nil { return nil } out := &StructuredOutputSpec{ Type: StructuredOutputType(spec.Type), } if spec.JSONSchema != nil { out.JSONSchema = &StructuredOutputJSONSpec{ Name: spec.JSONSchema.Name, Strict: spec.JSONSchema.Strict, Schema: copyAny(spec.JSONSchema.Schema), } } return out } func copyStringMap(src map[string]string) map[string]string { if src == nil { return nil } out := make(map[string]string, len(src)) for k, v := range src { out[k] = v } return out } func copyAnyMap(src map[string]any) map[string]any { if src == nil { return nil } out := make(map[string]any, len(src)) for k, v := range src { out[k] = copyAny(v) } return out } func copyAny(value any) any { if value == nil { return nil } switch v := value.(type) { case map[string]any: return copyAnyMap(v) case []any: out := make([]any, len(v)) for i, item := range v { out[i] = copyAny(item) } return out case []string: return copyStringSlice(v) case []byte: return copyBytes(v) default: return copyReflectValue(reflect.ValueOf(value)).Interface() } } func copyReflectValue(value reflect.Value) reflect.Value { if !value.IsValid() { return value } switch value.Kind() { case reflect.Interface: if value.IsNil() { return reflect.Zero(value.Type()) } copied := copyReflectValue(value.Elem()) if copied.IsValid() && copied.Type().AssignableTo(value.Type()) { return copied } out := reflect.New(value.Type()).Elem() out.Set(copied) return out case reflect.Pointer: if value.IsNil() { return reflect.Zero(value.Type()) } out := reflect.New(value.Type().Elem()) out.Elem().Set(copyReflectValue(value.Elem())) return out case reflect.Map: if value.IsNil() { return reflect.Zero(value.Type()) } out := reflect.MakeMapWithSize(value.Type(), value.Len()) iter := value.MapRange() for iter.Next() { out.SetMapIndex(copyReflectValue(iter.Key()), copyReflectValue(iter.Value())) } return out case reflect.Slice: if value.IsNil() { return reflect.Zero(value.Type()) } out := reflect.MakeSlice(value.Type(), value.Len(), value.Cap()) for i := 0; i < value.Len(); i++ { out.Index(i).Set(copyReflectValue(value.Index(i))) } return out case reflect.Array: out := reflect.New(value.Type()).Elem() for i := 0; i < value.Len(); i++ { out.Index(i).Set(copyReflectValue(value.Index(i))) } return out default: return value } } func copyStringSlice(src []string) []string { if src == nil { return nil } out := make([]string, len(src)) copy(out, src) return out } func copyBytes(src []byte) []byte { if src == nil { return nil } out := make([]byte, len(src)) copy(out, src) return out } func copyFloat64Ptr(src *float64) *float64 { if src == nil { return nil } v := *src return &v } func copyStringPtr(src *string) *string { if src == nil { return nil } v := *src return &v } func copyIntPtr(src *int) *int { if src == nil { return nil } v := *src return &v }