From 9589ca198b366ff19bbed1286f44e2e44f2161df Mon Sep 17 00:00:00 2001 From: dal Date: Mon, 18 Aug 2025 08:07:13 -0600 Subject: [PATCH] upgrade and fixes --- apps/trigger/package.json | 2 +- packages/ai/src/llm/ai-fallback.test.ts | 36 +++++++++++--- ...quential-thinking-tool-transform-helper.ts | 14 +++--- .../sequential-thinking-tool-execute.ts | 2 +- .../sequential-thinking-tool.test.ts | 9 +++- .../messages/chatConversationHistory.ts | 49 ++----------------- pnpm-workspace.yaml | 4 +- 7 files changed, 51 insertions(+), 65 deletions(-) diff --git a/apps/trigger/package.json b/apps/trigger/package.json index fc688b8ad..6c92809ff 100644 --- a/apps/trigger/package.json +++ b/apps/trigger/package.json @@ -40,4 +40,4 @@ "devDependencies": { "@trigger.dev/build": "4.0.0-v4-beta.27" } -} +} \ No newline at end of file diff --git a/packages/ai/src/llm/ai-fallback.test.ts b/packages/ai/src/llm/ai-fallback.test.ts index f98318d69..4ce5ade28 100644 --- a/packages/ai/src/llm/ai-fallback.test.ts +++ b/packages/ai/src/llm/ai-fallback.test.ts @@ -365,7 +365,24 @@ test('throws error when all models fail', async () => { test('model reset interval resets to first model', async () => { vi.useFakeTimers(); - const model1 = new MockLanguageModelV2({ modelId: 'primary-model' }); + let model1CallCount = 0; + const model1 = new MockLanguageModelV2({ + modelId: 'primary-model', + doGenerate: async () => { + model1CallCount++; + if (model1CallCount === 1) { + throw new Error('Test error'); + } + return { + content: [{ type: 'text', text: 'Primary response' }], + finishReason: 'stop', + finishReasonReturnValue: undefined, + usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 }, + rawCall: { rawPrompt: '', rawSettings: {} }, + warnings: [], + }; + }, + }); const model2 = new MockLanguageModelV2({ modelId: 'fallback-model' }); const fallback = createFallback({ @@ -373,8 +390,11 @@ test('model reset interval resets to first model', async () => { modelResetInterval: 60000, // 1 minute }); - // Force switch to model 2 - fallback.currentModelIndex = 1; + // Force switch to model 2 by making model1 fail + try { + await fallback.doGenerate({ prompt: [] }); + } catch {} + expect(fallback.modelId).toBe('fallback-model'); // Advance time past reset interval @@ -558,10 +578,12 @@ test('FallbackModel basic functionality', () => { expect(fallback.specificationVersion).toBe('v2'); expect(fallback.supportedUrls).toEqual({}); - // Test switching models - fallback.currentModelIndex = 1; - expect(fallback.modelId).toBe('model-2'); - expect(fallback.provider).toBe('provider-2'); + // Test model properties after switching (would switch on error) + expect(fallback.currentModelIndex).toBe(0); + // Model 2 properties are accessible but not active until switch + const secondModel = fallback.settings.models[1]; + expect(secondModel?.modelId).toBe('model-2'); + expect(secondModel?.provider).toBe('provider-2'); }); test('constructor throws error when no models provided', () => { diff --git a/packages/ai/src/tools/planning-thinking-tools/sequential-thinking-tool/helpers/sequential-thinking-tool-transform-helper.ts b/packages/ai/src/tools/planning-thinking-tools/sequential-thinking-tool/helpers/sequential-thinking-tool-transform-helper.ts index 84f0a22b3..479452512 100644 --- a/packages/ai/src/tools/planning-thinking-tools/sequential-thinking-tool/helpers/sequential-thinking-tool-transform-helper.ts +++ b/packages/ai/src/tools/planning-thinking-tools/sequential-thinking-tool/helpers/sequential-thinking-tool-transform-helper.ts @@ -12,14 +12,14 @@ import type { SequentialThinkingState } from '../sequential-thinking-tool'; */ export function createSequentialThinkingReasoningMessage( sequentialThinkingState: SequentialThinkingState, - toolCallId: string, + toolCallId?: string, status: ChatMessageReasoning_status = 'loading' -): ChatMessageReasoningMessage_Text { +): ChatMessageReasoningMessage_Text | null { // Use entry_id from state or fallback to provided toolCallId - const id = toolCallId; + const id = sequentialThinkingState.toolCallId || toolCallId; if (!id) { - throw new Error('Tool call ID is required'); + return null; } // Determine title based on status @@ -49,11 +49,11 @@ export function createSequentialThinkingReasoningMessage( export function createSequentialThinkingRawLlmMessageEntry( sequentialThinkingState: SequentialThinkingState, toolCallId?: string -): ModelMessage { - const id = toolCallId; +): ModelMessage | null { + const id = sequentialThinkingState.toolCallId || toolCallId; if (!id) { - throw new Error('Tool call ID is required'); + return null; } // Build the input object with available state diff --git a/packages/ai/src/tools/planning-thinking-tools/sequential-thinking-tool/sequential-thinking-tool-execute.ts b/packages/ai/src/tools/planning-thinking-tools/sequential-thinking-tool/sequential-thinking-tool-execute.ts index de8696567..1486701ca 100644 --- a/packages/ai/src/tools/planning-thinking-tools/sequential-thinking-tool/sequential-thinking-tool-execute.ts +++ b/packages/ai/src/tools/planning-thinking-tools/sequential-thinking-tool/sequential-thinking-tool-execute.ts @@ -38,7 +38,7 @@ async function processSequentialThinking( const rawLlmMessage = createSequentialThinkingRawLlmMessageEntry(state, state.toolCallId); const rawToolResultEntry = createRawToolResultEntry( - context.messageId, + state.toolCallId, SEQUENTIAL_THINKING_TOOL_NAME, output ); diff --git a/packages/ai/src/tools/planning-thinking-tools/sequential-thinking-tool/sequential-thinking-tool.test.ts b/packages/ai/src/tools/planning-thinking-tools/sequential-thinking-tool/sequential-thinking-tool.test.ts index 190fd02aa..af151a622 100644 --- a/packages/ai/src/tools/planning-thinking-tools/sequential-thinking-tool/sequential-thinking-tool.test.ts +++ b/packages/ai/src/tools/planning-thinking-tools/sequential-thinking-tool/sequential-thinking-tool.test.ts @@ -39,6 +39,13 @@ describe('Sequential Thinking Tool', () => { const tool = createSequentialThinkingTool(context); expect(tool.execute).toBeDefined(); + expect(tool.onInputStart).toBeDefined(); + + // First call onInputStart to set up the state (simulating streaming) + if (tool.onInputStart) { + await tool.onInputStart({ toolCallId: 'test-tool-call-123', messages: [] }); + } + const execute = tool.execute; if (!execute) throw new Error('execute is undefined'); @@ -48,7 +55,7 @@ describe('Sequential Thinking Tool', () => { nextThoughtNeeded: true, thoughtNumber: 1, }, - { toolCallId: 'test', messages: [] } + { toolCallId: 'test-tool-call-123', messages: [] } ); expect(result).toEqual({ success: true }); diff --git a/packages/database/src/queries/messages/chatConversationHistory.ts b/packages/database/src/queries/messages/chatConversationHistory.ts index 732be81e6..2a25d9cc0 100644 --- a/packages/database/src/queries/messages/chatConversationHistory.ts +++ b/packages/database/src/queries/messages/chatConversationHistory.ts @@ -1,4 +1,4 @@ -import type { CoreMessage } from 'ai'; +import type { CoreMessage, ModelMessage } from 'ai'; import { and, eq, isNull } from 'drizzle-orm'; import { z } from 'zod'; import { db } from '../../connection'; @@ -64,52 +64,12 @@ async function getAllMessagesForChat(chatId: string): Promise< return chatMessages; } -// Helper function to get the most recent raw LLM messages -function getMostRecentRawLlmMessages( - chatMessages: Array<{ rawLlmMessages: unknown; isCompleted: boolean }> -): CoreMessage[] { - try { - // Find the most recent completed message with valid rawLlmMessages - // We iterate backwards to find the most recent valid message - for (let i = chatMessages.length - 1; i >= 0; i--) { - const message = chatMessages[i]; - - // Skip if message is not completed - if (!message?.isCompleted) { - continue; - } - - // Skip if rawLlmMessages is empty, null, or default empty object - const rawMessages = message.rawLlmMessages; - if ( - !rawMessages || - (typeof rawMessages === 'object' && Object.keys(rawMessages).length === 0) || - rawMessages === '{}' - ) { - continue; - } - - // Check if it's a valid array - if (Array.isArray(rawMessages) && rawMessages.length > 0) { - return rawMessages as CoreMessage[]; - } - } - - // No valid messages found - return []; - } catch (processingError) { - throw new Error( - `Failed to process conversation history: ${processingError instanceof Error ? processingError.message : 'Unknown processing error'}` - ); - } -} - // Zod schemas for validation export const ChatConversationHistoryInputSchema = z.object({ messageId: z.string().uuid('Message ID must be a valid UUID'), }); -export const ChatConversationHistoryOutputSchema = z.array(z.any()); // CoreMessage[] but allowing any for flexibility +export const ChatConversationHistoryOutputSchema = z.array(z.custom()); export type ChatConversationHistoryInput = z.infer; export type ChatConversationHistoryOutput = z.infer; @@ -132,12 +92,9 @@ export async function getChatConversationHistory( // Get all messages for this chat const chatMessages = await getAllMessagesForChat(chatId); - // Get the most recent rawLlmMessages which contains the complete conversation history - const conversationHistory = getMostRecentRawLlmMessages(chatMessages); - // Validate output try { - return ChatConversationHistoryOutputSchema.parse(conversationHistory); + return ChatConversationHistoryOutputSchema.parse(chatMessages); } catch (validationError) { throw new Error( `Output validation failed: ${validationError instanceof Error ? validationError.message : 'Invalid output format'}` diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c915a178a..16d209364 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -11,8 +11,8 @@ packages: catalog: "@supabase/supabase-js": "^2.50.0" - "@trigger.dev/build": "^4.0.0-v4-beta.27" - "@trigger.dev/sdk": "^4.0.0-v4-beta.27" + "@trigger.dev/build": "^4.0.0-v4-beta.28" + "@trigger.dev/sdk": "^4.0.0-v4-beta.28" "@platejs/autoformat": "^49.0.0" "@platejs/basic-nodes": "^49.0.0" ai: "^5.0.5"