Skip to content

Commit 58a9991

Browse files
authored
fix: preserve text part timing in session processor (#21691)
1 parent c29392d commit 58a9991

2 files changed

Lines changed: 92 additions & 2 deletions

File tree

packages/opencode/src/session/processor.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,10 @@ export namespace SessionProcessor {
352352
},
353353
{ text: ctx.currentText.text },
354354
)).text
355-
ctx.currentText.time = { start: Date.now(), end: Date.now() }
355+
{
356+
const end = Date.now()
357+
ctx.currentText.time = { start: ctx.currentText.time?.start ?? end, end }
358+
}
356359
if (value.providerMetadata) ctx.currentText.metadata = value.providerMetadata
357360
yield* session.updatePart(ctx.currentText)
358361
ctx.currentText = undefined

packages/opencode/test/session/processor-effect.test.ts

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { Log } from "../../src/util/log"
2121
import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
2222
import { provideTmpdirServer } from "../fixture/fixture"
2323
import { testEffect } from "../lib/effect"
24-
import { reply, TestLLMServer } from "../lib/llm-server"
24+
import { raw, reply, TestLLMServer } from "../lib/llm-server"
2525

2626
Log.init({ print: false })
2727

@@ -218,6 +218,93 @@ it.live("session.processor effect tests capture llm input cleanly", () =>
218218
),
219219
)
220220

221+
it.live("session.processor effect tests preserve text start time", () =>
222+
provideTmpdirServer(
223+
({ dir, llm }) =>
224+
Effect.gen(function* () {
225+
const gate = defer<void>()
226+
const { processors, session, provider } = yield* boot()
227+
228+
yield* llm.push(
229+
raw({
230+
head: [
231+
{
232+
id: "chatcmpl-test",
233+
object: "chat.completion.chunk",
234+
choices: [{ delta: { role: "assistant" } }],
235+
},
236+
{
237+
id: "chatcmpl-test",
238+
object: "chat.completion.chunk",
239+
choices: [{ delta: { content: "hello" } }],
240+
},
241+
],
242+
wait: gate.promise,
243+
tail: [
244+
{
245+
id: "chatcmpl-test",
246+
object: "chat.completion.chunk",
247+
choices: [{ delta: {}, finish_reason: "stop" }],
248+
},
249+
],
250+
}),
251+
)
252+
253+
const chat = yield* session.create({})
254+
const parent = yield* user(chat.id, "hi")
255+
const msg = yield* assistant(chat.id, parent.id, path.resolve(dir))
256+
const mdl = yield* provider.getModel(ref.providerID, ref.modelID)
257+
const handle = yield* processors.create({
258+
assistantMessage: msg,
259+
sessionID: chat.id,
260+
model: mdl,
261+
})
262+
263+
const run = yield* handle
264+
.process({
265+
user: {
266+
id: parent.id,
267+
sessionID: chat.id,
268+
role: "user",
269+
time: parent.time,
270+
agent: parent.agent,
271+
model: { providerID: ref.providerID, modelID: ref.modelID },
272+
} satisfies MessageV2.User,
273+
sessionID: chat.id,
274+
model: mdl,
275+
agent: agent(),
276+
system: [],
277+
messages: [{ role: "user", content: "hi" }],
278+
tools: {},
279+
})
280+
.pipe(Effect.forkChild)
281+
282+
yield* Effect.promise(async () => {
283+
const stop = Date.now() + 500
284+
while (Date.now() < stop) {
285+
const text = MessageV2.parts(msg.id).find((part): part is MessageV2.TextPart => part.type === "text")
286+
if (text?.time?.start) return
287+
await Bun.sleep(10)
288+
}
289+
throw new Error("timed out waiting for text part")
290+
})
291+
yield* Effect.sleep("20 millis")
292+
gate.resolve()
293+
294+
const exit = yield* Fiber.await(run)
295+
const text = MessageV2.parts(msg.id).find((part): part is MessageV2.TextPart => part.type === "text")
296+
297+
expect(Exit.isSuccess(exit)).toBe(true)
298+
expect(text?.text).toBe("hello")
299+
expect(text?.time?.start).toBeDefined()
300+
expect(text?.time?.end).toBeDefined()
301+
if (!text?.time?.start || !text.time.end) return
302+
expect(text.time.start).toBeLessThan(text.time.end)
303+
}),
304+
{ git: true, config: (url) => providerCfg(url) },
305+
),
306+
)
307+
221308
it.live("session.processor effect tests stop after token overflow requests compaction", () =>
222309
provideTmpdirServer(
223310
({ dir, llm }) =>

0 commit comments

Comments
 (0)