11import { GraphNodeId , RunRecord } from "../../types.js" ;
2- import { buildCollectSlashCommand } from "./naturalDeterministic.js" ;
2+ import {
3+ buildCollectSlashCommand ,
4+ extractCollectRequestFromNatural ,
5+ resolveNodeAlias
6+ } from "./naturalDeterministic.js" ;
37import { CollectCommandRequest } from "./collectOptions.js" ;
48
59export interface NaturalActionTextClient {
@@ -8,6 +12,7 @@ export interface NaturalActionTextClient {
812 sandboxMode ?: string ;
913 approvalPolicy ?: string ;
1014 systemPrompt ?: string ;
15+ reasoningEffort ?: string ;
1116 abortSignal ?: AbortSignal ;
1217 } ) : Promise < string > ;
1318}
@@ -62,7 +67,7 @@ interface StructuredAction {
6267 title ?: string ;
6368}
6469
65- const ACTION_EXTRACTION_TIMEOUT_MS = 30000 ;
70+ const ACTION_EXTRACTION_TIMEOUT_MS = 12000 ;
6671
6772const ACTION_HINT_PATTERNS = [
6873 / (?: c o l l e c t | g a t h e r | f e t c h | s e a r c h | l o o k u p | f i n d | r e s e a r c h | i n v e s t i g a t e | a n a l y [ s z ] e | c l e a r | r e m o v e | d e l e t e | j u m p | g o \s + t o | m o v e \s + t o | r e n a m e | c h a n g e ) / iu,
@@ -104,6 +109,11 @@ export function looksLikeStructuredActionRequest(text: string): boolean {
104109export async function extractStructuredActionPlan (
105110 ctx : StructuredNaturalActionContext
106111) : Promise < StructuredNaturalActionPlan | undefined > {
112+ const fastPlan = extractFastStructuredActionPlan ( ctx . input , ctx . runs , ctx . activeRunId ) ;
113+ if ( fastPlan ) {
114+ return fastPlan ;
115+ }
116+
107117 const activeRun = resolveActiveRun ( ctx . runs , ctx . activeRunId ) ;
108118 const prompt = buildStructuredActionPrompt ( ctx . input , ctx . runs , activeRun ?. id ) ;
109119 ctx . onProgress ?.( "Extracting structured action intent..." ) ;
@@ -144,6 +154,174 @@ export async function extractStructuredActionPlan(
144154 } ;
145155}
146156
157+ function extractFastStructuredActionPlan (
158+ input : string ,
159+ runs : RunRecord [ ] ,
160+ activeRunId ?: string
161+ ) : StructuredNaturalActionPlan | undefined {
162+ const targetRun = resolveActiveRun ( runs , activeRunId ) ;
163+ const clearThenFollowUp = extractFastClearThenFollowupActions ( input , targetRun ) ;
164+ const actions : StructuredAction [ ] =
165+ clearThenFollowUp ??
166+ ( ( ) => {
167+ const analyzeAction = extractFastAnalyzeAction ( input ) ;
168+ if ( analyzeAction ) {
169+ return [ analyzeAction ] ;
170+ }
171+ const collectRequest = extractCollectRequestFromNatural ( input ) ;
172+ if ( collectRequest ) {
173+ const collectAction = convertCollectRequestToStructuredAction ( collectRequest , targetRun ) ;
174+ return collectAction ? [ collectAction ] : [ ] ;
175+ }
176+ const clearAction = extractFastClearAction ( input ) ;
177+ if ( clearAction ) {
178+ return [ clearAction ] ;
179+ }
180+ const jumpAction = extractFastJumpAction ( input ) ;
181+ return jumpAction ? [ jumpAction ] : [ ] ;
182+ } ) ( ) ;
183+
184+ if ( actions . length === 0 ) {
185+ return undefined ;
186+ }
187+ const commands = actions
188+ . map ( ( action ) => buildCommandForStructuredAction ( action , targetRun ?. id ) )
189+ . filter ( Boolean ) as string [ ] ;
190+ if ( commands . length === 0 ) {
191+ return undefined ;
192+ }
193+ return {
194+ lines : buildSummaryLines ( actions , detectLanguage ( input ) ) ,
195+ commands,
196+ displayActions : actions . map ( ( action ) => summarizeAction ( action , detectLanguage ( input ) ) ) ,
197+ targetRunId : targetRun ?. id
198+ } ;
199+ }
200+
201+ function extractFastClearThenFollowupActions (
202+ raw : string ,
203+ targetRun ?: RunRecord
204+ ) : StructuredAction [ ] | undefined {
205+ const clearAction = extractFastClearAction ( raw ) ;
206+ if ( ! clearAction ) {
207+ return undefined ;
208+ }
209+ const remainder = extractFollowupAfterClear ( raw ) ;
210+ if ( ! remainder ) {
211+ return undefined ;
212+ }
213+ const analyzeAction = extractFastAnalyzeAction ( remainder ) ;
214+ if ( analyzeAction ) {
215+ return [ clearAction , analyzeAction ] ;
216+ }
217+ const collectRequest = extractCollectRequestFromNatural ( remainder ) ;
218+ if ( collectRequest ) {
219+ const collectAction = convertCollectRequestToStructuredAction ( collectRequest , targetRun ) ;
220+ if ( collectAction ) {
221+ return [ clearAction , collectAction ] ;
222+ }
223+ }
224+ const jumpAction = extractFastJumpAction ( remainder ) ;
225+ if ( jumpAction ) {
226+ return [ clearAction , jumpAction ] ;
227+ }
228+ return undefined ;
229+ }
230+
231+ function extractFollowupAfterClear ( raw : string ) : string | undefined {
232+ const normalized = raw . trim ( ) ;
233+ const match = normalized . match (
234+ / (?: 지 우 고 | 삭 제 하 고 | 제 거 하 고 | 없 애 고 | c l e a r (?: \s + o u t ) ? | r e m o v e (?: \s + a l l ) ? | d e l e t e (?: \s + a l l ) ? ) ( [ \s \S ] + ) / iu
235+ ) ;
236+ const remainder = match ?. [ 1 ] ?. trim ( ) ;
237+ if ( ! remainder || remainder === normalized ) {
238+ return undefined ;
239+ }
240+ return remainder . replace ( / ^ (?: 다 시 | t h e n | a n d \s + t h e n ) \s + / iu, "" ) . trim ( ) || undefined ;
241+ }
242+
243+ function convertCollectRequestToStructuredAction (
244+ request : CollectCommandRequest ,
245+ targetRun ?: RunRecord
246+ ) : StructuredAction | undefined {
247+ return {
248+ type : "collect" ,
249+ query : normalizeCollectQueryReference ( request . query , targetRun ) ,
250+ limit : request . limit ,
251+ additional : request . additional ,
252+ filters : {
253+ last_years : request . filters . lastYears ,
254+ year : request . filters . year ,
255+ date_range : request . filters . dateRange ,
256+ fields : request . filters . fieldsOfStudy ,
257+ venues : request . filters . venues ,
258+ publication_types : request . filters . publicationTypes ,
259+ min_citations : request . filters . minCitationCount ,
260+ open_access : request . filters . openAccessPdf
261+ } ,
262+ sort : {
263+ field : request . sort . field ,
264+ order : request . sort . order
265+ } ,
266+ bibtex_mode : request . bibtexMode ,
267+ dry_run : request . dryRun
268+ } ;
269+ }
270+
271+ function normalizeCollectQueryReference ( query : string | undefined , targetRun ?: RunRecord ) : string | undefined {
272+ const trimmed = query ?. trim ( ) ;
273+ if ( ! trimmed ) {
274+ return trimmed ;
275+ }
276+ if ( / ^ (?: t i t l e | r u n t i t l e | 현 재 t i t l e | 현 재 제 목 | 제 목 ) $ / iu. test ( trimmed ) ) {
277+ return targetRun ?. title ?? trimmed ;
278+ }
279+ if ( / ^ (?: t o p i c | r u n t o p i c | 현 재 t o p i c | 현 재 주 제 | 주 제 ) $ / iu. test ( trimmed ) ) {
280+ return targetRun ?. topic ?? trimmed ;
281+ }
282+ return trimmed ;
283+ }
284+
285+ function extractFastAnalyzeAction ( raw : string ) : StructuredAction | undefined {
286+ const normalized = raw . trim ( ) ;
287+ const lower = normalized . toLowerCase ( ) ;
288+ const hasAnalyzeVerb = / 분 석 | a n a l y [ s z ] e / u. test ( normalized ) ;
289+ if ( ! hasAnalyzeVerb ) {
290+ return undefined ;
291+ }
292+ const topMatch =
293+ normalized . match ( / 상 위 \s * ( \d + ) \s * (?: 개 | 편 | 건 ) ? / u) ||
294+ lower . match ( / \b t o p \s + ( \d + ) \b / u) ||
295+ normalized . match ( / ( \d + ) \s * (?: 개 | 편 | 건 | p a p e r s ? ) \s * (?: 만 ) ? [ ^ . \n ] { 0 , 40 } 분 석 / u) ||
296+ normalized . match ( / 분 석 [ ^ . \n ] { 0 , 40 } ( \d + ) \s * (?: 개 | 편 | 건 | p a p e r s ? ) / u) ;
297+ const topN = toPositiveInt ( topMatch ?. [ 1 ] ) ;
298+ return topN ? { type : "analyze_papers" , top_n : topN } : undefined ;
299+ }
300+
301+ function extractFastClearAction ( raw : string ) : StructuredAction | undefined {
302+ const normalized = raw . trim ( ) ;
303+ const hasPaperWord = / 논 문 | p a p e r | p a p e r s / u. test ( normalized ) ;
304+ const hasClearVerb =
305+ / 삭 제 | 제 거 | 지 워 | 지 우 | 없 애 | c l e a r | r e m o v e | d e l e t e / u. test ( normalized ) &&
306+ / 모 두 | 전 부 | 전 체 | a l l / u. test ( normalized ) ;
307+ if ( ! hasPaperWord || ! hasClearVerb ) {
308+ return undefined ;
309+ }
310+ return { type : "clear" , node : "collect_papers" } ;
311+ }
312+
313+ function extractFastJumpAction ( raw : string ) : StructuredAction | undefined {
314+ const node = resolveNodeAlias ( raw ) ;
315+ if ( ! node ) {
316+ return undefined ;
317+ }
318+ const hasMoveVerb = / 이 동 | 점 프 | 돌 아 가 | 되 돌 아 가 | g o \s + t o | m o v e \s + t o | j u m p / u. test ( raw ) ;
319+ if ( ! hasMoveVerb ) {
320+ return undefined ;
321+ }
322+ return { type : "jump" , node, force : false } ;
323+ }
324+
147325function buildStructuredActionPrompt ( input : string , runs : RunRecord [ ] , activeRunId ?: string ) : string {
148326 const runsSnapshot = runs . slice ( 0 , 8 ) . map ( ( run ) => ( {
149327 id : run . id ,
@@ -537,6 +715,7 @@ async function runForTextWithTimeout(
537715 sandboxMode ?: string ;
538716 approvalPolicy ?: string ;
539717 systemPrompt ?: string ;
718+ reasoningEffort ?: string ;
540719 abortSignal ?: AbortSignal ;
541720 } ,
542721 timeoutMs : number
0 commit comments