import { MCP_MAX_ACTIVITIES } from '@ghostfolio/common/config'; import { Type as ActivityType } from '@prisma/client'; import { z } from 'zod'; // The strict mode of the structured outputs requires every property, hence a // value which the file does not show is null instead of being left out. The // values are checked when the rows and the matches are joined to the // activities to import, so that a wrong value or a wrong match skips its rows // only const PRINTED_TEXT = z.string().trim().nullable(); export const ACTIVITY_ROWS_OUTPUT = z.object({ rows: z .array( z.object({ accountName: PRINTED_TEXT.describe( 'The name or the number of the account, as printed' ), currency: z .string() .nullable() .describe( 'The currency of the fee and of the unit price, as an ISO 4217 code in upper case' ), date: z .string() .describe('The date of the activity, as an ISO 8601 date'), fee: z.number().describe('The fee of the activity'), isin: PRINTED_TEXT.describe('The ISIN of the asset, as printed'), name: PRINTED_TEXT.describe( 'The name of the asset, or the description of a fee, an interest or a liability, as printed' ), quantity: z.number().describe('The quantity of the activity'), symbol: PRINTED_TEXT.describe( 'The ticker symbol of the asset, as printed' ), type: z.enum(ActivityType).describe('The type of the activity'), unitPrice: z.number().describe('The unit price of the activity') }) ) .describe(`The activities of the file, at most ${MCP_MAX_ACTIVITIES}`) }); export const ROW_MATCHES_OUTPUT = z.object({ accounts: z .array( z.object({ accountId: z .string() .nullable() .describe( 'The id of the account of the user which matches, or null if no account matches' ), name: z.string().describe('The account name of the file, as given') }) ) .describe('One entry for each account name of the file'), assets: z .array( z.object({ currency: z .string() .nullable() .describe( 'The currency of the asset profile, as given by search-asset-profiles' ), dataSource: z .string() .nullable() .describe( 'The data source of the asset profile, as given by search-asset-profiles' ), id: z.number().int().describe('The id of the asset, as given'), symbol: z .string() .nullable() .describe( 'The symbol of the asset profile, as given by search-asset-profiles, or null if no asset profile matches' ) }) ) .describe('One entry for each asset of the file') });