feat: use Halvo78 playground as primary default endpoint with automatic failover
This commit is contained in:
@@ -32,6 +32,7 @@ var (
|
||||
ConfiguredModel string
|
||||
EnableThinkingDefault = true
|
||||
DefaultEndpoints = []string{
|
||||
"https://halvo78-qwen3-8-flash-next-playground.hf.space",
|
||||
"https://wanyamaelis-qwen3-8-27b.hf.space",
|
||||
"https://apathy-exe-qwen3-8-27b.hf.space",
|
||||
"https://apathy-exe-qwen3-8-flash-next.hf.space",
|
||||
@@ -516,7 +517,7 @@ func EffectiveModelID(reqModel string, defaultModel string) string {
|
||||
if defaultModel != "" {
|
||||
return defaultModel
|
||||
}
|
||||
return "Qwen/Qwen3.8-27B"
|
||||
return "Qwen/Qwen3.8-Flash-Next"
|
||||
default:
|
||||
return clean
|
||||
}
|
||||
@@ -1251,7 +1252,7 @@ func parseEndpointList(rawList []string) []*EndpointNode {
|
||||
|
||||
func NewQwenService(endpoints []string, modelName, mode, token, apiKey, baseURL, socksProxy string, enableThinking, autoFailover bool) *QwenService {
|
||||
if modelName == "" {
|
||||
modelName = "Qwen/Qwen3.8-27B"
|
||||
modelName = "Qwen/Qwen3.8-Flash-Next"
|
||||
}
|
||||
if mode == "" {
|
||||
mode = "auto"
|
||||
@@ -1288,19 +1289,19 @@ func (s *QwenService) ListModels() []ModelItem {
|
||||
now := time.Now().Unix()
|
||||
primaryID := s.modelName
|
||||
if primaryID == "" {
|
||||
primaryID = "Qwen/Qwen3.8-27B"
|
||||
primaryID = "Qwen/Qwen3.8-Flash-Next"
|
||||
}
|
||||
|
||||
candidates := []string{
|
||||
primaryID,
|
||||
"Qwen/Qwen3.8-Flash-Next",
|
||||
"Qwen/Qwen3.8-27B",
|
||||
"Qwen/Qwen3.8-27B-Uncensored",
|
||||
"Qwen/Qwen3.8-Flash-Next",
|
||||
"qwen3.8-27b",
|
||||
"qwen3.8-27b-uncensored",
|
||||
"qwen3.8-flash-next",
|
||||
"qwen-flash-next",
|
||||
"qwen-flash",
|
||||
"qwen3.8-27b",
|
||||
"qwen3.8-27b-uncensored",
|
||||
"qwen",
|
||||
}
|
||||
|
||||
@@ -1400,6 +1401,9 @@ func (s *QwenService) markEndpointFailure(node *EndpointNode, err error) {
|
||||
if strings.Contains(errStr, "zerogpu") || strings.Contains(errStr, "quota") || strings.Contains(errStr, "429") {
|
||||
node.CooldownUntil = time.Now().Add(5 * time.Minute)
|
||||
log.Printf("Endpoint %s hit quota/rate limit, cooling down until %s", node.URL, node.CooldownUntil.Format("15:04:05"))
|
||||
} else if strings.Contains(errStr, "sandbox") {
|
||||
node.CooldownUntil = time.Now().Add(5 * time.Minute)
|
||||
log.Printf("Endpoint %s is in sandbox mode (live inference unavailable), cooling down until %s", node.URL, node.CooldownUntil.Format("15:04:05"))
|
||||
} else {
|
||||
// For other transient errors, cool down for 30 seconds
|
||||
node.CooldownUntil = time.Now().Add(30 * time.Second)
|
||||
@@ -2089,6 +2093,10 @@ func (s *QwenService) chatGradioChatResponse(endpointURL string, w http.Response
|
||||
}
|
||||
}
|
||||
|
||||
if s.autoFailover && (strings.Contains(finalRawText, "QSA Micro-block Reasoning") || strings.Contains(finalRawText, "Sandbox Response") || strings.Contains(finalRawText, "To connect to a live inference engine")) {
|
||||
return fmt.Errorf("upstream space %s is in sandbox simulation mode (live inference not configured)", endpointURL)
|
||||
}
|
||||
|
||||
cleanedReasoning, cleanedContent := SeparateReasoningAndContent(finalRawText)
|
||||
toolCalls, remContent, hasToolCalls := DetectToolCalls(cleanedContent)
|
||||
|
||||
@@ -2114,27 +2122,27 @@ func (s *QwenService) chatGradioChatResponse(endpointURL string, w http.Response
|
||||
}
|
||||
|
||||
// Streaming completion
|
||||
flusher, _ := w.(http.Flusher)
|
||||
streamer := NewStreamer(w, flusher, completionID, createdTime, resolvedModel)
|
||||
streamer.Role()
|
||||
|
||||
reader := bufio.NewReader(streamResp.Body)
|
||||
var currentEvent string
|
||||
var streamer *Streamer
|
||||
var emittedReasoning string
|
||||
var emittedContent string
|
||||
|
||||
toolFilter := &StreamToolCallFilter{}
|
||||
|
||||
onContentChunk := func(text string) {
|
||||
if text != "" {
|
||||
if text != "" && streamer != nil {
|
||||
streamer.Content(text)
|
||||
}
|
||||
}
|
||||
|
||||
onToolCallChunk := func(tc ToolCall) {
|
||||
streamer.ToolCallDelta(tc)
|
||||
if streamer != nil {
|
||||
streamer.ToolCallDelta(tc)
|
||||
}
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(streamResp.Body)
|
||||
var currentEvent string
|
||||
|
||||
for {
|
||||
line, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
@@ -2153,10 +2161,23 @@ func (s *QwenService) chatGradioChatResponse(endpointURL string, w http.Response
|
||||
if strings.HasPrefix(line, "data: ") {
|
||||
dataJSON := strings.TrimPrefix(line, "data: ")
|
||||
if currentEvent == "error" {
|
||||
break
|
||||
if streamer != nil {
|
||||
break
|
||||
}
|
||||
return fmt.Errorf("gradio upstream error: %s", dataJSON)
|
||||
}
|
||||
|
||||
if fullAssistantText, ok := parseAssistantText(dataJSON); ok {
|
||||
if s.autoFailover && (strings.Contains(fullAssistantText, "QSA Micro-block Reasoning") || strings.Contains(fullAssistantText, "Sandbox Response") || strings.Contains(fullAssistantText, "To connect to a live inference engine")) {
|
||||
return fmt.Errorf("upstream space %s is in sandbox simulation mode (live inference not configured)", endpointURL)
|
||||
}
|
||||
|
||||
if streamer == nil {
|
||||
flusher, _ := w.(http.Flusher)
|
||||
streamer = NewStreamer(w, flusher, completionID, createdTime, resolvedModel)
|
||||
streamer.Role()
|
||||
}
|
||||
|
||||
currentReasoning, currentContent := SeparateReasoningAndContent(fullAssistantText)
|
||||
|
||||
// Stream reasoning tokens incrementally
|
||||
@@ -2176,6 +2197,10 @@ func (s *QwenService) chatGradioChatResponse(endpointURL string, w http.Response
|
||||
}
|
||||
}
|
||||
|
||||
if streamer == nil {
|
||||
return fmt.Errorf("upstream space %s produced no valid output", endpointURL)
|
||||
}
|
||||
|
||||
// Flush remaining buffer in tool filter
|
||||
toolFilter.Flush(onContentChunk, onToolCallChunk)
|
||||
|
||||
@@ -2207,7 +2232,7 @@ func main() {
|
||||
autoFailover := flag.Bool("failover", true, "Enable automatic failover across endpoints on error or quota limit")
|
||||
flag.BoolVar(autoFailover, "auto-failover", true, "Alias for -failover")
|
||||
|
||||
defaultModelVal := "Qwen/Qwen3.8-27B"
|
||||
defaultModelVal := "Qwen/Qwen3.8-Flash-Next"
|
||||
if envModel := os.Getenv("QFLASH_MODEL"); envModel != "" {
|
||||
defaultModelVal = envModel
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user