@librechat/agents 3.1.74 → 3.1.75
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/README.md +66 -0
- package/dist/cjs/agents/AgentContext.cjs +84 -37
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +4 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/messages/cache.cjs +37 -3
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +85 -38
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +4 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/messages/cache.mjs +37 -3
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +14 -4
- package/dist/types/agents/__tests__/promptCacheLiveHelpers.d.ts +46 -0
- package/dist/types/types/graph.d.ts +3 -1
- package/dist/types/types/run.d.ts +2 -0
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +123 -44
- package/src/agents/__tests__/AgentContext.anthropic.live.test.ts +116 -0
- package/src/agents/__tests__/AgentContext.bedrock.live.test.ts +149 -0
- package/src/agents/__tests__/AgentContext.test.ts +155 -2
- package/src/agents/__tests__/promptCacheLiveHelpers.ts +165 -0
- package/src/llm/anthropic/utils/message_inputs.ts +6 -1
- package/src/llm/anthropic/utils/server-tool-inputs.test.ts +77 -0
- package/src/messages/cache.test.ts +104 -3
- package/src/messages/cache.ts +54 -3
- package/src/specs/anthropic.simple.test.ts +61 -0
- package/src/specs/summarization.test.ts +7 -3
- package/src/types/graph.ts +3 -1
- package/src/types/run.ts +2 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.mjs","sources":["../../../src/messages/cache.ts"],"sourcesContent":["import {\n AIMessage,\n BaseMessage,\n ToolMessage,\n HumanMessage,\n SystemMessage,\n MessageContentComplex,\n} from '@langchain/core/messages';\nimport type { AnthropicMessage } from '@/types/messages';\nimport type Anthropic from '@anthropic-ai/sdk';\nimport { ContentTypes } from '@/common/enum';\n\ntype MessageWithContent = {\n content?: string | MessageContentComplex[];\n};\n\n/**\n * Deep clones a message's content to prevent mutation of the original.\n */\nfunction deepCloneContent<T extends string | MessageContentComplex[]>(\n content: T\n): T {\n if (typeof content === 'string') {\n return content;\n }\n if (Array.isArray(content)) {\n return content.map((block) => ({ ...block })) as T;\n }\n return content;\n}\n\n/**\n * Clones a message with new content. For LangChain BaseMessage instances,\n * constructs a proper class instance so that `instanceof` checks are preserved\n * in downstream code (e.g., ensureThinkingBlockInMessages).\n * For plain objects (AnthropicMessage), uses object spread.\n */\nfunction cloneMessage<T extends MessageWithContent>(\n message: T,\n content: string | MessageContentComplex[]\n): T {\n if (message instanceof BaseMessage) {\n const baseParams = {\n content,\n additional_kwargs: { ...message.additional_kwargs },\n response_metadata: { ...message.response_metadata },\n id: message.id,\n name: message.name,\n };\n\n const msgType = message.getType();\n switch (msgType) {\n case 'ai':\n return new AIMessage({\n ...baseParams,\n tool_calls: (message as unknown as AIMessage).tool_calls,\n }) as unknown as T;\n case 'human':\n return new HumanMessage(baseParams) as unknown as T;\n case 'system':\n return new SystemMessage(baseParams) as unknown as T;\n case 'tool':\n return new ToolMessage({\n ...baseParams,\n tool_call_id: (message as unknown as ToolMessage).tool_call_id,\n }) as unknown as T;\n default:\n break;\n }\n }\n\n const {\n lc_kwargs: _lc_kwargs,\n lc_serializable: _lc_serializable,\n lc_namespace: _lc_namespace,\n ...rest\n } = message as T & {\n lc_kwargs?: unknown;\n lc_serializable?: unknown;\n lc_namespace?: unknown;\n };\n\n const cloned = { ...rest, content } as T;\n\n // LangChain messages don't have a direct 'role' property - derive it from getType()\n if (\n 'getType' in message &&\n typeof message.getType === 'function' &&\n !('role' in cloned)\n ) {\n const msgType = (message as unknown as BaseMessage).getType();\n const roleMap: Record<string, string> = {\n human: 'user',\n ai: 'assistant',\n system: 'system',\n tool: 'tool',\n };\n (cloned as Record<string, unknown>).role = roleMap[msgType] || msgType;\n }\n\n return cloned;\n}\n\n/**\n * Anthropic API: Adds cache control to the appropriate user messages in the payload.\n * Strips ALL existing cache control (both Anthropic and Bedrock formats) from all messages,\n * then adds fresh cache control to the last 2 user messages in a single backward pass.\n * This ensures we don't accumulate stale cache points across multiple turns.\n * Returns a new array - only clones messages that require modification.\n * @param messages - The array of message objects.\n * @returns - A new array of message objects with cache control added.\n */\nexport function addCacheControl<T extends AnthropicMessage | BaseMessage>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages) || messages.length < 2) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n let userMessagesModified = 0;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n const isUserMessage =\n ('getType' in originalMessage && originalMessage.getType() === 'human') ||\n ('role' in originalMessage && originalMessage.role === 'user');\n const hasArrayContent = Array.isArray(content);\n const needsCacheAdd =\n userMessagesModified < 2 &&\n isUserMessage &&\n (typeof content === 'string' || hasArrayContent);\n\n // Skip messages that don't need any work\n if (!needsCacheAdd && !hasArrayContent) {\n continue;\n }\n\n let workingContent: MessageContentComplex[];\n let modified = false;\n\n if (hasArrayContent) {\n // Single pass: clone blocks, strip cache markers and cache points,\n // find last text block index for cache insertion — all at once.\n const src = content as MessageContentComplex[];\n workingContent = [];\n let lastTextIndex = -1;\n for (let j = 0; j < src.length; j++) {\n const block = src[j];\n if (isCachePoint(block)) {\n modified = true;\n continue; // skip cache point blocks\n }\n const cloned = { ...block };\n if ('cache_control' in cloned) {\n delete (cloned as Record<string, unknown>).cache_control;\n modified = true;\n }\n if ('type' in cloned && cloned.type === 'text') {\n lastTextIndex = workingContent.length;\n }\n workingContent.push(cloned as MessageContentComplex);\n }\n\n if (!modified && !needsCacheAdd) {\n continue; // nothing to strip and no cache to add\n }\n\n // Add cache control to the last text block for user messages\n if (needsCacheAdd && lastTextIndex >= 0) {\n (\n workingContent[lastTextIndex] as Anthropic.TextBlockParam\n ).cache_control = {\n type: 'ephemeral',\n };\n userMessagesModified++;\n }\n } else if (typeof content === 'string' && needsCacheAdd) {\n workingContent = [\n { type: 'text', text: content, cache_control: { type: 'ephemeral' } },\n ] as unknown as MessageContentComplex[];\n userMessagesModified++;\n } else {\n continue;\n }\n\n updatedMessages[i] = cloneMessage(\n originalMessage as MessageWithContent,\n workingContent\n ) as T;\n }\n\n return updatedMessages;\n}\n\n/**\n * Checks if a content block is a cache point\n */\nfunction isCachePoint(block: MessageContentComplex): boolean {\n return 'cachePoint' in block && !('type' in block);\n}\n\n/**\n * Checks if a message's content has Anthropic cache_control fields.\n */\nfunction hasAnthropicCacheControl(content: MessageContentComplex[]): boolean {\n for (let i = 0; i < content.length; i++) {\n if ('cache_control' in content[i]) return true;\n }\n return false;\n}\n\n/**\n * Removes all Anthropic cache_control fields from messages\n * Used when switching from Anthropic to Bedrock provider\n * Returns a new array - only clones messages that require modification.\n */\nexport function stripAnthropicCacheControl<T extends MessageWithContent>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages)) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n\n for (let i = 0; i < updatedMessages.length; i++) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n\n if (!Array.isArray(content) || !hasAnthropicCacheControl(content)) {\n continue;\n }\n\n const clonedContent = deepCloneContent(content);\n for (let j = 0; j < clonedContent.length; j++) {\n const block = clonedContent[j] as Record<string, unknown>;\n if ('cache_control' in block) {\n delete block.cache_control;\n }\n }\n updatedMessages[i] = cloneMessage(originalMessage, clonedContent);\n }\n\n return updatedMessages;\n}\n\n/**\n * Checks if a message's content has Bedrock cachePoint blocks.\n */\nfunction hasBedrockCachePoint(content: MessageContentComplex[]): boolean {\n for (let i = 0; i < content.length; i++) {\n if (isCachePoint(content[i])) return true;\n }\n return false;\n}\n\n/**\n * Removes all Bedrock cachePoint blocks from messages\n * Used when switching from Bedrock to Anthropic provider\n * Returns a new array - only clones messages that require modification.\n */\nexport function stripBedrockCacheControl<T extends MessageWithContent>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages)) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n\n for (let i = 0; i < updatedMessages.length; i++) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n\n if (!Array.isArray(content) || !hasBedrockCachePoint(content)) {\n continue;\n }\n\n const clonedContent = deepCloneContent(content).filter(\n (block) => !isCachePoint(block as MessageContentComplex)\n );\n updatedMessages[i] = cloneMessage(originalMessage, clonedContent);\n }\n\n return updatedMessages;\n}\n\n/**\n * Adds Bedrock Converse API cache points to the last two messages.\n * Inserts `{ cachePoint: { type: 'default' } }` as a separate content block\n * immediately after the last text block in each targeted message.\n * Strips ALL existing cache control (both Bedrock and Anthropic formats) from all messages,\n * then adds fresh cache points to the last 2 messages in a single backward pass.\n * This ensures we don't accumulate stale cache points across multiple turns.\n * Returns a new array - only clones messages that require modification.\n * @param messages - The array of message objects.\n * @returns - A new array of message objects with cache points added.\n */\nexport function addBedrockCacheControl<\n T extends Partial<BaseMessage> & MessageWithContent,\n>(messages: T[]): T[] {\n if (!Array.isArray(messages) || messages.length < 2) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n let messagesModified = 0;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const originalMessage = updatedMessages[i];\n const isToolMessage =\n 'getType' in originalMessage &&\n typeof originalMessage.getType === 'function' &&\n originalMessage.getType() === 'tool';\n\n const content = originalMessage.content;\n const hasArrayContent = Array.isArray(content);\n const isEmptyString = typeof content === 'string' && content === '';\n const needsCacheAdd =\n messagesModified < 2 &&\n !isToolMessage &&\n !isEmptyString &&\n (typeof content === 'string' || hasArrayContent);\n\n if (!needsCacheAdd && !hasArrayContent) {\n continue;\n }\n\n let workingContent: MessageContentComplex[];\n let modified = false;\n\n if (hasArrayContent) {\n // Single pass: clone blocks, strip cache markers, find last\n // non-empty text block for cache point insertion — all at once.\n const src = content as MessageContentComplex[];\n workingContent = [];\n let lastNonEmptyTextIndex = -1;\n for (let j = 0; j < src.length; j++) {\n const block = src[j];\n if (isCachePoint(block)) {\n modified = true;\n continue;\n }\n const cloned = { ...block };\n if ('cache_control' in cloned) {\n delete (cloned as Record<string, unknown>).cache_control;\n modified = true;\n }\n const type = (cloned as { type?: string }).type;\n if (type === ContentTypes.TEXT || type === 'text') {\n const text = (cloned as { text?: string }).text;\n if (text != null && text.trim() !== '') {\n lastNonEmptyTextIndex = workingContent.length;\n }\n }\n workingContent.push(cloned as MessageContentComplex);\n }\n\n if (!modified && !needsCacheAdd) {\n continue;\n }\n\n // Insert cache point after the last non-empty text block.\n // Skip if no cacheable text content exists (whitespace-only messages).\n if (needsCacheAdd && lastNonEmptyTextIndex >= 0) {\n workingContent.splice(lastNonEmptyTextIndex + 1, 0, {\n cachePoint: { type: 'default' },\n } as MessageContentComplex);\n messagesModified++;\n }\n } else if (typeof content === 'string' && needsCacheAdd) {\n workingContent = [\n { type: ContentTypes.TEXT, text: content },\n { cachePoint: { type: 'default' } } as MessageContentComplex,\n ];\n messagesModified++;\n } else {\n continue;\n }\n\n updatedMessages[i] = cloneMessage(originalMessage, workingContent);\n }\n\n return updatedMessages;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cache.mjs","sources":["../../../src/messages/cache.ts"],"sourcesContent":["import {\n AIMessage,\n BaseMessage,\n ToolMessage,\n HumanMessage,\n SystemMessage,\n MessageContentComplex,\n} from '@langchain/core/messages';\nimport type { AnthropicMessage } from '@/types/messages';\nimport type Anthropic from '@anthropic-ai/sdk';\nimport { ContentTypes } from '@/common/enum';\n\ntype MessageWithContent = {\n content?: string | MessageContentComplex[];\n};\n\ntype MessageContentWithCacheControl = MessageContentComplex & {\n cache_control?: unknown;\n};\n\n/**\n * Deep clones a message's content to prevent mutation of the original.\n */\nfunction deepCloneContent<T extends string | MessageContentComplex[]>(\n content: T\n): T {\n if (typeof content === 'string') {\n return content;\n }\n if (Array.isArray(content)) {\n return content.map((block) => ({ ...block })) as T;\n }\n return content;\n}\n\n/**\n * Clones a message with new content. For LangChain BaseMessage instances,\n * constructs a proper class instance so that `instanceof` checks are preserved\n * in downstream code (e.g., ensureThinkingBlockInMessages).\n * For plain objects (AnthropicMessage), uses object spread.\n */\nfunction cloneMessage<T extends MessageWithContent>(\n message: T,\n content: string | MessageContentComplex[]\n): T {\n if (message instanceof BaseMessage) {\n const baseParams = {\n content,\n additional_kwargs: { ...message.additional_kwargs },\n response_metadata: { ...message.response_metadata },\n id: message.id,\n name: message.name,\n };\n\n const msgType = message.getType();\n switch (msgType) {\n case 'ai':\n return new AIMessage({\n ...baseParams,\n tool_calls: (message as unknown as AIMessage).tool_calls,\n }) as unknown as T;\n case 'human':\n return new HumanMessage(baseParams) as unknown as T;\n case 'system':\n return new SystemMessage(baseParams) as unknown as T;\n case 'tool':\n return new ToolMessage({\n ...baseParams,\n tool_call_id: (message as unknown as ToolMessage).tool_call_id,\n }) as unknown as T;\n default:\n break;\n }\n }\n\n const {\n lc_kwargs: _lc_kwargs,\n lc_serializable: _lc_serializable,\n lc_namespace: _lc_namespace,\n ...rest\n } = message as T & {\n lc_kwargs?: unknown;\n lc_serializable?: unknown;\n lc_namespace?: unknown;\n };\n\n const cloned = { ...rest, content } as T;\n\n // LangChain messages don't have a direct 'role' property - derive it from getType()\n if (\n 'getType' in message &&\n typeof message.getType === 'function' &&\n !('role' in cloned)\n ) {\n const msgType = (message as unknown as BaseMessage).getType();\n const roleMap: Record<string, string> = {\n human: 'user',\n ai: 'assistant',\n system: 'system',\n tool: 'tool',\n };\n (cloned as Record<string, unknown>).role = roleMap[msgType] || msgType;\n }\n\n return cloned;\n}\n\nfunction stripAnthropicCacheControlFromBlocks(\n content: MessageContentComplex[]\n): { content: MessageContentComplex[]; modified: boolean } {\n let modified = false;\n const strippedContent = content.map((block) => {\n if (!('cache_control' in block)) {\n return block;\n }\n\n const cloned: MessageContentWithCacheControl = { ...block };\n delete cloned.cache_control;\n modified = true;\n return cloned;\n });\n\n return { content: strippedContent, modified };\n}\n\nfunction sanitizeBedrockSystemMessage<T extends MessageWithContent>(\n message: T\n): T {\n const content = message.content;\n if (!Array.isArray(content)) {\n return message;\n }\n\n const stripped = stripAnthropicCacheControlFromBlocks(content);\n if (!stripped.modified) {\n return message;\n }\n\n return cloneMessage(message, stripped.content);\n}\n\n/**\n * Anthropic API: Adds cache control to the appropriate user messages in the payload.\n * Strips ALL existing cache control (both Anthropic and Bedrock formats) from all messages,\n * then adds fresh cache control to the last 2 user messages in a single backward pass.\n * This ensures we don't accumulate stale cache points across multiple turns.\n * Returns a new array - only clones messages that require modification.\n * @param messages - The array of message objects.\n * @returns - A new array of message objects with cache control added.\n */\nexport function addCacheControl<T extends AnthropicMessage | BaseMessage>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages) || messages.length < 2) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n let userMessagesModified = 0;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n const isUserMessage =\n ('getType' in originalMessage && originalMessage.getType() === 'human') ||\n ('role' in originalMessage && originalMessage.role === 'user');\n const hasArrayContent = Array.isArray(content);\n const needsCacheAdd =\n userMessagesModified < 2 &&\n isUserMessage &&\n (typeof content === 'string' || hasArrayContent);\n\n // Skip messages that don't need any work\n if (!needsCacheAdd && !hasArrayContent) {\n continue;\n }\n\n let workingContent: MessageContentComplex[];\n let modified = false;\n\n if (hasArrayContent) {\n // Single pass: clone blocks, strip cache markers and cache points,\n // find last text block index for cache insertion — all at once.\n const src = content as MessageContentComplex[];\n workingContent = [];\n let lastTextIndex = -1;\n for (let j = 0; j < src.length; j++) {\n const block = src[j];\n if (isCachePoint(block)) {\n modified = true;\n continue; // skip cache point blocks\n }\n const cloned = { ...block };\n if ('cache_control' in cloned) {\n delete (cloned as Record<string, unknown>).cache_control;\n modified = true;\n }\n if ('type' in cloned && cloned.type === 'text') {\n lastTextIndex = workingContent.length;\n }\n workingContent.push(cloned as MessageContentComplex);\n }\n\n if (!modified && !needsCacheAdd) {\n continue; // nothing to strip and no cache to add\n }\n\n // Add cache control to the last text block for user messages\n if (needsCacheAdd && lastTextIndex >= 0) {\n (\n workingContent[lastTextIndex] as Anthropic.TextBlockParam\n ).cache_control = {\n type: 'ephemeral',\n };\n userMessagesModified++;\n }\n } else if (typeof content === 'string' && needsCacheAdd) {\n workingContent = [\n { type: 'text', text: content, cache_control: { type: 'ephemeral' } },\n ] as unknown as MessageContentComplex[];\n userMessagesModified++;\n } else {\n continue;\n }\n\n updatedMessages[i] = cloneMessage(\n originalMessage as MessageWithContent,\n workingContent\n ) as T;\n }\n\n return updatedMessages;\n}\n\n/**\n * Checks if a content block is a cache point\n */\nfunction isCachePoint(block: MessageContentComplex): boolean {\n return 'cachePoint' in block && !('type' in block);\n}\n\n/**\n * Checks if a message's content has Anthropic cache_control fields.\n */\nfunction hasAnthropicCacheControl(content: MessageContentComplex[]): boolean {\n for (let i = 0; i < content.length; i++) {\n if ('cache_control' in content[i]) return true;\n }\n return false;\n}\n\n/**\n * Removes all Anthropic cache_control fields from messages\n * Used when switching from Anthropic to Bedrock provider\n * Returns a new array - only clones messages that require modification.\n */\nexport function stripAnthropicCacheControl<T extends MessageWithContent>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages)) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n\n for (let i = 0; i < updatedMessages.length; i++) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n\n if (!Array.isArray(content) || !hasAnthropicCacheControl(content)) {\n continue;\n }\n\n const clonedContent = deepCloneContent(content);\n for (let j = 0; j < clonedContent.length; j++) {\n const block = clonedContent[j] as Record<string, unknown>;\n if ('cache_control' in block) {\n delete block.cache_control;\n }\n }\n updatedMessages[i] = cloneMessage(originalMessage, clonedContent);\n }\n\n return updatedMessages;\n}\n\n/**\n * Checks if a message's content has Bedrock cachePoint blocks.\n */\nfunction hasBedrockCachePoint(content: MessageContentComplex[]): boolean {\n for (let i = 0; i < content.length; i++) {\n if (isCachePoint(content[i])) return true;\n }\n return false;\n}\n\n/**\n * Removes all Bedrock cachePoint blocks from messages\n * Used when switching from Bedrock to Anthropic provider\n * Returns a new array - only clones messages that require modification.\n */\nexport function stripBedrockCacheControl<T extends MessageWithContent>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages)) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n\n for (let i = 0; i < updatedMessages.length; i++) {\n const originalMessage = updatedMessages[i];\n const content = originalMessage.content;\n\n if (!Array.isArray(content) || !hasBedrockCachePoint(content)) {\n continue;\n }\n\n const clonedContent = deepCloneContent(content).filter(\n (block) => !isCachePoint(block as MessageContentComplex)\n );\n updatedMessages[i] = cloneMessage(originalMessage, clonedContent);\n }\n\n return updatedMessages;\n}\n\n/**\n * Adds Bedrock Converse API cache points to the last two messages.\n * Inserts `{ cachePoint: { type: 'default' } }` as a separate content block\n * immediately after the last text block in each targeted message.\n * Strips ALL existing cache control (both Bedrock and Anthropic formats) from all messages,\n * then adds fresh cache points to the last 2 messages in a single backward pass.\n * This ensures we don't accumulate stale cache points across multiple turns.\n * Returns a new array - only clones messages that require modification.\n * @param messages - The array of message objects.\n * @returns - A new array of message objects with cache points added.\n */\nexport function addBedrockCacheControl<\n T extends Partial<BaseMessage> & MessageWithContent,\n>(messages: T[]): T[] {\n if (!Array.isArray(messages) || messages.length < 2) {\n return messages;\n }\n\n const updatedMessages: T[] = [...messages];\n let messagesModified = 0;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const originalMessage = updatedMessages[i];\n const messageType =\n 'getType' in originalMessage &&\n typeof originalMessage.getType === 'function'\n ? originalMessage.getType()\n : undefined;\n const messageRole =\n 'role' in originalMessage && typeof originalMessage.role === 'string'\n ? originalMessage.role\n : undefined;\n\n const isSystemMessage =\n messageType === 'system' || messageRole === 'system';\n if (isSystemMessage) {\n updatedMessages[i] = sanitizeBedrockSystemMessage(originalMessage);\n continue;\n }\n\n const isToolMessage = messageType === 'tool' || messageRole === 'tool';\n const content = originalMessage.content;\n const hasArrayContent = Array.isArray(content);\n const isEmptyString = typeof content === 'string' && content === '';\n const needsCacheAdd =\n messagesModified < 2 &&\n !isToolMessage &&\n !isEmptyString &&\n (typeof content === 'string' || hasArrayContent);\n\n if (!needsCacheAdd && !hasArrayContent) {\n continue;\n }\n\n let workingContent: MessageContentComplex[];\n let modified = false;\n\n if (hasArrayContent) {\n // Single pass: clone blocks, strip cache markers, find last\n // non-empty text block for cache point insertion — all at once.\n const src = content as MessageContentComplex[];\n workingContent = [];\n let lastNonEmptyTextIndex = -1;\n for (let j = 0; j < src.length; j++) {\n const block = src[j];\n if (isCachePoint(block)) {\n modified = true;\n continue;\n }\n const cloned = { ...block };\n if ('cache_control' in cloned) {\n delete (cloned as Record<string, unknown>).cache_control;\n modified = true;\n }\n const type = (cloned as { type?: string }).type;\n if (type === ContentTypes.TEXT || type === 'text') {\n const text = (cloned as { text?: string }).text;\n if (text != null && text.trim() !== '') {\n lastNonEmptyTextIndex = workingContent.length;\n }\n }\n workingContent.push(cloned as MessageContentComplex);\n }\n\n if (!modified && !needsCacheAdd) {\n continue;\n }\n\n // Insert cache point after the last non-empty text block.\n // Skip if no cacheable text content exists (whitespace-only messages).\n if (needsCacheAdd && lastNonEmptyTextIndex >= 0) {\n workingContent.splice(lastNonEmptyTextIndex + 1, 0, {\n cachePoint: { type: 'default' },\n } as MessageContentComplex);\n messagesModified++;\n }\n } else if (typeof content === 'string' && needsCacheAdd) {\n workingContent = [\n { type: ContentTypes.TEXT, text: content },\n { cachePoint: { type: 'default' } } as MessageContentComplex,\n ];\n messagesModified++;\n } else {\n continue;\n }\n\n updatedMessages[i] = cloneMessage(originalMessage, workingContent);\n }\n\n return updatedMessages;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -126,7 +126,7 @@ export declare class AgentContext {
|
|
|
126
126
|
private summaryTokenCount;
|
|
127
127
|
/**
|
|
128
128
|
* Where the summary should be injected:
|
|
129
|
-
* - `'system_prompt'`: cross-run summary, included in
|
|
129
|
+
* - `'system_prompt'`: cross-run summary, included in the dynamic system tail
|
|
130
130
|
* - `'user_message'`: mid-run compaction, injected as HumanMessage on clean slate
|
|
131
131
|
* - `'none'`: no summary present
|
|
132
132
|
*/
|
|
@@ -193,7 +193,8 @@ export declare class AgentContext {
|
|
|
193
193
|
private buildProgrammaticOnlyToolsInstructions;
|
|
194
194
|
/**
|
|
195
195
|
* Gets the system runnable, creating it lazily if needed.
|
|
196
|
-
* Includes instructions, additional instructions, and
|
|
196
|
+
* Includes stable instructions, dynamic additional instructions, and
|
|
197
|
+
* programmatic-only tools documentation.
|
|
197
198
|
* Only rebuilds when marked stale (via markToolsAsDiscovered).
|
|
198
199
|
*/
|
|
199
200
|
get systemRunnable(): Runnable<BaseMessage[], (BaseMessage | SystemMessage)[], RunnableConfig<Record<string, unknown>>> | undefined;
|
|
@@ -203,10 +204,16 @@ export declare class AgentContext {
|
|
|
203
204
|
*/
|
|
204
205
|
initializeSystemRunnable(): void;
|
|
205
206
|
/**
|
|
206
|
-
* Builds the
|
|
207
|
+
* Builds the cacheable instructions string (without creating SystemMessage).
|
|
207
208
|
* Includes agent identity preamble and handoff context when available.
|
|
208
209
|
*/
|
|
209
|
-
private
|
|
210
|
+
private buildStableInstructionsString;
|
|
211
|
+
/**
|
|
212
|
+
* Builds the dynamic system-tail string (without creating SystemMessage).
|
|
213
|
+
* Keep this out of prompt-cache-marked content so volatile context does not
|
|
214
|
+
* invalidate the stable prefix.
|
|
215
|
+
*/
|
|
216
|
+
private buildDynamicInstructionsString;
|
|
210
217
|
/**
|
|
211
218
|
* Builds the agent identity preamble including handoff context if present.
|
|
212
219
|
* This helps the agent understand its role in the multi-agent workflow.
|
|
@@ -217,6 +224,9 @@ export declare class AgentContext {
|
|
|
217
224
|
* Only called when content has actually changed.
|
|
218
225
|
*/
|
|
219
226
|
private buildSystemRunnable;
|
|
227
|
+
private hasAnthropicPromptCache;
|
|
228
|
+
private hasBedrockPromptCache;
|
|
229
|
+
private buildSystemMessage;
|
|
220
230
|
/**
|
|
221
231
|
* Reset context for a new run
|
|
222
232
|
*/
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { UsageMetadata } from '@langchain/core/messages';
|
|
2
|
+
import type * as t from '@/types';
|
|
3
|
+
import { Providers } from '@/common';
|
|
4
|
+
type LivePromptCacheProvider = Providers.ANTHROPIC | Providers.BEDROCK;
|
|
5
|
+
type PromptCacheExpectedSystemBlock = {
|
|
6
|
+
type: 'text';
|
|
7
|
+
text: string;
|
|
8
|
+
cache_control?: {
|
|
9
|
+
type: 'ephemeral';
|
|
10
|
+
};
|
|
11
|
+
} | {
|
|
12
|
+
cachePoint: {
|
|
13
|
+
type: 'default';
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
type LivePromptCacheClientOptions = t.ClientOptions | t.BedrockAnthropicClientOptions;
|
|
17
|
+
export declare function buildStableInstructions({ nonce, providerLabel, }: {
|
|
18
|
+
nonce: string;
|
|
19
|
+
providerLabel: string;
|
|
20
|
+
}): string;
|
|
21
|
+
export declare function buildDynamicInstructions({ marker, tailDescription, }: {
|
|
22
|
+
marker: string;
|
|
23
|
+
tailDescription: string;
|
|
24
|
+
}): string;
|
|
25
|
+
export declare function waitForCachePropagation(): Promise<void>;
|
|
26
|
+
export declare function assertSystemPayloadShape({ agentId, provider, clientOptions, stableInstructions, dynamicInstructions, expectedContent, }: {
|
|
27
|
+
agentId: string;
|
|
28
|
+
provider: LivePromptCacheProvider;
|
|
29
|
+
clientOptions: LivePromptCacheClientOptions;
|
|
30
|
+
stableInstructions: string;
|
|
31
|
+
dynamicInstructions: string;
|
|
32
|
+
expectedContent: PromptCacheExpectedSystemBlock[];
|
|
33
|
+
}): Promise<void>;
|
|
34
|
+
export declare function runLiveTurn({ provider, providerLabel, clientOptions, runId, threadId, stableInstructions, dynamicInstructions, }: {
|
|
35
|
+
provider: LivePromptCacheProvider;
|
|
36
|
+
providerLabel: string;
|
|
37
|
+
clientOptions: LivePromptCacheClientOptions;
|
|
38
|
+
runId: string;
|
|
39
|
+
threadId: string;
|
|
40
|
+
stableInstructions: string;
|
|
41
|
+
dynamicInstructions: string;
|
|
42
|
+
}): Promise<{
|
|
43
|
+
text: string;
|
|
44
|
+
usage: UsageMetadata;
|
|
45
|
+
}>;
|
|
46
|
+
export {};
|
|
@@ -311,10 +311,12 @@ export interface AgentInputs {
|
|
|
311
311
|
toolMap?: ToolMap;
|
|
312
312
|
tools?: GraphTools;
|
|
313
313
|
provider: Providers;
|
|
314
|
+
/** Stable/cacheable system instructions. */
|
|
314
315
|
instructions?: string;
|
|
315
316
|
streamBuffer?: number;
|
|
316
317
|
maxContextTokens?: number;
|
|
317
318
|
clientOptions?: ClientOptions;
|
|
319
|
+
/** Dynamic system tail appended after stable instructions without provider cache markers. */
|
|
318
320
|
additional_instructions?: string;
|
|
319
321
|
reasoningKey?: 'reasoning_content' | 'reasoning';
|
|
320
322
|
/** Format content blocks as strings (for legacy compatibility i.e. Ollama/Azure Serverless) */
|
|
@@ -340,7 +342,7 @@ export interface AgentInputs {
|
|
|
340
342
|
summarizationEnabled?: boolean;
|
|
341
343
|
summarizationConfig?: SummarizationConfig;
|
|
342
344
|
/** Cross-run summary from a previous run, forwarded from formatAgentMessages.
|
|
343
|
-
* Injected into the system
|
|
345
|
+
* Injected into the dynamic system tail via AgentContext. */
|
|
344
346
|
initialSummary?: {
|
|
345
347
|
text: string;
|
|
346
348
|
tokenCount: number;
|
|
@@ -63,7 +63,9 @@ export interface AgentStateChannels {
|
|
|
63
63
|
messages: BaseMessage[];
|
|
64
64
|
next: string;
|
|
65
65
|
[key: string]: unknown;
|
|
66
|
+
/** Stable/cacheable system instructions. */
|
|
66
67
|
instructions?: string;
|
|
68
|
+
/** Dynamic system tail appended after stable instructions. */
|
|
67
69
|
additional_instructions?: string;
|
|
68
70
|
}
|
|
69
71
|
export interface Member {
|
package/package.json
CHANGED
|
@@ -20,6 +20,16 @@ import { addCacheControl } from '@/messages/cache';
|
|
|
20
20
|
import { DEFAULT_RESERVE_RATIO } from '@/messages';
|
|
21
21
|
import { toJsonSchema } from '@/utils/schema';
|
|
22
22
|
|
|
23
|
+
type AgentSystemTextBlock = {
|
|
24
|
+
type: 'text';
|
|
25
|
+
text: string;
|
|
26
|
+
cache_control?: { type: 'ephemeral' };
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
type AgentSystemContentBlock =
|
|
30
|
+
| AgentSystemTextBlock
|
|
31
|
+
| { cachePoint: { type: 'default' } };
|
|
32
|
+
|
|
23
33
|
/**
|
|
24
34
|
* Encapsulates agent-specific state that can vary between agents in a multi-agent system
|
|
25
35
|
*/
|
|
@@ -249,7 +259,7 @@ export class AgentContext {
|
|
|
249
259
|
private summaryTokenCount: number = 0;
|
|
250
260
|
/**
|
|
251
261
|
* Where the summary should be injected:
|
|
252
|
-
* - `'system_prompt'`: cross-run summary, included in
|
|
262
|
+
* - `'system_prompt'`: cross-run summary, included in the dynamic system tail
|
|
253
263
|
* - `'user_message'`: mid-run compaction, injected as HumanMessage on clean slate
|
|
254
264
|
* - `'none'`: no summary present
|
|
255
265
|
*/
|
|
@@ -417,7 +427,8 @@ export class AgentContext {
|
|
|
417
427
|
|
|
418
428
|
/**
|
|
419
429
|
* Gets the system runnable, creating it lazily if needed.
|
|
420
|
-
* Includes instructions, additional instructions, and
|
|
430
|
+
* Includes stable instructions, dynamic additional instructions, and
|
|
431
|
+
* programmatic-only tools documentation.
|
|
421
432
|
* Only rebuilds when marked stale (via markToolsAsDiscovered).
|
|
422
433
|
*/
|
|
423
434
|
get systemRunnable():
|
|
@@ -431,8 +442,10 @@ export class AgentContext {
|
|
|
431
442
|
return this.cachedSystemRunnable;
|
|
432
443
|
}
|
|
433
444
|
|
|
434
|
-
|
|
435
|
-
|
|
445
|
+
this.cachedSystemRunnable = this.buildSystemRunnable({
|
|
446
|
+
stableInstructions: this.buildStableInstructionsString(),
|
|
447
|
+
dynamicInstructions: this.buildDynamicInstructionsString(),
|
|
448
|
+
});
|
|
436
449
|
this.systemRunnableStale = false;
|
|
437
450
|
return this.cachedSystemRunnable;
|
|
438
451
|
}
|
|
@@ -443,17 +456,19 @@ export class AgentContext {
|
|
|
443
456
|
*/
|
|
444
457
|
initializeSystemRunnable(): void {
|
|
445
458
|
if (this.systemRunnableStale || this.cachedSystemRunnable === undefined) {
|
|
446
|
-
|
|
447
|
-
|
|
459
|
+
this.cachedSystemRunnable = this.buildSystemRunnable({
|
|
460
|
+
stableInstructions: this.buildStableInstructionsString(),
|
|
461
|
+
dynamicInstructions: this.buildDynamicInstructionsString(),
|
|
462
|
+
});
|
|
448
463
|
this.systemRunnableStale = false;
|
|
449
464
|
}
|
|
450
465
|
}
|
|
451
466
|
|
|
452
467
|
/**
|
|
453
|
-
* Builds the
|
|
468
|
+
* Builds the cacheable instructions string (without creating SystemMessage).
|
|
454
469
|
* Includes agent identity preamble and handoff context when available.
|
|
455
470
|
*/
|
|
456
|
-
private
|
|
471
|
+
private buildStableInstructionsString(): string {
|
|
457
472
|
const parts: string[] = [];
|
|
458
473
|
|
|
459
474
|
const identityPreamble = this.buildIdentityPreamble();
|
|
@@ -465,6 +480,22 @@ export class AgentContext {
|
|
|
465
480
|
parts.push(this.instructions);
|
|
466
481
|
}
|
|
467
482
|
|
|
483
|
+
const programmaticToolsDoc = this.buildProgrammaticOnlyToolsInstructions();
|
|
484
|
+
if (programmaticToolsDoc) {
|
|
485
|
+
parts.push(programmaticToolsDoc);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
return parts.join('\n\n');
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Builds the dynamic system-tail string (without creating SystemMessage).
|
|
493
|
+
* Keep this out of prompt-cache-marked content so volatile context does not
|
|
494
|
+
* invalidate the stable prefix.
|
|
495
|
+
*/
|
|
496
|
+
private buildDynamicInstructionsString(): string {
|
|
497
|
+
const parts: string[] = [];
|
|
498
|
+
|
|
468
499
|
if (
|
|
469
500
|
this.additionalInstructions != null &&
|
|
470
501
|
this.additionalInstructions !== ''
|
|
@@ -472,14 +503,10 @@ export class AgentContext {
|
|
|
472
503
|
parts.push(this.additionalInstructions);
|
|
473
504
|
}
|
|
474
505
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
// Cross-run summary: include in system prompt so the model has context
|
|
481
|
-
// from the prior run. Mid-run summaries are injected as a HumanMessage
|
|
482
|
-
// on the post-compaction clean slate instead (see buildSystemRunnable).
|
|
506
|
+
// Cross-run summary: include in the system tail so the model has context
|
|
507
|
+
// from the prior run without invalidating the cacheable prefix. Mid-run
|
|
508
|
+
// summaries are injected as a HumanMessage on the post-compaction clean
|
|
509
|
+
// slate instead (see buildSystemRunnable).
|
|
483
510
|
if (
|
|
484
511
|
this._summaryLocation === 'system_prompt' &&
|
|
485
512
|
this.summaryText != null &&
|
|
@@ -523,9 +550,13 @@ export class AgentContext {
|
|
|
523
550
|
* Build system runnable from pre-built instructions string.
|
|
524
551
|
* Only called when content has actually changed.
|
|
525
552
|
*/
|
|
526
|
-
private buildSystemRunnable(
|
|
527
|
-
|
|
528
|
-
|
|
553
|
+
private buildSystemRunnable({
|
|
554
|
+
stableInstructions,
|
|
555
|
+
dynamicInstructions,
|
|
556
|
+
}: {
|
|
557
|
+
stableInstructions: string;
|
|
558
|
+
dynamicInstructions: string;
|
|
559
|
+
}):
|
|
529
560
|
| Runnable<
|
|
530
561
|
BaseMessage[],
|
|
531
562
|
(BaseMessage | SystemMessage)[],
|
|
@@ -537,35 +568,17 @@ export class AgentContext {
|
|
|
537
568
|
this.summaryText != null &&
|
|
538
569
|
this.summaryText !== '';
|
|
539
570
|
|
|
540
|
-
if (!
|
|
571
|
+
if (!stableInstructions && !dynamicInstructions && !hasMidRunSummary) {
|
|
541
572
|
this.systemMessageTokens = 0;
|
|
542
573
|
return undefined;
|
|
543
574
|
}
|
|
544
575
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
| undefined;
|
|
552
|
-
if (anthropicOptions?.promptCache === true) {
|
|
553
|
-
usePromptCache = true;
|
|
554
|
-
finalInstructions = {
|
|
555
|
-
content: [
|
|
556
|
-
{
|
|
557
|
-
type: 'text',
|
|
558
|
-
text: instructionsString,
|
|
559
|
-
cache_control: { type: 'ephemeral' },
|
|
560
|
-
},
|
|
561
|
-
],
|
|
562
|
-
};
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
const systemMessage = instructionsString
|
|
567
|
-
? new SystemMessage(finalInstructions)
|
|
568
|
-
: undefined;
|
|
576
|
+
const usePromptCache = this.hasAnthropicPromptCache();
|
|
577
|
+
const systemMessage = this.buildSystemMessage({
|
|
578
|
+
stableInstructions,
|
|
579
|
+
dynamicInstructions,
|
|
580
|
+
usePromptCache,
|
|
581
|
+
});
|
|
569
582
|
|
|
570
583
|
if (this.tokenCounter) {
|
|
571
584
|
this.systemMessageTokens = systemMessage
|
|
@@ -615,6 +628,72 @@ export class AgentContext {
|
|
|
615
628
|
}).withConfig({ runName: 'prompt' });
|
|
616
629
|
}
|
|
617
630
|
|
|
631
|
+
private hasAnthropicPromptCache(): boolean {
|
|
632
|
+
if (this.provider !== Providers.ANTHROPIC) {
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
const anthropicOptions = this.clientOptions as
|
|
636
|
+
| t.AnthropicClientOptions
|
|
637
|
+
| undefined;
|
|
638
|
+
return anthropicOptions?.promptCache === true;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
private hasBedrockPromptCache(): boolean {
|
|
642
|
+
if (this.provider !== Providers.BEDROCK) {
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
const bedrockOptions = this.clientOptions as
|
|
646
|
+
| t.BedrockAnthropicClientOptions
|
|
647
|
+
| undefined;
|
|
648
|
+
return bedrockOptions?.promptCache === true;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
private buildSystemMessage({
|
|
652
|
+
stableInstructions,
|
|
653
|
+
dynamicInstructions,
|
|
654
|
+
usePromptCache,
|
|
655
|
+
}: {
|
|
656
|
+
stableInstructions: string;
|
|
657
|
+
dynamicInstructions: string;
|
|
658
|
+
usePromptCache: boolean;
|
|
659
|
+
}): SystemMessage | undefined {
|
|
660
|
+
if (!stableInstructions && !dynamicInstructions) {
|
|
661
|
+
return undefined;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
if (usePromptCache) {
|
|
665
|
+
const content: AgentSystemContentBlock[] = [];
|
|
666
|
+
if (stableInstructions) {
|
|
667
|
+
content.push({
|
|
668
|
+
type: 'text',
|
|
669
|
+
text: stableInstructions,
|
|
670
|
+
cache_control: { type: 'ephemeral' },
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
if (dynamicInstructions) {
|
|
674
|
+
content.push({ type: 'text', text: dynamicInstructions });
|
|
675
|
+
}
|
|
676
|
+
return new SystemMessage({ content } as BaseMessageFields);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if (this.hasBedrockPromptCache() && stableInstructions) {
|
|
680
|
+
const content: AgentSystemContentBlock[] = [
|
|
681
|
+
{ type: 'text', text: stableInstructions },
|
|
682
|
+
{ cachePoint: { type: 'default' } },
|
|
683
|
+
];
|
|
684
|
+
if (dynamicInstructions) {
|
|
685
|
+
content.push({ type: 'text', text: dynamicInstructions });
|
|
686
|
+
}
|
|
687
|
+
return new SystemMessage({ content } as BaseMessageFields);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
return new SystemMessage(
|
|
691
|
+
[stableInstructions, dynamicInstructions]
|
|
692
|
+
.filter((part) => part !== '')
|
|
693
|
+
.join('\n\n')
|
|
694
|
+
);
|
|
695
|
+
}
|
|
696
|
+
|
|
618
697
|
/**
|
|
619
698
|
* Reset context for a new run
|
|
620
699
|
*/
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// src/agents/__tests__/AgentContext.anthropic.live.test.ts
|
|
2
|
+
/**
|
|
3
|
+
* Live Anthropic prompt-cache verification.
|
|
4
|
+
*
|
|
5
|
+
* Run with:
|
|
6
|
+
* RUN_ANTHROPIC_PROMPT_CACHE_LIVE_TESTS=1 ANTHROPIC_API_KEY=... npm test -- AgentContext.anthropic.live.test.ts --runInBand
|
|
7
|
+
*/
|
|
8
|
+
import { config as dotenvConfig } from 'dotenv';
|
|
9
|
+
dotenvConfig();
|
|
10
|
+
|
|
11
|
+
import { describe, expect, it } from '@jest/globals';
|
|
12
|
+
import type * as t from '@/types';
|
|
13
|
+
import {
|
|
14
|
+
runLiveTurn,
|
|
15
|
+
assertSystemPayloadShape,
|
|
16
|
+
buildDynamicInstructions,
|
|
17
|
+
buildStableInstructions,
|
|
18
|
+
waitForCachePropagation,
|
|
19
|
+
} from './promptCacheLiveHelpers';
|
|
20
|
+
import { Providers } from '@/common';
|
|
21
|
+
|
|
22
|
+
const shouldRunLive =
|
|
23
|
+
process.env.RUN_ANTHROPIC_PROMPT_CACHE_LIVE_TESTS === '1' &&
|
|
24
|
+
process.env.ANTHROPIC_API_KEY != null &&
|
|
25
|
+
process.env.ANTHROPIC_API_KEY !== '';
|
|
26
|
+
|
|
27
|
+
const describeIfLive = shouldRunLive ? describe : describe.skip;
|
|
28
|
+
|
|
29
|
+
const modelName =
|
|
30
|
+
process.env.ANTHROPIC_PROMPT_CACHE_MODEL ?? 'claude-sonnet-4-5';
|
|
31
|
+
const providerLabel = 'Anthropic';
|
|
32
|
+
|
|
33
|
+
function createClientOptions(): t.AnthropicClientOptions {
|
|
34
|
+
return {
|
|
35
|
+
modelName,
|
|
36
|
+
temperature: 0,
|
|
37
|
+
maxTokens: 8,
|
|
38
|
+
streaming: true,
|
|
39
|
+
streamUsage: true,
|
|
40
|
+
promptCache: true,
|
|
41
|
+
clientOptions: {
|
|
42
|
+
defaultHeaders: {
|
|
43
|
+
'anthropic-beta': 'prompt-caching-2024-07-31',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
describeIfLive('AgentContext Anthropic prompt cache live API', () => {
|
|
50
|
+
it('caches only the stable system prefix while dynamic tail changes', async () => {
|
|
51
|
+
const nonce = `agent-cache-live-${Date.now()}`;
|
|
52
|
+
const clientOptions = createClientOptions();
|
|
53
|
+
const stableInstructions = buildStableInstructions({
|
|
54
|
+
nonce,
|
|
55
|
+
providerLabel,
|
|
56
|
+
});
|
|
57
|
+
const firstDynamicInstructions = buildDynamicInstructions({
|
|
58
|
+
marker: 'alpha',
|
|
59
|
+
tailDescription:
|
|
60
|
+
'The Dynamic Marker line is runtime context and must remain outside the cached prefix.',
|
|
61
|
+
});
|
|
62
|
+
const secondDynamicInstructions = buildDynamicInstructions({
|
|
63
|
+
marker: 'bravo',
|
|
64
|
+
tailDescription:
|
|
65
|
+
'The Dynamic Marker line is runtime context and must remain outside the cached prefix.',
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
await assertSystemPayloadShape({
|
|
69
|
+
agentId: 'live-cache-shape-check',
|
|
70
|
+
provider: Providers.ANTHROPIC,
|
|
71
|
+
clientOptions,
|
|
72
|
+
stableInstructions,
|
|
73
|
+
dynamicInstructions: firstDynamicInstructions,
|
|
74
|
+
expectedContent: [
|
|
75
|
+
{
|
|
76
|
+
type: 'text',
|
|
77
|
+
text: stableInstructions,
|
|
78
|
+
cache_control: { type: 'ephemeral' },
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
type: 'text',
|
|
82
|
+
text: firstDynamicInstructions,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const first = await runLiveTurn({
|
|
88
|
+
provider: Providers.ANTHROPIC,
|
|
89
|
+
providerLabel,
|
|
90
|
+
clientOptions,
|
|
91
|
+
runId: `${nonce}-first`,
|
|
92
|
+
threadId: `${nonce}-thread`,
|
|
93
|
+
stableInstructions,
|
|
94
|
+
dynamicInstructions: firstDynamicInstructions,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
expect(first.text.toLowerCase()).toContain('alpha');
|
|
98
|
+
expect(first.usage.input_token_details?.cache_creation).toBeGreaterThan(0);
|
|
99
|
+
expect(first.usage.input_token_details?.cache_read ?? 0).toBe(0);
|
|
100
|
+
|
|
101
|
+
await waitForCachePropagation();
|
|
102
|
+
|
|
103
|
+
const second = await runLiveTurn({
|
|
104
|
+
provider: Providers.ANTHROPIC,
|
|
105
|
+
providerLabel,
|
|
106
|
+
clientOptions,
|
|
107
|
+
runId: `${nonce}-second`,
|
|
108
|
+
threadId: `${nonce}-thread`,
|
|
109
|
+
stableInstructions,
|
|
110
|
+
dynamicInstructions: secondDynamicInstructions,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
expect(second.text.toLowerCase()).toContain('bravo');
|
|
114
|
+
expect(second.usage.input_token_details?.cache_read).toBeGreaterThan(0);
|
|
115
|
+
}, 120_000);
|
|
116
|
+
});
|