Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
16 changes: 16 additions & 0 deletions core/completion/litellm_completion.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
import os
import re # Import re for parsing model name
from typing import Any, AsyncGenerator, Dict, List, Optional, Tuple, Union

Expand Down Expand Up @@ -236,6 +237,21 @@ def __init__(self, model_key: str):

self.model_config = settings.REGISTERED_MODELS[model_key]

# Resolve an api_key_env reference (e.g., "ORCAROUTER_API_KEY") into the
# api_key passed to LiteLLM, so keys stay in the environment rather than
# in morphik.toml.
api_key_env = self.model_config.get("api_key_env")
if api_key_env:
api_key = os.environ.get(api_key_env)
if api_key:
self.model_config["api_key"] = api_key
else:
logger.warning(
f"Model '{model_key}' references api_key_env '{api_key_env}', "
"but it is not set in the environment. Continuing without an api_key."
)
self.model_config.pop("api_key_env", None)

# Check if it's an Ollama model for potential direct usage
self.is_ollama = "ollama" in self.model_config.get("model_name", "").lower()
self.ollama_api_base = None
Expand Down
59 changes: 59 additions & 0 deletions core/tests/unit/test_litellm_completion_orcarouter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""Unit tests for LiteLLMCompletion api_key_env resolution (used by the OrcaRouter model)."""

import os
from types import SimpleNamespace
from unittest.mock import patch

import pytest

from core.completion.litellm_completion import LiteLLMCompletionModel

ORCAROUTER_MODEL = {
"model_name": "openai/orcarouter/auto",
"api_base": "https://api.orcarouter.ai/v1",
"api_key_env": "ORCAROUTER_API_KEY",
}


def _settings_with(registered_models):
return SimpleNamespace(REGISTERED_MODELS=registered_models)


class TestApiKeyEnvResolution:
def test_resolves_api_key_env_into_api_key(self):
os.environ["ORCAROUTER_API_KEY"] = "sk-orca-test"
try:
with patch(
"core.completion.litellm_completion.get_settings",
return_value=_settings_with({"orcarouter_auto": dict(ORCAROUTER_MODEL)}),
):
model = LiteLLMCompletionModel("orcarouter_auto")
assert model.model_config["api_key"] == "sk-orca-test"
assert "api_key_env" not in model.model_config
finally:
os.environ.pop("ORCAROUTER_API_KEY", None)

def test_missing_env_var_warns_and_omits_api_key(self):
os.environ.pop("ORCAROUTER_API_KEY", None)
with patch(
"core.completion.litellm_completion.get_settings",
return_value=_settings_with({"orcarouter_auto": dict(ORCAROUTER_MODEL)}),
):
model = LiteLLMCompletionModel("orcarouter_auto")
assert "api_key" not in model.model_config
assert "api_key_env" not in model.model_config

def test_model_without_api_key_env_is_unchanged(self):
with patch(
"core.completion.litellm_completion.get_settings",
return_value=_settings_with({"openai_gpt": {"model_name": "gpt-4.1"}}),
):
model = LiteLLMCompletionModel("openai_gpt")
assert model.model_config == {"model_name": "gpt-4.1"}

def test_unknown_model_key_raises(self):
with patch(
"core.completion.litellm_completion.get_settings",
return_value=_settings_with({}),
), pytest.raises(ValueError, match="not found in registered_models"):
LiteLLMCompletionModel("does_not_exist")
4 changes: 4 additions & 0 deletions morphik.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ gemini_flash = { model_name = "gemini/gemini-2.5-flash-preview-05-20" } # gemini
# gemini_flash = { model_name = "gemini/gemini-2.5-pro-preview-06-05" } # { model_name = "claude-4-sonnet-20250514"} # { model_name = "gpt-4.1" } # { model_name = "gemini/gemini-2.5-pro-preview-06-05" } # {model_name = "o3-2025-04-16"} # { model_name = "claude-3-7-sonnet-latest"} # { model_name = "gemini/gemini-2.5-flash-preview-05-20" } # gemini-2.5-flash-preview-05-20
# gemini_flash = { model_name = "groq/meta-llama/llama-4-maverick-17b-128e-instruct"}

# OrcaRouter model (OpenAI-compatible gateway, https://www.orcarouter.ai)
# Set ORCAROUTER_API_KEY in the environment (keys start with sk-orca-).
orcarouter_auto = { model_name = "openai/orcarouter/auto", api_base = "https://api.orcarouter.ai/v1", api_key_env = "ORCAROUTER_API_KEY" }

# Ollama models (modify api_base based on your deployment)
# - Local Ollama: "http://localhost:11434" (default)
# - Morphik in Docker, Ollama local: "http://host.docker.internal:11434"
Expand Down