feat(llm): support remote API base (Ollama/LM Studio/LiteLLM) + docs (#24)

Co-authored-by: Ahmed Allam <ahmed39652003@gmail.com>
Co-authored-by: Ahmed Allam <49919286+0xallam@users.noreply.github.com>
This commit is contained in:
Stanislav Luchanskiy
2025-09-24 22:32:58 +03:00
committed by GitHub
parent af01294c46
commit ac6d5c6dae
4 changed files with 92 additions and 27 deletions

View File

@@ -89,12 +89,12 @@ strix --target api.your-app.com --instruction "Prioritize authentication and aut
### ⚙️ Configuration ### ⚙️ Configuration
```bash ```bash
# Required
export STRIX_LLM="openai/gpt-5" export STRIX_LLM="openai/gpt-5"
export LLM_API_KEY="your-api-key" export LLM_API_KEY="your-api-key"
# Recommended # Optional
export PERPLEXITY_API_KEY="your-api-key" export LLM_API_BASE="your-api-base-url" # if using a local model, e.g. Ollama, LMStudio
export PERPLEXITY_API_KEY="your-api-key" # for search capabilities
``` ```
[📚 View supported AI models](https://docs.litellm.ai/docs/providers) [📚 View supported AI models](https://docs.litellm.ai/docs/providers)

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "strix-agent" name = "strix-agent"
version = "0.1.17" version = "0.1.18"
description = "Open-source AI Hackers for your apps" description = "Open-source AI Hackers for your apps"
authors = ["Strix <hi@usestrix.com>"] authors = ["Strix <hi@usestrix.com>"]
readme = "README.md" readme = "README.md"

View File

@@ -48,8 +48,23 @@ def validate_environment() -> None:
if not os.getenv("STRIX_LLM"): if not os.getenv("STRIX_LLM"):
missing_required_vars.append("STRIX_LLM") missing_required_vars.append("STRIX_LLM")
has_base_url = any(
[
os.getenv("LLM_API_BASE"),
os.getenv("OPENAI_API_BASE"),
os.getenv("LITELLM_BASE_URL"),
os.getenv("OLLAMA_API_BASE"),
]
)
if not os.getenv("LLM_API_KEY"): if not os.getenv("LLM_API_KEY"):
if not has_base_url:
missing_required_vars.append("LLM_API_KEY") missing_required_vars.append("LLM_API_KEY")
else:
missing_optional_vars.append("LLM_API_KEY")
if not has_base_url:
missing_optional_vars.append("LLM_API_BASE")
if not os.getenv("PERPLEXITY_API_KEY"): if not os.getenv("PERPLEXITY_API_KEY"):
missing_optional_vars.append("PERPLEXITY_API_KEY") missing_optional_vars.append("PERPLEXITY_API_KEY")
@@ -65,26 +80,43 @@ def validate_environment() -> None:
error_text.append(" is not set\n", style="white") error_text.append(" is not set\n", style="white")
if missing_optional_vars: if missing_optional_vars:
error_text.append( error_text.append("\nOptional environment variables:\n", style="dim white")
"\nOptional (but recommended) environment variables:\n", style="dim white"
)
for var in missing_optional_vars: for var in missing_optional_vars:
error_text.append(f"{var}", style="dim yellow") error_text.append(f"{var}", style="dim yellow")
error_text.append(" is not set\n", style="dim white") error_text.append(" is not set\n", style="dim white")
error_text.append("\nRequired environment variables:\n", style="white") error_text.append("\nRequired environment variables:\n", style="white")
for var in missing_required_vars:
if var == "STRIX_LLM":
error_text.append("", style="white") error_text.append("", style="white")
error_text.append("STRIX_LLM", style="bold cyan") error_text.append("STRIX_LLM", style="bold cyan")
error_text.append( error_text.append(
" - Model name to use with litellm (e.g., 'openai/gpt-5')\n", " - Model name to use with litellm (e.g., 'openai/gpt-5')\n",
style="white", style="white",
) )
elif var == "LLM_API_KEY":
error_text.append("", style="white") error_text.append("", style="white")
error_text.append("LLM_API_KEY", style="bold cyan") error_text.append("LLM_API_KEY", style="bold cyan")
error_text.append(" - API key for the LLM provider\n", style="white") error_text.append(
" - API key for the LLM provider (required for cloud providers)\n",
style="white",
)
if missing_optional_vars: if missing_optional_vars:
error_text.append("\nOptional environment variables:\n", style="white") error_text.append("\nOptional environment variables:\n", style="white")
for var in missing_optional_vars:
if var == "LLM_API_KEY":
error_text.append("", style="white")
error_text.append("LLM_API_KEY", style="bold cyan")
error_text.append(" - API key for the LLM provider\n", style="white")
elif var == "LLM_API_BASE":
error_text.append("", style="white")
error_text.append("LLM_API_BASE", style="bold cyan")
error_text.append(
" - Custom API base URL if using local models (e.g., Ollama, LMStudio)\n",
style="white",
)
elif var == "PERPLEXITY_API_KEY":
error_text.append("", style="white") error_text.append("", style="white")
error_text.append("PERPLEXITY_API_KEY", style="bold cyan") error_text.append("PERPLEXITY_API_KEY", style="bold cyan")
error_text.append( error_text.append(
@@ -94,10 +126,25 @@ def validate_environment() -> None:
error_text.append("\nExample setup:\n", style="white") error_text.append("\nExample setup:\n", style="white")
error_text.append("export STRIX_LLM='openai/gpt-5'\n", style="dim white") error_text.append("export STRIX_LLM='openai/gpt-5'\n", style="dim white")
if "LLM_API_KEY" in missing_required_vars:
error_text.append("export LLM_API_KEY='your-api-key-here'\n", style="dim white") error_text.append("export LLM_API_KEY='your-api-key-here'\n", style="dim white")
if missing_optional_vars: if missing_optional_vars:
for var in missing_optional_vars:
if var == "LLM_API_KEY":
error_text.append( error_text.append(
"export PERPLEXITY_API_KEY='your-perplexity-key-here'", style="dim white" "export LLM_API_KEY='your-api-key-here' # optional with local models\n",
style="dim white",
)
elif var == "LLM_API_BASE":
error_text.append(
"export LLM_API_BASE='http://localhost:11434' # needed for local models only\n",
style="dim white",
)
elif var == "PERPLEXITY_API_KEY":
error_text.append(
"export PERPLEXITY_API_KEY='your-perplexity-key-here'\n", style="dim white"
) )
panel = Panel( panel = Panel(
@@ -152,6 +199,15 @@ async def warm_up_llm() -> None:
if api_key: if api_key:
litellm.api_key = api_key litellm.api_key = api_key
api_base = (
os.getenv("LLM_API_BASE")
or os.getenv("OPENAI_API_BASE")
or os.getenv("LITELLM_BASE_URL")
or os.getenv("OLLAMA_API_BASE")
)
if api_base:
litellm.api_base = api_base
test_messages = [ test_messages = [
{"role": "system", "content": "You are a helpful assistant."}, {"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Reply with just 'OK'."}, {"role": "user", "content": "Reply with just 'OK'."},

View File

@@ -28,6 +28,15 @@ api_key = os.getenv("LLM_API_KEY")
if api_key: if api_key:
litellm.api_key = api_key litellm.api_key = api_key
api_base = (
os.getenv("LLM_API_BASE")
or os.getenv("OPENAI_API_BASE")
or os.getenv("LITELLM_BASE_URL")
or os.getenv("OLLAMA_API_BASE")
)
if api_base:
litellm.api_base = api_base
class LLMRequestFailedError(Exception): class LLMRequestFailedError(Exception):
def __init__(self, message: str, details: str | None = None): def __init__(self, message: str, details: str | None = None):