ignore: v2 experiments

This commit is contained in:
Dax Raad
2026-04-14 14:23:30 -04:00
parent a53fae1511
commit 6ce5c01b1a
11 changed files with 1306 additions and 120 deletions

View File

@@ -0,0 +1 @@
export namespace SessionCommon {}

View File

@@ -1,114 +1,51 @@
import { Identifier } from "@/id/id"
import { Database } from "@/node"
import type { SessionID } from "@/session/schema"
import { SessionEntryTable } from "@/session/session.sql"
import { withStatics } from "@/util/schema"
import { Context, DateTime, Effect, Layer, Schema } from "effect"
import { eq } from "../storage/db"
import { Schema } from "effect"
import { SessionEvent } from "./session-event"
import { produce } from "immer"
export namespace SessionEntry {
export const ID = Schema.String.pipe(Schema.brand("Session.Entry.ID")).pipe(
withStatics((s) => ({
create: () => s.make(Identifier.ascending("entry")),
prefix: "ent",
})),
)
export const ID = SessionEvent.ID
export type ID = Schema.Schema.Type<typeof ID>
const Base = {
id: ID,
id: SessionEvent.ID,
metadata: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
time: Schema.Struct({
created: Schema.DateTimeUtc,
}),
}
export class Source extends Schema.Class<Source>("Session.Entry.Source")({
start: Schema.Number,
end: Schema.Number,
text: Schema.String,
}) {}
export class FileAttachment extends Schema.Class<FileAttachment>("Session.Entry.File.Attachment")({
uri: Schema.String,
mime: Schema.String,
name: Schema.String.pipe(Schema.optional),
description: Schema.String.pipe(Schema.optional),
source: Source.pipe(Schema.optional),
}) {
static create(url: string) {
return new FileAttachment({
uri: url,
mime: "text/plain",
})
}
}
export class AgentAttachment extends Schema.Class<AgentAttachment>("Session.Entry.Agent.Attachment")({
name: Schema.String,
source: Source.pipe(Schema.optional),
}) {}
export class User extends Schema.Class<User>("Session.Entry.User")({
...Base,
text: SessionEvent.Prompt.fields.text,
files: SessionEvent.Prompt.fields.files,
agents: SessionEvent.Prompt.fields.agents,
type: Schema.Literal("user"),
text: Schema.String,
files: Schema.Array(FileAttachment).pipe(Schema.optional),
agents: Schema.Array(AgentAttachment).pipe(Schema.optional),
time: Schema.Struct({
created: Schema.DateTimeUtc,
}),
}) {
static create(input: { text: User["text"]; files?: User["files"]; agents?: User["agents"] }) {
const msg = new User({
id: ID.create(),
static fromEvent(event: SessionEvent.Prompt) {
return new User({
id: event.id,
type: "user",
...input,
time: {
created: Effect.runSync(DateTime.now),
},
metadata: event.metadata,
text: event.text,
files: event.files,
agents: event.agents,
time: { created: event.timestamp },
})
return msg
}
}
export class Synthetic extends Schema.Class<Synthetic>("Session.Entry.Synthetic")({
...SessionEvent.Synthetic.fields,
...Base,
type: Schema.Literal("synthetic"),
text: Schema.String,
}) {}
export class Request extends Schema.Class<Request>("Session.Entry.Request")({
...Base,
type: Schema.Literal("start"),
model: Schema.Struct({
id: Schema.String,
providerID: Schema.String,
variant: Schema.String.pipe(Schema.optional),
}),
}) {}
export class Text extends Schema.Class<Text>("Session.Entry.Text")({
...Base,
type: Schema.Literal("text"),
text: Schema.String,
time: Schema.Struct({
...Base.time.fields,
completed: Schema.DateTimeUtc.pipe(Schema.optional),
}),
}) {}
export class Reasoning extends Schema.Class<Reasoning>("Session.Entry.Reasoning")({
...Base,
type: Schema.Literal("reasoning"),
text: Schema.String,
time: Schema.Struct({
...Base.time.fields,
completed: Schema.DateTimeUtc.pipe(Schema.optional),
}),
}) {}
export class ToolStatePending extends Schema.Class<ToolStatePending>("Session.Entry.ToolState.Pending")({
status: Schema.Literal("pending"),
input: Schema.Record(Schema.String, Schema.Unknown),
raw: Schema.String,
input: Schema.String,
}) {}
export class ToolStateRunning extends Schema.Class<ToolStateRunning>("Session.Entry.ToolState.Running")({
@@ -124,7 +61,7 @@ export namespace SessionEntry {
output: Schema.String,
title: Schema.String,
metadata: Schema.Record(Schema.String, Schema.Unknown),
attachments: Schema.Array(FileAttachment).pipe(Schema.optional),
attachments: SessionEvent.FileAttachment.pipe(Schema.Array, Schema.optional),
}) {}
export class ToolStateError extends Schema.Class<ToolStateError>("Session.Entry.ToolState.Error")({
@@ -132,34 +69,42 @@ export namespace SessionEntry {
input: Schema.Record(Schema.String, Schema.Unknown),
error: Schema.String,
metadata: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
time: Schema.Struct({
start: Schema.Number,
end: Schema.Number,
}),
}) {}
export const ToolState = Schema.Union([ToolStatePending, ToolStateRunning, ToolStateCompleted, ToolStateError])
export type ToolState = Schema.Schema.Type<typeof ToolState>
export class Tool extends Schema.Class<Tool>("Session.Entry.Tool")({
...Base,
export class AssistantTool extends Schema.Class<AssistantTool>("Session.Entry.Assistant.Tool")({
type: Schema.Literal("tool"),
callID: Schema.String,
name: Schema.String,
state: ToolState,
time: Schema.Struct({
...Base.time.fields,
created: Schema.DateTimeUtc,
ran: Schema.DateTimeUtc.pipe(Schema.optional),
completed: Schema.DateTimeUtc.pipe(Schema.optional),
pruned: Schema.DateTimeUtc.pipe(Schema.optional),
}),
}) {}
export class Complete extends Schema.Class<Complete>("Session.Entry.Complete")({
export class AssistantText extends Schema.Class<AssistantText>("Session.Entry.Assistant.Text")({
type: Schema.Literal("text"),
text: Schema.String,
}) {}
export class AssistantReasoning extends Schema.Class<AssistantReasoning>("Session.Entry.Assistant.Reasoning")({
type: Schema.Literal("reasoning"),
text: Schema.String,
}) {}
export const AssistantContent = Schema.Union([AssistantText, AssistantReasoning, AssistantTool])
export type AssistantContent = Schema.Schema.Type<typeof AssistantContent>
export class Assistant extends Schema.Class<Assistant>("Session.Entry.Assistant")({
...Base,
type: Schema.Literal("complete"),
cost: Schema.Number,
reason: Schema.String,
type: Schema.Literal("assistant"),
content: AssistantContent.pipe(Schema.Array),
cost: Schema.Number.pipe(Schema.optional),
tokens: Schema.Struct({
input: Schema.Number,
output: Schema.Number,
@@ -168,30 +113,174 @@ export namespace SessionEntry {
read: Schema.Number,
write: Schema.Number,
}),
}).pipe(Schema.optional),
error: Schema.String.pipe(Schema.optional),
time: Schema.Struct({
created: Schema.DateTimeUtc,
completed: Schema.DateTimeUtc.pipe(Schema.optional),
}),
}) {}
export class Retry extends Schema.Class<Retry>("Session.Entry.Retry")({
...Base,
type: Schema.Literal("retry"),
attempt: Schema.Number,
error: Schema.String,
}) {}
export class Compaction extends Schema.Class<Compaction>("Session.Entry.Compaction")({
...Base,
...SessionEvent.Compacted.fields,
type: Schema.Literal("compaction"),
auto: Schema.Boolean,
overflow: Schema.Boolean.pipe(Schema.optional),
...Base,
}) {}
export const Entry = Schema.Union([User, Synthetic, Request, Tool, Text, Reasoning, Complete, Retry, Compaction], {
mode: "oneOf",
})
export const Entry = Schema.Union([User, Synthetic, Assistant, Compaction])
export type Entry = Schema.Schema.Type<typeof Entry>
export type Type = Entry["type"]
export type History = {
entries: Entry[]
pending: Entry[]
}
export function step(old: History, event: SessionEvent.Event): History {
return produce(old, (draft) => {
const lastAssistant = draft.entries.findLast((x) => x.type === "assistant")
const pendingAssistant = lastAssistant && !lastAssistant.time.completed ? lastAssistant : undefined
switch (event.type) {
case "prompt": {
if (pendingAssistant) {
// @ts-expect-error
draft.pending.push(User.fromEvent(event))
break
}
// @ts-expect-error
draft.entries.push(User.fromEvent(event))
break
}
case "step.started": {
if (pendingAssistant) pendingAssistant.time.completed = event.timestamp
draft.entries.push({
id: event.id,
type: "assistant",
time: {
created: event.timestamp,
},
content: [],
})
break
}
case "text.started": {
if (!pendingAssistant) break
pendingAssistant.content.push({
type: "text",
text: "",
})
break
}
case "text.delta": {
if (!pendingAssistant) break
const match = pendingAssistant.content.findLast((x) => x.type === "text")
if (match) match.text += event.delta
break
}
case "text.ended": {
break
}
case "tool.input.started": {
if (!pendingAssistant) break
pendingAssistant.content.push({
type: "tool",
callID: event.callID,
name: event.name,
time: {
created: event.timestamp,
},
state: {
status: "pending",
input: "",
},
})
break
}
case "tool.input.delta": {
if (!pendingAssistant) break
const match = pendingAssistant.content.findLast((x) => x.type === "tool")
if (match) match.state.input += event.delta
break
}
case "tool.input.ended": {
break
}
case "tool.called": {
if (!pendingAssistant) break
const match = pendingAssistant.content.findLast((x) => x.type === "tool")
if (match) {
match.time.ran = event.timestamp
match.state = {
status: "running",
input: event.input,
}
}
break
}
case "tool.success": {
if (!pendingAssistant) break
const match = pendingAssistant.content.findLast((x) => x.type === "tool")
if (match && match.state.status === "running") {
match.state = {
status: "completed",
input: match.state.input,
output: event.output ?? "",
title: event.title,
metadata: event.metadata ?? {},
// @ts-expect-error
attachments: event.attachments ?? [],
}
}
break
}
case "tool.error": {
if (!pendingAssistant) break
const match = pendingAssistant.content.findLast((x) => x.type === "tool")
if (match && match.state.status === "running") {
match.state = {
status: "error",
error: event.error,
input: match.state.input,
metadata: event.metadata ?? {},
}
}
break
}
case "reasoning.started": {
if (!pendingAssistant) break
pendingAssistant.content.push({
type: "reasoning",
text: "",
})
break
}
case "reasoning.delta": {
if (!pendingAssistant) break
const match = pendingAssistant.content.findLast((x) => x.type === "reasoning")
if (match) match.text += event.delta
break
}
case "reasoning.ended": {
if (!pendingAssistant) break
const match = pendingAssistant.content.findLast((x) => x.type === "reasoning")
if (match) match.text = event.text
break
}
case "step.ended": {
if (!pendingAssistant) break
pendingAssistant.time.completed = event.timestamp
pendingAssistant.cost = event.cost
pendingAssistant.tokens = event.tokens
break
}
}
})
}
/*
export interface Interface {
readonly decode: (row: typeof SessionEntryTable.$inferSelect) => Entry
readonly fromSession: (sessionID: SessionID) => Effect.Effect<Entry[], never>
@@ -224,4 +313,5 @@ export namespace SessionEntry {
})
}),
)
*/
}

View File

@@ -0,0 +1,443 @@
import { Identifier } from "@/id/id"
import { withStatics } from "@/util/schema"
import * as DateTime from "effect/DateTime"
import { Schema } from "effect"
export namespace SessionEvent {
export const ID = Schema.String.pipe(
Schema.brand("Session.Event.ID"),
withStatics((s) => ({
create: () => s.make(Identifier.create("evt", "ascending")),
})),
)
export type ID = Schema.Schema.Type<typeof ID>
type Stamp = Schema.Schema.Type<typeof Schema.DateTimeUtc>
type BaseInput = {
id?: ID
metadata?: Record<string, unknown>
timestamp?: Stamp
}
const Base = {
id: ID,
metadata: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
timestamp: Schema.DateTimeUtc,
}
export class Source extends Schema.Class<Source>("Session.Event.Source")({
start: Schema.Number,
end: Schema.Number,
text: Schema.String,
}) {}
export class FileAttachment extends Schema.Class<FileAttachment>("Session.Event.FileAttachment")({
uri: Schema.String,
mime: Schema.String,
name: Schema.String.pipe(Schema.optional),
description: Schema.String.pipe(Schema.optional),
source: Source.pipe(Schema.optional),
}) {
static create(input: FileAttachment) {
return new FileAttachment({
...input,
})
}
}
export class AgentAttachment extends Schema.Class<AgentAttachment>("Session.Event.AgentAttachment")({
name: Schema.String,
source: Source.pipe(Schema.optional),
}) {}
export class Prompt extends Schema.Class<Prompt>("Session.Event.Prompt")({
...Base,
type: Schema.Literal("prompt"),
text: Schema.String,
files: Schema.Array(FileAttachment).pipe(Schema.optional),
agents: Schema.Array(AgentAttachment).pipe(Schema.optional),
}) {
static create(input: BaseInput & { text: string; files?: FileAttachment[]; agents?: AgentAttachment[] }) {
return new Prompt({
id: input.id ?? ID.create(),
type: "prompt",
timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()),
metadata: input.metadata,
text: input.text,
files: input.files,
agents: input.agents,
})
}
}
export class Synthetic extends Schema.Class<Synthetic>("Session.Event.Synthetic")({
...Base,
type: Schema.Literal("synthetic"),
text: Schema.String,
}) {
static create(input: BaseInput & { text: string }) {
return new Synthetic({
id: input.id ?? ID.create(),
type: "synthetic",
timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()),
metadata: input.metadata,
text: input.text,
})
}
}
export namespace Step {
export class Started extends Schema.Class<Started>("Session.Event.Step.Started")({
...Base,
type: Schema.Literal("step.started"),
model: Schema.Struct({
id: Schema.String,
providerID: Schema.String,
variant: Schema.String.pipe(Schema.optional),
}),
}) {
static create(input: BaseInput & { model: { id: string; providerID: string; variant?: string } }) {
return new Started({
id: input.id ?? ID.create(),
type: "step.started",
timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()),
metadata: input.metadata,
model: input.model,
})
}
}
export class Ended extends Schema.Class<Ended>("Session.Event.Step.Ended")({
...Base,
type: Schema.Literal("step.ended"),
reason: Schema.String,
cost: Schema.Number,
tokens: Schema.Struct({
input: Schema.Number,
output: Schema.Number,
reasoning: Schema.Number,
cache: Schema.Struct({
read: Schema.Number,
write: Schema.Number,
}),
}),
}) {
static create(input: BaseInput & { reason: string; cost: number; tokens: Ended["tokens"] }) {
return new Ended({
id: input.id ?? ID.create(),
type: "step.ended",
timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()),
metadata: input.metadata,
reason: input.reason,
cost: input.cost,
tokens: input.tokens,
})
}
}
}
export namespace Text {
export class Started extends Schema.Class<Started>("Session.Event.Text.Started")({
...Base,
type: Schema.Literal("text.started"),
}) {
static create(input: BaseInput = {}) {
return new Started({
id: input.id ?? ID.create(),
type: "text.started",
timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()),
metadata: input.metadata,
})
}
}
export class Delta extends Schema.Class<Delta>("Session.Event.Text.Delta")({
...Base,
type: Schema.Literal("text.delta"),
delta: Schema.String,
}) {
static create(input: BaseInput & { delta: string }) {
return new Delta({
id: input.id ?? ID.create(),
type: "text.delta",
timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()),
metadata: input.metadata,
delta: input.delta,
})
}
}
export class Ended extends Schema.Class<Ended>("Session.Event.Text.Ended")({
...Base,
type: Schema.Literal("text.ended"),
text: Schema.String,
}) {
static create(input: BaseInput & { text: string }) {
return new Ended({
id: input.id ?? ID.create(),
type: "text.ended",
timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()),
metadata: input.metadata,
text: input.text,
})
}
}
}
export namespace Reasoning {
export class Started extends Schema.Class<Started>("Session.Event.Reasoning.Started")({
...Base,
type: Schema.Literal("reasoning.started"),
}) {
static create(input: BaseInput = {}) {
return new Started({
id: input.id ?? ID.create(),
type: "reasoning.started",
timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()),
metadata: input.metadata,
})
}
}
export class Delta extends Schema.Class<Delta>("Session.Event.Reasoning.Delta")({
...Base,
type: Schema.Literal("reasoning.delta"),
delta: Schema.String,
}) {
static create(input: BaseInput & { delta: string }) {
return new Delta({
id: input.id ?? ID.create(),
type: "reasoning.delta",
timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()),
metadata: input.metadata,
delta: input.delta,
})
}
}
export class Ended extends Schema.Class<Ended>("Session.Event.Reasoning.Ended")({
...Base,
type: Schema.Literal("reasoning.ended"),
text: Schema.String,
}) {
static create(input: BaseInput & { text: string }) {
return new Ended({
id: input.id ?? ID.create(),
type: "reasoning.ended",
timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()),
metadata: input.metadata,
text: input.text,
})
}
}
}
export namespace Tool {
export namespace Input {
export class Started extends Schema.Class<Started>("Session.Event.Tool.Input.Started")({
...Base,
callID: Schema.String,
name: Schema.String,
type: Schema.Literal("tool.input.started"),
}) {
static create(input: BaseInput & { callID: string; name: string }) {
return new Started({
id: input.id ?? ID.create(),
type: "tool.input.started",
timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()),
metadata: input.metadata,
callID: input.callID,
name: input.name,
})
}
}
export class Delta extends Schema.Class<Delta>("Session.Event.Tool.Input.Delta")({
...Base,
callID: Schema.String,
type: Schema.Literal("tool.input.delta"),
delta: Schema.String,
}) {
static create(input: BaseInput & { callID: string; delta: string }) {
return new Delta({
id: input.id ?? ID.create(),
type: "tool.input.delta",
timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()),
metadata: input.metadata,
callID: input.callID,
delta: input.delta,
})
}
}
export class Ended extends Schema.Class<Ended>("Session.Event.Tool.Input.Ended")({
...Base,
callID: Schema.String,
type: Schema.Literal("tool.input.ended"),
text: Schema.String,
}) {
static create(input: BaseInput & { callID: string; text: string }) {
return new Ended({
id: input.id ?? ID.create(),
type: "tool.input.ended",
timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()),
metadata: input.metadata,
callID: input.callID,
text: input.text,
})
}
}
}
export class Called extends Schema.Class<Called>("Session.Event.Tool.Called")({
...Base,
type: Schema.Literal("tool.called"),
callID: Schema.String,
tool: Schema.String,
input: Schema.Record(Schema.String, Schema.Unknown),
provider: Schema.Struct({
executed: Schema.Boolean,
metadata: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
}),
}) {
static create(
input: BaseInput & {
callID: string
tool: string
input: Record<string, unknown>
provider: Called["provider"]
},
) {
return new Called({
id: input.id ?? ID.create(),
type: "tool.called",
timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()),
metadata: input.metadata,
callID: input.callID,
tool: input.tool,
input: input.input,
provider: input.provider,
})
}
}
export class Success extends Schema.Class<Success>("Session.Event.Tool.Success")({
...Base,
type: Schema.Literal("tool.success"),
callID: Schema.String,
title: Schema.String,
output: Schema.String.pipe(Schema.optional),
attachments: Schema.Array(FileAttachment).pipe(Schema.optional),
provider: Schema.Struct({
executed: Schema.Boolean,
metadata: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
}),
}) {
static create(
input: BaseInput & {
callID: string
title: string
output?: string
attachments?: FileAttachment[]
provider: Success["provider"]
},
) {
return new Success({
id: input.id ?? ID.create(),
type: "tool.success",
timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()),
metadata: input.metadata,
callID: input.callID,
title: input.title,
output: input.output,
attachments: input.attachments,
provider: input.provider,
})
}
}
export class Error extends Schema.Class<Error>("Session.Event.Tool.Error")({
...Base,
type: Schema.Literal("tool.error"),
callID: Schema.String,
error: Schema.String,
provider: Schema.Struct({
executed: Schema.Boolean,
metadata: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional),
}),
}) {
static create(input: BaseInput & { callID: string; error: string; provider: Error["provider"] }) {
return new Error({
id: input.id ?? ID.create(),
type: "tool.error",
timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()),
metadata: input.metadata,
callID: input.callID,
error: input.error,
provider: input.provider,
})
}
}
}
export class Retried extends Schema.Class<Retried>("Session.Event.Retried")({
...Base,
type: Schema.Literal("retried"),
error: Schema.String,
}) {
static create(input: BaseInput & { error: string }) {
return new Retried({
id: input.id ?? ID.create(),
type: "retried",
timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()),
metadata: input.metadata,
error: input.error,
})
}
}
export class Compacted extends Schema.Class<Compacted>("Session.Event.Compated")({
...Base,
type: Schema.Literal("compacted"),
auto: Schema.Boolean,
overflow: Schema.Boolean.pipe(Schema.optional),
}) {
static create(input: BaseInput & { auto: boolean; overflow?: boolean }) {
return new Compacted({
id: input.id ?? ID.create(),
type: "compacted",
timestamp: input.timestamp ?? DateTime.makeUnsafe(Date.now()),
metadata: input.metadata,
auto: input.auto,
overflow: input.overflow,
})
}
}
export const Event = Schema.Union(
[
Prompt,
Synthetic,
Step.Started,
Step.Ended,
Text.Started,
Text.Delta,
Text.Ended,
Tool.Input.Started,
Tool.Input.Delta,
Tool.Input.Ended,
Tool.Called,
Tool.Success,
Tool.Error,
Reasoning.Started,
Reasoning.Delta,
Reasoning.Ended,
Retried,
Compacted,
],
{
mode: "oneOf",
},
)
export type Event = Schema.Schema.Type<typeof Event>
export type Type = Event["type"]
}