mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-23 20:00:15 +01:00
📦 feat: Model & Assistants Combobox for Side Panel (#2380)
* WIP: dynamic settings * WIP: update tests and validations * refactor(SidePanel): use hook for Links * WIP: dynamic settings, slider implemented * feat(useDebouncedInput): dynamic typing with generic * refactor(generate): add `custom` optionType to be non-conforming to conversation schema * feat: DynamicDropdown * refactor(DynamicSlider): custom optionType handling and useEffect for conversation updates elsewhere * refactor(Panel): add more test cases * chore(DynamicSlider): note * refactor(useDebouncedInput): import defaultDebouncedDelay from ~/common` * WIP: implement remaining ComponentTypes * chore: add com_sidepanel_parameters * refactor: add langCode handling for dynamic settings * chore(useOriginNavigate): change path to '/c/' * refactor: explicit textarea focus on new convo, share textarea idea via ~/common * refactor: useParameterEffects: reset if convo or preset Ids change, share and maintain statefulness in side panel * wip: combobox * chore: minor styling for Select components * wip: combobox select styling for side panel * feat: complete combobox * refactor: model select for side panel switcher * refactor(Combobox): add portal * chore: comment out dynamic parameters panel for future PR and delete prompt files * refactor(Combobox): add icon field for options, change hover bg-color, add displayValue * fix(useNewConvo): proper textarea focus with setTimeout * refactor(AssistantSwitcher): use Combobox * refactor(ModelSwitcher): add textarea focus on model switch
This commit is contained in:
parent
f64a2cb0b0
commit
8e5f1ad575
33 changed files with 2850 additions and 462 deletions
525
packages/data-provider/specs/generate.spec.ts
Normal file
525
packages/data-provider/specs/generate.spec.ts
Normal file
|
|
@ -0,0 +1,525 @@
|
|||
/* eslint-disable jest/no-conditional-expect */
|
||||
import { ZodError, z } from 'zod';
|
||||
import { generateDynamicSchema, validateSettingDefinitions } from '../src/generate';
|
||||
import type { SettingsConfiguration } from '../src/generate';
|
||||
|
||||
describe('generateDynamicSchema', () => {
|
||||
it('should generate a schema for number settings with range', () => {
|
||||
const settings: SettingsConfiguration = [
|
||||
{
|
||||
key: 'testNumber',
|
||||
description: 'A test number setting',
|
||||
type: 'number',
|
||||
default: 5,
|
||||
range: { min: 1, max: 10, step: 1 },
|
||||
component: 'slider',
|
||||
optionType: 'conversation',
|
||||
columnSpan: 2,
|
||||
label: 'Test Number Slider',
|
||||
},
|
||||
];
|
||||
|
||||
const schema = generateDynamicSchema(settings);
|
||||
const result = schema.safeParse({ testNumber: 6 });
|
||||
|
||||
expect(result.success).toBeTruthy();
|
||||
expect(result['data']).toEqual({ testNumber: 6 });
|
||||
});
|
||||
|
||||
it('should generate a schema for boolean settings', () => {
|
||||
const settings: SettingsConfiguration = [
|
||||
{
|
||||
key: 'testBoolean',
|
||||
description: 'A test boolean setting',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
component: 'switch',
|
||||
optionType: 'model', // Only if relevant to your application's context
|
||||
columnSpan: 1,
|
||||
label: 'Test Boolean Switch',
|
||||
},
|
||||
];
|
||||
|
||||
const schema = generateDynamicSchema(settings);
|
||||
const result = schema.safeParse({ testBoolean: false });
|
||||
|
||||
expect(result.success).toBeTruthy();
|
||||
expect(result['data']).toEqual({ testBoolean: false });
|
||||
});
|
||||
|
||||
it('should generate a schema for string settings', () => {
|
||||
const settings: SettingsConfiguration = [
|
||||
{
|
||||
key: 'testString',
|
||||
description: 'A test string setting',
|
||||
type: 'string',
|
||||
default: 'default value',
|
||||
component: 'input',
|
||||
optionType: 'model', // Optional and only if relevant
|
||||
columnSpan: 3,
|
||||
label: 'Test String Input',
|
||||
placeholder: 'Enter text here...',
|
||||
minText: 0, // Optional
|
||||
maxText: 100, // Optional
|
||||
},
|
||||
];
|
||||
|
||||
const schema = generateDynamicSchema(settings);
|
||||
const result = schema.safeParse({ testString: 'custom value' });
|
||||
|
||||
expect(result.success).toBeTruthy();
|
||||
expect(result['data']).toEqual({ testString: 'custom value' });
|
||||
});
|
||||
|
||||
it('should generate a schema for enum settings', () => {
|
||||
const settings: SettingsConfiguration = [
|
||||
{
|
||||
key: 'testEnum',
|
||||
description: 'A test enum setting',
|
||||
type: 'enum',
|
||||
default: 'option1',
|
||||
options: ['option1', 'option2', 'option3'],
|
||||
enumMappings: {
|
||||
option1: 'First Option',
|
||||
option2: 'Second Option',
|
||||
option3: 'Third Option',
|
||||
},
|
||||
component: 'dropdown',
|
||||
columnSpan: 2,
|
||||
label: 'Test Enum Dropdown',
|
||||
},
|
||||
];
|
||||
|
||||
const schema = generateDynamicSchema(settings);
|
||||
const result = schema.safeParse({ testEnum: 'option2' });
|
||||
|
||||
expect(result.success).toBeTruthy();
|
||||
expect(result['data']).toEqual({ testEnum: 'option2' });
|
||||
});
|
||||
|
||||
it('should fail for incorrect enum value', () => {
|
||||
const settings: SettingsConfiguration = [
|
||||
{
|
||||
key: 'testEnum',
|
||||
description: 'A test enum setting',
|
||||
type: 'enum',
|
||||
default: 'option1',
|
||||
options: ['option1', 'option2', 'option3'],
|
||||
component: 'dropdown',
|
||||
},
|
||||
];
|
||||
|
||||
const schema = generateDynamicSchema(settings);
|
||||
const result = schema.safeParse({ testEnum: 'option4' }); // This option does not exist
|
||||
|
||||
expect(result.success).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateSettingDefinitions', () => {
|
||||
// Test for valid setting configurations
|
||||
test('should not throw error for valid settings', () => {
|
||||
const validSettings: SettingsConfiguration = [
|
||||
{
|
||||
key: 'themeColor',
|
||||
component: 'input',
|
||||
type: 'string',
|
||||
default: '#ffffff',
|
||||
label: 'Theme Color',
|
||||
columns: 2,
|
||||
columnSpan: 1,
|
||||
optionType: 'model',
|
||||
},
|
||||
{
|
||||
key: 'fontSize',
|
||||
component: 'slider',
|
||||
type: 'number',
|
||||
range: { min: 8, max: 36 },
|
||||
default: 14,
|
||||
columnSpan: 2,
|
||||
},
|
||||
];
|
||||
|
||||
expect(() => validateSettingDefinitions(validSettings)).not.toThrow();
|
||||
});
|
||||
|
||||
// Test for incorrectly configured columns
|
||||
test('should throw error for invalid columns configuration', () => {
|
||||
const invalidSettings: SettingsConfiguration = [
|
||||
{
|
||||
key: 'themeColor',
|
||||
component: 'input',
|
||||
type: 'string',
|
||||
columns: 5,
|
||||
},
|
||||
];
|
||||
|
||||
expect(() => validateSettingDefinitions(invalidSettings)).toThrow(ZodError);
|
||||
});
|
||||
|
||||
test('should correctly handle columnSpan defaulting based on columns', () => {
|
||||
const settingsWithColumnAdjustment: SettingsConfiguration = [
|
||||
{
|
||||
key: 'fontSize',
|
||||
component: 'slider',
|
||||
type: 'number',
|
||||
columns: 4,
|
||||
range: { min: 8, max: 14 },
|
||||
default: 11,
|
||||
},
|
||||
];
|
||||
|
||||
expect(() => validateSettingDefinitions(settingsWithColumnAdjustment)).not.toThrow();
|
||||
});
|
||||
|
||||
// Test for label defaulting to key if not provided
|
||||
test('label should default to key if not explicitly set', () => {
|
||||
const settingsWithDefaultLabel: SettingsConfiguration = [
|
||||
{ key: 'fontWeight', component: 'dropdown', type: 'string', options: ['normal', 'bold'] },
|
||||
];
|
||||
|
||||
expect(() => validateSettingDefinitions(settingsWithDefaultLabel)).not.toThrow();
|
||||
expect(settingsWithDefaultLabel[0].label).toBe('fontWeight');
|
||||
});
|
||||
|
||||
// Test for minText and maxText in input/textarea component
|
||||
test('should throw error for negative minText or maxText', () => {
|
||||
const settingsWithNegativeTextLimits: SettingsConfiguration = [
|
||||
{ key: 'biography', component: 'textarea', type: 'string', minText: -1 },
|
||||
];
|
||||
|
||||
expect(() => validateSettingDefinitions(settingsWithNegativeTextLimits)).toThrow(ZodError);
|
||||
});
|
||||
|
||||
// Validate optionType with tConversationSchema
|
||||
test('should throw error for optionType "conversation" not matching schema', () => {
|
||||
const settingsWithInvalidConversationOptionType: SettingsConfiguration = [
|
||||
{ key: 'userAge', component: 'input', type: 'number', optionType: 'conversation' },
|
||||
];
|
||||
|
||||
expect(() => validateSettingDefinitions(settingsWithInvalidConversationOptionType)).toThrow(
|
||||
ZodError,
|
||||
);
|
||||
});
|
||||
|
||||
// Test for columnSpan defaulting and label defaulting to key
|
||||
test('columnSpan defaults based on columns and label defaults to key if not set', () => {
|
||||
const settings: SettingsConfiguration = [
|
||||
{
|
||||
key: 'textSize',
|
||||
type: 'number',
|
||||
component: 'slider',
|
||||
range: { min: 10, max: 20 },
|
||||
columns: 4,
|
||||
},
|
||||
];
|
||||
|
||||
validateSettingDefinitions(settings); // Perform validation which also mutates settings with default values
|
||||
|
||||
expect(settings[0].columnSpan).toBe(2); // Expects columnSpan to default based on columns
|
||||
expect(settings[0].label).toBe('textSize'); // Expects label to default to key
|
||||
});
|
||||
|
||||
// Test for errors thrown due to invalid columns value
|
||||
test('throws error if columns value is out of range', () => {
|
||||
const settings: SettingsConfiguration = [
|
||||
{
|
||||
key: 'themeMode',
|
||||
type: 'string',
|
||||
component: 'dropdown',
|
||||
options: ['dark', 'light'],
|
||||
columns: 5,
|
||||
},
|
||||
];
|
||||
|
||||
expect(() => validateSettingDefinitions(settings)).toThrow(ZodError);
|
||||
});
|
||||
|
||||
// Test range validation for slider component
|
||||
test('slider component range validation', () => {
|
||||
const settings: SettingsConfiguration = [
|
||||
{ key: 'volume', type: 'number', component: 'slider' }, // Missing range
|
||||
];
|
||||
|
||||
expect(() => validateSettingDefinitions(settings)).toThrow(ZodError);
|
||||
});
|
||||
|
||||
// Test options validation for enum type in slider component
|
||||
test('slider component with enum type requires at least 2 options', () => {
|
||||
const settings: SettingsConfiguration = [
|
||||
{ key: 'color', type: 'enum', component: 'slider', options: ['red'] }, // Not enough options
|
||||
];
|
||||
|
||||
expect(() => validateSettingDefinitions(settings)).toThrow(ZodError);
|
||||
});
|
||||
|
||||
// Test checkbox component options validation
|
||||
test('checkbox component must have 1-2 options if options are provided', () => {
|
||||
const settings: SettingsConfiguration = [
|
||||
{
|
||||
key: 'agreeToTerms',
|
||||
type: 'boolean',
|
||||
component: 'checkbox',
|
||||
options: ['Yes', 'No', 'Maybe'],
|
||||
}, // Too many options
|
||||
];
|
||||
|
||||
expect(() => validateSettingDefinitions(settings)).toThrow(ZodError);
|
||||
});
|
||||
|
||||
// Test dropdown component options validation
|
||||
test('dropdown component requires at least 2 options', () => {
|
||||
const settings: SettingsConfiguration = [
|
||||
{ key: 'country', type: 'enum', component: 'dropdown', options: ['USA'] }, // Not enough options
|
||||
];
|
||||
|
||||
expect(() => validateSettingDefinitions(settings)).toThrow(ZodError);
|
||||
});
|
||||
|
||||
// Validate minText and maxText constraints in input and textarea
|
||||
test('validate minText and maxText constraints', () => {
|
||||
const settings: SettingsConfiguration = [
|
||||
{ key: 'biography', type: 'string', component: 'textarea', minText: 10, maxText: 5 }, // Incorrect minText and maxText
|
||||
];
|
||||
|
||||
expect(() => validateSettingDefinitions(settings)).toThrow(ZodError);
|
||||
});
|
||||
|
||||
// Validate optionType constraint with tConversationSchema
|
||||
test('validate optionType constraint with tConversationSchema', () => {
|
||||
const settings: SettingsConfiguration = [
|
||||
{ key: 'userAge', type: 'number', component: 'input', optionType: 'conversation' }, // No corresponding schema in tConversationSchema
|
||||
];
|
||||
|
||||
expect(() => validateSettingDefinitions(settings)).toThrow(ZodError);
|
||||
});
|
||||
|
||||
// Validate correct handling of boolean settings with default values
|
||||
test('correct handling of boolean settings with defaults', () => {
|
||||
const settings: SettingsConfiguration = [
|
||||
{ key: 'enableFeatureX', type: 'boolean', component: 'switch' }, // Missing default, should default to false
|
||||
];
|
||||
|
||||
validateSettingDefinitions(settings); // This would populate default values where missing
|
||||
|
||||
expect(settings[0].default).toBe(false); // Expects default to be false for boolean without explicit default
|
||||
});
|
||||
|
||||
// Validate that number slider without default uses middle of range
|
||||
test('number slider without default uses middle of range', () => {
|
||||
const settings: SettingsConfiguration = [
|
||||
{ key: 'brightness', type: 'number', component: 'slider', range: { min: 0, max: 100 } }, // Missing default
|
||||
];
|
||||
|
||||
validateSettingDefinitions(settings); // This would populate default values where missing
|
||||
|
||||
expect(settings[0].default).toBe(50); // Expects default to be midpoint of range
|
||||
});
|
||||
});
|
||||
|
||||
const settingsConfiguration: SettingsConfiguration = [
|
||||
{
|
||||
key: 'temperature',
|
||||
description:
|
||||
'Higher values = more random, while lower values = more focused and deterministic. We recommend altering this or Top P but not both.',
|
||||
type: 'number',
|
||||
default: 1,
|
||||
range: {
|
||||
min: 0,
|
||||
max: 2,
|
||||
step: 0.01,
|
||||
},
|
||||
component: 'slider',
|
||||
},
|
||||
{
|
||||
key: 'top_p',
|
||||
description:
|
||||
'An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We recommend altering this or temperature but not both.',
|
||||
type: 'number',
|
||||
default: 1,
|
||||
range: {
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
},
|
||||
component: 'slider',
|
||||
},
|
||||
{
|
||||
key: 'presence_penalty',
|
||||
description:
|
||||
'Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model\'s likelihood to talk about new topics.',
|
||||
type: 'number',
|
||||
default: 0,
|
||||
range: {
|
||||
min: -2,
|
||||
max: 2,
|
||||
step: 0.01,
|
||||
},
|
||||
component: 'slider',
|
||||
},
|
||||
{
|
||||
key: 'frequency_penalty',
|
||||
description:
|
||||
'Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model\'s likelihood to repeat the same line verbatim.',
|
||||
type: 'number',
|
||||
default: 0,
|
||||
range: {
|
||||
min: -2,
|
||||
max: 2,
|
||||
step: 0.01,
|
||||
},
|
||||
component: 'slider',
|
||||
},
|
||||
{
|
||||
key: 'resendFiles',
|
||||
description:
|
||||
'Resend all previously attached files. Note: this will increase token cost and you may experience errors with many attachments.',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
component: 'switch',
|
||||
},
|
||||
{
|
||||
key: 'imageDetail',
|
||||
description:
|
||||
'The resolution for Vision requests. "Low" is cheaper and faster, "High" is more detailed and expensive, and "Auto" will automatically choose between the two based on the image resolution.',
|
||||
type: 'enum',
|
||||
default: 'auto',
|
||||
options: ['low', 'high', 'auto'],
|
||||
component: 'slider',
|
||||
},
|
||||
{
|
||||
key: 'promptPrefix',
|
||||
type: 'string',
|
||||
default: '',
|
||||
component: 'input',
|
||||
placeholder: 'Set custom instructions to include in System Message. Default: none',
|
||||
},
|
||||
{
|
||||
key: 'chatGptLabel',
|
||||
type: 'string',
|
||||
default: '',
|
||||
component: 'input',
|
||||
placeholder: 'Set a custom name for your AI',
|
||||
},
|
||||
];
|
||||
|
||||
describe('Settings Validation and Schema Generation', () => {
|
||||
// Test 1: Validate settings definitions do not throw for valid configuration
|
||||
test('validateSettingDefinitions does not throw for valid configuration', () => {
|
||||
expect(() => validateSettingDefinitions(settingsConfiguration)).not.toThrow();
|
||||
});
|
||||
|
||||
test('validateSettingDefinitions throws for invalid type in settings', () => {
|
||||
const settingsWithInvalidType = [
|
||||
...settingsConfiguration,
|
||||
{
|
||||
key: 'newSetting',
|
||||
description: 'A setting with an unsupported type',
|
||||
type: 'unsupportedType', // Assuming 'unsupportedType' is not supported
|
||||
component: 'input',
|
||||
},
|
||||
];
|
||||
|
||||
expect(() =>
|
||||
validateSettingDefinitions(settingsWithInvalidType as SettingsConfiguration),
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
test('validateSettingDefinitions throws for missing required fields', () => {
|
||||
const settingsMissingRequiredField = [
|
||||
...settingsConfiguration,
|
||||
{
|
||||
key: 'incompleteSetting',
|
||||
type: 'number',
|
||||
// Missing 'component',
|
||||
},
|
||||
];
|
||||
|
||||
expect(() =>
|
||||
validateSettingDefinitions(settingsMissingRequiredField as SettingsConfiguration),
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
test('validateSettingDefinitions throws for default value out of range', () => {
|
||||
const settingsOutOfRange = [
|
||||
...settingsConfiguration,
|
||||
{
|
||||
key: 'rangeTestSetting',
|
||||
description: 'A setting with default value out of specified range',
|
||||
type: 'number',
|
||||
default: 5,
|
||||
range: {
|
||||
min: 0,
|
||||
max: 1,
|
||||
},
|
||||
component: 'slider',
|
||||
},
|
||||
];
|
||||
|
||||
expect(() => validateSettingDefinitions(settingsOutOfRange as SettingsConfiguration)).toThrow();
|
||||
});
|
||||
|
||||
test('validateSettingDefinitions throws for enum setting with incorrect default', () => {
|
||||
const settingsWithIncorrectEnumDefault = [
|
||||
...settingsConfiguration,
|
||||
{
|
||||
key: 'enumSetting',
|
||||
description: 'Enum setting with a default not in options',
|
||||
type: 'enum',
|
||||
default: 'unlistedOption',
|
||||
options: ['option1', 'option2'],
|
||||
component: 'dropdown',
|
||||
},
|
||||
];
|
||||
|
||||
expect(() =>
|
||||
validateSettingDefinitions(settingsWithIncorrectEnumDefault as SettingsConfiguration),
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
// Test 2: Generate dynamic schema and validate correct input
|
||||
test('generateDynamicSchema generates a schema that validates correct input', () => {
|
||||
const schema = generateDynamicSchema(settingsConfiguration);
|
||||
const validInput = {
|
||||
temperature: 0.5,
|
||||
top_p: 0.8,
|
||||
presence_penalty: 1,
|
||||
frequency_penalty: -1,
|
||||
resendFiles: true,
|
||||
imageDetail: 'high',
|
||||
promptPrefix: 'Hello, AI.',
|
||||
chatGptLabel: 'My Custom AI',
|
||||
};
|
||||
|
||||
expect(schema.parse(validInput)).toEqual(validInput);
|
||||
});
|
||||
|
||||
// Test 3: Generate dynamic schema and catch invalid input
|
||||
test('generateDynamicSchema generates a schema that catches invalid input and provides detailed errors', async () => {
|
||||
const schema = generateDynamicSchema(settingsConfiguration);
|
||||
const invalidInput: z.infer<typeof schema> = {
|
||||
temperature: 2.5, // Out of range
|
||||
top_p: -0.5, // Out of range
|
||||
presence_penalty: 3, // Out of range
|
||||
frequency_penalty: -3, // Out of range
|
||||
resendFiles: 'yes', // Wrong type
|
||||
imageDetail: 'ultra', // Invalid option
|
||||
promptPrefix: 123, // Wrong type
|
||||
chatGptLabel: true, // Wrong type
|
||||
};
|
||||
|
||||
const result = schema.safeParse(invalidInput);
|
||||
expect(result.success).toBeFalsy();
|
||||
if (!result.success) {
|
||||
const errorPaths = result.error.issues.map((issue) => issue.path.join('.'));
|
||||
expect(errorPaths).toContain('temperature');
|
||||
expect(errorPaths).toContain('top_p');
|
||||
expect(errorPaths).toContain('presence_penalty');
|
||||
expect(errorPaths).toContain('frequency_penalty');
|
||||
expect(errorPaths).toContain('resendFiles');
|
||||
expect(errorPaths).toContain('imageDetail');
|
||||
expect(errorPaths).toContain('promptPrefix');
|
||||
expect(errorPaths).toContain('chatGptLabel');
|
||||
}
|
||||
});
|
||||
});
|
||||
474
packages/data-provider/src/generate.ts
Normal file
474
packages/data-provider/src/generate.ts
Normal file
|
|
@ -0,0 +1,474 @@
|
|||
import { z, ZodError, ZodIssueCode } from 'zod';
|
||||
import { tConversationSchema, googleSettings as google, openAISettings as openAI } from './schemas';
|
||||
import type { ZodIssue } from 'zod';
|
||||
import type { TConversation, TSetOption } from './schemas';
|
||||
|
||||
export type GoogleSettings = Partial<typeof google>;
|
||||
export type OpenAISettings = Partial<typeof google>;
|
||||
|
||||
export type ComponentType = 'input' | 'textarea' | 'slider' | 'checkbox' | 'switch' | 'dropdown';
|
||||
|
||||
export type OptionType = 'conversation' | 'model' | 'custom';
|
||||
|
||||
export enum ComponentTypes {
|
||||
Input = 'input',
|
||||
Textarea = 'textarea',
|
||||
Slider = 'slider',
|
||||
Checkbox = 'checkbox',
|
||||
Switch = 'switch',
|
||||
Dropdown = 'dropdown',
|
||||
}
|
||||
|
||||
export enum OptionTypes {
|
||||
Conversation = 'conversation',
|
||||
Model = 'model',
|
||||
Custom = 'custom',
|
||||
}
|
||||
export interface SettingDefinition {
|
||||
key: string;
|
||||
description?: string;
|
||||
type: 'number' | 'boolean' | 'string' | 'enum';
|
||||
default?: number | boolean | string;
|
||||
showDefault?: boolean;
|
||||
options?: string[];
|
||||
range?: SettingRange;
|
||||
enumMappings?: Record<string, number | boolean | string>;
|
||||
component: ComponentType;
|
||||
optionType?: OptionType;
|
||||
columnSpan?: number;
|
||||
columns?: number;
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
labelCode?: boolean;
|
||||
placeholderCode?: boolean;
|
||||
descriptionCode?: boolean;
|
||||
minText?: number;
|
||||
maxText?: number;
|
||||
includeInput?: boolean; // Specific to slider component
|
||||
}
|
||||
|
||||
export type DynamicSettingProps = Partial<SettingDefinition> & {
|
||||
readonly?: boolean;
|
||||
settingKey: string;
|
||||
setOption: TSetOption;
|
||||
defaultValue?: number | boolean | string;
|
||||
};
|
||||
|
||||
const requiredSettingFields = ['key', 'type', 'component'];
|
||||
|
||||
export interface SettingRange {
|
||||
min: number;
|
||||
max: number;
|
||||
step?: number;
|
||||
}
|
||||
|
||||
export type SettingsConfiguration = SettingDefinition[];
|
||||
|
||||
export function generateDynamicSchema(settings: SettingsConfiguration) {
|
||||
const schemaFields: { [key: string]: z.ZodTypeAny } = {};
|
||||
|
||||
for (const setting of settings) {
|
||||
const { key, type, default: defaultValue, range, options, minText, maxText } = setting;
|
||||
|
||||
if (type === 'number') {
|
||||
let schema = z.number();
|
||||
if (range) {
|
||||
schema = schema.min(range.min);
|
||||
schema = schema.max(range.max);
|
||||
}
|
||||
if (typeof defaultValue === 'number') {
|
||||
schemaFields[key] = schema.default(defaultValue);
|
||||
} else {
|
||||
schemaFields[key] = schema;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type === 'boolean') {
|
||||
const schema = z.boolean();
|
||||
if (typeof defaultValue === 'boolean') {
|
||||
schemaFields[key] = schema.default(defaultValue);
|
||||
} else {
|
||||
schemaFields[key] = schema;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type === 'string') {
|
||||
let schema = z.string();
|
||||
if (minText) {
|
||||
schema = schema.min(minText);
|
||||
}
|
||||
if (maxText) {
|
||||
schema = schema.max(maxText);
|
||||
}
|
||||
if (typeof defaultValue === 'string') {
|
||||
schemaFields[key] = schema.default(defaultValue);
|
||||
} else {
|
||||
schemaFields[key] = schema;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type === 'enum') {
|
||||
if (!options || options.length === 0) {
|
||||
console.warn(`Missing or empty 'options' for enum setting '${key}'.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const schema = z.enum(options as [string, ...string[]]);
|
||||
if (typeof defaultValue === 'string') {
|
||||
schemaFields[key] = schema.default(defaultValue);
|
||||
} else {
|
||||
schemaFields[key] = schema;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
console.warn(`Unsupported setting type: ${type}`);
|
||||
}
|
||||
|
||||
return z.object(schemaFields);
|
||||
}
|
||||
|
||||
const ZodTypeToSettingType: Record<string, string | undefined> = {
|
||||
ZodString: 'string',
|
||||
ZodNumber: 'number',
|
||||
ZodBoolean: 'boolean',
|
||||
};
|
||||
|
||||
const minColumns = 1;
|
||||
const maxColumns = 4;
|
||||
const minSliderOptions = 2;
|
||||
const minDropdownOptions = 2;
|
||||
|
||||
/**
|
||||
* Validates the provided setting using the constraints unique to each component type.
|
||||
* @throws {ZodError} Throws a ZodError if any validation fails.
|
||||
*/
|
||||
export function validateSettingDefinitions(settings: SettingsConfiguration): void {
|
||||
const errors: ZodIssue[] = [];
|
||||
// Validate columns
|
||||
const columnsSet = new Set<number>();
|
||||
for (const setting of settings) {
|
||||
if (setting.columns !== undefined) {
|
||||
if (setting.columns < minColumns || setting.columns > maxColumns) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `Invalid columns value for setting ${setting.key}. Must be between ${minColumns} and ${maxColumns}.`,
|
||||
path: ['columns'],
|
||||
});
|
||||
} else {
|
||||
columnsSet.add(setting.columns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const columns = columnsSet.size === 1 ? columnsSet.values().next().value : 2;
|
||||
|
||||
for (const setting of settings) {
|
||||
for (const field of requiredSettingFields) {
|
||||
if (setting[field as keyof SettingDefinition] === undefined) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `Missing required field ${field} for setting ${setting.key}.`,
|
||||
path: [field],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// check accepted types
|
||||
if (!['number', 'boolean', 'string', 'enum'].includes(setting.type)) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `Invalid type for setting ${setting.key}. Must be one of 'number', 'boolean', 'string', 'enum'.`,
|
||||
path: ['type'],
|
||||
});
|
||||
}
|
||||
|
||||
// Predefined constraints based on components
|
||||
if (setting.component === 'input' || setting.component === 'textarea') {
|
||||
if (setting.type === 'number' && setting.component === 'textarea') {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `Textarea component for setting ${setting.key} must have type string.`,
|
||||
path: ['type'],
|
||||
});
|
||||
// continue;
|
||||
}
|
||||
|
||||
if (
|
||||
setting.minText !== undefined &&
|
||||
setting.maxText !== undefined &&
|
||||
setting.minText > setting.maxText
|
||||
) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `For setting ${setting.key}, minText cannot be greater than maxText.`,
|
||||
path: [setting.key, 'minText', 'maxText'],
|
||||
});
|
||||
// continue;
|
||||
}
|
||||
if (!setting.placeholder) {
|
||||
setting.placeholder = '';
|
||||
} // Default placeholder
|
||||
}
|
||||
|
||||
if (setting.component === 'slider') {
|
||||
if (setting.type === 'number' && !setting.range) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `Slider component for setting ${setting.key} must have a range if type is number.`,
|
||||
path: ['range'],
|
||||
});
|
||||
// continue;
|
||||
}
|
||||
if (
|
||||
setting.type === 'enum' &&
|
||||
(!setting.options || setting.options.length < minSliderOptions)
|
||||
) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `Slider component for setting ${setting.key} requires at least ${minSliderOptions} options for enum type.`,
|
||||
path: ['options'],
|
||||
});
|
||||
// continue;
|
||||
}
|
||||
setting.includeInput = setting.type === 'number' ? setting.includeInput ?? true : false; // Default to true if type is number
|
||||
}
|
||||
|
||||
if (setting.component === 'slider' && setting.type === 'number') {
|
||||
if (setting.default === undefined && setting.range) {
|
||||
// Set default to the middle of the range if unspecified
|
||||
setting.default = Math.round((setting.range.min + setting.range.max) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (setting.component === 'checkbox' || setting.component === 'switch') {
|
||||
if (setting.options && setting.options.length > 2) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `Checkbox/Switch component for setting ${setting.key} must have 1-2 options.`,
|
||||
path: ['options'],
|
||||
});
|
||||
// continue;
|
||||
}
|
||||
if (!setting.default && setting.type === 'boolean') {
|
||||
setting.default = false; // Default to false if type is boolean
|
||||
}
|
||||
}
|
||||
|
||||
if (setting.component === 'dropdown') {
|
||||
if (!setting.options || setting.options.length < minDropdownOptions) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `Dropdown component for setting ${setting.key} requires at least ${minDropdownOptions} options.`,
|
||||
path: ['options'],
|
||||
});
|
||||
// continue;
|
||||
}
|
||||
if (!setting.default && setting.options && setting.options.length > 0) {
|
||||
setting.default = setting.options[0]; // Default to first option if not specified
|
||||
}
|
||||
}
|
||||
|
||||
// Default columnSpan
|
||||
if (!setting.columnSpan) {
|
||||
setting.columnSpan = Math.floor(columns / 2);
|
||||
}
|
||||
|
||||
// Default label to key
|
||||
if (!setting.label) {
|
||||
setting.label = setting.key;
|
||||
}
|
||||
|
||||
// Validate minText and maxText for input/textarea
|
||||
if (setting.component === 'input' || setting.component === 'textarea') {
|
||||
if (setting.minText !== undefined && setting.minText < 0) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `Invalid minText value for setting ${setting.key}. Must be non-negative.`,
|
||||
path: ['minText'],
|
||||
});
|
||||
}
|
||||
if (setting.maxText !== undefined && setting.maxText < 0) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `Invalid maxText value for setting ${setting.key}. Must be non-negative.`,
|
||||
path: ['maxText'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Validate optionType and conversation schema
|
||||
if (setting.optionType !== OptionTypes.Custom) {
|
||||
const conversationSchema = tConversationSchema.shape[setting.key as keyof TConversation];
|
||||
if (!conversationSchema) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `Setting ${setting.key} with optionType "${setting.optionType}" must be defined in tConversationSchema.`,
|
||||
path: ['optionType'],
|
||||
});
|
||||
} else {
|
||||
const zodType = conversationSchema._def.typeName;
|
||||
const settingTypeEquivalent = ZodTypeToSettingType[zodType] || null;
|
||||
if (settingTypeEquivalent !== setting.type) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `Setting ${setting.key} with optionType "${setting.optionType}" must match the type defined in tConversationSchema.`,
|
||||
path: ['optionType'],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Default value checks */
|
||||
if (setting.type === 'number' && isNaN(setting.default as number)) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `Invalid default value for setting ${setting.key}. Must be a number.`,
|
||||
path: ['default'],
|
||||
});
|
||||
}
|
||||
|
||||
if (setting.type === 'boolean' && typeof setting.default !== 'boolean') {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `Invalid default value for setting ${setting.key}. Must be a boolean.`,
|
||||
path: ['default'],
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
(setting.type === 'string' || setting.type === 'enum') &&
|
||||
typeof setting.default !== 'string'
|
||||
) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `Invalid default value for setting ${setting.key}. Must be a string.`,
|
||||
path: ['default'],
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
setting.type === 'enum' &&
|
||||
setting.options &&
|
||||
!setting.options.includes(setting.default as string)
|
||||
) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `Invalid default value for setting ${
|
||||
setting.key
|
||||
}. Must be one of the options: [${setting.options.join(', ')}].`,
|
||||
path: ['default'],
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
setting.type === 'number' &&
|
||||
setting.range &&
|
||||
typeof setting.default === 'number' &&
|
||||
(setting.default < setting.range.min || setting.default > setting.range.max)
|
||||
) {
|
||||
errors.push({
|
||||
code: ZodIssueCode.custom,
|
||||
message: `Invalid default value for setting ${setting.key}. Must be within the range [${setting.range.min}, ${setting.range.max}].`,
|
||||
path: ['default'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new ZodError(errors);
|
||||
}
|
||||
}
|
||||
|
||||
export const generateOpenAISchema = (customOpenAI: OpenAISettings) => {
|
||||
const defaults = { ...openAI, ...customOpenAI };
|
||||
return tConversationSchema
|
||||
.pick({
|
||||
model: true,
|
||||
chatGptLabel: true,
|
||||
promptPrefix: true,
|
||||
temperature: true,
|
||||
top_p: true,
|
||||
presence_penalty: true,
|
||||
frequency_penalty: true,
|
||||
resendFiles: true,
|
||||
imageDetail: true,
|
||||
})
|
||||
.transform((obj) => ({
|
||||
...obj,
|
||||
model: obj.model ?? defaults.model.default,
|
||||
chatGptLabel: obj.chatGptLabel ?? null,
|
||||
promptPrefix: obj.promptPrefix ?? null,
|
||||
temperature: obj.temperature ?? defaults.temperature.default,
|
||||
top_p: obj.top_p ?? defaults.top_p.default,
|
||||
presence_penalty: obj.presence_penalty ?? defaults.presence_penalty.default,
|
||||
frequency_penalty: obj.frequency_penalty ?? defaults.frequency_penalty.default,
|
||||
resendFiles:
|
||||
typeof obj.resendFiles === 'boolean' ? obj.resendFiles : defaults.resendFiles.default,
|
||||
imageDetail: obj.imageDetail ?? defaults.imageDetail.default,
|
||||
}))
|
||||
.catch(() => ({
|
||||
model: defaults.model.default,
|
||||
chatGptLabel: null,
|
||||
promptPrefix: null,
|
||||
temperature: defaults.temperature.default,
|
||||
top_p: defaults.top_p.default,
|
||||
presence_penalty: defaults.presence_penalty.default,
|
||||
frequency_penalty: defaults.frequency_penalty.default,
|
||||
resendFiles: defaults.resendFiles.default,
|
||||
imageDetail: defaults.imageDetail.default,
|
||||
}));
|
||||
};
|
||||
|
||||
export const generateGoogleSchema = (customGoogle: GoogleSettings) => {
|
||||
const defaults = { ...google, ...customGoogle };
|
||||
return tConversationSchema
|
||||
.pick({
|
||||
model: true,
|
||||
modelLabel: true,
|
||||
promptPrefix: true,
|
||||
examples: true,
|
||||
temperature: true,
|
||||
maxOutputTokens: true,
|
||||
topP: true,
|
||||
topK: true,
|
||||
})
|
||||
.transform((obj) => {
|
||||
const isGeminiPro = obj?.model?.toLowerCase()?.includes('gemini-pro');
|
||||
|
||||
const maxOutputTokensMax = isGeminiPro
|
||||
? defaults.maxOutputTokens.maxGeminiPro
|
||||
: defaults.maxOutputTokens.max;
|
||||
const maxOutputTokensDefault = isGeminiPro
|
||||
? defaults.maxOutputTokens.defaultGeminiPro
|
||||
: defaults.maxOutputTokens.default;
|
||||
|
||||
let maxOutputTokens = obj.maxOutputTokens ?? maxOutputTokensDefault;
|
||||
maxOutputTokens = Math.min(maxOutputTokens, maxOutputTokensMax);
|
||||
|
||||
return {
|
||||
...obj,
|
||||
model: obj.model ?? defaults.model.default,
|
||||
modelLabel: obj.modelLabel ?? null,
|
||||
promptPrefix: obj.promptPrefix ?? null,
|
||||
examples: obj.examples ?? [{ input: { content: '' }, output: { content: '' } }],
|
||||
temperature: obj.temperature ?? defaults.temperature.default,
|
||||
maxOutputTokens,
|
||||
topP: obj.topP ?? defaults.topP.default,
|
||||
topK: obj.topK ?? defaults.topK.default,
|
||||
};
|
||||
})
|
||||
.catch(() => ({
|
||||
model: defaults.model.default,
|
||||
modelLabel: null,
|
||||
promptPrefix: null,
|
||||
examples: [{ input: { content: '' }, output: { content: '' } }],
|
||||
temperature: defaults.temperature.default,
|
||||
maxOutputTokens: defaults.maxOutputTokens.default,
|
||||
topP: defaults.topP.default,
|
||||
topK: defaults.topK.default,
|
||||
}));
|
||||
};
|
||||
|
|
@ -4,6 +4,7 @@ export * from './config';
|
|||
export * from './file-config';
|
||||
/* schema helpers */
|
||||
export * from './parsers';
|
||||
export * from './generate';
|
||||
/* types (exports schemas from `./types` as they contain needed in other defs) */
|
||||
export * from './types';
|
||||
export * from './types/assistants';
|
||||
|
|
|
|||
|
|
@ -17,6 +17,26 @@ export enum EModelEndpoint {
|
|||
custom = 'custom',
|
||||
}
|
||||
|
||||
export enum ImageDetail {
|
||||
low = 'low',
|
||||
auto = 'auto',
|
||||
high = 'high',
|
||||
}
|
||||
|
||||
export const imageDetailNumeric = {
|
||||
[ImageDetail.low]: 0,
|
||||
[ImageDetail.auto]: 1,
|
||||
[ImageDetail.high]: 2,
|
||||
};
|
||||
|
||||
export const imageDetailValue = {
|
||||
0: ImageDetail.low,
|
||||
1: ImageDetail.auto,
|
||||
2: ImageDetail.high,
|
||||
};
|
||||
|
||||
export const eImageDetailSchema = z.nativeEnum(ImageDetail);
|
||||
|
||||
export const defaultAssistantFormValues = {
|
||||
assistant: '',
|
||||
id: '',
|
||||
|
|
@ -46,38 +66,77 @@ export const ImageVisionTool: FunctionTool = {
|
|||
export const isImageVisionTool = (tool: FunctionTool | FunctionToolCall) =>
|
||||
tool.type === 'function' && tool.function?.name === ImageVisionTool?.function?.name;
|
||||
|
||||
export const endpointSettings = {
|
||||
[EModelEndpoint.google]: {
|
||||
model: {
|
||||
default: 'chat-bison',
|
||||
},
|
||||
maxOutputTokens: {
|
||||
min: 1,
|
||||
max: 2048,
|
||||
step: 1,
|
||||
default: 1024,
|
||||
maxGeminiPro: 8192,
|
||||
defaultGeminiPro: 8192,
|
||||
},
|
||||
temperature: {
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
default: 0.2,
|
||||
},
|
||||
topP: {
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
default: 0.8,
|
||||
},
|
||||
topK: {
|
||||
min: 1,
|
||||
max: 40,
|
||||
step: 0.01,
|
||||
default: 40,
|
||||
},
|
||||
export const openAISettings = {
|
||||
model: {
|
||||
default: 'gpt-3.5-turbo',
|
||||
},
|
||||
temperature: {
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
default: 1,
|
||||
},
|
||||
top_p: {
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
default: 1,
|
||||
},
|
||||
presence_penalty: {
|
||||
min: 0,
|
||||
max: 2,
|
||||
step: 0.01,
|
||||
default: 0,
|
||||
},
|
||||
frequency_penalty: {
|
||||
min: 0,
|
||||
max: 2,
|
||||
step: 0.01,
|
||||
default: 0,
|
||||
},
|
||||
resendFiles: {
|
||||
default: true,
|
||||
},
|
||||
imageDetail: {
|
||||
default: ImageDetail.auto,
|
||||
},
|
||||
};
|
||||
|
||||
export const googleSettings = {
|
||||
model: {
|
||||
default: 'chat-bison',
|
||||
},
|
||||
maxOutputTokens: {
|
||||
min: 1,
|
||||
max: 2048,
|
||||
step: 1,
|
||||
default: 1024,
|
||||
maxGeminiPro: 8192,
|
||||
defaultGeminiPro: 8192,
|
||||
},
|
||||
temperature: {
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
default: 0.2,
|
||||
},
|
||||
topP: {
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
default: 0.8,
|
||||
},
|
||||
topK: {
|
||||
min: 1,
|
||||
max: 40,
|
||||
step: 0.01,
|
||||
default: 40,
|
||||
},
|
||||
};
|
||||
|
||||
export const endpointSettings = {
|
||||
[EModelEndpoint.openAI]: openAISettings,
|
||||
[EModelEndpoint.google]: googleSettings,
|
||||
};
|
||||
|
||||
const google = endpointSettings[EModelEndpoint.google];
|
||||
|
|
@ -86,26 +145,6 @@ export const eModelEndpointSchema = z.nativeEnum(EModelEndpoint);
|
|||
|
||||
export const extendedModelEndpointSchema = z.union([eModelEndpointSchema, z.string()]);
|
||||
|
||||
export enum ImageDetail {
|
||||
low = 'low',
|
||||
auto = 'auto',
|
||||
high = 'high',
|
||||
}
|
||||
|
||||
export const imageDetailNumeric = {
|
||||
[ImageDetail.low]: 0,
|
||||
[ImageDetail.auto]: 1,
|
||||
[ImageDetail.high]: 2,
|
||||
};
|
||||
|
||||
export const imageDetailValue = {
|
||||
0: ImageDetail.low,
|
||||
1: ImageDetail.auto,
|
||||
2: ImageDetail.high,
|
||||
};
|
||||
|
||||
export const eImageDetailSchema = z.nativeEnum(ImageDetail);
|
||||
|
||||
export const tPluginAuthConfigSchema = z.object({
|
||||
authField: z.string(),
|
||||
label: z.string(),
|
||||
|
|
@ -278,12 +317,14 @@ export const tPresetUpdateSchema = tConversationSchema.merge(
|
|||
|
||||
export type TPreset = z.infer<typeof tPresetSchema>;
|
||||
|
||||
export type TSetOption = (
|
||||
param: number | string,
|
||||
) => (newValue: number | string | boolean | Partial<TPreset>) => void;
|
||||
|
||||
export type TConversation = z.infer<typeof tConversationSchema> & {
|
||||
presetOverride?: Partial<TPreset>;
|
||||
};
|
||||
|
||||
// type DefaultSchemaValues = Partial<typeof google>;
|
||||
|
||||
export const openAISchema = tConversationSchema
|
||||
.pick({
|
||||
model: true,
|
||||
|
|
@ -298,26 +339,27 @@ export const openAISchema = tConversationSchema
|
|||
})
|
||||
.transform((obj) => ({
|
||||
...obj,
|
||||
model: obj.model ?? 'gpt-3.5-turbo',
|
||||
model: obj.model ?? openAISettings.model.default,
|
||||
chatGptLabel: obj.chatGptLabel ?? null,
|
||||
promptPrefix: obj.promptPrefix ?? null,
|
||||
temperature: obj.temperature ?? 1,
|
||||
top_p: obj.top_p ?? 1,
|
||||
presence_penalty: obj.presence_penalty ?? 0,
|
||||
frequency_penalty: obj.frequency_penalty ?? 0,
|
||||
resendFiles: typeof obj.resendFiles === 'boolean' ? obj.resendFiles : true,
|
||||
imageDetail: obj.imageDetail ?? ImageDetail.auto,
|
||||
temperature: obj.temperature ?? openAISettings.temperature.default,
|
||||
top_p: obj.top_p ?? openAISettings.top_p.default,
|
||||
presence_penalty: obj.presence_penalty ?? openAISettings.presence_penalty.default,
|
||||
frequency_penalty: obj.frequency_penalty ?? openAISettings.frequency_penalty.default,
|
||||
resendFiles:
|
||||
typeof obj.resendFiles === 'boolean' ? obj.resendFiles : openAISettings.resendFiles.default,
|
||||
imageDetail: obj.imageDetail ?? openAISettings.imageDetail.default,
|
||||
}))
|
||||
.catch(() => ({
|
||||
model: 'gpt-3.5-turbo',
|
||||
model: openAISettings.model.default,
|
||||
chatGptLabel: null,
|
||||
promptPrefix: null,
|
||||
temperature: 1,
|
||||
top_p: 1,
|
||||
presence_penalty: 0,
|
||||
frequency_penalty: 0,
|
||||
resendFiles: true,
|
||||
imageDetail: ImageDetail.auto,
|
||||
temperature: openAISettings.temperature.default,
|
||||
top_p: openAISettings.top_p.default,
|
||||
presence_penalty: openAISettings.presence_penalty.default,
|
||||
frequency_penalty: openAISettings.frequency_penalty.default,
|
||||
resendFiles: openAISettings.resendFiles.default,
|
||||
imageDetail: openAISettings.imageDetail.default,
|
||||
}));
|
||||
|
||||
export const googleSchema = tConversationSchema
|
||||
|
|
@ -674,53 +716,3 @@ export const compactPluginsSchema = tConversationSchema
|
|||
return removeNullishValues(newObj);
|
||||
})
|
||||
.catch(() => ({}));
|
||||
|
||||
// const createGoogleSchema = (customGoogle: DefaultSchemaValues) => {
|
||||
// const defaults = { ...google, ...customGoogle };
|
||||
// return tConversationSchema
|
||||
// .pick({
|
||||
// model: true,
|
||||
// modelLabel: true,
|
||||
// promptPrefix: true,
|
||||
// examples: true,
|
||||
// temperature: true,
|
||||
// maxOutputTokens: true,
|
||||
// topP: true,
|
||||
// topK: true,
|
||||
// })
|
||||
// .transform((obj) => {
|
||||
// const isGeminiPro = obj?.model?.toLowerCase()?.includes('gemini-pro');
|
||||
|
||||
// const maxOutputTokensMax = isGeminiPro
|
||||
// ? defaults.maxOutputTokens.maxGeminiPro
|
||||
// : defaults.maxOutputTokens.max;
|
||||
// const maxOutputTokensDefault = isGeminiPro
|
||||
// ? defaults.maxOutputTokens.defaultGeminiPro
|
||||
// : defaults.maxOutputTokens.default;
|
||||
|
||||
// let maxOutputTokens = obj.maxOutputTokens ?? maxOutputTokensDefault;
|
||||
// maxOutputTokens = Math.min(maxOutputTokens, maxOutputTokensMax);
|
||||
|
||||
// return {
|
||||
// ...obj,
|
||||
// model: obj.model ?? defaults.model.default,
|
||||
// modelLabel: obj.modelLabel ?? null,
|
||||
// promptPrefix: obj.promptPrefix ?? null,
|
||||
// examples: obj.examples ?? [{ input: { content: '' }, output: { content: '' } }],
|
||||
// temperature: obj.temperature ?? defaults.temperature.default,
|
||||
// maxOutputTokens,
|
||||
// topP: obj.topP ?? defaults.topP.default,
|
||||
// topK: obj.topK ?? defaults.topK.default,
|
||||
// };
|
||||
// })
|
||||
// .catch(() => ({
|
||||
// model: defaults.model.default,
|
||||
// modelLabel: null,
|
||||
// promptPrefix: null,
|
||||
// examples: [{ input: { content: '' }, output: { content: '' } }],
|
||||
// temperature: defaults.temperature.default,
|
||||
// maxOutputTokens: defaults.maxOutputTokens.default,
|
||||
// topP: defaults.topP.default,
|
||||
// topK: defaults.topK.default,
|
||||
// }));
|
||||
// };
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue