Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions Parse-Dashboard/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const path = require('path');
const Authentication = require('./Authentication.js');
const fs = require('fs');
const ConfigKeyCache = require('./configKeyCache.js');
const { buildModelParams } = require('./openAIModelParams.js');
const currentVersionFeatures = require('../package.json').parseDashboardFeatures;
const Parse = require('parse/node');

Expand Down Expand Up @@ -984,8 +985,9 @@ You have direct access to the Parse database through function calls, so you can
const requestBody = {
model: model,
messages: messages,
temperature: 0.7,
max_tokens: 2000,
// Token-limit / temperature params depend on the model generation so
// both legacy (GPT-4.1 and older) and next-gen (GPT-5+/o-series) work.
...buildModelParams(model),
tools: databaseTools,
tool_choice: 'auto',
stream: false
Expand Down Expand Up @@ -1074,8 +1076,7 @@ You have direct access to the Parse database through function calls, so you can
const followUpRequestBody = {
model: model,
messages: followUpMessages,
temperature: 0.7,
max_tokens: 2000,
...buildModelParams(model),
tools: databaseTools,
tool_choice: 'auto',
stream: false
Expand Down
74 changes: 74 additions & 0 deletions Parse-Dashboard/openAIModelParams.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
'use strict';

/**
* Helpers for building OpenAI Chat Completions request parameters that work
* across both legacy and next-generation models.
*
* OpenAI changed the Chat Completions API contract starting with the GPT-5
* family and the "o" reasoning series (o1, o3, ...):
* - The token-limit parameter was renamed from `max_tokens` to
* `max_completion_tokens`. Sending `max_tokens` to these models fails with
* "Unsupported parameter: 'max_tokens' is not supported with this model.
* Use 'max_completion_tokens' instead."
* - Only the default `temperature` (1) is accepted. Sending a custom value
* fails with "Unsupported value: 'temperature' does not support ...".
*
* Legacy models (GPT-4.1 and older, GPT-4o, GPT-4-turbo, GPT-3.5, ...) keep the
* original contract. These helpers detect the model generation and emit the
* correct parameters so both old and new models work without the caller needing
* to know the difference.
*/

const MAX_OUTPUT_TOKENS = 2000;
const DEFAULT_TEMPERATURE = 0.7;

/**
* Determine whether a model uses the next-generation Chat Completions contract
* (renamed token parameter, fixed default temperature).
*
* @param {string} model The configured model name, e.g. "gpt-4.1", "gpt-5.5".
* @returns {boolean} True for GPT-5 and later plus the "o" reasoning series.
*/
function usesNextGenContract(model) {
const normalized = typeof model === 'string' ? model.trim().toLowerCase() : '';

// "o" reasoning series: o1, o1-mini, o3, o3-mini, o4-mini, ...
if (/^o\d/.test(normalized)) {
return true;
}

// "gpt-<major>[.<minor>]..." — treat major version 5 and above as next-gen.
const match = normalized.match(/^gpt-(\d+)/);
if (match) {
return parseInt(match[1], 10) >= 5;
}

// Unknown/unmatched names default to the legacy contract to preserve
// backwards compatibility.
return false;
}

/**
* Build the request-body parameters that differ between model generations: the
* token-limit field and (for legacy models only) the temperature.
*
* @param {string} model The configured model name.
* @returns {object} A partial request body with the correct token/temperature keys.
*/
function buildModelParams(model) {
if (usesNextGenContract(model)) {
// Next-gen models: renamed token field, and only the default temperature is
// allowed — so we omit `temperature` entirely.
return { max_completion_tokens: MAX_OUTPUT_TOKENS };
}

// Legacy models: original parameters.
return { max_tokens: MAX_OUTPUT_TOKENS, temperature: DEFAULT_TEMPERATURE };
}

module.exports = {
usesNextGenContract,
buildModelParams,
MAX_OUTPUT_TOKENS,
DEFAULT_TEMPERATURE,
};
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1799,11 +1799,13 @@ To configure the AI agent for your dashboard, you need to add the `agent` config
| `agent.models` | Array | Yes | Array of AI model configurations available to the agent. |
| `agent.models[*].name` | String | Yes | The display name for the model (e.g., `ChatGPT 4.1`). |
| `agent.models[*].provider` | String | Yes | The AI provider identifier (e.g., "openai"). |
| `agent.models[*].model` | String | Yes | The specific model name from the provider (e.g., `gpt-4.1`). |
| `agent.models[*].model` | String | Yes | The specific model name from the provider (e.g., `gpt-4.1`, `gpt-5.5`). |
| `agent.models[*].apiKey` | String | Yes | The API key for authenticating with the AI provider. |

The agent will use the configured models to process natural language commands and perform database operations using the master key from your app configuration.

Both legacy OpenAI models (GPT-4.1 and older, e.g. `gpt-4.1`, `gpt-4o`, `gpt-3.5-turbo`) and newer models (the GPT-5 family and the `o`-reasoning series, e.g. `gpt-5.4`, `gpt-5.5`, `o1`, `o3`) are supported. The dashboard automatically adapts the request parameters to each model's API contract — newer models require `max_completion_tokens` instead of `max_tokens` and only accept the default `temperature` — so no extra configuration is needed when switching between model generations.

### Providers

> [!Note]
Expand Down
69 changes: 69 additions & 0 deletions src/lib/tests/openAIModelParams.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2016-present, Parse, LLC
* All rights reserved.
*
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*/
jest.dontMock('../../../Parse-Dashboard/openAIModelParams.js');

const {
usesNextGenContract,
buildModelParams,
MAX_OUTPUT_TOKENS,
DEFAULT_TEMPERATURE,
} = require('../../../Parse-Dashboard/openAIModelParams.js');

describe('openAIModelParams.usesNextGenContract', () => {
it('treats GPT-4.1 and older as legacy', () => {
expect(usesNextGenContract('gpt-4.1')).toBe(false);
expect(usesNextGenContract('gpt-4')).toBe(false);
expect(usesNextGenContract('gpt-4o')).toBe(false);
expect(usesNextGenContract('gpt-4-turbo')).toBe(false);
expect(usesNextGenContract('gpt-4.5-preview')).toBe(false);
expect(usesNextGenContract('gpt-3.5-turbo')).toBe(false);
});

it('treats GPT-5 and newer as next-gen', () => {
expect(usesNextGenContract('gpt-5')).toBe(true);
expect(usesNextGenContract('gpt-5.4')).toBe(true);
expect(usesNextGenContract('gpt-5.5')).toBe(true);
expect(usesNextGenContract('gpt-6')).toBe(true);
expect(usesNextGenContract('gpt-10')).toBe(true);
});

it('treats the "o" reasoning series as next-gen', () => {
expect(usesNextGenContract('o1')).toBe(true);
expect(usesNextGenContract('o1-mini')).toBe(true);
expect(usesNextGenContract('o3')).toBe(true);
expect(usesNextGenContract('o4-mini')).toBe(true);
});

it('is case- and whitespace-insensitive', () => {
expect(usesNextGenContract(' GPT-5.5 ')).toBe(true);
expect(usesNextGenContract('GPT-4.1')).toBe(false);
});

it('defaults unknown/invalid names to legacy for backwards compatibility', () => {
expect(usesNextGenContract('')).toBe(false);
expect(usesNextGenContract(undefined)).toBe(false);
expect(usesNextGenContract(null)).toBe(false);
expect(usesNextGenContract('some-custom-model')).toBe(false);
});
});

describe('openAIModelParams.buildModelParams', () => {
it('uses max_tokens and temperature for legacy models', () => {
expect(buildModelParams('gpt-4.1')).toEqual({
max_tokens: MAX_OUTPUT_TOKENS,
temperature: DEFAULT_TEMPERATURE,
});
});

it('uses max_completion_tokens and omits temperature for next-gen models', () => {
const params = buildModelParams('gpt-5.5');
expect(params).toEqual({ max_completion_tokens: MAX_OUTPUT_TOKENS });
expect(params).not.toHaveProperty('max_tokens');
expect(params).not.toHaveProperty('temperature');
});
});