Skip to content

Commit 1ef2be7

Browse files
committed
fix: generate two level json schema
1 parent ad1ae16 commit 1ef2be7

File tree

2 files changed

+118
-26
lines changed

2 files changed

+118
-26
lines changed

src/utils/input-schema-to-markdown.ts

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,71 @@ function visibleEmpty(value: string) {
44
return value === '' ? '<empty>' : value;
55
}
66

7-
export function inputSchemaToMarkdown(inputSchema: IActorInputSchema) {
8-
const requiredFields = new Set(inputSchema.required || []);
7+
function formatProperty(key: string, value: any, requiredFields: Set<string>, level = 2): string {
8+
const isRequired = requiredFields.has(key);
9+
const requiredText = isRequired ? 'required' : 'optional';
910

10-
let markdown = '# Input Schema';
11-
if (inputSchema.description) {
12-
markdown += '\n\n';
13-
markdown += inputSchema.description;
14-
}
11+
let result = `${'#'.repeat(level)} \`${key}\` ${requiredText} ${value.type}`;
1512

16-
for (const [key, value] of Object.entries(inputSchema.properties)) {
17-
const isRequired = requiredFields.has(key);
18-
const requiredText = isRequired ? 'required' : 'optional';
13+
if (value.format) {
14+
result += ` format:${value.format}`;
15+
}
1916

20-
let line = `## \`${key}\` ${requiredText} ${value.type}`;
17+
if (value.prefill !== undefined && !Array.isArray(value.prefill)) {
18+
result += ' prefill:';
19+
result += visibleEmpty(String(value.prefill));
20+
} else if (value.default !== undefined) {
21+
result += ' default:';
22+
result += visibleEmpty(String(value.default));
23+
}
2124

22-
if (value.prefill !== undefined && !Array.isArray(value.prefill)) {
23-
line += ' prefill:';
24-
line += visibleEmpty(String(value.prefill));
25-
} else if (value.default !== undefined) {
26-
line += ' default:';
27-
line += visibleEmpty(String(value.default));
25+
// Handle nested properties for objects
26+
if (value.type === 'object' && value.properties) {
27+
result += '\n';
28+
const nestedEntries = Object.entries(value.properties);
29+
for (let i = 0; i < nestedEntries.length; i++) {
30+
const [nestedKey, nestedValue] = nestedEntries[i];
31+
result += formatProperty(nestedKey, nestedValue, requiredFields, level + 1);
32+
if (i < nestedEntries.length - 1) {
33+
result += '\n';
34+
}
2835
}
36+
return result;
37+
}
2938

30-
markdown += '\n\n';
31-
markdown += line;
32-
markdown += '\n';
33-
39+
if (value.enum || value.description) {
40+
result += '\n';
3441
if (value.enum) {
3542
let enumLine = 'options: ';
3643
enumLine += value.enum.map(visibleEmpty).join(', ');
37-
markdown += enumLine;
38-
markdown += '\n';
44+
result += enumLine;
45+
result += '\n';
46+
}
47+
if (value.description) {
48+
result += value.description;
3949
}
50+
}
51+
52+
return result;
53+
}
54+
55+
export function inputSchemaToMarkdown(inputSchema: IActorInputSchema) {
56+
const requiredFields = new Set(inputSchema.required || []);
4057

41-
markdown += value.description;
58+
let markdown = '# JSON Schema';
59+
if (inputSchema.description) {
60+
markdown += '\n\n';
61+
markdown += inputSchema.description;
62+
}
63+
markdown += '\n\n'; // Add blank line after title/description
64+
65+
const properties = Object.entries(inputSchema.properties);
66+
for (let i = 0; i < properties.length; i++) {
67+
const [key, value] = properties[i];
68+
markdown += formatProperty(key, value, requiredFields);
69+
if (i < properties.length - 1) {
70+
markdown += '\n\n'; // Add blank line between properties
71+
}
4272
}
4373

4474
return markdown;

tests/unit/input-schema-to-markdown.test.ts

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ describe('inputSchemaToMarkdown', () => {
4343

4444
const result = inputSchemaToMarkdown(schema);
4545
expect(result).toMatchInlineSnapshot(`
46-
"# Input Schema
46+
"# JSON Schema
4747
4848
This scraper will get post and page details from Facebook pages of your choice. To try it out, just paste a Facebook Page URL and click ▷ Start. If you need any guidance, just <a href='https://blog.apify.com/scrape-facebook-posts-data/' target='_blank' rel='noopener'>follow this tutorial</a>.
4949
@@ -176,7 +176,7 @@ describe('inputSchemaToMarkdown', () => {
176176

177177
const result = inputSchemaToMarkdown(schema);
178178
expect(result).toMatchInlineSnapshot(`
179-
"# Input Schema
179+
"# JSON Schema
180180
181181
To extract contact details from Google places, simply enter 🔍 <b>Search term</b>, add 📍 <b>Location</b>, and 💯 <b>Number of places</b> to extract. Section 🎯 <b>Filters</b> contains various extra features, filters, and sorting options. <br><br> Sections <b>with asterisk*</b> are just alternative ways to start the input (📡 Geolocation parameters, 🛰 Polygons, 🔗 URLs). They can be combined with any of the features and sorting options from the <b>Filters</b> section.
182182
@@ -234,4 +234,66 @@ describe('inputSchemaToMarkdown', () => {
234234
Max 300 results per search URL. Valid format for URLs contains <code>google.com/maps/</code>. This feature also supports uncommon URL formats such as: <code>google.com?cid=***</code>, <code>goo.gl/maps</code>, and custom place list URL."
235235
`);
236236
});
237+
238+
it('should format schema for rag web browser results', () => {
239+
const schema = {
240+
type: 'object',
241+
properties: {
242+
crawl: {
243+
type: 'object',
244+
properties: {
245+
httpStatusCode: {
246+
type: 'integer',
247+
},
248+
httpStatusMessage: {
249+
type: 'string',
250+
},
251+
loadedAt: {
252+
type: 'string',
253+
format: 'date-time',
254+
},
255+
uniqueKey: {
256+
type: 'string',
257+
},
258+
requestStatus: {
259+
type: 'string',
260+
},
261+
},
262+
},
263+
searchResult: {
264+
type: 'object',
265+
},
266+
metadata: {
267+
type: 'object',
268+
},
269+
query: {
270+
type: 'string',
271+
},
272+
markdown: {
273+
type: 'string',
274+
format: 'style',
275+
},
276+
},
277+
};
278+
279+
const result = inputSchemaToMarkdown(schema);
280+
expect(result).toMatchInlineSnapshot(`
281+
"# JSON Schema
282+
283+
## \`crawl\` optional object
284+
### \`httpStatusCode\` optional integer
285+
### \`httpStatusMessage\` optional string
286+
### \`loadedAt\` optional string format:date-time
287+
### \`uniqueKey\` optional string
288+
### \`requestStatus\` optional string
289+
290+
## \`searchResult\` optional object
291+
292+
## \`metadata\` optional object
293+
294+
## \`query\` optional string
295+
296+
## \`markdown\` optional string format:style"
297+
`);
298+
});
237299
});

0 commit comments

Comments
 (0)