Skip to content

Commit 4774bfb

Browse files
committed
in-prog async browser creation and form fill
1 parent 989eb99 commit 4774bfb

File tree

3 files changed

+30
-29
lines changed

3 files changed

+30
-29
lines changed

examples/integrations/cartesia/__init__.py

Whitespace-only changes.

examples/integrations/cartesia/form_filling_node.py

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,13 @@ def __init__(
108108
# Form state
109109
self.form_fields: Dict[str, Any] = {}
110110
self.collected_data: Dict[str, str] = {}
111-
self.questions: list[FormQuestion] = []
111+
# Pre-initialize questions so conversation can start immediately
112+
self.questions: list[FormQuestion] = self._create_questions_from_analysis({})
112113
self.current_question_index = 0
113114

114115
# Browser initialization
115116
self.browser_init_task = None
117+
self.browser_initializing = False
116118

117119
# Enhanced prompt for form filling
118120
enhanced_prompt = system_prompt + """
@@ -137,6 +139,12 @@ def __init__(
137139

138140
async def _initialize_browser(self):
139141
"""Initialize browser and extract form fields"""
142+
# Prevent multiple initializations
143+
if self.browser_initializing or self.stagehand_filler:
144+
logger.info("🔒 Browser already initializing or initialized, skipping")
145+
return
146+
147+
self.browser_initializing = True
140148
try:
141149
logger.info("🌐 Initializing browser and analyzing form")
142150
self.stagehand_filler = StagehandFormFiller(
@@ -159,14 +167,15 @@ async def _initialize_browser(self):
159167
except Exception as e:
160168
logger.warning(f"Could not extract form fields: {e}")
161169

162-
# Always create questions - don't depend on form extraction
163-
self.questions = self._create_questions_from_analysis({})
164-
165-
logger.info(f"✅ Browser ready with {len(self.questions)} questions")
170+
# Questions already initialized in __init__, no need to recreate
171+
logger.info(f"✅ Browser ready, form can now be filled")
166172

167173
except Exception as e:
168174
logger.error(f"❌ Failed to initialize browser: {e}")
175+
self.browser_initializing = False
169176
raise
177+
finally:
178+
self.browser_initializing = False
170179

171180
def _create_questions_from_analysis(self, form_analysis: Dict[str, Any]) -> list[FormQuestion]:
172181
"""Create questions based on form analysis"""
@@ -241,19 +250,12 @@ def _create_questions_from_analysis(self, form_analysis: Dict[str, Any]) -> list
241250
async def _fill_form_field_async(self, field_name: str, value: str):
242251
"""Fill a form field asynchronously in background (non-blocking)"""
243252
try:
244-
await self._fill_form_field(field_name, value)
245-
except Exception as e:
246-
logger.error(f"❌ Background form filling error for {field_name}: {e}")
247-
248-
async def _fill_form_field(self, field_name: str, value: str):
249-
"""Fill a form field in the browser in real-time"""
250-
if not self.stagehand_filler:
251-
logger.warning("⚠️ Browser not initialized yet")
252-
return
253-
254-
try:
253+
# Wait for browser initialization if needed
254+
if self.browser_init_task:
255+
logger.info(f"⏳ Waiting for browser to initialize before filling {field_name}")
256+
await self.browser_init_task
257+
255258
logger.info(f"🖊️ Filling field '{field_name}' with: {value} in background")
256-
257259
# Use StagehandFormFiller's fill_field method which handles the mapping
258260
success = await self.stagehand_filler.fill_field(field_name, value)
259261

@@ -264,10 +266,15 @@ async def _fill_form_field(self, field_name: str, value: str):
264266

265267
except Exception as e:
266268
logger.error(f"Error filling field {field_name}: {e}")
267-
raise # Re-raise so background task can catch it
268-
269+
raise # Re-raise so background task can catch it
270+
269271
async def _submit_form(self):
270272
"""Submit the completed form"""
273+
# Wait for browser initialization if needed
274+
if self.browser_init_task and not self.stagehand_filler:
275+
logger.info("⏳ Waiting for browser to initialize before submitting form")
276+
await self.browser_init_task
277+
271278
if not self.stagehand_filler:
272279
return False
273280

@@ -309,12 +316,10 @@ async def process_context(
309316
AgentResponse: Text responses to the user
310317
EndCall: Call termination when form is complete
311318
"""
312-
# Initialize browser on first call
313-
if not self.browser_init_task:
319+
# Initialize browser on first call (non-blocking)
320+
if not self.browser_init_task and not self.stagehand_filler:
314321
self.browser_init_task = asyncio.create_task(self._initialize_browser())
315-
# Wait for initialization to complete
316-
await self.browser_init_task
317-
logger.info(f"📝 Initialization complete. Questions loaded: {len(self.questions)}")
322+
logger.info("🚀 Browser initialization started in background")
318323

319324
# Get current question after initialization
320325
current_question = self.get_current_question()
@@ -388,7 +393,6 @@ async def process_context(
388393
Then acknowledge their answer naturally.
389394
"""
390395

391-
# Enhanced config
392396
enhanced_config = gemini_types.GenerateContentConfig(
393397
system_instruction=self.generation_config.system_instruction + question_context,
394398
temperature=self.temperature,
@@ -427,13 +431,10 @@ async def process_context(
427431

428432
# Store data first
429433
self.collected_data[field_name] = value
430-
431434
# Fill the form field asynchronously in background (non-blocking)
432435
asyncio.create_task(self._fill_form_field_async(field_name, value))
433-
434436
# Log the collected data
435437
logger.info(f"📊 Collected: {field_name}={value}")
436-
437438
# Move to next question immediately (don't wait for form filling)
438439
self.current_question_index += 1
439440
field_recorded = True

examples/integrations/cartesia/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ async def handle_new_call(system: VoiceAgentSystem, call_request: CallRequest):
3333
system_prompt=SYSTEM_PROMPT,
3434
gemini_client=gemini_client,
3535
form_url=FORM_URL,
36-
headless=True, # Run browser in background for production
36+
headless=False
3737
)
3838

3939
# Set up bridge for event handling

0 commit comments

Comments
 (0)