[Enhancement] async_playwright 추가 및 OAuth 로그인 흐름 개선, Planner 제외 (오히려 문제가 일어남)

This commit is contained in:
암냥 2025-06-14 18:08:33 +09:00
commit 9b797a8d21
2 changed files with 49 additions and 12 deletions

View file

@ -3,7 +3,7 @@ ANONYMIZED_TELEMETRY=false
GOOGLE_API_KEY=
# 권장 (다른 모델로 교체 가능) [다른 모델로 교체시 성능 보장 불가]
GOOGLE_MODEL=gemini-2.5-flash-preview-05-20
GOOGLE_PLANNER_MODEL=gemini-2.5-flash-preview-05-20
#GOOGLE_PLANNER_MODEL=gemini-2.5-flash-preview-05-20
# min(INITIAL_BACKOFF * (2 ** try_cnt), MAX_BACKOFF)만큼 API가 실패시 대기합니다.
INITIAL_BACKOFF=60
@ -17,6 +17,9 @@ BACKEND_URL=http://localhost:11081
# 브라우저 언어 설정
LANG=en_US
# 필수 뒤에 있는 이메일 주소는 Google 계정의 로그인 힌트로 사용됩니다.
# 이메일의 전체를 입력해주세요
GOOGLE_ID=bot.imnya.ng@gmail.com
# provider 계정 (본인이 사용하지 않는 계정 권장) (Github, apple, kakao등 다른 계정 추가 가능)
# PROVIDOR_CREDENTIALS_IN_LLM는 True로 설정시, 아래 계정 정보가 LLM에 포함되어 사용됩니다.

56
main.py
View file

@ -17,6 +17,7 @@ from browser_use import (
BrowserProfile,
Controller,
)
from patchright.async_api import async_playwright as async_patchright
from lib.is_html import is_html_url
from lib.read_txt import read_lines_between
from lib.prompt import extend_planner_system_message
@ -39,11 +40,12 @@ backend_url = os.getenv("BACKEND_URL", "http://localhost:11081")
print("🔧 환경 설정:")
print(f"🔗 Backend URL: {backend_url}")
api_key = os.getenv('GOOGLE_API_KEY')
api_key = os.getenv("GOOGLE_API_KEY")
print(f"🔑 Google API Key: {api_key[-4:] if api_key else None}")
print(f"🌐 Google Model: {os.getenv('GOOGLE_MODEL')}")
print(f"🌐 Google Planner Model: {os.getenv('GOOGLE_PLANNER_MODEL')}")
# API 쿼터 처리를 위한 콜백 핸들러
class QuotaExhaustedHandler(BaseCallbackHandler):
def on_llm_error(self, error, **kwargs):
@ -147,8 +149,8 @@ async def scan_one_url(url: str, skip_html_check: bool = False):
print("🔗 No proxy configured, using direct connection.")
# user_data_dir 설정
#user_data_path = Path("./data/user_data").resolve()
#user_data_path.mkdir(parents=True, exist_ok=True)
# user_data_path = Path("./data/user_data").resolve()
# user_data_path.mkdir(parents=True, exist_ok=True)
storage_state_path = Path("./data/storage_state.json").resolve()
storage_state_temp_path = Path("./data/storage_state_temp.json").resolve()
@ -156,7 +158,9 @@ async def scan_one_url(url: str, skip_html_check: bool = False):
if storage_state_path.exists():
if storage_state_temp_path.exists():
storage_state_temp_path.unlink()
storage_state_temp_path.write_text(storage_state_path.read_text(), encoding="utf-8")
storage_state_temp_path.write_text(
storage_state_path.read_text(), encoding="utf-8"
)
print(f"🔄 Using existing storage state: {storage_state_temp_path}")
else:
storage_state_temp_path = None
@ -164,11 +168,17 @@ async def scan_one_url(url: str, skip_html_check: bool = False):
# BrowserProfile에 모든 설정 포함
profile = BrowserProfile(
disable_security=True,
deterministic_rendering=True,
stealth=True,
headless=False,
#user_data_dir=str(user_data_path),
# user_data_dir=str(user_data_path),
user_data_dir=None,
storage_state=str(storage_state_temp_path) if storage_state_temp_path and storage_state_temp_path.exists() else None,
executable_path="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
storage_state=(
str(storage_state_temp_path)
if storage_state_temp_path and storage_state_temp_path.exists()
else None
),
viewport={"width": 1600, "height": 900},
# 프록시 설정
proxy={"server": proxy_url} if proxy_url else None,
@ -184,11 +194,23 @@ async def scan_one_url(url: str, skip_html_check: bool = False):
"--ignore-certificate-errors",
"--ignore-ssl-errors",
"--allow-running-insecure-content",
"--disable-web-security",
"--disable-features=VizDisplayCompositor",
"--disable-blink-features=AutomationControlled",
"--no-first-run",
"--no-service-autorun",
"--password-store=basic",
"--use-mock-keychain",
"--no-default-browser-check",
"--disable-extensions-file-access-check",
"--disable-extensions-http-throttling",
"--disable-component-extensions-with-background-pages",
],
)
# BrowserSession에 profile 전달
session = BrowserSession(
playwright=(await async_patchright().start()),
browser_profile=profile,
)
@ -200,9 +222,19 @@ async def scan_one_url(url: str, skip_html_check: bool = False):
agent = Agent(
browser_session=session,
initial_actions=initial_actions,
task="Navigate to the login page, and collect the OAuth provider buttons and their login URLs. Ignore Passkey.",
llm=CreateChatGoogleGenerativeAI(os.getenv("GOOGLE_MODEL") or "fallback"),
planner_llm=CreateChatGoogleGenerativeAI(os.getenv("GOOGLE_PLANNER_MODEL") or "fallback"),
task=(
"Navigate to the login page, identify all OAuth provider buttons (excluding Passkey), "
"and for each one: click the button, follow the full OAuth login flow as far as possible "
"with a real user account (without using a fake or non-existent account), and capture the "
"final redirect URL after login. Do not stop at just collecting the initial authorization URL—"
"actually perform the login step like a real user would. "
"If the OAuth buttons do not appear immediately, wait briefly to allow the page to load completely before proceeding. "
"Always log out before starting the login process, and make sure to attempt the login again from a clean state."
),
llm=CreateChatGoogleGenerativeAI(
os.getenv("GOOGLE_MODEL") or "fallback"
),
# planner_llm=CreateChatGoogleGenerativeAI(os.getenv("GOOGLE_PLANNER_MODEL") or "fallback"),
controller=controller,
extend_planner_system_message=extend_planner_system_message(),
)
@ -214,7 +246,7 @@ async def scan_one_url(url: str, skip_html_check: bool = False):
await clean_resources(agent, session)
# API 쿼터 문제인지 확인
if "ResourceExhausted" in str(e) or "429" in str(e):
wait = min(INITIAL_BACKOFF * (2 ** try_cnt), MAX_BACKOFF)
wait = min(INITIAL_BACKOFF * (2**try_cnt), MAX_BACKOFF)
print(f"⚠️ API 쿼터 에러: {e}. {wait}초 대기 후 재시도합니다...")
await asyncio.sleep(wait)
try_cnt += 1
@ -245,7 +277,9 @@ async def scan_one_url(url: str, skip_html_check: bool = False):
print("🔐 Detected OAuth Providers and URLs:")
for entry in oauth_entries:
if "<" in entry.oauth_uri or "..." in entry.oauth_uri:
print(f"⚠️ WARNING: {entry.provider} URL may be masked or incomplete:\n{entry.oauth_uri}\n")
print(
f"⚠️ WARNING: {entry.provider} URL may be masked or incomplete:\n{entry.oauth_uri}\n"
)
else:
print(f"- {entry.provider}: {entry.oauth_uri}")
print("-" * 50)