Skip to content

Commit 2cd1996

Browse files
committed
Add versioning of messages, repo, integrate with guide
1 parent 282a2ac commit 2cd1996

File tree

6 files changed

+255
-74
lines changed

6 files changed

+255
-74
lines changed

pom.xml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,12 @@
1919

2020
<properties>
2121
<java.version>21</java.version>
22-
<embabel-agent.version>0.3.2-SNAPSHOT</embabel-agent.version>
2322
<drivine.version>0.0.22</drivine.version>
2423
<kotlin.version>2.0.20</kotlin.version>
2524
</properties>
2625

2726
<dependencies>
2827

29-
<!-- Embabel Agent API (for Conversation interface) -->
30-
<dependency>
31-
<groupId>com.embabel.agent</groupId>
32-
<artifactId>embabel-agent-api</artifactId>
33-
<version>${embabel-agent.version}</version>
34-
</dependency>
35-
3628
<!-- Drivine4j Spring Boot Starter -->
3729
<dependency>
3830
<groupId>org.drivine</groupId>

src/main/kotlin/com/embabel/chat/store/model/StoredMessage.kt

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
package com.embabel.chat.store.model
22

3+
import org.drivine.annotation.Direction
4+
import org.drivine.annotation.GraphRelationship
5+
import org.drivine.annotation.GraphView
36
import org.drivine.annotation.NodeFragment
47
import org.drivine.annotation.NodeId
8+
import org.drivine.annotation.Root
59
import java.time.Instant
610

711
/**
8-
* A message stored in a chat session.
12+
* Raw message node data.
913
*
10-
* Messages are identified by UUIDv7 which provides chronological ordering
11-
* when sorted lexicographically.
14+
* Use this directly when you don't need the author relationship loaded,
15+
* or use [StoredMessage] when you need the full graph view with author.
1216
*/
1317
@NodeFragment(labels = ["StoredMessage"])
14-
data class StoredMessage(
18+
data class MessageData(
1519
/**
1620
* Unique message identifier (UUIDv7 recommended for chronological ordering).
1721
*/
@@ -43,3 +47,46 @@ data class StoredMessage(
4347
const val ROLE_SYSTEM = "system"
4448
}
4549
}
50+
51+
/**
52+
* A message stored in a chat session, with its author relationship.
53+
*
54+
* This is a GraphView that includes:
55+
* - The message node data ([MessageData])
56+
* - The optional author relationship to a [SessionUser]
57+
*
58+
* Example Neo4j structure:
59+
* ```
60+
* (msg:StoredMessage)-[:AUTHORED_BY]->(user:SessionUser)
61+
* ```
62+
*
63+
* If you don't need the author loaded, you can use [MessageData] directly
64+
* in your own GraphView for lighter reads.
65+
*/
66+
@GraphView
67+
data class StoredMessage(
68+
/**
69+
* The message node data.
70+
*/
71+
@Root val message: MessageData,
72+
73+
/**
74+
* The user who authored this message.
75+
* Null for system-generated messages (assistant responses).
76+
*/
77+
@GraphRelationship(type = "AUTHORED_BY", direction = Direction.OUTGOING)
78+
val author: SessionUser? = null
79+
) {
80+
// Convenience accessors for common properties
81+
val messageId: String get() = message.messageId
82+
val role: String get() = message.role
83+
val content: String get() = message.content
84+
val createdAt: Instant get() = message.createdAt
85+
val metadata: Map<String, Any>? get() = message.metadata
86+
87+
companion object {
88+
const val ROLE_USER = MessageData.ROLE_USER
89+
const val ROLE_ASSISTANT = MessageData.ROLE_ASSISTANT
90+
const val ROLE_SYSTEM = MessageData.ROLE_SYSTEM
91+
}
92+
}

src/main/kotlin/com/embabel/chat/store/repository/SessionRepository.kt renamed to src/main/kotlin/com/embabel/chat/store/repository/ChatSessionRepository.kt

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.embabel.chat.store.repository
22

3+
import com.embabel.chat.store.model.MessageData
34
import com.embabel.chat.store.model.SessionUser
45
import com.embabel.chat.store.model.StoredMessage
56
import com.embabel.chat.store.model.StoredSession
@@ -12,7 +13,7 @@ import java.util.Optional
1213
* Sessions are owned by users (implementing SessionUser) and contain an ordered
1314
* list of messages.
1415
*/
15-
interface SessionRepository {
16+
interface ChatSessionRepository {
1617

1718
// ==================== Session CRUD ====================
1819

@@ -26,6 +27,27 @@ interface SessionRepository {
2627
*/
2728
fun createSession(sessionId: String, owner: SessionUser, title: String? = null): StoredSession
2829

30+
/**
31+
* Create a new session with an initial message.
32+
*
33+
* This is a convenience method that combines session creation with adding
34+
* the first message in a single operation.
35+
*
36+
* @param sessionId the session ID (should be UUIDv7 for chronological ordering)
37+
* @param owner the user who owns the session
38+
* @param title optional title for the session
39+
* @param messageData the initial message data
40+
* @param messageAuthor optional author of the message (null for system messages)
41+
* @return the created session with the message
42+
*/
43+
fun createSessionWithMessage(
44+
sessionId: String,
45+
owner: SessionUser,
46+
title: String? = null,
47+
messageData: MessageData,
48+
messageAuthor: SessionUser? = null
49+
): StoredSession
50+
2951
/**
3052
* Find a session by its ID.
3153
*
@@ -63,10 +85,11 @@ interface SessionRepository {
6385
* Add a message to a session.
6486
*
6587
* @param sessionId the session ID
66-
* @param message the message to add
88+
* @param messageData the message data
89+
* @param author optional author of the message (null for system/assistant messages)
6790
* @return the updated session
6891
*/
69-
fun addMessage(sessionId: String, message: StoredMessage): StoredSession
92+
fun addMessage(sessionId: String, messageData: MessageData, author: SessionUser? = null): StoredSession
7093

7194
/**
7295
* Get all messages in a session.

src/main/kotlin/com/embabel/chat/store/repository/SessionRepositoryImpl.kt renamed to src/main/kotlin/com/embabel/chat/store/repository/ChatSessionRepositoryImpl.kt

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,28 @@
11
package com.embabel.chat.store.repository
22

3+
import com.embabel.chat.store.model.MessageData
34
import com.embabel.chat.store.model.SessionData
45
import com.embabel.chat.store.model.SessionUser
56
import com.embabel.chat.store.model.StoredMessage
67
import com.embabel.chat.store.model.StoredSession
78
import org.drivine.manager.GraphObjectManager
89
import org.slf4j.LoggerFactory
9-
import org.springframework.stereotype.Repository
1010
import org.springframework.transaction.annotation.Transactional
1111
import java.time.Instant
1212
import java.util.Optional
1313

1414
/**
15-
* Drivine-based implementation of SessionRepository.
15+
* Drivine-based implementation of ChatSessionRepository.
1616
*
1717
* Uses GraphObjectManager for type-safe graph operations.
1818
* Note: Full DSL support requires code generation. This implementation
1919
* uses basic GraphObjectManager operations.
2020
*/
21-
@Repository
22-
open class SessionRepositoryImpl(
21+
open class ChatSessionRepositoryImpl(
2322
private val graphObjectManager: GraphObjectManager
24-
) : SessionRepository {
23+
) : ChatSessionRepository {
2524

26-
private val logger = LoggerFactory.getLogger(SessionRepositoryImpl::class.java)
25+
private val logger = LoggerFactory.getLogger(ChatSessionRepositoryImpl::class.java)
2726

2827
@Transactional
2928
override fun createSession(sessionId: String, owner: SessionUser, title: String?): StoredSession {
@@ -42,6 +41,34 @@ open class SessionRepositoryImpl(
4241
return graphObjectManager.save(session)
4342
}
4443

44+
@Transactional
45+
override fun createSessionWithMessage(
46+
sessionId: String,
47+
owner: SessionUser,
48+
title: String?,
49+
messageData: MessageData,
50+
messageAuthor: SessionUser?
51+
): StoredSession {
52+
logger.debug("Creating session {} with initial message for owner {}", sessionId, owner.id)
53+
54+
val message = StoredMessage(
55+
message = messageData,
56+
author = messageAuthor
57+
)
58+
59+
val session = StoredSession(
60+
session = SessionData(
61+
sessionId = sessionId,
62+
title = title,
63+
createdAt = Instant.now()
64+
),
65+
owner = owner,
66+
messages = listOf(message)
67+
)
68+
69+
return graphObjectManager.save(session)
70+
}
71+
4572
@Transactional(readOnly = true)
4673
override fun findBySessionId(sessionId: String): Optional<StoredSession> {
4774
val results = graphObjectManager.loadAll(StoredSession::class.java)
@@ -72,12 +99,18 @@ open class SessionRepositoryImpl(
7299
}
73100

74101
@Transactional
75-
override fun addMessage(sessionId: String, message: StoredMessage): StoredSession {
76-
logger.debug("Adding message {} to session {}", message.messageId, sessionId)
102+
override fun addMessage(sessionId: String, messageData: MessageData, author: SessionUser?): StoredSession {
103+
logger.debug("Adding message {} to session {}", messageData.messageId, sessionId)
77104

78105
val session = findBySessionId(sessionId).orElseThrow {
79106
IllegalArgumentException("Session not found: $sessionId")
80107
}
108+
109+
val message = StoredMessage(
110+
message = messageData,
111+
author = author
112+
)
113+
81114
val updated = session.withMessage(message)
82115
return graphObjectManager.save(updated)
83116
}

src/test/kotlin/com/embabel/chat/store/TestConfiguration.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package com.embabel.chat.store
22

33
import com.embabel.chat.store.model.SessionUser
44
import com.embabel.chat.store.model.TestSessionUser
5+
import com.embabel.chat.store.repository.ChatSessionRepository
6+
import com.embabel.chat.store.repository.ChatSessionRepositoryImpl
57
import org.drivine.autoconfigure.EnableDrivine
68
import org.drivine.autoconfigure.EnableDrivineTestConfig
79
import org.drivine.manager.GraphObjectManager
@@ -47,4 +49,9 @@ class TestApplication {
4749
fun graphObjectManager(factory: GraphObjectManagerFactory): GraphObjectManager {
4850
return factory.get("neo")
4951
}
52+
53+
@Bean
54+
fun chatSessionRepository(graphObjectManager: GraphObjectManager): ChatSessionRepository {
55+
return ChatSessionRepositoryImpl(graphObjectManager)
56+
}
5057
}

0 commit comments

Comments
 (0)