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
5 changes: 4 additions & 1 deletion backend/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ SQLITE_DB_PATH=

# LLM Integrations
OPENAI_API_KEY=
EVOLINK_API_KEY=
EVOLINK_API_BASE=https://direct.evolink.ai/v1
EVOLINK_CHAT_MODEL=gpt-5.2
ANTHROPIC_API_KEY=
GOOGLEAI_API_KEY=

Expand Down Expand Up @@ -60,4 +63,4 @@ SERP_API_KEY=

# Devzery API Test
DEVZERY_API_KEY=
DEVZERY_SOURCE_NAME=
DEVZERY_SOURCE_NAME=
3 changes: 1 addition & 2 deletions backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ The `VideoDBTool` (`director/tools/videodb_tool.py`) provides an interface for i

## 🧠 LLM Integration

The `BaseLLM` class (`director/llm/base.py`) provides an abstraction for integrating different Language Model providers. The current implementation supports OpenAI (`director/llm/openai.py`).
The `BaseLLM` class (`director/llm/base.py`) provides an abstraction for integrating different Language Model providers. The current implementation supports OpenAI (`director/llm/openai.py`) and EvoLink (`director/llm/evolink.py`).

## 📦 Dependencies

Expand Down Expand Up @@ -123,4 +123,3 @@ make run
## 📚 Further Documentation

For more detailed information about specific components, please refer to the MkDocs documentation.

5 changes: 4 additions & 1 deletion backend/director/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class LLMType(str, Enum):
"""Enum for LLM types"""

OPENAI = "openai"
EVOLINK = "evolink"
ANTHROPIC = "anthropic"
GOOGLEAI = "googleai"
VIDEODB_PROXY = "videodb_proxy"
Expand All @@ -27,7 +28,9 @@ class EnvPrefix(str, Enum):
"""Enum for environment prefixes"""

OPENAI_ = "OPENAI_"
EVOLINK_ = "EVOLINK_"
ANTHROPIC_ = "ANTHROPIC_"
GOOGLEAI_ = "GOOGLEAI_"

DOWNLOADS_PATH="director/downloads"

DOWNLOADS_PATH = "director/downloads"
8 changes: 7 additions & 1 deletion backend/director/llm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from director.constants import LLMType

from director.llm.openai import OpenAI
from director.llm.evolink import Evolink
from director.llm.anthropic import AnthropicAI
from director.llm.googleai import GoogleAI
from director.llm.videodb_proxy import VideoDBProxy
Expand All @@ -12,13 +13,18 @@ def get_default_llm():
"""Get default LLM"""

openai = True if os.getenv("OPENAI_API_KEY") else False
evolink = True if os.getenv("EVOLINK_API_KEY") else False
anthropic = True if os.getenv("ANTHROPIC_API_KEY") else False
googleai = True if os.getenv("GOOGLEAI_API_KEY") else False

default_llm = os.getenv("DEFAULT_LLM")

if openai or default_llm == LLMType.OPENAI:
if default_llm == LLMType.EVOLINK:
return Evolink()
elif openai or default_llm == LLMType.OPENAI:
return OpenAI()
elif evolink:
return Evolink()
elif anthropic or default_llm == LLMType.ANTHROPIC:
return AnthropicAI()
elif googleai or default_llm == LLMType.GOOGLEAI:
Expand Down
52 changes: 52 additions & 0 deletions backend/director/llm/evolink.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from enum import Enum

from pydantic import Field, FieldValidationInfo, field_validator
from pydantic_settings import SettingsConfigDict

from director.constants import EnvPrefix, LLMType
from director.llm.openai import OpenAI, OpenaiConfig


class EvolinkChatModel(str, Enum):
"""Enum for EvoLink chat models."""

GPT5_2 = "gpt-5.2"
GPT5_1 = "gpt-5.1"
GEMINI_3_1_PRO = "gemini-3.1-pro"
DEEPSEEK_V4_PRO = "deepseek-v4-pro"
DOUBAO_SEED_2_0_PRO = "doubao-seed-2.0-pro"


class EvolinkConfig(OpenaiConfig):
"""EvoLink Config."""

model_config = SettingsConfigDict(
env_prefix=EnvPrefix.EVOLINK_,
extra="ignore",
)

llm_type: str = LLMType.EVOLINK
api_key: str = ""
api_base: str = "https://direct.evolink.ai/v1"
chat_model: str = Field(default=EvolinkChatModel.GPT5_2)

@field_validator("api_key")
@classmethod
def validate_non_empty(cls, v, info: FieldValidationInfo):
if not v:
raise ValueError(
f"{info.field_name} must not be empty. Please set {EnvPrefix.EVOLINK_.value}{info.field_name.upper()} environment variable."
)
return v


class Evolink(OpenAI):
"""EvoLink LLM integration using the OpenAI-compatible Chat Completions API."""

def __init__(self, config: EvolinkConfig = None):
"""
:param config: EvoLink Config
"""
if config is None:
config = EvolinkConfig()
super().__init__(config=config)
42 changes: 42 additions & 0 deletions backend/tests/llm/test_evolink.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import pytest

from director.constants import LLMType
from director.llm.evolink import Evolink, EvolinkConfig, EvolinkChatModel


def test_evolink_config_uses_direct_api_defaults(monkeypatch):
monkeypatch.setenv("EVOLINK_API_KEY", "test-key")

config = EvolinkConfig()

assert config.llm_type == LLMType.EVOLINK
assert config.api_key == "test-key"
assert config.api_base == "https://direct.evolink.ai/v1"
assert config.chat_model == EvolinkChatModel.GPT5_2


def test_evolink_config_supports_env_overrides(monkeypatch):
monkeypatch.setenv("EVOLINK_API_KEY", "test-key")
monkeypatch.setenv("EVOLINK_API_BASE", "https://example.com/v1")
monkeypatch.setenv("EVOLINK_CHAT_MODEL", "deepseek-v4-pro")

config = EvolinkConfig()

assert config.api_base == "https://example.com/v1"
assert config.chat_model == "deepseek-v4-pro"


def test_evolink_config_requires_api_key(monkeypatch):
monkeypatch.delenv("EVOLINK_API_KEY", raising=False)

with pytest.raises(ValueError, match="EVOLINK_API_KEY"):
EvolinkConfig()


def test_default_llm_can_select_evolink(monkeypatch):
import director.llm as llm_module

monkeypatch.setenv("DEFAULT_LLM", LLMType.EVOLINK)
monkeypatch.setenv("EVOLINK_API_KEY", "test-key")

assert isinstance(llm_module.get_default_llm(), Evolink)
17 changes: 17 additions & 0 deletions docs/llm/evolink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## EvoLink

EvoLink extends the OpenAI LLM integration and uses EvoLink's OpenAI-compatible Chat Completions API.

Set `EVOLINK_API_KEY` to your EvoLink API key. The default API base is `https://direct.evolink.ai/v1`, and the default chat model is `gpt-5.2`.

### EvoLink Config

EvoLink Config is the configuration object for EvoLink. It is used to configure EvoLink and is passed to EvoLink when it is created.

::: director.llm.evolink.EvolinkConfig

### EvoLink Interface

EvoLink is the LLM used by the agents and tools. It is used to generate responses to messages.

::: director.llm.evolink.Evolink
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ nav:
- 'Interface': 'llm/interface.md'
- Integrations:
- 'OpenAI': 'llm/openai.md'
- 'EvoLink': 'llm/evolink.md'
- 'AnthropicAI': 'llm/anthropic.md'
- 'GoogleAI': 'llm/googleai.md'
- 'Database':
Expand Down