2026-09-08 16:59:17 +03:00
// groqqer: Pure Go OpenAI-compatible LLM gateway for the Groq Streamlit space
// Reverse engineers https://dromerosm-groq-chatbot.hf.space via direct WebSocket Protobuf wire format
2026-09-08 15:12:35 +03:00
// Created by Luxferre in 2026, released into the public domain
package main
import (
"bufio"
2026-09-08 16:59:17 +03:00
"bytes"
2026-09-08 15:12:35 +03:00
"context"
"crypto/rand"
2026-09-08 16:59:17 +03:00
"crypto/tls"
2026-09-08 15:12:35 +03:00
"encoding/base64"
"encoding/binary"
"encoding/json"
2026-09-08 16:59:17 +03:00
"errors"
2026-09-08 15:12:35 +03:00
"flag"
"fmt"
"io"
"log"
"net"
"net/http"
2026-09-08 16:59:17 +03:00
"net/url"
2026-09-08 15:12:35 +03:00
"os"
"os/signal"
"regexp"
"strconv"
"strings"
"sync"
"syscall"
"time"
)
var (
DefaultUserAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
DefaultTargetURL = "https://dromerosm-groq-chatbot.hf.space"
2026-09-08 16:59:17 +03:00
DefaultModel = "qwen/qwen3.6-27b"
2026-09-08 15:12:35 +03:00
)
// ---------------------------------------------------------------------------
// OpenAI API Data Structures
// ---------------------------------------------------------------------------
type ModelItem struct {
ID string `json:"id"`
Object string `json:"object"`
Created int64 `json:"created"`
OwnedBy string `json:"owned_by"`
}
type ModelsResponse struct {
Object string `json:"object"`
Data [] ModelItem `json:"data"`
}
type ToolCallFunction struct {
Name string `json:"name"`
Arguments string `json:"arguments"`
}
type ToolCall struct {
Index * int `json:"index,omitempty"`
ID string `json:"id,omitempty"`
Type string `json:"type,omitempty"`
Function ToolCallFunction `json:"function"`
}
type Tool struct {
Type string `json:"type"`
Function interface {} `json:"function"`
}
type ChatMessage struct {
Role string `json:"role"`
Content interface {} `json:"content"`
ReasoningContent string `json:"reasoning_content,omitempty"`
ToolCalls [] ToolCall `json:"tool_calls,omitempty"`
ToolCallID string `json:"tool_call_id,omitempty"`
Name string `json:"name,omitempty"`
}
func ( m * ChatMessage ) GetContentString () string {
if m . Content == nil {
return ""
}
if str , ok := m . Content .( string ); ok {
return str
}
if parts , ok := m . Content .([] interface {}); ok {
var sb strings . Builder
for _ , p := range parts {
if str , ok := p .( string ); ok {
sb . WriteString ( str )
} else if itemMap , ok := p .( map [ string ] interface {}); ok {
if textVal , ok := itemMap [ "text" ].( string ); ok {
sb . WriteString ( textVal )
}
}
}
return sb . String ()
}
b , err := json . Marshal ( m . Content )
if err == nil {
return string ( b )
}
return fmt . Sprintf ( "%v" , m . Content )
}
type ChatCompletionRequest struct {
Model string `json:"model"`
Messages [] ChatMessage `json:"messages"`
Tools [] Tool `json:"tools,omitempty"`
ToolChoice interface {} `json:"tool_choice,omitempty"`
Stream bool `json:"stream"`
MaxTokens int `json:"max_tokens"`
MaxCompletionTokens int `json:"max_completion_tokens"`
ReasoningEffort string `json:"reasoning_effort,omitempty"`
}
type ChatCompletionResponseChoice struct {
Index int `json:"index"`
Message ChatMessage `json:"message"`
FinishReason string `json:"finish_reason"`
}
type Usage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
TotalTokens int `json:"total_tokens"`
}
type ChatCompletionResponse struct {
ID string `json:"id"`
Object string `json:"object"`
Created int64 `json:"created"`
Model string `json:"model"`
Choices [] ChatCompletionResponseChoice `json:"choices"`
Usage Usage `json:"usage"`
}
type StreamDelta struct {
Role string `json:"role,omitempty"`
Content string `json:"content,omitempty"`
ReasoningContent string `json:"reasoning_content,omitempty"`
ToolCalls [] ToolCall `json:"tool_calls,omitempty"`
}
type StreamChoice struct {
Index int `json:"index"`
Delta StreamDelta `json:"delta"`
FinishReason string `json:"finish_reason,omitempty"`
}
type StreamResponse struct {
ID string `json:"id"`
Object string `json:"object"`
Created int64 `json:"created"`
Model string `json:"model"`
Choices [] StreamChoice `json:"choices"`
}
// ---------------------------------------------------------------------------
// UUID & Utility Functions
// ---------------------------------------------------------------------------
func GenerateUUID () string {
var b [ 16 ] byte
_ , _ = rand . Read ( b [:])
b [ 6 ] = ( b [ 6 ] & 0x0f ) | 0x40
b [ 8 ] = ( b [ 8 ] & 0x3f ) | 0x80
return fmt . Sprintf ( "%08x-%04x-%04x-%04x-%012x" , b [ 0 : 4 ], b [ 4 : 6 ], b [ 6 : 8 ], b [ 8 : 10 ], b [ 10 : 16 ])
}
// ---------------------------------------------------------------------------
// SOCKS5 Proxy Client (RFC 1928 / RFC 1929)
// ---------------------------------------------------------------------------
type SOCKS5Config struct {
Address string
Username string
Password string
}
func ParseSOCKS5URL ( proxyURL string ) ( * SOCKS5Config , error ) {
cleanURL := strings . TrimSpace ( proxyURL )
if cleanURL == "" {
return nil , nil
}
if strings . HasPrefix ( cleanURL , "socks5://" ) {
cleanURL = strings . TrimPrefix ( cleanURL , "socks5://" )
} else if strings . HasPrefix ( cleanURL , "socks5h://" ) {
cleanURL = strings . TrimPrefix ( cleanURL , "socks5h://" )
}
cfg := & SOCKS5Config {}
if atIdx := strings . LastIndex ( cleanURL , "@" ); atIdx != - 1 {
userPass := cleanURL [: atIdx ]
cfg . Address = cleanURL [ atIdx + 1 :]
if colonIdx := strings . Index ( userPass , ":" ); colonIdx != - 1 {
cfg . Username = userPass [: colonIdx ]
cfg . Password = userPass [ colonIdx + 1 :]
} else {
cfg . Username = userPass
}
} else {
cfg . Address = cleanURL
}
if ! strings . Contains ( cfg . Address , ":" ) {
cfg . Address = cfg . Address + ":1080"
}
return cfg , nil
}
func DialSOCKS5 ( ctx context . Context , proxyURL , targetAddr string ) ( net . Conn , error ) {
cfg , err := ParseSOCKS5URL ( proxyURL )
if err != nil {
return nil , fmt . Errorf ( "invalid socks5 proxy configuration: %w" , err )
}
if cfg == nil {
var d net . Dialer
return d . DialContext ( ctx , "tcp" , targetAddr )
}
var d net . Dialer
conn , err := d . DialContext ( ctx , "tcp" , cfg . Address )
if err != nil {
return nil , fmt . Errorf ( "failed to connect to socks5 proxy at %s: %w" , cfg . Address , err )
}
deadline , ok := ctx . Deadline ()
if ! ok {
deadline = time . Now (). Add ( 30 * time . Second )
}
conn . SetDeadline ( deadline )
defer conn . SetDeadline ( time . Time {})
var greeting [] byte
if cfg . Username != "" {
greeting = [] byte { 0x05 , 0x02 , 0x00 , 0x02 }
} else {
greeting = [] byte { 0x05 , 0x01 , 0x00 }
}
if _ , err := conn . Write ( greeting ); err != nil {
conn . Close ()
return nil , fmt . Errorf ( "failed to write socks5 greeting: %w" , err )
}
resp := make ([] byte , 2 )
if _ , err := io . ReadFull ( conn , resp ); err != nil {
conn . Close ()
return nil , fmt . Errorf ( "failed to read socks5 greeting response: %w" , err )
}
if resp [ 0 ] != 0x05 {
conn . Close ()
return nil , fmt . Errorf ( "unsupported socks version: 0x%02x" , resp [ 0 ])
}
if resp [ 1 ] == 0x02 {
if cfg . Username == "" {
conn . Close ()
return nil , fmt . Errorf ( "socks5 proxy requires authentication, but no credentials provided" )
}
uLen := byte ( len ( cfg . Username ))
pLen := byte ( len ( cfg . Password ))
authReq := [] byte { 0x01 , uLen }
authReq = append ( authReq , [] byte ( cfg . Username ) ... )
authReq = append ( authReq , pLen )
authReq = append ( authReq , [] byte ( cfg . Password ) ... )
if _ , err := conn . Write ( authReq ); err != nil {
conn . Close ()
return nil , fmt . Errorf ( "failed to send socks5 authentication: %w" , err )
}
authResp := make ([] byte , 2 )
if _ , err := io . ReadFull ( conn , authResp ); err != nil {
conn . Close ()
return nil , fmt . Errorf ( "failed to read socks5 auth response: %w" , err )
}
if authResp [ 1 ] != 0x00 {
conn . Close ()
return nil , fmt . Errorf ( "socks5 authentication failed with status 0x%02x" , authResp [ 1 ])
}
} else if resp [ 1 ] != 0x00 {
conn . Close ()
return nil , fmt . Errorf ( "socks5 proxy rejected authentication methods: 0x%02x" , resp [ 1 ])
}
host , portStr , err := net . SplitHostPort ( targetAddr )
if err != nil {
conn . Close ()
return nil , fmt . Errorf ( "invalid target address %s: %w" , targetAddr , err )
}
port , err := strconv . Atoi ( portStr )
if err != nil || port < 1 || port > 65535 {
conn . Close ()
return nil , fmt . Errorf ( "invalid port in target address %s" , targetAddr )
}
reqBuf := [] byte { 0x05 , 0x01 , 0x00 }
ip := net . ParseIP ( host )
if ip4 := ip . To4 (); ip4 != nil {
reqBuf = append ( reqBuf , 0x01 )
reqBuf = append ( reqBuf , ip4 ... )
} else if ip6 := ip . To16 (); ip6 != nil {
reqBuf = append ( reqBuf , 0x04 )
reqBuf = append ( reqBuf , ip6 ... )
} else {
reqBuf = append ( reqBuf , 0x03 , byte ( len ( host )))
reqBuf = append ( reqBuf , [] byte ( host ) ... )
}
reqBuf = append ( reqBuf , byte ( port >> 8 ), byte ( port & 0xFF ))
if _ , err := conn . Write ( reqBuf ); err != nil {
conn . Close ()
return nil , fmt . Errorf ( "failed to send socks5 connect request: %w" , err )
}
respHdr := make ([] byte , 4 )
if _ , err := io . ReadFull ( conn , respHdr ); err != nil {
conn . Close ()
return nil , fmt . Errorf ( "failed to read socks5 connect response: %w" , err )
}
if respHdr [ 1 ] != 0x00 {
conn . Close ()
return nil , fmt . Errorf ( "socks5 connect failed with reply code 0x%02x" , respHdr [ 1 ])
}
switch respHdr [ 3 ] {
case 0x01 :
bnd := make ([] byte , 6 )
io . ReadFull ( conn , bnd )
case 0x03 :
lenBuf := make ([] byte , 1 )
io . ReadFull ( conn , lenBuf )
bnd := make ([] byte , int ( lenBuf [ 0 ]) + 2 )
io . ReadFull ( conn , bnd )
case 0x04 :
bnd := make ([] byte , 18 )
io . ReadFull ( conn , bnd )
}
return conn , nil
}
2026-09-08 16:59:17 +03:00
// ---------------------------------------------------------------------------
// Pure Go Protobuf Wire Formatter & Decoder
// ---------------------------------------------------------------------------
func encodeVarint ( val uint64 ) [] byte {
var buf [] byte
for val >= 0x80 {
buf = append ( buf , byte ( val | 0x80 ))
val >>= 7
}
buf = append ( buf , byte ( val ))
return buf
}
func encodeTag ( fieldNum int , wireType int ) [] byte {
return encodeVarint ( uint64 (( fieldNum << 3 ) | wireType ))
}
func encodeLengthDelimited ( fieldNum int , data [] byte ) [] byte {
tag := encodeTag ( fieldNum , 2 )
length := encodeVarint ( uint64 ( len ( data )))
res := append ( tag , length ... )
return append ( res , data ... )
}
func encodeString ( fieldNum int , str string ) [] byte {
return encodeLengthDelimited ( fieldNum , [] byte ( str ))
}
type ProtoField struct {
Tag int
WireType int
Varint uint64
Data [] byte
}
func decodeProtoFields ( data [] byte ) [] ProtoField {
var fields [] ProtoField
r := bytes . NewReader ( data )
for r . Len () > 0 {
rawTag , err := binary . ReadUvarint ( r )
if err != nil {
break
}
fieldNum := int ( rawTag >> 3 )
wireType := int ( rawTag & 0x07 )
pf := ProtoField { Tag : fieldNum , WireType : wireType }
switch wireType {
case 0 :
v , err := binary . ReadUvarint ( r )
if err != nil {
return fields
}
pf . Varint = v
case 1 :
buf := make ([] byte , 8 )
if _ , err := io . ReadFull ( r , buf ); err != nil {
return fields
}
pf . Data = buf
case 2 :
length , err := binary . ReadUvarint ( r )
if err != nil {
return fields
}
buf := make ([] byte , length )
if _ , err := io . ReadFull ( r , buf ); err != nil {
return fields
}
pf . Data = buf
case 5 :
buf := make ([] byte , 4 )
if _ , err := io . ReadFull ( r , buf ); err != nil {
return fields
}
pf . Data = buf
default :
return fields
}
fields = append ( fields , pf )
}
return fields
}
type ParsedForwardMsg struct {
IsFinished bool
DeltaPath [] uint64
Markdown string
ChatInputID string
SelectboxID string
Options [] string
AlertText string
}
func parseForwardMsg ( data [] byte ) ParsedForwardMsg {
var res ParsedForwardMsg
fields := decodeProtoFields ( data )
for _ , f := range fields {
if f . Tag == 6 && ( f . Varint == 0 || f . Varint == 1 || f . Varint == 3 || f . Varint == 4 || f . Varint == 5 ) {
res . IsFinished = true
}
if f . Tag == 2 && f . WireType == 2 { // ForwardMsgMetadata
for _ , mf := range decodeProtoFields ( f . Data ) {
if mf . Tag == 2 { // delta_path
if mf . WireType == 2 { // packed uint32
r := bytes . NewReader ( mf . Data )
for r . Len () > 0 {
v , _ := binary . ReadUvarint ( r )
res . DeltaPath = append ( res . DeltaPath , v )
}
} else if mf . WireType == 0 {
res . DeltaPath = append ( res . DeltaPath , mf . Varint )
}
}
}
}
if f . Tag == 5 && f . WireType == 2 { // Delta
dFields := decodeProtoFields ( f . Data )
for _ , df := range dFields {
if df . Tag == 3 && df . WireType == 2 { // Element new_element
eFields := decodeProtoFields ( df . Data )
for _ , ef := range eFields {
if ef . Tag == 29 && ef . WireType == 2 { // Markdown
mFields := decodeProtoFields ( ef . Data )
for _ , mf := range mFields {
if mf . Tag == 1 && mf . WireType == 2 {
res . Markdown = string ( mf . Data )
}
}
} else if ef . Tag == 49 && ef . WireType == 2 { // ChatInput
cFields := decodeProtoFields ( ef . Data )
for _ , cf := range cFields {
if cf . Tag == 1 && cf . WireType == 2 {
res . ChatInputID = string ( cf . Data )
}
}
} else if ef . Tag == 25 && ef . WireType == 2 { // Selectbox
sFields := decodeProtoFields ( ef . Data )
for _ , sf := range sFields {
if sf . Tag == 1 && sf . WireType == 2 {
res . SelectboxID = string ( sf . Data )
} else if sf . Tag == 4 && sf . WireType == 2 {
res . Options = append ( res . Options , string ( sf . Data ))
}
}
} else if ef . Tag == 30 && ef . WireType == 2 { // Alert
aFields := decodeProtoFields ( ef . Data )
for _ , af := range aFields {
if af . Tag == 1 && af . WireType == 2 {
res . AlertText = string ( af . Data )
}
}
}
}
}
}
}
}
return res
}
// ---------------------------------------------------------------------------
// RFC 6455 Pure Go WebSocket Client
// ---------------------------------------------------------------------------
func sendWSBinaryFrame ( conn io . Writer , payload [] byte ) error {
var header [] byte
header = append ( header , 0x82 ) // Binary frame (0x02) | FIN (0x80)
length := len ( payload )
maskKey := make ([] byte , 4 )
_ , _ = rand . Read ( maskKey )
if length < 126 {
header = append ( header , byte ( length | 0x80 ))
} else if length < 65536 {
header = append ( header , 126 | 0x80 )
var b [ 2 ] byte
binary . BigEndian . PutUint16 ( b [:], uint16 ( length ))
header = append ( header , b [:] ... )
} else {
header = append ( header , 127 | 0x80 )
var b [ 8 ] byte
binary . BigEndian . PutUint64 ( b [:], uint64 ( length ))
header = append ( header , b [:] ... )
}
header = append ( header , maskKey ... )
masked := make ([] byte , length )
for i := 0 ; i < length ; i ++ {
masked [ i ] = payload [ i ] ^ maskKey [ i % 4 ]
}
_ , err := conn . Write ( append ( header , masked ... ))
return err
}
func sendWSPongFrame ( conn io . Writer , payload [] byte ) error {
var header [] byte
header = append ( header , 0x8a ) // Pong (0x0A) | FIN (0x80)
length := len ( payload )
maskKey := make ([] byte , 4 )
_ , _ = rand . Read ( maskKey )
header = append ( header , byte ( length | 0x80 ))
header = append ( header , maskKey ... )
masked := make ([] byte , length )
for i := 0 ; i < length ; i ++ {
masked [ i ] = payload [ i ] ^ maskKey [ i % 4 ]
}
_ , err := conn . Write ( append ( header , masked ... ))
return err
}
func readWSFrame ( conn net . Conn , reader * bufio . Reader , timeout time . Duration ) ([] byte , byte , error ) {
if timeout > 0 {
_ = conn . SetReadDeadline ( time . Now (). Add ( timeout ))
} else {
_ = conn . SetReadDeadline ( time . Time {})
}
b1 , err := reader . ReadByte ()
if err != nil {
return nil , 0 , err
}
b2 , err := reader . ReadByte ()
if err != nil {
return nil , 0 , err
}
opcode := b1 & 0x0f
isMasked := ( b2 & 0x80 ) != 0
length := int ( b2 & 0x7f )
if length == 126 {
var ext uint16
if err := binary . Read ( reader , binary . BigEndian , & ext ); err != nil {
return nil , 0 , err
}
length = int ( ext )
} else if length == 127 {
var ext uint64
if err := binary . Read ( reader , binary . BigEndian , & ext ); err != nil {
return nil , 0 , err
}
length = int ( ext )
}
var maskKey [ 4 ] byte
if isMasked {
if _ , err := io . ReadFull ( reader , maskKey [:]); err != nil {
return nil , 0 , err
}
}
payload := make ([] byte , length )
if _ , err := io . ReadFull ( reader , payload ); err != nil {
return nil , 0 , err
}
if isMasked {
for i := 0 ; i < length ; i ++ {
payload [ i ] ^= maskKey [ i % 4 ]
}
}
return payload , opcode , nil
}
// ---------------------------------------------------------------------------
// Streamlit Session & Client
// ---------------------------------------------------------------------------
type StreamlitSession struct {
conn net . Conn
reader * bufio . Reader
chatInputID string
selectboxID string
options [] string
activeModel string
closed bool
mu sync . Mutex
}
func ( s * StreamlitSession ) Close () {
s . mu . Lock ()
defer s . mu . Unlock ()
if ! s . closed {
s . closed = true
if s . conn != nil {
_ = s . conn . Close ()
}
}
}
func dialStreamlitWebSocket ( ctx context . Context , targetURL , proxyURL , userAgent string ) ( net . Conn , * bufio . Reader , error ) {
u , err := url . Parse ( targetURL )
if err != nil {
return nil , nil , fmt . Errorf ( "invalid target url: %w" , err )
}
host := u . Hostname ()
port := u . Port ()
isTLS := u . Scheme == "https" || u . Scheme == "wss"
if port == "" {
if isTLS {
port = "443"
} else {
port = "80"
}
}
targetAddr := net . JoinHostPort ( host , port )
var rawConn net . Conn
if proxyURL != "" {
rawConn , err = DialSOCKS5 ( ctx , proxyURL , targetAddr )
} else {
var d net . Dialer
rawConn , err = d . DialContext ( ctx , "tcp" , targetAddr )
}
if err != nil {
return nil , nil , fmt . Errorf ( "failed to connect to %s: %w" , targetAddr , err )
}
var conn net . Conn = rawConn
if isTLS {
tlsConfig := & tls . Config {
ServerName : host ,
}
tlsConn := tls . Client ( rawConn , tlsConfig )
if err := tlsConn . HandshakeContext ( ctx ); err != nil {
rawConn . Close ()
return nil , nil , fmt . Errorf ( "tls handshake failed: %w" , err )
}
conn = tlsConn
}
keyBytes := make ([] byte , 16 )
_ , _ = rand . Read ( keyBytes )
secKey := base64 . StdEncoding . EncodeToString ( keyBytes )
originScheme := "https"
if ! isTLS {
originScheme = "http"
}
origin := fmt . Sprintf ( "%s://%s" , originScheme , host )
req := fmt . Sprintf (
"GET /_stcore/stream HTTP/1.1\r\n" +
"Host: %s\r\n" +
"Upgrade: websocket\r\n" +
"Connection: Upgrade\r\n" +
"Sec-WebSocket-Key: %s\r\n" +
"Sec-WebSocket-Version: 13\r\n" +
"Origin: %s\r\n" +
"User-Agent: %s\r\n\r\n" ,
host , secKey , origin , userAgent ,
)
if _ , err := conn . Write ([] byte ( req )); err != nil {
conn . Close ()
return nil , nil , fmt . Errorf ( "failed to write websocket handshake request: %w" , err )
}
reader := bufio . NewReader ( conn )
statusLine , err := reader . ReadString ( '\n' )
if err != nil || ! strings . Contains ( statusLine , "101" ) {
conn . Close ()
return nil , nil , fmt . Errorf ( "websocket handshake rejected: %s" , statusLine )
}
for {
line , err := reader . ReadString ( '\n' )
if err != nil || strings . TrimSpace ( line ) == "" {
break
}
}
return conn , reader , nil
}
func OpenStreamlitSession ( ctx context . Context , targetURL , proxyURL , userAgent string ) ( * StreamlitSession , error ) {
conn , reader , err := dialStreamlitWebSocket ( ctx , targetURL , proxyURL , userAgent )
if err != nil {
return nil , err
}
sess := & StreamlitSession {
conn : conn ,
reader : reader ,
}
// Initial rerun_script BackMsg (ClientState: query_string = "")
clientState := encodeString ( 1 , "" )
backMsg := encodeLengthDelimited ( 11 , clientState )
if err := sendWSBinaryFrame ( conn , backMsg ); err != nil {
sess . Close ()
return nil , fmt . Errorf ( "failed to send initial rerun BackMsg: %w" , err )
}
// Read frames until script_finished
for {
payload , opcode , err := readWSFrame ( conn , reader , 25 * time . Second )
if err != nil {
sess . Close ()
return nil , fmt . Errorf ( "error reading initial session frames: %w" , err )
}
if opcode == 0x09 { // Ping
_ = sendWSPongFrame ( conn , payload )
continue
}
parsed := parseForwardMsg ( payload )
if parsed . ChatInputID != "" {
sess . chatInputID = parsed . ChatInputID
}
if parsed . SelectboxID != "" {
sess . selectboxID = parsed . SelectboxID
sess . options = parsed . Options
}
if parsed . IsFinished {
break
}
}
if sess . chatInputID == "" {
// Default fallback chatInput widget ID if not matched in stream
sess . chatInputID = "$$ID-ae617304c8297cf4ddb1a23ee392ce5b-None"
}
if sess . selectboxID == "" {
sess . selectboxID = "$$ID-625687b40312ff8d73403e7858a2f552-chat_model_selector"
}
return sess , nil
}
func ( s * StreamlitSession ) SwitchModel ( modelID string ) error {
if modelID == "" || s . selectboxID == "" {
return nil
}
cleanModel := strings . TrimSpace ( modelID )
if idx := strings . Index ( cleanModel , " (" ); idx != - 1 {
cleanModel = strings . TrimSpace ( cleanModel [: idx ])
}
// Match model against options
matchedModel := cleanModel
for _ , opt := range s . options {
rawOpt := opt
if idx := strings . Index ( rawOpt , " (" ); idx != - 1 {
rawOpt = strings . TrimSpace ( rawOpt [: idx ])
}
if strings . EqualFold ( rawOpt , cleanModel ) || strings . Contains ( strings . ToLower ( rawOpt ), strings . ToLower ( cleanModel )) {
matchedModel = rawOpt
break
}
}
if s . activeModel == matchedModel {
return nil
}
// Streamlit Selectbox sends string_value (field 6) in WidgetState
sbWidget := append ( encodeString ( 1 , s . selectboxID ), encodeString ( 6 , matchedModel ) ... )
wsData := encodeLengthDelimited ( 1 , sbWidget )
wStates := encodeLengthDelimited ( 2 , wsData )
csData := append ( encodeString ( 1 , "" ), wStates ... )
switchBackMsg := encodeLengthDelimited ( 11 , csData )
if err := sendWSBinaryFrame ( s . conn , switchBackMsg ); err != nil {
return fmt . Errorf ( "failed to send model switch BackMsg: %w" , err )
}
for {
payload , opcode , err := readWSFrame ( s . conn , s . reader , 25 * time . Second )
if err != nil {
return fmt . Errorf ( "error reading model switch frames: %w" , err )
}
if opcode == 0x09 {
_ = sendWSPongFrame ( s . conn , payload )
continue
}
parsed := parseForwardMsg ( payload )
if parsed . ChatInputID != "" {
s . chatInputID = parsed . ChatInputID
}
if parsed . SelectboxID != "" {
s . selectboxID = parsed . SelectboxID
}
if parsed . IsFinished {
break
}
}
s . activeModel = matchedModel
return nil
}
type GatewayAlertError struct {
Alert string
IsRateLimit bool
RetryAfter int
}
func ( e * GatewayAlertError ) Error () string {
return e . Alert
}
func parseRetryAfter ( alert string ) int {
re := regexp . MustCompile ( `(?i)(?:retry\s+after|retry\s+in)\s+(\d+)` )
match := re . FindStringSubmatch ( alert )
if len ( match ) >= 2 {
sec , _ := strconv . Atoi ( match [ 1 ])
if sec > 0 {
return sec
}
}
return 6
}
func isRateLimit ( alert string ) bool {
lower := strings . ToLower ( alert )
return strings . Contains ( lower , "rate limit" ) || strings . Contains ( lower , "too many requests" ) || strings . Contains ( lower , "429" )
}
func resolveAlert ( alerts [] string ) ( alertText string , isRL bool , retryAfter int ) {
for _ , a := range alerts {
if isRateLimit ( a ) {
return a , true , parseRetryAfter ( a )
}
}
for _ , a := range alerts {
lower := strings . ToLower ( a )
if ! strings . Contains ( lower , "the last response did not finish" ) {
return a , false , 0
}
}
if len ( alerts ) > 0 {
return alerts [ len ( alerts ) - 1 ], false , 0
}
return "" , false , 0
}
func isStaticUIMarkdown ( text , promptText string ) bool {
trimmed := strings . TrimSpace ( text )
if trimmed == "" || trimmed == strings . TrimSpace ( promptText ) {
return true
}
staticPrefixes := [] string {
"Model:" ,
"Model :" ,
"- **Model ID:**" ,
"[Powered by Groq]" ,
"---" ,
"-----" ,
"Text models available" ,
"This model accepts and returns text" ,
"This model uses non-streaming responses" ,
"Select a vision-capable model" ,
"Attached document:" ,
"### Upload an Image" ,
"### Usage Summary" ,
"### Chat Interface" ,
"### Upload Context" ,
"### Comparison Context" ,
"A document is already attached" ,
"**Token Usage" ,
"**Latest API Rate Snapshot:**" ,
"**Request Max Tokens:**" ,
"**Conversation Total:**" ,
"**Important:**" ,
"Important:" ,
"Partial response: generation did not finish." ,
}
for _ , p := range staticPrefixes {
if strings . HasPrefix ( trimmed , p ) {
return true
}
}
if strings . Contains ( trimmed , "**Important:**" ) ||
strings . Contains ( trimmed , "Important:" ) ||
strings . Contains ( trimmed , "older messages were left out of this request" ) ||
strings . Contains ( trimmed , "Response limit adjusted to" ) {
return true
}
return false
}
func ( s * StreamlitSession ) SubmitPrompt ( promptText string , onChunk func ( delta string ), onAlert func ( alert string )) error {
// Construct BackMsg with chat_input_value and selected model in selectbox
chatInputVal := encodeString ( 1 , promptText )
ciWidget := append ( encodeString ( 1 , s . chatInputID ), encodeLengthDelimited ( 15 , chatInputVal ) ... )
var wsData [] byte
if s . selectboxID != "" && s . activeModel != "" {
sbWidget := append ( encodeString ( 1 , s . selectboxID ), encodeString ( 6 , s . activeModel ) ... )
wsData = append ( encodeLengthDelimited ( 1 , sbWidget ), encodeLengthDelimited ( 1 , ciWidget ) ... )
} else {
wsData = encodeLengthDelimited ( 1 , ciWidget )
}
wStates := encodeLengthDelimited ( 2 , wsData )
csData := append ( encodeString ( 1 , "" ), wStates ... )
promptBackMsg := encodeLengthDelimited ( 11 , csData )
if err := sendWSBinaryFrame ( s . conn , promptBackMsg ); err != nil {
return fmt . Errorf ( "failed to send prompt BackMsg: %w" , err )
}
lastLen := 0
assistantPathKey := ""
userEchoSeen := false
var alerts [] string
for {
payload , opcode , err := readWSFrame ( s . conn , s . reader , 90 * time . Second )
if err != nil {
return fmt . Errorf ( "connection closed during response: %w" , err )
}
if opcode == 0x09 { // Ping
_ = sendWSPongFrame ( s . conn , payload )
continue
}
parsed := parseForwardMsg ( payload )
if parsed . AlertText != "" {
alerts = append ( alerts , parsed . AlertText )
if onAlert != nil {
onAlert ( parsed . AlertText )
}
}
if parsed . Markdown != "" {
// Ignore sidebar elements (deltaPath[0] == 1)
if len ( parsed . DeltaPath ) > 0 && parsed . DeltaPath [ 0 ] == 1 {
continue
}
// In this Streamlit space:
// DeltaPath[0] == 0 is the main page.
// DeltaPath[1] <= 5 are the header widgets (0-4) and the user chat echo container (5).
// The assistant response is always DeltaPath[1] >= 6.
if len ( parsed . DeltaPath ) >= 2 && parsed . DeltaPath [ 0 ] == 0 && parsed . DeltaPath [ 1 ] <= 5 {
continue
}
// Metadata or captions inside message containers have index >= 1 (e.g. [0, 6, 1])
if len ( parsed . DeltaPath ) >= 3 && parsed . DeltaPath [ len ( parsed . DeltaPath ) - 1 ] != 0 {
continue
}
// Check if user echo arrived (fallback for environments without hierarchical delta paths)
trimmedMD := strings . TrimSpace ( parsed . Markdown )
trimmedPrompt := strings . TrimSpace ( promptText )
if ! userEchoSeen {
if trimmedMD == trimmedPrompt ||
strings . HasPrefix ( trimmedPrompt , trimmedMD ) ||
strings . HasPrefix ( trimmedMD , trimmedPrompt ) {
userEchoSeen = true
continue
}
}
// If text is not static UI, stream assistant delta
if ! isStaticUIMarkdown ( parsed . Markdown , promptText ) {
pathKey := fmt . Sprintf ( "%v" , parsed . DeltaPath )
if assistantPathKey == "" {
assistantPathKey = pathKey
}
if pathKey == assistantPathKey {
if len ( parsed . Markdown ) > lastLen {
delta := parsed . Markdown [ lastLen :]
lastLen = len ( parsed . Markdown )
if onChunk != nil {
onChunk ( delta )
}
}
}
}
}
if parsed . IsFinished {
break
}
}
if lastLen == 0 {
if alertText , isRL , retrySec := resolveAlert ( alerts ); alertText != "" {
return & GatewayAlertError {
Alert : alertText ,
IsRateLimit : isRL ,
RetryAfter : retrySec ,
}
}
return fmt . Errorf ( "empty assistant response received from space" )
}
return nil
}
2026-09-08 15:12:35 +03:00
// ---------------------------------------------------------------------------
// Tool and Message Processing
// ---------------------------------------------------------------------------
func BuildToolInstruction ( tools [] Tool , toolChoice interface {}) string {
if len ( tools ) == 0 {
return ""
}
toolsBytes , _ := json . Marshal ( tools )
var sb strings . Builder
sb . WriteString ( "\n\n# Tool Calling Instructions\nYou have access to the following functions:\n<tools>\n" )
sb . WriteString ( string ( toolsBytes ))
2026-09-08 16:59:17 +03:00
sb . WriteString ( "\n</tools>\n\n" )
sb . WriteString ( "When you need to call a function, respond ONLY with a <tool_call> block formatted exactly as follows:\n" )
sb . WriteString ( "<tool_call>\n{\"name\": \"<function-name>\", \"arguments\": {<args-json-object>}}\n</tool_call>\n\n" )
sb . WriteString ( "CRITICAL EXECUTION RULES:\n" )
sb . WriteString ( "1. If you invoke a tool, output ONLY the <tool_call> block. Do not write conversational greetings, explanations, or filler text outside the tags.\n" )
sb . WriteString ( "2. Put any reasoning or thought process inside <think>...</think> tags.\n" )
sb . WriteString ( "3. If multiple tools need to be called, output each inside its own <tool_call>...</tool_call> block.\n" )
sb . WriteString ( "4. When tool execution results are provided in <tool_response> blocks, inspect the output:\n" )
sb . WriteString ( " - If further steps or additional tools are needed to fulfill the user's request, emit the next <tool_call> block.\n" )
sb . WriteString ( " - If all needed information has been retrieved, synthesize the answers and deliver the final response to the user.\n" )
sb . WriteString ( " - Never stop or terminate the conversation prematurely while intermediate steps remain.\n" )
2026-09-08 15:12:35 +03:00
if toolChoice != nil {
if choiceStr , ok := toolChoice .( string ); ok {
if choiceStr == "required" {
sb . WriteString ( "\n\nYou MUST invoke at least one tool from the list above." )
}
} else if choiceMap , ok := toolChoice .( map [ string ] interface {}); ok {
if fnMap , ok := choiceMap [ "function" ].( map [ string ] interface {}); ok {
if fnName , ok := fnMap [ "name" ].( string ); ok && fnName != "" {
sb . WriteString ( fmt . Sprintf ( "\n\nYou MUST call the function %q." , fnName ))
}
}
}
}
return sb . String ()
}
2026-09-08 16:59:17 +03:00
func ExtractToolName ( fn interface {}) string {
if fn == nil {
return ""
}
if fnMap , ok := fn .( map [ string ] interface {}); ok {
if name , ok := fnMap [ "name" ].( string ); ok {
return strings . TrimSpace ( name )
}
}
if tcf , ok := fn .( ToolCallFunction ); ok {
return strings . TrimSpace ( tcf . Name )
}
b , err := json . Marshal ( fn )
if err == nil {
var m map [ string ] interface {}
if json . Unmarshal ( b , & m ) == nil {
if name , ok := m [ "name" ].( string ); ok {
return strings . TrimSpace ( name )
}
}
}
return ""
}
func GetAllowedToolNames ( tools [] Tool , toolChoice interface {}) map [ string ] bool {
if len ( tools ) == 0 {
return nil
}
if choiceStr , ok := toolChoice .( string ); ok {
if choiceStr == "none" {
return nil
}
}
if choiceMap , ok := toolChoice .( map [ string ] interface {}); ok {
if fnMap , ok := choiceMap [ "function" ].( map [ string ] interface {}); ok {
if name , ok := fnMap [ "name" ].( string ); ok && strings . TrimSpace ( name ) != "" {
return map [ string ] bool { strings . TrimSpace ( name ): true }
}
}
}
allowed := make ( map [ string ] bool )
for _ , t := range tools {
name := ExtractToolName ( t . Function )
if name != "" {
allowed [ name ] = true
}
}
if len ( allowed ) == 0 {
return nil
}
return allowed
}
var toolCallTagRegex = regexp . MustCompile ( `(?s)<tool_call>.*?</tool_call>` )
func stripToolCallTags ( s string ) string {
return strings . TrimSpace ( toolCallTagRegex . ReplaceAllString ( s , "" ))
}
2026-09-08 15:12:35 +03:00
func FormatPrompt ( req ChatCompletionRequest ) string {
toolInstruction := BuildToolInstruction ( req . Tools , req . ToolChoice )
2026-09-08 16:59:17 +03:00
// Build mapping from tool_call_id to function name
toolCallIDToName := make ( map [ string ] string )
for _ , msg := range req . Messages {
if msg . Role == "assistant" {
for _ , tc := range msg . ToolCalls {
if tc . ID != "" && tc . Function . Name != "" {
toolCallIDToName [ tc . ID ] = tc . Function . Name
}
}
}
}
// Find where trailing tool responses start (if any)
firstTrailingToolIdx := len ( req . Messages )
for i := len ( req . Messages ) - 1 ; i >= 0 ; i -- {
role := req . Messages [ i ]. Role
if role == "tool" || role == "function" {
firstTrailingToolIdx = i
} else {
break
}
}
2026-09-08 15:12:35 +03:00
var systemInstructions [] string
var historyTurns [] string
var currentTurn string
2026-09-08 16:59:17 +03:00
// Process messages before the trailing tool responses
endHistoryIdx := firstTrailingToolIdx
if firstTrailingToolIdx == len ( req . Messages ) && len ( req . Messages ) > 0 {
if req . Messages [ len ( req . Messages ) - 1 ]. Role == "user" {
endHistoryIdx = len ( req . Messages ) - 1
currentTurn = req . Messages [ len ( req . Messages ) - 1 ]. GetContentString ()
}
}
for i := 0 ; i < endHistoryIdx ; i ++ {
msg := req . Messages [ i ]
2026-09-08 15:12:35 +03:00
contentStr := msg . GetContentString ()
switch msg . Role {
case "system" :
if contentStr != "" {
systemInstructions = append ( systemInstructions , contentStr )
}
case "assistant" :
2026-09-08 16:59:17 +03:00
cleanAssistant := strings . TrimSpace ( contentStr )
if strings . HasPrefix ( cleanAssistant , "Model:" ) || strings . HasPrefix ( cleanAssistant , "Model :" ) {
cleanAssistant = ""
}
if len ( msg . ToolCalls ) > 0 {
cleanAssistant = stripToolCallTags ( cleanAssistant )
}
2026-09-08 15:12:35 +03:00
var sb strings . Builder
2026-09-08 16:59:17 +03:00
if cleanAssistant != "" {
sb . WriteString ( cleanAssistant )
2026-09-08 15:12:35 +03:00
}
for _ , tc := range msg . ToolCalls {
if sb . Len () > 0 {
sb . WriteString ( "\n" )
}
args := tc . Function . Arguments
if strings . TrimSpace ( args ) == "" {
args = "{}"
}
sb . WriteString ( fmt . Sprintf ( "<tool_call>\n{\"name\": %q, \"arguments\": %s}\n</tool_call>" , tc . Function . Name , args ))
}
2026-09-08 16:59:17 +03:00
if sb . Len () > 0 {
historyTurns = append ( historyTurns , "Assistant: " + sb . String ())
}
2026-09-08 15:12:35 +03:00
case "tool" , "function" :
toolName := msg . Name
2026-09-08 16:59:17 +03:00
if resolved , ok := toolCallIDToName [ msg . ToolCallID ]; ok && resolved != "" {
toolName = resolved
} else if toolName == "" {
2026-09-08 15:12:35 +03:00
toolName = msg . ToolCallID
}
var contentJSON [] byte
if json . Valid ([] byte ( contentStr )) {
contentJSON = [] byte ( contentStr )
} else {
contentJSON , _ = json . Marshal ( contentStr )
}
2026-09-08 16:59:17 +03:00
jsonStr := string ( contentJSON )
if len ( jsonStr ) > 2000 {
jsonStr = jsonStr [: 1800 ] + `"... [truncated]"`
2026-09-08 15:12:35 +03:00
}
2026-09-08 16:59:17 +03:00
var turnText string
if msg . ToolCallID != "" {
turnText = fmt . Sprintf ( "<tool_response>\n{\"name\": %q, \"tool_call_id\": %q, \"content\": %s}\n</tool_response>" , toolName , msg . ToolCallID , jsonStr )
} else {
turnText = fmt . Sprintf ( "<tool_response>\n{\"name\": %q, \"content\": %s}\n</tool_response>" , toolName , jsonStr )
}
historyTurns = append ( historyTurns , fmt . Sprintf ( "Tool Result (%s): %s" , toolName , turnText ))
2026-09-08 15:12:35 +03:00
case "user" :
2026-09-08 16:59:17 +03:00
historyTurns = append ( historyTurns , "User: " + contentStr )
2026-09-08 15:12:35 +03:00
}
}
2026-09-08 16:59:17 +03:00
// If we have trailing tool responses, group them together with a continuation directive
if firstTrailingToolIdx < len ( req . Messages ) {
var toolResSb strings . Builder
toolResSb . WriteString ( "[Tool Execution Results]\n" )
for i := firstTrailingToolIdx ; i < len ( req . Messages ); i ++ {
msg := req . Messages [ i ]
toolName := msg . Name
if resolved , ok := toolCallIDToName [ msg . ToolCallID ]; ok && resolved != "" {
toolName = resolved
} else if toolName == "" {
toolName = msg . ToolCallID
}
contentStr := msg . GetContentString ()
var contentJSON [] byte
if json . Valid ([] byte ( contentStr )) {
contentJSON = [] byte ( contentStr )
} else {
contentJSON , _ = json . Marshal ( contentStr )
}
jsonStr := string ( contentJSON )
if len ( jsonStr ) > 3500 {
jsonStr = jsonStr [: 3200 ] + `"... [truncated]"`
}
toolResSb . WriteString ( "<tool_response>\n" )
if msg . ToolCallID != "" {
toolResSb . WriteString ( fmt . Sprintf ( "{\"name\": %q, \"tool_call_id\": %q, \"content\": %s}\n" , toolName , msg . ToolCallID , jsonStr ))
} else {
toolResSb . WriteString ( fmt . Sprintf ( "{\"name\": %q, \"content\": %s}\n" , toolName , jsonStr ))
}
toolResSb . WriteString ( "</tool_response>\n" )
}
toolResSb . WriteString ( "\n[Next Steps Directive]\n" )
toolResSb . WriteString ( "You have received the results of the tool execution(s) above.\n" )
toolResSb . WriteString ( "- Analyze these results in the context of the user request and conversation history.\n" )
toolResSb . WriteString ( "- If additional tool calls are needed to complete the task, emit the next <tool_call> block immediately.\n" )
toolResSb . WriteString ( "- If all information needed to fulfill the request is now available, provide the final comprehensive response to the user.\n" )
toolResSb . WriteString ( "- Do not terminate or stop without answering the user or taking the next step." )
currentTurn = toolResSb . String ()
}
// Prune history turns if history exceeds budget (~12000 chars)
totalHistLen := 0
for _ , t := range historyTurns {
totalHistLen += len ( t )
}
if totalHistLen > 12000 && len ( historyTurns ) > 3 {
firstTurn := historyTurns [ 0 ]
var pruned [] string
pruned = append ( pruned , firstTurn )
pruned = append ( pruned , "[... earlier conversation turns omitted for brevity ...]" )
budget := 10000
var recent [] string
currentBudget := 0
for i := len ( historyTurns ) - 1 ; i >= 1 ; i -- {
tLen := len ( historyTurns [ i ])
if currentBudget + tLen > budget {
break
}
currentBudget += tLen
recent = append ([] string { historyTurns [ i ]}, recent ... )
}
pruned = append ( pruned , recent ... )
historyTurns = pruned
}
2026-09-08 15:12:35 +03:00
var promptBuilder strings . Builder
if len ( systemInstructions ) > 0 || toolInstruction != "" {
promptBuilder . WriteString ( "[System Instructions]\n" )
for _ , sys := range systemInstructions {
promptBuilder . WriteString ( sys )
promptBuilder . WriteString ( "\n" )
}
if toolInstruction != "" {
promptBuilder . WriteString ( strings . TrimSpace ( toolInstruction ))
promptBuilder . WriteString ( "\n" )
}
promptBuilder . WriteString ( "\n" )
}
if len ( historyTurns ) > 0 {
promptBuilder . WriteString ( "[Conversation History]\n" )
for _ , turn := range historyTurns {
promptBuilder . WriteString ( turn )
promptBuilder . WriteString ( "\n" )
}
promptBuilder . WriteString ( "\n" )
}
if currentTurn != "" {
2026-09-08 16:59:17 +03:00
if strings . HasPrefix ( currentTurn , "[Tool Execution Results]" ) {
promptBuilder . WriteString ( currentTurn )
} else {
if len ( historyTurns ) > 0 || len ( systemInstructions ) > 0 {
promptBuilder . WriteString ( "[User]\n" )
}
promptBuilder . WriteString ( currentTurn )
2026-09-08 15:12:35 +03:00
}
}
return strings . TrimSpace ( promptBuilder . String ())
}
func cleanJSONBlock ( input string ) string {
s := strings . TrimSpace ( input )
if strings . HasPrefix ( s , "```" ) {
lines := strings . Split ( s , "\n" )
if len ( lines ) >= 2 {
if strings . HasPrefix ( lines [ len ( lines ) - 1 ], "```" ) {
lines = lines [ 1 : len ( lines ) - 1 ]
} else {
lines = lines [ 1 :]
}
s = strings . TrimSpace ( strings . Join ( lines , "\n" ))
}
}
return s
}
func sanitizeJSONValue ( v interface {}) interface {} {
switch val := v .( type ) {
case string :
return strings . TrimSpace ( val )
case map [ string ] interface {}:
cleanMap := make ( map [ string ] interface {})
for k , childV := range val {
cleanKey := strings . TrimSpace ( k )
cleanMap [ cleanKey ] = sanitizeJSONValue ( childV )
}
return cleanMap
case [] interface {}:
cleanSlice := make ([] interface {}, len ( val ))
for i , childV := range val {
cleanSlice [ i ] = sanitizeJSONValue ( childV )
}
return cleanSlice
default :
return v
}
}
var toolNameRegex = regexp . MustCompile ( `"\s*(?:name|function|action|call)\s*"\s*:\s*"\s*([^"]+?)\s*"` )
func repairToolCallJSON ( jsonStr string ) ( ToolCall , bool ) {
nameMatch := toolNameRegex . FindStringSubmatch ( jsonStr )
if len ( nameMatch ) < 2 {
return ToolCall {}, false
}
nameVal := strings . TrimSpace ( nameMatch [ 1 ])
argsStr := "{}"
argsKwList := [] string { `"arguments"` , `" parameters "` , `"arguments "` , `" parameters"` , `"parameters"` , `"args"` , `"input"` }
argsIdx := - 1
for _ , kw := range argsKwList {
idx := strings . Index ( jsonStr , kw )
if idx >= 0 {
argsIdx = idx + len ( kw )
break
}
}
var targetStr string
if argsIdx >= 0 {
targetStr = strings . TrimSpace ( jsonStr [ argsIdx :])
if strings . HasPrefix ( targetStr , ":" ) {
targetStr = strings . TrimSpace ( targetStr [ 1 :])
}
} else {
targetStr = jsonStr
}
if strings . HasPrefix ( targetStr , "{" ) {
endIdx := strings . LastIndex ( targetStr , "}" )
if endIdx > 0 {
objCandidate := targetStr [: endIdx + 1 ]
var testMap map [ string ] interface {}
if json . Unmarshal ([] byte ( objCandidate ), & testMap ) == nil {
b , _ := json . Marshal ( sanitizeJSONValue ( testMap ))
return ToolCall {
ID : "call_" + GenerateUUID ()[: 8 ],
Type : "function" ,
Function : ToolCallFunction {
Name : nameVal ,
Arguments : string ( b ),
},
}, true
}
}
} else if strings . HasPrefix ( targetStr , `"` ) {
endIdx := strings . LastIndex ( targetStr , `"` )
if endIdx > 0 {
val := strings . TrimSpace ( targetStr [ 1 : endIdx ])
b , _ := json . Marshal ( val )
argsStr = string ( b )
}
}
return ToolCall {
ID : "call_" + GenerateUUID ()[: 8 ],
Type : "function" ,
Function : ToolCallFunction {
Name : nameVal ,
Arguments : argsStr ,
},
}, true
}
func parseSingleToolCall ( jsonStr string ) ( ToolCall , bool ) {
cleaned := cleanJSONBlock ( jsonStr )
var raw map [ string ] interface {}
if err := json . Unmarshal ([] byte ( cleaned ), & raw ); err == nil {
sanitizedRaw , ok := sanitizeJSONValue ( raw ).( map [ string ] interface {})
if ! ok {
sanitizedRaw = raw
}
for _ , wrapperKey := range [] string { "function" , "function_call" , "tool_call" } {
if fnObj , ok := sanitizedRaw [ wrapperKey ].( map [ string ] interface {}); ok {
if nameVal , ok := fnObj [ "name" ].( string ); ok && nameVal != "" {
argsStr := "{}"
var argsVal interface {}
if a , hasA := fnObj [ "arguments" ]; hasA {
argsVal = a
} else if p , hasP := fnObj [ "parameters" ]; hasP {
argsVal = p
} else if args , hasArgs := fnObj [ "args" ]; hasArgs {
argsVal = args
}
if argsVal != nil {
if s , isStr := argsVal .( string ); isStr {
var innerObj interface {}
if json . Unmarshal ([] byte ( s ), & innerObj ) == nil {
b , _ := json . Marshal ( sanitizeJSONValue ( innerObj ))
argsStr = string ( b )
} else {
argsStr = strings . TrimSpace ( s )
}
} else {
b , _ := json . Marshal ( sanitizeJSONValue ( argsVal ))
argsStr = string ( b )
}
}
return ToolCall {
ID : "call_" + GenerateUUID ()[: 8 ],
Type : "function" ,
Function : ToolCallFunction {
Name : nameVal ,
Arguments : argsStr ,
},
}, true
}
}
}
nameVal := ""
for _ , key := range [] string { "name" , "function" , "action" , "call" } {
if n , ok := sanitizedRaw [ key ].( string ); ok && n != "" {
nameVal = n
break
}
}
if nameVal != "" {
argsStr := "{}"
var argsVal interface {}
for _ , key := range [] string { "arguments" , "parameters" , "args" , "input" } {
if a , ok := sanitizedRaw [ key ]; ok {
argsVal = a
break
}
}
if argsVal != nil {
if s , isStr := argsVal .( string ); isStr {
var innerObj interface {}
if json . Unmarshal ([] byte ( s ), & innerObj ) == nil {
b , _ := json . Marshal ( sanitizeJSONValue ( innerObj ))
argsStr = string ( b )
} else {
argsStr = strings . TrimSpace ( s )
}
} else {
b , _ := json . Marshal ( sanitizeJSONValue ( argsVal ))
argsStr = string ( b )
}
} else {
argsMap := make ( map [ string ] interface {})
for k , v := range sanitizedRaw {
if k != "name" && k != "function" && k != "type" && k != "action" && k != "call" {
argsMap [ k ] = v
}
}
if len ( argsMap ) > 0 {
b , _ := json . Marshal ( sanitizeJSONValue ( argsMap ))
argsStr = string ( b )
}
}
return ToolCall {
ID : "call_" + GenerateUUID ()[: 8 ],
Type : "function" ,
Function : ToolCallFunction {
Name : nameVal ,
Arguments : argsStr ,
},
}, true
}
}
return repairToolCallJSON ( cleaned )
}
2026-09-08 16:59:17 +03:00
func parseToolCallsFromText ( input string ) ([] ToolCall , bool ) {
cleaned := cleanJSONBlock ( strings . TrimSpace ( input ))
if cleaned == "" {
return nil , false
}
// Check if it is a JSON array of tool calls: [...]
if strings . HasPrefix ( cleaned , "[" ) && strings . HasSuffix ( cleaned , "]" ) {
var rawSlice [] interface {}
if err := json . Unmarshal ([] byte ( cleaned ), & rawSlice ); err == nil && len ( rawSlice ) > 0 {
var calls [] ToolCall
for _ , item := range rawSlice {
b , _ := json . Marshal ( item )
if tc , ok := parseSingleToolCall ( string ( b )); ok {
calls = append ( calls , tc )
} else if tc , ok := repairToolCallJSON ( string ( b )); ok {
calls = append ( calls , tc )
}
}
if len ( calls ) > 0 {
return calls , true
}
}
}
// Check single tool call
if tc , ok := parseSingleToolCall ( cleaned ); ok {
return [] ToolCall { tc }, true
}
return nil , false
}
func parseXMLToolCall ( block string ) ([] ToolCall , bool ) {
2026-09-08 15:12:35 +03:00
inner := strings . TrimSpace ( block )
if strings . HasPrefix ( inner , "<tool_call>" ) {
inner = strings . TrimPrefix ( inner , "<tool_call>" )
}
if strings . HasSuffix ( inner , "</tool_call>" ) {
inner = strings . TrimSuffix ( inner , "</tool_call>" )
}
inner = cleanJSONBlock ( inner )
2026-09-08 16:59:17 +03:00
if calls , ok := parseToolCallsFromText ( inner ); ok {
return calls , true
2026-09-08 15:12:35 +03:00
}
var fnName string
if strings . Contains ( inner , "<name>" ) && strings . Contains ( inner , "</name>" ) {
nStart := strings . Index ( inner , "<name>" ) + len ( "<name>" )
nEnd := strings . Index ( inner , "</name>" )
if nStart < nEnd {
fnName = strings . TrimSpace ( inner [ nStart : nEnd ])
}
}
var argsStr string
if strings . Contains ( inner , "<arguments>" ) && strings . Contains ( inner , "</arguments>" ) {
aStart := strings . Index ( inner , "<arguments>" ) + len ( "<arguments>" )
aEnd := strings . Index ( inner , "</arguments>" )
if aStart < aEnd {
argsStr = strings . TrimSpace ( inner [ aStart : aEnd ])
}
}
if fnName != "" {
if argsStr == "" {
argsStr = "{}"
}
2026-09-08 16:59:17 +03:00
return [] ToolCall {
{
ID : "call_" + GenerateUUID ()[: 8 ],
Type : "function" ,
Function : ToolCallFunction {
Name : fnName ,
Arguments : argsStr ,
},
2026-09-08 15:12:35 +03:00
},
}, true
}
2026-09-08 16:59:17 +03:00
return nil , false
2026-09-08 15:12:35 +03:00
}
func ExtractToolCallBlocks ( content string ) ( blocks [] string , remaining string ) {
2026-09-08 16:59:17 +03:00
var b [] string
2026-09-08 15:12:35 +03:00
s := content
2026-09-08 16:59:17 +03:00
searchOffset := 0
var remainingBuilder strings . Builder
lastPos := 0
2026-09-08 15:12:35 +03:00
2026-09-08 16:59:17 +03:00
for {
relStart := strings . Index ( s [ searchOffset :], "<tool_call>" )
if relStart == - 1 {
break
}
startIdx := searchOffset + relStart
afterStart := startIdx + len ( "<tool_call>" )
2026-09-08 15:12:35 +03:00
2026-09-08 16:59:17 +03:00
relEnd := strings . Index ( s [ afterStart :], "</tool_call>" )
if relEnd == - 1 {
// Unclosed <tool_call>, do not treat as block
searchOffset = afterStart
continue
2026-09-08 15:12:35 +03:00
}
2026-09-08 16:59:17 +03:00
if innerNext := strings . Index ( s [ afterStart : afterStart + relEnd ], "<tool_call>" ); innerNext != - 1 {
searchOffset = afterStart + innerNext
continue
2026-09-08 15:12:35 +03:00
}
2026-09-08 16:59:17 +03:00
endIdx := afterStart + relEnd + len ( "</tool_call>" )
blockText := s [ startIdx : endIdx ]
b = append ( b , blockText )
2026-09-08 15:12:35 +03:00
2026-09-08 16:59:17 +03:00
if startIdx > lastPos {
remainingBuilder . WriteString ( s [ lastPos : startIdx ])
2026-09-08 15:12:35 +03:00
}
2026-09-08 16:59:17 +03:00
lastPos = endIdx
searchOffset = endIdx
2026-09-08 15:12:35 +03:00
}
2026-09-08 16:59:17 +03:00
if lastPos < len ( s ) {
remainingBuilder . WriteString ( s [ lastPos :])
2026-09-08 15:12:35 +03:00
}
2026-09-08 16:59:17 +03:00
return b , strings . TrimSpace ( remainingBuilder . String ())
2026-09-08 15:12:35 +03:00
}
2026-09-08 16:59:17 +03:00
func DetectToolCalls ( content string , allowedTools map [ string ] bool ) ([] ToolCall , string , bool ) {
if len ( allowedTools ) == 0 {
return nil , content , false
}
2026-09-08 15:12:35 +03:00
2026-09-08 16:59:17 +03:00
type validBlock struct {
startIdx int
endIdx int
calls [] ToolCall
}
var validBlocks [] validBlock
searchOffset := 0
for {
relStart := strings . Index ( content [ searchOffset :], "<tool_call>" )
if relStart == - 1 {
break
2026-09-08 15:12:35 +03:00
}
2026-09-08 16:59:17 +03:00
startIdx := searchOffset + relStart
afterStart := startIdx + len ( "<tool_call>" )
relEnd := strings . Index ( content [ afterStart :], "</tool_call>" )
if relEnd == - 1 {
// Unclosed <tool_call> tag - leave as plain content
searchOffset = afterStart
continue
}
if innerNext := strings . Index ( content [ afterStart : afterStart + relEnd ], "<tool_call>" ); innerNext != - 1 {
searchOffset = afterStart + innerNext
continue
}
endIdx := afterStart + relEnd + len ( "</tool_call>" )
blockText := content [ startIdx : endIdx ]
if parsedCalls , ok := parseXMLToolCall ( blockText ); ok {
var callsForBlock [] ToolCall
for _ , c := range parsedCalls {
if allowedTools [ c . Function . Name ] {
callsForBlock = append ( callsForBlock , c )
}
}
if len ( callsForBlock ) > 0 {
validBlocks = append ( validBlocks , validBlock {
startIdx : startIdx ,
endIdx : endIdx ,
calls : callsForBlock ,
})
searchOffset = endIdx
continue
}
}
// Not a valid tool call or function name not allowed - keep in content
searchOffset = endIdx
2026-09-08 15:12:35 +03:00
}
2026-09-08 16:59:17 +03:00
if len ( validBlocks ) > 0 {
var allCalls [] ToolCall
var remainingBuilder strings . Builder
lastPos := 0
for _ , vb := range validBlocks {
if vb . startIdx > lastPos {
remainingBuilder . WriteString ( content [ lastPos : vb . startIdx ])
}
allCalls = append ( allCalls , vb . calls ... )
lastPos = vb . endIdx
}
if lastPos < len ( content ) {
remainingBuilder . WriteString ( content [ lastPos :])
}
for i := range allCalls {
idx := i
allCalls [ i ]. Index = & idx
}
cleanRemaining := strings . TrimSpace ( remainingBuilder . String ())
return allCalls , cleanRemaining , true
2026-09-08 15:12:35 +03:00
}
2026-09-08 16:59:17 +03:00
// Direct JSON fallback (only if every parsed call is present in allowedTools)
trimmed := strings . TrimSpace ( content )
if ( strings . HasPrefix ( trimmed , "{" ) && strings . HasSuffix ( trimmed , "}" )) ||
( strings . HasPrefix ( trimmed , "[" ) && strings . HasSuffix ( trimmed , "]" )) ||
( strings . HasPrefix ( trimmed , "```json" ) && strings . HasSuffix ( trimmed , "```" )) {
if directCalls , ok := parseToolCallsFromText ( trimmed ); ok {
var validCalls [] ToolCall
for _ , c := range directCalls {
if allowedTools [ c . Function . Name ] {
validCalls = append ( validCalls , c )
}
}
if len ( validCalls ) > 0 && len ( validCalls ) == len ( directCalls ) {
for i := range validCalls {
idx := i
validCalls [ i ]. Index = & idx
}
return validCalls , "" , true
}
}
2026-09-08 15:12:35 +03:00
}
return nil , content , false
}
func ExtractThinking ( content string ) ( string , string ) {
if strings . Contains ( content , "<think>" ) && strings . Contains ( content , "</think>" ) {
start := strings . Index ( content , "<think>" )
end := strings . Index ( content , "</think>" )
if start < end {
reasoning := content [ start + len ( "<think>" ) : end ]
rem := content [: start ] + content [ end + len ( "</think>" ):]
rem = strings . TrimPrefix ( rem , "\n\n" )
rem = strings . TrimPrefix ( rem , "\n" )
return strings . TrimSpace ( rem ), strings . TrimSpace ( reasoning )
}
}
return content , ""
}
// ---------------------------------------------------------------------------
// Streaming Filters & Framing
// ---------------------------------------------------------------------------
type StreamThinkingFilter struct {
inThinking bool
buf string
}
func NewStreamThinkingFilter () * StreamThinkingFilter {
return & StreamThinkingFilter {}
}
func hasPrefixOf ( target string , prefixes [] string ) int {
for _ , p := range prefixes {
if strings . HasSuffix ( target , p ) {
return len ( p )
}
}
return 0
}
func ( f * StreamThinkingFilter ) Feed ( chunk string , onContent func ( string ), onReasoning func ( string )) {
f . buf += chunk
thinkStartTag := "<think>"
thinkEndTag := "</think>"
thinkStartPrefixes := [] string { "<" , "<t" , "<th" , "<thi" , "<thin" , "<think" }
thinkEndPrefixes := [] string { "<" , "</" , "</t" , "</th" , "</thi" , "</thin" , "</think" }
for len ( f . buf ) > 0 {
if ! f . inThinking {
if idx := strings . Index ( f . buf , thinkStartTag ); idx != - 1 {
before := f . buf [: idx ]
if before != "" {
onContent ( before )
}
f . inThinking = true
f . buf = f . buf [ idx + len ( thinkStartTag ):]
} else if matchLen := hasPrefixOf ( f . buf , thinkStartPrefixes ); matchLen > 0 {
safe := f . buf [: len ( f . buf ) - matchLen ]
if safe != "" {
onContent ( safe )
}
f . buf = f . buf [ len ( f . buf ) - matchLen :]
break
} else {
onContent ( f . buf )
f . buf = ""
break
}
} else {
if idx := strings . Index ( f . buf , thinkEndTag ); idx != - 1 {
before := f . buf [: idx ]
if before != "" {
onReasoning ( before )
}
f . inThinking = false
f . buf = f . buf [ idx + len ( thinkEndTag ):]
f . buf = strings . TrimPrefix ( f . buf , "\n\n" )
f . buf = strings . TrimPrefix ( f . buf , "\n" )
} else if matchLen := hasPrefixOf ( f . buf , thinkEndPrefixes ); matchLen > 0 {
safe := f . buf [: len ( f . buf ) - matchLen ]
if safe != "" {
onReasoning ( safe )
}
f . buf = f . buf [ len ( f . buf ) - matchLen :]
break
} else {
onReasoning ( f . buf )
f . buf = ""
break
}
}
}
}
func ( f * StreamThinkingFilter ) Flush ( onContent func ( string ), onReasoning func ( string )) {
if len ( f . buf ) > 0 {
if f . inThinking {
onReasoning ( f . buf )
} else {
onContent ( f . buf )
}
f . buf = ""
}
}
type StreamToolCallFilter struct {
2026-09-08 16:59:17 +03:00
allowedTools map [ string ] bool
inToolCall bool
buf string
toolCallBuf string
toolIndex int
emittedCall bool
preambleBuf string
2026-09-08 15:12:35 +03:00
}
2026-09-08 16:59:17 +03:00
func NewStreamToolCallFilter ( allowedTools map [ string ] bool ) * StreamToolCallFilter {
return & StreamToolCallFilter {
allowedTools : allowedTools ,
}
2026-09-08 15:12:35 +03:00
}
2026-09-08 16:59:17 +03:00
func ( f * StreamToolCallFilter ) appendPreambleOrContent ( text string , onContent func ( string )) {
if text == "" {
return
}
if f . emittedCall {
onContent ( text )
return
}
if len ( f . preambleBuf ) + len ( text ) < 128 && ! strings . Contains ( f . preambleBuf + text , "\n\n" ) {
f . preambleBuf += text
} else {
if f . preambleBuf != "" {
onContent ( f . preambleBuf )
f . preambleBuf = ""
}
onContent ( text )
}
}
func ( f * StreamToolCallFilter ) Feed ( chunk string , onContent func ( string ), onReasoning func ( string ), onToolCall func ( ToolCall )) {
if len ( f . allowedTools ) == 0 {
if chunk != "" {
onContent ( chunk )
}
return
}
2026-09-08 15:12:35 +03:00
f . buf += chunk
toolStartTag := "<tool_call>"
toolEndTag := "</tool_call>"
startPrefixes := [] string { "<" , "<t" , "<to" , "<too" , "<tool" , "<tool_" , "<tool_c" , "<tool_ca" , "<tool_cal" , "<tool_call" }
endPrefixes := [] string { "<" , "</" , "</t" , "</to" , "</too" , "</tool" , "</tool_" , "</tool_c" , "</tool_ca" , "</tool_cal" , "</tool_call" }
for len ( f . buf ) > 0 {
if ! f . inToolCall {
if idx := strings . Index ( f . buf , toolStartTag ); idx != - 1 {
before := f . buf [: idx ]
f . inToolCall = true
f . buf = f . buf [ idx + len ( toolStartTag ):]
2026-09-08 16:59:17 +03:00
f . preambleBuf += before
2026-09-08 15:12:35 +03:00
} else if matchLen := hasPrefixOf ( f . buf , startPrefixes ); matchLen > 0 {
safe := f . buf [: len ( f . buf ) - matchLen ]
f . buf = f . buf [ len ( f . buf ) - matchLen :]
2026-09-08 16:59:17 +03:00
f . appendPreambleOrContent ( safe , onContent )
2026-09-08 15:12:35 +03:00
break
} else {
2026-09-08 16:59:17 +03:00
safe := f . buf
2026-09-08 15:12:35 +03:00
f . buf = ""
2026-09-08 16:59:17 +03:00
f . appendPreambleOrContent ( safe , onContent )
2026-09-08 15:12:35 +03:00
break
}
} else {
if idx := strings . Index ( f . buf , toolEndTag ); idx != - 1 {
f . toolCallBuf += f . buf [: idx ]
f . buf = f . buf [ idx + len ( toolEndTag ):]
f . inToolCall = false
2026-09-08 16:59:17 +03:00
rawBlock := "<tool_call>" + f . toolCallBuf + "</tool_call>"
tcs , ok := parseXMLToolCall ( rawBlock )
var validCalls [] ToolCall
if ok {
for _ , tc := range tcs {
if f . allowedTools [ tc . Function . Name ] {
validCalls = append ( validCalls , tc )
}
}
}
if len ( validCalls ) > 0 {
if strings . TrimSpace ( f . preambleBuf ) != "" {
onReasoning ( f . preambleBuf )
}
f . preambleBuf = ""
for _ , tc := range validCalls {
idxCopy := f . toolIndex
tc . Index = & idxCopy
f . toolIndex ++
f . emittedCall = true
onToolCall ( tc )
}
2026-09-08 15:12:35 +03:00
} else {
2026-09-08 16:59:17 +03:00
// Tool call invalid or function name not allowed - flush as content
if f . preambleBuf != "" {
onContent ( f . preambleBuf )
f . preambleBuf = ""
}
onContent ( rawBlock )
2026-09-08 15:12:35 +03:00
}
f . toolCallBuf = ""
} else if matchLen := hasPrefixOf ( f . buf , endPrefixes ); matchLen > 0 {
safe := f . buf [: len ( f . buf ) - matchLen ]
f . toolCallBuf += safe
f . buf = f . buf [ len ( f . buf ) - matchLen :]
break
} else {
f . toolCallBuf += f . buf
f . buf = ""
break
}
}
}
}
2026-09-08 16:59:17 +03:00
func ( f * StreamToolCallFilter ) Flush ( onContent func ( string ), onReasoning func ( string ), onToolCall func ( ToolCall )) {
if len ( f . allowedTools ) == 0 {
if f . buf != "" {
onContent ( f . buf )
f . buf = ""
}
return
}
2026-09-08 15:12:35 +03:00
if f . inToolCall && len ( f . toolCallBuf ) > 0 {
2026-09-08 16:59:17 +03:00
rawBlock := "<tool_call>" + f . toolCallBuf + "</tool_call>"
tcs , ok := parseXMLToolCall ( rawBlock )
var validCalls [] ToolCall
if ok {
for _ , tc := range tcs {
if f . allowedTools [ tc . Function . Name ] {
validCalls = append ( validCalls , tc )
}
}
}
if len ( validCalls ) > 0 {
if strings . TrimSpace ( f . preambleBuf ) != "" {
onReasoning ( f . preambleBuf )
}
f . preambleBuf = ""
for _ , tc := range validCalls {
idxCopy := f . toolIndex
tc . Index = & idxCopy
f . toolIndex ++
f . emittedCall = true
onToolCall ( tc )
}
2026-09-08 15:12:35 +03:00
} else {
2026-09-08 16:59:17 +03:00
if f . preambleBuf != "" {
onContent ( f . preambleBuf )
f . preambleBuf = ""
}
2026-09-08 15:12:35 +03:00
onContent ( "<tool_call>" + f . toolCallBuf )
}
f . toolCallBuf = ""
2026-09-08 16:59:17 +03:00
f . inToolCall = false
}
if f . emittedCall {
if strings . TrimSpace ( f . preambleBuf ) != "" {
onReasoning ( f . preambleBuf )
}
f . preambleBuf = ""
} else {
if f . preambleBuf != "" {
onContent ( f . preambleBuf )
f . preambleBuf = ""
}
2026-09-08 15:12:35 +03:00
}
if len ( f . buf ) > 0 {
onContent ( f . buf )
f . buf = ""
}
}
type Streamer struct {
w http . ResponseWriter
flusher http . Flusher
id string
created int64
model string
}
func NewStreamer ( w http . ResponseWriter , flusher http . Flusher , id string , created int64 , model string ) * Streamer {
w . Header (). Set ( "Content-Type" , "text/event-stream" )
w . Header (). Set ( "Cache-Control" , "no-cache" )
w . Header (). Set ( "Connection" , "keep-alive" )
return & Streamer { w : w , flusher : flusher , id : id , created : created , model : model }
}
func ( s * Streamer ) Role () {
sendStreamDelta ( s . w , s . flusher , s . id , s . created , s . model , StreamDelta { Role : "assistant" }, "" )
}
func ( s * Streamer ) Reasoning ( text string ) {
if text != "" {
sendStreamDelta ( s . w , s . flusher , s . id , s . created , s . model , StreamDelta { ReasoningContent : text }, "" )
}
}
func ( s * Streamer ) Content ( text string ) {
if text != "" {
sendStreamDelta ( s . w , s . flusher , s . id , s . created , s . model , StreamDelta { Content : text }, "" )
}
}
func ( s * Streamer ) ToolCall ( tc ToolCall ) {
sendStreamDelta ( s . w , s . flusher , s . id , s . created , s . model , StreamDelta { ToolCalls : [] ToolCall { tc }}, "" )
}
func ( s * Streamer ) Finish ( reason string ) {
sendStreamDelta ( s . w , s . flusher , s . id , s . created , s . model , StreamDelta {}, reason )
fmt . Fprintf ( s . w , "data: [DONE]\n\n" )
if s . flusher != nil {
s . flusher . Flush ()
}
}
func sendStreamDelta ( w http . ResponseWriter , flusher http . Flusher , id string , created int64 , model string , delta StreamDelta , finishReason string ) {
chunk := StreamResponse {
ID : id ,
Object : "chat.completion.chunk" ,
Created : created ,
Model : model ,
Choices : [] StreamChoice {
{
Index : 0 ,
Delta : delta ,
FinishReason : finishReason ,
},
},
}
b , _ := json . Marshal ( chunk )
fmt . Fprintf ( w , "data: %s\n\n" , b )
if flusher != nil {
flusher . Flush ()
}
}
func WriteCompletionResponse ( w http . ResponseWriter , completionID string , created int64 , model string , content string , reasoning string , toolCalls [] ToolCall , finishReason string ) {
if finishReason == "" {
finishReason = "stop"
}
var contentVal interface {} = content
2026-09-08 16:59:17 +03:00
if len ( toolCalls ) > 0 {
finishReason = "tool_calls"
if reasoning == "" && strings . TrimSpace ( content ) != "" {
reasoning = strings . TrimSpace ( content )
}
2026-09-08 15:12:35 +03:00
contentVal = nil
}
resp := ChatCompletionResponse {
ID : completionID ,
Object : "chat.completion" ,
Created : created ,
Model : model ,
Choices : [] ChatCompletionResponseChoice {
{
Index : 0 ,
Message : ChatMessage {
Role : "assistant" ,
Content : contentVal ,
ReasoningContent : reasoning ,
ToolCalls : toolCalls ,
},
FinishReason : finishReason ,
},
},
Usage : Usage {
PromptTokens : 0 ,
CompletionTokens : 0 ,
TotalTokens : 0 ,
},
}
w . Header (). Set ( "Content-Type" , "application/json" )
json . NewEncoder ( w ). Encode ( resp )
}
2026-09-08 16:59:17 +03:00
type APIErrorDetail struct {
Message string `json:"message"`
Type string `json:"type"`
Param * string `json:"param"`
Code int `json:"code"`
2026-09-08 15:12:35 +03:00
}
2026-09-08 16:59:17 +03:00
type APIErrorResponse struct {
Error APIErrorDetail `json:"error"`
2026-09-08 15:12:35 +03:00
}
2026-09-08 16:59:17 +03:00
func WriteAPIError ( w http . ResponseWriter , status int , message string , errType string , code int ) {
w . Header (). Set ( "Content-Type" , "application/json" )
w . WriteHeader ( status )
resp := APIErrorResponse {
Error : APIErrorDetail {
Message : message ,
Type : errType ,
Code : code ,
},
2026-09-08 15:12:35 +03:00
}
2026-09-08 16:59:17 +03:00
_ = json . NewEncoder ( w ). Encode ( resp )
2026-09-08 15:12:35 +03:00
}
// ---------------------------------------------------------------------------
// Gateway Controller & Request Orchestration
// ---------------------------------------------------------------------------
type GroqqerGateway struct {
2026-09-08 16:59:17 +03:00
targetURL string
proxyURL string
userAgent string
defaultModel string
port int
models [] ModelItem
modelsMu sync . RWMutex
2026-09-08 15:12:35 +03:00
}
func DefaultFallbackModels () [] string {
return [] string {
"qwen/qwen3.6-27b" ,
"qwen/qwen3.8-27b" ,
2026-09-08 16:59:17 +03:00
"openai/gpt-oss-120b" ,
"openai/gpt-oss-20b" ,
2026-09-08 15:12:35 +03:00
"groq/compound" ,
"groq/compound-mini" ,
"allam-2-7b" ,
"llama-3.3-70b-versatile" ,
"llama-3.1-8b-instant" ,
"meta-llama/llama-4-scout-17b-16e-instruct" ,
}
}
func ( g * GroqqerGateway ) RefreshModels () {
2026-09-08 16:59:17 +03:00
ctx , cancel := context . WithTimeout ( context . Background (), 20 * time . Second )
defer cancel ()
2026-09-08 15:12:35 +03:00
2026-09-08 16:59:17 +03:00
sess , err := OpenStreamlitSession ( ctx , g . targetURL , g . proxyURL , g . userAgent )
if err != nil {
log . Printf ( "warning: model discovery session failed: %v" , err )
if len ( g . GetModels ()) == 0 {
g . setFallbackModels ()
}
return
}
defer sess . Close ()
2026-09-08 15:12:35 +03:00
now := time . Now (). Unix ()
var items [] ModelItem
2026-09-08 16:59:17 +03:00
for _ , opt := range sess . options {
rawOpt := opt
if idx := strings . Index ( rawOpt , " (" ); idx != - 1 {
rawOpt = strings . TrimSpace ( rawOpt [: idx ])
}
if rawOpt != "" {
items = append ( items , ModelItem {
ID : rawOpt ,
Object : "model" ,
Created : now ,
OwnedBy : "groq" ,
})
}
}
if len ( items ) == 0 {
g . setFallbackModels ()
return
}
g . modelsMu . Lock ()
g . models = items
g . modelsMu . Unlock ()
}
func ( g * GroqqerGateway ) setFallbackModels () {
g . modelsMu . Lock ()
defer g . modelsMu . Unlock ()
now := time . Now (). Unix ()
var items [] ModelItem
for _ , id := range DefaultFallbackModels () {
2026-09-08 15:12:35 +03:00
items = append ( items , ModelItem {
ID : id ,
Object : "model" ,
Created : now ,
OwnedBy : "groq" ,
})
}
g . models = items
}
func ( g * GroqqerGateway ) GetModels () [] ModelItem {
g . modelsMu . RLock ()
defer g . modelsMu . RUnlock ()
if len ( g . models ) == 0 {
now := time . Now (). Unix ()
var items [] ModelItem
for _ , id := range DefaultFallbackModels () {
items = append ( items , ModelItem {
ID : id ,
Object : "model" ,
Created : now ,
OwnedBy : "groq" ,
})
}
return items
}
out := make ([] ModelItem , len ( g . models ))
copy ( out , g . models )
return out
}
2026-09-08 16:59:17 +03:00
func ( g * GroqqerGateway ) MatchModel ( requestedModel string ) string {
if requestedModel == "" {
return g . defaultModel
}
models := g . GetModels ()
for _ , m := range models {
if strings . EqualFold ( m . ID , requestedModel ) {
return m . ID
}
}
// Try suffix match e.g. "qwen3.6-27b" for "qwen/qwen3.6-27b"
cleanReq := strings . ToLower ( strings . TrimSpace ( requestedModel ))
if strings . Contains ( cleanReq , "/" ) {
parts := strings . Split ( cleanReq , "/" )
cleanReq = parts [ len ( parts ) - 1 ]
}
for _ , m := range models {
if strings . Contains ( strings . ToLower ( m . ID ), cleanReq ) {
return m . ID
}
}
return requestedModel
}
2026-09-08 15:12:35 +03:00
func ( g * GroqqerGateway ) HandleModels ( w http . ResponseWriter , r * http . Request ) {
w . Header (). Set ( "Access-Control-Allow-Origin" , "*" )
w . Header (). Set ( "Access-Control-Allow-Methods" , "GET, POST, OPTIONS" )
w . Header (). Set ( "Access-Control-Allow-Headers" , "Authorization, Content-Type" )
if r . Method == "OPTIONS" {
w . WriteHeader ( http . StatusOK )
return
}
models := g . GetModels ()
resp := ModelsResponse {
Object : "list" ,
Data : models ,
}
w . Header (). Set ( "Content-Type" , "application/json" )
json . NewEncoder ( w ). Encode ( resp )
}
func ( g * GroqqerGateway ) HandleChatCompletions ( w http . ResponseWriter , r * http . Request ) {
w . Header (). Set ( "Access-Control-Allow-Origin" , "*" )
w . Header (). Set ( "Access-Control-Allow-Methods" , "GET, POST, OPTIONS" )
w . Header (). Set ( "Access-Control-Allow-Headers" , "Authorization, Content-Type" )
if r . Method == "OPTIONS" {
w . WriteHeader ( http . StatusOK )
return
}
if r . Method != "POST" {
2026-09-08 16:59:17 +03:00
WriteAPIError ( w , http . StatusMethodNotAllowed , "Method not allowed" , "invalid_request_error" , 405 )
2026-09-08 15:12:35 +03:00
return
}
var req ChatCompletionRequest
if err := json . NewDecoder ( r . Body ). Decode ( & req ); err != nil {
2026-09-08 16:59:17 +03:00
WriteAPIError ( w , http . StatusBadRequest , "Invalid JSON payload: " + err . Error (), "invalid_request_error" , 400 )
2026-09-08 15:12:35 +03:00
return
}
if len ( req . Messages ) == 0 {
2026-09-08 16:59:17 +03:00
WriteAPIError ( w , http . StatusBadRequest , "messages array must not be empty" , "invalid_request_error" , 400 )
2026-09-08 15:12:35 +03:00
return
}
2026-09-08 16:59:17 +03:00
requestedModel := g . MatchModel ( req . Model )
2026-09-08 15:12:35 +03:00
promptText := FormatPrompt ( req )
completionID := "chatcmpl-" + GenerateUUID ()
created := time . Now (). Unix ()
2026-09-08 16:59:17 +03:00
allowedTools := GetAllowedToolNames ( req . Tools , req . ToolChoice )
2026-09-08 15:12:35 +03:00
log . Printf ( "Handling completion: model=%s, stream=%v, messages=%d" , requestedModel , req . Stream , len ( req . Messages ))
2026-09-08 16:59:17 +03:00
ctx , cancel := context . WithTimeout ( r . Context (), 180 * time . Second )
defer cancel ()
2026-09-08 15:12:35 +03:00
2026-09-08 16:59:17 +03:00
start := time . Now ()
maxAttempts := 3
2026-09-08 15:12:35 +03:00
2026-09-08 16:59:17 +03:00
for attempt := 0 ; attempt < maxAttempts ; attempt ++ {
sess , err := OpenStreamlitSession ( ctx , g . targetURL , g . proxyURL , g . userAgent )
if err != nil {
log . Printf ( "session connection error (attempt %d): %v" , attempt + 1 , err )
if attempt < maxAttempts - 1 {
time . Sleep ( 1 * time . Second )
continue
}
WriteAPIError ( w , http . StatusBadGateway , "Failed to connect to target space: " + err . Error (), "api_error" , 502 )
2026-09-08 15:12:35 +03:00
return
}
2026-09-08 16:59:17 +03:00
if err := sess . SwitchModel ( requestedModel ); err != nil {
log . Printf ( "model switch warning: %v" , err )
}
2026-09-08 15:12:35 +03:00
2026-09-08 16:59:17 +03:00
effectiveModel := sess . activeModel
if effectiveModel == "" {
effectiveModel = requestedModel
}
2026-09-08 15:12:35 +03:00
2026-09-08 16:59:17 +03:00
if req . Stream {
flusher , ok := w .( http . Flusher )
if ! ok {
sess . Close ()
WriteAPIError ( w , http . StatusInternalServerError , "Streaming unsupported" , "api_error" , 500 )
return
2026-09-08 15:12:35 +03:00
}
2026-09-08 16:59:17 +03:00
var streamer * Streamer
var thinkingFilter * StreamThinkingFilter
var toolFilter * StreamToolCallFilter
var streamStarted bool
var alertMsg string
2026-09-08 15:12:35 +03:00
2026-09-08 16:59:17 +03:00
initStreamer := func () {
if ! streamStarted {
streamer = NewStreamer ( w , flusher , completionID , created , effectiveModel )
streamer . Role ()
thinkingFilter = NewStreamThinkingFilter ()
toolFilter = NewStreamToolCallFilter ( allowedTools )
streamStarted = true
2026-09-08 15:12:35 +03:00
}
2026-09-08 16:59:17 +03:00
}
2026-09-08 15:12:35 +03:00
2026-09-08 16:59:17 +03:00
err = sess . SubmitPrompt ( promptText ,
func ( delta string ) {
initStreamer ()
2026-09-08 15:12:35 +03:00
thinkingFilter . Feed ( delta ,
func ( cleanContent string ) {
toolFilter . Feed ( cleanContent ,
func ( userText string ) {
streamer . Content ( userText )
},
2026-09-08 16:59:17 +03:00
func ( reasoning string ) {
streamer . Reasoning ( reasoning )
},
2026-09-08 15:12:35 +03:00
func ( tc ToolCall ) {
streamer . ToolCall ( tc )
},
)
},
func ( reasoning string ) {
streamer . Reasoning ( reasoning )
},
)
2026-09-08 16:59:17 +03:00
},
func ( alert string ) {
alertMsg = alert
},
)
sess . Close ()
if err != nil {
var alertErr * GatewayAlertError
if errors . As ( err , & alertErr ) && ! streamStarted && alertErr . IsRateLimit {
if attempt < maxAttempts - 1 {
waitSec := alertErr . RetryAfter
if waitSec <= 0 {
waitSec = 6
}
if waitSec > 20 {
waitSec = 20
}
log . Printf ( "Rate limit hit on streaming %s. Waiting %d seconds before retry (attempt %d)..." , effectiveModel , waitSec , attempt + 1 )
select {
case <- time . After ( time . Duration ( waitSec ) * time . Second + 500 * time . Millisecond ):
continue
case <- ctx . Done ():
WriteAPIError ( w , http . StatusTooManyRequests , "Rate limit retry cancelled: context deadline exceeded" , "rate_limit_error" , 429 )
return
}
}
w . Header (). Set ( "Retry-After" , strconv . Itoa ( alertErr . RetryAfter ))
WriteAPIError ( w , http . StatusTooManyRequests , alertErr . Alert , "rate_limit_error" , 429 )
return
2026-09-08 15:12:35 +03:00
}
2026-09-08 16:59:17 +03:00
if ! streamStarted {
if errors . As ( err , & alertErr ) {
lower := strings . ToLower ( alertErr . Alert )
if strings . Contains ( lower , "too large" ) || strings . Contains ( lower , "cannot fit" ) {
WriteAPIError ( w , http . StatusBadRequest , alertErr . Alert , "invalid_request_error" , 400 )
return
}
WriteAPIError ( w , http . StatusBadGateway , alertErr . Alert , "api_error" , 502 )
return
}
WriteAPIError ( w , http . StatusInternalServerError , "Streaming error: " + err . Error (), "api_error" , 500 )
return
}
if alertMsg != "" {
streamer . Content ( "\n\n" + alertMsg )
2026-09-08 15:12:35 +03:00
}
}
2026-09-08 16:59:17 +03:00
if streamStarted {
finishReason := "stop"
thinkingFilter . Flush (
func ( cleanContent string ) {
toolFilter . Feed ( cleanContent ,
func ( userText string ) {
streamer . Content ( userText )
},
func ( reasoning string ) {
streamer . Reasoning ( reasoning )
},
func ( tc ToolCall ) {
streamer . ToolCall ( tc )
},
)
},
func ( reasoning string ) {
streamer . Reasoning ( reasoning )
},
)
toolFilter . Flush (
2026-09-08 15:12:35 +03:00
func ( userText string ) {
streamer . Content ( userText )
},
2026-09-08 16:59:17 +03:00
func ( reasoning string ) {
streamer . Reasoning ( reasoning )
},
2026-09-08 15:12:35 +03:00
func ( tc ToolCall ) {
streamer . ToolCall ( tc )
},
)
2026-09-08 16:59:17 +03:00
if toolFilter . emittedCall {
finishReason = "tool_calls"
}
2026-09-08 15:12:35 +03:00
2026-09-08 16:59:17 +03:00
streamer . Finish ( finishReason )
log . Printf ( "Streaming completion finished for %s in %v" , effectiveModel , time . Since ( start ))
return
}
WriteAPIError ( w , http . StatusInternalServerError , "Empty response from space" , "api_error" , 500 )
return
2026-09-08 15:12:35 +03:00
}
2026-09-08 16:59:17 +03:00
// Non-streaming completion
var fullBuilder strings . Builder
var alertMsg string
err = sess . SubmitPrompt ( promptText ,
func ( delta string ) {
fullBuilder . WriteString ( delta )
},
func ( alert string ) {
alertMsg = alert
},
)
sess . Close ()
_ = alertMsg
if err != nil {
var alertErr * GatewayAlertError
if errors . As ( err , & alertErr ) && alertErr . IsRateLimit {
if attempt < maxAttempts - 1 {
waitSec := alertErr . RetryAfter
if waitSec <= 0 {
waitSec = 6
}
if waitSec > 20 {
waitSec = 20
}
log . Printf ( "Rate limit hit on %s. Waiting %d seconds before retry (attempt %d)..." , effectiveModel , waitSec , attempt + 1 )
select {
case <- time . After ( time . Duration ( waitSec ) * time . Second + 500 * time . Millisecond ):
continue
case <- ctx . Done ():
WriteAPIError ( w , http . StatusTooManyRequests , "Rate limit retry cancelled: context deadline exceeded" , "rate_limit_error" , 429 )
return
}
}
w . Header (). Set ( "Retry-After" , strconv . Itoa ( alertErr . RetryAfter ))
WriteAPIError ( w , http . StatusTooManyRequests , alertErr . Alert , "rate_limit_error" , 429 )
return
}
if errors . As ( err , & alertErr ) {
lower := strings . ToLower ( alertErr . Alert )
if strings . Contains ( lower , "too large" ) || strings . Contains ( lower , "cannot fit" ) {
WriteAPIError ( w , http . StatusBadRequest , alertErr . Alert , "invalid_request_error" , 400 )
return
}
WriteAPIError ( w , http . StatusBadGateway , alertErr . Alert , "api_error" , 502 )
return
}
WriteAPIError ( w , http . StatusInternalServerError , "Prompt execution error: " + err . Error (), "api_error" , 500 )
return
}
fullText := fullBuilder . String ()
if strings . TrimSpace ( fullText ) == "" {
WriteAPIError ( w , http . StatusInternalServerError , "The model returned an empty response" , "api_error" , 500 )
return
}
cleanContent , reasoningContent := ExtractThinking ( fullText )
toolCalls , remainingContent , hasTools := DetectToolCalls ( cleanContent , allowedTools )
finishReason := "stop"
if hasTools {
finishReason = "tool_calls"
cleanContent = remainingContent
}
WriteCompletionResponse ( w , completionID , created , effectiveModel , cleanContent , reasoningContent , toolCalls , finishReason )
log . Printf ( "Completion finished for %s in %v" , effectiveModel , time . Since ( start ))
2026-09-08 15:12:35 +03:00
return
}
}
// ---------------------------------------------------------------------------
// Main Entrypoint & Signal Handling
// ---------------------------------------------------------------------------
func main () {
port := flag . Int ( "port" , 8080 , "HTTP server listening port" )
targetURL := flag . String ( "target" , DefaultTargetURL , "Groq Streamlit space URL" )
defaultModel := flag . String ( "default-model" , DefaultModel , "Default model ID if unspecified" )
userAgent := flag . String ( "user-agent" , DefaultUserAgent , "User-Agent string" )
flag . StringVar ( userAgent , "ua" , DefaultUserAgent , "User-Agent string (alias)" )
socksProxy := flag . String ( "socks" , "" , "SOCKS5 proxy (e.g. socks5://127.0.0.1:1080)" )
flag . StringVar ( socksProxy , "proxy" , "" , "Proxy address alias" )
flag . StringVar ( socksProxy , "socks5" , "" , "SOCKS5 proxy alias" )
2026-09-08 16:59:17 +03:00
// Retain flags for backward compatibility (browser/Xvfb are no longer required)
_ = flag . String ( "browser" , "" , "Ignored: browser is no longer required (headless WebSocket mode)" )
_ = flag . Bool ( "xvfb" , false , "Ignored: Xvfb is no longer required" )
_ = flag . Bool ( "no-xvfb" , false , "Ignored: Xvfb is no longer required" )
_ = flag . Bool ( "headless" , true , "Ignored: always runs headless via direct WebSocket" )
2026-09-08 15:12:35 +03:00
2026-09-08 16:59:17 +03:00
flag . Parse ()
2026-09-08 15:12:35 +03:00
effectiveProxy := * socksProxy
if effectiveProxy == "" {
for _ , envVar := range [] string { "ALL_PROXY" , "all_proxy" , "SOCKS5_PROXY" , "socks5_proxy" , "SOCKS_PROXY" , "socks_proxy" } {
if val := os . Getenv ( envVar ); val != "" {
effectiveProxy = val
break
}
}
}
2026-09-08 16:59:17 +03:00
log . Printf ( "Initializing groqqer gateway (pure Go WebSocket client, zero browser dependency)..." )
2026-09-08 15:12:35 +03:00
gateway := & GroqqerGateway {
2026-09-08 16:59:17 +03:00
targetURL : * targetURL ,
proxyURL : effectiveProxy ,
userAgent : * userAgent ,
2026-09-08 15:12:35 +03:00
defaultModel : * defaultModel ,
port : * port ,
}
2026-09-08 16:59:17 +03:00
// Initial discovery of live models in background
2026-09-08 15:12:35 +03:00
go func () {
2026-09-08 16:59:17 +03:00
log . Println ( "Discovering available models from Streamlit space..." )
2026-09-08 15:12:35 +03:00
gateway . RefreshModels ()
models := gateway . GetModels ()
log . Printf ( "Ready with %d active models. Default: %s" , len ( models ), gateway . defaultModel )
}()
mux := http . NewServeMux ()
mux . HandleFunc ( "/v1/models" , gateway . HandleModels )
mux . HandleFunc ( "/models" , gateway . HandleModels )
mux . HandleFunc ( "/v1/chat/completions" , gateway . HandleChatCompletions )
mux . HandleFunc ( "/chat/completions" , gateway . HandleChatCompletions )
// Root status handler
mux . HandleFunc ( "/" , func ( w http . ResponseWriter , r * http . Request ) {
if r . URL . Path != "/" {
http . NotFound ( w , r )
return
}
w . Header (). Set ( "Content-Type" , "application/json" )
json . NewEncoder ( w ). Encode ( map [ string ] interface {}{
"service" : "groqqer" ,
2026-09-08 16:59:17 +03:00
"mode" : "headless-websocket-protobuf" ,
2026-09-08 15:12:35 +03:00
"status" : "running" ,
"target" : * targetURL ,
"default" : * defaultModel ,
"models" : "/v1/models" ,
"endpoints" : [] string { "/v1/chat/completions" , "/v1/models" },
})
})
server := & http . Server {
Addr : fmt . Sprintf ( ":%d" , * port ),
Handler : mux ,
}
// Graceful shutdown handling
sigChan := make ( chan os . Signal , 1 )
signal . Notify ( sigChan , os . Interrupt , syscall . SIGTERM )
go func () {
<- sigChan
log . Println ( "Shutting down groqqer..." )
ctx , cancel := context . WithTimeout ( context . Background (), 5 * time . Second )
defer cancel ()
_ = server . Shutdown ( ctx )
os . Exit ( 0 )
}()
log . Printf ( "groqqer gateway listening on http://127.0.0.1:%d" , * port )
if err := server . ListenAndServe (); err != nil && err != http . ErrServerClosed {
log . Fatalf ( "server error: %v" , err )
}
}