alive-ai 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +24 -0
- package/LICENSE +21 -0
- package/README.md +143 -0
- package/alive_ai/__init__.py +3 -0
- package/brain/__init__.py +59 -0
- package/brain/almost_said.py +154 -0
- package/brain/bid_detector.py +636 -0
- package/brain/conversation_flow.py +135 -0
- package/brain/curiosity.py +328 -0
- package/brain/default_mode.py +1438 -0
- package/brain/dreams.py +220 -0
- package/brain/embeddings/__init__.py +82 -0
- package/brain/emotional_memory.py +949 -0
- package/brain/global_activity.py +173 -0
- package/brain/group_dynamics.py +63 -0
- package/brain/linguistic.py +235 -0
- package/brain/llm/__init__.py +63 -0
- package/brain/llm/base.py +33 -0
- package/brain/llm/fallback_router.py +309 -0
- package/brain/llm/manifest.md +30 -0
- package/brain/llm/ollama.py +218 -0
- package/brain/llm/openrouter.py +151 -0
- package/brain/llm/provider.py +205 -0
- package/brain/llm/unified.py +423 -0
- package/brain/llm/zai.py +169 -0
- package/brain/manifest.md +23 -0
- package/brain/memory/__init__.py +123 -0
- package/brain/memory/episodic.py +92 -0
- package/brain/memory/fact_extractor.py +209 -0
- package/brain/memory/index.py +54 -0
- package/brain/memory/manager.py +151 -0
- package/brain/memory/summarizer.py +102 -0
- package/brain/memory/vector_store.py +297 -0
- package/brain/memory/working.py +43 -0
- package/brain/narrative.py +343 -0
- package/brain/stt/__init__.py +4 -0
- package/brain/stt/google_stt.py +83 -0
- package/brain/stt/whisper_stt.py +82 -0
- package/brain/subconscious/__init__.py +33 -0
- package/brain/subconscious/actions.py +136 -0
- package/brain/subconscious/evaluation.py +166 -0
- package/brain/subconscious/goal_system.py +90 -0
- package/brain/subconscious/goals.py +41 -0
- package/brain/subconscious/impulse_generator.py +200 -0
- package/brain/subconscious/impulses.py +48 -0
- package/brain/subconscious/learning.py +24 -0
- package/brain/subconscious/learning_system.py +79 -0
- package/brain/subconscious/loop.py +398 -0
- package/brain/subconscious/manifest.md +32 -0
- package/brain/subconscious/relationship.py +47 -0
- package/brain/subconscious/relationship_memory.py +83 -0
- package/brain/subconscious/response_analyzer.py +74 -0
- package/brain/subconscious/templates.py +70 -0
- package/brain/subconscious/thought.py +37 -0
- package/brain/subconscious/working_memory.py +97 -0
- package/cli/index.js +371 -0
- package/config/directives.example.json +28 -0
- package/config/instructions.example.md +16 -0
- package/config/self.example.json +74 -0
- package/config/settings.example.json +95 -0
- package/core/__init__.py +1 -0
- package/core/config.py +54 -0
- package/core/directives.py +198 -0
- package/core/events.py +50 -0
- package/core/follow_up.py +267 -0
- package/core/hot_reload.py +174 -0
- package/core/initialization.py +253 -0
- package/core/manifest.md +28 -0
- package/core/media_handler.py +241 -0
- package/core/memory_monitor.py +200 -0
- package/core/message_handler.py +1440 -0
- package/core/proactive_generator.py +277 -0
- package/core/self.py +188 -0
- package/core/settings.py +169 -0
- package/core/skills_registry.py +357 -0
- package/core/state.py +27 -0
- package/core/subconscious_bridge.py +93 -0
- package/core/thinking.py +175 -0
- package/core/user_manager.py +306 -0
- package/core/user_tracker.py +144 -0
- package/demo/index.html +144 -0
- package/docker-compose.yml +28 -0
- package/docs/assets/logo.svg +15 -0
- package/docs/index.html +355 -0
- package/heart/__init__.py +93 -0
- package/heart/afterglow.py +215 -0
- package/heart/attachment.py +186 -0
- package/heart/circadian.py +251 -0
- package/heart/complex_emotions.py +114 -0
- package/heart/conflicts.py +589 -0
- package/heart/core.py +387 -0
- package/heart/emotional_decay.py +59 -0
- package/heart/emotional_memory.py +261 -0
- package/heart/emotional_state.py +146 -0
- package/heart/emotional_variability.py +156 -0
- package/heart/hormonal.py +424 -0
- package/heart/inconsistency.py +1222 -0
- package/heart/integrity.py +469 -0
- package/heart/interoception.py +997 -0
- package/heart/love.py +120 -0
- package/heart/manifest.md +25 -0
- package/heart/mood_shifts.py +169 -0
- package/heart/phantom_somatic.py +259 -0
- package/heart/predictive.py +374 -0
- package/heart/scars.py +474 -0
- package/heart/somatic.py +482 -0
- package/heart/soul.py +633 -0
- package/heart/telemetry.py +942 -0
- package/heart/triggers.py +119 -0
- package/heart/unconscious.py +443 -0
- package/input/__init__.py +1 -0
- package/input/manifest.md +24 -0
- package/input/telegram/__init__.py +1 -0
- package/input/telegram/commands.py +762 -0
- package/input/telegram/listener.py +532 -0
- package/main.py +90 -0
- package/manifest.md +28 -0
- package/mypics/.gitkeep +1 -0
- package/myvids/.gitkeep +1 -0
- package/output/__init__.py +1 -0
- package/output/images/__init__.py +1 -0
- package/output/images/fal_gen.py +43 -0
- package/output/manifest.md +26 -0
- package/output/text/__init__.py +1 -0
- package/output/text/sender.py +22 -0
- package/output/voice/__init__.py +64 -0
- package/output/voice/google_tts.py +252 -0
- package/output/voice/gtts_tts.py +214 -0
- package/output/voice/vibe_tts.py +190 -0
- package/package.json +58 -0
- package/pyproject.toml +23 -0
- package/requirements.txt +21 -0
- package/skills/__init__.py +1 -0
- package/skills/anticipation_engine/__init__.py +8 -0
- package/skills/anticipation_engine/engine.py +618 -0
- package/skills/anticipation_engine/manifest.md +192 -0
- package/skills/calendar/__init__.py +1 -0
- package/skills/content_unlocks/__init__.py +8 -0
- package/skills/content_unlocks/manifest.md +231 -0
- package/skills/content_unlocks/unlocks.py +945 -0
- package/skills/exclusive_moments/__init__.py +8 -0
- package/skills/exclusive_moments/manifest.md +145 -0
- package/skills/exclusive_moments/moments.py +506 -0
- package/skills/intimacy_layers/__init__.py +8 -0
- package/skills/intimacy_layers/layers.py +703 -0
- package/skills/intimacy_layers/manifest.md +203 -0
- package/skills/manifest.md +67 -0
- package/skills/memory_callbacks/__init__.py +9 -0
- package/skills/memory_callbacks/callbacks.py +748 -0
- package/skills/memory_callbacks/manifest.md +170 -0
- package/skills/message_scheduler/__init__.py +19 -0
- package/skills/message_scheduler/manifest.md +107 -0
- package/skills/message_scheduler/scheduler.py +510 -0
- package/skills/photo_manager/__init__.py +1 -0
- package/skills/photo_manager/scanner.py +296 -0
- package/skills/relationship_milestones/__init__.py +8 -0
- package/skills/relationship_milestones/manifest.md +206 -0
- package/skills/relationship_milestones/tracker.py +494 -0
- package/skills/self_authorship/__init__.py +23 -0
- package/skills/self_authorship/author.py +331 -0
- package/skills/self_authorship/manifest.md +24 -0
- package/skills/video_manager/__init__.py +5 -0
- package/skills/video_manager/manifest.md +37 -0
- package/skills/video_manager/scanner.py +229 -0
- package/webui/__init__.py +3 -0
- package/webui/app.py +936 -0
- package/webui/bridge.py +366 -0
- package/webui/static/index.html +2070 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Brain: LLM - OpenRouter API Client
|
|
3
|
+
OpenRouter provides unified access to many LLM providers
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import aiohttp
|
|
7
|
+
import asyncio
|
|
8
|
+
import time
|
|
9
|
+
from typing import Optional, List, Dict
|
|
10
|
+
from .base import BaseLLM
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class OpenRouterClient(BaseLLM):
|
|
14
|
+
"""OpenRouter API client - unified access to many models"""
|
|
15
|
+
|
|
16
|
+
BASE_URL = "https://openrouter.ai/api/v1"
|
|
17
|
+
|
|
18
|
+
# Site URL for rankings (required by OpenRouter)
|
|
19
|
+
SITE_URL = "https://alive_ai.ai"
|
|
20
|
+
SITE_NAME = "Alive-AI Girlfriend"
|
|
21
|
+
|
|
22
|
+
def __init__(self, api_key: str, model: str = "anthropic/claude-3.5-sonnet"):
|
|
23
|
+
super().__init__(api_key, model)
|
|
24
|
+
self.session: Optional[aiohttp.ClientSession] = None
|
|
25
|
+
self._available: Optional[bool] = None
|
|
26
|
+
self._last_check: float = 0
|
|
27
|
+
|
|
28
|
+
async def _get_session(self) -> aiohttp.ClientSession:
|
|
29
|
+
if self.session is None or self.session.closed:
|
|
30
|
+
self.session = aiohttp.ClientSession()
|
|
31
|
+
return self.session
|
|
32
|
+
|
|
33
|
+
async def is_available(self) -> bool:
|
|
34
|
+
"""Check if OpenRouter API is accessible and configured"""
|
|
35
|
+
# Cache availability for 60 seconds
|
|
36
|
+
if self._available is not None and time.time() - self._last_check < 60:
|
|
37
|
+
return self._available
|
|
38
|
+
|
|
39
|
+
if not self.api_key:
|
|
40
|
+
self._available = False
|
|
41
|
+
self._last_check = time.time()
|
|
42
|
+
return False
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
session = await self._get_session()
|
|
46
|
+
|
|
47
|
+
# Check API key validity by listing models
|
|
48
|
+
async with session.get(
|
|
49
|
+
f"{self.BASE_URL}/models",
|
|
50
|
+
headers={
|
|
51
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
52
|
+
},
|
|
53
|
+
timeout=aiohttp.ClientTimeout(total=10)
|
|
54
|
+
) as resp:
|
|
55
|
+
if resp.status == 200:
|
|
56
|
+
self._available = True
|
|
57
|
+
self._last_check = time.time()
|
|
58
|
+
return True
|
|
59
|
+
elif resp.status == 401:
|
|
60
|
+
print("[OpenRouter] Invalid API key")
|
|
61
|
+
self._available = False
|
|
62
|
+
self._last_check = time.time()
|
|
63
|
+
return False
|
|
64
|
+
except Exception as e:
|
|
65
|
+
print(f"[OpenRouter] Availability check failed: {e}")
|
|
66
|
+
|
|
67
|
+
self._available = False
|
|
68
|
+
self._last_check = time.time()
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
async def chat(
|
|
72
|
+
self,
|
|
73
|
+
messages: List[Dict[str, str]],
|
|
74
|
+
max_tokens: int = 500,
|
|
75
|
+
temperature: float = None
|
|
76
|
+
) -> Optional[str]:
|
|
77
|
+
"""Send chat completion request via OpenRouter"""
|
|
78
|
+
import os
|
|
79
|
+
# Use passed temperature, or environment variable, or default high value
|
|
80
|
+
if temperature is None:
|
|
81
|
+
temperature = float(os.environ.get("LLM_TEMPERATURE", "0.95"))
|
|
82
|
+
|
|
83
|
+
session = await self._get_session()
|
|
84
|
+
|
|
85
|
+
headers = {
|
|
86
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
87
|
+
"Content-Type": "application/json",
|
|
88
|
+
"HTTP-Referer": self.SITE_URL,
|
|
89
|
+
"X-Title": self.SITE_NAME
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
payload = {
|
|
93
|
+
"model": self.model,
|
|
94
|
+
"messages": messages,
|
|
95
|
+
"max_tokens": max_tokens,
|
|
96
|
+
"temperature": temperature,
|
|
97
|
+
"frequency_penalty": 0.8, # Penalize repeated phrases - increased from 0.5
|
|
98
|
+
"presence_penalty": 0.6, # Encourage topic diversity - increased from 0.3
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
async with session.post(
|
|
103
|
+
f"{self.BASE_URL}/chat/completions",
|
|
104
|
+
headers=headers,
|
|
105
|
+
json=payload,
|
|
106
|
+
timeout=aiohttp.ClientTimeout(total=60)
|
|
107
|
+
) as resp:
|
|
108
|
+
if resp.status != 200:
|
|
109
|
+
error = await resp.text()
|
|
110
|
+
print(f"[OpenRouter] Error {resp.status}: {error[:300]}")
|
|
111
|
+
self._available = False
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
data = await resp.json()
|
|
115
|
+
# Check for error response
|
|
116
|
+
if "error" in data:
|
|
117
|
+
print(f"[OpenRouter] API Error: {data['error']}")
|
|
118
|
+
self._available = False
|
|
119
|
+
return None
|
|
120
|
+
# Log token usage
|
|
121
|
+
if "usage" in data:
|
|
122
|
+
usage = data["usage"]
|
|
123
|
+
print(f"[LLM] Tokens - Input: {usage.get('prompt_tokens', '?')} | Output: {usage.get('completion_tokens', '?')} | Total: {usage.get('total_tokens', '?')}")
|
|
124
|
+
if "choices" not in data or not data["choices"]:
|
|
125
|
+
print(f"[OpenRouter] No choices in response: {list(data.keys())}")
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
content = data["choices"][0]["message"]["content"]
|
|
129
|
+
|
|
130
|
+
if not content or not content.strip():
|
|
131
|
+
print(f"[OpenRouter] Empty content!")
|
|
132
|
+
return None
|
|
133
|
+
|
|
134
|
+
print(f"[OpenRouter] Response: {content[:100]}...")
|
|
135
|
+
|
|
136
|
+
# Mark as available since we got a response
|
|
137
|
+
self._available = True
|
|
138
|
+
self._last_check = time.time()
|
|
139
|
+
return content
|
|
140
|
+
|
|
141
|
+
except asyncio.TimeoutError:
|
|
142
|
+
print(f"[OpenRouter] Timeout (60s)")
|
|
143
|
+
return None
|
|
144
|
+
except Exception as e:
|
|
145
|
+
print(f"[OpenRouter] Exception: {e}")
|
|
146
|
+
return None
|
|
147
|
+
|
|
148
|
+
async def close(self):
|
|
149
|
+
"""Close the client session"""
|
|
150
|
+
if self.session and not self.session.closed:
|
|
151
|
+
await self.session.close()
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Brain: LLM - Provider Factory
|
|
3
|
+
Creates the appropriate LLM client based on configuration.
|
|
4
|
+
Supports both legacy single-provider mode and new unified fallback mode.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from typing import Optional, Tuple
|
|
9
|
+
from .base import BaseLLM
|
|
10
|
+
from .zai import ZAIClient
|
|
11
|
+
from .openrouter import OpenRouterClient
|
|
12
|
+
from .ollama import OllamaClient
|
|
13
|
+
from .unified import UnifiedLLM, get_unified_llm
|
|
14
|
+
from .fallback_router import FallbackRouter, get_fallback_router
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_provider_config(task: str = "main") -> Tuple[str, str, str]:
|
|
18
|
+
"""Get provider and model for a specific task
|
|
19
|
+
|
|
20
|
+
Tasks:
|
|
21
|
+
- main: Main conversation model
|
|
22
|
+
- thinking: Deep reasoning, complex decisions
|
|
23
|
+
- fast: Quick responses, impulses, subconscious
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Tuple of (provider_name, api_key, model_name)
|
|
27
|
+
"""
|
|
28
|
+
# Try to get from settings.json first
|
|
29
|
+
try:
|
|
30
|
+
from core.settings import get as settings_get
|
|
31
|
+
|
|
32
|
+
# Check if fallback mode is enabled
|
|
33
|
+
llm_fallback = settings_get("LLM_FALLBACK", {})
|
|
34
|
+
if llm_fallback.get("ENABLED", False):
|
|
35
|
+
# Use unified mode - get config from settings
|
|
36
|
+
provider = "unified"
|
|
37
|
+
api_key = "" # Not used in unified mode
|
|
38
|
+
model = "" # Not used in unified mode
|
|
39
|
+
return provider, api_key, model
|
|
40
|
+
|
|
41
|
+
# Legacy mode - use settings.json
|
|
42
|
+
provider = settings_get("LLM_PROVIDER", "zai").lower()
|
|
43
|
+
|
|
44
|
+
if provider == "openrouter":
|
|
45
|
+
api_key = settings_get("OPENROUTER_API_KEY", "") or os.environ.get("OPENROUTER_API_KEY", "")
|
|
46
|
+
if task == "fast":
|
|
47
|
+
model = settings_get("OPENROUTER_MODEL_FAST", "openai/gpt-4o-mini")
|
|
48
|
+
elif task == "thinking":
|
|
49
|
+
model = settings_get("OPENROUTER_MODEL_THINKING", "anthropic/claude-3.5-sonnet")
|
|
50
|
+
else:
|
|
51
|
+
model = settings_get("OPENROUTER_MODEL_MAIN", "anthropic/claude-3.5-sonnet")
|
|
52
|
+
else:
|
|
53
|
+
# Default to ZAI
|
|
54
|
+
api_key = settings_get("ZAI_API_KEY", "") or os.environ.get("ZAI_API_KEY", "")
|
|
55
|
+
if task == "fast":
|
|
56
|
+
model = settings_get("ZAI_MODEL_FAST", "glm-4-flash")
|
|
57
|
+
elif task == "thinking":
|
|
58
|
+
model = settings_get("ZAI_MODEL_THINKING", "glm-4.6v")
|
|
59
|
+
else:
|
|
60
|
+
model = settings_get("ZAI_MODEL_MAIN", "glm-4.6v")
|
|
61
|
+
|
|
62
|
+
return provider, api_key, model
|
|
63
|
+
|
|
64
|
+
except ImportError:
|
|
65
|
+
# Fallback to environment variables
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
# Environment variable fallback
|
|
69
|
+
provider = os.environ.get("LLM_PROVIDER", "zai").lower()
|
|
70
|
+
|
|
71
|
+
if provider == "openrouter":
|
|
72
|
+
api_key = os.environ.get("OPENROUTER_API_KEY", "")
|
|
73
|
+
if task == "fast":
|
|
74
|
+
model = os.environ.get("OPENROUTER_MODEL_FAST", "openai/gpt-4o-mini")
|
|
75
|
+
elif task == "thinking":
|
|
76
|
+
model = os.environ.get("OPENROUTER_MODEL_THINKING", "anthropic/claude-3.5-sonnet")
|
|
77
|
+
else:
|
|
78
|
+
model = os.environ.get("OPENROUTER_MODEL_MAIN", "anthropic/claude-3.5-sonnet")
|
|
79
|
+
else:
|
|
80
|
+
# Default to ZAI
|
|
81
|
+
api_key = os.environ.get("ZAI_API_KEY", "")
|
|
82
|
+
if task == "fast":
|
|
83
|
+
model = os.environ.get("ZAI_MODEL_FAST", "glm-4-flash")
|
|
84
|
+
elif task == "thinking":
|
|
85
|
+
model = os.environ.get("ZAI_MODEL_THINKING", "glm-4.6v")
|
|
86
|
+
else:
|
|
87
|
+
model = os.environ.get("ZAI_MODEL_MAIN", "glm-4.6v")
|
|
88
|
+
|
|
89
|
+
return provider, api_key, model
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def get_llm_client(task: str = "main") -> Optional[BaseLLM]:
|
|
93
|
+
"""Get LLM client for a specific task
|
|
94
|
+
|
|
95
|
+
Usage:
|
|
96
|
+
# Main conversation
|
|
97
|
+
llm = get_llm_client("main")
|
|
98
|
+
|
|
99
|
+
# Quick impulse generation
|
|
100
|
+
fast_llm = get_llm_client("fast")
|
|
101
|
+
|
|
102
|
+
# Deep thinking
|
|
103
|
+
think_llm = get_llm_client("thinking")
|
|
104
|
+
"""
|
|
105
|
+
provider, api_key, model = get_provider_config(task)
|
|
106
|
+
|
|
107
|
+
# Handle unified mode
|
|
108
|
+
if provider == "unified":
|
|
109
|
+
return get_unified_llm_client()
|
|
110
|
+
|
|
111
|
+
if not api_key:
|
|
112
|
+
print(f"[LLM] No API key for provider: {provider}")
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
if provider == "openrouter":
|
|
116
|
+
return OpenRouterClient(api_key, model)
|
|
117
|
+
else:
|
|
118
|
+
return ZAIClient(api_key, model)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def get_fast_llm() -> Optional[BaseLLM]:
|
|
122
|
+
"""Get fast LLM for quick tasks (impulses, subconscious)"""
|
|
123
|
+
return get_llm_client("fast")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def get_thinking_llm() -> Optional[BaseLLM]:
|
|
127
|
+
"""Get thinking LLM for complex reasoning"""
|
|
128
|
+
return get_llm_client("thinking")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def get_main_llm() -> Optional[BaseLLM]:
|
|
132
|
+
"""Get main LLM for conversations"""
|
|
133
|
+
return get_llm_client("main")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# ============================================================
|
|
137
|
+
# Unified LLM Support
|
|
138
|
+
# ============================================================
|
|
139
|
+
|
|
140
|
+
# Cache for the unified client
|
|
141
|
+
_unified_client: Optional[UnifiedLLM] = None
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def get_unified_llm_client() -> Optional[UnifiedLLM]:
|
|
145
|
+
"""
|
|
146
|
+
Get the unified LLM client with fallback chain.
|
|
147
|
+
|
|
148
|
+
This is the recommended way to get an LLM client as it:
|
|
149
|
+
- Tries ZAI first
|
|
150
|
+
- Falls back to OpenRouter if ZAI fails
|
|
151
|
+
- Falls back to local Ollama if both fail
|
|
152
|
+
- Handles empty responses and errors gracefully
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
UnifiedLLM instance or None if not configured
|
|
156
|
+
"""
|
|
157
|
+
global _unified_client
|
|
158
|
+
|
|
159
|
+
if _unified_client is not None:
|
|
160
|
+
return _unified_client
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
from core.settings import get as settings_get
|
|
164
|
+
|
|
165
|
+
# Get fallback configuration
|
|
166
|
+
llm_config = settings_get("LLM_FALLBACK", {})
|
|
167
|
+
if not llm_config.get("ENABLED", False):
|
|
168
|
+
print("[LLM] Fallback not enabled, using single provider")
|
|
169
|
+
return None
|
|
170
|
+
|
|
171
|
+
_unified_client = get_unified_llm(settings_getter=settings_get)
|
|
172
|
+
print(f"[LLM] Unified LLM initialized with fallback chain")
|
|
173
|
+
return _unified_client
|
|
174
|
+
|
|
175
|
+
except ImportError:
|
|
176
|
+
print("[LLM] Settings not available for unified LLM")
|
|
177
|
+
return None
|
|
178
|
+
except Exception as e:
|
|
179
|
+
print(f"[LLM] Error creating unified LLM: {e}")
|
|
180
|
+
return None
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def get_fallback_llm_client() -> Optional[FallbackRouter]:
|
|
184
|
+
"""
|
|
185
|
+
Get the fallback router for LLM requests.
|
|
186
|
+
|
|
187
|
+
This provides a simpler interface than UnifiedLLM with:
|
|
188
|
+
- Direct provider list
|
|
189
|
+
- Simple logging
|
|
190
|
+
- No state management
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
FallbackRouter instance or None if not configured
|
|
194
|
+
"""
|
|
195
|
+
try:
|
|
196
|
+
return get_fallback_router()
|
|
197
|
+
except Exception as e:
|
|
198
|
+
print(f"[LLM] Error creating fallback router: {e}")
|
|
199
|
+
return None
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def reset_unified_client():
|
|
203
|
+
"""Reset the unified client cache (for testing or reconfiguration)"""
|
|
204
|
+
global _unified_client
|
|
205
|
+
_unified_client = None
|