Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
0bc7ca4
feat(zoo-gateway): cache model catalog per session with ETag revalida…
JamesRobert20 Aug 31, 2026
0d675ab
fix(zoo-gateway): resolve CI type errors and raise patch coverage
JamesRobert20 Aug 31, 2026
9de812b
test(zoo-gateway): cover remaining session cache and ETag paths
JamesRobert20 Aug 31, 2026
236196f
fix(zoo-gateway): address CodeRabbit review on session cache lifecycle
JamesRobert20 Aug 31, 2026
e1d4309
fix: keep auth session catalog during forced model refresh
JamesRobert20 Aug 31, 2026
8f9f7e9
fix(zoo-gateway): surface reasoning traces from delta stream
JamesRobert20 Sep 4, 2026
04b896a
fix(zoo-gateway): address edelauna review feedback
JamesRobert20 Sep 4, 2026
c29d17b
fix(zoo-gateway): address CodeRabbit review and kill survived mutations
JamesRobert20 Sep 4, 2026
00ff678
fix(model-cache): add generation counter to prevent stale fetch from …
JamesRobert20 Sep 4, 2026
006d32e
Merge remote-tracking branch 'origin/main' into fix/zoo-gateway-model…
JamesRobert20 Sep 4, 2026
1ac2f22
fix(model-cache): fix in-flight cleanup clobbering newer post-sign-ou…
JamesRobert20 Sep 4, 2026
65211c7
test(model-cache): kill survived mutants — cache bound, empty-entry g…
JamesRobert20 Sep 4, 2026
8c404cb
fix(model-cache): use const for fetchWithCleanup to satisfy prefer-co…
JamesRobert20 Sep 4, 2026
444e9c7
fix(task): await disposal cleanup (#1526)
roomote Sep 4, 2026
d95df41
fix(task): cover cleanup completion branches
roomote Sep 4, 2026
1141702
fix(task): preserve abort completion semantics
roomote Sep 4, 2026
f008344
test(task): cover disposal failure branches
roomote Sep 4, 2026
bd19346
test(task): fail fast on stalled disposal
roomote Sep 4, 2026
7d135f9
test(task): cover multi-task shutdown disposal
roomote Sep 4, 2026
7bddaf1
test(formal): model task cleanup protocol
roomote Sep 4, 2026
a54e4db
test(formal): model provider cleanup phases
roomote Sep 4, 2026
ca1f005
fix(task): simplify disposal promise memoization
roomote Sep 4, 2026
d602da4
test(task): assert rejected abort invocation
roomote Sep 4, 2026
6453af1
Merge branch 'fix/task-dispose-teardown-2o7nhyn77n069' into fix/zoo-g…
JamesRobert20 Sep 5, 2026
da9767b
Merge branch 'main' into fix/zoo-gateway-models-session-cache
JamesRobert20 Sep 5, 2026
ffef3bd
Merge branch 'main' into fix/zoo-gateway-models-session-cache
JamesRobert20 Sep 5, 2026
d173c0d
test(model-cache): kill session-cache mutants without resetModules
JamesRobert20 Sep 5, 2026
d979578
test(model-cache): kill remaining session-cache mutation survivors
JamesRobert20 Sep 5, 2026
6d1dbe7
test(model-cache): kill the 8 surviving mutants from mutation.json
JamesRobert20 Sep 5, 2026
b4e12e8
Merge branch 'main' into fix/zoo-gateway-models-session-cache
JamesRobert20 Sep 5, 2026
f3f566f
Update src/api/providers/fetchers/modelCache.ts
edelauna Sep 5, 2026
4d6acc2
Merge main into the Zoo Gateway catalog cache and keep reasoning on f…
JamesRobert20 Oct 20, 2026
83e2c2a
Fix the catalog-cache CI failures after updating the branch.
JamesRobert20 Oct 20, 2026
2ecea9a
Merge remote-tracking branch 'origin/main' into fix/zoo-gateway-model…
JamesRobert20 Oct 20, 2026
d8df7e3
Merge branch 'main' into fix/zoo-gateway-models-session-cache
JamesRobert20 Oct 20, 2026
a5d5e62
Merge branch 'main' into fix/zoo-gateway-models-session-cache
JamesRobert20 Oct 22, 2026
62f20ef
Merge branch 'main' into fix/zoo-gateway-models-session-cache
JamesRobert20 Oct 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions knip.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"ignoreDependencies": ["lint-staged"],
"ignoreBinaries": ["dotenvx"],
"ignoreExportsUsedInFile": true,
"tags": ["-@knipignore"],
"playwright": false,
"playwright-ct": false,
"workspaces": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ describe("model catalog adapters", () => {
fetcher.mockReset()
fetcher.mockResolvedValue(CATALOG)
}
fetchers.getZooGatewayModels.mockResolvedValue({ kind: "ok", models: CATALOG })
})

describe.each(cases)("$name", ({ create, fetcher, scope, expectedArgs, forwardsSignal }) => {
Expand Down
77 changes: 77 additions & 0 deletions src/api/providers/__tests__/zoo-gateway.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,83 @@ describe("ZooGatewayHandler", () => {
])
})

it("yields reasoning chunks from delta.reasoning and delta.reasoning_content before text", async () => {
mockCreate.mockImplementation(async () =>
asyncStreamFrom([
{
choices: [{ delta: { reasoning: "Let me think" }, index: 0 }],
},
{
choices: [{ delta: { reasoning_content: " carefully", content: "answer" }, index: 0 }],
},
]),
)

const handler = new ZooGatewayHandler(mockOptions)
const chunks = await collectStream(handler.createMessage("prompt", [{ role: "user", content: "Hi" }]))

expect(chunks).toEqual([
{ type: "reasoning", text: "Let me think" },
{ type: "reasoning", text: " carefully" },
{ type: "text", text: "answer" },
])
})

it("preserves reasoning on follow-up requests for non-Anthropic models", () => {
const handler = new ZooGatewayHandler({ ...mockOptions, zooGatewayModelId: "reflection/beam-free" })

expect(handler.getModel().info.preserveReasoning).toBe(true)
})

it("leaves Anthropic models on their existing thinking path", () => {
const handler = new ZooGatewayHandler(mockOptions)

expect(handler.getModel().info.preserveReasoning).toBeUndefined()
})

it("sends reasoning_content on the follow-up request when history has a reasoning block", async () => {
mockCreate.mockImplementation(async () => asyncStreamFrom([]))
const handler = new ZooGatewayHandler({ ...mockOptions, zooGatewayModelId: "reflection/beam-free" })
const history = [
{
role: "assistant" as const,
content: [
{ type: "reasoning" as const, text: "I need to read a file.", summary: [] },
{ type: "tool_use" as const, id: "call_abc", name: "read_file", input: { path: "foo.txt" } },
],
},
{
role: "user" as const,
content: [{ type: "tool_result" as const, tool_use_id: "call_abc", content: "file contents" }],
},
] as unknown as Parameters<ZooGatewayHandler["createMessage"]>[1]

await handler.createMessage("prompt", history).next()

const assistant = mockCreate.mock.calls[0][0].messages.find(
(message: { role?: string }) => message.role === "assistant",
)
expect(assistant.reasoning_content).toBe("I need to read a file.")
expect(assistant.tool_calls[0].id).toBe("call_abc")
expect(assistant.tool_calls[0].function.name).toBe("read_file")
})

it("does not yield a reasoning chunk when the delta has no reasoning text", async () => {
mockCreate.mockImplementation(async () =>
asyncStreamFrom([
{
choices: [{ delta: { content: "just text" }, index: 0 }],
},
]),
)

const handler = new ZooGatewayHandler(mockOptions)
const chunks = await collectStream(handler.createMessage("prompt", [{ role: "user", content: "Hi" }]))

expect(chunks).toEqual([{ type: "text", text: "just text" }])
expect(chunks.some((chunk) => chunk.type === "reasoning")).toBe(false)
})

it("throws the upstream reason when the gateway sends an in-stream error chunk", async () => {
mockCreate.mockImplementation(async () =>
asyncStreamFrom([
Expand Down
Loading
Loading