@livekit/agents 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_exceptions.cjs.map +1 -1
- package/dist/_exceptions.d.ts.map +1 -1
- package/dist/_exceptions.js.map +1 -1
- package/dist/beta/workflows/task_group.cjs +7 -4
- package/dist/beta/workflows/task_group.cjs.map +1 -1
- package/dist/beta/workflows/task_group.d.ts.map +1 -1
- package/dist/beta/workflows/task_group.js +7 -4
- package/dist/beta/workflows/task_group.js.map +1 -1
- package/dist/inference/interruption/http_transport.cjs.map +1 -1
- package/dist/inference/interruption/http_transport.d.cts +3 -1
- package/dist/inference/interruption/http_transport.d.ts +3 -1
- package/dist/inference/interruption/http_transport.d.ts.map +1 -1
- package/dist/inference/interruption/http_transport.js.map +1 -1
- package/dist/inference/interruption/ws_transport.cjs +37 -32
- package/dist/inference/interruption/ws_transport.cjs.map +1 -1
- package/dist/inference/interruption/ws_transport.d.ts.map +1 -1
- package/dist/inference/interruption/ws_transport.js +37 -32
- package/dist/inference/interruption/ws_transport.js.map +1 -1
- package/dist/inference/tts.cjs.map +1 -1
- package/dist/inference/tts.d.cts +42 -4
- package/dist/inference/tts.d.ts +42 -4
- package/dist/inference/tts.d.ts.map +1 -1
- package/dist/inference/tts.js.map +1 -1
- package/dist/inference/tts.test.cjs +72 -0
- package/dist/inference/tts.test.cjs.map +1 -1
- package/dist/inference/tts.test.js +72 -0
- package/dist/inference/tts.test.js.map +1 -1
- package/dist/llm/chat_context.cjs +102 -31
- package/dist/llm/chat_context.cjs.map +1 -1
- package/dist/llm/chat_context.d.ts.map +1 -1
- package/dist/llm/chat_context.js +102 -31
- package/dist/llm/chat_context.js.map +1 -1
- package/dist/llm/chat_context.test.cjs +123 -5
- package/dist/llm/chat_context.test.cjs.map +1 -1
- package/dist/llm/chat_context.test.js +123 -5
- package/dist/llm/chat_context.test.js.map +1 -1
- package/dist/llm/fallback_adapter.cjs +2 -0
- package/dist/llm/fallback_adapter.cjs.map +1 -1
- package/dist/llm/fallback_adapter.d.ts.map +1 -1
- package/dist/llm/fallback_adapter.js +2 -0
- package/dist/llm/fallback_adapter.js.map +1 -1
- package/dist/llm/index.cjs +2 -0
- package/dist/llm/index.cjs.map +1 -1
- package/dist/llm/index.d.cts +1 -1
- package/dist/llm/index.d.ts +1 -1
- package/dist/llm/index.d.ts.map +1 -1
- package/dist/llm/index.js +2 -0
- package/dist/llm/index.js.map +1 -1
- package/dist/llm/utils.cjs +89 -0
- package/dist/llm/utils.cjs.map +1 -1
- package/dist/llm/utils.d.cts +8 -0
- package/dist/llm/utils.d.ts +8 -0
- package/dist/llm/utils.d.ts.map +1 -1
- package/dist/llm/utils.js +88 -0
- package/dist/llm/utils.js.map +1 -1
- package/dist/llm/utils.test.cjs +90 -0
- package/dist/llm/utils.test.cjs.map +1 -1
- package/dist/llm/utils.test.js +98 -2
- package/dist/llm/utils.test.js.map +1 -1
- package/dist/stt/stt.cjs +8 -0
- package/dist/stt/stt.cjs.map +1 -1
- package/dist/stt/stt.d.cts +8 -0
- package/dist/stt/stt.d.ts +8 -0
- package/dist/stt/stt.d.ts.map +1 -1
- package/dist/stt/stt.js +8 -0
- package/dist/stt/stt.js.map +1 -1
- package/dist/tts/fallback_adapter.cjs +6 -0
- package/dist/tts/fallback_adapter.cjs.map +1 -1
- package/dist/tts/fallback_adapter.d.ts.map +1 -1
- package/dist/tts/fallback_adapter.js +6 -0
- package/dist/tts/fallback_adapter.js.map +1 -1
- package/dist/typed_promise.cjs +48 -0
- package/dist/typed_promise.cjs.map +1 -0
- package/dist/typed_promise.d.cts +24 -0
- package/dist/typed_promise.d.ts +24 -0
- package/dist/typed_promise.d.ts.map +1 -0
- package/dist/typed_promise.js +28 -0
- package/dist/typed_promise.js.map +1 -0
- package/dist/utils.cjs +2 -2
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.js +2 -2
- package/dist/utils.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -2
- package/src/_exceptions.ts +5 -0
- package/src/beta/workflows/task_group.ts +14 -5
- package/src/inference/interruption/http_transport.ts +2 -1
- package/src/inference/interruption/ws_transport.ts +44 -34
- package/src/inference/tts.test.ts +87 -0
- package/src/inference/tts.ts +46 -6
- package/src/llm/chat_context.test.ts +137 -5
- package/src/llm/chat_context.ts +119 -38
- package/src/llm/fallback_adapter.ts +5 -2
- package/src/llm/index.ts +2 -0
- package/src/llm/utils.test.ts +103 -2
- package/src/llm/utils.ts +128 -0
- package/src/stt/stt.ts +9 -1
- package/src/tts/fallback_adapter.ts +9 -2
- package/src/typed_promise.ts +67 -0
- package/src/utils.ts +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/llm/chat_context.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { describe, expect, it } from 'vitest';\nimport { initializeLogger } from '../log.js';\nimport { FakeLLM } from '../voice/testing/fake_llm.js';\nimport {\n type AudioContent,\n ChatContext,\n type ChatItem,\n ChatMessage,\n FunctionCall,\n FunctionCallOutput,\n type ImageContent,\n ReadonlyChatContext,\n} from './chat_context.js';\n\ninitializeLogger({ pretty: false, level: 'error' });\n\ndescribe('ChatContext.toJSON', () => {\n it('should match snapshot for empty context', () => {\n const context = new ChatContext();\n expect(context.toJSON()).toMatchSnapshot();\n });\n\n it('should match snapshot for simple conversation', () => {\n const context = new ChatContext();\n\n context.addMessage({\n id: 'msg_system_1',\n role: 'system',\n content: 'You are a helpful assistant.',\n createdAt: 1000000000,\n });\n\n context.addMessage({\n id: 'msg_user_1',\n role: 'user',\n content: 'Hello, how are you?',\n createdAt: 1000000001,\n });\n\n context.addMessage({\n id: 'msg_assistant_1',\n role: 'assistant',\n content: \"I'm doing well, thank you! How can I help you today?\",\n createdAt: 1000000002,\n });\n\n expect(context.toJSON()).toMatchSnapshot('simple-conversation-no-timestamps');\n\n expect(context.toJSON({ excludeTimestamp: false })).toMatchSnapshot(\n 'simple-conversation-with-timestamps',\n );\n });\n\n it('should match snapshot for multimodal content', () => {\n const context = new ChatContext();\n\n const imageContent: ImageContent = {\n id: 'img_test_1',\n type: 'image_content',\n image: 'https://example.com/test-image.jpg',\n inferenceDetail: 'high',\n inferenceWidth: 1024,\n inferenceHeight: 768,\n mimeType: 'image/jpeg',\n _cache: {},\n };\n\n const audioContent: AudioContent = {\n type: 'audio_content',\n frame: [], // This won't be included in JSON\n transcript: 'This is a test audio transcript',\n };\n\n context.addMessage({\n id: 'msg_user_2',\n role: 'user',\n content: [\n 'Check out this image and audio:',\n imageContent,\n audioContent,\n 'What do you think?',\n ],\n createdAt: 2000000000,\n });\n\n expect(context.toJSON()).toMatchSnapshot('multimodal-default-exclusions');\n\n expect(\n context.toJSON({\n excludeImage: false,\n excludeAudio: true,\n }),\n ).toMatchSnapshot('multimodal-with-images-only');\n\n expect(\n context.toJSON({\n excludeImage: true,\n excludeAudio: false,\n }),\n ).toMatchSnapshot('multimodal-with-audio-only');\n\n expect(\n context.toJSON({\n excludeImage: false,\n excludeAudio: false,\n excludeTimestamp: false,\n }),\n ).toMatchSnapshot('multimodal-full-content');\n });\n\n it('should match snapshot for function calls', () => {\n const context = new ChatContext();\n\n context.addMessage({\n id: 'msg_user_3',\n role: 'user',\n content: \"What's the weather in Paris?\",\n createdAt: 3000000000,\n });\n\n const functionCall = new FunctionCall({\n id: 'func_call_1',\n callId: 'call_weather_123',\n name: 'get_weather',\n args: '{\"location\": \"Paris, France\", \"unit\": \"celsius\"}',\n createdAt: 3000000001,\n });\n context.insert(functionCall);\n\n const functionOutput = new FunctionCallOutput({\n id: 'func_output_1',\n callId: 'call_weather_123',\n name: 'get_weather',\n output: '{\"temperature\": 22, \"condition\": \"partly cloudy\", \"humidity\": 65}',\n isError: false,\n createdAt: 3000000002,\n });\n context.insert(functionOutput);\n\n context.addMessage({\n id: 'msg_assistant_2',\n role: 'assistant',\n content: 'The weather in Paris is currently 22°C and partly cloudy with 65% humidity.',\n createdAt: 3000000003,\n });\n\n expect(context.toJSON()).toMatchSnapshot('conversation-with-function-calls');\n\n expect(\n context.toJSON({\n excludeFunctionCall: true,\n }),\n ).toMatchSnapshot('conversation-without-function-calls');\n\n expect(\n context.toJSON({\n excludeTimestamp: false,\n }),\n ).toMatchSnapshot('conversation-with-function-calls-and-timestamps');\n });\n\n it('should match snapshot for edge cases', () => {\n const context = new ChatContext();\n\n context.addMessage({\n id: 'msg_empty_1',\n role: 'user',\n content: [],\n createdAt: 5000000000,\n });\n\n const silentAudio: AudioContent = {\n type: 'audio_content',\n frame: [],\n transcript: undefined,\n };\n\n context.addMessage({\n id: 'msg_silent_audio',\n role: 'user',\n content: [silentAudio],\n createdAt: 5000000001,\n });\n\n context.addMessage({\n id: 'msg_multi_text',\n role: 'assistant',\n content: ['Part 1. ', 'Part 2. ', 'Part 3.'],\n createdAt: 5000000002,\n });\n\n const minimalCall = new FunctionCall({\n id: 'func_minimal',\n callId: 'minimal',\n name: 'test',\n args: '{}',\n createdAt: 5000000003,\n });\n context.insert(minimalCall);\n\n const namelessOutput = new FunctionCallOutput({\n id: 'func_output_nameless',\n callId: 'minimal',\n output: 'OK',\n isError: false,\n createdAt: 5000000004,\n });\n context.insert(namelessOutput);\n\n context.addMessage({\n id: 'msg_special_chars',\n role: 'user',\n content:\n 'Test with special chars: \\n\\t\\r \"quotes\" \\'apostrophes\\' \\\\backslashes\\\\ {braces} [brackets]',\n createdAt: 5000000005,\n });\n\n expect(context.toJSON()).toMatchSnapshot('edge-cases-default');\n expect(\n context.toJSON({\n excludeTimestamp: false,\n excludeAudio: false,\n }),\n ).toMatchSnapshot('edge-cases-with-details');\n });\n\n it('should match snapshot for message property variations', () => {\n const context = new ChatContext();\n\n context.addMessage({\n id: 'custom-message-id-123',\n role: 'user',\n content: 'Message with custom ID',\n createdAt: 6000000000,\n });\n\n context.addMessage({\n id: 'msg_interrupted',\n role: 'assistant',\n content: 'This response was interrupted...',\n interrupted: true,\n createdAt: 6000000001,\n });\n\n context.addMessage({\n id: 'msg_dev_2',\n role: 'developer',\n content: 'Developer message',\n createdAt: 6000000002,\n });\n\n context.addMessage({\n id: 'msg_system_3',\n role: 'system',\n content: 'System message',\n createdAt: 6000000003,\n });\n\n const detailedImage: ImageContent = {\n id: 'img_detailed',\n type: 'image_content',\n image: 'https://example.com/image.jpg',\n inferenceDetail: 'low',\n inferenceWidth: 512,\n inferenceHeight: 512,\n mimeType: 'image/png',\n _cache: { cached: true },\n };\n\n context.addMessage({\n id: 'msg_with_image',\n role: 'user',\n content: ['Image with all properties:', detailedImage],\n createdAt: 6000000004,\n });\n\n expect(context.toJSON()).toMatchSnapshot('message-properties-default');\n expect(\n context.toJSON({\n excludeImage: false,\n excludeTimestamp: false,\n }),\n ).toMatchSnapshot('message-properties-full');\n });\n});\n\ndescribe('ChatContext._summarize', () => {\n it('keeps chronological timestamps with summary + tail', async () => {\n const ctx = new ChatContext();\n ctx.addMessage({ role: 'system', content: 'System prompt', createdAt: 0 });\n ctx.addMessage({ role: 'user', content: 'hello', createdAt: 1000 });\n ctx.addMessage({ role: 'assistant', content: 'hi there', createdAt: 2000 });\n ctx.insert(\n new FunctionCallOutput({\n callId: 'call_1',\n name: 'lookup',\n output: '{\"ok\":true}',\n isError: false,\n createdAt: 3500,\n }),\n );\n ctx.addMessage({ role: 'user', content: 'my color is blue', createdAt: 3000 });\n ctx.addMessage({ role: 'assistant', content: 'noted', createdAt: 4000 });\n\n const fake = new FakeLLM([\n {\n input: 'Conversation to summarize:\\n\\nuser: hello\\nassistant: hi there',\n content: 'condensed head',\n },\n ]);\n\n await ctx._summarize(fake, { keepLastTurns: 1 });\n\n const summary = ctx.items.find(\n (item) =>\n item.type === 'message' && item.role === 'assistant' && item.extra?.is_summary === true,\n );\n expect(summary).toBeDefined();\n if (!summary || summary.type !== 'message') {\n throw new Error('summary message is missing');\n }\n\n expect(summary.createdAt).toBeCloseTo(2999.999, 6);\n\n const createdAts = ctx.items.map((item) => item.createdAt);\n const sorted = [...createdAts].sort((a, b) => a - b);\n expect(createdAts).toEqual(sorted);\n });\n});\n\ndescribe('ReadonlyChatContext with immutable array', () => {\n it('should have readonly property set to true', () => {\n const items: ChatItem[] = [\n new ChatMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Test'],\n interrupted: false,\n createdAt: Date.now(),\n }),\n ];\n const readonlyContext = new ReadonlyChatContext(items);\n\n expect(readonlyContext.readonly).toBe(true);\n });\n\n it('should prevent setting items property', () => {\n const items: ChatItem[] = [\n new ChatMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Test'],\n interrupted: false,\n createdAt: Date.now(),\n }),\n ];\n const readonlyContext = new ReadonlyChatContext(items);\n expect(() => {\n readonlyContext.items = [];\n }).toThrow(\n `Cannot set items on a read-only chat context. Please use .copy() and agent.update_chat_ctx() to modify the chat context.`,\n );\n });\n\n it('should prevent modifications through array methods', () => {\n const items: ChatItem[] = [\n new ChatMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Test'],\n interrupted: false,\n createdAt: Date.now(),\n }),\n ];\n const readonlyContext = new ReadonlyChatContext(items);\n const newItem = new ChatMessage({\n id: 'msg_2',\n role: 'assistant',\n content: ['Response'],\n interrupted: false,\n createdAt: Date.now(),\n });\n\n const mutableItems = readonlyContext.items;\n expect(() => mutableItems.push(newItem)).toThrow(\n 'Cannot call push() on a read-only array. Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => mutableItems.pop()).toThrow(\n 'Cannot call pop() on a read-only array. Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => mutableItems.shift()).toThrow(\n 'Cannot call shift() on a read-only array. Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => mutableItems.unshift(newItem)).toThrow(\n 'Cannot call unshift() on a read-only array. Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => mutableItems.splice(0, 1)).toThrow(\n 'Cannot call splice() on a read-only array. Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => mutableItems.sort()).toThrow(\n 'Cannot call sort() on a read-only array. Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => mutableItems.reverse()).toThrow(\n 'Cannot call reverse() on a read-only array. Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => mutableItems.fill(newItem)).toThrow(\n 'Cannot call fill() on a read-only array. Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => mutableItems.copyWithin(0, 1)).toThrow(\n 'Cannot call copyWithin() on a read-only array. Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n });\n\n it('should prevent bracket notation assignment and deletion', () => {\n const items: ChatItem[] = [\n new ChatMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Test'],\n interrupted: false,\n createdAt: Date.now(),\n }),\n ];\n const readonlyContext = new ReadonlyChatContext(items);\n const newItem = new ChatMessage({\n id: 'msg_2',\n role: 'assistant',\n content: ['Response'],\n interrupted: false,\n createdAt: Date.now(),\n });\n\n expect(() => {\n readonlyContext.items[0] = newItem;\n }).toThrow(\n 'Cannot assign to read-only array index \"0\". Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => {\n delete readonlyContext.items[0];\n }).toThrow(\n 'Cannot delete read-only array index \"0\". Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => {\n readonlyContext.items[99] = newItem;\n }).toThrow(\n 'Cannot assign to read-only array index \"99\". Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n });\n\n it('should allow read operations on the immutable array', () => {\n const items: ChatItem[] = [\n new ChatMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Test 1'],\n interrupted: false,\n createdAt: 1000,\n }),\n new ChatMessage({\n id: 'msg_2',\n role: 'assistant',\n content: ['Test 2'],\n interrupted: false,\n createdAt: 2000,\n }),\n ];\n const readonlyContext = new ReadonlyChatContext(items);\n\n expect(readonlyContext.items.length).toBe(2);\n expect(readonlyContext.items[0]).toEqual(items[0]);\n expect(readonlyContext.items[1]).toEqual(items[1]);\n expect(readonlyContext.items.find((item: ChatItem) => item.id === 'msg_2')).toEqual(items[1]);\n expect(readonlyContext.items.map((item: ChatItem) => item.id)).toEqual(['msg_1', 'msg_2']);\n expect(\n readonlyContext.items.filter(\n (item: ChatItem) => item.type === 'message' && item.role === 'user',\n ),\n ).toHaveLength(1);\n\n // forEach should work for reading\n const ids: string[] = [];\n readonlyContext.items.forEach((item) => ids.push(item.id));\n expect(ids).toEqual(['msg_1', 'msg_2']);\n });\n});\n\ndescribe('ChatContext.isEquivalent', () => {\n it('should return true for same reference', () => {\n const ctx = new ChatContext();\n ctx.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n });\n\n expect(ctx.isEquivalent(ctx)).toBe(true);\n });\n\n it('should return true for identical empty contexts', () => {\n const ctx1 = new ChatContext();\n const ctx2 = new ChatContext();\n\n expect(ctx1.isEquivalent(ctx2)).toBe(true);\n });\n\n it('should return false for contexts with different lengths', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n });\n ctx2.addMessage({\n id: 'msg_2',\n role: 'assistant',\n content: 'Hi',\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return false for contexts with different item IDs', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_2',\n role: 'user',\n content: 'Hello',\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return false for contexts with different item types', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n });\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCall({\n id: 'msg_1',\n callId: 'call_1',\n name: 'test',\n args: '{}',\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n describe('message comparison', () => {\n it('should return true for identical messages', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n interrupted: false,\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n interrupted: false,\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(true);\n });\n\n it('should return false for messages with different roles', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'assistant',\n content: 'Hello',\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return false for messages with different interrupted flags', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n interrupted: false,\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n interrupted: true,\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return false for messages with different content', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'World',\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return true for messages with identical array content', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Hello', 'World'],\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Hello', 'World'],\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(true);\n });\n\n it('should return false for messages with different array content', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Hello', 'World'],\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Hello'],\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return true for messages with identical image content', () => {\n const imageContent: ImageContent = {\n id: 'img_1',\n type: 'image_content',\n image: 'https://example.com/image.jpg',\n inferenceDetail: 'high',\n inferenceWidth: 1024,\n inferenceHeight: 768,\n mimeType: 'image/jpeg',\n _cache: {},\n };\n\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Check this:', imageContent],\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Check this:', { ...imageContent }],\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(true);\n });\n\n it('should return false for messages with different image content', () => {\n const imageContent1: ImageContent = {\n id: 'img_1',\n type: 'image_content',\n image: 'https://example.com/image1.jpg',\n inferenceDetail: 'high',\n _cache: {},\n };\n\n const imageContent2: ImageContent = {\n id: 'img_2',\n type: 'image_content',\n image: 'https://example.com/image2.jpg',\n inferenceDetail: 'high',\n _cache: {},\n };\n\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Check this:', imageContent1],\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Check this:', imageContent2],\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n });\n\n describe('function call comparison', () => {\n it('should return true for identical function calls', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{\"location\": \"Paris\"}',\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{\"location\": \"Paris\"}',\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(true);\n });\n\n it('should return false for function calls with different names', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{}',\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_time',\n args: '{}',\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return false for function calls with different call IDs', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{}',\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_2',\n name: 'get_weather',\n args: '{}',\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return false for function calls with different arguments', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{\"location\": \"Paris\"}',\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{\"location\": \"London\"}',\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should ignore timestamps', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{}',\n createdAt: 1000,\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{}',\n createdAt: 2000,\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(true);\n });\n });\n\n describe('function call output comparison', () => {\n it('should return true for identical function call outputs', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{\"temperature\": 22}',\n isError: false,\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{\"temperature\": 22}',\n isError: false,\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(true);\n });\n\n it('should return false for function call outputs with different names', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{}',\n isError: false,\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_time',\n output: '{}',\n isError: false,\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return false for function call outputs with different call IDs', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{}',\n isError: false,\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_2',\n name: 'get_weather',\n output: '{}',\n isError: false,\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return false for function call outputs with different output values', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{\"temperature\": 22}',\n isError: false,\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{\"temperature\": 25}',\n isError: false,\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return false for function call outputs with different error flags', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: 'Error occurred',\n isError: false,\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: 'Error occurred',\n isError: true,\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should ignore timestamps', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{}',\n isError: false,\n createdAt: 1000,\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{}',\n isError: false,\n createdAt: 2000,\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(true);\n });\n });\n\n describe('complex context comparison', () => {\n it('should return true for identical complex contexts', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'What is the weather?',\n });\n ctx1.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{\"location\": \"Paris\"}',\n }),\n );\n ctx1.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{\"temperature\": 22}',\n isError: false,\n }),\n );\n ctx1.addMessage({\n id: 'msg_2',\n role: 'assistant',\n content: 'The weather is 22°C',\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'What is the weather?',\n });\n ctx2.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{\"location\": \"Paris\"}',\n }),\n );\n ctx2.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{\"temperature\": 22}',\n isError: false,\n }),\n );\n ctx2.addMessage({\n id: 'msg_2',\n role: 'assistant',\n content: 'The weather is 22°C',\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(true);\n });\n });\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/llm/chat_context.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { describe, expect, it } from 'vitest';\nimport { initializeLogger } from '../log.js';\nimport { FakeLLM } from '../voice/testing/fake_llm.js';\nimport {\n type AudioContent,\n ChatContext,\n type ChatItem,\n ChatMessage,\n FunctionCall,\n FunctionCallOutput,\n type ImageContent,\n ReadonlyChatContext,\n} from './chat_context.js';\n\ninitializeLogger({ pretty: false, level: 'error' });\n\nconst summaryXml = (summary: string) =>\n ['<chat_history_summary>', summary, '</chat_history_summary>'].join('\\n');\n\nclass TrackingFakeLLM extends FakeLLM {\n chatCalls = 0;\n\n chat(...args: Parameters<FakeLLM['chat']>) {\n this.chatCalls += 1;\n return super.chat(...args);\n }\n}\n\ndescribe('ChatContext.toJSON', () => {\n it('should match snapshot for empty context', () => {\n const context = new ChatContext();\n expect(context.toJSON()).toMatchSnapshot();\n });\n\n it('should match snapshot for simple conversation', () => {\n const context = new ChatContext();\n\n context.addMessage({\n id: 'msg_system_1',\n role: 'system',\n content: 'You are a helpful assistant.',\n createdAt: 1000000000,\n });\n\n context.addMessage({\n id: 'msg_user_1',\n role: 'user',\n content: 'Hello, how are you?',\n createdAt: 1000000001,\n });\n\n context.addMessage({\n id: 'msg_assistant_1',\n role: 'assistant',\n content: \"I'm doing well, thank you! How can I help you today?\",\n createdAt: 1000000002,\n });\n\n expect(context.toJSON()).toMatchSnapshot('simple-conversation-no-timestamps');\n\n expect(context.toJSON({ excludeTimestamp: false })).toMatchSnapshot(\n 'simple-conversation-with-timestamps',\n );\n });\n\n it('should match snapshot for multimodal content', () => {\n const context = new ChatContext();\n\n const imageContent: ImageContent = {\n id: 'img_test_1',\n type: 'image_content',\n image: 'https://example.com/test-image.jpg',\n inferenceDetail: 'high',\n inferenceWidth: 1024,\n inferenceHeight: 768,\n mimeType: 'image/jpeg',\n _cache: {},\n };\n\n const audioContent: AudioContent = {\n type: 'audio_content',\n frame: [], // This won't be included in JSON\n transcript: 'This is a test audio transcript',\n };\n\n context.addMessage({\n id: 'msg_user_2',\n role: 'user',\n content: [\n 'Check out this image and audio:',\n imageContent,\n audioContent,\n 'What do you think?',\n ],\n createdAt: 2000000000,\n });\n\n expect(context.toJSON()).toMatchSnapshot('multimodal-default-exclusions');\n\n expect(\n context.toJSON({\n excludeImage: false,\n excludeAudio: true,\n }),\n ).toMatchSnapshot('multimodal-with-images-only');\n\n expect(\n context.toJSON({\n excludeImage: true,\n excludeAudio: false,\n }),\n ).toMatchSnapshot('multimodal-with-audio-only');\n\n expect(\n context.toJSON({\n excludeImage: false,\n excludeAudio: false,\n excludeTimestamp: false,\n }),\n ).toMatchSnapshot('multimodal-full-content');\n });\n\n it('should match snapshot for function calls', () => {\n const context = new ChatContext();\n\n context.addMessage({\n id: 'msg_user_3',\n role: 'user',\n content: \"What's the weather in Paris?\",\n createdAt: 3000000000,\n });\n\n const functionCall = new FunctionCall({\n id: 'func_call_1',\n callId: 'call_weather_123',\n name: 'get_weather',\n args: '{\"location\": \"Paris, France\", \"unit\": \"celsius\"}',\n createdAt: 3000000001,\n });\n context.insert(functionCall);\n\n const functionOutput = new FunctionCallOutput({\n id: 'func_output_1',\n callId: 'call_weather_123',\n name: 'get_weather',\n output: '{\"temperature\": 22, \"condition\": \"partly cloudy\", \"humidity\": 65}',\n isError: false,\n createdAt: 3000000002,\n });\n context.insert(functionOutput);\n\n context.addMessage({\n id: 'msg_assistant_2',\n role: 'assistant',\n content: 'The weather in Paris is currently 22°C and partly cloudy with 65% humidity.',\n createdAt: 3000000003,\n });\n\n expect(context.toJSON()).toMatchSnapshot('conversation-with-function-calls');\n\n expect(\n context.toJSON({\n excludeFunctionCall: true,\n }),\n ).toMatchSnapshot('conversation-without-function-calls');\n\n expect(\n context.toJSON({\n excludeTimestamp: false,\n }),\n ).toMatchSnapshot('conversation-with-function-calls-and-timestamps');\n });\n\n it('should match snapshot for edge cases', () => {\n const context = new ChatContext();\n\n context.addMessage({\n id: 'msg_empty_1',\n role: 'user',\n content: [],\n createdAt: 5000000000,\n });\n\n const silentAudio: AudioContent = {\n type: 'audio_content',\n frame: [],\n transcript: undefined,\n };\n\n context.addMessage({\n id: 'msg_silent_audio',\n role: 'user',\n content: [silentAudio],\n createdAt: 5000000001,\n });\n\n context.addMessage({\n id: 'msg_multi_text',\n role: 'assistant',\n content: ['Part 1. ', 'Part 2. ', 'Part 3.'],\n createdAt: 5000000002,\n });\n\n const minimalCall = new FunctionCall({\n id: 'func_minimal',\n callId: 'minimal',\n name: 'test',\n args: '{}',\n createdAt: 5000000003,\n });\n context.insert(minimalCall);\n\n const namelessOutput = new FunctionCallOutput({\n id: 'func_output_nameless',\n callId: 'minimal',\n output: 'OK',\n isError: false,\n createdAt: 5000000004,\n });\n context.insert(namelessOutput);\n\n context.addMessage({\n id: 'msg_special_chars',\n role: 'user',\n content:\n 'Test with special chars: \\n\\t\\r \"quotes\" \\'apostrophes\\' \\\\backslashes\\\\ {braces} [brackets]',\n createdAt: 5000000005,\n });\n\n expect(context.toJSON()).toMatchSnapshot('edge-cases-default');\n expect(\n context.toJSON({\n excludeTimestamp: false,\n excludeAudio: false,\n }),\n ).toMatchSnapshot('edge-cases-with-details');\n });\n\n it('should match snapshot for message property variations', () => {\n const context = new ChatContext();\n\n context.addMessage({\n id: 'custom-message-id-123',\n role: 'user',\n content: 'Message with custom ID',\n createdAt: 6000000000,\n });\n\n context.addMessage({\n id: 'msg_interrupted',\n role: 'assistant',\n content: 'This response was interrupted...',\n interrupted: true,\n createdAt: 6000000001,\n });\n\n context.addMessage({\n id: 'msg_dev_2',\n role: 'developer',\n content: 'Developer message',\n createdAt: 6000000002,\n });\n\n context.addMessage({\n id: 'msg_system_3',\n role: 'system',\n content: 'System message',\n createdAt: 6000000003,\n });\n\n const detailedImage: ImageContent = {\n id: 'img_detailed',\n type: 'image_content',\n image: 'https://example.com/image.jpg',\n inferenceDetail: 'low',\n inferenceWidth: 512,\n inferenceHeight: 512,\n mimeType: 'image/png',\n _cache: { cached: true },\n };\n\n context.addMessage({\n id: 'msg_with_image',\n role: 'user',\n content: ['Image with all properties:', detailedImage],\n createdAt: 6000000004,\n });\n\n expect(context.toJSON()).toMatchSnapshot('message-properties-default');\n expect(\n context.toJSON({\n excludeImage: false,\n excludeTimestamp: false,\n }),\n ).toMatchSnapshot('message-properties-full');\n });\n});\n\ndescribe('ChatContext._summarize', () => {\n it('includes function calls in the summarization source and keeps chronological order', async () => {\n const ctx = new ChatContext();\n ctx.addMessage({ role: 'system', content: 'System prompt', createdAt: 0 });\n ctx.addMessage({ role: 'user', content: 'hello', createdAt: 1000 });\n ctx.addMessage({ role: 'assistant', content: 'hi there', createdAt: 2000 });\n ctx.insert(\n FunctionCall.create({\n callId: 'call_1',\n name: 'lookup',\n args: '{\"order\":\"123\"}',\n createdAt: 2500,\n }),\n );\n ctx.insert(\n new FunctionCallOutput({\n callId: 'call_1',\n name: 'lookup',\n output: '{\"status\":\"delivered\"}',\n isError: false,\n createdAt: 2600,\n }),\n );\n ctx.addMessage({ role: 'user', content: 'my color is blue', createdAt: 3000 });\n ctx.addMessage({ role: 'assistant', content: 'noted', createdAt: 4000 });\n\n const fake = new FakeLLM([\n {\n input: [\n 'Conversation to summarize:',\n '',\n '<user>',\n 'hello',\n '</user>',\n '<assistant>',\n 'hi there',\n '</assistant>',\n '<function_call name=\"lookup\" call_id=\"call_1\">',\n '{\"order\":\"123\"}',\n '</function_call>',\n '<function_call_output name=\"lookup\" call_id=\"call_1\">',\n '{\"status\":\"delivered\"}',\n '</function_call_output>',\n ].join('\\n'),\n content: 'condensed head',\n },\n ]);\n\n await ctx._summarize(fake, { keepLastTurns: 1 });\n\n const summary = ctx.items.find(\n (item) =>\n item.type === 'message' && item.role === 'assistant' && item.extra?.is_summary === true,\n );\n expect(summary).toBeDefined();\n if (!summary || summary.type !== 'message') {\n throw new Error('summary message is missing');\n }\n\n expect(summary.textContent).toBe(summaryXml('condensed head'));\n expect(summary.createdAt).toBeCloseTo(2999.999999, 6);\n expect(ctx.items.filter((item) => item.type === 'function_call')).toHaveLength(0);\n expect(ctx.items.filter((item) => item.type === 'function_call_output')).toHaveLength(0);\n\n const createdAts = ctx.items.map((item) => item.createdAt);\n const sorted = [...createdAts].sort((a, b) => a - b);\n expect(createdAts).toEqual(sorted);\n });\n\n it('preserves interleaved tool items that belong to the recent tail', async () => {\n const ctx = new ChatContext();\n ctx.addMessage({ role: 'system', content: 'System prompt', createdAt: 0 });\n ctx.addMessage({ role: 'user', content: 'my earbuds are broken', createdAt: 1000 });\n ctx.addMessage({\n role: 'assistant',\n content: 'Can you share your order number?',\n createdAt: 2000,\n });\n ctx.addMessage({ role: 'user', content: 'Order #123', createdAt: 3000 });\n ctx.insert(\n FunctionCall.create({\n callId: 'call_2',\n name: 'lookup_order',\n args: '{\"order\":\"123\"}',\n createdAt: 3500,\n }),\n );\n ctx.insert(\n new FunctionCallOutput({\n callId: 'call_2',\n name: 'lookup_order',\n output: '{\"status\":\"delivered\"}',\n isError: false,\n createdAt: 3600,\n }),\n );\n ctx.addMessage({\n role: 'assistant',\n content: 'Found your order. Let me check the warranty.',\n createdAt: 4000,\n });\n ctx.addMessage({ role: 'user', content: 'Thanks.', createdAt: 5000 });\n ctx.addMessage({ role: 'assistant', content: 'You are under warranty.', createdAt: 6000 });\n\n const fake = new FakeLLM([\n {\n input: [\n 'Conversation to summarize:',\n '',\n '<user>',\n 'my earbuds are broken',\n '</user>',\n '<assistant>',\n 'Can you share your order number?',\n '</assistant>',\n ].join('\\n'),\n content: 'older summary',\n },\n ]);\n\n await ctx._summarize(fake, { keepLastTurns: 2 });\n\n const functionItems = ctx.items.filter(\n (item) => item.type === 'function_call' || item.type === 'function_call_output',\n );\n expect(functionItems).toHaveLength(2);\n expect(functionItems.map((item) => item.createdAt)).toEqual([3500, 3600]);\n\n const rawTailMessages = ctx.items.filter(\n (item) =>\n item.type === 'message' &&\n (item.role === 'user' || item.role === 'assistant') &&\n item.extra?.is_summary !== true,\n );\n expect(rawTailMessages).toHaveLength(4);\n expect(rawTailMessages.map((item) => item.textContent)).toEqual([\n 'Order #123',\n 'Found your order. Let me check the warranty.',\n 'Thanks.',\n 'You are under warranty.',\n ]);\n\n const createdAts = ctx.items.map((item) => item.createdAt);\n const sorted = [...createdAts].sort((a, b) => a - b);\n expect(createdAts).toEqual(sorted);\n });\n\n it('skips summarization when the recent-turn budget already covers the history', async () => {\n const ctx = new ChatContext();\n ctx.addMessage({ role: 'system', content: 'System prompt', createdAt: 0 });\n ctx.addMessage({ role: 'user', content: 'hello', createdAt: 1000 });\n ctx.addMessage({ role: 'assistant', content: 'hi there', createdAt: 2000 });\n\n const llm = new TrackingFakeLLM();\n const originalIds = ctx.items.map((item) => item.id);\n\n const result = await ctx._summarize(llm, { keepLastTurns: 1 });\n\n expect(result).toBe(ctx);\n expect(llm.chatCalls).toBe(0);\n expect(ctx.items.map((item) => item.id)).toEqual(originalIds);\n });\n});\n\ndescribe('ReadonlyChatContext with immutable array', () => {\n it('should have readonly property set to true', () => {\n const items: ChatItem[] = [\n new ChatMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Test'],\n interrupted: false,\n createdAt: Date.now(),\n }),\n ];\n const readonlyContext = new ReadonlyChatContext(items);\n\n expect(readonlyContext.readonly).toBe(true);\n });\n\n it('should prevent setting items property', () => {\n const items: ChatItem[] = [\n new ChatMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Test'],\n interrupted: false,\n createdAt: Date.now(),\n }),\n ];\n const readonlyContext = new ReadonlyChatContext(items);\n expect(() => {\n readonlyContext.items = [];\n }).toThrow(\n `Cannot set items on a read-only chat context. Please use .copy() and agent.update_chat_ctx() to modify the chat context.`,\n );\n });\n\n it('should prevent modifications through array methods', () => {\n const items: ChatItem[] = [\n new ChatMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Test'],\n interrupted: false,\n createdAt: Date.now(),\n }),\n ];\n const readonlyContext = new ReadonlyChatContext(items);\n const newItem = new ChatMessage({\n id: 'msg_2',\n role: 'assistant',\n content: ['Response'],\n interrupted: false,\n createdAt: Date.now(),\n });\n\n const mutableItems = readonlyContext.items;\n expect(() => mutableItems.push(newItem)).toThrow(\n 'Cannot call push() on a read-only array. Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => mutableItems.pop()).toThrow(\n 'Cannot call pop() on a read-only array. Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => mutableItems.shift()).toThrow(\n 'Cannot call shift() on a read-only array. Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => mutableItems.unshift(newItem)).toThrow(\n 'Cannot call unshift() on a read-only array. Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => mutableItems.splice(0, 1)).toThrow(\n 'Cannot call splice() on a read-only array. Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => mutableItems.sort()).toThrow(\n 'Cannot call sort() on a read-only array. Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => mutableItems.reverse()).toThrow(\n 'Cannot call reverse() on a read-only array. Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => mutableItems.fill(newItem)).toThrow(\n 'Cannot call fill() on a read-only array. Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => mutableItems.copyWithin(0, 1)).toThrow(\n 'Cannot call copyWithin() on a read-only array. Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n });\n\n it('should prevent bracket notation assignment and deletion', () => {\n const items: ChatItem[] = [\n new ChatMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Test'],\n interrupted: false,\n createdAt: Date.now(),\n }),\n ];\n const readonlyContext = new ReadonlyChatContext(items);\n const newItem = new ChatMessage({\n id: 'msg_2',\n role: 'assistant',\n content: ['Response'],\n interrupted: false,\n createdAt: Date.now(),\n });\n\n expect(() => {\n readonlyContext.items[0] = newItem;\n }).toThrow(\n 'Cannot assign to read-only array index \"0\". Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => {\n delete readonlyContext.items[0];\n }).toThrow(\n 'Cannot delete read-only array index \"0\". Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n\n expect(() => {\n readonlyContext.items[99] = newItem;\n }).toThrow(\n 'Cannot assign to read-only array index \"99\". Please use .copy() and agent.update_chat_ctx() to modify the chat context.',\n );\n });\n\n it('should allow read operations on the immutable array', () => {\n const items: ChatItem[] = [\n new ChatMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Test 1'],\n interrupted: false,\n createdAt: 1000,\n }),\n new ChatMessage({\n id: 'msg_2',\n role: 'assistant',\n content: ['Test 2'],\n interrupted: false,\n createdAt: 2000,\n }),\n ];\n const readonlyContext = new ReadonlyChatContext(items);\n\n expect(readonlyContext.items.length).toBe(2);\n expect(readonlyContext.items[0]).toEqual(items[0]);\n expect(readonlyContext.items[1]).toEqual(items[1]);\n expect(readonlyContext.items.find((item: ChatItem) => item.id === 'msg_2')).toEqual(items[1]);\n expect(readonlyContext.items.map((item: ChatItem) => item.id)).toEqual(['msg_1', 'msg_2']);\n expect(\n readonlyContext.items.filter(\n (item: ChatItem) => item.type === 'message' && item.role === 'user',\n ),\n ).toHaveLength(1);\n\n // forEach should work for reading\n const ids: string[] = [];\n readonlyContext.items.forEach((item) => ids.push(item.id));\n expect(ids).toEqual(['msg_1', 'msg_2']);\n });\n});\n\ndescribe('ChatContext.isEquivalent', () => {\n it('should return true for same reference', () => {\n const ctx = new ChatContext();\n ctx.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n });\n\n expect(ctx.isEquivalent(ctx)).toBe(true);\n });\n\n it('should return true for identical empty contexts', () => {\n const ctx1 = new ChatContext();\n const ctx2 = new ChatContext();\n\n expect(ctx1.isEquivalent(ctx2)).toBe(true);\n });\n\n it('should return false for contexts with different lengths', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n });\n ctx2.addMessage({\n id: 'msg_2',\n role: 'assistant',\n content: 'Hi',\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return false for contexts with different item IDs', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_2',\n role: 'user',\n content: 'Hello',\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return false for contexts with different item types', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n });\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCall({\n id: 'msg_1',\n callId: 'call_1',\n name: 'test',\n args: '{}',\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n describe('message comparison', () => {\n it('should return true for identical messages', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n interrupted: false,\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n interrupted: false,\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(true);\n });\n\n it('should return false for messages with different roles', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'assistant',\n content: 'Hello',\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return false for messages with different interrupted flags', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n interrupted: false,\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n interrupted: true,\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return false for messages with different content', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'Hello',\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'World',\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return true for messages with identical array content', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Hello', 'World'],\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Hello', 'World'],\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(true);\n });\n\n it('should return false for messages with different array content', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Hello', 'World'],\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Hello'],\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return true for messages with identical image content', () => {\n const imageContent: ImageContent = {\n id: 'img_1',\n type: 'image_content',\n image: 'https://example.com/image.jpg',\n inferenceDetail: 'high',\n inferenceWidth: 1024,\n inferenceHeight: 768,\n mimeType: 'image/jpeg',\n _cache: {},\n };\n\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Check this:', imageContent],\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Check this:', { ...imageContent }],\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(true);\n });\n\n it('should return false for messages with different image content', () => {\n const imageContent1: ImageContent = {\n id: 'img_1',\n type: 'image_content',\n image: 'https://example.com/image1.jpg',\n inferenceDetail: 'high',\n _cache: {},\n };\n\n const imageContent2: ImageContent = {\n id: 'img_2',\n type: 'image_content',\n image: 'https://example.com/image2.jpg',\n inferenceDetail: 'high',\n _cache: {},\n };\n\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Check this:', imageContent1],\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'user',\n content: ['Check this:', imageContent2],\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n });\n\n describe('function call comparison', () => {\n it('should return true for identical function calls', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{\"location\": \"Paris\"}',\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{\"location\": \"Paris\"}',\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(true);\n });\n\n it('should return false for function calls with different names', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{}',\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_time',\n args: '{}',\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return false for function calls with different call IDs', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{}',\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_2',\n name: 'get_weather',\n args: '{}',\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return false for function calls with different arguments', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{\"location\": \"Paris\"}',\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{\"location\": \"London\"}',\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should ignore timestamps', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{}',\n createdAt: 1000,\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{}',\n createdAt: 2000,\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(true);\n });\n });\n\n describe('function call output comparison', () => {\n it('should return true for identical function call outputs', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{\"temperature\": 22}',\n isError: false,\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{\"temperature\": 22}',\n isError: false,\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(true);\n });\n\n it('should return false for function call outputs with different names', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{}',\n isError: false,\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_time',\n output: '{}',\n isError: false,\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return false for function call outputs with different call IDs', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{}',\n isError: false,\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_2',\n name: 'get_weather',\n output: '{}',\n isError: false,\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return false for function call outputs with different output values', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{\"temperature\": 22}',\n isError: false,\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{\"temperature\": 25}',\n isError: false,\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should return false for function call outputs with different error flags', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: 'Error occurred',\n isError: false,\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: 'Error occurred',\n isError: true,\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(false);\n });\n\n it('should ignore timestamps', () => {\n const ctx1 = new ChatContext();\n ctx1.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{}',\n isError: false,\n createdAt: 1000,\n }),\n );\n\n const ctx2 = new ChatContext();\n ctx2.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{}',\n isError: false,\n createdAt: 2000,\n }),\n );\n\n expect(ctx1.isEquivalent(ctx2)).toBe(true);\n });\n });\n\n describe('complex context comparison', () => {\n it('should return true for identical complex contexts', () => {\n const ctx1 = new ChatContext();\n ctx1.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'What is the weather?',\n });\n ctx1.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{\"location\": \"Paris\"}',\n }),\n );\n ctx1.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{\"temperature\": 22}',\n isError: false,\n }),\n );\n ctx1.addMessage({\n id: 'msg_2',\n role: 'assistant',\n content: 'The weather is 22°C',\n });\n\n const ctx2 = new ChatContext();\n ctx2.addMessage({\n id: 'msg_1',\n role: 'user',\n content: 'What is the weather?',\n });\n ctx2.insert(\n new FunctionCall({\n id: 'func_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{\"location\": \"Paris\"}',\n }),\n );\n ctx2.insert(\n new FunctionCallOutput({\n id: 'output_1',\n callId: 'call_1',\n name: 'get_weather',\n output: '{\"temperature\": 22}',\n isError: false,\n }),\n );\n ctx2.addMessage({\n id: 'msg_2',\n role: 'assistant',\n content: 'The weather is 22°C',\n });\n\n expect(ctx1.isEquivalent(ctx2)).toBe(true);\n });\n });\n});\n"],"mappings":"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","names":[]}
|
|
@@ -9,6 +9,14 @@ import {
|
|
|
9
9
|
ReadonlyChatContext
|
|
10
10
|
} from "./chat_context.js";
|
|
11
11
|
initializeLogger({ pretty: false, level: "error" });
|
|
12
|
+
const summaryXml = (summary) => ["<chat_history_summary>", summary, "</chat_history_summary>"].join("\n");
|
|
13
|
+
class TrackingFakeLLM extends FakeLLM {
|
|
14
|
+
chatCalls = 0;
|
|
15
|
+
chat(...args) {
|
|
16
|
+
this.chatCalls += 1;
|
|
17
|
+
return super.chat(...args);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
12
20
|
describe("ChatContext.toJSON", () => {
|
|
13
21
|
it("should match snapshot for empty context", () => {
|
|
14
22
|
const context = new ChatContext();
|
|
@@ -241,25 +249,48 @@ describe("ChatContext.toJSON", () => {
|
|
|
241
249
|
});
|
|
242
250
|
});
|
|
243
251
|
describe("ChatContext._summarize", () => {
|
|
244
|
-
it("
|
|
252
|
+
it("includes function calls in the summarization source and keeps chronological order", async () => {
|
|
245
253
|
const ctx = new ChatContext();
|
|
246
254
|
ctx.addMessage({ role: "system", content: "System prompt", createdAt: 0 });
|
|
247
255
|
ctx.addMessage({ role: "user", content: "hello", createdAt: 1e3 });
|
|
248
256
|
ctx.addMessage({ role: "assistant", content: "hi there", createdAt: 2e3 });
|
|
257
|
+
ctx.insert(
|
|
258
|
+
FunctionCall.create({
|
|
259
|
+
callId: "call_1",
|
|
260
|
+
name: "lookup",
|
|
261
|
+
args: '{"order":"123"}',
|
|
262
|
+
createdAt: 2500
|
|
263
|
+
})
|
|
264
|
+
);
|
|
249
265
|
ctx.insert(
|
|
250
266
|
new FunctionCallOutput({
|
|
251
267
|
callId: "call_1",
|
|
252
268
|
name: "lookup",
|
|
253
|
-
output: '{"
|
|
269
|
+
output: '{"status":"delivered"}',
|
|
254
270
|
isError: false,
|
|
255
|
-
createdAt:
|
|
271
|
+
createdAt: 2600
|
|
256
272
|
})
|
|
257
273
|
);
|
|
258
274
|
ctx.addMessage({ role: "user", content: "my color is blue", createdAt: 3e3 });
|
|
259
275
|
ctx.addMessage({ role: "assistant", content: "noted", createdAt: 4e3 });
|
|
260
276
|
const fake = new FakeLLM([
|
|
261
277
|
{
|
|
262
|
-
input:
|
|
278
|
+
input: [
|
|
279
|
+
"Conversation to summarize:",
|
|
280
|
+
"",
|
|
281
|
+
"<user>",
|
|
282
|
+
"hello",
|
|
283
|
+
"</user>",
|
|
284
|
+
"<assistant>",
|
|
285
|
+
"hi there",
|
|
286
|
+
"</assistant>",
|
|
287
|
+
'<function_call name="lookup" call_id="call_1">',
|
|
288
|
+
'{"order":"123"}',
|
|
289
|
+
"</function_call>",
|
|
290
|
+
'<function_call_output name="lookup" call_id="call_1">',
|
|
291
|
+
'{"status":"delivered"}',
|
|
292
|
+
"</function_call_output>"
|
|
293
|
+
].join("\n"),
|
|
263
294
|
content: "condensed head"
|
|
264
295
|
}
|
|
265
296
|
]);
|
|
@@ -274,11 +305,98 @@ describe("ChatContext._summarize", () => {
|
|
|
274
305
|
if (!summary || summary.type !== "message") {
|
|
275
306
|
throw new Error("summary message is missing");
|
|
276
307
|
}
|
|
277
|
-
expect(summary.
|
|
308
|
+
expect(summary.textContent).toBe(summaryXml("condensed head"));
|
|
309
|
+
expect(summary.createdAt).toBeCloseTo(2999.999999, 6);
|
|
310
|
+
expect(ctx.items.filter((item) => item.type === "function_call")).toHaveLength(0);
|
|
311
|
+
expect(ctx.items.filter((item) => item.type === "function_call_output")).toHaveLength(0);
|
|
312
|
+
const createdAts = ctx.items.map((item) => item.createdAt);
|
|
313
|
+
const sorted = [...createdAts].sort((a, b) => a - b);
|
|
314
|
+
expect(createdAts).toEqual(sorted);
|
|
315
|
+
});
|
|
316
|
+
it("preserves interleaved tool items that belong to the recent tail", async () => {
|
|
317
|
+
const ctx = new ChatContext();
|
|
318
|
+
ctx.addMessage({ role: "system", content: "System prompt", createdAt: 0 });
|
|
319
|
+
ctx.addMessage({ role: "user", content: "my earbuds are broken", createdAt: 1e3 });
|
|
320
|
+
ctx.addMessage({
|
|
321
|
+
role: "assistant",
|
|
322
|
+
content: "Can you share your order number?",
|
|
323
|
+
createdAt: 2e3
|
|
324
|
+
});
|
|
325
|
+
ctx.addMessage({ role: "user", content: "Order #123", createdAt: 3e3 });
|
|
326
|
+
ctx.insert(
|
|
327
|
+
FunctionCall.create({
|
|
328
|
+
callId: "call_2",
|
|
329
|
+
name: "lookup_order",
|
|
330
|
+
args: '{"order":"123"}',
|
|
331
|
+
createdAt: 3500
|
|
332
|
+
})
|
|
333
|
+
);
|
|
334
|
+
ctx.insert(
|
|
335
|
+
new FunctionCallOutput({
|
|
336
|
+
callId: "call_2",
|
|
337
|
+
name: "lookup_order",
|
|
338
|
+
output: '{"status":"delivered"}',
|
|
339
|
+
isError: false,
|
|
340
|
+
createdAt: 3600
|
|
341
|
+
})
|
|
342
|
+
);
|
|
343
|
+
ctx.addMessage({
|
|
344
|
+
role: "assistant",
|
|
345
|
+
content: "Found your order. Let me check the warranty.",
|
|
346
|
+
createdAt: 4e3
|
|
347
|
+
});
|
|
348
|
+
ctx.addMessage({ role: "user", content: "Thanks.", createdAt: 5e3 });
|
|
349
|
+
ctx.addMessage({ role: "assistant", content: "You are under warranty.", createdAt: 6e3 });
|
|
350
|
+
const fake = new FakeLLM([
|
|
351
|
+
{
|
|
352
|
+
input: [
|
|
353
|
+
"Conversation to summarize:",
|
|
354
|
+
"",
|
|
355
|
+
"<user>",
|
|
356
|
+
"my earbuds are broken",
|
|
357
|
+
"</user>",
|
|
358
|
+
"<assistant>",
|
|
359
|
+
"Can you share your order number?",
|
|
360
|
+
"</assistant>"
|
|
361
|
+
].join("\n"),
|
|
362
|
+
content: "older summary"
|
|
363
|
+
}
|
|
364
|
+
]);
|
|
365
|
+
await ctx._summarize(fake, { keepLastTurns: 2 });
|
|
366
|
+
const functionItems = ctx.items.filter(
|
|
367
|
+
(item) => item.type === "function_call" || item.type === "function_call_output"
|
|
368
|
+
);
|
|
369
|
+
expect(functionItems).toHaveLength(2);
|
|
370
|
+
expect(functionItems.map((item) => item.createdAt)).toEqual([3500, 3600]);
|
|
371
|
+
const rawTailMessages = ctx.items.filter(
|
|
372
|
+
(item) => {
|
|
373
|
+
var _a;
|
|
374
|
+
return item.type === "message" && (item.role === "user" || item.role === "assistant") && ((_a = item.extra) == null ? void 0 : _a.is_summary) !== true;
|
|
375
|
+
}
|
|
376
|
+
);
|
|
377
|
+
expect(rawTailMessages).toHaveLength(4);
|
|
378
|
+
expect(rawTailMessages.map((item) => item.textContent)).toEqual([
|
|
379
|
+
"Order #123",
|
|
380
|
+
"Found your order. Let me check the warranty.",
|
|
381
|
+
"Thanks.",
|
|
382
|
+
"You are under warranty."
|
|
383
|
+
]);
|
|
278
384
|
const createdAts = ctx.items.map((item) => item.createdAt);
|
|
279
385
|
const sorted = [...createdAts].sort((a, b) => a - b);
|
|
280
386
|
expect(createdAts).toEqual(sorted);
|
|
281
387
|
});
|
|
388
|
+
it("skips summarization when the recent-turn budget already covers the history", async () => {
|
|
389
|
+
const ctx = new ChatContext();
|
|
390
|
+
ctx.addMessage({ role: "system", content: "System prompt", createdAt: 0 });
|
|
391
|
+
ctx.addMessage({ role: "user", content: "hello", createdAt: 1e3 });
|
|
392
|
+
ctx.addMessage({ role: "assistant", content: "hi there", createdAt: 2e3 });
|
|
393
|
+
const llm = new TrackingFakeLLM();
|
|
394
|
+
const originalIds = ctx.items.map((item) => item.id);
|
|
395
|
+
const result = await ctx._summarize(llm, { keepLastTurns: 1 });
|
|
396
|
+
expect(result).toBe(ctx);
|
|
397
|
+
expect(llm.chatCalls).toBe(0);
|
|
398
|
+
expect(ctx.items.map((item) => item.id)).toEqual(originalIds);
|
|
399
|
+
});
|
|
282
400
|
});
|
|
283
401
|
describe("ReadonlyChatContext with immutable array", () => {
|
|
284
402
|
it("should have readonly property set to true", () => {
|