refactor(v2): simplify memory storage layer
This commit is contained in:
parent
479b49ffa6
commit
2743504e60
2 changed files with 84 additions and 86 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
import { Context, DateTime, Effect, Layer } from "effect"
|
import { DateTime, Effect, Layer } from "effect"
|
||||||
import { SessionMessage } from "@opencode-ai/core/session-message"
|
import { SessionMessage } from "@opencode-ai/core/session-message"
|
||||||
import { SessionStorage } from "./storage"
|
import { SessionStorage } from "./storage"
|
||||||
|
|
||||||
|
|
@ -7,86 +7,84 @@ export interface State {
|
||||||
readonly messages: Map<string, SessionMessage.Message[]>
|
readonly messages: Map<string, SessionMessage.Message[]>
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StateService extends Context.Service<StateService, State>()("@opencode/v2/session/StorageMemoryState") {}
|
const makeState = (): State => ({
|
||||||
|
|
||||||
const stateLayer = Layer.sync(StateService, () => ({
|
|
||||||
sessions: new Map(),
|
sessions: new Map(),
|
||||||
messages: new Map(),
|
messages: new Map(),
|
||||||
}))
|
})
|
||||||
|
|
||||||
const storageLayer = Layer.effect(
|
export const make = (state = makeState()) =>
|
||||||
SessionStorage.Service,
|
SessionStorage.Service.of({
|
||||||
Effect.gen(function* () {
|
get: (sessionID) => Effect.sync(() => state.sessions.get(sessionID)),
|
||||||
const state = yield* StateService
|
list: (input) =>
|
||||||
return SessionStorage.Service.of({
|
Effect.sync(() => {
|
||||||
get: (sessionID) => Effect.sync(() => state.sessions.get(sessionID)),
|
const direction = input.cursor?.direction ?? "next"
|
||||||
list: (input) =>
|
const order = SessionStorage.pageOrder(input.order ?? "desc", direction)
|
||||||
Effect.sync(() => {
|
const rows = Array.from(state.sessions.values())
|
||||||
const direction = input.cursor?.direction ?? "next"
|
.filter((row) => {
|
||||||
const order = SessionStorage.pageOrder(input.order ?? "desc", direction)
|
if (input.directory && row.directory !== input.directory) return false
|
||||||
const rows = Array.from(state.sessions.values())
|
if (input.path && row.path !== input.path && !row.path?.startsWith(`${input.path}/`)) return false
|
||||||
.filter((row) => {
|
if (input.workspaceID && row.workspaceID !== input.workspaceID) return false
|
||||||
if (input.directory && row.directory !== input.directory) return false
|
if (input.roots && row.parentID) return false
|
||||||
if (input.path && row.path !== input.path && !row.path?.startsWith(`${input.path}/`)) return false
|
if (input.start && DateTime.toEpochMillis(row.time.updated) < input.start) return false
|
||||||
if (input.workspaceID && row.workspaceID !== input.workspaceID) return false
|
if (input.search && !row.title.includes(input.search)) return false
|
||||||
if (input.roots && row.parentID) return false
|
if (!input.cursor) return true
|
||||||
if (input.start && DateTime.toEpochMillis(row.time.updated) < input.start) return false
|
return compareCursor(row.id, DateTime.toEpochMillis(row.time.updated), input.cursor, order)
|
||||||
if (input.search && !row.title.includes(input.search)) return false
|
})
|
||||||
if (!input.cursor) return true
|
.toSorted((a, b) =>
|
||||||
return compareCursor(row.id, DateTime.toEpochMillis(row.time.updated), input.cursor, order)
|
compareRows(
|
||||||
})
|
a.id,
|
||||||
.toSorted((a, b) =>
|
DateTime.toEpochMillis(a.time.updated),
|
||||||
compareRows(
|
b.id,
|
||||||
a.id,
|
DateTime.toEpochMillis(b.time.updated),
|
||||||
DateTime.toEpochMillis(a.time.updated),
|
order,
|
||||||
b.id,
|
),
|
||||||
DateTime.toEpochMillis(b.time.updated),
|
)
|
||||||
order,
|
const limited = input.limit === undefined ? rows : rows.slice(0, input.limit)
|
||||||
),
|
return direction === "previous" ? limited.toReversed() : limited
|
||||||
)
|
}),
|
||||||
const limited = input.limit === undefined ? rows : rows.slice(0, input.limit)
|
messages: (input) =>
|
||||||
return direction === "previous" ? limited.toReversed() : limited
|
Effect.sync(() => {
|
||||||
}),
|
const direction = input.cursor?.direction ?? "next"
|
||||||
messages: (input) =>
|
const order = SessionStorage.pageOrder(input.order ?? "desc", direction)
|
||||||
Effect.sync(() => {
|
const rows = (state.messages.get(input.sessionID) ?? [])
|
||||||
const direction = input.cursor?.direction ?? "next"
|
.filter((message) => {
|
||||||
const order = SessionStorage.pageOrder(input.order ?? "desc", direction)
|
if (!input.cursor) return true
|
||||||
const rows = (state.messages.get(input.sessionID) ?? [])
|
return compareCursor(message.id, DateTime.toEpochMillis(message.time.created), input.cursor, order)
|
||||||
.filter((message) => {
|
})
|
||||||
if (!input.cursor) return true
|
.toSorted((a, b) =>
|
||||||
return compareCursor(message.id, DateTime.toEpochMillis(message.time.created), input.cursor, order)
|
|
||||||
})
|
|
||||||
.toSorted((a, b) =>
|
|
||||||
compareRows(
|
|
||||||
a.id,
|
|
||||||
DateTime.toEpochMillis(a.time.created),
|
|
||||||
b.id,
|
|
||||||
DateTime.toEpochMillis(b.time.created),
|
|
||||||
order,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
const limited = input.limit === undefined ? rows : rows.slice(0, input.limit)
|
|
||||||
return direction === "previous" ? limited.toReversed() : limited
|
|
||||||
}),
|
|
||||||
context: (sessionID) =>
|
|
||||||
Effect.sync(() => {
|
|
||||||
const messages = (state.messages.get(sessionID) ?? []).toSorted((a, b) =>
|
|
||||||
compareRows(
|
compareRows(
|
||||||
a.id,
|
a.id,
|
||||||
DateTime.toEpochMillis(a.time.created),
|
DateTime.toEpochMillis(a.time.created),
|
||||||
b.id,
|
b.id,
|
||||||
DateTime.toEpochMillis(b.time.created),
|
DateTime.toEpochMillis(b.time.created),
|
||||||
"asc",
|
order,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
const index = messages.findLastIndex((message) => message.type === "compaction")
|
const limited = input.limit === undefined ? rows : rows.slice(0, input.limit)
|
||||||
return index === -1 ? messages : messages.slice(index)
|
return direction === "previous" ? limited.toReversed() : limited
|
||||||
}),
|
}),
|
||||||
})
|
context: (sessionID) =>
|
||||||
}),
|
Effect.sync(() => {
|
||||||
)
|
const messages = (state.messages.get(sessionID) ?? []).toSorted((a, b) =>
|
||||||
|
compareRows(
|
||||||
|
a.id,
|
||||||
|
DateTime.toEpochMillis(a.time.created),
|
||||||
|
b.id,
|
||||||
|
DateTime.toEpochMillis(b.time.created),
|
||||||
|
"asc",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
const index = messages.findLastIndex((message) => message.type === "compaction")
|
||||||
|
return index === -1 ? messages : messages.slice(index)
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
export const layer = storageLayer.pipe(Layer.provideMerge(stateLayer))
|
const storageLayer = Layer.sync(SessionStorage.Service, () => {
|
||||||
|
const state = makeState()
|
||||||
|
return make(state)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const layer = storageLayer
|
||||||
|
|
||||||
export const defaultLayer = layer
|
export const defaultLayer = layer
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,11 @@ const sessionB = SessionID.make("ses_storage_b")
|
||||||
const sessionC = SessionID.make("ses_storage_c")
|
const sessionC = SessionID.make("ses_storage_c")
|
||||||
const sessionD = SessionID.make("ses_storage_d")
|
const sessionD = SessionID.make("ses_storage_d")
|
||||||
const encodeMessage = Schema.encodeSync(SessionMessage.Message)
|
const encodeMessage = Schema.encodeSync(SessionMessage.Message)
|
||||||
|
const memoryState: SessionStorageMemory.State = {
|
||||||
|
sessions: new Map(),
|
||||||
|
messages: new Map(),
|
||||||
|
}
|
||||||
|
const memoryLayer = Layer.sync(SessionStorage.Service, () => SessionStorageMemory.make(memoryState))
|
||||||
|
|
||||||
interface Seeds<R> {
|
interface Seeds<R> {
|
||||||
readonly reset: Effect.Effect<void, never, R>
|
readonly reset: Effect.Effect<void, never, R>
|
||||||
|
|
@ -162,29 +167,27 @@ const sqlSeeds: Seeds<never> = {
|
||||||
|
|
||||||
sessionStorageContract("SessionStorageSql", SessionStorageSql.defaultLayer, sqlSeeds)
|
sessionStorageContract("SessionStorageSql", SessionStorageSql.defaultLayer, sqlSeeds)
|
||||||
|
|
||||||
const memorySeeds: Seeds<SessionStorageMemory.StateService> = {
|
const memorySeeds: Seeds<never> = {
|
||||||
reset: Effect.gen(function* () {
|
reset: Effect.sync(() => {
|
||||||
const memoryState = yield* SessionStorageMemory.StateService
|
|
||||||
memoryState.sessions.clear()
|
memoryState.sessions.clear()
|
||||||
memoryState.messages.clear()
|
memoryState.messages.clear()
|
||||||
}),
|
}),
|
||||||
project: Effect.void,
|
project: Effect.void,
|
||||||
session: (input) =>
|
session: (input) =>
|
||||||
Effect.gen(function* () {
|
Effect.sync(() => {
|
||||||
const memoryState = yield* SessionStorageMemory.StateService
|
|
||||||
memoryState.sessions.set(input.id, makeSessionRow(input))
|
memoryState.sessions.set(input.id, makeSessionRow(input))
|
||||||
}),
|
}),
|
||||||
userMessage: (input) =>
|
userMessage: (input) =>
|
||||||
Effect.gen(function* () {
|
Effect.sync(() => {
|
||||||
yield* appendMemoryMessage(makeUserMessage(input))
|
appendMemoryMessage(makeUserMessage(input))
|
||||||
}),
|
}),
|
||||||
compaction: (input) =>
|
compaction: (input) =>
|
||||||
Effect.gen(function* () {
|
Effect.sync(() => {
|
||||||
yield* appendMemoryMessage(makeCompaction(input))
|
appendMemoryMessage(makeCompaction(input))
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionStorageContract("SessionStorageMemory", SessionStorageMemory.layer, memorySeeds)
|
sessionStorageContract("SessionStorageMemory", memoryLayer, memorySeeds)
|
||||||
|
|
||||||
function seedProject() {
|
function seedProject() {
|
||||||
Database.use((db) =>
|
Database.use((db) =>
|
||||||
|
|
@ -324,10 +327,7 @@ function seedMessage(
|
||||||
}
|
}
|
||||||
|
|
||||||
function appendMemoryMessage(message: SessionMessage.Message) {
|
function appendMemoryMessage(message: SessionMessage.Message) {
|
||||||
return Effect.gen(function* () {
|
const current = memoryState.messages.get(sessionA) ?? []
|
||||||
const memoryState = yield* SessionStorageMemory.StateService
|
current.push(message)
|
||||||
const current = memoryState.messages.get(sessionA) ?? []
|
memoryState.messages.set(sessionA, current)
|
||||||
current.push(message)
|
|
||||||
memoryState.messages.set(sessionA, current)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue