Skip to content

Commit 3ee4543

Browse files
jirispilkaMQ37
andauthored
fix: truncate properties (#46)
* fix - truncate properties * Add enum length check and truncate if exceeds limit * snapshot * Refactor `shortenEnum` function and remove debug logs --------- Co-authored-by: MQ <themq37@gmail.com> Co-authored-by: MQ <me@kopecky.io>
1 parent 06b4152 commit 3ee4543

3 files changed

Lines changed: 51 additions & 10 deletions

File tree

src/actors.ts

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Ajv } from 'ajv';
22
import { ApifyClient } from 'apify-client';
33

4-
import { ACTOR_ADDITIONAL_INSTRUCTIONS, defaults, MAX_DESCRIPTION_LENGTH, ACTOR_README_MAX_LENGTH } from './const.js';
4+
import { ACTOR_ADDITIONAL_INSTRUCTIONS, defaults, MAX_DESCRIPTION_LENGTH, ACTOR_README_MAX_LENGTH, ACTOR_ENUM_MAX_LENGTH } from './const.js';
55
import { log } from './logger.js';
66
import type { ActorDefinitionPruned, ActorDefinitionWithDesc, IActorInputSchema, ISchemaProperties, Tool } from './types.js';
77

@@ -78,15 +78,40 @@ function pruneActorDefinition(response: ActorDefinitionWithDesc): ActorDefinitio
7878
}
7979

8080
/**
81-
* Shortens the description and enum values of schema properties.
81+
* Helper function to shorten the enum list if it is too long.
82+
*
83+
* @param {string[]} enumList - The list of enum values to be shortened.
84+
* @returns {string[] | undefined} - The shortened enum list or undefined if the list is too long.
85+
*/
86+
export function shortenEnum(enumList: string[]): string[] | undefined {
87+
let charCount = 0;
88+
const resultEnumList = enumList.filter((enumValue) => {
89+
charCount += enumValue.length;
90+
return charCount <= ACTOR_ENUM_MAX_LENGTH;
91+
});
92+
93+
return resultEnumList.length > 0 ? resultEnumList : undefined;
94+
}
95+
96+
/**
97+
* Shortens the description, enum, and items.enum properties of the schema properties.
8298
* @param properties
8399
*/
84100
export function shortenProperties(properties: { [key: string]: ISchemaProperties}): { [key: string]: ISchemaProperties } {
85101
for (const property of Object.values(properties)) {
86102
if (property.description.length > MAX_DESCRIPTION_LENGTH) {
87103
property.description = `${property.description.slice(0, MAX_DESCRIPTION_LENGTH)}...`;
88104
}
105+
106+
if (property.enum && property.enum?.length > 0) {
107+
property.enum = shortenEnum(property.enum);
108+
}
109+
110+
if (property.items?.enum && property.items.enum.length > 0) {
111+
property.items.enum = shortenEnum(property.items.enum);
112+
}
89113
}
114+
90115
return properties;
91116
}
92117

@@ -282,7 +307,7 @@ function buildNestedProperties(properties: Record<string, ISchemaProperties>): R
282307
* Tool name can't contain /, so it is replaced with _
283308
*
284309
* The input schema processing workflow:
285-
* 1. Properties are marked as required using markInputPropertiesAsRequired()
310+
* 1. Properties are marked as required using markInputPropertiesAsRequired() to add "REQUIRED" prefix to descriptions
286311
* 2. Nested properties are built by analyzing editor type (proxy, requestListSources) using buildNestedProperties()
287312
* 3. Properties are filtered using filterSchemaProperties()
288313
* 4. Properties are shortened using shortenProperties()
@@ -298,11 +323,11 @@ export async function getActorsAsTools(actors: string[]): Promise<Tool[]> {
298323
for (const result of results) {
299324
if (result) {
300325
if (result.input && 'properties' in result.input && result.input) {
301-
const propertiesMarkedAsRequired = markInputPropertiesAsRequired(result.input);
302-
const propertiesObjectsBuilt = buildNestedProperties(propertiesMarkedAsRequired);
303-
const propertiesFiltered = filterSchemaProperties(propertiesObjectsBuilt);
304-
const propertiesShortened = shortenProperties(propertiesFiltered);
305-
result.input.properties = addEnumsToDescriptionsWithExamples(propertiesShortened);
326+
result.input.properties = markInputPropertiesAsRequired(result.input);
327+
result.input.properties = buildNestedProperties(result.input.properties);
328+
result.input.properties = filterSchemaProperties(result.input.properties);
329+
result.input.properties = shortenProperties(result.input.properties);
330+
result.input.properties = addEnumsToDescriptionsWithExamples(result.input.properties);
306331
}
307332
try {
308333
const memoryMbytes = result.defaultRunOptions?.memoryMbytes || defaults.maxMemoryMbytes;

src/const.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ export const defaults = {
1515
maxMemoryMbytes: 4096,
1616
};
1717

18-
export const ACTOR_README_MAX_LENGTH = 5000;
18+
export const ACTOR_README_MAX_LENGTH = 5_000;
19+
export const ACTOR_ENUM_MAX_LENGTH = 200;
1920
export const ACTOR_OUTPUT_MAX_CHARS_PER_ITEM = 5_000;
2021
export const ACTOR_OUTPUT_TRUNCATED_MESSAGE = `Output was truncated because it will not fit into context.`
2122
+ `There is no reason to call this tool again!`;

tests/actors-test.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { describe, it, expect } from 'vitest';
22

3-
import { actorNameToToolName, inferArrayItemType } from '../src/actors.js';
3+
import { actorNameToToolName, inferArrayItemType, shortenEnum } from '../src/actors.js';
4+
import { ACTOR_ENUM_MAX_LENGTH } from '../src/const.js';
45

56
describe('actors', () => {
67
describe('actorNameToToolName', () => {
@@ -39,5 +40,19 @@ describe('actors', () => {
3940
};
4041
expect(inferArrayItemType(property)).toBe('string');
4142
});
43+
44+
it('shorten enum list', () => {
45+
const enumList: string[] = [];
46+
const wordLength = 10;
47+
const wordCount = 30;
48+
49+
for (let i = 0; i < wordCount; i++) {
50+
enumList.push('a'.repeat(wordLength));
51+
}
52+
53+
const shortenedList = shortenEnum(enumList);
54+
55+
expect(shortenedList?.length || 0).toBe(ACTOR_ENUM_MAX_LENGTH / wordLength);
56+
});
4257
});
4358
});

0 commit comments

Comments
 (0)