Skip to content

Commit c620946

Browse files
airingursbclaudehappy-otter
committed
feat: add openai_base_url for DeepSeek/Ollama/custom endpoint support
Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
1 parent 83fe61e commit c620946

5 files changed

Lines changed: 20 additions & 5 deletions

File tree

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ MINIFLUX_API_KEY=
99
AI_PROVIDER=claude
1010
ANTHROPIC_API_KEY=
1111
OPENAI_API_KEY=
12+
OPENAI_BASE_URL= # custom endpoint: https://api.deepseek.com/v1, etc.
1213

1314
# Telegram
1415
TELEGRAM_BOT_TOKEN=

src/muse/analyzer/ai_client.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
logger = structlog.get_logger()
1212

1313
CLAUDE_API_URL = "https://api.anthropic.com/v1/messages"
14-
OPENAI_API_URL = "https://api.openai.com/v1/chat/completions"
14+
DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1"
1515

1616

1717
class AIRequestError(Exception):
@@ -23,6 +23,7 @@ class AIClient:
2323
provider: str # "claude" | "openai"
2424
api_key: str
2525
model: str = ""
26+
base_url: str = ""
2627
max_retries: int = 3
2728
base_delay: float = 2.0
2829

@@ -109,9 +110,14 @@ async def _call_claude(
109110
async def _call_openai(
110111
self, system_prompt: str, user_prompt: str
111112
) -> tuple[str, dict]:
113+
url = (
114+
f"{self.base_url.rstrip('/')}/chat/completions"
115+
if self.base_url
116+
else f"{DEFAULT_OPENAI_BASE_URL}/chat/completions"
117+
)
112118
async with httpx.AsyncClient(timeout=60.0) as client:
113119
resp = await client.post(
114-
OPENAI_API_URL,
120+
url,
115121
headers={
116122
"Authorization": f"Bearer {self.api_key}",
117123
"Content-Type": "application/json",

src/muse/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class Settings(BaseSettings):
2020
ai_provider: str = "claude" # claude | openai
2121
anthropic_api_key: str = ""
2222
openai_api_key: str = ""
23+
openai_base_url: str = "" # custom endpoint for DeepSeek, Ollama, etc.
2324

2425
# Telegram
2526
telegram_bot_token: str

src/muse/scheduler.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ async def collect_signals_job(
9090
if settings.ai_provider == "claude"
9191
else settings.openai_api_key
9292
)
93-
ai_client = AIClient(provider=settings.ai_provider, api_key=api_key)
93+
ai_client = AIClient(
94+
provider=settings.ai_provider, api_key=api_key, base_url=settings.openai_base_url
95+
)
9496
detector = SignalDetector(
9597
ai_client=ai_client,
9698
system_prompt_path=str(PROMPTS_DIR / "signal_detection_system.txt"),
@@ -201,7 +203,9 @@ async def extract_opportunities_job(
201203
if settings.ai_provider == "claude"
202204
else settings.openai_api_key
203205
)
204-
ai_client = AIClient(provider=settings.ai_provider, api_key=api_key)
206+
ai_client = AIClient(
207+
provider=settings.ai_provider, api_key=api_key, base_url=settings.openai_base_url
208+
)
205209
extractor = OpportunityExtractor(
206210
ai_client=ai_client,
207211
system_prompt_path=str(PROMPTS_DIR / "opportunity_extraction_system.txt"),
@@ -331,7 +335,9 @@ async def generate_ideas_job(
331335
if settings.ai_provider == "claude"
332336
else settings.openai_api_key
333337
)
334-
ai_client = AIClient(provider=settings.ai_provider, api_key=api_key)
338+
ai_client = AIClient(
339+
provider=settings.ai_provider, api_key=api_key, base_url=settings.openai_base_url
340+
)
335341
generator = IdeaGenerator(
336342
ai_client=ai_client,
337343
system_prompt_path=str(PROMPTS_DIR / "idea_generation_system.txt"),

tests/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
os.environ.setdefault("SMTP_USER", "")
1111
os.environ.setdefault("SMTP_PASSWORD", "")
1212
os.environ.setdefault("EMAIL_RECIPIENTS", "")
13+
os.environ.setdefault("OPENAI_BASE_URL", "")
1314
os.environ.setdefault("NOTION_API_KEY", "")
1415
os.environ.setdefault("NOTION_IDEAS_DATABASE_ID", "")
1516
os.environ.setdefault("MONTHLY_SCHEDULE_DAY", "1")

0 commit comments

Comments
 (0)