summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
authorKit Langton <[email protected]>2026-04-09 10:05:14 -0400
committerGitHub <[email protected]>2026-04-09 10:05:14 -0400
commit58a99916bb96edf5cf605dc03e1be1e4bacf9ff7 (patch)
tree50e7127390734f9747777fc5b9ee65066fff46de /packages
parentc29392d0857f11208753bd95be76c6069c070289 (diff)
downloadopencode-58a99916bb96edf5cf605dc03e1be1e4bacf9ff7.tar.gz
opencode-58a99916bb96edf5cf605dc03e1be1e4bacf9ff7.zip
fix: preserve text part timing in session processor (#21691)
Diffstat (limited to 'packages')
-rw-r--r--packages/opencode/src/session/processor.ts5
-rw-r--r--packages/opencode/test/session/processor-effect.test.ts89
2 files changed, 92 insertions, 2 deletions
diff --git a/packages/opencode/src/session/processor.ts b/packages/opencode/src/session/processor.ts
index d66e77490..497747a5d 100644
--- a/packages/opencode/src/session/processor.ts
+++ b/packages/opencode/src/session/processor.ts
@@ -352,7 +352,10 @@ export namespace SessionProcessor {
},
{ text: ctx.currentText.text },
)).text
- ctx.currentText.time = { start: Date.now(), end: Date.now() }
+ {
+ const end = Date.now()
+ ctx.currentText.time = { start: ctx.currentText.time?.start ?? end, end }
+ }
if (value.providerMetadata) ctx.currentText.metadata = value.providerMetadata
yield* session.updatePart(ctx.currentText)
ctx.currentText = undefined
diff --git a/packages/opencode/test/session/processor-effect.test.ts b/packages/opencode/test/session/processor-effect.test.ts
index f3a65b3ba..a3b335b6d 100644
--- a/packages/opencode/test/session/processor-effect.test.ts
+++ b/packages/opencode/test/session/processor-effect.test.ts
@@ -21,7 +21,7 @@ import { Log } from "../../src/util/log"
import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
import { provideTmpdirServer } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
-import { reply, TestLLMServer } from "../lib/llm-server"
+import { raw, reply, TestLLMServer } from "../lib/llm-server"
Log.init({ print: false })
@@ -218,6 +218,93 @@ it.live("session.processor effect tests capture llm input cleanly", () =>
),
)
+it.live("session.processor effect tests preserve text start time", () =>
+ provideTmpdirServer(
+ ({ dir, llm }) =>
+ Effect.gen(function* () {
+ const gate = defer<void>()
+ const { processors, session, provider } = yield* boot()
+
+ yield* llm.push(
+ raw({
+ head: [
+ {
+ id: "chatcmpl-test",
+ object: "chat.completion.chunk",
+ choices: [{ delta: { role: "assistant" } }],
+ },
+ {
+ id: "chatcmpl-test",
+ object: "chat.completion.chunk",
+ choices: [{ delta: { content: "hello" } }],
+ },
+ ],
+ wait: gate.promise,
+ tail: [
+ {
+ id: "chatcmpl-test",
+ object: "chat.completion.chunk",
+ choices: [{ delta: {}, finish_reason: "stop" }],
+ },
+ ],
+ }),
+ )
+
+ const chat = yield* session.create({})
+ const parent = yield* user(chat.id, "hi")
+ const msg = yield* assistant(chat.id, parent.id, path.resolve(dir))
+ const mdl = yield* provider.getModel(ref.providerID, ref.modelID)
+ const handle = yield* processors.create({
+ assistantMessage: msg,
+ sessionID: chat.id,
+ model: mdl,
+ })
+
+ const run = yield* handle
+ .process({
+ user: {
+ id: parent.id,
+ sessionID: chat.id,
+ role: "user",
+ time: parent.time,
+ agent: parent.agent,
+ model: { providerID: ref.providerID, modelID: ref.modelID },
+ } satisfies MessageV2.User,
+ sessionID: chat.id,
+ model: mdl,
+ agent: agent(),
+ system: [],
+ messages: [{ role: "user", content: "hi" }],
+ tools: {},
+ })
+ .pipe(Effect.forkChild)
+
+ yield* Effect.promise(async () => {
+ const stop = Date.now() + 500
+ while (Date.now() < stop) {
+ const text = MessageV2.parts(msg.id).find((part): part is MessageV2.TextPart => part.type === "text")
+ if (text?.time?.start) return
+ await Bun.sleep(10)
+ }
+ throw new Error("timed out waiting for text part")
+ })
+ yield* Effect.sleep("20 millis")
+ gate.resolve()
+
+ const exit = yield* Fiber.await(run)
+ const text = MessageV2.parts(msg.id).find((part): part is MessageV2.TextPart => part.type === "text")
+
+ expect(Exit.isSuccess(exit)).toBe(true)
+ expect(text?.text).toBe("hello")
+ expect(text?.time?.start).toBeDefined()
+ expect(text?.time?.end).toBeDefined()
+ if (!text?.time?.start || !text.time.end) return
+ expect(text.time.start).toBeLessThan(text.time.end)
+ }),
+ { git: true, config: (url) => providerCfg(url) },
+ ),
+)
+
it.live("session.processor effect tests stop after token overflow requests compaction", () =>
provideTmpdirServer(
({ dir, llm }) =>