-
Notifications
You must be signed in to change notification settings - Fork 1
steel+morph #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
steel+morph #18
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "title": "Morph Computer Use", | ||
| "root": false, | ||
| "pages": ["---Morph Computer Use---", "quickstart"] | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,300 @@ | ||||||||
| --- | ||||||||
| title: Quickstart | ||||||||
| sidebarTitle: Quickstart | ||||||||
| description: This guide shows how to use Morph Computer Use with Steel to agentically test code changes in full-stack applications using a live cloud browser. | ||||||||
| llm: true | ||||||||
| --- | ||||||||
|
|
||||||||
| Morph Computer Use (`morph-computer-use-v0`) is state of the art for agentically testing codegen changes in full-stack applications. It's **10x cheaper** and **250% faster** than general-purpose models like Claude Sonnet, making it ideal for automated testing workflows where you need to verify that code changes work correctly in a real browser environment. | ||||||||
|
|
||||||||
| ### Requirements | ||||||||
| :::prerequisites | ||||||||
| * **Steel API key** | ||||||||
|
|
||||||||
| * **Morph API key** - Get yours at [morphllm.com](https://morphllm.com) | ||||||||
|
|
||||||||
| * **Python 3.11+** | ||||||||
| ::: | ||||||||
|
|
||||||||
| ### Step 1: Project Setup and Install Dependencies | ||||||||
|
|
||||||||
| ```package-install python | ||||||||
| steel-sdk browser-use python-dotenv | ||||||||
| ``` | ||||||||
|
|
||||||||
|
|
||||||||
| ### Step 2: Environment Variables | ||||||||
|
|
||||||||
| Create a `.env` file with your API keys and a test task: | ||||||||
|
|
||||||||
| ```env ENV -wcn -f .env | ||||||||
| STEEL_API_KEY=your-steel-api-key-here | ||||||||
| MORPH_API_KEY=your-morph-api-key-here | ||||||||
| TASK="Test the checkout flow: add an item to cart, proceed to checkout, and verify the order total is displayed correctly" | ||||||||
| ``` | ||||||||
|
|
||||||||
|
|
||||||||
| ### Step 3: Initialize Steel & Connect via CDP | ||||||||
|
|
||||||||
| Set up Steel, load env vars, and prepare to connect Morph via browser-use. | ||||||||
|
|
||||||||
| ```python Python -wcn -f main.py | ||||||||
| import os | ||||||||
| import time | ||||||||
| import asyncio | ||||||||
| from dotenv import load_dotenv | ||||||||
| from steel import Steel | ||||||||
| from browser_use import Agent, BrowserSession | ||||||||
| from browser_use.llm import ChatOpenAI | ||||||||
|
|
||||||||
| load_dotenv() | ||||||||
|
|
||||||||
| # Replace with your own API keys | ||||||||
| STEEL_API_KEY = os.getenv("STEEL_API_KEY") or "your-steel-api-key-here" | ||||||||
| MORPH_API_KEY = os.getenv("MORPH_API_KEY") or "your-morph-api-key-here" | ||||||||
|
|
||||||||
| # Replace with your own test task | ||||||||
| TASK = os.getenv("TASK") or "Test the checkout flow: add an item to cart, proceed to checkout, and verify the order total is displayed correctly" | ||||||||
| ``` | ||||||||
|
|
||||||||
|
|
||||||||
| ### Step 4: Configure Morph Computer Use Model | ||||||||
|
|
||||||||
| Morph is OpenAI-compatible, so we use ChatOpenAI with Morph's endpoint to configure the agent for testing. | ||||||||
|
|
||||||||
| ```python Python -wcn -f main.py | ||||||||
| async def main(): | ||||||||
| print("🚀 Steel + Morph Computer Use Assistant") | ||||||||
| print("=" * 60) | ||||||||
|
|
||||||||
| if STEEL_API_KEY == "your-steel-api-key-here": | ||||||||
| print("⚠️ WARNING: Please replace 'your-steel-api-key-here' with your actual Steel API key") | ||||||||
| print(" Get your API key at: https://app.steel.dev/settings/api-keys") | ||||||||
| return | ||||||||
|
|
||||||||
| if MORPH_API_KEY == "your-morph-api-key-here": | ||||||||
| print("⚠️ WARNING: Please replace 'your-morph-api-key-here' with your actual Morph API key") | ||||||||
| print(" Get your API key at: https://morphllm.com") | ||||||||
| return | ||||||||
|
|
||||||||
| print("\nStarting Steel browser session...") | ||||||||
|
|
||||||||
| client = Steel(steel_api_key=STEEL_API_KEY) | ||||||||
|
|
||||||||
| try: | ||||||||
| session = client.sessions.create() | ||||||||
| print("✅ Steel browser session started!") | ||||||||
| print(f"View live session at: {session.session_viewer_url}") | ||||||||
|
|
||||||||
| cdp_url = f"{session.websocket_url}&apiKey={STEEL_API_KEY}" | ||||||||
|
|
||||||||
| # Configure Morph Computer Use model | ||||||||
| # Morph is OpenAI-compatible, so we use ChatOpenAI with Morph's endpoint | ||||||||
| model = ChatOpenAI( | ||||||||
| model="morph-computer-use-v0", | ||||||||
| base_url="https://api.morphllm.com/v1", | ||||||||
| api_key=MORPH_API_KEY, | ||||||||
| temperature=0.3, | ||||||||
| ) | ||||||||
|
|
||||||||
| # Create the agent with Morph model and Steel browser session | ||||||||
| agent = Agent( | ||||||||
| task=TASK, | ||||||||
| llm=model, | ||||||||
| browser_session=BrowserSession(cdp_url=cdp_url) | ||||||||
| ) | ||||||||
| ``` | ||||||||
|
|
||||||||
|
|
||||||||
| ### Step 5: Run the Test Task | ||||||||
|
|
||||||||
| Execute the test task and monitor the results. | ||||||||
|
|
||||||||
| ```python Python -wcn -f main.py | ||||||||
| start_time = time.time() | ||||||||
|
|
||||||||
| print(f"🎯 Executing test task: {TASK}") | ||||||||
| print("=" * 60) | ||||||||
|
|
||||||||
| try: | ||||||||
| result = await agent.run() | ||||||||
|
|
||||||||
| duration = f"{(time.time() - start_time):.1f}" | ||||||||
|
|
||||||||
| print("\n" + "=" * 60) | ||||||||
| print("🎉 TEST EXECUTION COMPLETED") | ||||||||
| print("=" * 60) | ||||||||
| print(f"⏱️ Duration: {duration} seconds") | ||||||||
| print(f"🎯 Task: {TASK}") | ||||||||
| if result: | ||||||||
| print(f"📋 Result:\n{result}") | ||||||||
| print("=" * 60) | ||||||||
|
|
||||||||
| except Exception as e: | ||||||||
| print(f"❌ Test execution failed: {e}") | ||||||||
| finally: | ||||||||
| if session: | ||||||||
| print("Releasing Steel session...") | ||||||||
| client.sessions.release(session.id) | ||||||||
| print(f"Session completed. View replay at {session.session_viewer_url}") | ||||||||
| print("Done!") | ||||||||
|
|
||||||||
| except Exception as e: | ||||||||
| print(f"❌ Failed to start Steel browser: {e}") | ||||||||
| print("Please check your STEEL_API_KEY and internet connection.") | ||||||||
|
|
||||||||
|
|
||||||||
| if __name__ == "__main__": | ||||||||
| asyncio.run(main()) | ||||||||
| ``` | ||||||||
|
|
||||||||
|
|
||||||||
| #### Run It | ||||||||
|
|
||||||||
| Execute your test automation: | ||||||||
|
|
||||||||
| ```bash | ||||||||
| python main.py | ||||||||
| ``` | ||||||||
|
|
||||||||
| You'll see a **session viewer URL** in your console—open it to watch the test execution live. | ||||||||
|
|
||||||||
| ### Full Example | ||||||||
|
|
||||||||
| Complete `main.py` for testing code changes agentically: | ||||||||
|
|
||||||||
| ```python Python -wc -f main.py | ||||||||
| """ | ||||||||
| Agentically test code changes in full stack apps using Morph Computer Use with Steel browsers. | ||||||||
| https://github.com/steel-dev/steel-cookbook/tree/main/examples/steel-morph-computer-use-starter | ||||||||
| """ | ||||||||
|
|
||||||||
| import os | ||||||||
| import sys | ||||||||
| import time | ||||||||
| import asyncio | ||||||||
| from dotenv import load_dotenv | ||||||||
| from steel import Steel | ||||||||
| from browser_use import Agent, BrowserSession | ||||||||
| from browser_use.llm import ChatOpenAI | ||||||||
|
|
||||||||
| load_dotenv() | ||||||||
|
|
||||||||
| # Replace with your own API keys | ||||||||
| STEEL_API_KEY = os.getenv("STEEL_API_KEY") or "your-steel-api-key-here" | ||||||||
| MORPH_API_KEY = os.getenv("MORPH_API_KEY") or "your-morph-api-key-here" | ||||||||
|
|
||||||||
| # Replace with your own test task | ||||||||
| TASK = os.getenv("TASK") or "Test the checkout flow: add an item to cart, proceed to checkout, and verify the order total is displayed correctly" | ||||||||
|
|
||||||||
| async def main(): | ||||||||
| print("🚀 Steel + Morph Computer Use Assistant") | ||||||||
| print("=" * 60) | ||||||||
|
|
||||||||
| if STEEL_API_KEY == "your-steel-api-key-here": | ||||||||
| print("⚠️ WARNING: Please replace 'your-steel-api-key-here' with your actual Steel API key") | ||||||||
| print(" Get your API key at: https://app.steel.dev/settings/api-keys") | ||||||||
| sys.exit(1) | ||||||||
|
|
||||||||
| if MORPH_API_KEY == "your-morph-api-key-here": | ||||||||
| print("⚠️ WARNING: Please replace 'your-morph-api-key-here' with your actual Morph API key") | ||||||||
| print(" Get your API key at: https://morphllm.com") | ||||||||
| sys.exit(1) | ||||||||
|
|
||||||||
| print("\nStarting Steel browser session...") | ||||||||
|
|
||||||||
| client = Steel(steel_api_key=STEEL_API_KEY) | ||||||||
|
|
||||||||
| try: | ||||||||
| session = client.sessions.create() | ||||||||
| print("✅ Steel browser session started!") | ||||||||
| print(f"View live session at: {session.session_viewer_url}") | ||||||||
|
|
||||||||
| print( | ||||||||
| f"\033[1;93mSteel Session created!\033[0m\n" | ||||||||
| f"View session at \033[1;37m{session.session_viewer_url}\033[0m\n" | ||||||||
| ) | ||||||||
|
Comment on lines
+210
to
+216
|
||||||||
|
|
||||||||
| cdp_url = f"{session.websocket_url}&apiKey={STEEL_API_KEY}" | ||||||||
|
|
||||||||
| # Configure Morph Computer Use model | ||||||||
| # Morph is OpenAI-compatible, so we use ChatOpenAI with Morph's endpoint | ||||||||
| model = ChatOpenAI( | ||||||||
| model="morph-computer-use-v0", | ||||||||
| base_url="https://api.morphllm.com/v1", | ||||||||
| api_key=MORPH_API_KEY, | ||||||||
| temperature=0.3, | ||||||||
| ) | ||||||||
|
|
||||||||
| # Create the agent with Morph model and Steel browser session | ||||||||
| agent = Agent( | ||||||||
| task=TASK, | ||||||||
| llm=model, | ||||||||
| browser_session=BrowserSession(cdp_url=cdp_url) | ||||||||
| ) | ||||||||
|
|
||||||||
| start_time = time.time() | ||||||||
|
|
||||||||
| print(f"🎯 Executing test task: {TASK}") | ||||||||
| print("=" * 60) | ||||||||
|
|
||||||||
| try: | ||||||||
| result = await agent.run() | ||||||||
|
|
||||||||
| duration = f"{(time.time() - start_time):.1f}" | ||||||||
|
|
||||||||
| print("\n" + "=" * 60) | ||||||||
| print("🎉 TEST EXECUTION COMPLETED") | ||||||||
| print("=" * 60) | ||||||||
| print(f"⏱️ Duration: {duration} seconds") | ||||||||
| print(f"🎯 Task: {TASK}") | ||||||||
| if result: | ||||||||
| print(f"📋 Result:\n{result}") | ||||||||
| print("=" * 60) | ||||||||
|
|
||||||||
| except Exception as e: | ||||||||
| print(f"❌ Test execution failed: {e}") | ||||||||
| raise | ||||||||
|
Comment on lines
+256
to
+257
|
||||||||
| finally: | ||||||||
| if session: | ||||||||
| print("Releasing Steel session...") | ||||||||
| client.sessions.release(session.id) | ||||||||
| print(f"Session completed. View replay at {session.session_viewer_url}") | ||||||||
| print("Done!") | ||||||||
|
|
||||||||
| except Exception as e: | ||||||||
| print(f"❌ Failed to start Steel browser: {e}") | ||||||||
| print("Please check your STEEL_API_KEY and internet connection.") | ||||||||
| raise | ||||||||
|
||||||||
| raise | |
| raise | |
| raise |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Step 4, the code uses
returnto exit when API keys are invalid, but in the full example (lines 194-202), the same validation usessys.exit(1). These should be consistent. The full example's approach withsys.exit(1)is more appropriate for a standalone script.