From 3540dcad0f0ef6fce9166a5833c1b6397fd9fedc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s?= Date: Tue, 5 Nov 2024 15:10:25 +0100 Subject: [PATCH] Allow caching system, user and assistant messages for Anthropic (#524) * Allow caching system, user and assistant messages for Anthropic We want to allow with our syntaxt for users to tag messages with Anthropic cache control tag. Also as part of this PR we extract first system messages in an anthropic prompt as part of the configuration. This way we can remove the warning And last change in this PR is that we allow system messages to have more than one message. This is something that's not valid in OpenAI but is valid in Anthropic so we allow this at compiler level but we restric for OpenAI at Rules level * chore: updated docs * chore: update docs 2 * chore: updated docs 3 * chore: updated docs 4 --------- Co-authored-by: Gerard Clos --- apps/gateway/package.json | 4 +- .../Editor/Playground/Preview/index.tsx | 1 + .../Editor/Playground/Preview.tsx | 62 ++-- docs/assets/provider_rules_1.png | Bin 78601 -> 69649 bytes docs/guides/getting-started/providers.mdx | 4 +- .../getting-started/providers/anthropic.mdx | 43 +++ docs/guides/prompt-manager/custom-rules.mdx | 27 +- .../provider-rules/anthropic.mdx | 75 ++++ .../prompt-manager/provider-rules/google.mdx | 23 ++ docs/mint.json | 22 +- .../src/compiler/base/nodes/tags/content.ts | 7 +- .../compiler/base/nodes/tags/message.test.ts | 283 +++++++++++++++ .../src/compiler/base/nodes/tags/message.ts | 8 +- packages/compiler/src/compiler/chain.test.ts | 124 ++++++- .../compiler/src/compiler/compile.test.ts | 255 +++---------- packages/compiler/src/compiler/compile.ts | 19 +- .../compiler/src/compiler/test/helpers.ts | 14 + packages/compiler/src/types/message.ts | 10 +- packages/core/src/services/ai/helpers.ts | 1 + packages/core/src/services/ai/index.test.ts | 15 +- packages/core/src/services/ai/index.ts | 16 +- .../ai/providers/rules/anthropic.test.ts | 157 +++++--- .../services/ai/providers/rules/anthropic.ts | 40 ++- .../ai/providers/rules/google.test.ts | 158 ++++---- .../src/services/ai/providers/rules/google.ts | 37 +- .../helpers/enforceAllSystemMessagesFirst.ts | 53 +++ .../src/services/ai/providers/rules/index.ts | 38 +- .../providers/rules/providerMetadata/index.ts | 151 ++++++++ .../ai/providers/rules/vercel.test.ts | 338 ++++++++++++++++++ .../src/services/ai/providers/rules/vercel.ts | 129 +++++++ .../services/chains/ChainValidator/index.ts | 3 +- packages/core/src/services/chains/run.test.ts | 2 +- .../commits/runDocumentAtCommit.test.ts | 4 +- .../services/providerLogs/serialize.test.ts | 32 +- .../src/services/providerLogs/serialize.ts | 5 +- .../src/ds/molecules/Chat/Message/index.tsx | 2 +- pnpm-lock.yaml | 2 +- 37 files changed, 1643 insertions(+), 521 deletions(-) create mode 100644 docs/guides/getting-started/providers/anthropic.mdx create mode 100644 docs/guides/prompt-manager/provider-rules/anthropic.mdx create mode 100644 docs/guides/prompt-manager/provider-rules/google.mdx create mode 100644 packages/compiler/src/compiler/base/nodes/tags/message.test.ts create mode 100644 packages/compiler/src/compiler/test/helpers.ts create mode 100644 packages/core/src/services/ai/providers/rules/helpers/enforceAllSystemMessagesFirst.ts create mode 100644 packages/core/src/services/ai/providers/rules/providerMetadata/index.ts create mode 100644 packages/core/src/services/ai/providers/rules/vercel.test.ts create mode 100644 packages/core/src/services/ai/providers/rules/vercel.ts diff --git a/apps/gateway/package.json b/apps/gateway/package.json index 15e567a0e..b93fb1b58 100644 --- a/apps/gateway/package.json +++ b/apps/gateway/package.json @@ -7,7 +7,7 @@ "scripts": { "dev": "tsx watch src/server", "build": "tsup --config tsup.config.ts && pnpm run sentry:sourcemaps", - "dev:debug": "tsx watch --inspect-brk src/server", + "dev:debug": "tsx watch --inspect src/server", "lint": "eslint src/", "prettier": "prettier --write \"**/*.{ts,tsx,md}\"", "tc": "tsc --noEmit", @@ -38,7 +38,7 @@ "@types/node": "^22.5.1", "@types/uuid": "^10.0.0", "tsup": "^8.2.4", - "tsx": "^4.16.2", + "tsx": "^4.19.2", "vitest": "^2.0.4" } } diff --git a/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/editor/_components/EvaluationEditor/Editor/Playground/Preview/index.tsx b/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/editor/_components/EvaluationEditor/Editor/Playground/Preview/index.tsx index 892794e2c..e10e7f4dd 100644 --- a/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/editor/_components/EvaluationEditor/Editor/Playground/Preview/index.tsx +++ b/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/editor/_components/EvaluationEditor/Editor/Playground/Preview/index.tsx @@ -77,6 +77,7 @@ export default function Preview({ {error || (metadata?.errors.length ?? 0) > 0 ? ( Run prompt diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/_components/DocumentEditor/Editor/Playground/Preview.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/_components/DocumentEditor/Editor/Playground/Preview.tsx index 548c39309..59ab892c0 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/_components/DocumentEditor/Editor/Playground/Preview.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/_components/DocumentEditor/Editor/Playground/Preview.tsx @@ -10,6 +10,7 @@ import { AppliedRules, applyCustomRules, LATITUDE_DOCS_URL, + ProviderRules, } from '@latitude-data/core/browser' import { Alert, @@ -27,6 +28,34 @@ import { ROUTES } from '$/services/routes' import useProviderApiKeys from '$/stores/providerApiKeys' import Link from 'next/link' +function WarningLink({ providerRule }: { providerRule: ProviderRules }) { + return ( + + + Learn more + + + ) +} + +function Warnings({ warnings }: { warnings: AppliedRules }) { + const rules = warnings.rules + if (!rules.length) return null + + return rules.map((rule, index) => ( + } + /> + )) +} + export default function Preview({ metadata, parameters, @@ -91,6 +120,7 @@ export default function Preview({ const rule = applyCustomRules({ providerType: provider.provider, messages: conversation.messages, + config: conversation.config, }) setFixedMessages(rule?.messages ?? conversation.messages) @@ -99,7 +129,7 @@ export default function Preview({ return (
- + {warningRule ? : null}
0 ? ( Run prompt @@ -158,32 +189,3 @@ export default function Preview({
) } - -function WarningMessage({ rule }: { rule: AppliedRules | undefined }) { - if (!rule) return null - - switch (rule.rule) { - case 'AnthropicMultipleSystemMessagesUnsupported': - return ( - - - Learn more - - - } - /> - ) - case 'GoogleSingleStartingSystemMessageSupported': - return - default: - return null - } -} diff --git a/docs/assets/provider_rules_1.png b/docs/assets/provider_rules_1.png index 059b65dfa7cf249cbef2f027320fa2009afcb86b..0b59b91b7bf34cf3de9d83c2e4630e29ead9788e 100644 GIT binary patch literal 69649 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 diff --git a/docs/guides/getting-started/providers.mdx b/docs/guides/getting-started/providers.mdx index 197c48254..952133d45 100644 --- a/docs/guides/getting-started/providers.mdx +++ b/docs/guides/getting-started/providers.mdx @@ -1,11 +1,11 @@ --- -title: Providers +title: Setup Providers description: Learn how to add providers into Latitude to use it in your prompts. --- ## Overview -Providers in Latitude are the foundation for connecting to various AI models and services. +Providers in Latitude are the foundation for connecting to various AI models and services. The name you introduce for each provider will be the one you will have to use in your prompts. diff --git a/docs/guides/getting-started/providers/anthropic.mdx b/docs/guides/getting-started/providers/anthropic.mdx new file mode 100644 index 000000000..5d35ec790 --- /dev/null +++ b/docs/guides/getting-started/providers/anthropic.mdx @@ -0,0 +1,43 @@ +--- +title: Anthropic +description: Common questions about Anthropic provider +--- + +## How do I use the Anthropic cache? + +[Anthropic cache](https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching) allows you to cache parts of a prompt. As explained in their documentation, you need to opt-in within the prompt to start caching parts of it. + +To do this, add `cacheControl: true` to the front matter of your prompt. + +```markdown +--- +provider: name-of-your-antropic-provider-in-latitude +model: claude-3-5-sonnet-20241022 +cacheControl: true +--- +``` + +Once this is set up, you can start caching specific parts of the prompt: + +``` +This part of the text is not cached + + Read this large book and answer users' questions. + + ...BIG_BOOK_CONTENT... + + +```` + +If you want an entire message to be cached, add the cache directive to the `user`, `assistant`, or `system` tags: + +``` + + This text will be cached. + + This text will also be cached. + + And this text as well. + +``` + diff --git a/docs/guides/prompt-manager/custom-rules.mdx b/docs/guides/prompt-manager/custom-rules.mdx index 98048ddc7..836e724ea 100644 --- a/docs/guides/prompt-manager/custom-rules.mdx +++ b/docs/guides/prompt-manager/custom-rules.mdx @@ -7,27 +7,6 @@ description: Some providers have specific rules that you need to follow when usi Some providers have specific rules that you need to follow when using them in your prompts. These rules are called "custom rules" and are enforced by the Latitude engine. -## Anthropic - -Anthropic only supports system messages at the beginning of the conversation. All other system messages get converted to user messages. - -![](/assets/provider_rules_1.png) - -You can add the system message via a property in the front matter of your prompt: - -``` ---- -provider: Anthropic -model: claude-3-5-sonnet-latest -system: | - This is a multi-line system prompt - that spans multiple lines ---- - -... - -``` - -## Google - -Google only supports system messages at the beginning of the conversation. All other system messages are converted to user messages. +## Provider rules +- [Anthropic](/guides/prompt-manager/provider-rules/anthropic) +- [Google](/guides/prompt-manager/provider-rules/google) diff --git a/docs/guides/prompt-manager/provider-rules/anthropic.mdx b/docs/guides/prompt-manager/provider-rules/anthropic.mdx new file mode 100644 index 000000000..0f2561edf --- /dev/null +++ b/docs/guides/prompt-manager/provider-rules/anthropic.mdx @@ -0,0 +1,75 @@ +--- +title: Anthropic provider rules +description: Learn about the rules you need to follow when using the Anthropic provider in Latitude. +--- + +1. [System messages must be followed by at least by another message](#rule-1-not-only-system-messages) +2. [No system messages are allowed after an assistant or user message](#rule-2-no-system-messages-after-assistant-or-user-messages) + +### System messages must be followed by at least another message + +Anthropic does not consider system messages as part of the list of general messages and thus, if you don't add at least a user or assistant message, the list of messages sent to anthropic would be empty, which would result in an error. + +```json +{ + "system": [ + { + "type": "text", + "text": "You are an AI assistant tasked with analyzing literary works. Your goal is to provide insightful commentary on themes, characters, and writing style.\n" + }, + { + "type": "text", + "text": "", + "cache_control": { "type": "ephemeral" } + } + ], + "messages": [] // this is invalid +} +``` + +So, the following prompt in Latitude is invalid for an Anthropic provider: + +``` +--- +provider: Anthropic +model: claude-3-5-sonnet-latest +--- + +This is a system message +``` + +This would generate the following warning: + +![](/assets/provider_rules_1.png) + +Instead, add at least another message wrapped in a `` or `` tag: + +``` +--- +provider: Anthropic +model: claude-3-5-sonnet-latest +--- + +This is a system message + +This is a user message +``` + +### No system messages are allowed after an assistant or user message + +Any system message added after an assistant or user message will be automatically converted into a user message: + +``` +--- +provider: Anthropic +model: claude-3-5-sonnet-latest +--- + +This is a system message + +This is a user message + +This is another system message /* This message will be converted to a user message */ +``` + +This is because Anthropic does not consider system messages as part of the list of general messages and thus they can't be concatenated with other messages. diff --git a/docs/guides/prompt-manager/provider-rules/google.mdx b/docs/guides/prompt-manager/provider-rules/google.mdx new file mode 100644 index 000000000..5652b8ac0 --- /dev/null +++ b/docs/guides/prompt-manager/provider-rules/google.mdx @@ -0,0 +1,23 @@ +--- +title: Google provider rules +description: Learn about the rules you need to follow when using the Google provider in Latitude. +--- + +1. [System messages must be at the beginning of the conversation](#rule-1-system-messages-must-be-at-the-beginning-of-the-conversation) + +### System messages must be at the beginning of the conversation + +Google only supports system messages at the beginning of the conversation. All other system messages are converted to user messages. + +``` +--- +provider: Google +model: gemini-1.5-flash +--- + +This is a system message + +This is a user message + +This is another system message /* This message will be converted to a user message */ +``` diff --git a/docs/mint.json b/docs/mint.json index e407f4d76..c4aa802fd 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -33,7 +33,13 @@ "guides/getting-started/introduction", "guides/getting-started/concepts", "guides/getting-started/quick-start", - "guides/getting-started/providers", + { + "group": "Providers", + "pages": [ + "guides/getting-started/providers", + "guides/getting-started/providers/anthropic" + ] + }, "guides/getting-started/invite-your-team" ] }, @@ -50,7 +56,14 @@ "guides/prompt-manager/version-control", "guides/prompt-manager/json-output", "guides/prompt-manager/tools", - "guides/prompt-manager/custom-rules", + { + "group": "Custom Rules", + "pages": [ + "guides/prompt-manager/custom-rules", + "guides/prompt-manager/provider-rules/anthropic", + "guides/prompt-manager/provider-rules/google" + ] + }, "guides/prompt-manager/cache" ] }, @@ -66,10 +79,7 @@ }, { "group": "Logs", - "pages": [ - "guides/logs/overview", - "guides/logs/upload-logs" - ] + "pages": ["guides/logs/overview", "guides/logs/upload-logs"] }, { "group": "Datasets", diff --git a/packages/compiler/src/compiler/base/nodes/tags/content.ts b/packages/compiler/src/compiler/base/nodes/tags/content.ts index 98e75086c..b52e28e53 100644 --- a/packages/compiler/src/compiler/base/nodes/tags/content.ts +++ b/packages/compiler/src/compiler/base/nodes/tags/content.ts @@ -1,3 +1,4 @@ +import { removeCommonIndent } from '$compiler/compiler/utils' import errors from '$compiler/error/errors' import { ContentTag } from '$compiler/parser/interfaces' import { ContentType } from '$compiler/types' @@ -15,7 +16,7 @@ export async function compile( popStrayText, addContent, }: CompileNodeContext, - _: Record, + attributes: Record, ) { if (isInsideContentTag) { baseNodeError(errors.contentTagInsideContent, node) @@ -29,17 +30,19 @@ export async function compile( isInsideContentTag: true, }) } - const textContent = popStrayText() + const textContent = removeCommonIndent(popStrayText()) // TODO: This if else is probably not required but the types enforce it. // Improve types. if (node.name === 'text') { addContent({ + ...attributes, type: ContentType.text, text: textContent, }) } else { addContent({ + ...attributes, type: ContentType.image, image: textContent, }) diff --git a/packages/compiler/src/compiler/base/nodes/tags/message.test.ts b/packages/compiler/src/compiler/base/nodes/tags/message.test.ts new file mode 100644 index 000000000..2750f456a --- /dev/null +++ b/packages/compiler/src/compiler/base/nodes/tags/message.test.ts @@ -0,0 +1,283 @@ +import { render } from '$compiler/compiler' +import { getExpectedError } from '$compiler/compiler/test/helpers' +import { removeCommonIndent } from '$compiler/compiler/utils' +import { CUSTOM_TAG_END, CUSTOM_TAG_START } from '$compiler/constants' +import CompileError from '$compiler/error/error' +import { + AssistantMessage, + ImageContent, + SystemMessage, + TextContent, + UserMessage, +} from '$compiler/types' +import { describe, expect, it } from 'vitest' + +describe('messages', async () => { + it('allows creating system, user, assistant', async () => { + const prompt = ` + system message + user message + assistant message + ` + const result = await render({ + prompt: removeCommonIndent(prompt), + parameters: {}, + }) + + expect(result.messages.length).toBe(3) + const systemMessage = result.messages[0]! + const userMessage = result.messages[1]! as UserMessage + const assistantMessage = result.messages[2]! as AssistantMessage + + expect(systemMessage.role).toBe('system') + expect(systemMessage.content).toEqual([ + { + type: 'text', + text: 'system message', + }, + ]) + + expect(userMessage.role).toBe('user') + expect((userMessage.content[0]! as TextContent).text).toBe('user message') + + expect(assistantMessage.role).toBe('assistant') + expect(assistantMessage.content).toEqual([ + { + type: 'text', + text: 'assistant message', + }, + ]) + }) + + it('fails when using an unknown tag', async () => { + const prompt = ` + message + ` + const action = () => + render({ + prompt: removeCommonIndent(prompt), + parameters: {}, + }) + const error = await getExpectedError(action, CompileError) + expect(error.code).toBe('unknown-tag') + }) + + it('can create messages with the common message tag', async () => { + const prompt = ` + message + ` + const result1 = await render({ + prompt: removeCommonIndent(prompt), + parameters: { + role: 'system', + }, + }) + const result2 = await render({ + prompt: removeCommonIndent(prompt), + parameters: { + role: 'user', + }, + }) + + expect(result1.messages.length).toBe(1) + const message1 = result1.messages[0]! + expect(message1.role).toBe('system') + expect(message1.content).toEqual([{ type: 'text', text: 'message' }]) + + expect(result2.messages.length).toBe(1) + const message2 = result2.messages[0]! + expect(message2.role).toBe('user') + expect((message2.content[0] as TextContent)!.text).toBe('message') + }) + + it('raises an error when using an invalid message role', async () => { + const prompt = ` + message + ` + const action = () => + render({ + prompt: removeCommonIndent(prompt), + parameters: {}, + }) + const error = await getExpectedError(action, CompileError) + expect(error.code).toBe('invalid-message-role') + }) + + it('throws an error when a message tag is inside another message', async () => { + const prompt = ` + + user message + + ` + const action = () => + render({ + prompt: removeCommonIndent(prompt), + parameters: {}, + }) + const error = await getExpectedError(action, CompileError) + expect(error.code).toBe('message-tag-inside-message') + }) + + it('creates a system message when no message tag is present', async () => { + const prompt = ` + Test message + user message + ` + const result = await render({ + prompt: removeCommonIndent(prompt), + parameters: {}, + }) + + expect(result.messages.length).toBe(2) + const systemMessage = result.messages[0]! as SystemMessage + const userMessage = result.messages[1]! as UserMessage + + expect(systemMessage.role).toBe('system') + expect(systemMessage.content).toEqual([ + { + type: 'text', + text: 'Test message', + }, + ]) + + expect(userMessage.role).toBe('user') + expect((userMessage.content[0]! as TextContent).text).toBe('user message') + }) + + it('allows message tag to have extra attributes', async () => { + const prompt = ` + User message + Assistant message + System message + ` + const result = await render({ + prompt: removeCommonIndent(prompt), + parameters: {}, + }) + + expect(result.messages).toEqual([ + { + role: 'user', + content: [{ type: 'text', text: 'User message' }], + foo: 'user_bar', + name: undefined, + }, + { + role: 'assistant', + content: [ + { + type: 'text', + text: 'Assistant message', + }, + ], + foo: 'assistant_bar', + toolCalls: [], + }, + { + role: 'system', + content: [ + { + type: 'text', + text: 'System message', + }, + ], + foo: 'system_bar', + }, + ]) + }) +}) + +describe('message contents', async () => { + it('all messages can have multiple content tags', async () => { + const prompt = ` + + text content + image content + another text content + + ` + const result = await render({ + prompt: removeCommonIndent(prompt), + parameters: {}, + }) + + expect(result.messages.length).toBe(1) + const message = result.messages[0]! as UserMessage + expect(message.content.length).toBe(3) + expect(message.content[0]!.type).toBe('text') + expect((message.content[0]! as TextContent).text).toBe('text content') + + expect(message.content[1]!.type).toBe('image') + expect((message.content[1]! as ImageContent).image).toBe('image content') + + expect(message.content[2]!.type).toBe('text') + expect((message.content[2]! as TextContent).text).toBe( + 'another text content', + ) + }) + + it('fails when using an invalid content type', async () => { + const prompt = ` + + text content + + ` + const action = () => + render({ + prompt: removeCommonIndent(prompt), + parameters: {}, + }) + const error = await getExpectedError(action, CompileError) + expect(error.code).toBe('unknown-tag') + }) + + it('creates a text content when no content tag is present', async () => { + const prompt = ` + + Test message + + ` + const result = await render({ + prompt: removeCommonIndent(prompt), + parameters: {}, + }) + + expect(result.messages.length).toBe(1) + const message = result.messages[0]! + + expect(message.content).toEqual([ + { + type: 'text', + text: 'Test message', + }, + ]) + }) + + it('allows content tag to have extra attributes', async () => { + const prompt = ` + + + Long text cached... + + Short text not cached + + ` + const result = await render({ + prompt: removeCommonIndent(prompt), + parameters: {}, + }) + expect(result.messages.length).toBe(1) + const message = result.messages[0]! + expect(message.content).toEqual([ + { + type: 'text', + text: 'Long text cached...', + cache_control: { type: 'ephimeral' }, + }, + { + type: 'text', + text: 'Short text not cached', + }, + ]) + }) +}) diff --git a/packages/compiler/src/compiler/base/nodes/tags/message.ts b/packages/compiler/src/compiler/base/nodes/tags/message.ts index 789d4b8c1..8ca081d32 100644 --- a/packages/compiler/src/compiler/base/nodes/tags/message.ts +++ b/packages/compiler/src/compiler/base/nodes/tags/message.ts @@ -11,7 +11,6 @@ import { MessageContent, MessageRole, SystemMessage, - TextContent, ToolMessage, UserMessage, } from '$compiler/types' @@ -92,13 +91,15 @@ function buildMessage( if (role === MessageRole.system) { return { + ...attributes, role, - content: (content[0] as TextContent)?.text ?? '', + content, } as SystemMessage } if (role === MessageRole.user) { return { + ...attributes, role, name: attributes.name ? String(attributes.name) : undefined, content, @@ -107,9 +108,10 @@ function buildMessage( if (role === MessageRole.assistant) { return { + ...attributes, role, toolCalls: toolCalls.map(({ value }) => value), - content: (content[0]! as TextContent).text, + content, } as AssistantMessage } diff --git a/packages/compiler/src/compiler/chain.test.ts b/packages/compiler/src/compiler/chain.test.ts index 77ca49306..6fa1c613d 100644 --- a/packages/compiler/src/compiler/chain.test.ts +++ b/packages/compiler/src/compiler/chain.test.ts @@ -101,7 +101,12 @@ describe('chain', async () => { const systemMessage = conversation.messages[0]! expect(systemMessage.role).toBe('system') - expect(systemMessage.content).toBe('System message') + expect(systemMessage.content).toEqual([ + { + type: 'text', + text: 'System message', + }, + ]) const userMessage = conversation.messages[1]! as UserMessage expect(userMessage.role).toBe('user') @@ -123,7 +128,12 @@ describe('chain', async () => { const assistantMessage = conversation.messages[4]! as AssistantMessage expect(assistantMessage.role).toBe('assistant') - expect(assistantMessage.content).toBe('Assistant message: foo') + expect(assistantMessage.content).toEqual([ + { + type: 'text', + text: 'Assistant message: foo', + }, + ]) }) it('stops at a step tag', async () => { @@ -145,16 +155,40 @@ describe('chain', async () => { expect(completed1).toBe(false) expect(conversation1.messages.length).toBe(1) - expect(conversation1.messages[0]!.content).toBe('Message 1') + expect(conversation1.messages[0]).toEqual({ + role: MessageRole.system, + content: [ + { + type: 'text', + text: 'Message 1', + }, + ], + }) const { completed: completed2, conversation: conversation2 } = await chain.step('response') expect(completed2).toBe(true) expect(conversation2.messages.length).toBe(3) - expect(conversation2.messages[0]!.content).toBe('Message 1') + expect(conversation2.messages[0]).toEqual({ + role: MessageRole.system, + content: [ + { + type: 'text', + text: 'Message 1', + }, + ], + }) expect(conversation2.messages[1]!.content).toBe('response') - expect(conversation2.messages[2]!.content).toBe('Message 2') + expect(conversation2.messages[2]!).toEqual({ + role: MessageRole.system, + content: [ + { + type: 'text', + text: 'Message 2', + }, + ], + }) }) it('fails when an assistant message is not provided in followup steps', async () => { @@ -239,9 +273,29 @@ describe('chain', async () => { }) const conversation = await complete({ chain }) - expect(conversation.messages[0]!.content).toBe('1') - expect(conversation.messages[1]!.content).toBe('') - expect(conversation.messages[2]!.content).toBe('2') + expect(conversation.messages[0]!).toEqual({ + role: MessageRole.system, + content: [ + { + type: 'text', + text: '1', + }, + ], + }) + expect(conversation.messages[1]).toEqual({ + role: MessageRole.assistant, + toolCalls: [], + content: '', + }) + expect(conversation.messages[2]).toEqual({ + role: MessageRole.system, + content: [ + { + type: 'text', + text: '2', + }, + ], + }) expect(func1).toHaveBeenCalledTimes(1) expect(func2).toHaveBeenCalledTimes(1) }) @@ -265,9 +319,15 @@ describe('chain', async () => { }) const conversation = await complete({ chain }) - expect( - conversation.messages[conversation.messages.length - 1]!.content, - ).toBe('6') + expect(conversation.messages[conversation.messages.length - 1]).toEqual({ + role: MessageRole.system, + content: [ + { + type: 'text', + text: '6', + }, + ], + }) }) it('maintains the scope in if statements', async () => { @@ -299,9 +359,15 @@ describe('chain', async () => { }) const conversation = await complete({ chain: correctChain }) - expect( - conversation.messages[conversation.messages.length - 1]!.content, - ).toBe('6') + expect(conversation.messages[conversation.messages.length - 1]!).toEqual({ + role: MessageRole.system, + content: [ + { + type: 'text', + text: '6', + }, + ], + }) const incorrectChain = new Chain({ prompt: incorrectPrompt, @@ -346,7 +412,15 @@ describe('chain', async () => { expect((conversation.messages[4]!.content[0]! as TextContent).text).toBe( '2', ) - expect(conversation.messages[6]!.content).toBe('3') + expect(conversation.messages[6]).toEqual({ + role: MessageRole.system, + content: [ + { + type: 'text', + text: '3', + }, + ], + }) }) it('cannot access variables created in a loop outside its scope', async () => { @@ -414,9 +488,16 @@ describe('chain', async () => { 3.3 `), ) - expect( - conversation.messages[conversation.messages.length - 1]!.content, - ).toBe('9') + + expect(conversation.messages[conversation.messages.length - 1]).toEqual({ + role: MessageRole.system, + content: [ + { + type: 'text', + text: '9', + }, + ], + }) }) it('saves the response in a variable', async () => { @@ -436,7 +517,12 @@ describe('chain', async () => { expect(conversation.messages.length).toBe(2) expect(conversation.messages[0]!.content).toBe('foo') - expect(conversation.messages[1]!.content).toBe('foo') + expect(conversation.messages[1]!.content).toEqual([ + { + type: 'text', + text: 'foo', + }, + ]) }) it('returns the correct configuration in all steps', async () => { diff --git a/packages/compiler/src/compiler/compile.test.ts b/packages/compiler/src/compiler/compile.test.ts index fdec8cdfb..255923b10 100644 --- a/packages/compiler/src/compiler/compile.test.ts +++ b/packages/compiler/src/compiler/compile.test.ts @@ -1,11 +1,11 @@ +import { getExpectedError } from '$compiler/compiler/test/helpers' import { CUSTOM_TAG_END, CUSTOM_TAG_START } from '$compiler/constants' import CompileError from '$compiler/error/error' import { AssistantMessage, - ImageContent, Message, MessageContent, - SystemMessage, + MessageRole, TextContent, UserMessage, } from '$compiler/types' @@ -14,19 +14,6 @@ import { describe, expect, it, vi } from 'vitest' import { render } from '.' import { removeCommonIndent } from './utils' -const getExpectedError = async ( - action: () => Promise, - errorClass: new () => T, -): Promise => { - try { - await action() - } catch (err) { - expect(err).toBeInstanceOf(errorClass) - return err as T - } - throw new Error('Expected an error to be thrown') -} - async function getCompiledText( prompt: string, parameters: Record = {}, @@ -86,8 +73,15 @@ describe('comments', async () => { expect(result.messages.length).toBe(1) const message = result.messages[0]! - const text = message.content - expect(text).toBe('anna\nbob\n\ncharlie') + expect(message).toEqual({ + role: MessageRole.system, + content: [ + { + type: 'text', + text: 'anna\nbob\n\ncharlie', + }, + ], + }) }) it('also allows using tag comments', async () => { @@ -105,188 +99,12 @@ describe('comments', async () => { expect(result.messages.length).toBe(1) const message = result.messages[0]! - expect(message.content).toBe('Test message') - }) -}) - -describe('messages', async () => { - it('allows creating system, user, assistant', async () => { - const prompt = ` - system message - user message - assistant message - ` - const result = await render({ - prompt: removeCommonIndent(prompt), - parameters: {}, - }) - - expect(result.messages.length).toBe(3) - const systemMessage = result.messages[0]! - const userMessage = result.messages[1]! as UserMessage - const assistantMessage = result.messages[2]! as AssistantMessage - - expect(systemMessage.role).toBe('system') - expect(systemMessage.content).toBe('system message') - - expect(userMessage.role).toBe('user') - expect((userMessage.content[0]! as TextContent).text).toBe('user message') - - expect(assistantMessage.role).toBe('assistant') - expect(assistantMessage.content).toBe('assistant message') - }) - - it('fails when using an unknown tag', async () => { - const prompt = ` - message - ` - const action = () => - render({ - prompt: removeCommonIndent(prompt), - parameters: {}, - }) - const error = await getExpectedError(action, CompileError) - expect(error.code).toBe('unknown-tag') - }) - - it('can create messages with the common message tag', async () => { - const prompt = ` - message - ` - const result1 = await render({ - prompt: removeCommonIndent(prompt), - parameters: { - role: 'system', - }, - }) - const result2 = await render({ - prompt: removeCommonIndent(prompt), - parameters: { - role: 'user', + expect(message.content).toEqual([ + { + type: 'text', + text: 'Test message', }, - }) - - expect(result1.messages.length).toBe(1) - const message1 = result1.messages[0]! - expect(message1.role).toBe('system') - expect(message1.content).toBe('message') - - expect(result2.messages.length).toBe(1) - const message2 = result2.messages[0]! - expect(message2.role).toBe('user') - expect((message2.content[0] as TextContent)!.text).toBe('message') - }) - - it('raises an error when using an invalid message role', async () => { - const prompt = ` - message - ` - const action = () => - render({ - prompt: removeCommonIndent(prompt), - parameters: {}, - }) - const error = await getExpectedError(action, CompileError) - expect(error.code).toBe('invalid-message-role') - }) - - it('throws an error when a message tag is inside another message', async () => { - const prompt = ` - - user message - - ` - const action = () => - render({ - prompt: removeCommonIndent(prompt), - parameters: {}, - }) - const error = await getExpectedError(action, CompileError) - expect(error.code).toBe('message-tag-inside-message') - }) - - it('creates a system message when no message tag is present', async () => { - const prompt = ` - Test message - user message - ` - const result = await render({ - prompt: removeCommonIndent(prompt), - parameters: {}, - }) - - expect(result.messages.length).toBe(2) - const systemMessage = result.messages[0]! as SystemMessage - const userMessage = result.messages[1]! as UserMessage - - expect(systemMessage.role).toBe('system') - expect(systemMessage.content).toBe('Test message') - - expect(userMessage.role).toBe('user') - expect((userMessage.content[0]! as TextContent).text).toBe('user message') - }) -}) - -describe('message contents', async () => { - it('all messages can have multiple content tags', async () => { - const prompt = ` - - text content - image content - another text content - - ` - const result = await render({ - prompt: removeCommonIndent(prompt), - parameters: {}, - }) - - expect(result.messages.length).toBe(1) - const message = result.messages[0]! as UserMessage - expect(message.content.length).toBe(3) - - expect(message.content[0]!.type).toBe('text') - expect((message.content[0]! as TextContent).text).toBe('text content') - - expect(message.content[1]!.type).toBe('image') - expect((message.content[1]! as ImageContent).image).toBe('image content') - - expect(message.content[2]!.type).toBe('text') - expect((message.content[2]! as TextContent).text).toBe( - 'another text content', - ) - }) - - it('fails when using an invalid content type', async () => { - const prompt = ` - - text content - - ` - const action = () => - render({ - prompt: removeCommonIndent(prompt), - parameters: {}, - }) - const error = await getExpectedError(action, CompileError) - expect(error.code).toBe('unknown-tag') - }) - - it('creates a text content when no content tag is present', async () => { - const prompt = ` - - Test message - - ` - const result = await render({ - prompt: removeCommonIndent(prompt), - parameters: {}, - }) - - expect(result.messages.length).toBe(1) - const message = result.messages[0]! - - expect(message.content).toBe('Test message') + ]) }) }) @@ -545,12 +363,12 @@ describe('conditional expressions', async () => { expect(message1.role).toBe('user') expect(message1.content.length).toBe(1) expect(message1.content[0]!.type).toBe('text') - expect((message1.content[0]! as TextContent).text).toBe('Foo!') + expect(message1.content).toEqual([{ type: 'text', text: 'Foo!' }]) expect(result2.messages.length).toBe(1) const message2 = result2.messages[0]! as AssistantMessage expect(message2.role).toBe('assistant') - expect(message2.content).toBe('Bar!') + expect(message2.content).toEqual([{ type: 'text', text: 'Bar!' }]) }) it('adds message contents conditionally', async () => { @@ -563,15 +381,40 @@ describe('conditional expressions', async () => { ${CUSTOM_TAG_START}/if${CUSTOM_TAG_END} ` - const result1 = await getCompiledText(prompt, { - foo: true, + + const result1 = await render({ + prompt: removeCommonIndent(prompt), + parameters: { foo: true }, }) - const result2 = await getCompiledText(prompt, { - foo: false, + const result2 = await render({ + prompt: removeCommonIndent(prompt), + parameters: { foo: false }, }) - expect(result1).toBe('Foo!') - expect(result2).toBe('Bar!') + expect(result1.messages).toEqual([ + { + role: MessageRole.user, + name: undefined, + content: [ + { + type: 'text', + text: 'Foo!', + }, + ], + }, + ]) + expect(result2.messages).toEqual([ + { + role: MessageRole.user, + name: undefined, + content: [ + { + type: 'text', + text: 'Bar!', + }, + ], + }, + ]) }) }) diff --git a/packages/compiler/src/compiler/compile.ts b/packages/compiler/src/compiler/compile.ts index 931feaee9..4e9115e10 100644 --- a/packages/compiler/src/compiler/compile.ts +++ b/packages/compiler/src/compiler/compile.ts @@ -13,7 +13,6 @@ import { MessageContent, MessageRole, SystemMessage, - TextContent, } from '$compiler/types' import type { Node as LogicalExpression } from 'estree' @@ -157,14 +156,14 @@ export class Compile { this.baseNodeError(errors.invalidToolCallPlacement, toolNode) }) - if (content.length > 0) { - const message = { - role: MessageRole.system, - content: (content[0] as TextContent).text, - } as SystemMessage + if (!content.length) return - this.addMessage(message) - } + const message = { + role: MessageRole.system, + content, + } as SystemMessage + + this.addMessage(message) } private popContent(): MessageContent[] { @@ -187,7 +186,7 @@ export class Compile { private popStepResponse() { if (this.stepResponse === undefined) return undefined - const response = { + const response: AssistantMessage = { role: MessageRole.assistant, content: this.stepResponse, toolCalls: [], @@ -195,7 +194,7 @@ export class Compile { this.stepResponse = undefined - return response as AssistantMessage + return response } private async resolveExpression( diff --git a/packages/compiler/src/compiler/test/helpers.ts b/packages/compiler/src/compiler/test/helpers.ts new file mode 100644 index 000000000..8cb896234 --- /dev/null +++ b/packages/compiler/src/compiler/test/helpers.ts @@ -0,0 +1,14 @@ +import { expect } from 'vitest' + +export async function getExpectedError( + action: () => Promise, + errorClass: new () => T, +): Promise { + try { + await action() + } catch (err) { + expect(err).toBeInstanceOf(errorClass) + return err as T + } + throw new Error('Expected an error to be thrown') +} diff --git a/packages/compiler/src/types/message.ts b/packages/compiler/src/types/message.ts index 14f4b3485..708db61e2 100644 --- a/packages/compiler/src/types/message.ts +++ b/packages/compiler/src/types/message.ts @@ -14,6 +14,7 @@ export enum MessageRole { interface IMessageContent { type: ContentType + [key: string]: unknown } export type TextContent = IMessageContent & { @@ -56,11 +57,11 @@ export type ToolCall = { interface IMessage { role: MessageRole content: MessageContent[] + [key: string]: unknown } -export type SystemMessage = { +export type SystemMessage = IMessage & { role: MessageRole.system - content: string } export type UserMessage = IMessage & { @@ -71,12 +72,13 @@ export type UserMessage = IMessage & { export type AssistantMessage = { role: MessageRole.assistant toolCalls: ToolCall[] - content: string | ToolRequestContent[] + content: string | ToolRequestContent[] | MessageContent[] } -export type ToolMessage = IMessage & { +export type ToolMessage = { role: MessageRole.tool content: ToolContent[] + [key: string]: unknown } export type Message = diff --git a/packages/core/src/services/ai/helpers.ts b/packages/core/src/services/ai/helpers.ts index 0ff8215f1..33681f1c8 100644 --- a/packages/core/src/services/ai/helpers.ts +++ b/packages/core/src/services/ai/helpers.ts @@ -48,6 +48,7 @@ export type Config = { provider: string model: string url?: string + cacheControl?: boolean schema?: JSONSchema7 azure?: { resourceName: string } google?: GoogleConfig diff --git a/packages/core/src/services/ai/index.test.ts b/packages/core/src/services/ai/index.test.ts index d73f9a2a1..75a0babef 100644 --- a/packages/core/src/services/ai/index.test.ts +++ b/packages/core/src/services/ai/index.test.ts @@ -39,7 +39,10 @@ describe('ai function', () => { } const messages: Message[] = [ - { role: MessageRole.system, content: 'System message' }, + { + role: MessageRole.system, + content: [{ type: ContentType.text, text: 'System message' }], + }, ] await expect( @@ -151,7 +154,10 @@ describe('ai function', () => { } const messages: Message[] = [ - { role: MessageRole.system, content: 'System message' }, + { + role: MessageRole.system, + content: [{ type: ContentType.text, text: 'System message' }], + }, { role: MessageRole.user, content: [{ type: ContentType.text, text: 'Hello' }], @@ -226,7 +232,10 @@ describe('ai function', () => { } const messages: Message[] = [ - { role: MessageRole.system, content: 'System message' }, + { + role: MessageRole.system, + content: [{ type: ContentType.text, text: 'System message' }], + }, { role: MessageRole.user, content: [{ type: ContentType.text, text: 'Hello' }], diff --git a/packages/core/src/services/ai/index.ts b/packages/core/src/services/ai/index.ts index 4334cd491..3ef666a6e 100644 --- a/packages/core/src/services/ai/index.ts +++ b/packages/core/src/services/ai/index.ts @@ -61,7 +61,7 @@ export async function ai({ provider: apiProvider, prompt, messages: originalMessages, - config, + config: originalConfig, schema, output, customLanguageModel, @@ -93,13 +93,15 @@ export async function ai({ const rule = applyCustomRules({ providerType: apiProvider.provider, messages: originalMessages, + config: originalConfig, }) const { provider, token: apiKey, url } = apiProvider + const config = rule.config as PartialConfig + const messages = rule.messages const model = config.model - - const messages = rule?.messages ?? originalMessages - const languageModelResult = createProvider({ + const tools = config.tools + const llmProvider = createProvider({ messages, provider, apiKey, @@ -107,12 +109,12 @@ export async function ai({ ...(url ? { url } : {}), }) - if (languageModelResult.error) return languageModelResult + if (llmProvider.error) return llmProvider const languageModel = customLanguageModel ? customLanguageModel - : languageModelResult.value(model) - const toolsResult = buildTools(config.tools) + : llmProvider.value(model, { cacheControl: config.cacheControl }) + const toolsResult = buildTools(tools) if (toolsResult.error) return toolsResult const commonOptions = { diff --git a/packages/core/src/services/ai/providers/rules/anthropic.test.ts b/packages/core/src/services/ai/providers/rules/anthropic.test.ts index 01e690be8..acc2368b9 100644 --- a/packages/core/src/services/ai/providers/rules/anthropic.test.ts +++ b/packages/core/src/services/ai/providers/rules/anthropic.test.ts @@ -1,69 +1,122 @@ -import { Message, TextContent } from '@latitude-data/compiler' -import { describe, expect, it } from 'vitest' +import { Message, MessageRole } from '@latitude-data/compiler' +import { beforeAll, describe, expect, it } from 'vitest' -import { applyCustomRules } from '.' +import { PartialConfig } from '../../helpers' import { Providers } from '../models' +import { applyCustomRules, ProviderRules } from './index' const providerType = Providers.Anthropic -describe('applyAntrhopicRules', () => { - it('does not modify the conversation when there are no system messages', () => { - const messages = [ - { - role: 'user', - content: [{ type: 'text', text: 'Hello' }], - }, - { - role: 'assistant', - content: 'Hello! How are you?', - }, - { - role: 'user', - content: [{ type: 'text', text: 'I am good' }], - }, - ] as Message[] +let config = {} as PartialConfig +let messages: Message[] +describe('applyAnthropicRules', () => { + describe('with system messages not at the beggining', () => { + beforeAll(() => { + messages = [ + { + role: 'system', + content: 'You are a helpful chatbot', + }, + { + role: 'system', + content: 'Respond to the user', + }, + { + role: 'user', + content: [{ type: 'text', text: 'Hello' }], + }, + { + role: 'system', + content: 'Use a short response', + }, + { + role: 'assistant', + content: 'Hi! How are you doing today?', + }, + { + role: 'system', + content: [ + { + type: 'text', + text: 'Use a short response', + }, + { + type: 'text', + text: 'Second a short response', + }, + ], + }, + ] as Message[] + }) + + it('only modifies system messages that are not at the beggining', () => { + const rules = applyCustomRules({ providerType, messages, config }) + + const appliedMessages = rules.messages + + expect(appliedMessages.length).toBe(messages.length) + expect(appliedMessages[0]).toEqual(messages[0]) + expect(appliedMessages[1]).toEqual(messages[1]) + expect(appliedMessages[2]).toEqual(messages[2]) + expect(appliedMessages[4]).toEqual({ + role: MessageRole.assistant, + content: [{ type: 'text', text: messages[4]!.content }], + }) + + expect(appliedMessages[3]).toEqual({ + role: MessageRole.user, + content: [{ type: 'text', text: messages[3]!.content }], + }) + + expect(appliedMessages[3]).toEqual({ + role: MessageRole.user, + content: [{ type: 'text', text: messages[3]!.content }], + }) - const rules = applyCustomRules({ providerType, messages }) + expect(appliedMessages[5]).toEqual({ + role: MessageRole.user, + content: [ + { type: 'text', text: 'Use a short response' }, + { type: 'text', text: 'Second a short response' }, + ], + }) + }) - expect(rules).toBeUndefined() + it('generates warning when system messages are not at the beggining', () => { + const rules = applyCustomRules({ providerType, messages, config }) + + expect(rules.rules).toEqual([ + { + rule: ProviderRules.Anthropic, + ruleMessage: + 'Anthropic only supports system messages at the beggining of the conversation. All other system messages have been converted to user messages.', + }, + ]) + }) }) - it('converts any system message to user messages', () => { - const messages = [ - { - role: 'system', - content: 'You are a helpful chatbot', - }, + it('Warns when only system messages are present', () => { + const theMessages = [ { - role: 'user', - content: [{ type: 'text', text: 'Hello' }], + role: MessageRole.system, + content: [{ type: 'text', text: 'You are a helpful chatbot' }], }, { - role: 'system', - content: 'Respond to the user', + role: MessageRole.system, + content: [{ type: 'text', text: 'Respond to the user' }], }, + ] as Message[] + const rules = applyCustomRules({ + providerType, + messages: theMessages, + config, + }) + expect(rules.rules).toEqual([ { - role: 'assistant', - content: 'Hi! How are you doing today?', + rule: ProviderRules.Anthropic, + ruleMessage: + 'Only system messages are present. You at least need one your message or your message in Anthropic.', }, - ] as Message[] - - const rules = applyCustomRules({ providerType, messages }) - - expect(rules?.messages.length).toBe(messages.length) - - expect(rules?.messages[0]!.role).toBe('user') - expect((rules?.messages[0]!.content[0] as TextContent)?.text).toEqual( - messages[0]!.content, - ) - - expect(rules?.messages[1]).toEqual(messages[1]) - - expect(rules?.messages[2]!.role).toBe('user') - expect((rules?.messages[2]!.content[0] as TextContent)?.text).toEqual( - messages[2]!.content, - ) - - expect(rules?.messages[3]).toEqual(messages[3]) + ]) }) }) diff --git a/packages/core/src/services/ai/providers/rules/anthropic.ts b/packages/core/src/services/ai/providers/rules/anthropic.ts index 934fdae34..e8288ee40 100644 --- a/packages/core/src/services/ai/providers/rules/anthropic.ts +++ b/packages/core/src/services/ai/providers/rules/anthropic.ts @@ -1,23 +1,29 @@ -import type { ContentType, MessageRole } from '@latitude-data/compiler' +import { MessageRole } from '@latitude-data/compiler' -import { AppliedRules, ApplyCustomRulesProps } from '.' +import { AppliedRules, ProviderRules } from '.' +import { enforceAllSystemMessagesFirst } from './helpers/enforceAllSystemMessagesFirst' -export function applyAnthropicRules({ - messages, -}: ApplyCustomRulesProps): AppliedRules | undefined { - if (!messages.some((m) => m.role === 'system')) return +export function applyAnthropicRules(appliedRule: AppliedRules): AppliedRules { + const rule = enforceAllSystemMessagesFirst(appliedRule, { + provider: ProviderRules.Anthropic, + message: + 'Anthropic only supports system messages at the beggining of the conversation. All other system messages have been converted to user messages.', + }) + const roles = rule.messages.map((m) => m.role) + const onlySystemMessages = roles.every((r) => r === MessageRole.system) + if (!onlySystemMessages) return rule + + const rules = [ + ...rule.rules, + { + rule: ProviderRules.Anthropic, + ruleMessage: + 'Only system messages are present. You at least need one your message or your message in Anthropic.', + }, + ] return { - rule: 'AnthropicMultipleSystemMessagesUnsupported', - ruleMessage: - 'Anthropic does not support multiple system messages. All system messages have been converted to user messages. If you want to add a system prompt please include it in the prompt frontmatter.', - messages: messages.map((m) => { - if (m.role !== 'system') return m - return { - ...m, - role: 'user' as MessageRole.user, - content: [{ type: 'text' as ContentType.text, text: m.content }], - } - }), + ...rule, + rules, } } diff --git a/packages/core/src/services/ai/providers/rules/google.test.ts b/packages/core/src/services/ai/providers/rules/google.test.ts index edfe89495..043827cd6 100644 --- a/packages/core/src/services/ai/providers/rules/google.test.ts +++ b/packages/core/src/services/ai/providers/rules/google.test.ts @@ -1,93 +1,97 @@ -import { Message, TextContent } from '@latitude-data/compiler' -import { describe, expect, it } from 'vitest' +import { Message, MessageRole } from '@latitude-data/compiler' +import { beforeAll, describe, expect, it } from 'vitest' -import { applyCustomRules } from '.' +import { applyCustomRules, ProviderRules } from '.' +import { PartialConfig } from '../../helpers' import { Providers } from '../models' const providerType = Providers.Google +let config = {} as PartialConfig +let messages: Message[] describe('applyGoogleRules', () => { - it('does not modify the conversation when there are no system messages', () => { - const messages = [ - { - role: 'user', - content: [{ type: 'text', text: 'Hello' }], - }, - { - role: 'assistant', - content: 'Hello! How are you?', - }, - { - role: 'user', - content: [{ type: 'text', text: 'I am good' }], - }, - ] as Message[] + describe('with system messages not at the beggining', () => { + beforeAll(() => { + messages = [ + { + role: 'system', + content: 'You are a helpful chatbot', + }, + { + role: 'system', + content: 'Respond to the user', + }, + { + role: 'user', + content: [{ type: 'text', text: 'Hello' }], + }, + { + role: 'system', + content: 'Use a short response', + }, + { + role: 'assistant', + content: 'Hi! How are you doing today?', + }, + { + role: 'system', + content: [ + { + type: 'text', + text: 'Use a short response', + }, + { + type: 'text', + text: 'Second a short response', + }, + ], + }, + ] as Message[] + }) - const rules = applyCustomRules({ providerType, messages }) + it('only modifies system messages that are not at the beggining', () => { + const rules = applyCustomRules({ providerType, messages, config }) - expect(rules).toBeUndefined() - }) - - it('does not modify the conversation when all system messages are at the beggining', () => { - const messages = [ - { - role: 'system', - content: 'You are a helpful chatbot', - }, - { - role: 'system', - content: 'Respond to the user', - }, - { - role: 'user', - content: [{ type: 'text', text: 'Hello' }], - }, - { - role: 'assistant', - content: 'Hi! How are you doing today?', - }, - ] as Message[] + const appliedMessages = rules.messages - const rules = applyCustomRules({ providerType, messages }) - expect(rules).toBeUndefined() - }) + expect(appliedMessages.length).toBe(messages.length) + expect(appliedMessages[0]).toEqual(messages[0]) + expect(appliedMessages[1]).toEqual(messages[1]) + expect(appliedMessages[2]).toEqual(messages[2]) + expect(appliedMessages[4]).toEqual({ + role: MessageRole.assistant, + content: [{ type: 'text', text: messages[4]!.content }], + }) - it('only modifies system messages that are not at the beggining', () => { - const messages = [ - { - role: 'system', - content: 'You are a helpful chatbot', - }, - { - role: 'system', - content: 'Respond to the user', - }, - { - role: 'user', - content: [{ type: 'text', text: 'Hello' }], - }, - { - role: 'system', - content: 'Use a short response', - }, - { - role: 'assistant', - content: 'Hi! How are you doing today?', - }, - ] as Message[] + expect(appliedMessages[3]).toEqual({ + role: MessageRole.user, + content: [{ type: 'text', text: messages[3]!.content }], + }) - const rules = applyCustomRules({ providerType, messages }) + expect(appliedMessages[3]).toEqual({ + role: MessageRole.user, + content: [{ type: 'text', text: messages[3]!.content }], + }) - expect(rules?.messages.length).toBe(messages.length) + expect(appliedMessages[5]).toEqual({ + role: MessageRole.user, + content: [ + { type: 'text', text: 'Use a short response' }, + { type: 'text', text: 'Second a short response' }, + ], + }) + }) - expect(rules?.messages[0]).toEqual(messages[0]) - expect(rules?.messages[1]).toEqual(messages[1]) - expect(rules?.messages[2]).toEqual(messages[2]) - expect(rules?.messages[4]).toEqual(messages[4]) + it('generates warning when system messages are not at the beggining', () => { + const rules = applyCustomRules({ providerType, messages, config }) - expect(rules?.messages[3]!.role).toBe('user') - expect((rules?.messages[3]!.content[0] as TextContent)?.text).toEqual( - messages[3]!.content, - ) + expect(rules.rules).toEqual([ + { + rule: ProviderRules.Google, + ruleMessage: + 'Google only supports system messages at the beggining of the conversation. All other system messages have been converted to user messages.', + }, + ]) + }) }) }) diff --git a/packages/core/src/services/ai/providers/rules/google.ts b/packages/core/src/services/ai/providers/rules/google.ts index 6c682b46b..e13bd2694 100644 --- a/packages/core/src/services/ai/providers/rules/google.ts +++ b/packages/core/src/services/ai/providers/rules/google.ts @@ -1,33 +1,10 @@ -import type { ContentType, MessageRole } from '@latitude-data/compiler' +import { enforceAllSystemMessagesFirst } from './helpers/enforceAllSystemMessagesFirst' +import { AppliedRules, ProviderRules } from './index' -import { AppliedRules, ApplyCustomRulesProps } from '.' - -export function applyGoogleRules({ - messages, -}: ApplyCustomRulesProps): AppliedRules | undefined { - const firstNonSystemMessageIndex = messages.findIndex( - (m) => m.role !== 'system', - ) - if (firstNonSystemMessageIndex === -1) return - - const messagesAfterFirstNonSystemMessage = messages.slice( - firstNonSystemMessageIndex, - ) - if (!messagesAfterFirstNonSystemMessage.some((m) => m.role === 'system')) - return - - return { - rule: 'GoogleSingleStartingSystemMessageSupported', - ruleMessage: +export function applyGoogleRules(appliedRule: AppliedRules): AppliedRules { + return enforceAllSystemMessagesFirst(appliedRule, { + provider: ProviderRules.Google, + message: 'Google only supports system messages at the beggining of the conversation. All other system messages have been converted to user messages.', - messages: messages.map((m, i) => { - if (i < firstNonSystemMessageIndex) return m - if (m.role !== 'system') return m - return { - ...m, - role: 'user' as MessageRole.user, - content: [{ type: 'text' as ContentType.text, text: m.content }], - } - }), - } + }) } diff --git a/packages/core/src/services/ai/providers/rules/helpers/enforceAllSystemMessagesFirst.ts b/packages/core/src/services/ai/providers/rules/helpers/enforceAllSystemMessagesFirst.ts new file mode 100644 index 000000000..7db21d10d --- /dev/null +++ b/packages/core/src/services/ai/providers/rules/helpers/enforceAllSystemMessagesFirst.ts @@ -0,0 +1,53 @@ +import { ContentType, MessageRole } from '@latitude-data/compiler' + +import { AppliedRules, ProviderRules } from '../index' + +const system = MessageRole.system + +export function enforceAllSystemMessagesFirst( + appliedRule: AppliedRules, + ruleConfig: { + provider: ProviderRules + message: string + }, +): AppliedRules { + const messages = appliedRule.messages + const firstNonSystemMessageIndex = messages.findIndex( + (m) => m.role !== system, + ) + + if (firstNonSystemMessageIndex === -1) { + return appliedRule + } + + const messagesAfterFirstNonSystemMessage = messages.slice( + firstNonSystemMessageIndex, + ) + if (!messagesAfterFirstNonSystemMessage.some((m) => m.role === system)) { + return appliedRule + } + + const rules = [ + ...appliedRule.rules, + { + rule: ruleConfig.provider, + ruleMessage: ruleConfig.message, + }, + ] + return { + ...appliedRule, + rules, + messages: messages.map((m, i) => { + if (i < firstNonSystemMessageIndex) return m + if (m.role !== system) return m + + return { + ...m, + role: MessageRole.user, + content: Array.isArray(m.content) + ? m.content + : [{ type: ContentType.text, text: m.content }], + } + }), + } +} diff --git a/packages/core/src/services/ai/providers/rules/index.ts b/packages/core/src/services/ai/providers/rules/index.ts index 0e62da292..bd5050966 100644 --- a/packages/core/src/services/ai/providers/rules/index.ts +++ b/packages/core/src/services/ai/providers/rules/index.ts @@ -1,34 +1,48 @@ -import type { Message } from '@latitude-data/compiler' +import type { Config, Message } from '@latitude-data/compiler' +import { PartialConfig } from '../../helpers' import { Providers } from '../models' import { applyAnthropicRules } from './anthropic' import { applyGoogleRules } from './google' +import { vercelSdkRules } from './vercel' -export type ProviderRules = - | 'AnthropicMultipleSystemMessagesUnsupported' - | 'GoogleSingleStartingSystemMessageSupported' +export enum ProviderRules { + Anthropic = 'anthropic', + Google = 'google', + VercelSDK = 'latitude', +} + +type ProviderRule = { rule: ProviderRules; ruleMessage: string } export type AppliedRules = { - rule: ProviderRules - ruleMessage?: string + rules: ProviderRule[] messages: Message[] + config: Config } -export type ApplyCustomRulesProps = { +type Props = { + providerType: Providers messages: Message[] + config: Config | PartialConfig } export function applyCustomRules({ providerType, messages, -}: ApplyCustomRulesProps & { providerType: Providers }): - | AppliedRules - | undefined { + config, +}: Props): AppliedRules { + let rules: AppliedRules = { + rules: [], + messages, + config, + } if (providerType === Providers.Anthropic) { - return applyAnthropicRules({ messages }) + rules = applyAnthropicRules(rules) } if (providerType === Providers.Google) { - return applyGoogleRules({ messages }) + rules = applyGoogleRules(rules) } + + return vercelSdkRules(rules, providerType) } diff --git a/packages/core/src/services/ai/providers/rules/providerMetadata/index.ts b/packages/core/src/services/ai/providers/rules/providerMetadata/index.ts new file mode 100644 index 000000000..20b2e8901 --- /dev/null +++ b/packages/core/src/services/ai/providers/rules/providerMetadata/index.ts @@ -0,0 +1,151 @@ +import { Message, MessageRole } from '@latitude-data/compiler' + +import { Providers } from '../../models' + +export const PROVIDER_TO_METADATA_KEY: Record = { + [Providers.OpenAI]: 'openai', + [Providers.Anthropic]: 'anthropic', + [Providers.Groq]: 'groq', + [Providers.Mistral]: 'mistral', + [Providers.Azure]: 'azure', + [Providers.Google]: 'google', + [Providers.Custom]: 'custom', +} + +const CONTENT_DEFINED_ATTRIBUTES = [ + 'text', + 'type', + 'image', + 'mimeType', + 'data', + 'toolCallId', + 'toolName', + 'args', +] as const + +type AttrArgs = { attributes: string[]; content: Record } +function genericAttributesProcessor({ attributes, content }: AttrArgs) { + return attributes.reduce((acc, key) => ({ ...acc, [key]: content[key] }), {}) +} + +function anthropicAttributesProcessor({ attributes, content }: AttrArgs) { + return attributes.reduce((acc, key) => { + const safeKey = key === 'cache_control' ? 'cacheControl' : key + return { ...acc, [safeKey]: content[key] } + }, {}) +} + +function processAttributes({ + attributes, + provider, + content, +}: AttrArgs & { + provider: Providers +}) { + switch (provider) { + case Providers.Anthropic: + return anthropicAttributesProcessor({ attributes, content }) + default: + return genericAttributesProcessor({ attributes, content }) + } +} + +export function getProviderMetadataKey(provider: Providers) { + return PROVIDER_TO_METADATA_KEY[provider] +} + +export type ProviderMetadata = Record> + +export function extractContentMetadata({ + content, + provider, +}: { + content: Record + provider: Providers +}) { + const definedAttributes = Object.keys(content).filter((key) => + CONTENT_DEFINED_ATTRIBUTES.includes( + key as (typeof CONTENT_DEFINED_ATTRIBUTES)[number], + ), + ) as (typeof CONTENT_DEFINED_ATTRIBUTES)[number][] + + const providerAttributes = Object.keys(content).filter( + (key) => !CONTENT_DEFINED_ATTRIBUTES.includes(key as any), + ) + const definedData = definedAttributes.reduce( + // @ts-ignore + (acc, key) => ({ ...acc, [key]: content[key] }), + {} as Record<(typeof CONTENT_DEFINED_ATTRIBUTES)[number], unknown>, + ) + + if (!providerAttributes.length) { + return definedData + } + + return { + ...definedData, + experimental_providerMetadata: { + [getProviderMetadataKey(provider)]: processAttributes({ + attributes: providerAttributes, + provider, + content, + }), + }, + } +} + +function removeUndefinedValues(data: Record) { + return Object.keys(data).reduce((acc, item) => { + if (data[item] === undefined) return acc + return { ...acc, [item]: data[item] } + }, {}) +} + +type MessageWithMetadata = Message & { + experimental_providerMetadata?: Record> +} +export function extractMessageMetadata({ + message, + provider, +}: { + message: Message + provider: Providers +}): MessageWithMetadata { + const { role, content, toolCalls, ...rest } = message + + let common = removeUndefinedValues({ + role, + content, + toolCalls, + }) as Message & { name?: string } + + if (Object.keys(rest).length === 0) return common + + if (role === MessageRole.user && Object.hasOwnProperty.call(rest, 'name')) { + // @ts-ignore + const name = rest.name + common = { + ...common, + // Issue: https://github.com/vercel/ai/pull/2199 + name, + } + } + + const { name: _, ...restWithoutName } = rest as { + name?: string + [key: string]: unknown + } + + if (!Object.keys(restWithoutName).length) return common + + return { + ...common, + experimental_providerMetadata: { + [getProviderMetadataKey(provider)]: processAttributes({ + attributes: Object.keys(restWithoutName), + provider, + content: restWithoutName, + }), + }, + } +} diff --git a/packages/core/src/services/ai/providers/rules/vercel.test.ts b/packages/core/src/services/ai/providers/rules/vercel.test.ts new file mode 100644 index 000000000..6ba0cbd0b --- /dev/null +++ b/packages/core/src/services/ai/providers/rules/vercel.test.ts @@ -0,0 +1,338 @@ +import { Message } from '@latitude-data/compiler' +import { describe, expect, it } from 'vitest' + +import { PartialConfig } from '../../helpers' +import { Providers } from '../models' +import { vercelSdkRules } from './vercel' + +let messages: Message[] +let config = {} as PartialConfig + +describe('applyVercelSdkRules', () => { + it('modify plain text messages to object', () => { + messages = [ + { + role: 'user', + content: [{ type: 'text', text: 'Hello' }], + }, + { + role: 'assistant', + content: 'Hello! How are you?', + }, + { + role: 'user', + content: [{ type: 'text', text: 'I am good' }], + }, + ] as Message[] + + const rules = vercelSdkRules( + { rules: [], messages, config }, + Providers.Anthropic, + ) + + expect(rules.messages).toEqual([ + ...(messages.slice(0, 1) as Message[]), + { + role: 'assistant', + content: [{ type: 'text', text: 'Hello! How are you?' }], + }, + { + role: 'user', + content: [{ type: 'text', text: 'I am good' }], + }, + ]) + }) + + it('put each system message part in a system message and set custom attributes there', () => { + messages = [ + { + role: 'system', + content: [ + { type: 'text', text: 'I am a' }, + { type: 'text', text: 'system message' }, + ], + }, + ] as Message[] + + const rules = vercelSdkRules( + { rules: [], messages, config }, + Providers.Anthropic, + ) + expect(rules.messages).toEqual([ + { role: 'system', content: 'I am a' }, + { role: 'system', content: 'system message' }, + ]) + }) + + it('already flattened messages', () => { + messages = [ + { role: 'system', content: 'I am a' }, + { role: 'system', content: 'system message' }, + ] as unknown as Message[] + + const rules = vercelSdkRules( + { rules: [], messages, config }, + Providers.Anthropic, + ) + expect(rules.messages).toEqual([ + { role: 'system', content: 'I am a' }, + { role: 'system', content: 'system message' }, + ]) + }) + + it('put root system message metadata into text parts', () => { + messages = [ + { + role: 'system', + cache_control: { type: 'ephemeral' }, + content: [ + { type: 'text', text: 'I am a' }, + { type: 'text', text: 'system message' }, + ], + }, + ] as Message[] + + const rules = vercelSdkRules( + { rules: [], messages, config }, + Providers.Anthropic, + ) + expect(rules.messages).toEqual([ + { + role: 'system', + content: 'I am a', + experimental_providerMetadata: { + anthropic: { + cacheControl: { type: 'ephemeral' }, + }, + }, + }, + { + role: 'system', + content: 'system message', + experimental_providerMetadata: { + anthropic: { + cacheControl: { type: 'ephemeral' }, + }, + }, + }, + ]) + }) + + it('put root user message metadata into text parts', () => { + messages = [ + { + role: 'user', + cache_control: { type: 'ephemeral' }, + content: [ + { type: 'text', text: 'I am a' }, + { type: 'text', text: 'system message' }, + ], + }, + ] as Message[] + + const rules = vercelSdkRules( + { rules: [], messages, config }, + Providers.Anthropic, + ) + expect(rules.messages).toEqual([ + { + role: 'user', + experimental_providerMetadata: { + anthropic: { + cacheControl: { type: 'ephemeral' }, + }, + }, + content: [ + { + type: 'text', + text: 'I am a', + experimental_providerMetadata: { + anthropic: { + cacheControl: { type: 'ephemeral' }, + }, + }, + }, + { + type: 'text', + text: 'system message', + experimental_providerMetadata: { + anthropic: { + cacheControl: { type: 'ephemeral' }, + }, + }, + }, + ], + }, + ]) + }) + + it('transform message attributes into provider metadata', () => { + messages = [ + { + role: 'user', + name: 'paco', + content: [{ type: 'text', text: 'Hello' }], + some_attribute: 'some_user_value', + another_attribute: { another_user: 'value' }, + }, + { + role: 'assistant', + content: 'Hello! How are you?', + }, + { + role: 'assistant', + content: [{ type: 'text', text: 'I am good' }], + some_attribute: 'some_assistant_value', + another_attribute: { another_assistant: 'value' }, + }, + { + role: 'system', + content: [{ type: 'text', text: 'I am good' }], + some_attribute: 'some_system_value', + cache_control: { type: 'ephemeral' }, + another_attribute: { another_system: 'value' }, + }, + ] as Message[] + + const rules = vercelSdkRules( + { rules: [], messages, config }, + Providers.Anthropic, + ) + const transformedMessages = rules.messages + expect(transformedMessages).toEqual([ + { + role: 'user', + name: 'paco', + content: [ + { + type: 'text', + text: 'Hello', + experimental_providerMetadata: { + anthropic: { + some_attribute: 'some_user_value', + another_attribute: { another_user: 'value' }, + }, + }, + }, + ], + experimental_providerMetadata: { + anthropic: { + some_attribute: 'some_user_value', + another_attribute: { another_user: 'value' }, + }, + }, + }, + { + role: 'assistant', + content: [{ type: 'text', text: 'Hello! How are you?' }], + }, + { + role: 'assistant', + content: [ + { + type: 'text', + text: 'I am good', + experimental_providerMetadata: { + anthropic: { + some_attribute: 'some_assistant_value', + another_attribute: { another_assistant: 'value' }, + }, + }, + }, + ], + experimental_providerMetadata: { + anthropic: { + some_attribute: 'some_assistant_value', + another_attribute: { another_assistant: 'value' }, + }, + }, + }, + { + role: 'system', + content: 'I am good', + experimental_providerMetadata: { + anthropic: { + some_attribute: 'some_system_value', + cacheControl: { type: 'ephemeral' }, + another_attribute: { another_system: 'value' }, + }, + }, + }, + ]) + }) + + it('transform message content attributes into content provider metadata', () => { + messages = [ + { + role: 'user', + name: 'paco', + content: [ + { + type: 'text', + text: 'Hello', + some_attribute: 'some_user_value', + another_attribute: { another_user: 'value' }, + }, + ], + }, + { + role: 'assistant', + content: 'Hello! How are you?', + }, + { + role: 'assistant', + content: [ + { + type: 'text', + text: 'I am good', + some_attribute: 'some_assistant_value', + another_attribute: { another_assistant: 'value' }, + }, + ], + }, + ] as Message[] + + const rules = vercelSdkRules( + { rules: [], messages, config }, + Providers.Anthropic, + ) + const transformedMessages = rules.messages + expect(transformedMessages).toEqual([ + { + role: 'user', + name: 'paco', + content: [ + { + type: 'text', + text: 'Hello', + experimental_providerMetadata: { + anthropic: { + some_attribute: 'some_user_value', + another_attribute: { another_user: 'value' }, + }, + }, + }, + ], + }, + { + role: 'assistant', + content: [{ type: 'text', text: 'Hello! How are you?' }], + }, + { + role: 'assistant', + content: [ + { + type: 'text', + text: 'I am good', + experimental_providerMetadata: { + anthropic: { + some_attribute: 'some_assistant_value', + another_attribute: { another_assistant: 'value' }, + }, + }, + }, + ], + }, + ]) + }) +}) diff --git a/packages/core/src/services/ai/providers/rules/vercel.ts b/packages/core/src/services/ai/providers/rules/vercel.ts new file mode 100644 index 000000000..a769fbf6f --- /dev/null +++ b/packages/core/src/services/ai/providers/rules/vercel.ts @@ -0,0 +1,129 @@ +import { + ContentType, + Message, + MessageRole, + SystemMessage, + TextContent, +} from '@latitude-data/compiler' + +import { Providers } from '../models' +import { AppliedRules } from './index' +import { + extractContentMetadata, + extractMessageMetadata, + getProviderMetadataKey, + type ProviderMetadata, +} from './providerMetadata' + +function flattenSystemMessage({ + message, + provider, +}: { + message: SystemMessage + provider: Providers +}): Message[] { + const content = message.content as TextContent[] | string + + // NOTO: `applyCustomRules` can be invoked multiple times + // during a chain. if system message.content is already + // a string we consider it already processed. + if (typeof content === 'string') return [message] + + const msgMetadata = + extractMessageMetadata({ + message, + provider, + }).experimental_providerMetadata ?? {} + + return content.flatMap((content) => { + const extracted = extractContentMetadata({ content, provider }) + // @ts-expect-error - metadata key can be not present + const metadata = (extracted.experimental_providerMetadata ?? + {}) as ProviderMetadata + const baseMsg = { role: message.role, content: content.text } + + if (!Object.keys(metadata).length && !Object.keys(msgMetadata).length) { + return baseMsg + } + + const key = getProviderMetadataKey(provider) + + return { + ...baseMsg, + experimental_providerMetadata: { + [key]: { + ...(msgMetadata?.[key] || {}), + ...(metadata?.[key] || {}), + }, + }, + } + }) as unknown as Message[] +} + +function groupContentMetadata({ + content, + provider, + messageMetadata, +}: { + content: Message['content'] + provider: Providers + messageMetadata?: ProviderMetadata +}) { + const key = getProviderMetadataKey(provider) + + if (typeof content === 'string') { + const baseMsg = { type: ContentType.text, text: content } + if (!messageMetadata) return [baseMsg] + + return [ + { + ...baseMsg, + experimental_providerMetadata: messageMetadata, + }, + ] + } + + return content.map((contentItem) => { + const extracted = extractContentMetadata({ content: contentItem, provider }) + if (!messageMetadata) return extracted + + // @ts-expect-error - metadata key can be not present + const contentMetadata = (extracted.experimental_providerMetadata ?? + {}) as ProviderMetadata + + return { + ...extracted, + experimental_providerMetadata: { + [key]: { + ...(messageMetadata?.[key] || {}), + ...(contentMetadata?.[key] || {}), + }, + }, + } + }) +} + +export function vercelSdkRules( + rules: AppliedRules, + provider: Providers, +): AppliedRules { + const messages = rules.messages.flatMap((message) => { + if (message.role === MessageRole.system) { + return flattenSystemMessage({ message, provider }) + } + + const msg = extractMessageMetadata({ + message, + provider, + }) + const content = groupContentMetadata({ + content: msg.content, + provider, + messageMetadata: msg.experimental_providerMetadata, + }) as unknown as Message['content'] + + return [{ ...msg, content } as Message] + }) as Message[] + + return { ...rules, messages } +} diff --git a/packages/core/src/services/chains/ChainValidator/index.ts b/packages/core/src/services/chains/ChainValidator/index.ts index fe537d2e9..86011a222 100644 --- a/packages/core/src/services/chains/ChainValidator/index.ts +++ b/packages/core/src/services/chains/ChainValidator/index.ts @@ -73,11 +73,12 @@ export class ChainValidator { const rule = applyCustomRules({ providerType: provider.provider, messages: conversation.messages, + config, }) return Result.ok({ provider, - config, + config: rule.config as Config, chainCompleted, conversation: { ...conversation, diff --git a/packages/core/src/services/chains/run.test.ts b/packages/core/src/services/chains/run.test.ts index 2752d7d65..2bb5c73c9 100644 --- a/packages/core/src/services/chains/run.test.ts +++ b/packages/core/src/services/chains/run.test.ts @@ -257,7 +257,7 @@ describe('runChain', () => { messages: [ { role: MessageRole.system, - content: 'System instruction', + content: [{ type: ContentType.text, text: 'System instruction' }], }, { role: MessageRole.user, diff --git a/packages/core/src/services/commits/runDocumentAtCommit.test.ts b/packages/core/src/services/commits/runDocumentAtCommit.test.ts index 156e40f1c..8ad6d3cf0 100644 --- a/packages/core/src/services/commits/runDocumentAtCommit.test.ts +++ b/packages/core/src/services/commits/runDocumentAtCommit.test.ts @@ -170,7 +170,7 @@ This is a test document { role: 'system', content: 'This is a test document' }, { role: 'assistant', - content: 'Fake AI generated text', + content: [{ type: 'text', text: 'Fake AI generated text' }], toolCalls: [], }, ], @@ -243,7 +243,7 @@ This is a test document messages: [ { role: 'assistant', - content: 'Fake AI generated text', + content: [{ type: 'text', text: 'Fake AI generated text' }], toolCalls: [], }, ], diff --git a/packages/core/src/services/providerLogs/serialize.test.ts b/packages/core/src/services/providerLogs/serialize.test.ts index 3a4aa078f..3d71e4911 100644 --- a/packages/core/src/services/providerLogs/serialize.test.ts +++ b/packages/core/src/services/providerLogs/serialize.test.ts @@ -91,7 +91,12 @@ describe('serialize', () => { // @ts-expect-error const providerLog: ProviderLog = { messages: [ - { role: MessageRole.system, content: 'You are an AI assistant' }, + { + role: MessageRole.system, + content: [ + { type: ContentType.text, text: 'You are an AI assistant' }, + ], + }, { role: MessageRole.user, content: [ @@ -109,7 +114,9 @@ describe('serialize', () => { all: [ { role: MessageRole.system, - content: 'You are an AI assistant', + content: [ + { type: ContentType.text, text: 'You are an AI assistant' }, + ], }, { role: MessageRole.user, @@ -125,7 +132,7 @@ describe('serialize', () => { ], first: { role: MessageRole.system, - content: 'You are an AI assistant', + content: [{ type: ContentType.text, text: 'You are an AI assistant' }], }, last: { role: MessageRole.assistant, @@ -158,16 +165,22 @@ describe('serialize', () => { all: [ { role: MessageRole.system, - content: 'You are an AI assistant', + content: [ + { type: ContentType.text, text: 'You are an AI assistant' }, + ], }, ], first: { role: MessageRole.system, - content: 'You are an AI assistant', + content: [ + { type: ContentType.text, text: 'You are an AI assistant' }, + ], }, last: { role: MessageRole.system, - content: 'You are an AI assistant', + content: [ + { type: ContentType.text, text: 'You are an AI assistant' }, + ], }, }, assistant: { @@ -319,7 +332,12 @@ describe('formatContext', () => { // @ts-expect-error const providerLog: ProviderLog = { messages: [ - { role: MessageRole.system, content: 'You are an AI assistant' }, + { + role: MessageRole.system, + content: [ + { type: ContentType.text, text: 'You are an AI assistant' }, + ], + }, { role: MessageRole.user, content: [ diff --git a/packages/core/src/services/providerLogs/serialize.ts b/packages/core/src/services/providerLogs/serialize.ts index 10673585f..89b5a6162 100644 --- a/packages/core/src/services/providerLogs/serialize.ts +++ b/packages/core/src/services/providerLogs/serialize.ts @@ -1,4 +1,4 @@ -import { MessageRole } from '@latitude-data/compiler' +import { ContentType, MessageRole } from '@latitude-data/compiler' import { Message, @@ -52,7 +52,8 @@ export function formatContext( Array.isArray(message.content) && message.content && message.content[0] && - 'text' in message.content[0] + 'text' in message.content[0] && + message.content[0].type === ContentType.text ) { content = message.content[0].text } else if ( diff --git a/packages/web-ui/src/ds/molecules/Chat/Message/index.tsx b/packages/web-ui/src/ds/molecules/Chat/Message/index.tsx index 3694ff89d..fbc76a45d 100644 --- a/packages/web-ui/src/ds/molecules/Chat/Message/index.tsx +++ b/packages/web-ui/src/ds/molecules/Chat/Message/index.tsx @@ -81,7 +81,7 @@ const ContentValue = ({ wordBreak='breakAll' key={`${index}-${lineIndex}`} > - {line} + {line ? line : '\n'} )) } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 91ee34931..4b4dfe4ce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -279,7 +279,7 @@ importers: specifier: ^8.2.4 version: 8.3.5(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.6.0) tsx: - specifier: ^4.16.2 + specifier: ^4.19.2 version: 4.19.2 vitest: specifier: ^2.0.4