more type safety

This commit is contained in:
dal 2025-08-13 10:49:35 -06:00
parent 8299e47e4e
commit 3e1181c121
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
4 changed files with 46 additions and 28 deletions

View File

@ -4,9 +4,9 @@ import { wrapTraced } from 'braintrust';
import z from 'zod'; import z from 'zod';
import { Sonnet4 } from '../../llm'; import { Sonnet4 } from '../../llm';
import { createExecuteSqlTool, createSequentialThinkingTool } from '../../tools'; import { createExecuteSqlTool, createSequentialThinkingTool } from '../../tools';
import { createMessageUserClarifyingQuestionTool } from '../../tools/communication-tools/message-user-clarifying-question/message-user-clarifying-question'; import { createMessageUserClarifyingQuestionTool, MESSAGE_USER_CLARIFYING_QUESTION_TOOL_NAME } from '../../tools/communication-tools/message-user-clarifying-question/message-user-clarifying-question';
import { createRespondWithoutAssetCreationTool } from '../../tools/communication-tools/respond-without-asset-creation/respond-without-asset-creation-tool'; import { createRespondWithoutAssetCreationTool, RESPOND_WITHOUT_ASSET_CREATION_TOOL_NAME } from '../../tools/communication-tools/respond-without-asset-creation/respond-without-asset-creation-tool';
import { createSubmitThoughtsTool } from '../../tools/communication-tools/submit-thoughts-tool/submit-thoughts-tool'; import { createSubmitThoughtsTool, SUBMIT_THOUGHTS_TOOL_NAME } from '../../tools/communication-tools/submit-thoughts-tool/submit-thoughts-tool';
import { healToolWithLlm } from '../../utils'; import { healToolWithLlm } from '../../utils';
import { import {
type AnalysisMode, type AnalysisMode,
@ -23,9 +23,9 @@ const DEFAULT_CACHE_OPTIONS = {
const STOP_CONDITIONS = [ const STOP_CONDITIONS = [
stepCountIs(25), stepCountIs(25),
hasToolCall('submitThoughts'), hasToolCall(SUBMIT_THOUGHTS_TOOL_NAME),
hasToolCall('respondWithoutAssetCreation'), hasToolCall(RESPOND_WITHOUT_ASSET_CREATION_TOOL_NAME),
hasToolCall('messageUserClarifyingQuestion'), hasToolCall(MESSAGE_USER_CLARIFYING_QUESTION_TOOL_NAME),
]; ];
export const ThinkAndPrepAgentOptionsSchema = z.object({ export const ThinkAndPrepAgentOptionsSchema = z.object({

View File

@ -91,11 +91,11 @@ function processToolOutput(output: unknown, files: ExtractedFile[]): void {
} }
} }
// Check if this is a reports tool output // Check if this is a create reports tool output
if (isReportsToolOutput(output)) { if (isCreateReportsToolOutput(output)) {
const operation = detectOperation(output.message); const operation = detectOperation(output.message);
// Extract successfully created/modified report files // Extract successfully created report files
if (output.files && Array.isArray(output.files)) { if (output.files && Array.isArray(output.files)) {
for (const file of output.files) { for (const file of output.files) {
files.push({ files.push({
@ -108,21 +108,22 @@ function processToolOutput(output: unknown, files: ExtractedFile[]): void {
}); });
} }
} }
}
// Check if this is a modify reports tool output
if (isModifyReportsToolOutput(output)) {
// For modify reports tool, extract from the file object // For modify reports tool, extract from the file object
if ('file' in output && output.file && typeof output.file === 'object') { if (output.file && typeof output.file === 'object') {
const file = output.file as Record<string, unknown>;
if (file.id && file.name && file.version_number) {
files.push({ files.push({
id: file.id as string, id: output.file.id,
fileType: 'report', fileType: 'report',
fileName: file.name as string, fileName: output.file.name,
status: 'completed', status: 'completed',
operation: 'modified', operation: 'modified',
versionNumber: file.version_number as number, versionNumber: output.file.version_number,
}); });
} }
} }
}
} }
/** /**
@ -168,30 +169,43 @@ function isDashboardsToolOutput(
} }
/** /**
* Type guard to check if output is from create/modify reports tool * Type guard to check if output is from create reports tool
*/ */
function isReportsToolOutput(output: unknown): output is CreateReportsOutput | ModifyReportsOutput { function isCreateReportsToolOutput(output: unknown): output is CreateReportsOutput {
if (!output || typeof output !== 'object') return false; if (!output || typeof output !== 'object') return false;
const obj = output as Record<string, unknown>; const obj = output as Record<string, unknown>;
// Check for create reports output structure // Check for create reports output structure
if ('files' in obj && 'message' in obj && Array.isArray(obj.files)) { if ('files' in obj && 'message' in obj && 'failed_files' in obj) {
// Check if files have report-specific properties if (!Array.isArray(obj.files)) return false;
// Check if files have report-specific properties (id, name, version_number)
return obj.files.every((file: unknown) => { return obj.files.every((file: unknown) => {
if (!file || typeof file !== 'object') return false; if (!file || typeof file !== 'object') return false;
const fileObj = file as Record<string, unknown>; const fileObj = file as Record<string, unknown>;
// Reports don't have file_type in the output, just id, name, version_number
return 'id' in fileObj && 'name' in fileObj && 'version_number' in fileObj; return 'id' in fileObj && 'name' in fileObj && 'version_number' in fileObj;
}); });
} }
// Check for modify reports output structure (has a single 'file' property) return false;
}
/**
* Type guard to check if output is from modify reports tool
*/
function isModifyReportsToolOutput(output: unknown): output is ModifyReportsOutput {
if (!output || typeof output !== 'object') return false;
const obj = output as Record<string, unknown>;
// Check for modify reports output structure (has success, message, and file properties)
if ('success' in obj && 'message' in obj && 'file' in obj) { if ('success' in obj && 'message' in obj && 'file' in obj) {
const file = obj.file; const file = obj.file;
if (file && typeof file === 'object') { if (file && typeof file === 'object') {
const fileObj = file as Record<string, unknown>; const fileObj = file as Record<string, unknown>;
return 'id' in fileObj && 'name' in fileObj && 'version_number' in fileObj; // Check for required file properties
return 'id' in fileObj && 'name' in fileObj && 'version_number' in fileObj && 'content' in fileObj && 'updated_at' in fileObj;
} }
} }

View File

@ -5,6 +5,8 @@ import { createRespondWithoutAssetCreationExecute } from './respond-without-asse
import { createRespondWithoutAssetCreationFinish } from './respond-without-asset-creation-finish'; import { createRespondWithoutAssetCreationFinish } from './respond-without-asset-creation-finish';
import { createRespondWithoutAssetCreationStart } from './respond-without-asset-creation-start'; import { createRespondWithoutAssetCreationStart } from './respond-without-asset-creation-start';
export const RESPOND_WITHOUT_ASSET_CREATION_TOOL_NAME = 'respondWithoutAssetCreation';
export const RespondWithoutAssetCreationInputSchema = z.object({ export const RespondWithoutAssetCreationInputSchema = z.object({
final_response: z final_response: z
.string() .string()

View File

@ -2,6 +2,8 @@ import { tool } from 'ai';
import { wrapTraced } from 'braintrust'; import { wrapTraced } from 'braintrust';
import { z } from 'zod'; import { z } from 'zod';
export const SUBMIT_THOUGHTS_TOOL_NAME = 'submitThoughts';
// Minimal schemas: no input/output for a signal-only tool // Minimal schemas: no input/output for a signal-only tool
const SubmitThoughtsInputSchema = z.object({}); const SubmitThoughtsInputSchema = z.object({});
const SubmitThoughtsOutputSchema = z.object({}); const SubmitThoughtsOutputSchema = z.object({});