Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 41 additions & 41 deletions app/components/DebugPromptView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { useEffect, useCallback, useState } from 'react';
import { JsonView } from 'react-json-view-lite';
import 'react-json-view-lite/dist/index.css';
import type { CoreMessage, FilePart, ToolCallPart, TextPart } from 'ai';
import type { ModelMessage, FilePart, ToolCallPart, TextPart } from 'ai';
import { ChevronDownIcon, ChevronRightIcon } from '@heroicons/react/20/solid';
import { ClipboardIcon, ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline';
import { useDebugPrompt } from '~/lib/hooks/useDebugPrompt';
Expand All @@ -14,58 +14,58 @@ import { IconButton } from '~/components/ui/IconButton';
* <DebugAllPromptsForChat initialChatId={}>
* <UserPromptGroup> // A group of LLM calls that were executed without the user typing anything in between
* <LlmPromptAndResponseView model="Anthropic's Best"> // A single call to an LLM and the response we received bac
* <CoreMessageView role='system'> // CoreMessage is a Vercel AI SDK type, it's a User or Assistant or System or Tool message.
* <ModelMessageView role='system'> // ModelMessage is a Vercel AI SDK type, it's a User or Assistant or System or Tool message.
* Do a good job.
* </CoreMessageView>
* <CoreMessageView role='user'>
* </ModelMessageView>
* <ModelMessageView role='user'>
* Please make an app!
* </CoreMessageView>
* <CoreMessageView role='assistant'>
* </ModelMessageView>
* <ModelMessageView role='assistant'>
* Will do! Please write these files and run this tool.
* </CoreMessageView>
* </ModelMessageView>
* </LlmPromptAndResponseView>
*
* --- this represents a new call to /api/chat and a new network request to an LLM provider
*
* <LlmPromptAndResponseView model="Something from AWS">
* <CoreMessageView role='system'> // We typically repeat back all these messages
* <ModelMessageView role='system'> // We typically repeat back all these messages
* Do a good job.
* </CoreMessageView>
* <CoreMessageView role='system'> // But we can also add new ones
* </ModelMessageView>
* <ModelMessageView role='system'> // But we can also add new ones
* BTW, the user is using Chrome on a Mac.
* </CoreMessageView>
* <CoreMessageView role='user'>
* </ModelMessageView>
* <ModelMessageView role='user'>
* Please make an app!
* </CoreMessageView>
* <CoreMessageView role='assistant'>
* </ModelMessageView>
* <ModelMessageView role='assistant'>
* Will do! Please write these files and run this tool.
* </CoreMessageView>
* <CoreMessageView role='tool'>
* </ModelMessageView>
* <ModelMessageView role='tool'>
* file write done! and also I deployed the app.
* </CoreMessageView>
* </ModelMessageView>
* </LlmPromptAndResponseView>
*
* --- this represents a new call to /api/chat and a new network request to an LLM provider
*
* <LlmPromptAndResponseView model="Something from AWS">
* <CoreMessageView role='system'> // We typically repeat back all these messages
* <ModelMessageView role='system'> // We typically repeat back all these messages
* Do a good job.
* </CoreMessageView>
* <CoreMessageView role='system'> // But we can also add new ones
* </ModelMessageView>
* <ModelMessageView role='system'> // But we can also add new ones
* BTW, the user is using Chrome on a Mac.
* </CoreMessageView>
* <CoreMessageView role='user'>
* </ModelMessageView>
* <ModelMessageView role='user'>
* Please make an app!
* </CoreMessageView>
* <CoreMessageView role='assistant'>
* </ModelMessageView>
* <ModelMessageView role='assistant'>
* Will do! Please write these files and run this tool.
* </CoreMessageView>
* <CoreMessageView role='tool'>
* </ModelMessageView>
* <ModelMessageView role='tool'>
* file write done! and also I deployed the app.
* </CoreMessageView>
* <CoreMessageView role='assistant'>
* </ModelMessageView>
* <ModelMessageView role='assistant'>
* OK all done, I made you an app!
* </CoreMessageView>
* </ModelMessageView>
* </LlmPromptAndResponseView>
* </UserPromptGroup>
*
Expand Down Expand Up @@ -107,7 +107,7 @@ function isToolCallPart(part: unknown): part is ToolCallPart {
return typeof part === 'object' && part !== null && 'type' in part && part.type === 'tool-call';
}

function getMessageCharCount(message: CoreMessage): number {
function getMessageCharCount(message: ModelMessage): number {
if (typeof message.content === 'string') return message.content.length;
if (Array.isArray(message.content)) {
return message.content.reduce((sum, part) => {
Expand Down Expand Up @@ -145,7 +145,7 @@ function getPreviewClass(text: string) {
return `preview-${Math.abs(text.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0))}`;
}

function findLastAssistantMessage(prompt: CoreMessage[]): string {
function findLastAssistantMessage(prompt: ModelMessage[]): string {
// The last assistant message in a LLM of messages is the response.
// It should generally just be the last message, full stop.
for (let i = prompt.length - 1; i >= 0; i--) {
Expand Down Expand Up @@ -186,7 +186,7 @@ function LlmPromptAndResponseView({ promptAndResponse }: { promptAndResponse: Ll
const totalInputChars = (prompt || []).reduce((sum, msg) => sum + getMessageCharCount(msg), 0);
const totalOutputChars = (completion || []).reduce((sum, msg) => sum + getMessageCharCount(msg), 0);

const getTokenEstimate = (message: CoreMessage) => {
const getTokenEstimate = (message: ModelMessage) => {
const charCount = getMessageCharCount(message);
return estimateTokenCount(charCount, totalInputChars, promptTokensTotal);
};
Expand Down Expand Up @@ -239,7 +239,7 @@ function LlmPromptAndResponseView({ promptAndResponse }: { promptAndResponse: Ll
</div>

{prompt.map((message, idx) => (
<CoreMessageView
<ModelMessageView
key={idx}
message={message}
getTokenEstimate={getTokenEstimate}
Expand All @@ -252,7 +252,7 @@ function LlmPromptAndResponseView({ promptAndResponse }: { promptAndResponse: Ll
{formatNumber(outputTokens)} completion tokens ({formatNumber(totalOutputChars)} chars)
</div>
{completion.map((message, idx) => (
<CoreMessageView key={idx} message={message} />
<ModelMessageView key={idx} message={message} />
))}
</div>
</div>
Expand All @@ -261,13 +261,13 @@ function LlmPromptAndResponseView({ promptAndResponse }: { promptAndResponse: Ll
);
}

type CoreMessageViewProps = {
message: CoreMessage;
getTokenEstimate?: (message: CoreMessage) => number;
type ModelMessageViewProps = {
message: ModelMessage;
getTokenEstimate?: (message: ModelMessage) => number;
totalCompletionTokens?: number;
};

function getMessagePreview(content: CoreMessage['content']): string {
function getMessagePreview(content: ModelMessage['content']): string {
if (typeof content === 'string') {
return content;
}
Expand All @@ -288,7 +288,7 @@ function getMessagePreview(content: CoreMessage['content']): string {
}

type MessageContentViewProps = {
content: CoreMessage['content'];
content: ModelMessage['content'];
showRawJson?: boolean;
};

Expand Down Expand Up @@ -371,7 +371,7 @@ const roleColors = {
tool: 'bg-purple-50 dark:bg-purple-900/20 border-purple-200 dark:border-purple-800',
} as const;

function CoreMessageView({ message, getTokenEstimate, totalCompletionTokens }: CoreMessageViewProps) {
function ModelMessageView({ message, getTokenEstimate, totalCompletionTokens }: ModelMessageViewProps) {
const [isExpanded, setIsExpanded] = useState(false);

const roleColor = roleColors[message.role as keyof typeof roleColors] || roleColors.user;
Expand Down Expand Up @@ -428,7 +428,7 @@ function CoreMessageView({ message, getTokenEstimate, totalCompletionTokens }: C

function findTriggeringUserMessage(promptAndResponses: LlmPromptAndResponse[]): string {
// Look through all prompt and responses in reverse order.
// Generally this should be the final CoreMessage of the prompt.
// Generally this should be the final ModelMessage of the prompt.
for (let i = promptAndResponses.length - 1; i >= 0; i--) {
const promptAndResponse = promptAndResponses[i];
// Look through messages in reverse order to find the last user message
Expand Down
26 changes: 13 additions & 13 deletions app/lib/.server/llm/convex-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
createDataStream,
streamText,
type CoreAssistantMessage,
type CoreMessage,
type ModelMessage,
type CoreToolMessage,
type DataStreamWriter,
type LanguageModelUsage,
Expand Down Expand Up @@ -108,7 +108,7 @@ export async function convexAgent(args: {
tools.edit = editTool;
}

const messagesForDataStream: CoreMessage[] = [
const messagesForDataStream: ModelMessage[] = [
{
role: 'system' as const,
content: ROLE_SYSTEM_PROMPT,
Expand All @@ -124,7 +124,7 @@ export async function convexAgent(args: {
execute(dataStream) {
const result = streamText({
model: provider.model,
maxTokens: provider.maxTokens,
maxOutputTokens: provider.maxTokens,
providerOptions: provider.options,
messages: messagesForDataStream,
tools,
Expand All @@ -139,7 +139,7 @@ export async function convexAgent(args: {
recordUsageCb,
toolsDisabledFromRepeatedErrors: shouldDisableTools,
recordRawPromptsForDebugging,
coreMessages: messagesForDataStream,
modelMessages: messagesForDataStream,
modelProvider,
modelChoice,
collapsedMessages,
Expand Down Expand Up @@ -207,7 +207,7 @@ async function onFinishHandler({
recordUsageCb,
toolsDisabledFromRepeatedErrors,
recordRawPromptsForDebugging,
coreMessages,
modelMessages,
modelProvider,
modelChoice,
collapsedMessages,
Expand All @@ -227,7 +227,7 @@ async function onFinishHandler({
) => Promise<void>;
recordRawPromptsForDebugging: boolean;
toolsDisabledFromRepeatedErrors: boolean;
coreMessages: CoreMessage[];
modelMessages: ModelMessage[];
modelProvider: ModelProvider;
modelChoice: string | undefined;
collapsedMessages: boolean;
Expand Down Expand Up @@ -327,13 +327,13 @@ async function onFinishHandler({
await recordUsageCb(messages[messages.length - 1], { usage, providerMetadata });
}
if (recordRawPromptsForDebugging) {
const responseCoreMessages = result.response.messages as (CoreAssistantMessage | CoreToolMessage)[];
const responseModelMessages = result.response.messages as (CoreAssistantMessage | CoreToolMessage)[];
// don't block the request but keep the request alive in Vercel Lambdas
waitUntil(
storeDebugPrompt(
coreMessages,
modelMessages,
chatInitialId,
responseCoreMessages,
responseModelMessages,
result,
{
usage,
Expand Down Expand Up @@ -406,9 +406,9 @@ function buildUsageRecord(usage: Usage): UsageRecord {
}

async function storeDebugPrompt(
promptCoreMessages: CoreMessage[],
promptModelMessages: ModelMessage[],
chatInitialId: string,
responseCoreMessages: CoreMessage[],
responseModelMessages: ModelMessage[],
result: Omit<StepResult<any>, 'stepType' | 'isContinued'>,
generation: { usage: LanguageModelUsage; providerMetadata?: ProviderMetadata },
modelProvider: ModelProvider,
Expand All @@ -418,15 +418,15 @@ async function storeDebugPrompt(
const modelId = result.response.modelId || '';
const usage = usageFromGeneration(generation);

const promptMessageData = new TextEncoder().encode(JSON.stringify(promptCoreMessages));
const promptMessageData = new TextEncoder().encode(JSON.stringify(promptModelMessages));
const compressedData = compressWithLz4Server(promptMessageData);

type Metadata = Omit<(typeof internal.debugPrompt.storeDebugPrompt)['_args'], 'promptCoreMessagesStorageId'>;
const { chefTokens } = calculateChefTokens(usage, modelProvider);

const metadata = {
chatInitialId,
responseCoreMessages,
responseCoreMessages: responseModelMessages,
finishReason,
modelId,
usage: buildUsageRecord(usage),
Expand Down
6 changes: 3 additions & 3 deletions app/lib/hooks/useDebugPrompt.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { useConvex, useMutation, useQuery } from 'convex/react';
import { useQueries as useReactQueries } from '@tanstack/react-query';
import { api } from '@convex/_generated/api';
import type { CoreMessage } from 'ai';
import type { ModelMessage } from 'ai';
import { decompressWithLz4 } from '~/lib/compression.client';
import { queryClientStore } from '~/lib/stores/reactQueryClient';
import { useEffect, useState } from 'react';
import { getConvexAuthToken } from '~/lib/stores/sessionId';

async function fetchPromptData(url: string): Promise<CoreMessage[]> {
async function fetchPromptData(url: string): Promise<ModelMessage[]> {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch prompt data: ${response.statusText}`);
Expand All @@ -17,7 +17,7 @@ async function fetchPromptData(url: string): Promise<CoreMessage[]> {
const decompressedData = decompressWithLz4(new Uint8Array(compressedData));
const textDecoder = new TextDecoder();
const jsonString = textDecoder.decode(decompressedData);
return JSON.parse(jsonString) as CoreMessage[];
return JSON.parse(jsonString) as ModelMessage[];
}

export function useAuthToken() {
Expand Down
4 changes: 2 additions & 2 deletions chef-agent/cleanupAssistantMessages.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { convertToCoreMessages } from 'ai';
import { convertToModelMessages } from 'ai';
import type { Message } from 'ai';
import { EXCLUDED_FILE_PATHS } from './constants.js';

Expand All @@ -24,7 +24,7 @@ export function cleanupAssistantMessages(messages: Message[]) {
(message.parts &&
message.parts.filter((part) => part.type === 'text' || part.type === 'tool-invocation').length > 0),
);
return convertToCoreMessages(processedMessages).filter((message) => message.content.length > 0);
return convertToModelMessages(processedMessages).filter((message) => message.content.length > 0);
}

function cleanMessage(message: string) {
Expand Down
4 changes: 2 additions & 2 deletions chef-agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
"typecheck": "tsc"
},
"dependencies": {
"ai": "^4.3.2",
"ai": "^5.0.8",
"jose": "^5.9.6",
"path-browserify": "^1.0.1",
"typescript": "^5.4.2",
"zod": "^3.24.1"
"zod": "^3.25.0"
},
"devDependencies": {
"@types/node": "^20.17.30",
Expand Down
24 changes: 10 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@
"node": ">=18.18.0"
},
"dependencies": {
"@ai-sdk/amazon-bedrock": "^2.2.9",
"@ai-sdk/anthropic": "^1.2.12",
"@ai-sdk/google": "^1.2.11",
"@ai-sdk/google-vertex": "^2.2.24",
"@ai-sdk/openai": "^1.3.6",
"@ai-sdk/react": "^1.2.5",
"@ai-sdk/xai": "^1.2.13",
"@ai-sdk/amazon-bedrock": "^2.0.0",
"@ai-sdk/anthropic": "^2.0.0",
"@ai-sdk/google": "^2.0.0",
"@ai-sdk/google-vertex": "^2.0.0",
"@ai-sdk/openai": "^2.0.5",
"@ai-sdk/react": "^2.0.0",
"@ai-sdk/xai": "^2.0.0",
"@auth0/auth0-react": "^2.3.0",
"@aws-sdk/credential-providers": "^3.782.0",
"@aws-sdk/rds-signer": "^3.782.0",
Expand Down Expand Up @@ -90,7 +90,7 @@
"@xterm/addon-fit": "^0.10.0",
"@xterm/addon-web-links": "^0.11.0",
"@xterm/xterm": "^5.5.0",
"ai": "^4.3.2",
"ai": "^5.0.8",
"allotment": "^1.20.3",
"chart.js": "^4.4.9",
"chef-agent": "workspace:*",
Expand Down Expand Up @@ -188,18 +188,14 @@
"vite-plugin-optimize-css-modules": "^1.1.0",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^2.1.9",
"zod": "^3.24.1"
"zod": "^3.25.0"
},
"resolutions": {
"@typescript-eslint/utils": "^8.0.0-alpha.30"
},
"pnpm": {
"overrides": {
"@remix-run/cloudflare": "npm:@remix-run/[email protected]",
"@ai-sdk/google": "npm:@convex-dev/[email protected]"
},
"patchedDependencies": {
"@ai-sdk/[email protected]": "patches/@[email protected]"
"@remix-run/cloudflare": "npm:@remix-run/[email protected]"
}
},
"packageManager": "[email protected]"
Expand Down
Loading