fix(translator): ESM-safe registry + tool-id pairing + responses max_tokens; add real-creds tests
- translator/index.js: replace require() with static side-effect imports (ESM-safe), lazy-init registry maps to survive circular import order - openai-responses->openai: map max_output_tokens -> max_tokens (avoid leaking field upstream) - gemini/antigravity -> openai: derive deterministic tool_call id from name so functionCall/functionResponse pair correctly (fixes provider tool-pairing 400s) - add offline unit tests (finish-reason, usage, session-manager, ollama malformed args, const guard) - add real-creds integration tests (provider-cases + all-formats matrix: 6 inbound formats x 4 scenarios) Includes co-located provider registry refactor (pricing/capabilities/media providers) and sessionManager updates. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,238 +1,12 @@
|
||||
// UI display config — registry providers derive from registry.display.
|
||||
// Non-registry providers (media-only: tts, stt, search, fetch) kept hardcoded here.
|
||||
// UI display config — all providers derive from registry.display.
|
||||
import REGISTRY from "open-sse/providers/registry/index.js";
|
||||
|
||||
export const RISK_NOTICE = "⚠️ Risk Notice: This provider uses a subscription/OAuth session not officially licensed for proxy/router use. Account may be restricted or banned. Use at your own risk.";
|
||||
|
||||
// Non-registry media-only providers display config
|
||||
const MEDIA_ONLY_DISPLAY = {
|
||||
"elevenlabs": {
|
||||
"name": "ElevenLabs",
|
||||
"icon": "record_voice_over",
|
||||
"color": "#6C47FF",
|
||||
"textIcon": "EL",
|
||||
"website": "https://elevenlabs.io",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://elevenlabs.io/app/settings/api-keys"
|
||||
}
|
||||
},
|
||||
"cartesia": {
|
||||
"name": "Cartesia",
|
||||
"icon": "spatial_audio",
|
||||
"color": "#FF4F8B",
|
||||
"textIcon": "CA",
|
||||
"website": "https://cartesia.ai",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://play.cartesia.ai/keys"
|
||||
},
|
||||
"hidden": true
|
||||
},
|
||||
"playht": {
|
||||
"name": "PlayHT",
|
||||
"icon": "play_circle",
|
||||
"color": "#00B4D8",
|
||||
"textIcon": "PH",
|
||||
"website": "https://play.ht",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://play.ht/studio/api-access"
|
||||
},
|
||||
"hidden": true
|
||||
},
|
||||
"local-device": {
|
||||
"name": "Local Device",
|
||||
"icon": "speaker",
|
||||
"color": "#64748B",
|
||||
"textIcon": "LD",
|
||||
"mediaPriority": 5
|
||||
},
|
||||
"google-tts": {
|
||||
"name": "Google TTS",
|
||||
"icon": "record_voice_over",
|
||||
"color": "#4285F4",
|
||||
"textIcon": "GT",
|
||||
"mediaPriority": 5
|
||||
},
|
||||
"edge-tts": {
|
||||
"name": "Edge TTS",
|
||||
"icon": "record_voice_over",
|
||||
"color": "#0078D4",
|
||||
"textIcon": "ET",
|
||||
"mediaPriority": 5
|
||||
},
|
||||
"coqui": {
|
||||
"name": "Coqui TTS",
|
||||
"icon": "record_voice_over",
|
||||
"color": "#10B981",
|
||||
"textIcon": "CQ",
|
||||
"website": "https://github.com/coqui-ai/TTS",
|
||||
"hidden": true
|
||||
},
|
||||
"tortoise": {
|
||||
"name": "Tortoise TTS",
|
||||
"icon": "record_voice_over",
|
||||
"color": "#7C3AED",
|
||||
"textIcon": "TT",
|
||||
"website": "https://github.com/neonbjb/tortoise-tts",
|
||||
"hidden": true
|
||||
},
|
||||
"inworld": {
|
||||
"name": "Inworld TTS",
|
||||
"icon": "record_voice_over",
|
||||
"color": "#FF6B6B",
|
||||
"textIcon": "IW",
|
||||
"website": "https://inworld.ai",
|
||||
"notice": {
|
||||
"text": "Free tier: 40 minutes/month TTS. Paid: TTS-1.5 Mini $0.01/min ($15/1M chars), TTS-1.5 Max $0.025/min ($30/1M chars). 270+ voices, 15 languages.",
|
||||
"apiKeyUrl": "https://platform.inworld.ai/api-keys"
|
||||
}
|
||||
},
|
||||
"aws-polly": {
|
||||
"name": "AWS Polly",
|
||||
"icon": "record_voice_over",
|
||||
"color": "#FF9900",
|
||||
"textIcon": "PL",
|
||||
"website": "https://aws.amazon.com/polly/",
|
||||
"notice": {
|
||||
"text": "Use AWS Secret Access Key as API key; set providerSpecificData.accessKeyId and optional region.",
|
||||
"apiKeyUrl": "https://console.aws.amazon.com/iam/home#/security_credentials"
|
||||
}
|
||||
},
|
||||
"jina-ai": {
|
||||
"name": "Jina AI",
|
||||
"icon": "blur_on",
|
||||
"color": "#2563EB",
|
||||
"textIcon": "JA",
|
||||
"website": "https://jina.ai",
|
||||
"notice": {
|
||||
"text": "10M free tokens on signup (non-commercial), no credit card required.",
|
||||
"apiKeyUrl": "https://jina.ai/?sui=apikey"
|
||||
}
|
||||
},
|
||||
"jina-reader": {
|
||||
"name": "Jina Reader",
|
||||
"icon": "menu_book",
|
||||
"color": "#000000",
|
||||
"textIcon": "JR",
|
||||
"website": "https://jina.ai/reader",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://jina.ai/?sui=apikey"
|
||||
}
|
||||
},
|
||||
"tavily": {
|
||||
"name": "Tavily",
|
||||
"icon": "search",
|
||||
"color": "#5B21B6",
|
||||
"textIcon": "TV",
|
||||
"website": "https://tavily.com",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://app.tavily.com/home"
|
||||
}
|
||||
},
|
||||
"brave-search": {
|
||||
"name": "Brave Search",
|
||||
"icon": "travel_explore",
|
||||
"color": "#FB542B",
|
||||
"textIcon": "BR",
|
||||
"website": "https://brave.com/search/api",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://api-dashboard.search.brave.com/app/keys"
|
||||
}
|
||||
},
|
||||
"serper": {
|
||||
"name": "Serper",
|
||||
"icon": "search",
|
||||
"color": "#4F46E5",
|
||||
"textIcon": "SP",
|
||||
"website": "https://serper.dev",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://serper.dev/api-key"
|
||||
}
|
||||
},
|
||||
"exa": {
|
||||
"name": "Exa",
|
||||
"icon": "manage_search",
|
||||
"color": "#2563EB",
|
||||
"textIcon": "EX",
|
||||
"website": "https://exa.ai",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://dashboard.exa.ai/api-keys"
|
||||
}
|
||||
},
|
||||
"searxng": {
|
||||
"name": "SearXNG",
|
||||
"icon": "saved_search",
|
||||
"color": "#3B82F6",
|
||||
"textIcon": "SX",
|
||||
"website": "https://docs.searxng.org"
|
||||
},
|
||||
"google-pse": {
|
||||
"name": "Google PSE",
|
||||
"icon": "search",
|
||||
"color": "#4285F4",
|
||||
"textIcon": "GP",
|
||||
"website": "https://programmablesearchengine.google.com",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://programmablesearchengine.google.com/controlpanel/create"
|
||||
}
|
||||
},
|
||||
"linkup": {
|
||||
"name": "Linkup",
|
||||
"icon": "link",
|
||||
"color": "#0EA5E9",
|
||||
"textIcon": "LK",
|
||||
"website": "https://linkup.so",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://app.linkup.so/api-keys"
|
||||
}
|
||||
},
|
||||
"searchapi": {
|
||||
"name": "SearchAPI",
|
||||
"icon": "search",
|
||||
"color": "#0EA5A4",
|
||||
"textIcon": "SA",
|
||||
"website": "https://www.searchapi.io",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://www.searchapi.io/dashboard"
|
||||
}
|
||||
},
|
||||
"youcom": {
|
||||
"name": "You.com Search",
|
||||
"icon": "search",
|
||||
"color": "#7C3AED",
|
||||
"textIcon": "YC",
|
||||
"website": "https://you.com",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://api.you.com"
|
||||
}
|
||||
},
|
||||
"firecrawl": {
|
||||
"name": "Firecrawl",
|
||||
"icon": "local_fire_department",
|
||||
"color": "#F59E0B",
|
||||
"textIcon": "FC",
|
||||
"website": "https://firecrawl.dev",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://www.firecrawl.dev/app/api-keys"
|
||||
}
|
||||
},
|
||||
"topaz": {
|
||||
"name": "Topaz",
|
||||
"icon": "image",
|
||||
"color": "#059669",
|
||||
"textIcon": "TP",
|
||||
"website": "https://topazlabs.com",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://topazlabs.com/account"
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Resolve "RISK_NOTICE" token → real notice text (registry stores token to avoid import cycle)
|
||||
const resolveDisplay = (d) =>
|
||||
d.deprecationNotice === "RISK_NOTICE" ? { ...d, deprecationNotice: RISK_NOTICE } : d;
|
||||
|
||||
// Merge: registry providers take precedence
|
||||
export const PROVIDER_DISPLAY = {
|
||||
...MEDIA_ONLY_DISPLAY,
|
||||
...Object.fromEntries(REGISTRY.filter(r => r.display).map(r => [r.id, resolveDisplay(r.display)])),
|
||||
};
|
||||
export const PROVIDER_DISPLAY = Object.fromEntries(
|
||||
REGISTRY.filter((r) => r.display).map((r) => [r.id, resolveDisplay(r.display)]),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user