From 82e97dde4f4e3e8b8171c251573a94a6ca35fd4d Mon Sep 17 00:00:00 2001 From: Gerard Clos Date: Sun, 29 Sep 2024 09:22:02 +0200 Subject: [PATCH] feature: providerlog context is no longer a json We think it's better if context is just a string with the full conversation excluding the response. --- .../ConnectEvaluationModal/List/index.tsx | 2 +- packages/core/src/helpers.ts | 2 +- .../documentVersionsRepository/index.test.ts | 5 +- packages/core/src/services/chains/run.test.ts | 11 +- .../providerLogs/formatForEvaluation.test.ts | 423 ++++++++++++++++++ .../providerLogs/formatForEvaluation.ts | 29 +- 6 files changed, 463 insertions(+), 9 deletions(-) create mode 100644 packages/core/src/services/providerLogs/formatForEvaluation.test.ts diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/dashboard/connect/_components/ConnectEvaluationModal/List/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/dashboard/connect/_components/ConnectEvaluationModal/List/index.tsx index 91507bd4d..841f46b7b 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/dashboard/connect/_components/ConnectEvaluationModal/List/index.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/dashboard/connect/_components/ConnectEvaluationModal/List/index.tsx @@ -23,7 +23,7 @@ export default function EvaluationList({ onSearchChange, }: EvaluationListProps) { return ( -
+
{ const documents = result.unwrap() expect(documents).toHaveLength(2) - expect(documents.map((d) => d.path)).toEqual(['foo', 'bar']) + expect(documents.map((d) => d.path).sort()).toEqual(['bar', 'foo']) + expect(new Set(documents.map((d) => d.path))).toEqual( + new Set(['foo', 'bar']), + ) expect(documents.every((d) => d.commitId === commit1Id)).toBe(true) }) diff --git a/packages/core/src/services/chains/run.test.ts b/packages/core/src/services/chains/run.test.ts index bee346ec8..71e5f2105 100644 --- a/packages/core/src/services/chains/run.test.ts +++ b/packages/core/src/services/chains/run.test.ts @@ -2,7 +2,7 @@ import { Chain, ContentType, MessageRole } from '@latitude-data/compiler' import { v4 as uuid } from 'uuid' import { beforeEach, describe, expect, it, vi } from 'vitest' -import { Workspace } from '../../browser' +import { objectToString, Workspace } from '../../browser' import { LogSources, Providers } from '../../constants' import * as factories from '../../tests/factories' import * as aiModule from '../ai' @@ -159,7 +159,7 @@ describe('runChain', () => { expect.objectContaining({ documentLogUuid: expect.any(String), object: { name: 'John', age: 30 }, - text: '{"name":"John","age":30}', + text: objectToString({ name: 'John', age: 30 }), usage: { totalTokens: 15 }, }), ) @@ -377,7 +377,7 @@ describe('runChain', () => { expect.objectContaining({ documentLogUuid: expect.any(String), object: { name: 'John', age: 30 }, - text: '{"name":"John","age":30}', + text: objectToString({ name: 'John', age: 30 }), usage: { totalTokens: 15 }, }), ) @@ -463,7 +463,10 @@ describe('runChain', () => { { name: 'John', age: 30 }, { name: 'Jane', age: 25 }, ], - text: '[{"name":"John","age":30},{"name":"Jane","age":25}]', + text: objectToString([ + { name: 'John', age: 30 }, + { name: 'Jane', age: 25 }, + ]), usage: { totalTokens: 20 }, }), ) diff --git a/packages/core/src/services/providerLogs/formatForEvaluation.test.ts b/packages/core/src/services/providerLogs/formatForEvaluation.test.ts new file mode 100644 index 000000000..c6c493d81 --- /dev/null +++ b/packages/core/src/services/providerLogs/formatForEvaluation.test.ts @@ -0,0 +1,423 @@ +import { ContentType, MessageRole } from '@latitude-data/compiler' +import { describe, expect, it } from 'vitest' + +import { ProviderLog, ProviderLogDto } from '../../browser' +import { formatContext, formatConversation } from './formatForEvaluation' + +describe('formatConversation', () => { + it('should format a ProviderLogDto with response correctly', () => { + // @ts-expect-error + const providerLogDto: ProviderLogDto = { + messages: [ + { + role: MessageRole.user, + content: [{ type: ContentType.text, text: 'Hello' }], + }, + { role: MessageRole.assistant, content: 'Hi there', toolCalls: [] }, + ], + response: 'How can I help you?', + toolCalls: [], + } + + const result = formatConversation(providerLogDto) + + expect(result).toEqual({ + all: [ + { + role: MessageRole.user, + content: [{ type: ContentType.text, text: 'Hello' }], + }, + { role: MessageRole.assistant, content: 'Hi there', toolCalls: [] }, + { + role: MessageRole.assistant, + content: 'How can I help you?', + toolCalls: [], + }, + ], + first: { + role: MessageRole.user, + content: [{ type: ContentType.text, text: 'Hello' }], + }, + last: { + role: MessageRole.assistant, + content: 'How can I help you?', + toolCalls: [], + }, + user: { + all: [ + { + role: MessageRole.user, + content: [{ type: ContentType.text, text: 'Hello' }], + }, + ], + first: { + role: MessageRole.user, + content: [{ type: ContentType.text, text: 'Hello' }], + }, + last: { + role: MessageRole.user, + content: [{ type: ContentType.text, text: 'Hello' }], + }, + }, + system: { + all: [], + first: null, + last: null, + }, + assistant: { + all: [ + { role: MessageRole.assistant, content: 'Hi there', toolCalls: [] }, + { + role: MessageRole.assistant, + content: 'How can I help you?', + toolCalls: [], + }, + ], + first: { + role: MessageRole.assistant, + content: 'Hi there', + toolCalls: [], + }, + last: { + role: MessageRole.assistant, + content: 'How can I help you?', + toolCalls: [], + }, + }, + }) + }) + + it('should format a ProviderLog with responseText correctly', () => { + // @ts-expect-error + const providerLog: ProviderLog = { + messages: [ + { role: MessageRole.system, content: 'You are an AI assistant' }, + { + role: MessageRole.user, + content: [ + { type: ContentType.text, text: "What's the weather like?" }, + ], + }, + ], + responseText: 'The weather is sunny today.', + toolCalls: [], + } + + const result = formatConversation(providerLog) + + expect(result).toEqual({ + all: [ + { + role: MessageRole.system, + content: 'You are an AI assistant', + }, + { + role: MessageRole.user, + content: [ + { type: ContentType.text, text: "What's the weather like?" }, + ], + }, + { + role: MessageRole.assistant, + content: 'The weather is sunny today.', + toolCalls: [], + }, + ], + first: { + role: MessageRole.system, + content: 'You are an AI assistant', + }, + last: { + role: MessageRole.assistant, + content: 'The weather is sunny today.', + toolCalls: [], + }, + user: { + all: [ + { + role: MessageRole.user, + content: [ + { type: ContentType.text, text: "What's the weather like?" }, + ], + }, + ], + first: { + role: MessageRole.user, + content: [ + { type: ContentType.text, text: "What's the weather like?" }, + ], + }, + last: { + role: MessageRole.user, + content: [ + { type: ContentType.text, text: "What's the weather like?" }, + ], + }, + }, + system: { + all: [ + { + role: MessageRole.system, + content: 'You are an AI assistant', + }, + ], + first: { + role: MessageRole.system, + content: 'You are an AI assistant', + }, + last: { + role: MessageRole.system, + content: 'You are an AI assistant', + }, + }, + assistant: { + all: [ + { + role: MessageRole.assistant, + content: 'The weather is sunny today.', + toolCalls: [], + }, + ], + first: { + role: MessageRole.assistant, + content: 'The weather is sunny today.', + toolCalls: [], + }, + last: { + role: MessageRole.assistant, + content: 'The weather is sunny today.', + toolCalls: [], + }, + }, + }) + }) + + it('should format a ProviderLog with responseObject correctly', () => { + const obj = { key: 'value', number: 42 } + const objStr = JSON.stringify(obj, null, 2) + const providerLog: ProviderLog = { + messages: [ + { + role: MessageRole.user, + content: [{ type: ContentType.text, text: 'Give me a JSON object' }], + }, + ], + // @ts-expect-error + responseObject: obj, + toolCalls: [], + } + + const result = formatConversation(providerLog) + + expect(result).toEqual({ + all: [ + { + role: MessageRole.user, + content: [{ type: ContentType.text, text: 'Give me a JSON object' }], + }, + { + role: MessageRole.assistant, + content: objStr, + toolCalls: [], + }, + ], + first: { + role: MessageRole.user, + content: [{ type: ContentType.text, text: 'Give me a JSON object' }], + }, + last: { + role: MessageRole.assistant, + content: objStr, + toolCalls: [], + }, + user: { + all: [ + { + role: MessageRole.user, + content: [ + { type: ContentType.text, text: 'Give me a JSON object' }, + ], + }, + ], + first: { + role: MessageRole.user, + content: [{ type: ContentType.text, text: 'Give me a JSON object' }], + }, + last: { + role: MessageRole.user, + content: [{ type: ContentType.text, text: 'Give me a JSON object' }], + }, + }, + system: { + all: [], + first: null, + last: null, + }, + assistant: { + all: [ + { + role: MessageRole.assistant, + content: objStr, + toolCalls: [], + }, + ], + first: { + role: MessageRole.assistant, + content: objStr, + toolCalls: [], + }, + last: { + role: MessageRole.assistant, + content: objStr, + toolCalls: [], + }, + }, + }) + }) + + it('should handle empty messages array', () => { + // @ts-expect-error + const providerLog: ProviderLog = { + messages: [], + responseText: 'Hello!', + toolCalls: [], + } + + const result = formatConversation(providerLog) + + expect(result).toEqual({ + all: [{ role: MessageRole.assistant, content: 'Hello!', toolCalls: [] }], + first: { role: MessageRole.assistant, content: 'Hello!', toolCalls: [] }, + last: { role: MessageRole.assistant, content: 'Hello!', toolCalls: [] }, + user: { + all: [], + first: null, + last: null, + }, + system: { + all: [], + first: null, + last: null, + }, + assistant: { + all: [ + { role: MessageRole.assistant, content: 'Hello!', toolCalls: [] }, + ], + first: { + role: MessageRole.assistant, + content: 'Hello!', + toolCalls: [], + }, + last: { role: MessageRole.assistant, content: 'Hello!', toolCalls: [] }, + }, + }) + }) +}) + +describe('formatContext', () => { + it('should format a conversation with text messages correctly', () => { + // @ts-expect-error + const providerLog: ProviderLog = { + messages: [ + { role: MessageRole.system, content: 'You are an AI assistant' }, + { + role: MessageRole.user, + content: [ + { type: ContentType.text, text: "What's the weather like?" }, + ], + }, + { + role: MessageRole.assistant, + content: 'The weather is sunny today.', + toolCalls: [], + }, + ], + responseText: 'Is there anything else I can help you with?', + toolCalls: [], + } + + const result = formatContext(providerLog) + + expect(result).toBe( + 'System:\nYou are an AI assistant\n\n' + + "User:\nWhat's the weather like?\n\n" + + 'Assistant:\nThe weather is sunny today.', + ) + }) + + it('should handle image content in messages', () => { + // @ts-expect-error + const providerLog: ProviderLog = { + messages: [ + { + role: MessageRole.user, + content: [ + { type: ContentType.text, text: 'What can you see in this image?' }, + { + type: ContentType.image, + image: 'https://example.com/image.jpg', + }, + ], + }, + { + role: MessageRole.assistant, + content: 'I see a beautiful landscape.', + toolCalls: [], + }, + ], + responseText: 'Would you like me to describe it in more detail?', + toolCalls: [], + } + + const result = formatContext(providerLog) + + expect(result).toBe( + 'User:\nWhat can you see in this image?\n\n' + + 'Assistant:\nI see a beautiful landscape.', + ) + }) + + it('should handle empty messages array', () => { + // @ts-expect-error + const providerLog: ProviderLog = { + messages: [], + responseText: 'Hello! How can I assist you today?', + toolCalls: [], + } + + const result = formatContext(providerLog) + + expect(result).toBe('') + }) + + it('should handle messages with string content', () => { + // @ts-expect-error + const providerLogDto: ProviderLogDto = { + messages: [ + { + role: MessageRole.user, + content: [{ type: ContentType.text, text: 'Tell me a joke' }], + }, + { + role: MessageRole.assistant, + content: 'Why did the chicken cross the road?', + toolCalls: [], + }, + { + role: MessageRole.user, + content: [{ type: ContentType.text, text: "I don't know, why?" }], + }, + ], + response: 'To get to the other side!', + toolCalls: [], + } + + const result = formatContext(providerLogDto) + + expect(result).toBe( + 'User:\nTell me a joke\n\n' + + 'Assistant:\nWhy did the chicken cross the road?\n\n' + + "User:\nI don't know, why?", + ) + }) +}) diff --git a/packages/core/src/services/providerLogs/formatForEvaluation.ts b/packages/core/src/services/providerLogs/formatForEvaluation.ts index a1defb37a..a3e08397a 100644 --- a/packages/core/src/services/providerLogs/formatForEvaluation.ts +++ b/packages/core/src/services/providerLogs/formatForEvaluation.ts @@ -33,8 +33,33 @@ export function formatConversation(providerLog: ProviderLogDto | ProviderLog) { return formatMessages(messages) } -export function formatContext(providerLog: ProviderLog | ProviderLogDto) { - return formatMessages(providerLog.messages) +export function formatContext( + providerLog: ProviderLog | ProviderLogDto, +): string { + const messages = providerLog.messages || [] + let formattedConversation = '' + + messages.forEach((message) => { + const speaker = message.role.charAt(0).toUpperCase() + message.role.slice(1) + let content = '' + if (typeof message.content === 'string') { + content = message.content + } else if ( + Array.isArray(message.content) && + 'text' in message.content[0]! + ) { + content = message.content[0].text + } else if ( + Array.isArray(message.content) && + 'image' in message.content[0]! + ) { + content = '[IMAGE]' + } + + formattedConversation += `${speaker}:\n${content}\n\n` + }) + + return formattedConversation.trim() } function formatMessages(messages: Message[]) {