mirror of
https://github.com/RightNow-AI/openfang.git
synced 2026-04-25 17:25:11 +02:00
community fixes
This commit is contained in:
@@ -922,7 +922,23 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-row" x-show="detailAgent.profile"><span class="detail-label">Profile</span><span class="detail-value" style="text-transform:capitalize" x-text="detailAgent.profile || '-'"></span></div>
|
<div class="detail-row" x-show="detailAgent.profile"><span class="detail-label">Profile</span><span class="detail-value" style="text-transform:capitalize" x-text="detailAgent.profile || '-'"></span></div>
|
||||||
<div class="detail-row"><span class="detail-label">Provider</span><span class="detail-value" x-text="detailAgent.model_provider"></span></div>
|
<div class="detail-row"><span class="detail-label">Provider</span>
|
||||||
|
<template x-if="!editingProvider">
|
||||||
|
<span>
|
||||||
|
<span class="detail-value" x-text="detailAgent.model_provider"></span>
|
||||||
|
<button class="btn btn-ghost btn-sm" style="margin-left:8px;padding:2px 8px;font-size:11px" @click="editingProvider = true; newProviderValue = detailAgent.model_provider">Change</button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template x-if="editingProvider">
|
||||||
|
<span class="flex gap-1" style="align-items:center">
|
||||||
|
<input class="form-input" style="width:160px;font-size:12px" x-model="newProviderValue" placeholder="provider" @keydown.enter="changeProvider()" @keydown.escape="editingProvider = false">
|
||||||
|
<button class="btn btn-primary btn-sm" @click="changeProvider()" :disabled="modelSaving" style="padding:2px 10px">
|
||||||
|
<span x-show="!modelSaving">Save</span><span x-show="modelSaving">...</span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-ghost btn-sm" @click="editingProvider = false" style="padding:2px 8px">Cancel</button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
<div class="detail-row"><span class="detail-label">Model</span>
|
<div class="detail-row"><span class="detail-label">Model</span>
|
||||||
<template x-if="!editingModel">
|
<template x-if="!editingModel">
|
||||||
<span>
|
<span>
|
||||||
|
|||||||
@@ -77,6 +77,8 @@ function agentsPage() {
|
|||||||
// -- Model switch --
|
// -- Model switch --
|
||||||
editingModel: false,
|
editingModel: false,
|
||||||
newModelValue: '',
|
newModelValue: '',
|
||||||
|
editingProvider: false,
|
||||||
|
newProviderValue: '',
|
||||||
modelSaving: false,
|
modelSaving: false,
|
||||||
// -- Fallback chain --
|
// -- Fallback chain --
|
||||||
editingFallback: false,
|
editingFallback: false,
|
||||||
@@ -638,6 +640,26 @@ function agentsPage() {
|
|||||||
this.modelSaving = false;
|
this.modelSaving = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ── Provider switch ──
|
||||||
|
async changeProvider() {
|
||||||
|
if (!this.detailAgent || !this.newProviderValue.trim()) return;
|
||||||
|
this.modelSaving = true;
|
||||||
|
try {
|
||||||
|
var combined = this.newProviderValue.trim() + '/' + this.detailAgent.model_name;
|
||||||
|
var resp = await OpenFangAPI.put('/api/agents/' + this.detailAgent.id + '/model', { model: combined });
|
||||||
|
OpenFangToast.success('Provider changed to ' + (resp && resp.provider ? resp.provider : this.newProviderValue.trim()));
|
||||||
|
this.editingProvider = false;
|
||||||
|
await Alpine.store('app').refreshAgents();
|
||||||
|
var agents = Alpine.store('app').agents;
|
||||||
|
for (var i = 0; i < agents.length; i++) {
|
||||||
|
if (agents[i].id === this.detailAgent.id) { this.detailAgent = agents[i]; break; }
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
OpenFangToast.error('Failed to change provider: ' + e.message);
|
||||||
|
}
|
||||||
|
this.modelSaving = false;
|
||||||
|
},
|
||||||
|
|
||||||
// ── Fallback model chain ──
|
// ── Fallback model chain ──
|
||||||
async addFallback() {
|
async addFallback() {
|
||||||
if (!this.detailAgent || !this.newFallbackValue.trim()) return;
|
if (!this.detailAgent || !this.newFallbackValue.trim()) return;
|
||||||
|
|||||||
@@ -5318,6 +5318,12 @@ fn infer_provider_from_model(model: &str) -> Option<String> {
|
|||||||
(lower.as_str(), false)
|
(lower.as_str(), false)
|
||||||
};
|
};
|
||||||
if has_delim {
|
if has_delim {
|
||||||
|
// Two or more slashes (e.g. "mlx-lm-lg/mlx-community/Qwen3-4B") means
|
||||||
|
// the first segment is explicitly a provider prefix — HuggingFace repo
|
||||||
|
// IDs only have one slash, so extra slashes are unambiguous.
|
||||||
|
if lower.chars().filter(|&c| c == '/').count() >= 2 {
|
||||||
|
return Some(prefix.to_string());
|
||||||
|
}
|
||||||
match prefix {
|
match prefix {
|
||||||
"minimax" | "gemini" | "anthropic" | "openai" | "groq" | "deepseek" | "mistral"
|
"minimax" | "gemini" | "anthropic" | "openai" | "groq" | "deepseek" | "mistral"
|
||||||
| "cohere" | "xai" | "ollama" | "together" | "fireworks" | "perplexity"
|
| "cohere" | "xai" | "ollama" | "together" | "fireworks" | "perplexity"
|
||||||
|
|||||||
@@ -124,7 +124,9 @@ async function startConnection() {
|
|||||||
if (msg.key.fromMe) continue;
|
if (msg.key.fromMe) continue;
|
||||||
if (msg.key.remoteJid === 'status@broadcast') continue;
|
if (msg.key.remoteJid === 'status@broadcast') continue;
|
||||||
|
|
||||||
const sender = msg.key.remoteJid || '';
|
const remoteJid = msg.key.remoteJid || '';
|
||||||
|
const isGroup = remoteJid.endsWith('@g.us');
|
||||||
|
|
||||||
let text = msg.message?.conversation
|
let text = msg.message?.conversation
|
||||||
|| msg.message?.extendedTextMessage?.text
|
|| msg.message?.extendedTextMessage?.text
|
||||||
|| msg.message?.imageMessage?.caption
|
|| msg.message?.imageMessage?.caption
|
||||||
@@ -141,19 +143,32 @@ async function startConnection() {
|
|||||||
else continue; // Only skip truly empty messages
|
else continue; // Only skip truly empty messages
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract phone number from JID (e.g. "1234567890@s.whatsapp.net" → "+1234567890")
|
// For groups: real sender is in participant; for DMs: it's remoteJid
|
||||||
const phone = '+' + sender.replace(/@.*$/, '');
|
const senderJid = isGroup ? (msg.key.participant || '') : remoteJid;
|
||||||
|
const phone = '+' + senderJid.replace(/@.*$/, '');
|
||||||
const pushName = msg.pushName || phone;
|
const pushName = msg.pushName || phone;
|
||||||
|
|
||||||
console.log(`[gateway] Incoming from ${pushName} (${phone}): ${text.substring(0, 80)}`);
|
const metadata = {
|
||||||
|
channel: 'whatsapp',
|
||||||
|
sender: phone,
|
||||||
|
sender_name: pushName,
|
||||||
|
};
|
||||||
|
if (isGroup) {
|
||||||
|
metadata.group_jid = remoteJid;
|
||||||
|
metadata.is_group = true;
|
||||||
|
console.log(`[gateway] Group msg from ${pushName} (${phone}) in ${remoteJid}: ${text.substring(0, 80)}`);
|
||||||
|
} else {
|
||||||
|
console.log(`[gateway] Incoming from ${pushName} (${phone}): ${text.substring(0, 80)}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Forward to OpenFang agent
|
// Forward to OpenFang agent
|
||||||
try {
|
try {
|
||||||
const response = await forwardToOpenFang(text, phone, pushName);
|
const response = await forwardToOpenFang(text, phone, pushName, metadata);
|
||||||
if (response && sock) {
|
if (response && sock) {
|
||||||
// Send agent response back to WhatsApp
|
// Reply in the same context: group → group, DM → DM
|
||||||
await sock.sendMessage(sender, { text: response });
|
const replyJid = isGroup ? remoteJid : senderJid.replace(/@.*$/, '') + '@s.whatsapp.net';
|
||||||
console.log(`[gateway] Replied to ${pushName}`);
|
await sock.sendMessage(replyJid, { text: response });
|
||||||
|
console.log(`[gateway] Replied to ${pushName}${isGroup ? ' in group ' + remoteJid : ''}`);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`[gateway] Forward/reply failed:`, err.message);
|
console.error(`[gateway] Forward/reply failed:`, err.message);
|
||||||
@@ -165,11 +180,11 @@ async function startConnection() {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Forward incoming message to OpenFang API, return agent response
|
// Forward incoming message to OpenFang API, return agent response
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
function forwardToOpenFang(text, phone, pushName) {
|
function forwardToOpenFang(text, phone, pushName, metadata) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const payload = JSON.stringify({
|
const payload = JSON.stringify({
|
||||||
message: text,
|
message: text,
|
||||||
metadata: {
|
metadata: metadata || {
|
||||||
channel: 'whatsapp',
|
channel: 'whatsapp',
|
||||||
sender: phone,
|
sender: phone,
|
||||||
sender_name: pushName,
|
sender_name: pushName,
|
||||||
@@ -223,8 +238,8 @@ async function sendMessage(to, text) {
|
|||||||
throw new Error('WhatsApp not connected');
|
throw new Error('WhatsApp not connected');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize phone → JID: "+1234567890" → "1234567890@s.whatsapp.net"
|
// If already a full JID (group or user), use as-is; otherwise normalize phone → JID
|
||||||
const jid = to.replace(/^\+/, '').replace(/@.*$/, '') + '@s.whatsapp.net';
|
const jid = to.includes('@') ? to : to.replace(/^\+/, '') + '@s.whatsapp.net';
|
||||||
|
|
||||||
await sock.sendMessage(jid, { text });
|
await sock.sendMessage(jid, { text });
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user