community fixes

This commit is contained in:
jaberjaber23
2026-03-15 05:57:59 +03:00
parent 7505007d8c
commit b3be3f4940
4 changed files with 72 additions and 13 deletions

View File

@@ -922,7 +922,23 @@
</select>
</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>
<template x-if="!editingModel">
<span>

View File

@@ -77,6 +77,8 @@ function agentsPage() {
// -- Model switch --
editingModel: false,
newModelValue: '',
editingProvider: false,
newProviderValue: '',
modelSaving: false,
// -- Fallback chain --
editingFallback: false,
@@ -638,6 +640,26 @@ function agentsPage() {
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 ──
async addFallback() {
if (!this.detailAgent || !this.newFallbackValue.trim()) return;

View File

@@ -5318,6 +5318,12 @@ fn infer_provider_from_model(model: &str) -> Option<String> {
(lower.as_str(), false)
};
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 {
"minimax" | "gemini" | "anthropic" | "openai" | "groq" | "deepseek" | "mistral"
| "cohere" | "xai" | "ollama" | "together" | "fireworks" | "perplexity"

View File

@@ -124,7 +124,9 @@ async function startConnection() {
if (msg.key.fromMe) 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
|| msg.message?.extendedTextMessage?.text
|| msg.message?.imageMessage?.caption
@@ -141,19 +143,32 @@ async function startConnection() {
else continue; // Only skip truly empty messages
}
// Extract phone number from JID (e.g. "1234567890@s.whatsapp.net" → "+1234567890")
const phone = '+' + sender.replace(/@.*$/, '');
// For groups: real sender is in participant; for DMs: it's remoteJid
const senderJid = isGroup ? (msg.key.participant || '') : remoteJid;
const phone = '+' + senderJid.replace(/@.*$/, '');
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
try {
const response = await forwardToOpenFang(text, phone, pushName);
const response = await forwardToOpenFang(text, phone, pushName, metadata);
if (response && sock) {
// Send agent response back to WhatsApp
await sock.sendMessage(sender, { text: response });
console.log(`[gateway] Replied to ${pushName}`);
// Reply in the same context: group → group, DM → DM
const replyJid = isGroup ? remoteJid : senderJid.replace(/@.*$/, '') + '@s.whatsapp.net';
await sock.sendMessage(replyJid, { text: response });
console.log(`[gateway] Replied to ${pushName}${isGroup ? ' in group ' + remoteJid : ''}`);
}
} catch (err) {
console.error(`[gateway] Forward/reply failed:`, err.message);
@@ -165,11 +180,11 @@ async function startConnection() {
// ---------------------------------------------------------------------------
// 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) => {
const payload = JSON.stringify({
message: text,
metadata: {
metadata: metadata || {
channel: 'whatsapp',
sender: phone,
sender_name: pushName,
@@ -223,8 +238,8 @@ async function sendMessage(to, text) {
throw new Error('WhatsApp not connected');
}
// Normalize phone → JID: "+1234567890" → "1234567890@s.whatsapp.net"
const jid = to.replace(/^\+/, '').replace(/@.*$/, '') + '@s.whatsapp.net';
// If already a full JID (group or user), use as-is; otherwise normalize phone → JID
const jid = to.includes('@') ? to : to.replace(/^\+/, '') + '@s.whatsapp.net';
await sock.sendMessage(jid, { text });
}