refactor(schema): apply session review decisions (#35793)
This commit is contained in:
parent
d27746e5b3
commit
ed6ad272ec
142 changed files with 4239 additions and 3174 deletions
|
|
@ -11,6 +11,7 @@ import {
|
|||
type ProviderErrorEvent,
|
||||
} from "@opencode-ai/llm"
|
||||
import { SessionError } from "@opencode-ai/schema/session-error"
|
||||
import { Money } from "@opencode-ai/schema/money"
|
||||
import { Cause, Effect, Exit, Fiber, FiberSet, Layer, Option, Semaphore, Stream } from "effect"
|
||||
import { AgentV2 } from "../../agent"
|
||||
import { Config } from "../../config"
|
||||
|
|
@ -67,13 +68,13 @@ export function calculateCost(costs: ModelV2.Info["cost"], tokens: StepTokens) {
|
|||
.filter((cost) => cost.tier?.type === "context" && context > cost.tier.size)
|
||||
.toSorted((a, b) => (b.tier?.size ?? 0) - (a.tier?.size ?? 0))[0]
|
||||
const cost = tier ?? costs.find((cost) => cost.tier === undefined)
|
||||
if (!cost) return 0
|
||||
return (
|
||||
if (!cost) return Money.USD.zero
|
||||
return Money.USD.make(
|
||||
(tokens.input * cost.input +
|
||||
(tokens.output + tokens.reasoning) * cost.output +
|
||||
tokens.cache.read * cost.cache.read +
|
||||
tokens.cache.write * cost.cache.write) /
|
||||
1_000_000
|
||||
1_000_000,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -165,7 +166,7 @@ const layer = Layer.effect(
|
|||
for (const message of yield* store.context(sessionID)) {
|
||||
if (message.type !== "assistant") continue
|
||||
for (const tool of message.content) {
|
||||
if (tool.type !== "tool" || (tool.state.status !== "pending" && tool.state.status !== "running")) continue
|
||||
if (tool.type !== "tool" || (tool.state.status !== "streaming" && tool.state.status !== "running")) continue
|
||||
yield* events.publish(SessionEvent.Tool.Failed, {
|
||||
sessionID,
|
||||
assistantMessageID: message.id,
|
||||
|
|
@ -300,8 +301,7 @@ const layer = Layer.effect(
|
|||
// Durable publishes are serialized so tool fibers and step settlement never interleave
|
||||
// mid-event.
|
||||
const serialized = <A, E, R>(effect: Effect.Effect<A, E, R>) => publication.withPermit(effect)
|
||||
const publish = (event: LLMEvent, outputPaths: ReadonlyArray<string> = [], error?: SessionError.Error) =>
|
||||
serialized(publisher.publish(event, outputPaths, error))
|
||||
const publish = (event: LLMEvent, error?: SessionError.Error) => serialized(publisher.publish(event, error))
|
||||
let overflowFailure: ProviderErrorEvent | undefined
|
||||
const providerStream = llm.stream(hookedRequest).pipe(
|
||||
Stream.runForEach((event) =>
|
||||
|
|
@ -359,7 +359,6 @@ const layer = Layer.effect(
|
|||
result: settlement.result,
|
||||
output: settlement.output,
|
||||
}),
|
||||
settlement.outputPaths ?? [],
|
||||
settlement.error,
|
||||
).pipe(
|
||||
Effect.andThen(
|
||||
|
|
@ -599,12 +598,30 @@ const layer = Layer.effect(
|
|||
return yield* compaction.compactManual({
|
||||
session,
|
||||
messages: yield* store.context(sessionID),
|
||||
inputID: pending.id,
|
||||
})
|
||||
}),
|
||||
).pipe(Effect.exit)
|
||||
if (Exit.isSuccess(compacted) && compacted.value) return true
|
||||
yield* events.publish(SessionEvent.Compaction.Failed, { sessionID })
|
||||
if (Exit.isFailure(compacted)) return yield* Effect.failCause(compacted.cause)
|
||||
if (Exit.isFailure(compacted)) {
|
||||
const unsettled = yield* SessionInput.pendingCompaction(db, sessionID)
|
||||
if (unsettled)
|
||||
yield* events.publish(SessionEvent.Compaction.Failed, {
|
||||
sessionID,
|
||||
reason: "manual",
|
||||
error: { type: "compaction.failed", message: Cause.pretty(compacted.cause) },
|
||||
inputID: unsettled.id,
|
||||
})
|
||||
return yield* Effect.failCause(compacted.cause)
|
||||
}
|
||||
const unsettled = yield* SessionInput.pendingCompaction(db, sessionID)
|
||||
if (unsettled)
|
||||
yield* events.publish(SessionEvent.Compaction.Failed, {
|
||||
sessionID,
|
||||
reason: "manual",
|
||||
error: { type: "compaction.failed", message: "Compaction could not start" },
|
||||
inputID: unsettled.id,
|
||||
})
|
||||
return true
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,13 +6,16 @@ import { SessionEvent } from "../event"
|
|||
import { SessionMessage } from "../message"
|
||||
import { SessionSchema } from "../schema"
|
||||
import { SessionError } from "@opencode-ai/schema/session-error"
|
||||
import { Money } from "@opencode-ai/schema/money"
|
||||
import { AgentV2 } from "../../agent"
|
||||
import { Snapshot } from "../../snapshot"
|
||||
|
||||
type Input = {
|
||||
readonly sessionID: SessionSchema.ID
|
||||
readonly agent: string
|
||||
readonly agent: AgentV2.ID
|
||||
readonly model: ModelV2.Ref
|
||||
readonly provider: string
|
||||
readonly snapshot?: string
|
||||
readonly snapshot?: Snapshot.ID
|
||||
readonly assistantMessageID?: SessionMessage.ID
|
||||
}
|
||||
|
||||
|
|
@ -226,7 +229,7 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input)
|
|||
})
|
||||
|
||||
const publishStepFailure = Effect.fnUntraced(function* (usage?: {
|
||||
readonly cost: number
|
||||
readonly cost: Money.USD
|
||||
readonly tokens: ReturnType<typeof tokens>
|
||||
}) {
|
||||
if (stepFailed || stepFailure === undefined) return
|
||||
|
|
@ -265,11 +268,7 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input)
|
|||
return tool ? Effect.succeed(tool.assistantMessageID) : Effect.die(new Error(`Unknown tool call: ${callID}`))
|
||||
}
|
||||
|
||||
const publish = Effect.fn("SessionRunner.publishLLMEvent")(function* (
|
||||
event: LLMEvent,
|
||||
outputPaths: ReadonlyArray<string> = [],
|
||||
error?: SessionError.Error,
|
||||
) {
|
||||
const publish = Effect.fn("SessionRunner.publishLLMEvent")(function* (event: LLMEvent, error?: SessionError.Error) {
|
||||
switch (event.type) {
|
||||
case "step-start":
|
||||
yield* startAssistant()
|
||||
|
|
@ -395,7 +394,6 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input)
|
|||
assistantMessageID: tool.assistantMessageID,
|
||||
callID: event.id,
|
||||
...result,
|
||||
outputPaths,
|
||||
...(executed ? { result: event.result } : {}),
|
||||
executed,
|
||||
resultState,
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ const providerMetadata = (
|
|||
): ProviderMetadata | undefined => (state === undefined ? undefined : { [provider]: state })
|
||||
|
||||
const toolInput = (tool: SessionMessage.AssistantTool) =>
|
||||
tool.state.status === "pending"
|
||||
tool.state.status === "streaming"
|
||||
? Option.getOrElse(decodeToolInput(tool.state.input), () => tool.state.input)
|
||||
: tool.state.input
|
||||
|
||||
|
|
@ -168,7 +168,7 @@ const assistant = (message: SessionMessage.Assistant, model: ModelV2.Ref) => {
|
|||
]
|
||||
}
|
||||
|
||||
function toLLMMessage(message: SessionMessage.Message, model: ModelV2.Ref): Message[] {
|
||||
function toLLMMessage(message: SessionMessage.Info, model: ModelV2.Ref): Message[] {
|
||||
switch (message.type) {
|
||||
case "agent-switched":
|
||||
case "model-switched":
|
||||
|
|
@ -202,7 +202,7 @@ function toLLMMessage(message: SessionMessage.Message, model: ModelV2.Ref): Mess
|
|||
Message.make({
|
||||
id: message.id,
|
||||
role: "user",
|
||||
content: `Shell command: ${message.shell.command}\n\n${message.output?.output ?? ""}`,
|
||||
content: `Shell command: ${message.command}\n\n${message.output?.output ?? ""}`,
|
||||
metadata: message.metadata,
|
||||
}),
|
||||
]
|
||||
|
|
@ -232,5 +232,5 @@ ${message.recent}
|
|||
}
|
||||
|
||||
/** Translate projected V2 Session history into canonical @opencode-ai/llm context. */
|
||||
export const toLLMMessages = (messages: readonly SessionMessage.Message[], model: ModelV2.Ref) =>
|
||||
export const toLLMMessages = (messages: readonly SessionMessage.Info[], model: ModelV2.Ref) =>
|
||||
messages.flatMap((message) => toLLMMessage(message, model))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue