model list fixes
This commit is contained in:
@@ -1771,6 +1771,130 @@ func (bb *BrowserBridge) RotateIdentity() (string, error) {
|
|||||||
return "", fmt.Errorf("failed to rotate identity: %v", evalRes)
|
return "", fmt.Errorf("failed to rotate identity: %v", evalRes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bb *BrowserBridge) EvaluateScript(js string) (string, error) {
|
||||||
|
bb.mu.Lock()
|
||||||
|
defer bb.mu.Unlock()
|
||||||
|
|
||||||
|
if bb.conn == nil {
|
||||||
|
if err := bb.Start(); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to start browser bridge: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
evalRes, err := sendCDPCommand(bb.conn, bb.reader, "Runtime.evaluate", map[string]interface{}{
|
||||||
|
"expression": js,
|
||||||
|
"awaitPromise": true,
|
||||||
|
"returnByValue": true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
resJSON, _ := json.Marshal(evalRes)
|
||||||
|
return string(resJSON), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bb *BrowserBridge) FetchFreeModels() ([]ModelItem, error) {
|
||||||
|
js := `(async () => {
|
||||||
|
let targetUrl = null;
|
||||||
|
for (let attempt = 0; attempt < 10; attempt++) {
|
||||||
|
const entries = performance.getEntriesByType('resource')
|
||||||
|
.map(r => r.name)
|
||||||
|
.filter(n => n.includes('model-helpers') || n.includes('model-selector'));
|
||||||
|
targetUrl = entries.find(n => n.includes('model-helpers'));
|
||||||
|
if (targetUrl) break;
|
||||||
|
|
||||||
|
const scripts = Array.from(document.querySelectorAll('script[src]')).map(s => s.src);
|
||||||
|
targetUrl = scripts.find(s => s.includes('model-helpers'));
|
||||||
|
if (targetUrl) break;
|
||||||
|
|
||||||
|
await new Promise(r => setTimeout(r, 500));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetUrl) return { error: "model-helpers bundle not found in active session" };
|
||||||
|
|
||||||
|
const resp = await fetch(targetUrl);
|
||||||
|
const code = await resp.text();
|
||||||
|
const startIdx = code.indexOf('var dE=[');
|
||||||
|
if (startIdx === -1) return { error: "model registry definition (var dE) not found" };
|
||||||
|
|
||||||
|
const sandbox = 'var Z = function(d){return new Date(d)}; function uE(){return{}}; function $(e){return e}; var fast={},images={},reasoning={},reasoningEffort={},toolCalling={},imageGeneration={},nativePDFs={}; ' + code.substring(0, startIdx) + code.substring(startIdx, code.indexOf(';function ', startIdx)) + '; dE;';
|
||||||
|
const models = eval(sandbox);
|
||||||
|
const now = new Date();
|
||||||
|
const freeList = [];
|
||||||
|
|
||||||
|
for (const m of models) {
|
||||||
|
const isRetired = m.retiredOn && new Date(m.retiredOn) <= now;
|
||||||
|
const isLegacy = !!m.legacy;
|
||||||
|
const hasExplicitFree = m.limits && m.limits.app && m.limits.app.free !== undefined && m.limits.app.free !== null;
|
||||||
|
const isPro = m.tier === 'pro' || m.tier === 'premier' || !!m.requiresPro || !!m.premium || !!m.disabled;
|
||||||
|
|
||||||
|
if (!isLegacy && !isRetired && !isPro && hasExplicitFree) {
|
||||||
|
let dev = (m.developer || m.provider || "t3").toLowerCase();
|
||||||
|
if (dev.includes("google")) dev = "google";
|
||||||
|
else if (dev.includes("openai")) dev = "openai";
|
||||||
|
else if (dev.includes("anthropic")) dev = "anthropic";
|
||||||
|
else if (dev.includes("meta")) dev = "meta";
|
||||||
|
else if (dev.includes("moonshot")) dev = "moonshot";
|
||||||
|
else if (dev.includes("alibaba") || dev.includes("qwen")) dev = "alibaba";
|
||||||
|
else if (dev.includes("deepseek")) dev = "deepseek";
|
||||||
|
else if (dev.includes("xai") || dev.includes("grok")) dev = "xai";
|
||||||
|
else if (dev.includes("z.ai") || dev.includes("glm")) dev = "zai";
|
||||||
|
else if (dev.includes("xiaomi")) dev = "xiaomi";
|
||||||
|
else if (dev.includes("minimax")) dev = "minimax";
|
||||||
|
|
||||||
|
freeList.push({
|
||||||
|
id: m.id,
|
||||||
|
owned_by: dev
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { models: freeList };
|
||||||
|
})()`
|
||||||
|
|
||||||
|
res, err := bb.EvaluateScript(js)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var cdpRes struct {
|
||||||
|
Result struct {
|
||||||
|
Result struct {
|
||||||
|
Value struct {
|
||||||
|
Error string `json:"error"`
|
||||||
|
Models []struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
OwnedBy string `json:"owned_by"`
|
||||||
|
} `json:"models"`
|
||||||
|
} `json:"value"`
|
||||||
|
} `json:"result"`
|
||||||
|
} `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal([]byte(res), &cdpRes); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cdpRes.Result.Result.Value.Error != "" {
|
||||||
|
return nil, fmt.Errorf("%s", cdpRes.Result.Result.Value.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var items []ModelItem
|
||||||
|
now := time.Now().Unix()
|
||||||
|
for _, m := range cdpRes.Result.Result.Value.Models {
|
||||||
|
if m.ID != "" {
|
||||||
|
items = append(items, ModelItem{
|
||||||
|
ID: m.ID,
|
||||||
|
Object: "model",
|
||||||
|
Created: now,
|
||||||
|
OwnedBy: m.OwnedBy,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (bb *BrowserBridge) ExecuteFetch(payload T3ChatPayload, deploymentID, clientContext string) (int, string, error) {
|
func (bb *BrowserBridge) ExecuteFetch(payload T3ChatPayload, deploymentID, clientContext string) (int, string, error) {
|
||||||
bb.mu.Lock()
|
bb.mu.Lock()
|
||||||
defer bb.mu.Unlock()
|
defer bb.mu.Unlock()
|
||||||
@@ -2162,6 +2286,24 @@ type T3Gateway struct {
|
|||||||
autoCapture bool
|
autoCapture bool
|
||||||
client *http.Client
|
client *http.Client
|
||||||
bridge *BrowserBridge
|
bridge *BrowserBridge
|
||||||
|
models []ModelItem
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultFreeModels() []ModelItem {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
return []ModelItem{
|
||||||
|
// Google (Verified Unlocked Free)
|
||||||
|
{ID: "gemini-3.5-flash-lite", Object: "model", Created: now, OwnedBy: "google"},
|
||||||
|
{ID: "gemini-3.1-flash-lite", Object: "model", Created: now, OwnedBy: "google"},
|
||||||
|
{ID: "gemini-3-flash", Object: "model", Created: now, OwnedBy: "google"},
|
||||||
|
|
||||||
|
// OpenAI (Verified Unlocked Free)
|
||||||
|
{ID: "gpt-5.4-mini", Object: "model", Created: now, OwnedBy: "openai"},
|
||||||
|
|
||||||
|
// Moonshot (Verified Unlocked Free)
|
||||||
|
{ID: "kimi-k2-0905", Object: "model", Created: now, OwnedBy: "moonshot"},
|
||||||
|
{ID: "kimi-k2.5", Object: "model", Created: now, OwnedBy: "moonshot"},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewT3Gateway(targetURL, clientContext, deploymentID, cookie, hcaptchaToken, defaultModel, browserBin string, autoCapture bool, headless bool, display string, useXvfb bool) *T3Gateway {
|
func NewT3Gateway(targetURL, clientContext, deploymentID, cookie, hcaptchaToken, defaultModel, browserBin string, autoCapture bool, headless bool, display string, useXvfb bool) *T3Gateway {
|
||||||
@@ -2200,6 +2342,7 @@ func NewT3Gateway(targetURL, clientContext, deploymentID, cookie, hcaptchaToken,
|
|||||||
autoCapture: autoCapture,
|
autoCapture: autoCapture,
|
||||||
client: &http.Client{Timeout: 300 * time.Second},
|
client: &http.Client{Timeout: 300 * time.Second},
|
||||||
bridge: bridge,
|
bridge: bridge,
|
||||||
|
models: DefaultFreeModels(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2226,20 +2369,35 @@ func (g *T3Gateway) GetCredentials() (cookie, token, deploymentID, clientContext
|
|||||||
return g.cookie, g.hcaptchaToken, g.deploymentID, g.clientContext
|
return g.cookie, g.hcaptchaToken, g.deploymentID, g.clientContext
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *T3Gateway) ListModels() []ModelItem {
|
func (g *T3Gateway) RefreshModels() error {
|
||||||
now := time.Now().Unix()
|
if g.bridge != nil {
|
||||||
return []ModelItem{
|
items, err := g.bridge.FetchFreeModels()
|
||||||
{ID: "gemini-3.5-flash-lite", Object: "model", Created: now, OwnedBy: "google"},
|
if err != nil {
|
||||||
{ID: "gemini-2.5-pro", Object: "model", Created: now, OwnedBy: "google"},
|
return err
|
||||||
{ID: "gemini-2.5-flash", Object: "model", Created: now, OwnedBy: "google"},
|
|
||||||
{ID: "claude-3-7-sonnet", Object: "model", Created: now, OwnedBy: "anthropic"},
|
|
||||||
{ID: "claude-3-5-sonnet", Object: "model", Created: now, OwnedBy: "anthropic"},
|
|
||||||
{ID: "gpt-4o", Object: "model", Created: now, OwnedBy: "openai"},
|
|
||||||
{ID: "gpt-4o-mini", Object: "model", Created: now, OwnedBy: "openai"},
|
|
||||||
{ID: "o3-mini", Object: "model", Created: now, OwnedBy: "openai"},
|
|
||||||
{ID: "deepseek-r1", Object: "model", Created: now, OwnedBy: "deepseek"},
|
|
||||||
{ID: "deepseek-v3", Object: "model", Created: now, OwnedBy: "deepseek"},
|
|
||||||
}
|
}
|
||||||
|
if len(items) > 0 {
|
||||||
|
g.mu.Lock()
|
||||||
|
g.models = items
|
||||||
|
g.mu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *T3Gateway) ListModels() []ModelItem {
|
||||||
|
g.mu.RLock()
|
||||||
|
defer g.mu.RUnlock()
|
||||||
|
if len(g.models) == 0 {
|
||||||
|
return DefaultFreeModels()
|
||||||
|
}
|
||||||
|
now := time.Now().Unix()
|
||||||
|
res := make([]ModelItem, len(g.models))
|
||||||
|
for i, m := range g.models {
|
||||||
|
res[i] = m
|
||||||
|
res[i].Created = now
|
||||||
|
}
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *T3Gateway) extractSessionID(cookieStr string) string {
|
func (g *T3Gateway) extractSessionID(cookieStr string) string {
|
||||||
@@ -2999,6 +3157,13 @@ func main() {
|
|||||||
gateway.bridge = nil
|
gateway.bridge = nil
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("Browser bridge active! Upstream requests will execute with genuine Chromium TLS fingerprint.")
|
fmt.Println("Browser bridge active! Upstream requests will execute with genuine Chromium TLS fingerprint.")
|
||||||
|
fmt.Println("Fetching live free-tier models to populate /v1/models...")
|
||||||
|
if err := gateway.RefreshModels(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "warning: live model discovery failed: %v (using default free-tier model catalog)\n", err)
|
||||||
|
} else {
|
||||||
|
models := gateway.ListModels()
|
||||||
|
fmt.Printf("Successfully populated %d active free-tier models into /v1/models\n", len(models))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user