Skip to content

Commit 956c97b

Browse files
authored
Merge pull request #219 from PredicateSystems/fix_image
fix advanced search
2 parents 3917d99 + 112af7d commit 956c97b

6 files changed

Lines changed: 1552 additions & 99 deletions

File tree

src/agents/planner-executor/extraction-keywords.ts

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -282,12 +282,62 @@ export function isTextExtractionTask(task: string): boolean {
282282
* @param extractQuery - What to extract
283283
* @returns Tuple of [systemPrompt, userPrompt]
284284
*/
285+
function looksLikeJson(text: string): boolean {
286+
let trimmed = text.trim();
287+
const fenceMatch = trimmed.match(/^`{1,4}\s*\n?([\s\S]*?)(?:\n?`{1,4}\s*)?$/);
288+
if (fenceMatch) {
289+
trimmed = fenceMatch[1].trim();
290+
}
291+
return (
292+
trimmed.startsWith('{') ||
293+
trimmed.startsWith('[') ||
294+
trimmed.startsWith('callback(') ||
295+
trimmed.startsWith('jsonp(')
296+
);
297+
}
298+
299+
function stripJsonp(text: string): string {
300+
let trimmed = text.trim();
301+
const fenceMatch = trimmed.match(/^`{1,4}\s*\n?([\s\S]*?)(?:\n?`{1,4}\s*)?$/);
302+
if (fenceMatch) {
303+
trimmed = fenceMatch[1].trim();
304+
}
305+
const m = trimmed.match(/^(?:callback|jsonp)\s*\(\s*([\s\S]*)\s*\)\s*;?\s*$/);
306+
return m ? m[1].trim() : trimmed;
307+
}
308+
285309
export function buildExtractionPrompt(pageContent: string, extractQuery: string): [string, string] {
286-
// NOTE: /no_think MUST be at the START of user message for Qwen3 models.
287-
// Without it, Qwen3 puts the answer in <think/> tags and content is empty.
288-
const system = `You extract specific text from page content. Return only the extracted text. Do NOT output any thinking, reasoning, or explanation.`;
310+
const isJson = looksLikeJson(pageContent);
311+
const contentForPrompt = isJson ? stripJsonp(pageContent) : pageContent;
312+
313+
let system: string;
314+
let user: string;
315+
316+
if (isJson) {
317+
system = `You extract data from JSON content. Return ONLY the extracted data as readable text. Do NOT output any thinking, reasoning, or explanation.`;
289318

290-
const user = `/no_think
319+
user = `/no_think
320+
You are a data extraction assistant. The page content below is JSON data from a search API response. Parse the JSON and extract the specific information requested.
321+
322+
JSON CONTENT:
323+
${contentForPrompt}
324+
325+
EXTRACTION REQUEST:
326+
${extractQuery}
327+
328+
INSTRUCTIONS:
329+
1. Parse the JSON structure carefully
330+
2. Look for the requested fields in the JSON objects (e.g., in "docs", "results", "items", "response.docs", or similar arrays)
331+
3. If the requested fields exist, extract and format them as readable text
332+
4. If the request asks for first/top/last N items, return exactly N matching items when available and do not include extra rows
333+
5. If the JSON does NOT contain the requested fields, list what fields ARE available and return "NOT_FOUND: available fields: <field list>"
334+
6. Return ONLY the extracted text or the NOT_FOUND message
335+
336+
EXTRACTED TEXT:`;
337+
} else {
338+
system = `You extract specific text from page content. Return only the extracted text. Do NOT output any thinking, reasoning, or explanation.`;
339+
340+
user = `/no_think
291341
You are a text extraction assistant. Given the page content below, extract the specific information requested.
292342
293343
PAGE CONTENT:
@@ -299,10 +349,12 @@ ${extractQuery}
299349
INSTRUCTIONS:
300350
1. Read the content carefully
301351
2. Find and extract ONLY the specific information requested
302-
3. Return ONLY the extracted text, nothing else
303-
4. If the information is not found, return "NOT_FOUND"
352+
3. If the request asks for first/top/last N items, return exactly N matching items when available and do not include extra rows
353+
4. Return ONLY the extracted text, nothing else
354+
5. If the information is not found, return "NOT_FOUND"
304355
305356
EXTRACTED TEXT:`;
357+
}
306358

307359
return [system, user];
308360
}
@@ -351,6 +403,8 @@ If the task asks to COUNT items (e.g., "how many listings", "number of results",
351403
- Set "countTarget" to describe what to count (e.g., "listings", "products", "articles")
352404
- The system will scroll through the entire page and sum up counts
353405
- Do NOT use EXTRACT for counting tasks — EXTRACT only sees the current viewport
406+
- Do NOT use SCROLL_AND_COUNT for "first N", "top N", "latest N", or "oldest N" requests.
407+
Those ask for a list of records, not a total count. Use EXTRACT for those.
354408
355409
Example - count all listings:
356410
Goal: "note how many listings are available"

src/agents/planner-executor/plan-models.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export const ActionType = z.enum([
4444
'CLICK',
4545
'TYPE',
4646
'TYPE_AND_SUBMIT',
47+
'FILL_FORM',
4748
'SCROLL',
4849
'SCROLL_AND_COUNT',
4950
'PRESS',
@@ -65,6 +66,8 @@ export interface PlanStep {
6566
target?: string;
6667
intent?: string;
6768
input?: string;
69+
fields?: Array<{ label: string; value: string }>;
70+
submitText?: string;
6871
verify: PredicateSpec[];
6972
required: boolean;
7073
stopIfTrue: boolean;
@@ -91,14 +94,19 @@ export const PlanStepSchema = z.lazy(() =>
9194
id: z.number().optional().describe('Step ID (1-indexed, contiguous)'),
9295
goal: z.string().optional().describe('Human-readable goal for this step'),
9396
action: ActionType.describe(
94-
'Action type: NAVIGATE, CLICK, TYPE, TYPE_AND_SUBMIT, SCROLL, SCROLL_AND_COUNT, PRESS, WAIT, EXTRACT, STUCK, DONE'
97+
'Action type: NAVIGATE, CLICK, TYPE, TYPE_AND_SUBMIT, FILL_FORM, SCROLL, SCROLL_AND_COUNT, PRESS, WAIT, EXTRACT, STUCK, DONE'
9598
),
9699
target: z
97100
.union([z.string(), z.record(z.string(), z.unknown())])
98101
.optional()
99102
.describe('URL for NAVIGATE action'),
100103
intent: z.string().optional().describe('Intent hint for CLICK action'),
101104
input: z.string().optional().describe('Text for TYPE_AND_SUBMIT action'),
105+
fields: z
106+
.array(z.object({ label: z.string(), value: z.string() }))
107+
.optional()
108+
.describe('Fields for FILL_FORM action'),
109+
submitText: z.string().optional().describe('Submit button text for FILL_FORM action'),
102110
verify: z.array(PredicateSpecSchema).default([]).describe('Verification predicates'),
103111
required: z.boolean().default(true).describe('If True, step failure triggers replan'),
104112
stopIfTrue: z

0 commit comments

Comments
 (0)