Skip to content

Commit d029386

Browse files
authored
Merge pull request #2 from embabel/feature/agent-integration
Prep integration into Embabel.
2 parents 2cd1996 + 803d973 commit d029386

File tree

30 files changed

+2177
-331
lines changed

30 files changed

+2177
-331
lines changed

README.md

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
# Embabel Chat Store
2+
3+
Conversation persistence layer for Embabel Agent. Provides pluggable storage backends for chat sessions - in-memory for development/testing, Neo4j for production.
4+
5+
## Quick Start
6+
7+
```kotlin
8+
// Get a factory for your storage type
9+
val factory = conversationFactoryProvider.get(ConversationStoreType.STORED)
10+
11+
// Create a conversation for a 1-1 chat
12+
val conversation = (factory as StoredConversationFactory)
13+
.createForParticipants(
14+
id = sessionId,
15+
user = currentUser,
16+
agent = assistantUser,
17+
title = "My Chat"
18+
)
19+
20+
// Add messages - automatically routed based on role
21+
conversation.addMessage(UserMessage("Hello!")) // from=user, to=agent
22+
conversation.addMessage(AssistantMessage("Hi!")) // from=agent, to=user
23+
```
24+
25+
## Architecture
26+
27+
```
28+
ConversationFactoryProvider
29+
├── InMemoryConversationFactory → InMemoryConversation (ephemeral)
30+
└── StoredConversationFactory → StoredConversation (Neo4j)
31+
```
32+
33+
### Storage Types
34+
35+
| Type | Class | Use Case |
36+
|------|-------|----------|
37+
| `IN_MEMORY` | `InMemoryConversation` | Testing, ephemeral chats |
38+
| `STORED` | `StoredConversation` | Production, persistent chats |
39+
40+
## Message Events
41+
42+
Subscribe to message lifecycle events for real-time updates:
43+
44+
```kotlin
45+
@EventListener
46+
fun onMessage(event: MessageEvent) {
47+
when (event.status) {
48+
MessageStatus.ADDED -> {
49+
// Message added - update UI immediately
50+
sendToWebSocket(event.toUserId, event)
51+
}
52+
MessageStatus.PERSISTED -> {
53+
// Message saved to storage
54+
}
55+
MessageStatus.PERSISTENCE_FAILED -> {
56+
// Handle error - event.error has details
57+
}
58+
}
59+
}
60+
```
61+
62+
### Event Fields
63+
64+
| Field | Description |
65+
|-------|-------------|
66+
| `conversationId` | Session ID |
67+
| `fromUserId` | Who sent the message |
68+
| `toUserId` | Who should receive it (for routing) |
69+
| `title` | Session title (for UI display) |
70+
| `message` | The message content |
71+
| `status` | ADDED, PERSISTED, or PERSISTENCE_FAILED |
72+
73+
## Message Attribution
74+
75+
Messages have explicit sender and recipient:
76+
77+
```
78+
(message:StoredMessage)-[:AUTHORED_BY]->(from:User)
79+
(message:StoredMessage)-[:SENT_TO]->(to:User)
80+
```
81+
82+
### Role-Based Routing (1-1 Chats)
83+
84+
| Role | From | To |
85+
|------|------|-----|
86+
| USER | user | agent |
87+
| ASSISTANT | agent | user |
88+
| SYSTEM | null | user |
89+
90+
### Multi-Party Chats
91+
92+
For multi-user or multi-agent scenarios, use `addMessageFromTo` for explicit routing:
93+
94+
```kotlin
95+
// Create conversation without default participants
96+
val conversation = factory.create(sessionId)
97+
98+
// Group chat with multiple users
99+
conversation.addMessageFromTo(UserMessage("Hi everyone!"), from = alice, to = bob)
100+
conversation.addMessageFromTo(UserMessage("Hello Alice!"), from = bob, to = alice)
101+
conversation.addMessageFromTo(UserMessage("Hey both!"), from = charlie, to = alice)
102+
103+
// Agent handoff - one agent passing to another
104+
conversation.addMessageFromTo(
105+
AssistantMessage("Transferring you to billing..."),
106+
from = supportAgent,
107+
to = customer
108+
)
109+
conversation.addMessageFromTo(
110+
AssistantMessage("Hi, I'm the billing specialist."),
111+
from = billingAgent,
112+
to = customer
113+
)
114+
115+
// Multi-agent collaboration
116+
conversation.addMessageFromTo(
117+
AssistantMessage("I'll research that."),
118+
from = researchAgent,
119+
to = coordinatorAgent
120+
)
121+
conversation.addMessageFromTo(
122+
AssistantMessage("Here's what I found..."),
123+
from = researchAgent,
124+
to = customer
125+
)
126+
```
127+
128+
#### Future Enhancements
129+
130+
Planned support for:
131+
- Group recipients (send to multiple users at once)
132+
- Participant lists on conversations
133+
- Agent-to-agent communication patterns
134+
- Broadcast messages
135+
136+
## Auto Title Generation
137+
138+
Provide a `TitleGenerator` to automatically generate session titles:
139+
140+
```kotlin
141+
val factory = StoredConversationFactory(
142+
repository = chatSessionRepository,
143+
eventPublisher = applicationEventPublisher,
144+
titleGenerator = { message ->
145+
// Generate title from first message
146+
llm.generate("Summarize in 5 words: ${message.content}")
147+
}
148+
)
149+
```
150+
151+
## User Implementation
152+
153+
Implement `StoredUser` for your user type. The `StoredUser` interface extends `User` from
154+
`embabel-agent-api` with Drivine annotations for Neo4j persistence:
155+
156+
```kotlin
157+
@NodeFragment(labels = ["User", "MyUser"])
158+
data class MyUser(
159+
@NodeId override val id: String,
160+
override val displayName: String,
161+
override val username: String,
162+
override val email: String?
163+
) : StoredUser
164+
```
165+
166+
Register with Drivine for polymorphic loading:
167+
168+
```kotlin
169+
persistenceManager.registerSubtype(
170+
StoredUser::class.java,
171+
"MyUser|User", // Labels sorted alphabetically
172+
MyUser::class.java
173+
)
174+
```
175+
176+
For simple cases without extra fields, use the built-in `SimpleStoredUser`:
177+
178+
```kotlin
179+
val user = SimpleStoredUser(
180+
id = "user-123",
181+
displayName = "Alice",
182+
username = "alice",
183+
email = "alice@example.com"
184+
)
185+
```
186+
187+
## Spring Boot Auto-Configuration
188+
189+
Add the dependency and configure:
190+
191+
```yaml
192+
embabel:
193+
chat:
194+
store:
195+
enabled: true # default
196+
```
197+
198+
Beans auto-configured:
199+
- `storedConversationFactory` - for persistent conversations
200+
- `inMemoryConversationFactory` - for ephemeral conversations
201+
- `conversationFactoryProvider` - aggregates all factories
202+
203+
## Dependencies
204+
205+
- `embabel-agent-api` - Core conversation interfaces
206+
- `drivine` - Neo4j graph persistence
207+
- Spring Boot (optional, for auto-configuration)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<parent>
7+
<groupId>com.embabel.agent</groupId>
8+
<artifactId>embabel-chat-store-parent</artifactId>
9+
<version>0.2.0-SNAPSHOT</version>
10+
</parent>
11+
12+
<artifactId>embabel-agent-starter-chat-store</artifactId>
13+
<packaging>jar</packaging>
14+
<name>Embabel Agent Chat Store Starter</name>
15+
<description>Starter for Embabel Chat Store - brings in library and autoconfiguration</description>
16+
17+
<dependencies>
18+
<!-- Autoconfigure (transitively brings in embabel-chat-store) -->
19+
<dependency>
20+
<groupId>com.embabel.agent</groupId>
21+
<artifactId>embabel-chat-store-autoconfigure</artifactId>
22+
</dependency>
23+
</dependencies>
24+
25+
</project>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<parent>
7+
<groupId>com.embabel.agent</groupId>
8+
<artifactId>embabel-chat-store-parent</artifactId>
9+
<version>0.2.0-SNAPSHOT</version>
10+
</parent>
11+
12+
<artifactId>embabel-chat-store-autoconfigure</artifactId>
13+
<packaging>jar</packaging>
14+
<name>Embabel Chat Store Autoconfigure</name>
15+
<description>Spring Boot Auto-Configuration for Embabel Chat Store</description>
16+
17+
<dependencies>
18+
<!-- Core library -->
19+
<dependency>
20+
<groupId>com.embabel.agent</groupId>
21+
<artifactId>embabel-chat-store</artifactId>
22+
</dependency>
23+
24+
<!-- Embabel Agent API (for ConversationFactory, etc.) -->
25+
<dependency>
26+
<groupId>com.embabel.agent</groupId>
27+
<artifactId>embabel-agent-api</artifactId>
28+
</dependency>
29+
30+
<!-- Spring Boot Autoconfigure -->
31+
<dependency>
32+
<groupId>org.springframework.boot</groupId>
33+
<artifactId>spring-boot-autoconfigure</artifactId>
34+
</dependency>
35+
36+
<dependency>
37+
<groupId>org.springframework.boot</groupId>
38+
<artifactId>spring-boot-configuration-processor</artifactId>
39+
<optional>true</optional>
40+
</dependency>
41+
42+
<!-- Kotlin Support -->
43+
<dependency>
44+
<groupId>org.jetbrains.kotlin</groupId>
45+
<artifactId>kotlin-stdlib</artifactId>
46+
</dependency>
47+
48+
<!-- Logging -->
49+
<dependency>
50+
<groupId>org.slf4j</groupId>
51+
<artifactId>slf4j-api</artifactId>
52+
</dependency>
53+
</dependencies>
54+
55+
<build>
56+
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
57+
<plugins>
58+
<plugin>
59+
<groupId>org.jetbrains.kotlin</groupId>
60+
<artifactId>kotlin-maven-plugin</artifactId>
61+
</plugin>
62+
</plugins>
63+
</build>
64+
65+
</project>

0 commit comments

Comments
 (0)