Skip to content

Commit

Permalink
Introduce support for GenAI for Tanzu Platform bindings.
Browse files Browse the repository at this point in the history
Currently this is a PreProvisionedService with a secret derived from the TPCF service key.

Signed-off-by: Stuart Charlton <stuart.charlton@broadcom.com>
  • Loading branch information
Stuart Charlton authored and sobychacko committed Aug 22, 2024
1 parent 72011ba commit 86cfe61
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.springframework.ai.bindings;

import org.springframework.cloud.bindings.Binding;
import org.springframework.cloud.bindings.Bindings;
import org.springframework.cloud.bindings.boot.BindingsPropertiesProcessor;
import org.springframework.core.env.Environment;

import java.util.Arrays;
import java.util.Map;

/**
* An implementation of {@link BindingsPropertiesProcessor} that detects {@link Binding}s
* of type: {@value TYPE}.
*
* @author Stuart Charlton
*/
public class TanzuBindingsPropertiesProcessor implements BindingsPropertiesProcessor {

/**
* The {@link Binding} type that this processor is interested in: {@value}.
**/
public static final String TYPE = "genai";

@Override
public void process(Environment environment, Bindings bindings, Map<String, Object> properties) {
if (!BindingsValidator.isTypeEnabled(environment, TYPE)) {
return;
}

bindings.filterBindings(TYPE).forEach(binding -> {
if (binding.getSecret().get("model-capabilities") != null) {
String[] capabilities = binding.getSecret().get("model-capabilities").trim().split("\\s*,\\s*");
if (Arrays.stream(capabilities).anyMatch("chat"::equals)) {
properties.put("spring.ai.openai.chat.api-key", binding.getSecret().get("api-key"));
properties.put("spring.ai.openai.chat.base-url", binding.getSecret().get("uri"));
properties.put("spring.ai.openai.chat.options.model", binding.getSecret().get("model-name"));
}
if (Arrays.stream(capabilities).anyMatch("embedding"::equals)) {
properties.put("spring.ai.openai.embedding.api-key", binding.getSecret().get("api-key"));
properties.put("spring.ai.openai.embedding.base-url", binding.getSecret().get("uri"));
properties.put("spring.ai.openai.embedding.options.model", binding.getSecret().get("model-name"));
}
}
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ org.springframework.cloud.bindings.boot.BindingsPropertiesProcessor=\
org.springframework.ai.bindings.ChromaBindingsPropertiesProcessor,\
org.springframework.ai.bindings.OllamaBindingsPropertiesProcessor,\
org.springframework.ai.bindings.OpenAiBindingsPropertiesProcessor,\
org.springframework.ai.bindings.TanzuBindingsPropertiesProcessor,\
org.springframework.ai.bindings.WeaviateBindingsPropertiesProcessor
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package org.springframework.ai.bindings;

import org.junit.jupiter.api.Test;
import org.springframework.cloud.bindings.Binding;
import org.springframework.cloud.bindings.Bindings;
import org.springframework.mock.env.MockEnvironment;

import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.ai.bindings.BindingsValidator.CONFIG_PATH;

/**
* Unit tests for {@link TanzuBindingsPropertiesProcessor}.
*
* @author Stuart Charlton
*/
class TanzuBindingsPropertiesProcessorTests {

private final Bindings bindings = new Bindings(new Binding("test-name", Paths.get("test-path"),
// @formatter:off
Map.of(
Binding.TYPE, TanzuBindingsPropertiesProcessor.TYPE,
"api-key", "demo",
"uri", "https://my.openai.example.net",
"model-name", "llava1.6",
"model-capabilities", " chat , vision "
)),
new Binding("test-name2", Paths.get("test-path2"),
Map.of(
Binding.TYPE, TanzuBindingsPropertiesProcessor.TYPE,
"api-key", "demo2",
"uri", "https://my.openai2.example.net",
"model-name", "text-embed-large",
"model-capabilities", "embedding")));
// @formatter:on

private final Bindings bindingsMissingModelCapabilities = new Bindings(
new Binding("test-name", Paths.get("test-path"),
// @formatter:off
Map.of(
Binding.TYPE, TanzuBindingsPropertiesProcessor.TYPE,
"api-key", "demo",
"uri", "https://my.openai.example.net"
)));
// @formatter:on

private final MockEnvironment environment = new MockEnvironment();

private final Map<String, Object> properties = new HashMap<>();

@Test
void propertiesAreContributed() {
new TanzuBindingsPropertiesProcessor().process(environment, bindings, properties);
assertThat(properties).containsEntry("spring.ai.openai.chat.api-key", "demo");
assertThat(properties).containsEntry("spring.ai.openai.chat.base-url", "https://my.openai.example.net");
assertThat(properties).containsEntry("spring.ai.openai.chat.options.model", "llava1.6");
assertThat(properties).containsEntry("spring.ai.openai.embedding.api-key", "demo2");
assertThat(properties).containsEntry("spring.ai.openai.embedding.base-url", "https://my.openai2.example.net");
assertThat(properties).containsEntry("spring.ai.openai.embedding.options.model", "text-embed-large");
}

@Test
void propertiesAreMissingModelCapabilities() {
new TanzuBindingsPropertiesProcessor().process(environment, bindingsMissingModelCapabilities, properties);
assertThat(properties).isEmpty();
}

@Test
void whenDisabledThenPropertiesAreNotContributed() {
environment.setProperty("%s.genai.enabled".formatted(CONFIG_PATH), "false");

new TanzuBindingsPropertiesProcessor().process(environment, bindings, properties);
assertThat(properties).isEmpty();
}

}

0 comments on commit 86cfe61

Please sign in to comment.