mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-17 18:25:26 -04:00
Fix provider setup and strip message metadata
This commit is contained in:
+16
-3
@@ -357,6 +357,19 @@ def _parse_anthropic_response(data: dict) -> str:
|
|||||||
return block.get("text", "")
|
return block.get("text", "")
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def _sanitize_llm_messages(messages: List[Dict]) -> List[Dict]:
|
||||||
|
"""Strip Odysseus-only metadata before sending messages to providers."""
|
||||||
|
allowed = {"role", "content", "name", "tool_call_id", "tool_calls", "function_call"}
|
||||||
|
cleaned = []
|
||||||
|
for msg in messages or []:
|
||||||
|
if not isinstance(msg, dict):
|
||||||
|
continue
|
||||||
|
item = {k: v for k, v in msg.items() if k in allowed and v is not None}
|
||||||
|
if "role" in item and "content" in item:
|
||||||
|
cleaned.append(item)
|
||||||
|
return cleaned
|
||||||
|
|
||||||
def _normalize_anthropic_url(url: str) -> str:
|
def _normalize_anthropic_url(url: str) -> str:
|
||||||
"""Ensure Anthropic URL points to /v1/messages."""
|
"""Ensure Anthropic URL points to /v1/messages."""
|
||||||
url = url.rstrip("/")
|
url = url.rstrip("/")
|
||||||
@@ -422,7 +435,7 @@ def llm_call(url: str, model: str, messages: List[Dict], temperature: float = LL
|
|||||||
if isinstance(headers, dict):
|
if isinstance(headers, dict):
|
||||||
h.update(headers)
|
h.update(headers)
|
||||||
|
|
||||||
messages_copy = [msg.copy() for msg in messages]
|
messages_copy = _sanitize_llm_messages(messages)
|
||||||
|
|
||||||
# Consolidate multiple system messages into one at the start.
|
# Consolidate multiple system messages into one at the start.
|
||||||
sys_parts = []
|
sys_parts = []
|
||||||
@@ -530,7 +543,7 @@ async def llm_call_async(
|
|||||||
) -> str:
|
) -> str:
|
||||||
"""Asynchronous LLM call using httpx with connection pooling, timeout, retry logic, and performance logging."""
|
"""Asynchronous LLM call using httpx with connection pooling, timeout, retry logic, and performance logging."""
|
||||||
provider = _detect_provider(url)
|
provider = _detect_provider(url)
|
||||||
messages_copy = [msg.copy() for msg in messages]
|
messages_copy = _sanitize_llm_messages(messages)
|
||||||
|
|
||||||
# Consolidate multiple system messages into one at the start.
|
# Consolidate multiple system messages into one at the start.
|
||||||
sys_parts = []
|
sys_parts = []
|
||||||
@@ -627,7 +640,7 @@ async def stream_llm(url: str, model: str, messages: List[Dict], temperature: fl
|
|||||||
- data: [DONE] — end of stream
|
- data: [DONE] — end of stream
|
||||||
"""
|
"""
|
||||||
provider = _detect_provider(url)
|
provider = _detect_provider(url)
|
||||||
messages_copy = [msg.copy() for msg in messages]
|
messages_copy = _sanitize_llm_messages(messages)
|
||||||
|
|
||||||
# Consolidate multiple system messages into one at the start.
|
# Consolidate multiple system messages into one at the start.
|
||||||
# Some models (e.g. Qwen3.5) reject system messages that aren't first.
|
# Some models (e.g. Qwen3.5) reject system messages that aren't first.
|
||||||
|
|||||||
@@ -101,6 +101,24 @@ function _extractSetupProviderCredential(input) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _normalizeSetupBaseUrl(raw) {
|
||||||
|
let u = (raw || '').trim();
|
||||||
|
u = u.replace(/^https?:\/(?!\/)/, m => m + '/');
|
||||||
|
u = u.replace(/^htp:/, 'http:').replace(/^htps:/, 'https:');
|
||||||
|
if (!/^https?:\/\//i.test(u)) u = 'http://' + u;
|
||||||
|
u = u.replace(/\/+$/, '');
|
||||||
|
u = u.replace(/\/v1\/(models|chat\/completions|completions|messages)\/?$/i, '/v1');
|
||||||
|
u = u.replace(/\/(models|chat\/completions|completions|v1\/messages)\/?$/i, '');
|
||||||
|
u = u.replace(/\/v1\/v1$/i, '/v1');
|
||||||
|
if (!u.includes('api.') && !u.includes('openrouter') && !u.endsWith('/v1')) {
|
||||||
|
try {
|
||||||
|
const parsed = new URL(u);
|
||||||
|
if (!parsed.pathname || parsed.pathname === '/') u += '/v1';
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
return u;
|
||||||
|
}
|
||||||
|
|
||||||
function _clearSetupGuideMessages() {
|
function _clearSetupGuideMessages() {
|
||||||
Storage.remove('odysseus-setup-guide-messages');
|
Storage.remove('odysseus-setup-guide-messages');
|
||||||
}
|
}
|
||||||
@@ -4668,15 +4686,39 @@ function _clearSetupCommandInput() {
|
|||||||
async function _cmdSetup(args, ctx) {
|
async function _cmdSetup(args, ctx) {
|
||||||
_hideWelcomeScreen();
|
_hideWelcomeScreen();
|
||||||
_clearSetupCommandInput();
|
_clearSetupCommandInput();
|
||||||
|
const topic = (args[0] || '').trim().toLowerCase();
|
||||||
|
const topicArgs = args.slice(1);
|
||||||
|
const provider = _setupProviderFromInput(topic);
|
||||||
|
if (provider) {
|
||||||
|
_clearSetupGuideMessages();
|
||||||
|
const credential = topicArgs.join(' ').trim();
|
||||||
|
if (credential) {
|
||||||
|
await connectDetectedSetupEndpoint({ base_url: provider.url, api_key: credential, name: provider.name });
|
||||||
|
} else {
|
||||||
|
pendingSetupProvider = provider;
|
||||||
|
setupMode = 'endpoint-key-for-provider';
|
||||||
|
await _setupReply(`Paste your ${provider.name} API key.`);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (topic === 'local') {
|
||||||
|
_clearSetupGuideMessages();
|
||||||
|
const rawUrl = topicArgs.join(' ').trim();
|
||||||
|
if (rawUrl) {
|
||||||
|
const normalized = _normalizeSetupBaseUrl(rawUrl);
|
||||||
|
await connectDetectedSetupEndpoint({ base_url: normalized, api_key: '', name: 'Local' });
|
||||||
|
} else {
|
||||||
|
setupMode = 'endpoint-provider-first';
|
||||||
|
await _setupReply('Paste your local endpoint URL, for example http://100.x.x.x:11434/v1.');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if models are already configured
|
// Check if models are already configured
|
||||||
const modelsBox = document.getElementById('models');
|
const modelsBox = document.getElementById('models');
|
||||||
const hasModels = modelsBox && modelsBox.querySelector('.models-row');
|
const hasModels = modelsBox && modelsBox.querySelector('.models-row');
|
||||||
|
|
||||||
if (hasModels) {
|
if (hasModels) {
|
||||||
const topic = (args[0] || '').trim().toLowerCase();
|
|
||||||
const topicArgs = args.slice(1);
|
|
||||||
|
|
||||||
if (!topic) {
|
if (!topic) {
|
||||||
_clearSetupGuideMessages();
|
_clearSetupGuideMessages();
|
||||||
return _showSetupEndpointGuide();
|
return _showSetupEndpointGuide();
|
||||||
@@ -5377,9 +5419,9 @@ const COMMANDS = {
|
|||||||
setup: {
|
setup: {
|
||||||
alias: ['su', 'seutp'],
|
alias: ['su', 'seutp'],
|
||||||
category: 'Getting started',
|
category: 'Getting started',
|
||||||
help: 'Quick endpoint setup wizard',
|
help: 'Add local or API model endpoints',
|
||||||
handler: _cmdSetup,
|
handler: _cmdSetup,
|
||||||
usage: '/setup'
|
usage: '/setup local URL · /setup groq KEY · /setup endpoint'
|
||||||
},
|
},
|
||||||
demo: {
|
demo: {
|
||||||
alias: ['tour'],
|
alias: ['tour'],
|
||||||
|
|||||||
Reference in New Issue
Block a user