Skip to content
This repository was archived by the owner on Feb 3, 2026. It is now read-only.

Commit 1d56cff

Browse files
committed
Add LLM reasonning and MCP configuration
Signed-off-by: Herklos <herklos@drakkar.software>
1 parent 2f723eb commit 1d56cff

16 files changed

+630
-20
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
### Added
99
- pydantic to requirements
1010
- add AbstractAIService
11+
- add AbstractWebSearchService
12+
- MCP server interface
1113

1214
## [1.7.3] - 2026-01-29
1315
### Added
@@ -68,7 +70,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6870

6971
## [1.6.21] - 2023-10-21
7072
### Added
71-
[Constant] add CONIG_LLM_CUSTOM_BASE_URL
73+
[Constant] add CONFIG_LLM_CUSTOM_BASE_URL
7274

7375
## [1.6.20] - 2023-10-07
7476
### Added

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# OctoBot-Services [1.7.3](https://github.com/Drakkar-Software/OctoBot-Services/tree/master/docs/CHANGELOG.md)
1+
# OctoBot-Services [1.7.4](https://github.com/Drakkar-Software/OctoBot-Services/tree/master/docs/CHANGELOG.md)
22
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/31a1caa6e5384d80bf890dba5c9b5e4b)](https://app.codacy.com/gh/Drakkar-Software/OctoBot-Services?utm_source=github.com&utm_medium=referral&utm_content=Drakkar-Software/OctoBot-Services&utm_campaign=Badge_Grade_Dashboard)
33
[![PyPI](https://img.shields.io/pypi/v/OctoBot-Services.svg)](https://pypi.python.org/pypi/OctoBot-Services/)
44
[![Github-Action-CI](https://github.com/Drakkar-Software/OctoBot-Services/workflows/OctoBot-Services-CI/badge.svg)](https://github.com/Drakkar-Software/OctoBot-Services/actions)

full_requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ gevent==25.5.1
3838
### used by flask-socketio with gevent (listed here because multiple libs are usable, force this one)
3939
gevent-websocket==0.10.1
4040
flask-socketio==5.5.1
41-
# chatgpt
41+
# openai
4242
openai==2.15.0
4343
# agents
4444
pydantic==2.12.5
45+
mcp==1.26.0
4546
# Coingecko
4647
coingecko-openapi-client>=1.3.0
4748
# Analysis tools

octobot_services/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
# License along with this library.
1616

1717
PROJECT_NAME = "OctoBot-Services"
18-
VERSION = "1.7.3" # major.minor.revision
18+
VERSION = "1.7.4" # major.minor.revision

octobot_services/api/__init__.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@
2121

2222
from octobot_services.api.services import (
2323
get_available_services,
24+
get_available_backtestable_services,
25+
get_available_ai_services,
26+
get_available_web_search_services,
27+
get_ai_service,
28+
get_web_search_service,
29+
is_service_available_in_backtesting,
2430
get_service,
2531
create_service_factory,
2632
stop_services,
@@ -39,6 +45,8 @@
3945
from octobot_services.api.service_feeds import (
4046
create_service_feed_factory,
4147
get_service_feed,
48+
get_available_backtestable_feeds,
49+
is_service_used_by_backtestable_feed,
4250
start_service_feed,
4351
stop_service_feed,
4452
clear_bot_id_feeds,
@@ -59,6 +67,12 @@
5967

6068
__all__ = [
6169
"get_available_services",
70+
"get_available_backtestable_services",
71+
"get_available_ai_services",
72+
"get_available_web_search_services",
73+
"get_ai_service",
74+
"get_web_search_service",
75+
"is_service_available_in_backtesting",
6276
"get_service",
6377
"create_service_factory",
6478
"stop_services",
@@ -73,6 +87,8 @@
7387
"stop_interfaces",
7488
"create_service_feed_factory",
7589
"get_service_feed",
90+
"get_available_backtestable_feeds",
91+
"is_service_used_by_backtestable_feed",
7692
"start_service_feed",
7793
"stop_service_feed",
7894
"clear_bot_id_feeds",

octobot_services/api/interfaces.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import async_channel.channels as channels
2020
import octobot_services.interfaces as interfaces
2121
import octobot_services.managers as managers
22+
import octobot_services.api.service_feeds as service_feeds_api
2223

2324

2425
def initialize_global_project_data(bot_api: object, project_name: str, project_version: str) -> None:
@@ -53,7 +54,12 @@ async def send_user_command(bot_id, subject, action, data, wait_for_processing=F
5354

5455

5556
def is_enabled_in_backtesting(interface_class) -> bool:
56-
return all(service.BACKTESTING_ENABLED for service in interface_class.REQUIRED_SERVICES)
57+
if not interface_class.REQUIRED_SERVICES:
58+
return True
59+
return all(
60+
service_feeds_api.is_service_used_by_backtestable_feed(service)
61+
for service in interface_class.REQUIRED_SERVICES
62+
)
5763

5864

5965
def is_interface_relevant(config, interface_class, backtesting_enabled):

octobot_services/api/service_feeds.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,25 @@
1313
#
1414
# You should have received a copy of the GNU Lesser General Public
1515
# License along with this library.
16+
import octobot_commons.tentacles_management as tentacles_management
17+
1618
import octobot_services.managers as managers
1719
import octobot_services.service_feeds as service_feeds
1820

1921

22+
def get_available_backtestable_feeds() -> list:
23+
feeds = tentacles_management.get_all_classes_from_parent(service_feeds.AbstractServiceFeed)
24+
return [feed for feed in feeds if feed.BACKTESTING_ENABLED]
25+
26+
27+
def is_service_used_by_backtestable_feed(service_class) -> bool:
28+
backtestable = get_available_backtestable_feeds()
29+
for feed in backtestable:
30+
if feed.REQUIRED_SERVICES and service_class in feed.REQUIRED_SERVICES:
31+
return True
32+
return False
33+
34+
2035
def create_service_feed_factory(config, main_async_loop, bot_id) -> service_feeds.ServiceFeedFactory:
2136
return service_feeds.ServiceFeedFactory(config, main_async_loop, bot_id)
2237

octobot_services/api/services.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#
1414
# You should have received a copy of the GNU Lesser General Public
1515
# License along with this library.
16+
import octobot_services.api.service_feeds as service_feeds_api
1617
import octobot_services.managers as managers
1718
import octobot_services.services as services
1819
import octobot_services.interfaces as interfaces
@@ -31,9 +32,57 @@ def _service_async_lock(service_class):
3132
return _SERVICE_ASYNC_LOCKS[service_class.__name__]
3233

3334

34-
def get_available_services() -> list:
35+
def get_available_services() -> list[type[services.AbstractService]]:
3536
return services.ServiceFactory.get_available_services()
3637

38+
def get_available_ai_services() -> list[type[services.AbstractAIService]]:
39+
return services.ServiceFactory.get_available_ai_services()
40+
41+
def get_available_web_search_services() -> list[type[services.AbstractWebSearchService]]:
42+
return services.ServiceFactory.get_available_web_search_services()
43+
44+
45+
def get_available_backtestable_services() -> list:
46+
return [
47+
service_class for service_class in services.ServiceFactory.get_available_services()
48+
if service_class.BACKTESTING_ENABLED
49+
]
50+
51+
async def _get_available_service_instance(
52+
get_available_services_func,
53+
service_type_name: str,
54+
is_backtesting: bool = False
55+
):
56+
available_services = get_available_services_func()
57+
for service_class in available_services:
58+
try:
59+
return await get_service(service_class, is_backtesting, None)
60+
except errors.CreationError:
61+
# Service is not running/initialized, skip it
62+
continue
63+
raise errors.CreationError(f"No {service_type_name} is currently running or available.")
64+
65+
async def get_ai_service(is_backtesting=False) -> services.AbstractAIService:
66+
return await _get_available_service_instance(
67+
get_available_ai_services,
68+
"AI service",
69+
is_backtesting
70+
)
71+
72+
async def get_web_search_service(is_backtesting=False) -> services.AbstractWebSearchService:
73+
return await _get_available_service_instance(
74+
get_available_web_search_services,
75+
"web search service",
76+
is_backtesting
77+
)
78+
79+
80+
def is_service_available_in_backtesting(service_class) -> bool:
81+
return (
82+
service_class.BACKTESTING_ENABLED
83+
or service_feeds_api.is_service_used_by_backtestable_feed(service_class)
84+
)
85+
3786

3887
async def get_service(service_class, is_backtesting, config=None):
3988
# prevent concurrent access when creating a service
@@ -47,7 +96,7 @@ async def get_service(service_class, is_backtesting, config=None):
4796
)
4897
if created:
4998
service = service_class.instance()
50-
if is_backtesting and not service.BACKTESTING_ENABLED:
99+
if is_backtesting and not is_service_available_in_backtesting(service_class):
51100
raise errors.UnavailableInBacktestingError(
52101
f"{service_class.__name__} service is not available in backtesting"
53102
)

octobot_services/constants.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,26 @@
8282
CONFIG_OPENAI_SECRET_KEY = "openai-secret-key"
8383
CONFIG_LLM_CUSTOM_BASE_URL = "llm-custom-base-url"
8484
CONFIG_LLM_MODEL = "llm-model"
85+
CONFIG_LLM_MODEL_FAST = "llm-model-fast"
86+
CONFIG_LLM_MODEL_REASONING = "llm-model-reasoning"
8587
CONFIG_LLM_DAILY_TOKENS_LIMIT = "llm-daily-tokens-limit"
88+
CONFIG_LLM_SHOW_REASONING = "llm-show-reasoning"
89+
CONFIG_LLM_REASONING_EFFORT = "llm-reasoning-effort"
90+
CONFIG_LLM_MCP_SERVERS = "llm-mcp-servers"
91+
CONFIG_LLM_AUTO_INJECT_MCP_TOOLS = "llm-auto-inject-mcp-tools"
8692
ENV_OPENAI_SECRET_KEY = "OPENAI_SECRET_KEY"
8793
ENV_GPT_MODEL = "GPT_MODEL"
8894
ENV_GPT_DAILY_TOKENS_LIMIT = "GPT_DAILY_TOKEN_LIMIT"
8995

96+
# MCP
97+
CONFIG_MCP = "mcp"
98+
CONFIG_MCP_IP = "ip"
99+
CONFIG_MCP_PORT = "port"
100+
ENV_MCP_PORT = "MCP_PORT"
101+
ENV_MCP_ADDRESS = "MCP_ADDRESS"
102+
DEFAULT_MCP_IP = '127.0.0.1'
103+
DEFAULT_MCP_PORT = 3001
104+
90105
# Google
91106
CONFIG_GOOGLE = "google"
92107
CONFIG_TREND_TOPICS = "trends"
@@ -123,6 +138,16 @@
123138
CONFIG_TAVILY_API_KEY = "api-key"
124139
CONFIG_TAVILY_PROJECT_ID = "project-id"
125140

141+
# SearXNG (self-hosted web search)
142+
CONFIG_SEARXNG = "searxng"
143+
CONFIG_SEARXNG_URL = "url"
144+
CONFIG_SEARXNG_PORT = "port"
145+
CONFIG_SEARXNG_CATEGORIES = "categories"
146+
CONFIG_SEARXNG_LANGUAGE = "language"
147+
CONFIG_SEARXNG_TIME_RANGE = "time_range"
148+
CONFIG_SEARXNG_SAFE_SEARCH = "safe_search"
149+
CONFIG_SEARXNG_ENGINES = "engines"
150+
126151
# Reddit
127152
CONFIG_REDDIT = "reddit"
128153
CONFIG_REDDIT_SUBREDDITS = "subreddits"

octobot_services/enums.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,8 @@ class ReadOnlyInfoType(enum.Enum):
4242
CLICKABLE = "clickable"
4343
CTA = "cta"
4444
READONLY = "readonly"
45+
46+
47+
class AIModelPolicy(enum.Enum):
48+
FAST = "fast"
49+
REASONING = "reasoning"

0 commit comments

Comments
 (0)