fleet-python 0.2.2__py3-none-any.whl → 0.2.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fleet-python might be problematic. Click here for more details.
- examples/dsl_example.py +107 -92
- examples/json_tasks_example.py +82 -0
- examples/nova_act_example.py +18 -169
- examples/openai_example.py +83 -298
- examples/openai_simple_example.py +61 -0
- examples/quickstart.py +5 -5
- fleet/__init__.py +15 -1
- fleet/client.py +18 -3
- fleet/{manager → instance}/__init__.py +4 -1
- fleet/{manager → instance}/client.py +42 -5
- fleet/{manager → instance}/models.py +13 -0
- fleet/playwright.py +291 -0
- fleet/resources/base.py +1 -1
- fleet/resources/browser.py +6 -9
- fleet/resources/sqlite.py +3 -3
- fleet/verifiers/__init__.py +15 -3
- fleet/verifiers/code.py +132 -0
- fleet/verifiers/{database_snapshot.py → db.py} +62 -22
- fleet/verifiers/sql_differ.py +1 -1
- {fleet_python-0.2.2.dist-info → fleet_python-0.2.3.dist-info}/METADATA +3 -1
- fleet_python-0.2.3.dist-info/RECORD +31 -0
- fleet_python-0.2.2.dist-info/RECORD +0 -27
- /fleet/{manager → instance}/base.py +0 -0
- {fleet_python-0.2.2.dist-info → fleet_python-0.2.3.dist-info}/WHEEL +0 -0
- {fleet_python-0.2.2.dist-info → fleet_python-0.2.3.dist-info}/licenses/LICENSE +0 -0
- {fleet_python-0.2.2.dist-info → fleet_python-0.2.3.dist-info}/top_level.txt +0 -0
examples/dsl_example.py
CHANGED
|
@@ -1,112 +1,127 @@
|
|
|
1
|
-
|
|
1
|
+
import asyncio
|
|
2
|
+
import fleet as flt
|
|
3
|
+
from fleet.verifiers import DatabaseSnapshot, IgnoreConfig, TASK_SUCCESSFUL_SCORE
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
|
|
6
|
+
def validate_new_deal_creation(
|
|
4
7
|
before: DatabaseSnapshot,
|
|
5
8
|
after: DatabaseSnapshot,
|
|
6
9
|
transcript: str | None = None,
|
|
7
10
|
) -> int:
|
|
8
|
-
"""Validate that
|
|
9
|
-
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if
|
|
17
|
-
raise AssertionError(
|
|
18
|
-
|
|
19
|
-
raj_id = raj_user["id"]
|
|
20
|
-
sarah_kim_id = sarah_kim_user["id"]
|
|
21
|
-
|
|
22
|
-
# Verify SCRUM-555 (data pipeline bug) is assigned to Sarah Kim
|
|
23
|
-
after.table("issues").eq("id", "SCRUM-555").assert_eq("owner", sarah_kim_id)
|
|
24
|
-
|
|
25
|
-
# Verify other bugs are assigned to Raj Patel
|
|
26
|
-
other_bugs = [
|
|
27
|
-
"SCRUM-780",
|
|
28
|
-
"SCRUM-781",
|
|
29
|
-
"SCRUM-790",
|
|
30
|
-
"SCRUM-822",
|
|
31
|
-
"SCRUM-882",
|
|
32
|
-
"SCRUM-897",
|
|
33
|
-
"SCRUM-956",
|
|
34
|
-
"SCRUM-1331",
|
|
35
|
-
"SCRUM-1312",
|
|
36
|
-
"SCRUM-1210",
|
|
37
|
-
"SCRUM-1230",
|
|
38
|
-
"SCRUM-1282",
|
|
39
|
-
]
|
|
40
|
-
for bug_id in other_bugs:
|
|
41
|
-
after.table("issues").eq("id", bug_id).assert_eq("owner", raj_id)
|
|
42
|
-
|
|
43
|
-
# Verify all bugs are in sprint_3
|
|
44
|
-
all_bugs = ["SCRUM-555"] + other_bugs
|
|
45
|
-
for bug_id in all_bugs:
|
|
46
|
-
after.table("sprint_issues").eq("issue_id", bug_id).assert_eq(
|
|
47
|
-
"sprint_id", "sprint_3"
|
|
11
|
+
"""Validate that a new deal was created"""
|
|
12
|
+
|
|
13
|
+
# Find the new deal entry
|
|
14
|
+
new_deal = after.table("entries").eq("id", 32302).first()
|
|
15
|
+
if not new_deal:
|
|
16
|
+
raise AssertionError("Expected new deal with id 32302 not found")
|
|
17
|
+
|
|
18
|
+
# Verify it's a deal type
|
|
19
|
+
if new_deal["type"] != "deal":
|
|
20
|
+
raise AssertionError(
|
|
21
|
+
f"Expected entry type to be 'deal', got '{new_deal['type']}'"
|
|
48
22
|
)
|
|
49
23
|
|
|
24
|
+
# Verify the deal has a name (should be "testing" based on the diff)
|
|
25
|
+
if not new_deal["name"]:
|
|
26
|
+
raise AssertionError("Expected deal to have a name")
|
|
27
|
+
|
|
28
|
+
# Parse the properties JSON to check basic deal properties
|
|
29
|
+
import json
|
|
30
|
+
|
|
31
|
+
properties = json.loads(new_deal["properties"])
|
|
32
|
+
|
|
33
|
+
# Verify it has basic deal properties
|
|
34
|
+
if "dealstage" not in properties:
|
|
35
|
+
raise AssertionError("Expected deal to have a dealstage property")
|
|
36
|
+
|
|
37
|
+
if "deal_type" not in properties:
|
|
38
|
+
raise AssertionError("Expected deal to have a deal_type property")
|
|
39
|
+
|
|
40
|
+
if "priority" not in properties:
|
|
41
|
+
raise AssertionError("Expected deal to have a priority property")
|
|
42
|
+
|
|
50
43
|
# Configure ignore settings
|
|
51
44
|
ignore_config = IgnoreConfig(
|
|
52
|
-
tables={"
|
|
45
|
+
tables={"pageviews"},
|
|
53
46
|
table_fields={
|
|
54
|
-
"
|
|
55
|
-
"users": {"updated_at", "created_at", "rowid"},
|
|
56
|
-
"sprint_issues": {"updated_at", "created_at", "rowid"},
|
|
47
|
+
"entries": {"createdDate", "lastModifiedDate", "createdAt", "updatedAt"},
|
|
57
48
|
},
|
|
58
49
|
)
|
|
59
50
|
|
|
60
51
|
# Build expected changes
|
|
61
|
-
expected_changes
|
|
62
|
-
|
|
63
|
-
# Assignment changes
|
|
64
|
-
expected_changes.append(
|
|
52
|
+
expected_changes = [
|
|
65
53
|
{
|
|
66
|
-
"table": "
|
|
67
|
-
"pk":
|
|
68
|
-
"field":
|
|
69
|
-
"after":
|
|
54
|
+
"table": "entries",
|
|
55
|
+
"pk": 32302,
|
|
56
|
+
"field": None,
|
|
57
|
+
"after": "__added__",
|
|
70
58
|
}
|
|
71
|
-
|
|
72
|
-
for bug_id in other_bugs:
|
|
73
|
-
expected_changes.append(
|
|
74
|
-
{
|
|
75
|
-
"table": "issues",
|
|
76
|
-
"pk": bug_id,
|
|
77
|
-
"field": "owner",
|
|
78
|
-
"after": raj_id,
|
|
79
|
-
}
|
|
80
|
-
)
|
|
59
|
+
]
|
|
81
60
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
61
|
+
before.diff(after, ignore_config).expect_only(expected_changes)
|
|
62
|
+
return TASK_SUCCESSFUL_SCORE
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
async def main():
|
|
66
|
+
# Create a new instance
|
|
67
|
+
print("Creating new Hubspot instance...")
|
|
68
|
+
env = await flt.env.make("hubspot:v1.2.7")
|
|
69
|
+
print(f"New Instance: {env.instance_id}")
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
# Reset the instance
|
|
73
|
+
response = await env.reset(seed=42)
|
|
74
|
+
print(f"Reset response: {response}")
|
|
75
|
+
|
|
76
|
+
# Run verifier before insertion (should fail)
|
|
77
|
+
print("\nRunning verifier before insertion...")
|
|
78
|
+
response = await env.verify(validate_new_deal_creation)
|
|
79
|
+
print(f"Success: {response.success}")
|
|
80
|
+
print(f"Result: {response.result}")
|
|
81
|
+
print(f"Error: {response.error}")
|
|
82
|
+
print(f"Message: {response.message}")
|
|
83
|
+
|
|
84
|
+
# Insert the deal entry
|
|
85
|
+
print("\nInserting deal entry...")
|
|
86
|
+
insert_query = """
|
|
87
|
+
INSERT INTO entries (id, name, type, owner_id, createdDate, lastModifiedDate, createdAt, updatedAt, archivedAt, properties)
|
|
88
|
+
VALUES (
|
|
89
|
+
32302,
|
|
90
|
+
'testing',
|
|
91
|
+
'deal',
|
|
92
|
+
1,
|
|
93
|
+
'2025-07-10T01:32:01.089Z',
|
|
94
|
+
'2025-07-10T01:32:01.089Z',
|
|
95
|
+
'2025-07-10 01:32:01',
|
|
96
|
+
'2025-07-10 01:32:01',
|
|
97
|
+
NULL,
|
|
98
|
+
'{"amount":null,"closedate":"2025-08-09","close_date":"2025-08-09","dealstage":"appointmentscheduled","pipeline":"default","description":null,"priority":"medium","deal_stage_probability":null,"deal_type":"newbusiness","hs_createdate":"2025-07-10T01:32:01.089Z","hs_object_source":"INTEGRATION","hs_object_source_id":"14696758","hs_object_source_label":"INTEGRATION","hs_is_closed":"false","hs_is_closed_count":"0","hs_is_closed_lost":"false","hs_is_closed_won":"false","hs_is_deal_split":"false","hs_is_open_count":"1","hs_num_associated_active_deal_registrations":"0","hs_num_associated_deal_registrations":"0","hs_num_associated_deal_splits":"0","hs_num_of_associated_line_items":"0","hs_num_target_accounts":"0","hs_number_of_call_engagements":"0","hs_number_of_inbound_calls":"0","hs_number_of_outbound_calls":"0","hs_number_of_overdue_tasks":"0","num_associated_contacts":"0","num_notes":"0","hs_closed_amount":"0","hs_closed_amount_in_home_currency":"0","hs_closed_deal_close_date":"0","hs_closed_deal_create_date":"0","hs_closed_won_count":"0","hs_v2_date_entered_current_stage":"2025-07-10T01:32:01.089Z","hs_v2_time_in_current_stage":"2025-07-10T01:32:01.089Z","hs_duration":"1752111121089","hs_open_deal_create_date":"1752111121089","days_to_close":"29","hs_days_to_close_raw":"29.936098506944443"}'
|
|
107
99
|
)
|
|
100
|
+
"""
|
|
108
101
|
|
|
109
|
-
|
|
110
|
-
|
|
102
|
+
db = env.db()
|
|
103
|
+
insert_result = await db.exec(insert_query)
|
|
104
|
+
print(f"Insert result: {insert_result}")
|
|
111
105
|
|
|
112
|
-
|
|
106
|
+
# Verify the insertion
|
|
107
|
+
print("\nVerifying insertion...")
|
|
108
|
+
query_result = await db.query("SELECT * FROM entries WHERE id = 32302")
|
|
109
|
+
print(f"Query result: {query_result}")
|
|
110
|
+
|
|
111
|
+
# Run verifier after insertion (should succeed)
|
|
112
|
+
print("\nRunning verifier after insertion...")
|
|
113
|
+
response = await env.verify(validate_new_deal_creation)
|
|
114
|
+
print(f"Success: {response.success}")
|
|
115
|
+
print(f"Result: {response.result}")
|
|
116
|
+
print(f"Error: {response.error}")
|
|
117
|
+
print(f"Message: {response.message}")
|
|
118
|
+
|
|
119
|
+
finally:
|
|
120
|
+
# Delete the instance
|
|
121
|
+
print("\nDeleting instance...")
|
|
122
|
+
await env.close()
|
|
123
|
+
print("Instance deleted.")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
if __name__ == "__main__":
|
|
127
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import asyncio
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
from typing import TypedDict, List
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import fleet as flt
|
|
8
|
+
from nova_act import NovaAct, ActResult
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
MAX_STEPS = 30
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Problem(TypedDict):
|
|
15
|
+
id: str
|
|
16
|
+
problem: str
|
|
17
|
+
category: str
|
|
18
|
+
difficulty: str
|
|
19
|
+
verifier_func: str
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def extract_function_name(function_str: str) -> str | None:
|
|
23
|
+
match = re.search(r"(?:async\s+)?def\s+(\w+)\s*\(", function_str)
|
|
24
|
+
if match:
|
|
25
|
+
return match.group(1)
|
|
26
|
+
raise ValueError(f"No function name found in {function_str}")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def main():
|
|
30
|
+
parser = argparse.ArgumentParser(
|
|
31
|
+
description="Load and display Jira problems from JSON file"
|
|
32
|
+
)
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"json_file", type=str, help="Path to the JSON file containing problems"
|
|
35
|
+
)
|
|
36
|
+
args = parser.parse_args()
|
|
37
|
+
|
|
38
|
+
file_path = Path(args.json_file)
|
|
39
|
+
if not file_path.exists():
|
|
40
|
+
raise FileNotFoundError(f"Error: File '{args.json_file}' not found")
|
|
41
|
+
|
|
42
|
+
env = await flt.env.make("fira:v1.2.7")
|
|
43
|
+
print(f"New Instance: {env.urls.app}")
|
|
44
|
+
|
|
45
|
+
successes = 0
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
with open(args.json_file, "r") as f:
|
|
49
|
+
data = json.load(f)
|
|
50
|
+
problems: List[Problem] = data["problems"]
|
|
51
|
+
|
|
52
|
+
print(f"Loaded {len(problems)} problems from '{args.json_file}'")
|
|
53
|
+
|
|
54
|
+
for i, problem in enumerate(problems):
|
|
55
|
+
print(f"Solving problem {i + 1} of {len(problems)}: {problem['id']}")
|
|
56
|
+
await env.reset()
|
|
57
|
+
|
|
58
|
+
def run_nova() -> ActResult:
|
|
59
|
+
with NovaAct(starting_page=env.urls.app, headless=True) as nova:
|
|
60
|
+
return nova.act(problem["problem"], max_steps=MAX_STEPS)
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
await asyncio.to_thread(run_nova)
|
|
64
|
+
except Exception as e:
|
|
65
|
+
print(f"Error: {e}")
|
|
66
|
+
|
|
67
|
+
function_name = extract_function_name(problem["verifier_func"])
|
|
68
|
+
print(f"Verifying {function_name} ({problem['id']})...")
|
|
69
|
+
response = await env.verify_raw(problem["verifier_func"], function_name)
|
|
70
|
+
print(response)
|
|
71
|
+
if response.success:
|
|
72
|
+
successes += 1
|
|
73
|
+
|
|
74
|
+
print(f"Successes: {successes}")
|
|
75
|
+
print(f"Total: {len(problems)}")
|
|
76
|
+
print(f"Success rate: {successes / len(problems)}")
|
|
77
|
+
finally:
|
|
78
|
+
await env.close()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
if __name__ == "__main__":
|
|
82
|
+
asyncio.run(main())
|
examples/nova_act_example.py
CHANGED
|
@@ -1,180 +1,29 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Nova Act + Fleet SDK Integration Example
|
|
3
|
-
|
|
4
|
-
This example demonstrates how to use Amazon Nova Act (an AI-powered browser automation SDK)
|
|
5
|
-
with Fleet's browser instances. Nova Act can navigate websites, fill forms, and extract data
|
|
6
|
-
using natural language commands.
|
|
7
|
-
|
|
8
|
-
Requirements:
|
|
9
|
-
1. Fleet SDK: pip install fleet-python
|
|
10
|
-
2. Nova Act SDK: pip install nova-act
|
|
11
|
-
3. Playwright Chrome: playwright install chrome
|
|
12
|
-
4. Environment variables:
|
|
13
|
-
- FLEET_API_KEY: Your Fleet API key
|
|
14
|
-
- NOVA_ACT_API_KEY: Your Nova Act API key (get from https://nova.amazon.com/act)
|
|
15
|
-
|
|
16
|
-
Note: Nova Act is currently only available in the US as a research preview.
|
|
17
|
-
|
|
18
|
-
Usage:
|
|
19
|
-
export FLEET_API_KEY=your_fleet_key
|
|
20
|
-
export NOVA_ACT_API_KEY=your_nova_act_key
|
|
21
|
-
python examples/nova_act_example.py
|
|
22
|
-
|
|
23
|
-
Important: Nova Act typically creates its own browser instance. Integration with
|
|
24
|
-
Fleet's CDP endpoint may not be fully supported in the current version.
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
1
|
import asyncio
|
|
28
2
|
import fleet as flt
|
|
29
|
-
import
|
|
30
|
-
import os
|
|
31
|
-
from concurrent.futures import ThreadPoolExecutor
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def test_nova_act_sync():
|
|
35
|
-
"""Test Nova Act in a synchronous context (outside asyncio loop)."""
|
|
36
|
-
print("\n🧪 Testing Nova Act independently...")
|
|
37
|
-
try:
|
|
38
|
-
with nova_act.NovaAct(
|
|
39
|
-
headless=False,
|
|
40
|
-
starting_page="https://example.com"
|
|
41
|
-
) as nova:
|
|
42
|
-
print("✅ Nova Act initialized successfully!")
|
|
43
|
-
result = nova.act("What is the main heading on this page?")
|
|
44
|
-
print(f"Test result: {result}")
|
|
45
|
-
return True
|
|
46
|
-
except Exception as e:
|
|
47
|
-
print(f"❌ Nova Act test failed: {type(e).__name__}: {str(e)}")
|
|
48
|
-
import traceback
|
|
49
|
-
traceback.print_exc()
|
|
50
|
-
return False
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def run_nova_act_with_fleet_data(fleet_app_url):
|
|
54
|
-
"""Run Nova Act examples using Fleet's app URL."""
|
|
55
|
-
print("\n🤖 Starting Nova Act with Fleet app URL...")
|
|
56
|
-
|
|
57
|
-
try:
|
|
58
|
-
with nova_act.NovaAct(
|
|
59
|
-
headless=False,
|
|
60
|
-
starting_page=fleet_app_url,
|
|
61
|
-
cdp_endpoint_url="wss://05bd8217.fleetai.com/cdp/devtools/browser/288477c8-2a6d-4e66-b8de-29bc3033c7a2"
|
|
62
|
-
) as nova:
|
|
63
|
-
print("✅ Nova Act started successfully!")
|
|
64
|
-
run_nova_examples(nova)
|
|
65
|
-
|
|
66
|
-
except Exception as e:
|
|
67
|
-
print(f"❌ Error during Nova Act operations: {type(e).__name__}: {str(e)}")
|
|
68
|
-
import traceback
|
|
69
|
-
traceback.print_exc()
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def run_nova_examples(nova):
|
|
73
|
-
"""Run Nova Act examples in a separate function."""
|
|
74
|
-
# Example 1: Navigate and interact with a website
|
|
75
|
-
print("\n📝 Example 1: Basic navigation and interaction")
|
|
76
|
-
nova.act("Navigate to https://example.com")
|
|
77
|
-
|
|
78
|
-
# Extract page title
|
|
79
|
-
result = nova.act(
|
|
80
|
-
"What is the title of this page?",
|
|
81
|
-
schema={"type": "object", "properties": {"title": {"type": "string"}}},
|
|
82
|
-
)
|
|
83
|
-
if result.matches_schema:
|
|
84
|
-
print(f"Page title: {result.parsed_response.get('title')}")
|
|
85
|
-
|
|
86
|
-
# Example 2: More complex interaction
|
|
87
|
-
print("\n📝 Example 2: Search on a website")
|
|
88
|
-
nova.act("Navigate to https://www.python.org")
|
|
89
|
-
nova.act("Search for 'asyncio' in the search box")
|
|
90
|
-
|
|
91
|
-
# Example 3: Extract structured data
|
|
92
|
-
print("\n📝 Example 3: Extract structured information")
|
|
93
|
-
result = nova.act(
|
|
94
|
-
"Find the first 3 search results and return their titles",
|
|
95
|
-
schema={
|
|
96
|
-
"type": "object",
|
|
97
|
-
"properties": {
|
|
98
|
-
"results": {"type": "array", "items": {"type": "string"}}
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
)
|
|
102
|
-
if result.matches_schema:
|
|
103
|
-
results = result.parsed_response.get("results", [])
|
|
104
|
-
print("Search results:")
|
|
105
|
-
for i, title in enumerate(results, 1):
|
|
106
|
-
print(f" {i}. {title}")
|
|
107
|
-
|
|
108
|
-
# Example 4: Fill out a form
|
|
109
|
-
print("\n📝 Example 4: Form interaction")
|
|
110
|
-
nova.act("Navigate to https://httpbin.org/forms/post")
|
|
111
|
-
nova.act("Fill the customer name field with 'John Doe'")
|
|
112
|
-
nova.act("Select 'Medium' for the size")
|
|
113
|
-
nova.act("Check the 'Bacon' topping")
|
|
114
|
-
|
|
115
|
-
# You can also use nova_act's ability to take screenshots
|
|
116
|
-
print("\n📸 Taking screenshot...")
|
|
117
|
-
nova.act("Take a screenshot of the current page")
|
|
3
|
+
from nova_act import NovaAct, ActResult
|
|
118
4
|
|
|
119
5
|
|
|
120
6
|
async def main():
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
# Check for Nova Act API key
|
|
124
|
-
nova_api_key = os.getenv("NOVA_ACT_API_KEY")
|
|
125
|
-
if not nova_api_key:
|
|
126
|
-
print("❌ NOVA_ACT_API_KEY environment variable not set!")
|
|
127
|
-
print("Please set it with: export NOVA_ACT_API_KEY=your_api_key")
|
|
128
|
-
return
|
|
129
|
-
else:
|
|
130
|
-
print(f"✅ Nova Act API key found: {nova_api_key[:8]}...{nova_api_key[-4:]}")
|
|
131
|
-
|
|
132
|
-
# Test Nova Act outside of asyncio loop
|
|
133
|
-
# with ThreadPoolExecutor() as executor:
|
|
134
|
-
# nova_test_future = executor.submit(test_nova_act_sync)
|
|
135
|
-
# nova_works = nova_test_future.result()
|
|
136
|
-
|
|
137
|
-
# if not nova_works:
|
|
138
|
-
# print("\nNova Act is not working properly. Please check:")
|
|
139
|
-
# print("1. You have a valid NOVA_ACT_API_KEY")
|
|
140
|
-
# print("2. You have installed nova-act: pip install nova-act")
|
|
141
|
-
# print("3. You have playwright installed: playwright install chrome")
|
|
142
|
-
# return
|
|
143
|
-
|
|
144
|
-
# Initialize Fleet client
|
|
145
|
-
fleet = flt.AsyncFleet()
|
|
146
|
-
print("\n🚀 Initializing Fleet client...")
|
|
7
|
+
instance = await flt.env.make("hubspot")
|
|
8
|
+
cdp_url = await instance.browser().cdp_url()
|
|
147
9
|
|
|
148
|
-
|
|
10
|
+
loop = asyncio.get_event_loop()
|
|
149
11
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
with ThreadPoolExecutor() as executor:
|
|
162
|
-
nova_future = executor.submit(run_nova_act_with_fleet_data, instance.urls.app)
|
|
163
|
-
nova_future.result() # Wait for Nova Act to complete
|
|
12
|
+
def run_nova() -> ActResult:
|
|
13
|
+
with NovaAct(
|
|
14
|
+
starting_page=instance.urls.app,
|
|
15
|
+
cdp_endpoint_url=cdp_url,
|
|
16
|
+
preview={"playwright_actuation": True},
|
|
17
|
+
) as nova:
|
|
18
|
+
future = asyncio.run_coroutine_threadsafe(
|
|
19
|
+
instance.browser().devtools_url(), loop
|
|
20
|
+
)
|
|
21
|
+
print("Devtools URL:", future.result())
|
|
22
|
+
return nova.act("Create a deal")
|
|
164
23
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
import traceback
|
|
168
|
-
traceback.print_exc()
|
|
24
|
+
await asyncio.to_thread(run_nova)
|
|
25
|
+
await instance.close()
|
|
169
26
|
|
|
170
27
|
|
|
171
28
|
if __name__ == "__main__":
|
|
172
|
-
|
|
173
|
-
asyncio.run(main())
|
|
174
|
-
except KeyboardInterrupt:
|
|
175
|
-
print("\n\n⚠️ Script interrupted by user")
|
|
176
|
-
print("Nova Act browser may still be running in the background.")
|
|
177
|
-
except Exception as e:
|
|
178
|
-
print(f"\n❌ Unexpected error: {type(e).__name__}: {str(e)}")
|
|
179
|
-
import traceback
|
|
180
|
-
traceback.print_exc()
|
|
29
|
+
asyncio.run(main())
|