mirror of
https://github.com/vegu-ai/talemate.git
synced 2026-09-01 19:48:52 +02:00
fix: resolve redundant fetches in OpenRouter client with asyncio locks
This commit is contained in:
@@ -47,6 +47,7 @@
|
||||
- "Character Visuals: Fixed an infinite loop in the cover image and portrait tabs that spammed `scene_assets/search` requests at the backend whenever a character had no scene assets to fall back on as references. The reference-asset search now runs at most once per character, breaking the watcher feedback loop that previously kept firing throughout the time the Generate dialog was open."
|
||||
- "macOS Modifiers: Ctrl+click affordances throughout the UI now also accept Cmd (Meta) on macOS, where the OS reserves Ctrl+click for the system context menu. Tooltips, hints, and docs label the modifier as Cmd on Mac and Ctrl elsewhere. Affects regenerate, scene tools (narrator/director/actor/creative), world state manager, contextual generate, message asset image, autocomplete (Ctrl+Enter), and history navigation. (#261)"
|
||||
- "Game Loop Actor Iter: Fixed a bug where the `game_loop_actor_iter` event was only firing for player turns, so agents that listen for actor iteration (like passive narration) silently skipped AI turns."
|
||||
- "OpenRouter: Fixed redundant provider/model list fetches when multiple OpenRouter clients are configured. Concurrent `status()` calls all saw the fetched-flag as still false and each kicked off its own HTTP request; the fetches are now serialized with an asyncio lock and double-checked locking so only one request runs."
|
||||
improvements:
|
||||
- "System Prompt Override Indicators: The system prompt override list now shows a pencil icon next to entries that have an active override, making it easy to see which prompts have been customized."
|
||||
- "Search Strictness: The distance_mod embedding preset value is now a float (was int) with a range of 0.1–2.0, allowing both tighter and looser similarity matching. A 'Search Strictness' slider is now available in the Context Database UI, letting users tune search sensitivity on the fly. Changes persist to the active embedding preset immediately."
|
||||
|
||||
@@ -50,6 +50,9 @@ DEFAULT_MODEL = "google/gemini-3-flash-preview"
|
||||
MODELS_FETCHED = False
|
||||
PROVIDERS_FETCHED = False
|
||||
|
||||
_MODELS_LOCK = asyncio.Lock()
|
||||
_PROVIDERS_LOCK = asyncio.Lock()
|
||||
|
||||
|
||||
async def fetch_available_models(api_key: str = None):
|
||||
"""Fetch available models from OpenRouter API"""
|
||||
@@ -58,30 +61,36 @@ async def fetch_available_models(api_key: str = None):
|
||||
if MODELS_FETCHED:
|
||||
return AVAILABLE_MODELS
|
||||
|
||||
try:
|
||||
log.debug("Fetching models from OpenRouter")
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(
|
||||
"https://openrouter.ai/api/v1/models", timeout=10.0
|
||||
)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
models = []
|
||||
for model in data.get("data", []):
|
||||
model_id = model.get("id")
|
||||
if model_id:
|
||||
models.append(model_id)
|
||||
AVAILABLE_MODELS = sorted(models)
|
||||
log.debug(f"Fetched {len(AVAILABLE_MODELS)} models from OpenRouter")
|
||||
else:
|
||||
log.warning(
|
||||
f"Failed to fetch models from OpenRouter: {response.status_code}"
|
||||
)
|
||||
except Exception as e:
|
||||
log.error(f"Error fetching models from OpenRouter: {e}")
|
||||
async with _MODELS_LOCK:
|
||||
if MODELS_FETCHED:
|
||||
return AVAILABLE_MODELS
|
||||
|
||||
MODELS_FETCHED = True
|
||||
return AVAILABLE_MODELS
|
||||
try:
|
||||
log.debug("Fetching models from OpenRouter")
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(
|
||||
"https://openrouter.ai/api/v1/models", timeout=10.0
|
||||
)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
models = []
|
||||
for model in data.get("data", []):
|
||||
model_id = model.get("id")
|
||||
if model_id:
|
||||
models.append(model_id)
|
||||
AVAILABLE_MODELS = sorted(models)
|
||||
log.debug(
|
||||
f"Fetched {len(AVAILABLE_MODELS)} models from OpenRouter"
|
||||
)
|
||||
else:
|
||||
log.warning(
|
||||
f"Failed to fetch models from OpenRouter: {response.status_code}"
|
||||
)
|
||||
except Exception as e:
|
||||
log.error(f"Error fetching models from OpenRouter: {e}")
|
||||
|
||||
MODELS_FETCHED = True
|
||||
return AVAILABLE_MODELS
|
||||
|
||||
|
||||
async def fetch_available_providers(api_key: str = None):
|
||||
@@ -91,43 +100,47 @@ async def fetch_available_providers(api_key: str = None):
|
||||
if PROVIDERS_FETCHED:
|
||||
return AVAILABLE_PROVIDERS
|
||||
|
||||
if not api_key:
|
||||
api_key = get_config().openrouter.api_key
|
||||
async with _PROVIDERS_LOCK:
|
||||
if PROVIDERS_FETCHED:
|
||||
return AVAILABLE_PROVIDERS
|
||||
|
||||
if not api_key:
|
||||
api_key = get_config().openrouter.api_key
|
||||
|
||||
if not api_key:
|
||||
log.warning("No OpenRouter API key available, cannot fetch providers")
|
||||
PROVIDERS_FETCHED = True
|
||||
return AVAILABLE_PROVIDERS
|
||||
|
||||
try:
|
||||
log.debug("Fetching providers from OpenRouter")
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(
|
||||
"https://openrouter.ai/api/v1/providers",
|
||||
headers={"Authorization": f"Bearer {api_key}"},
|
||||
timeout=10.0,
|
||||
)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
providers = []
|
||||
for provider in data.get("data", []):
|
||||
provider_name = provider.get("name")
|
||||
if provider_name:
|
||||
providers.append(provider_name)
|
||||
AVAILABLE_PROVIDERS = sorted(providers)
|
||||
log.info(
|
||||
f"Fetched {len(AVAILABLE_PROVIDERS)} providers from OpenRouter"
|
||||
)
|
||||
else:
|
||||
log.error(
|
||||
f"Failed to fetch providers from OpenRouter: HTTP {response.status_code}"
|
||||
)
|
||||
except Exception as e:
|
||||
log.error(f"Error fetching providers from OpenRouter: {e}")
|
||||
|
||||
if not api_key:
|
||||
log.warning("No OpenRouter API key available, cannot fetch providers")
|
||||
PROVIDERS_FETCHED = True
|
||||
return AVAILABLE_PROVIDERS
|
||||
|
||||
try:
|
||||
log.debug("Fetching providers from OpenRouter")
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(
|
||||
"https://openrouter.ai/api/v1/providers",
|
||||
headers={"Authorization": f"Bearer {api_key}"},
|
||||
timeout=10.0,
|
||||
)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
providers = []
|
||||
for provider in data.get("data", []):
|
||||
provider_name = provider.get("name")
|
||||
if provider_name:
|
||||
providers.append(provider_name)
|
||||
AVAILABLE_PROVIDERS = sorted(providers)
|
||||
log.info(
|
||||
f"Fetched {len(AVAILABLE_PROVIDERS)} providers from OpenRouter"
|
||||
)
|
||||
else:
|
||||
log.error(
|
||||
f"Failed to fetch providers from OpenRouter: HTTP {response.status_code}"
|
||||
)
|
||||
except Exception as e:
|
||||
log.error(f"Error fetching providers from OpenRouter: {e}")
|
||||
|
||||
PROVIDERS_FETCHED = True
|
||||
return AVAILABLE_PROVIDERS
|
||||
|
||||
|
||||
def on_talemate_started(event):
|
||||
"""Spawn background tasks to fetch models and providers"""
|
||||
|
||||
Reference in New Issue
Block a user