Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ LLM__OPENAI_API_KEY=
LLM__GROQ_API_KEY=
LLM__GEMINI_API_KEY=
LLM__OPENROUTER_API_KEY=
LLM__DEEPSEEK_API_KEY=
LLM__GITHUB_TOKEN=

# LLM preferences
LLM__PREFERRED_PROVIDER=openai
# JSON array of fallback provider order
LLM__FALLBACK_PROVIDERS=["groq", "openrouter", "gemini"]
LLM__FALLBACK_PROVIDERS=["groq", "openrouter", "deepseek", "gemini"]
LLM__DEFAULT_MODEL=gpt-4o
LLM__TEMPERATURE=0.7
LLM__MAX_TOKENS=4096
Expand Down
1 change: 1 addition & 0 deletions backend/app/api/v1/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ async def list_llm_providers() -> list[LLMProviderStatus]:
("groq", llm.groq_api_key, "llama-3.1-70b-versatile"),
("gemini", llm.gemini_api_key, "gemini-pro"),
("openrouter", llm.openrouter_api_key, llm.default_model),
("deepseek", llm.deepseek_api_key, "deepseek-chat"),
("github", llm.github_token, "gpt-4o"),
]

Expand Down
1 change: 1 addition & 0 deletions backend/app/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class LLMSettings(BaseSettings):
groq_api_key: SecretStr = SecretStr("")
gemini_api_key: SecretStr = SecretStr("")
openrouter_api_key: SecretStr = SecretStr("")
deepseek_api_key: SecretStr = SecretStr("")
github_token: SecretStr = SecretStr("")
preferred_provider: str = "openai"
fallback_providers: list[str] = ["groq", "openrouter"]
Expand Down
79 changes: 70 additions & 9 deletions backend/app/core/automation/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,71 @@ async def run(self) -> Any:
await self._browser.close()

def _get_default_llm(self) -> Any:
"""Create a default LangChain ChatOpenAI instance.
"""Create a LangChain-compatible LLM based on the preferred provider.

Falls back through: OpenAI → DeepSeek (OpenAI-compatible) → Groq.
Comment on lines +120 to +122

Returns:
ChatOpenAI configured from application settings.
LangChain chat model instance configured from application settings.

Raises:
BrowserError: If langchain-openai is not installed.
BrowserError: If no suitable LLM package is installed.
"""
settings = get_settings()
llm_config = settings.llm
preferred = llm_config.preferred_provider
Comment on lines +130 to +132

openai_key = llm_config.openai_api_key.get_secret_value()
deepseek_key = llm_config.deepseek_api_key.get_secret_value()
groq_key = llm_config.groq_api_key.get_secret_value()

# Try preferred provider in order
if preferred == "deepseek" and deepseek_key:
return self._build_chat_openai(
model=llm_config.default_model if "deepseek" in llm_config.default_model.lower() else "deepseek-chat",
api_key=deepseek_key,
base_url="https://api.deepseek.com/v1",
)

if preferred != "deepseek" and openai_key:
return self._build_chat_openai(
model=llm_config.default_model,
api_key=openai_key,
)

# Fallback: DeepSeek via OpenAI-compatible
if deepseek_key:
return self._build_chat_openai(
model="deepseek-chat",
api_key=deepseek_key,
base_url="https://api.deepseek.com/v1",
)

# Fallback: Groq
if groq_key:
try:
from langchain_groq import ChatGroq
except ImportError as exc:
raise BrowserError(
"langchain-groq not installed. Install with: pip install langchain-groq"
) from exc
return ChatGroq(
model="llama-3.1-70b-versatile",
api_key=groq_key,
temperature=llm_config.temperature,
)

raise BrowserError(
"No LLM provider configured. Set at least OPENAI_API_KEY, "
"DEEPSEEK_API_KEY, or GROQ_API_KEY in your .env file."
)

def _build_chat_openai(
self, model: str, api_key: str, base_url: str | None = None,
) -> Any:
"""Create a ChatOpenAI instance, optionally with a custom base URL.

This works for both OpenAI and OpenAI-compatible providers like DeepSeek.
"""
try:
from langchain_openai import ChatOpenAI
Expand All @@ -133,12 +191,15 @@ def _get_default_llm(self) -> Any:
"Install with: pip install langchain-openai"
) from exc

settings = get_settings()
return ChatOpenAI(
model=settings.llm.default_model,
api_key=settings.llm.openai_api_key.get_secret_value(),
temperature=settings.llm.temperature,
)
kwargs: dict[str, Any] = {
"model": model,
"api_key": api_key,
"temperature": get_settings().llm.temperature,
}
Comment on lines +212 to +216
if base_url:
kwargs["base_url"] = base_url

return ChatOpenAI(**kwargs)

async def close(self) -> None:
"""Close the browser session and release resources."""
Expand Down
1 change: 1 addition & 0 deletions backend/app/core/llm/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def _configure_api_keys(self) -> None:
"groq_api_key": self._llm.groq_api_key.get_secret_value(),
"gemini_api_key": self._llm.gemini_api_key.get_secret_value(),
"openrouter_api_key": self._llm.openrouter_api_key.get_secret_value(),
"deepseek_api_key": self._llm.deepseek_api_key.get_secret_value(),
}
for attr, value in key_map.items():
if value:
Expand Down