Изменение выносит типы контента в :content-api и разделяет потоки событий.
This commit is contained in:
+56
-26
@@ -59,57 +59,87 @@ target-specific артефакты + общий `kotlinMultiplatform`.
|
||||
|
||||
## Основные типы
|
||||
|
||||
`:proto` — **тонкий** контракт: сами интерфейсы `Agent`/`Conversation`/`Message`,
|
||||
а общие типы содержимого и события живут в нижележащих модулях:
|
||||
`Content`/`MessageContext`/`MessageOrigin`/`TurnTokens` — в `:content-api`,
|
||||
`Event`/`OnlineEvent`/`OutboxStore`/`OnlineOutbox` — в `:outbox-api`.
|
||||
|
||||
```kotlin
|
||||
interface Agent {
|
||||
fun id: String
|
||||
suspend fun createConversation(title: String? = null): Conversation
|
||||
interface Agent : AutoCloseable {
|
||||
val id: String
|
||||
val info: AgentInfo
|
||||
val journal: JournalStore // append-only audit (read-only)
|
||||
val outbox: OutboxStore // durable-события: catchup+live по курсору
|
||||
val onlineOutbox: OnlineOutbox // live-only: стриминг, без курсора
|
||||
val conversationStore: ConversationStore
|
||||
fun createConversation(temp: Boolean): Conversation
|
||||
suspend fun getConversation(id: String): Conversation?
|
||||
suspend fun getConversations(offset: Int = 0): Flow<Conversation>
|
||||
suspend fun events(after: Instant): Flow<AgentEvent> // created/deleted/renamed
|
||||
suspend fun deleteConversation(id: String): Boolean
|
||||
suspend fun renameConversation(id: String, title: String?): Instant?
|
||||
}
|
||||
|
||||
interface Conversation : AutoCloseable {
|
||||
val id: String
|
||||
val updatedAt: Instant
|
||||
val isTemporal: Boolean
|
||||
val title: String?
|
||||
val isSupportImageInput: Boolean
|
||||
val isSupportImageOutput: Boolean
|
||||
suspend fun send(content: List<Content>): Flow<Event> // write+read вместе, как раньше
|
||||
suspend fun events(after: Instant): Flow<Event> // отдельная live-подписка
|
||||
suspend fun getMessages(offset: Int = 0): Flow<Message>
|
||||
suspend fun rename(title: String): Boolean
|
||||
fun interrupt()
|
||||
suspend fun send(content: List<Content>, context: MessageContext? = null) // fire-and-forget
|
||||
suspend fun getMessages(after: Instant, offset: Int, limit: Int): List<Message>
|
||||
suspend fun rename(title: String)
|
||||
suspend fun interrupt()
|
||||
}
|
||||
|
||||
// :content-api
|
||||
sealed interface Content {
|
||||
class Text(val body: String) : Content
|
||||
class Image(val data: ByteArray, val mime: String) : Content
|
||||
data class Text(val body: String) : Content
|
||||
data class Image(val data: ByteArray, val mime: String) : Content
|
||||
}
|
||||
enum class MessageOrigin { USER, SYSTEM, EVENT }
|
||||
data class MessageContext(origin: MessageOrigin, description: String?, sourceId: String?, metadata: JsonElement?)
|
||||
|
||||
// :proto
|
||||
sealed interface Message {
|
||||
val id: String
|
||||
val date: Instant
|
||||
interface Body : Message { val content: List<Content> }
|
||||
interface System : Message
|
||||
class UserMessage(...) : Body
|
||||
class AssistantMessage(...) : Body
|
||||
class ToolCall(...) : System
|
||||
class ToolResult(...) : System
|
||||
class UserMessage(id, content: List<Content>, date, context: MessageContext?) : Message
|
||||
class AssistantMessage(id, content: List<Content>, date, tokens: TurnTokens?, reasoning: String?) : Message
|
||||
class ToolCall(...) : Message
|
||||
class ToolResult(...) : Message
|
||||
class Error(...) : Message
|
||||
}
|
||||
|
||||
// :outbox-api — durable (перезапрашиваются по курсору `after`)
|
||||
sealed interface Event {
|
||||
enum ResponseType { TEXT, IMAGE }
|
||||
class StartReasoning(...) : Event
|
||||
class StartResponse(val type: ResponseType) : Event
|
||||
class AppendText(val body: String) : Event
|
||||
class AppendImage(val body: ByteArray, val mime: String) : Event
|
||||
class End(...) : Event
|
||||
class Interrupted(...) : Event
|
||||
class Error(val message: String, val code: Int? = null) : Event
|
||||
val date: Instant
|
||||
class UserMessage(date, id, content: List<Content>, context: MessageContext?) : Event
|
||||
class AssistantMessage(date, id, content: List<Content>, reasoning: String?, tokens: TurnTokens?) : Event
|
||||
class ToolCall(...) : Event
|
||||
class ToolResult(...) : Event
|
||||
class ToolFailed(...) : Event
|
||||
class Interrupted(date) : Event
|
||||
class Error(date, message, code) : Event
|
||||
class ConversationClosing(...) : Event
|
||||
class CompactionTriggered(...) : Event
|
||||
}
|
||||
|
||||
// :outbox-api — online (live-only, НИКОГДА не сохраняются)
|
||||
sealed interface OnlineEvent {
|
||||
val date: Instant
|
||||
enum ResponseType { TEXT, IMAGE }
|
||||
class Working(date) : OnlineEvent
|
||||
class End(date) : OnlineEvent
|
||||
class StartReasoning(date) : OnlineEvent
|
||||
class StartResponse(date, responseType: ResponseType) : OnlineEvent
|
||||
class AppendText(date, body: String) : OnlineEvent
|
||||
class AppendImage(date, body: ByteArray, mime: String) : OnlineEvent
|
||||
}
|
||||
```
|
||||
|
||||
Ход в терминах маркеров: онлайн `Working` → … → онлайн `End`; durable —
|
||||
`UserMessage` в начале и `AssistantMessage`/`Interrupted`/`Error` в конце.
|
||||
|
||||
## Чего здесь НЕТ
|
||||
|
||||
- Никакого HTTP/SSE/JSON. Это контракт. Сериализация живёт в `:server`
|
||||
|
||||
Reference in New Issue
Block a user