Add Fireworks AI Provider Support (#4374)

* add fireworks provider

* fix

* add context length

* Fireworks fixes

* Add logo image

---------

Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
This commit is contained in:
ershang-fireworks
2025-08-15 01:52:46 +08:00
committed by GitHub
parent 659930d5ba
commit e9be0f1c64
10 changed files with 145 additions and 6 deletions
+1
View File
@@ -0,0 +1 @@
<svg preserveAspectRatio="xMinYMid meet" class="h-5" viewBox="0 0 638 315" xmlns="http://www.w3.org/2000/svg"><path d="M318.563 221.755C300.863 221.755 284.979 211.247 278.206 194.978L196.549 0H244.342L318.842 178.361L393.273 0H441.066L358.92 195.048C352.112 211.247 336.263 221.755 318.563 221.755Z"></path><path d="M425.111 314.933C407.481 314.933 391.667 304.494 384.824 288.366C377.947 272.097 381.507 253.524 393.936 240.921L542.657 90.2803L561.229 134.094L425.076 271.748L619.147 270.666L637.72 314.479L425.146 315.003L425.076 314.933H425.111Z"></path><path d="M0 314.408L18.5727 270.595L212.643 271.677L76.525 133.988L95.0977 90.1748L243.819 240.816C256.247 253.384 259.843 272.026 252.93 288.26C246.088 304.424 230.203 314.827 212.643 314.827L0.0698221 314.339L0 314.408Z"></path></svg>

After

Width:  |  Height:  |  Size: 795 B

+23 -6
View File
@@ -691,7 +691,7 @@
</span>
</div>
</div>
<div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,nanogpt,deepseek,xai,pollinations,moonshot">
<div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,nanogpt,deepseek,xai,pollinations,moonshot,fireworks">
<div class="range-block-title" data-i18n="Temperature">
Temperature
</div>
@@ -704,7 +704,7 @@
</div>
</div>
</div>
<div class="range-block" data-source="openai,aimlapi,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek,xai,pollinations,moonshot">
<div class="range-block" data-source="openai,aimlapi,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek,xai,pollinations,moonshot,fireworks">
<div class="range-block-title" data-i18n="Frequency Penalty">
Frequency Penalty
</div>
@@ -717,7 +717,7 @@
</div>
</div>
</div>
<div class="range-block" data-source="openai,aimlapi,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek,xai,pollinations,moonshot">
<div class="range-block" data-source="openai,aimlapi,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek,xai,pollinations,moonshot,fireworks">
<div class="range-block-title" data-i18n="Presence Penalty">
Presence Penalty
</div>
@@ -743,7 +743,7 @@
</div>
</div>
</div>
<div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,nanogpt,deepseek,xai,pollinations,moonshot">
<div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,nanogpt,deepseek,xai,pollinations,moonshot,fireworks">
<div class="range-block-title" data-i18n="Top P">
Top P
</div>
@@ -1984,7 +1984,7 @@
</b>
</div>
</div>
<div class="range-block" data-source="openai,cohere,mistralai,custom,claude,aimlapi,openrouter,groq,deepseek,makersuite,vertexai,ai21,xai,pollinations,moonshot">
<div class="range-block" data-source="openai,cohere,mistralai,custom,claude,aimlapi,openrouter,groq,deepseek,makersuite,vertexai,ai21,xai,pollinations,moonshot,fireworks">
<label for="openai_function_calling" class="checkbox_label flexWrap widthFreeExpand">
<input id="openai_function_calling" type="checkbox" />
<span data-i18n="Enable function calling">Enable function calling</span>
@@ -2065,7 +2065,7 @@
</span>
</div>
</div>
<div class="range-block" data-source="deepseek,aimlapi,openrouter,custom,claude,xai,makersuite,vertexai,pollinations,moonshot,mistralai">
<div class="range-block" data-source="deepseek,aimlapi,openrouter,custom,claude,xai,makersuite,vertexai,pollinations,moonshot,mistralai,fireworks">
<label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">
<input id="openai_show_thoughts" type="checkbox" />
<span data-i18n="Request model reasoning">Request model reasoning</span>
@@ -2793,6 +2793,7 @@
<option value="claude">Claude</option>
<option value="cohere">Cohere</option>
<option value="deepseek">DeepSeek</option>
<option value="fireworks">Fireworks AI</option>
<option value="groq">Groq</option>
<option value="makersuite">Google AI Studio</option>
<option value="vertexai">Google Vertex AI</option>
@@ -3484,6 +3485,22 @@
</select>
</div>
</div>
<div id="fireworks_form" data-source="fireworks">
<h4 data-i18n="Fireworks AI API Key">Fireworks AI API Key</h4>
<div class="flex-container">
<input id="api_key_fireworks" name="api_key_fireworks" class="text_pole flex1" value="" type="text" autocomplete="off">
<div title="Manage API keys" data-i18n="[title]Manage API keys" class="menu_button fa-solid fa-key fa-fw manage-api-keys" data-key="api_key_fireworks"></div>
</div>
<div data-for="api_key_fireworks" class="neutral_warning" data-i18n="For privacy reasons, your API key will be hidden after you click 'Connect'.">
For privacy reasons, your API key will be hidden after you click 'Connect'.
</div>
<div>
<h4 data-i18n="Fireworks AI Model">Fireworks AI Model</h4>
<select id="model_fireworks_select">
<option value="" data-i18n="-- Connect to the API --">-- Connect to the API --</option>
</select>
</div>
</div>
<div id="perplexity_form" data-source="perplexity">
<h4 data-i18n="Perplexity API Key">Perplexity API Key</h4>
<div class="flex-container">
+1
View File
@@ -407,6 +407,7 @@ function RA_autoconnect(PrevApi) {
|| (secret_state[SECRET_KEYS.XAI] && oai_settings.chat_completion_source == chat_completion_sources.XAI)
|| (secret_state[SECRET_KEYS.AIMLAPI] && oai_settings.chat_completion_source == chat_completion_sources.AIMLAPI)
|| (secret_state[SECRET_KEYS.MOONSHOT] && oai_settings.chat_completion_source == chat_completion_sources.MOONSHOT)
|| (secret_state[SECRET_KEYS.FIREWORKS] && oai_settings.chat_completion_source == chat_completion_sources.FIREWORKS)
|| (oai_settings.chat_completion_source === chat_completion_sources.POLLINATIONS)
|| (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM)
) {
+85
View File
@@ -185,6 +185,7 @@ export const chat_completion_sources = {
XAI: 'xai',
POLLINATIONS: 'pollinations',
MOONSHOT: 'moonshot',
FIREWORKS: 'fireworks',
};
const character_names_behavior = {
@@ -458,6 +459,7 @@ const oai_settings = {
xai_model: 'grok-3-beta',
pollinations_model: 'openai',
moonshot_model: 'kimi-latest',
fireworks_model: 'accounts/fireworks/models/kimi-k2-instruct',
custom_model: '',
custom_url: '',
custom_include_body: '',
@@ -1620,6 +1622,8 @@ export function getChatCompletionModel(source = null) {
return oai_settings.pollinations_model;
case chat_completion_sources.MOONSHOT:
return oai_settings.moonshot_model;
case chat_completion_sources.FIREWORKS:
return oai_settings.fireworks_model;
default:
console.error(`Unknown chat completion source: ${activeSource}`);
return '';
@@ -1872,6 +1876,27 @@ function saveModelList(data) {
$('#model_groq_select').val(oai_settings.groq_model).trigger('change');
}
if (oai_settings.chat_completion_source === chat_completion_sources.FIREWORKS) {
$('#model_fireworks_select').empty();
model_list.forEach((model) => {
if (!model?.supports_chat) {
return;
}
$('#model_fireworks_select').append(
$('<option>', {
value: model.id,
text: model.id,
}));
});
const selectedModel = model_list.find(model => model.id === oai_settings.fireworks_model);
if (model_list.length > 0 && (!selectedModel || !oai_settings.fireworks_model)) {
oai_settings.fireworks_model = model_list[0].id;
}
$('#model_fireworks_select').val(oai_settings.fireworks_model).trigger('change');
}
}
function appendOpenRouterOptions(model_list, groupModels = false, sort = false) {
@@ -4487,6 +4512,31 @@ function getMoonshotMaxContext(model, isUnlocked) {
return Object.entries(contextMap).find(([key]) => model.includes(key))?.[1] || max_32k;
}
/**
* Get the maximum context size for the Fireworks model
* @param {string} model Model identifier
* @param {boolean} isUnlocked Whether context limits are unlocked
* @returns {number} Maximum context size in tokens
*/
function getFireworksMaxContext(model, isUnlocked) {
if (isUnlocked) {
return unlocked_max;
}
// First check if model info is available from model_list
if (Array.isArray(model_list) && model_list.length > 0) {
const modelInfo = model_list.find((record) => record.id === model);
if (modelInfo?.context_length) {
return modelInfo.context_length;
}
if (modelInfo?.context_window) {
return modelInfo.context_window;
}
}
return max_32k;
}
async function onModelChange() {
biasCache = undefined;
let value = String($(this).val() || '');
@@ -4627,6 +4677,15 @@ async function onModelChange() {
oai_settings.moonshot_model = value;
}
if ($(this).is('#model_fireworks_select')) {
if (!value) {
console.debug('Null Fireworks model selected. Ignoring.');
return;
}
console.log('Fireworks model changed to', value);
oai_settings.fireworks_model = value;
}
if ([chat_completion_sources.MAKERSUITE, chat_completion_sources.VERTEXAI].includes(oai_settings.chat_completion_source)) {
if (oai_settings.max_context_unlocked) {
$('#openai_max_context').attr('max', max_2mil);
@@ -4899,6 +4958,15 @@ async function onModelChange() {
$('#temp_openai').attr('max', claude_max_temp).val(oai_settings.temp_openai).trigger('input');
}
if (oai_settings.chat_completion_source === chat_completion_sources.FIREWORKS) {
const maxContext = getFireworksMaxContext(oai_settings.fireworks_model, oai_settings.max_context_unlocked);
$('#openai_max_context').attr('max', maxContext);
oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
$('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
oai_settings.temp_openai = Math.min(oai_max_temp, oai_settings.temp_openai);
$('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
}
$('#openai_max_context_counter').attr('max', Number($('#openai_max_context').attr('max')));
saveSettingsDebounced();
@@ -5143,6 +5211,19 @@ async function onConnectButtonClick(e) {
}
}
if (oai_settings.chat_completion_source == chat_completion_sources.FIREWORKS) {
const api_key_fireworks = String($('#api_key_fireworks').val()).trim();
if (api_key_fireworks.length) {
await writeSecret(SECRET_KEYS.FIREWORKS, api_key_fireworks);
}
if (!secret_state[SECRET_KEYS.FIREWORKS]) {
console.log('No secret key saved for Fireworks');
return;
}
}
startStatusLoading();
saveSettingsDebounced();
await getStatusOpen();
@@ -5207,6 +5288,9 @@ function toggleChatCompletionForms() {
else if (oai_settings.chat_completion_source == chat_completion_sources.MOONSHOT) {
$('#model_moonshot_select').trigger('change');
}
else if (oai_settings.chat_completion_source == chat_completion_sources.FIREWORKS) {
$('#model_fireworks_select').trigger('change');
}
$('[data-source]').each(function () {
const validSources = $(this).data('source').split(',');
$(this).toggle(validSources.includes(oai_settings.chat_completion_source));
@@ -6139,6 +6223,7 @@ export function initOpenAI() {
$('#model_xai_select').on('change', onModelChange);
$('#model_pollinations_select').on('change', onModelChange);
$('#model_moonshot_select').on('change', onModelChange);
$('#model_fireworks_select').on('change', onModelChange);
$('#settings_preset_openai').on('change', onSettingsPresetChange);
$('#new_oai_preset').on('click', onNewPresetClick);
$('#delete_oai_preset').on('click', onDeletePresetClick);
+3
View File
@@ -60,6 +60,7 @@ export const SECRET_KEYS = {
AIMLAPI: 'api_key_aimlapi',
FALAI: 'api_key_falai',
XAI: 'api_key_xai',
FIREWORKS: 'api_key_fireworks',
VERTEXAI_SERVICE_ACCOUNT: 'vertexai_service_account_json',
MINIMAX: 'api_key_minimax',
MINIMAX_GROUP_ID: 'minimax_group_id',
@@ -107,6 +108,7 @@ const FRIENDLY_NAMES = {
[SECRET_KEYS.FALAI]: 'FAL.AI',
[SECRET_KEYS.AZURE_TTS]: 'Azure TTS',
[SECRET_KEYS.AIMLAPI]: 'AI/ML API',
[SECRET_KEYS.FIREWORKS]: 'Fireworks AI',
[SECRET_KEYS.DEEPL]: 'DeepL',
[SECRET_KEYS.LIBRE]: 'LibreTranslate',
[SECRET_KEYS.LIBRE_URL]: 'LibreTranslate Endpoint (e.g. http://127.0.0.1:5000/translate)',
@@ -151,6 +153,7 @@ const INPUT_MAP = {
[SECRET_KEYS.XAI]: '#api_key_xai',
[SECRET_KEYS.VERTEXAI_SERVICE_ACCOUNT]: '#vertexai_service_account_json',
[SECRET_KEYS.MOONSHOT]: '#api_key_moonshot',
[SECRET_KEYS.FIREWORKS]: '#api_key_fireworks',
};
const getLabel = () => moment().format('L LT');
+1
View File
@@ -4855,6 +4855,7 @@ function getModelOptions(quiet) {
{ id: 'model_xai_select', api: 'openai', type: chat_completion_sources.XAI },
{ id: 'model_pollinations_select', api: 'openai', type: chat_completion_sources.POLLINATIONS },
{ id: 'model_moonshot_select', api: 'openai', type: chat_completion_sources.MOONSHOT },
{ id: 'model_fireworks_select', api: 'openai', type: chat_completion_sources.FIREWORKS },
{ id: 'model_novel_select', api: 'novel', type: null },
{ id: 'horde_model', api: 'koboldhorde', type: null },
];
+8
View File
@@ -605,6 +605,13 @@ export class ToolManager {
}
}
if (oai_settings.chat_completion_source === chat_completion_sources.FIREWORKS && Array.isArray(model_list)) {
const currentModel = model_list.find(model => model.id === oai_settings.fireworks_model);
if (currentModel) {
return currentModel.supports_tools;
}
}
const supportedSources = [
chat_completion_sources.OPENAI,
chat_completion_sources.CUSTOM,
@@ -621,6 +628,7 @@ export class ToolManager {
chat_completion_sources.XAI,
chat_completion_sources.POLLINATIONS,
chat_completion_sources.MOONSHOT,
chat_completion_sources.FIREWORKS,
];
return supportedSources.includes(oai_settings.chat_completion_source);
}
+1
View File
@@ -179,6 +179,7 @@ export const CHAT_COMPLETION_SOURCES = {
XAI: 'xai',
POLLINATIONS: 'pollinations',
MOONSHOT: 'moonshot',
FIREWORKS: 'fireworks',
};
/**
@@ -66,6 +66,7 @@ const API_XAI = 'https://api.x.ai/v1';
const API_AIMLAPI = 'https://api.aimlapi.com/v1';
const API_POLLINATIONS = 'https://text.pollinations.ai/openai';
const API_MOONSHOT = 'https://api.moonshot.ai/v1';
const API_FIREWORKS = 'https://api.fireworks.ai/inference/v1';
/**
* Gets OpenRouter transforms based on the request.
@@ -1251,6 +1252,10 @@ router.post('/status', async function (request, statusResponse) {
apiUrl = API_MOONSHOT;
apiKey = readSecret(request.user.directories, SECRET_KEYS.MOONSHOT);
headers = {};
} else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.FIREWORKS) {
apiUrl = API_FIREWORKS;
apiKey = readSecret(request.user.directories, SECRET_KEYS.FIREWORKS);
headers = {};
} else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MAKERSUITE) {
apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE);
apiUrl = trimTrailingSlash(request.body.reverse_proxy || API_MAKERSUITE);
@@ -1609,6 +1614,22 @@ router.post('/generate', function (request, response) {
},
};
}
} else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.FIREWORKS) {
apiUrl = API_FIREWORKS;
apiKey = readSecret(request.user.directories, SECRET_KEYS.FIREWORKS);
headers = {};
bodyParams = {};
if (request.body.json_schema) {
bodyParams['response_format'] = {
type: 'json_schema',
json_schema: {
name: request.body.json_schema.name,
description: request.body.json_schema.description,
schema: request.body.json_schema.value,
strict: request.body.json_schema.strict ?? true,
},
};
}
} else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.NANOGPT) {
apiUrl = API_NANOGPT;
apiKey = readSecret(request.user.directories, SECRET_KEYS.NANOGPT);
+1
View File
@@ -53,6 +53,7 @@ export const SECRET_KEYS = {
SERPER: 'api_key_serper',
AIMLAPI: 'api_key_aimlapi',
XAI: 'api_key_xai',
FIREWORKS: 'api_key_fireworks',
VERTEXAI_SERVICE_ACCOUNT: 'vertexai_service_account_json',
MINIMAX: 'api_key_minimax',
MINIMAX_GROUP_ID: 'minimax_group_id',