diff --git a/src/const.ts b/src/const.ts index a797b90b..346c274a 100644 --- a/src/const.ts +++ b/src/const.ts @@ -9,12 +9,12 @@ export const ACTOR_MAX_MEMORY_MBYTES = 4_096; // If the Actor requires 8GB of me // Tool output /** - * Usual tool output limit is 25k tokens, let's use 20k - * just in case where 1 token =~ 4 characters thus 80k chars. + * Usual tool output limit is 25k tokens where 1 token =~ 4 characters + * thus 50k chars so we have some buffer becase there was some issue with Claude code Actor call output token count. * This is primarily used for Actor tool call output, but we can then * reuse this in other tools as well. */ -export const TOOL_MAX_OUTPUT_CHARS = 80000; +export const TOOL_MAX_OUTPUT_CHARS = 50000; // MCP Server export const SERVER_NAME = 'apify-mcp-server'; diff --git a/src/utils/actor.ts b/src/utils/actor.ts index e9645a9d..e6eefd63 100644 --- a/src/utils/actor.ts +++ b/src/utils/actor.ts @@ -75,7 +75,7 @@ export function ensureOutputWithinCharLimit(items: DatasetItem[], importantField * If important fields are defined, use only those fields for that fallback step. */ let sourceItems = items; - // Try only the important fields + // Try keeping only the important fields if (importantFields.length > 0) { const importantItems = items.map((item) => getValuesByDotKeys(item, importantFields)); const importantItemsString = JSON.stringify(importantItems); diff --git a/tests/unit/utils.actor.test.ts b/tests/unit/utils.actor.test.ts index 390eadc3..26e9ea35 100644 --- a/tests/unit/utils.actor.test.ts +++ b/tests/unit/utils.actor.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; -import { getActorDefinitionStorageFieldNames } from '../../src/utils/actor.js'; +import { ensureOutputWithinCharLimit, getActorDefinitionStorageFieldNames } from '../../src/utils/actor.js'; describe('getActorDefinitionStorageFieldNames', () => { it('should return an array of field names from a single view (display.properties and transformation.fields)', () => { @@ -98,3 +98,40 @@ describe('getActorDefinitionStorageFieldNames', () => { expect(result.sort()).toEqual(['bar', 'baz', 'foo']); }); }); + +describe('ensureOutputWithinCharLimit', () => { + it('should return all items when limit is high', () => { + const items = [ + { id: 1, name: 'Item 1', value: 'test' }, + { id: 2, name: 'Item 2', value: 'test' }, + ]; + const charLimit = JSON.stringify(items).length; + const result = ensureOutputWithinCharLimit(items, [], charLimit); + expect(result).toEqual(items); + }); + + it('should use important fields when all items exceed limit', () => { + const items = [ + { id: 1, name: 'Item 1', description: 'Very long description that makes this item exceed the limit', extra: 'unnecessary data' }, + { id: 2, name: 'Item 2', description: 'Another long description', extra: 'more unnecessary data' }, + ]; + const importantFields = ['id', 'name']; + const charLimit = 100; // Very small limit + const result = ensureOutputWithinCharLimit(items, importantFields, charLimit); + expect(result).toEqual([ + { id: 1, name: 'Item 1' }, + { id: 2, name: 'Item 2' }, + ]); + }); + + it('should remove all items when limit is extremely small', () => { + const items = [ + { id: 1, name: 'Item 1' }, + { id: 2, name: 'Item 2' }, + ]; + const charLimit = 10; // Extremely small limit - even empty array JSON "[]" is 2 chars + const result = ensureOutputWithinCharLimit(items, [], charLimit); + expect(result).toEqual([]); + expect(JSON.stringify(result).length).toBeLessThanOrEqual(charLimit); + }); +});