diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index d288ac2..003cdc5 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -31,5 +31,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index c359214..06e8383 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,8 @@ Add your magic here!
Illustrates:
+- An injected demo showing how any Spring component can be injected with an Embabel `Ai` instance to enable it to
+ perform LLM operations.
- A simple agent
- Unit tests for an agent verifying prompts and hyperparameters
@@ -47,6 +49,12 @@ When the Embabel shell comes up, use the story agent like this:
x "Tell me a story about...[your topic]"
```
+Try the `InjectedDemo` command to see simple, non-agent use:
+
+```java
+animal
+```
+
## A2A Support
Embabel integrates with the [A2A](https://github.com/google-a2a/A2A) protocol, allowing you to connect to other
diff --git a/pom.xml b/pom.xml
index fcdf101..6ae53b5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,7 +18,7 @@
21
- 0.1.0
+ 0.1.1
@@ -63,7 +63,7 @@
false
-
+
embabel-snapshots
https://repo.embabel.com/artifactory/libs-snapshot
diff --git a/src/main/java/com/embabel/template/ProjectNameApplication.java b/src/main/java/com/embabel/ProjectNameApplication.java
similarity index 97%
rename from src/main/java/com/embabel/template/ProjectNameApplication.java
rename to src/main/java/com/embabel/ProjectNameApplication.java
index 85f9ad4..9f89218 100644
--- a/src/main/java/com/embabel/template/ProjectNameApplication.java
+++ b/src/main/java/com/embabel/ProjectNameApplication.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.embabel.template;
+package com.embabel;
import com.embabel.agent.config.annotation.EnableAgentShell;
import com.embabel.agent.config.annotation.EnableAgents;
diff --git a/src/main/java/com/embabel/template/DemoShell.java b/src/main/java/com/embabel/template/DemoShell.java
new file mode 100644
index 0000000..c5976e0
--- /dev/null
+++ b/src/main/java/com/embabel/template/DemoShell.java
@@ -0,0 +1,14 @@
+package com.embabel.template;
+
+import com.embabel.template.injected.InjectedDemo;
+import org.springframework.shell.standard.ShellComponent;
+import org.springframework.shell.standard.ShellMethod;
+
+@ShellComponent
+record DemoShell(InjectedDemo injectedDemo) {
+
+ @ShellMethod("Invent an animal")
+ String animal() {
+ return injectedDemo.inventAnimal().toString();
+ }
+}
diff --git a/src/main/java/com/embabel/template/agent/WriteAndReviewAgent.java b/src/main/java/com/embabel/template/agent/WriteAndReviewAgent.java
index 250eed4..18ca0d6 100644
--- a/src/main/java/com/embabel/template/agent/WriteAndReviewAgent.java
+++ b/src/main/java/com/embabel/template/agent/WriteAndReviewAgent.java
@@ -20,13 +20,11 @@
import com.embabel.agent.api.annotation.Agent;
import com.embabel.agent.api.annotation.Export;
import com.embabel.agent.api.common.OperationContext;
-import com.embabel.agent.api.common.PromptRunner;
import com.embabel.agent.domain.io.UserInput;
import com.embabel.agent.domain.library.HasContent;
import com.embabel.agent.prompt.persona.Persona;
-import com.embabel.common.ai.model.AutoModelSelectionCriteria;
+import com.embabel.agent.prompt.persona.RoleGoalBackstory;
import com.embabel.common.ai.model.LlmOptions;
-import com.embabel.common.ai.prompt.PromptContributionLocation;
import com.embabel.common.core.types.Timestamped;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
@@ -37,21 +35,16 @@
import java.time.format.DateTimeFormatter;
abstract class Personas {
- static final Persona WRITER = Persona.create(
- "Roald Dahl",
- "A creative storyteller who loves to weave imaginative tales that are a bit unconventional",
- "Quirky",
- "Create memorable stories that captivate the reader's imagination.",
- "",
- PromptContributionLocation.BEGINNING
- );
+ static final RoleGoalBackstory WRITER = RoleGoalBackstory
+ .withRole("Creative Storyteller")
+ .andGoal("Write engaging and imaginative stories")
+ .andBackstory("Has a PhD in French literature; used to work in a circus");
+
static final Persona REVIEWER = Persona.create(
"Media Book Review",
"New York Times Book Reviewer",
"Professional and insightful",
- "Help guide readers toward good stories",
- "",
- PromptContributionLocation.BEGINNING
+ "Help guide readers toward good stories"
);
}
@@ -112,8 +105,9 @@ class WriteAndReviewAgent {
export = @Export(remote = true, name = "writeAndReviewStory"))
@Action
ReviewedStory reviewStory(UserInput userInput, Story story, OperationContext context) {
- String review = context.promptRunner()
- .withLlm(LlmOptions.fromCriteria(AutoModelSelectionCriteria.INSTANCE))
+ var review = context
+ .ai()
+ .withAutoLlm()
.withPromptContributor(Personas.REVIEWER)
.generateText(String.format("""
You will be given a short story to review.
@@ -141,12 +135,11 @@ ReviewedStory reviewStory(UserInput userInput, Story story, OperationContext con
@Action
Story craftStory(UserInput userInput, OperationContext context) {
- PromptRunner runner = context.promptRunner()
- // Higher temperature for more creative output
- .withLlm(LlmOptions.fromCriteria(AutoModelSelectionCriteria.INSTANCE, 0.9))
- .withPromptContributor(Personas.WRITER);
-
- return runner.createObject(String.format("""
+ return context.ai()
+ // Higher temperature for more creative output
+ .withLlm(LlmOptions.withAutoLlm().withTemperature(.7))
+ .withPromptContributor(Personas.WRITER)
+ .createObject(String.format("""
Craft a short story in %d words or less.
The story should be engaging and imaginative.
Use the user's input as inspiration if possible.
@@ -155,8 +148,8 @@ Story craftStory(UserInput userInput, OperationContext context) {
# User input
%s
""",
- storyWordCount,
- userInput.getContent()
- ).trim(), Story.class);
+ storyWordCount,
+ userInput.getContent()
+ ).trim(), Story.class);
}
}
\ No newline at end of file
diff --git a/src/main/java/com/embabel/template/injected/InjectedDemo.java b/src/main/java/com/embabel/template/injected/InjectedDemo.java
new file mode 100644
index 0000000..0392f80
--- /dev/null
+++ b/src/main/java/com/embabel/template/injected/InjectedDemo.java
@@ -0,0 +1,27 @@
+package com.embabel.template.injected;
+
+import com.embabel.agent.api.common.Ai;
+import org.springframework.stereotype.Component;
+
+/**
+ * Demonstrate injection of Embabel's OperationContext into a Spring component.
+ *
+ * @param ai Embabel AI helper, injected by Spring
+ */
+@Component
+public record InjectedDemo(Ai ai) {
+
+ public record Animal(String name, String species) {
+ }
+
+ public Animal inventAnimal() {
+ return ai
+ .withDefaultLlm()
+ .createObject("""
+ You just woke up in a magical forest.
+ Invent a fictional animal.
+ The animal should have a name and a species.
+ """,
+ Animal.class);
+ }
+}
diff --git a/src/test/java/com/embabel/agent/testing/integration/EmbabelMockitoIntegrationTest.java b/src/test/java/com/embabel/agent/testing/integration/EmbabelMockitoIntegrationTest.java
deleted file mode 100644
index 787b39f..0000000
--- a/src/test/java/com/embabel/agent/testing/integration/EmbabelMockitoIntegrationTest.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package com.embabel.agent.testing.integration;
-
-import com.embabel.agent.core.AgentPlatform;
-import com.embabel.agent.spi.LlmInteraction;
-import com.embabel.agent.spi.LlmOperations;
-import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mockito;
-import org.mockito.stubbing.OngoingStubbing;
-import org.mockito.verification.VerificationMode;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.TestPropertySource;
-import org.springframework.test.context.bean.override.mockito.MockitoBean;
-
-import static org.mockito.ArgumentMatchers.*;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-@SpringBootTest
-@TestPropertySource(properties = {
- "embabel.agent.llm.default-model=test-model",
- "embabel.agent.verbosity.debug=true",
- "spring.shell.interactive.enabled=false",
- "spring.shell.noninteractive.enabled=false"
-})
-public class EmbabelMockitoIntegrationTest {
-
- @Autowired
- protected AgentPlatform agentPlatform;
-
- @MockitoBean
- protected LlmOperations llmOperations;
-
- // Stubbing methods
- protected OngoingStubbing whenCreateObject(String prompt, Class outputClass, LlmInteraction llmInteraction) {
- // Mock the lower level LLM operation to create an object
- // that will ultimately be called
- return when(llmOperations.createObject(prompt, llmInteraction, eq(outputClass), any(), any()));
- }
-
- protected OngoingStubbing whenCreateObject(String prompt, Class outputClass) {
- return when(llmOperations.createObject(prompt, any(), eq(outputClass), any(), any()));
- }
-
- protected OngoingStubbing whenGenerateText(String prompt, LlmInteraction llmInteraction) {
- return when(llmOperations.createObject(prompt, llmInteraction, eq(String.class), any(), any()));
- }
-
- protected OngoingStubbing whenGenerateText(String prompt) {
- return when(llmOperations.createObject(prompt, any(), eq(String.class), any(), any()));
- }
-
- // Verification methods
- protected void verifyCreateObject(String prompt, Class outputClass, LlmInteraction llmInteraction) {
- verify(llmOperations).createObject(eq(prompt), eq(llmInteraction), eq(outputClass), any(), any());
- }
-
- protected void verifyCreateObject(String prompt, Class outputClass) {
- verify(llmOperations).createObject(eq(prompt), any(), eq(outputClass), any(), any());
- }
-
- protected void verifyCreateObject(String prompt, Class outputClass, LlmInteraction llmInteraction, VerificationMode mode) {
- verify(llmOperations, mode).createObject(eq(prompt), eq(llmInteraction), eq(outputClass), any(), any());
- }
-
- protected void verifyCreateObject(String prompt, Class outputClass, VerificationMode mode) {
- verify(llmOperations, mode).createObject(eq(prompt), any(), eq(outputClass), any(), any());
- }
-
- protected void verifyGenerateText(String prompt, LlmInteraction llmInteraction) {
- verify(llmOperations).createObject(eq(prompt), eq(llmInteraction), eq(String.class), any(), any());
- }
-
- protected void verifyGenerateText(String prompt) {
- verify(llmOperations).createObject(eq(prompt), any(), eq(String.class), any(), any());
- }
-
- protected void verifyGenerateText(String prompt, LlmInteraction llmInteraction, VerificationMode mode) {
- verify(llmOperations, mode).createObject(eq(prompt), eq(llmInteraction), eq(String.class), any(), any());
- }
-
- protected void verifyGenerateText(String prompt, VerificationMode mode) {
- verify(llmOperations, mode).createObject(eq(prompt), any(), eq(String.class), any(), any());
- }
-
- // Verification methods with argument matchers
- protected void verifyCreateObjectMatching(ArgumentMatcher promptMatcher, Class outputClass) {
- verify(llmOperations).createObject(argThat(promptMatcher), any(), eq(outputClass), any(), any());
- }
-
- protected void verifyCreateObjectMatching(ArgumentMatcher promptMatcher, Class outputClass, ArgumentMatcher llmInteractionMatcher) {
- verify(llmOperations).createObject(argThat(promptMatcher),
- argThat(llmInteractionMatcher),
- eq(outputClass), any(), any());
- }
-
- protected void verifyCreateObjectMatching(ArgumentMatcher promptMatcher, Class outputClass, LlmInteraction llmInteraction) {
- verify(llmOperations).createObject(argThat(promptMatcher), eq(llmInteraction), eq(outputClass), any(), any());
- }
-
- protected void verifyCreateObjectMatching(ArgumentMatcher promptMatcher, Class outputClass, VerificationMode mode) {
- verify(llmOperations, mode).createObject(argThat(promptMatcher), any(), eq(outputClass), any(), any());
- }
-
- protected void verifyGenerateTextMatching(ArgumentMatcher promptMatcher) {
- verify(llmOperations).createObject(argThat(promptMatcher), any(), eq(String.class), any(), any());
- }
-
- protected void verifyGenerateTextMatching(ArgumentMatcher promptMatcher, LlmInteraction llmInteraction) {
- Mockito.verify(llmOperations).createObject(argThat(promptMatcher), eq(llmInteraction), eq(String.class), any(), any());
- }
-
- protected void verifyGenerateTextMatching(ArgumentMatcher promptMatcher, VerificationMode mode) {
- Mockito.verify(llmOperations, mode).createObject(argThat(promptMatcher), any(), eq(String.class), any(), any());
- }
-
- // Convenience verification methods
- protected void verifyNoInteractions() {
- Mockito.verifyNoInteractions(llmOperations);
- }
-
- protected void verifyNoMoreInteractions() {
- Mockito.verifyNoMoreInteractions(llmOperations);
- }
-
- // Argument captor helpers
- protected ArgumentCaptor capturePrompt() {
- return ArgumentCaptor.forClass(String.class);
- }
-
- protected ArgumentCaptor captureLlmInteraction() {
- return ArgumentCaptor.forClass(LlmInteraction.class);
- }
-
- protected ArgumentCaptor> captureOutputClass() {
- return ArgumentCaptor.forClass(Class.class);
- }
-}
\ No newline at end of file
diff --git a/src/test/java/com/embabel/template/agent/WriteAndReviewAgentIntegrationTest.java b/src/test/java/com/embabel/template/agent/WriteAndReviewAgentIntegrationTest.java
index 9ea9036..a3ece1e 100644
--- a/src/test/java/com/embabel/template/agent/WriteAndReviewAgentIntegrationTest.java
+++ b/src/test/java/com/embabel/template/agent/WriteAndReviewAgentIntegrationTest.java
@@ -13,7 +13,7 @@
* This will run under Spring Boot against an AgentPlatform instance
* that has loaded all our agents.
*/
-class StoryWriterIntegrationTest extends EmbabelMockitoIntegrationTest {
+class WriteAndReviewAgentIntegrationTest extends EmbabelMockitoIntegrationTest {
@Test
void shouldExecuteCompleteWorkflow() {
@@ -39,7 +39,7 @@ void shouldExecuteCompleteWorkflow() {
"Expected review to match: " + reviewedStoryResult);
verifyCreateObjectMatching(prompt -> prompt.contains("Craft a short story"), Story.class,
- llm -> llm.getLlm().getTemperature() == 0.9 && llm.getToolGroups().isEmpty());
+ llm -> llm.getLlm().getTemperature() == 0.7 && llm.getToolGroups().isEmpty());
verifyGenerateTextMatching(prompt -> prompt.contains("You will be given a short story to review"));
verifyNoMoreInteractions();
}
diff --git a/src/test/java/com/embabel/template/agent/WriteAndReviewAgentTest.java b/src/test/java/com/embabel/template/agent/WriteAndReviewAgentTest.java
index d3db4ee..b37485a 100644
--- a/src/test/java/com/embabel/template/agent/WriteAndReviewAgentTest.java
+++ b/src/test/java/com/embabel/template/agent/WriteAndReviewAgentTest.java
@@ -25,9 +25,6 @@ void testWriteAndReviewAgent() {
String prompt = promptRunner.getLlmInvocations().getFirst().getPrompt();
assertTrue(prompt.contains("knight"), "Expected prompt to contain 'knight'");
- var temp = promptRunner.getLlmInvocations().getFirst().getInteraction().getLlm().getTemperature();
- assertEquals(0.9, temp, 0.01,
- "Expected temperature to be 0.9: Higher for more creative output");
}
@Test