Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/Builders/PromptBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,32 @@ public function usingModel(ModelInterface $model): self
return $this;
}

/**
* Sets the model configuration.
*
* Merges the provided configuration with the builder's configuration,
* with builder configuration taking precedence.
*
* @since n.e.x.t
*
* @param ModelConfig $config The model configuration to merge.
* @return self
*/
public function usingModelConfig(ModelConfig $config): self
{
// Convert both configs to arrays
$builderConfigArray = $this->modelConfig->toArray();
$providedConfigArray = $config->toArray();

// Merge arrays with builder config taking precedence
$mergedArray = array_merge($providedConfigArray, $builderConfigArray);

// Create new config from merged array
$this->modelConfig = ModelConfig::fromArray($mergedArray);

return $this;
}

/**
* Sets the provider to use for generation.
*
Expand Down
4 changes: 3 additions & 1 deletion src/Providers/Models/DTO/ModelConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -909,7 +909,9 @@ static function (FunctionDeclaration $function_declaration): array {
$data[self::KEY_OUTPUT_MEDIA_ASPECT_RATIO] = $this->outputMediaAspectRatio;
}

$data[self::KEY_CUSTOM_OPTIONS] = $this->customOptions;
if (!empty($this->customOptions)) {
$data[self::KEY_CUSTOM_OPTIONS] = $this->customOptions;
}

return $data;
}
Expand Down
93 changes: 93 additions & 0 deletions tests/unit/Builders/PromptBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,99 @@ public function testUsingModel(): void
$this->assertSame($model, $actualModel);
}

/**
* Tests usingModelConfig method.
*
* @return void
*/
public function testUsingModelConfig(): void
{
$builder = new PromptBuilder($this->registry);

// Set some initial config values on the builder
$builder->usingSystemInstruction('Builder instruction')
->usingMaxTokens(500)
->usingTemperature(0.5);

// Create a config to merge
$config = new ModelConfig();
$config->setSystemInstruction('Config instruction');
$config->setMaxTokens(1000);
$config->setTopP(0.9);
$config->setTopK(40);

$result = $builder->usingModelConfig($config);

// Assert fluent interface
$this->assertSame($builder, $result);

// Get the merged config
$reflection = new \ReflectionClass($builder);
$configProperty = $reflection->getProperty('modelConfig');
$configProperty->setAccessible(true);

/** @var ModelConfig $mergedConfig */
$mergedConfig = $configProperty->getValue($builder);

// Assert builder values take precedence
$this->assertEquals('Builder instruction', $mergedConfig->getSystemInstruction());
$this->assertEquals(500, $mergedConfig->getMaxTokens());
$this->assertEquals(0.5, $mergedConfig->getTemperature());

// Assert config values are used when builder doesn't have them
$this->assertEquals(0.9, $mergedConfig->getTopP());
$this->assertEquals(40, $mergedConfig->getTopK());
}

/**
* Tests usingModelConfig with custom options.
*
* @return void
*/
public function testUsingModelConfigWithCustomOptions(): void
{
$builder = new PromptBuilder($this->registry);

// Create a config with custom options
$config = new ModelConfig();
$config->setCustomOption('stopSequences', ['CONFIG_STOP']);
$config->setCustomOption('otherOption', 'value');

$builder->usingModelConfig($config);

// Get the merged config
$reflection = new \ReflectionClass($builder);
$configProperty = $reflection->getProperty('modelConfig');
$configProperty->setAccessible(true);

/** @var ModelConfig $mergedConfig */
$mergedConfig = $configProperty->getValue($builder);
$customOptions = $mergedConfig->getCustomOptions();

// Assert config custom options are preserved
$this->assertArrayHasKey('stopSequences', $customOptions);
$this->assertIsArray($customOptions['stopSequences']);
$this->assertEquals(['CONFIG_STOP'], $customOptions['stopSequences']);
$this->assertArrayHasKey('otherOption', $customOptions);
$this->assertEquals('value', $customOptions['otherOption']);

// Now set a builder value that overrides one of the custom options
$builder->usingStopSequences('STOP');

// Get the config again
$mergedConfig = $configProperty->getValue($builder);
$customOptions = $mergedConfig->getCustomOptions();

// Assert builder's stop sequences override the config's
$this->assertArrayHasKey('stopSequences', $customOptions);
$this->assertIsArray($customOptions['stopSequences']);
$this->assertEquals(['STOP'], $customOptions['stopSequences']);

// Assert other custom options are still preserved
$this->assertArrayHasKey('otherOption', $customOptions);
$this->assertEquals('value', $customOptions['otherOption']);
}

/**
* Tests usingProvider method.
*
Expand Down
44 changes: 39 additions & 5 deletions tests/unit/Providers/Models/DTO/ModelConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -304,9 +304,8 @@ public function testToArrayNoProperties(): void
$array = $config->toArray();

$this->assertIsArray($array);
$this->assertCount(1, $array);
$this->assertArrayHasKey(ModelConfig::KEY_CUSTOM_OPTIONS, $array);
$this->assertEquals([], $array[ModelConfig::KEY_CUSTOM_OPTIONS]);
$this->assertCount(0, $array);
$this->assertArrayNotHasKey(ModelConfig::KEY_CUSTOM_OPTIONS, $array);
}

/**
Expand All @@ -323,14 +322,49 @@ public function testToArrayPartialProperties(): void
$array = $config->toArray();

$this->assertIsArray($array);
$this->assertCount(3, $array);
$this->assertCount(2, $array);
$this->assertEquals(0.5, $array[ModelConfig::KEY_TEMPERATURE]);
$this->assertEquals(100, $array[ModelConfig::KEY_MAX_TOKENS]);
$this->assertEquals([], $array[ModelConfig::KEY_CUSTOM_OPTIONS]);
$this->assertArrayNotHasKey(ModelConfig::KEY_CUSTOM_OPTIONS, $array);
$this->assertArrayNotHasKey(ModelConfig::KEY_SYSTEM_INSTRUCTION, $array);
$this->assertArrayNotHasKey(ModelConfig::KEY_TOP_P, $array);
}

/**
* Tests custom options are only included when not empty.
*
* @return void
*/
public function testToArrayCustomOptionsOnlyIncludedWhenNotEmpty(): void
{
// Test with empty custom options (default)
$config = new ModelConfig();
$config->setTemperature(0.7);

$array = $config->toArray();
$this->assertArrayNotHasKey(ModelConfig::KEY_CUSTOM_OPTIONS, $array);

// Test with non-empty custom options
$config->setCustomOption('key1', 'value1');
$array = $config->toArray();
$this->assertArrayHasKey(ModelConfig::KEY_CUSTOM_OPTIONS, $array);
$this->assertEquals(['key1' => 'value1'], $array[ModelConfig::KEY_CUSTOM_OPTIONS]);

// Test with multiple custom options
$config->setCustomOption('key2', ['nested' => 'value']);
$array = $config->toArray();
$this->assertArrayHasKey(ModelConfig::KEY_CUSTOM_OPTIONS, $array);
$this->assertEquals([
'key1' => 'value1',
'key2' => ['nested' => 'value']
], $array[ModelConfig::KEY_CUSTOM_OPTIONS]);

// Test resetting custom options to empty
$config->setCustomOptions([]);
$array = $config->toArray();
$this->assertArrayNotHasKey(ModelConfig::KEY_CUSTOM_OPTIONS, $array);
}

/**
* Tests creating from array with all properties.
*
Expand Down