upgrade and fixes

This commit is contained in:
dal 2025-08-18 08:07:13 -06:00
parent 0cf763d9a3
commit 9589ca198b
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
7 changed files with 51 additions and 65 deletions

View File

@ -40,4 +40,4 @@
"devDependencies": {
"@trigger.dev/build": "4.0.0-v4-beta.27"
}
}
}

View File

@ -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', () => {

View File

@ -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

View File

@ -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
);

View File

@ -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 });

View File

@ -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<ModelMessage>());
export type ChatConversationHistoryInput = z.infer<typeof ChatConversationHistoryInputSchema>;
export type ChatConversationHistoryOutput = z.infer<typeof ChatConversationHistoryOutputSchema>;
@ -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'}`

View File

@ -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"