( llmInputMapping: Record<string, unknown> | undefined, userInputMapping: Record<string, unknown> | string | undefined )
| 726 | * User-provided non-empty values take precedence. |
| 727 | */ |
| 728 | export function deepMergeInputMapping( |
| 729 | llmInputMapping: Record<string, unknown> | undefined, |
| 730 | userInputMapping: Record<string, unknown> | string | undefined |
| 731 | ): Record<string, unknown> { |
| 732 | // Parse user inputMapping if it's a JSON string |
| 733 | let parsedUserMapping: Record<string, unknown> = {} |
| 734 | if (typeof userInputMapping === 'string') { |
| 735 | try { |
| 736 | const parsed = JSON.parse(userInputMapping) |
| 737 | if (isRecordLike(parsed)) { |
| 738 | parsedUserMapping = parsed |
| 739 | } |
| 740 | } catch { |
| 741 | // Invalid JSON, treat as empty |
| 742 | } |
| 743 | } else if ( |
| 744 | typeof userInputMapping === 'object' && |
| 745 | userInputMapping !== null && |
| 746 | !Array.isArray(userInputMapping) |
| 747 | ) { |
| 748 | parsedUserMapping = userInputMapping |
| 749 | } |
| 750 | |
| 751 | // If no LLM mapping, return user mapping (or empty) |
| 752 | if (!llmInputMapping || typeof llmInputMapping !== 'object') { |
| 753 | return parsedUserMapping |
| 754 | } |
| 755 | |
| 756 | // Deep merge: LLM values as base, user non-empty values override |
| 757 | // If user provides empty object {}, LLM values fill all fields (intentional) |
| 758 | const merged: Record<string, unknown> = { ...llmInputMapping } |
| 759 | |
| 760 | for (const [key, userValue] of Object.entries(parsedUserMapping)) { |
| 761 | // Only override LLM value if user provided a non-empty value |
| 762 | if (isNonEmpty(userValue)) { |
| 763 | merged[key] = userValue |
| 764 | } |
| 765 | } |
| 766 | |
| 767 | return merged |
| 768 | } |
| 769 | |
| 770 | /** |
| 771 | * Merges user-provided parameters with LLM-generated parameters. |
no test coverage detected