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
5 changes: 5 additions & 0 deletions .idea/jarRepositories.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

<properties>
<java.version>21</java.version>
<embabel-agent.version>0.1.0</embabel-agent.version>
<embabel-agent.version>0.1.1</embabel-agent.version>
</properties>

<dependencies>
Expand Down Expand Up @@ -63,7 +63,7 @@
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repository>
<repository>
<id>embabel-snapshots</id>
<url>https://repo.embabel.com/artifactory/libs-snapshot</url>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/com/embabel/template/DemoShell.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
43 changes: 18 additions & 25 deletions src/main/java/com/embabel/template/agent/WriteAndReviewAgent.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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"
);
}

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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);
}
}
27 changes: 27 additions & 0 deletions src/main/java/com/embabel/template/injected/InjectedDemo.java
Original file line number Diff line number Diff line change
@@ -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);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading