File size: 8,692 Bytes
22cdcb2 6a7a454 6bfaeea 02f33b7 6bfaeea 02f33b7 6bfaeea 02f33b7 22cdcb2 6a7a454 22cdcb2 6bfaeea 02f33b7 22cdcb2 6a7a454 22cdcb2 fcc7f02 6bfaeea 22cdcb2 6a7a454 22cdcb2 6bfaeea 22cdcb2 fcc7f02 02f33b7 6bfaeea 02f33b7 6a7a454 02f33b7 6a7a454 02f33b7 6a7a454 02f33b7 bfe39d8 02f33b7 6bfaeea fcc7f02 02f33b7 6a7a454 02f33b7 fcc7f02 02f33b7 fcc7f02 02f33b7 fcc7f02 02f33b7 bfe39d8 02f33b7 bfe39d8 02f33b7 bfe39d8 02f33b7 bfe39d8 02f33b7 bfe39d8 02f33b7 bfe39d8 02f33b7 fcc7f02 6a7a454 bfe39d8 fcc7f02 6bfaeea fcc7f02 bfe39d8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | """
title: Coding Fallback Pipe
author: nerdur
author_url: https://nerdur-webui.hf.space
version: 4.2
description: |
Fallback redoslijed za kodiranje:
1. NVIDIA NIM (Qwen3-Coder, GLM-4.7)
2. Gemini 2.5 Flash
3. Groq (Kimi K2, DeepSeek R1, Llama 3.3)
4. Cerebras
5. SambaNova
Korisnik može napisati "next" ili "sljedeći" da dobije
odgovor od sljedećeg modela u listi.
"""
from pydantic import BaseModel
from typing import Optional, Union, Iterator
import requests
import os
import json
_last_model_index: dict = {}
# Max tokeni po modelu — svaki ima drugačiji limit
MODEL_MAX_TOKENS = {
"qwen/qwen3-coder-480b-a35b-instruct": 32768,
"zhipuai/glm-4.7": 8192,
"gemini-2.5-flash": 65536,
"moonshotai/kimi-k2-instruct": 32768,
"deepseek-r1-distill-llama-70b": 32768,
"llama-3.3-70b-versatile": 32768,
"llama-3.3-70b": 8192,
"Meta-Llama-3.3-70B-Instruct": 16384,
}
DEFAULT_MAX_TOKENS = 16384
class Pipe:
class Valves(BaseModel):
nvidia_api_key: str = ""
google_api_key: str = ""
groq_api_key: str = ""
cerebras_api_key: str = ""
sambanova_api_key: str = ""
enabled: bool = True
request_timeout: int = 120 # Povećano sa 45 na 120s
def __init__(self):
self.type = "pipe"
self.id = "coding_fallback"
self.name = "🔀 Smart Coding Router (Multi-Provider)"
self.valves = self.Valves()
def pipes(self):
return [{"id": self.id, "name": self.name}]
def _build_providers(self):
nvidia_key = self.valves.nvidia_api_key or os.getenv("NVIDIA_ID_API_KEY") or os.getenv("NVIDIA_API_KEY", "")
google_key = self.valves.google_api_key or os.getenv("GOOGLE_API_KEY", "")
groq_key = self.valves.groq_api_key or os.getenv("GROQ_API_KEY", "")
cerebras_key = self.valves.cerebras_api_key or os.getenv("CEREBRAS_API_KEY", "")
sambanova_key = self.valves.sambanova_api_key or os.getenv("SAMBANOVA_API_KEY", "")
providers = []
if nvidia_key:
for model in [
"qwen/qwen3-coder-480b-a35b-instruct",
"zhipuai/glm-4.7",
]:
providers.append(("NVIDIA", "https://integrate.api.nvidia.com/v1", model, nvidia_key))
if google_key:
providers.append(("Gemini", "https://generativelanguage.googleapis.com/v1beta/openai", "gemini-2.5-flash", google_key))
if groq_key:
for model in [
"moonshotai/kimi-k2-instruct",
"deepseek-r1-distill-llama-70b",
"llama-3.3-70b-versatile",
]:
providers.append(("Groq", "https://api.groq.com/openai/v1", model, groq_key))
if cerebras_key:
providers.append(("Cerebras", "https://api.cerebras.ai/v1", "llama-3.3-70b", cerebras_key))
if sambanova_key:
providers.append(("SambaNova", "https://api.sambanova.ai/v1", "Meta-Llama-3.3-70B-Instruct", sambanova_key))
return providers
def _call_model(self, provider_name, base_url, model_id, api_key, messages, stream, max_tokens, temperature):
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
# Koristi model-specifični limit, ali ne više od onoga što korisnik traži
model_limit = MODEL_MAX_TOKENS.get(model_id, DEFAULT_MAX_TOKENS)
actual_max_tokens = min(max_tokens, model_limit)
payload = {
"model": model_id,
"messages": messages,
"stream": stream,
"max_tokens": actual_max_tokens,
"temperature": temperature,
}
print(f"🔀 Coding Router [{provider_name}]: {model_id} (max_tokens={actual_max_tokens})...")
response = requests.post(
f"{base_url}/chat/completions",
headers=headers,
json=payload,
stream=stream,
timeout=self.valves.request_timeout,
)
if response.status_code in (429, 500, 502, 503, 529):
print(f"⚠️ [{provider_name}] {model_id} → HTTP {response.status_code}, prelazim...")
return None, f"HTTP {response.status_code}"
if response.status_code == 401:
print(f"⚠️ [{provider_name}] Neispravan API ključ")
return None, "401 Unauthorized"
response.raise_for_status()
if stream:
def generate(resp=response, pname=provider_name, mid=model_id):
print(f"✅ [{pname}] Streaming: {mid}")
for line in resp.iter_lines():
if line:
decoded = line.decode("utf-8")
if decoded.strip() == "data: [DONE]":
continue
yield decoded + "\n"
return generate(), None
else:
data = response.json()
content = data["choices"][0]["message"]["content"]
print(f"✅ [{provider_name}] Odgovor od: {model_id}")
return content, None
def pipe(self, body: dict, __user__: Optional[dict] = None) -> Union[str, Iterator]:
if not self.valves.enabled:
return "Router je isključen."
providers = self._build_providers()
if not providers:
return "❌ Nijedan API ključ nije postavljen. Provjeri Space Secrets."
messages = body.get("messages", [])
stream = body.get("stream", False)
# Korisnik može overridati, ali default je visok
max_tokens = body.get("max_tokens", DEFAULT_MAX_TOKENS)
temperature = body.get("temperature", 0.2)
user_id = (__user__ or {}).get("id", "default")
last_user_msg = ""
for msg in reversed(messages):
if msg.get("role") == "user":
last_user_msg = msg.get("content", "").strip().lower()
break
next_triggers = ["next", "sljedeći", "sledeci", "sljedeci", "drugi model",
"promjeni model", "probaj drugi", "skip", "next model"]
want_next = any(t in last_user_msg for t in next_triggers)
if want_next and user_id in _last_model_index:
start_index = _last_model_index[user_id] + 1
if start_index >= len(providers):
return "❌ Nema više modela u listi. Počinjem od početka!\n\nNapiši svoju poruku ponovo."
messages = [m for m in messages if not any(
t in m.get("content", "").lower() for t in next_triggers
)]
print(f"🔀 Korisnik traži sljedeći model, počinjem od indeksa {start_index}")
else:
start_index = 0
last_error = None
for i in range(start_index, len(providers)):
provider_name, base_url, model_id, api_key = providers[i]
try:
result, error = self._call_model(
provider_name, base_url, model_id, api_key,
messages, stream, max_tokens, temperature
)
if result is not None:
_last_model_index[user_id] = i
footer = f"\n\n`{provider_name} · {model_id}` — napiši **next** za drugi model"
if stream:
def stream_with_footer(gen=result, f=footer):
yield from gen
footer_chunk = json.dumps({
"choices": [{
"delta": {"content": f},
"finish_reason": None
}]
})
yield f"data: {footer_chunk}\n\n"
yield "data: [DONE]\n\n"
return stream_with_footer()
else:
return result + footer
last_error = error
if error == "401 Unauthorized":
while i + 1 < len(providers) and providers[i + 1][0] == provider_name:
i += 1
except requests.exceptions.Timeout:
print(f"⚠️ [{provider_name}] {model_id} timeout ({self.valves.request_timeout}s), prelazim...")
last_error = "timeout"
continue
except Exception as e:
print(f"⚠️ [{provider_name}] {model_id} greška: {e}")
last_error = str(e)
continue
return f"❌ Svi modeli neuspješni. Posljednja greška: {last_error}"
|