Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"origin_code": "BOS",
"destination_code": "ATL",
"depart_begin_date": "2025-11-05",
"depart_end_date": "2025-11-05",
"return_begin_date": "2025-11-08",
"return_end_date": "2025-11-08",
"passenger_adults": "1"
}
255 changes: 255 additions & 0 deletions example_routines/spirit_flight_availability_search_routine.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
{
"id": "routine_4f1c2d2a-7b9f-4f1e-b2db-6a7f6e2c8f41",
"created_at": 1762292460,
"updated_at": 1762292460,
"name": "Spirit flight availability search (v3/search)",
"description": "Automates Spirit token retrieval and flight availability search using observed endpoints and payloads (v3/search and prod-token).",
"operations": [
{
"type": "navigate",
"url": "https://www.spirit.com/book/flights"
},
{
"type": "sleep",
"timeout_seconds": 2.5
},
{
"type": "fetch",
"endpoint": {
"url": "https://www.spirit.com/api/prod-token/api/v1/token",
"description": "Issue JWT used for subsequent API calls (returns data.token).",
"method": "POST",
"headers": {
"sec-ch-ua-platform": "macOS",
"Cache-Control": "no-cache",
"Referer": "https://www.spirit.com/",
"Pragma": "no-cache",
"sec-ch-ua": "\"Chromium\";v=\"142\", \"Google Chrome\";v=\"142\", \"Not_A Brand\";v=\"99\"",
"sec-ch-ua-mobile": "?0",
"Ocp-Apim-Subscription-Key": "3b6a6994753b4efc86376552e52b8432",
"Accept": "application/json, text/plain, */*",
"Content-Type": "application/json",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36"
},
"body": {
"applicationName": "dotRezWeb"
},
"credentials": "same-origin"
},
"session_storage_key": "token_response"
},
{
"type": "fetch",
"endpoint": {
"url": "https://www.spirit.com/api/prod-availability/api/availability/v3/search",
"description": "Search flight availability by origin, destination, and dates (supports one-way or roundtrip via criteria array).",
"method": "POST",
"headers": {
"sec-ch-ua-platform": "macOS",
"Authorization": "Bearer \"{{sessionStorage:token_response.data.token}}\"",
"Cache-Control": "no-cache",
"Referer": "https://www.spirit.com/book/flights",
"Accept-Language": "en-US",
"Pragma": "no-cache",
"sec-ch-ua": "\"Chromium\";v=\"142\", \"Google Chrome\";v=\"142\", \"Not_A Brand\";v=\"99\"",
"sec-ch-ua-mobile": "?0",
"Ocp-Apim-Subscription-Key": "3b6a6994753b4efc86376552e52b8432",
"Accept": "application/json, text/plain, */*",
"Content-Type": "application/json",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36"
},
"body": {
"includeWifiAvailability": true,
"criteria": [
{
"stations": {
"originStationCodes": [
"\"{{origin_code}}\""
],
"destinationStationCodes": [
"\"{{destination_code}}\""
],
"searchDestinationMacs": false
},
"dates": {
"beginDate": "\"{{depart_begin_date}}\"",
"endDate": "\"{{depart_end_date}}\""
},
"filters": {
"filter": "Default"
}
},
{
"stations": {
"originStationCodes": [
"\"{{destination_code}}\""
],
"destinationStationCodes": [
"\"{{origin_code}}\""
],
"searchOriginMacs": false
},
"dates": {
"beginDate": "\"{{return_begin_date}}\"",
"endDate": "\"{{return_end_date}}\""
},
"filters": {
"filter": "Default"
}
}
],
"passengers": {
"types": [
{
"type": "ADT",
"count": "{{passenger_adults}}"
}
]
},
"codes": {
"currency": "USD"
},
"fareFilters": {
"loyalty": "MonetaryOnly",
"types": [],
"classControl": 1
},
"taxesAndFees": "TaxesAndFees",
"originalJourneyKeys": [],
"originalBookingRecordLocator": null,
"infantCount": 0,
"birthDates": [],
"includeBundleAvailability": true
},
"credentials": "same-origin"
},
"session_storage_key": "availability_search_response"
},
{
"type": "return",
"session_storage_key": "availability_search_response"
}
],
"incognito": true,
"parameters": [
{
"name": "origin_code",
"type": "string",
"required": true,
"description": "IATA origin station code.",
"default": null,
"examples": [
"BOS"
],
"min_length": 3,
"max_length": 3,
"min_value": null,
"max_value": null,
"pattern": "^[A-Z]{3}$",
"enum_values": null,
"format": null
},
{
"name": "destination_code",
"type": "string",
"required": true,
"description": "IATA destination station code.",
"default": null,
"examples": [
"ATL"
],
"min_length": 3,
"max_length": 3,
"min_value": null,
"max_value": null,
"pattern": "^[A-Z]{3}$",
"enum_values": null,
"format": null
},
{
"name": "depart_begin_date",
"type": "date",
"required": true,
"description": "Outbound begin date (YYYY-MM-DD).",
"default": null,
"examples": [
"2025-11-05"
],
"min_length": null,
"max_length": null,
"min_value": null,
"max_value": null,
"pattern": null,
"enum_values": null,
"format": "YYYY-MM-DD"
},
{
"name": "depart_end_date",
"type": "date",
"required": true,
"description": "Outbound end date (YYYY-MM-DD).",
"default": null,
"examples": [
"2025-11-05"
],
"min_length": null,
"max_length": null,
"min_value": null,
"max_value": null,
"pattern": null,
"enum_values": null,
"format": "YYYY-MM-DD"
},
{
"name": "return_begin_date",
"type": "date",
"required": true,
"description": "Return begin date (YYYY-MM-DD). For one-way, set same as depart and the server will ignore the second leg.",
"default": null,
"examples": [
"2025-11-08"
],
"min_length": null,
"max_length": null,
"min_value": null,
"max_value": null,
"pattern": null,
"enum_values": null,
"format": "YYYY-MM-DD"
},
{
"name": "return_end_date",
"type": "date",
"required": true,
"description": "Return end date (YYYY-MM-DD). For one-way, set same as depart and the server will ignore the second leg.",
"default": null,
"examples": [
"2025-11-08"
],
"min_length": null,
"max_length": null,
"min_value": null,
"max_value": null,
"pattern": null,
"enum_values": null,
"format": "YYYY-MM-DD"
},
{
"name": "passenger_adults",
"type": "integer",
"required": true,
"description": "Number of adult passengers (ADT).",
"default": 1,
"examples": [
1
],
"min_length": null,
"max_length": null,
"min_value": 1,
"max_value": 9,
"pattern": null,
"enum_values": null,
"format": null
}
]
}
15 changes: 7 additions & 8 deletions src/routine_discovery/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -650,14 +650,13 @@ def productionize_routine(self, routine: Routine) -> ProductionRoutine:
f"CRITICAL: PLACEHOLDERS ARE REPLACED AT RUNTIME AND MUST RESULT IN VALID JSON! "
f"EXPLANATION: Placeholders like {{{{key}}}} are replaced at runtime with actual values. The format you choose determines the resulting JSON type. "
f"For STRING values: Use \\\"{{{{key}}}}\\\" format (escaped quote + placeholder + escaped quote). "
f"This means in the JSON file you write: \\\"\\\"{{{{user_name}}}}\\\"\\\". At runtime, the \\\"{{{{user_name}}}}\\\" part gets replaced, "
f"so \\\"\\\"{{{{user_name}}}}\\\"\\\" becomes \\\"\\\"John\\\"\\\" which becomes \\\"John\\\" (valid JSON string). "
f"For NUMERIC/NULL values: Use \\\"{{{{key}}}}\\\" format (regular quote + placeholder + quote). "
f"This means in the JSON file you write: \\\"{{{{item_id}}}}\\\". At runtime, the {{{{item_id}}}} part gets replaced with the number, "
f"and the surrounding quotes are removed, so \\\"{{{{item_id}}}}\\\" with value 42 becomes just 42 (valid JSON number, not string). "
f"Example: \\\"{{{{total_price}}}}\\\" with value 29.99 → becomes 29.99 (quotes removed, valid JSON number). "
f"Example: \\\"{{{{optional_data}}}}\\\" with null → becomes null (quotes removed, valid JSON null). "
"""Placeholders will be resolved using this: param_pattern = r'(?:"|\\\\")\\{\\{([^}"]*)\\}\\}(?:"|\\\\")'"""
f"This means in the JSON file you write: \"\\\"{{{{user_name}}}}\\\"\". At runtime, the \\\"{{{{user_name}}}}\\\" part gets replaced, "
f"so \"\\\"{{{{user_name}}}}\\\"\" becomes \"John\" (valid JSON string). "
f"For NUMERIC/NULL values: Use \"{{{{key}}}}\" format (regular quote + placeholder + quote). "
f"This means in the JSON file you write: \"{{{{item_id}}}}\". At runtime, the {{{{item_id}}}} part gets replaced with the number, "
f"and the surrounding quotes are removed, so \"{{{{item_id}}}}\" with value 42 becomes just 42 (valid JSON number, not string). "
f"Example: \"{{{{total_price}}}}\" with value 29.99 → becomes 29.99 (quotes removed, valid JSON number). "
f"Example: \"{{{{optional_data}}}}\" with null → becomes null (quotes removed, valid JSON null). "
f"The resulting JSON MUST be valid and parseable after all placeholder replacements are done."
)
self._add_to_message_history("user", message)
Expand Down
Loading