test(review): pin full-transcript history past 50 rows + changelog (PR #202)
Address the PR #202 review (approve-with-comments). The only actionable non-blocking item was the test-coverage suggestion: the source switch in AiChatService.handle from findRecent(chatId, ws, 50) to findAllByChat(chatId, ws) was not pinned by a test. handle() is a streaming method the project marks as not unit-testable, so cover the behavioral guarantee it now relies on at the repo/integration level — seed a chat of 60 messages and assert the default findAllByChat (exactly how handle calls it) returns the FULL transcript in chronological order, including the first turn the old 50-window would have dropped. Also document the behavior change under CHANGELOG [Unreleased] -> Changed. The two stability items (token-budget trim before streamText; O(N) history rebuild per turn) are deferred: the reviewer flagged both as non-blocking conscious trade-offs aligned with the PR's stated goal, and the trim is a larger architecture change out of scope for this follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -75,6 +75,15 @@ per-workspace rolling-day token budget.
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- **AI chat now feeds the model the full stored transcript.** The per-turn model
|
||||||
|
conversation was rebuilt from a sliding window of the 50 most recent stored
|
||||||
|
rows, which silently dropped the beginning of any longer chat. It is now
|
||||||
|
rebuilt from the complete non-deleted transcript in chronological order, so
|
||||||
|
the model sees every turn (a 5000-row backstop guards process memory — a
|
||||||
|
safety net far above any realistic chat, not a conversational limit). On a
|
||||||
|
very long chat this can eventually reach the model's context window; the
|
||||||
|
client already surfaces that as "start a new chat". (#202)
|
||||||
|
|
||||||
- **AI chat default provider is now `openai-compatible` (reasoning surfaced).**
|
- **AI chat default provider is now `openai-compatible` (reasoning surfaced).**
|
||||||
For the `openai` driver the chat provider defaults to the openai-compatible
|
For the `openai` driver the chat provider defaults to the openai-compatible
|
||||||
implementation, so a workspace pointing at z.ai/GLM/DeepSeek now streams the
|
implementation, so a workspace pointing at z.ai/GLM/DeepSeek now streams the
|
||||||
|
|||||||
@@ -267,4 +267,36 @@ describe('AiChatMessageRepo.update + sweepStreaming [integration]', () => {
|
|||||||
const all = await repo.findAllByChat(cappedChat, workspaceId, 100);
|
const all = await repo.findAllByChat(cappedChat, workspaceId, 100);
|
||||||
expect(all.map((r) => r.content)).toEqual(['m1-oldest', 'm2', 'm3-newest']);
|
expect(all.map((r) => r.content)).toEqual(['m1-oldest', 'm2', 'm3-newest']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('default findAllByChat returns the FULL transcript past 50 rows — no recent-tail window (#202)', async () => {
|
||||||
|
// PR #202 swapped the model-history rebuild in AiChatService.handle from
|
||||||
|
// findRecent(chatId, ws, 50) to findAllByChat(chatId, ws) WITHOUT a limit
|
||||||
|
// arg. This pins the behavioral guarantee that switch relies on: a chat
|
||||||
|
// longer than the old 50-msg window comes back in FULL (oldest -> newest),
|
||||||
|
// so no early turns are silently dropped from what the model sees. The old
|
||||||
|
// 50-cap would have returned only the last 50 of these 60 rows.
|
||||||
|
const longChat = (
|
||||||
|
await createChat(db, { workspaceId, creatorId: userId })
|
||||||
|
).id;
|
||||||
|
const base = Date.now();
|
||||||
|
const total = 60;
|
||||||
|
for (let i = 0; i < total; i++) {
|
||||||
|
await createMessage(db, {
|
||||||
|
workspaceId,
|
||||||
|
chatId: longChat,
|
||||||
|
content: `msg-${i}`,
|
||||||
|
// Strictly increasing timestamps so ordering is deterministic.
|
||||||
|
createdAt: new Date(base + i * 1000),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default args == exactly how handle() calls it now.
|
||||||
|
const history = await repo.findAllByChat(longChat, workspaceId);
|
||||||
|
expect(history).toHaveLength(total);
|
||||||
|
expect(history.map((r) => r.content)).toEqual(
|
||||||
|
Array.from({ length: total }, (_, i) => `msg-${i}`),
|
||||||
|
);
|
||||||
|
// The very first turn (which the old 50-window would have dropped) is present.
|
||||||
|
expect(history[0]!.content).toBe('msg-0');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user