mirror of
https://github.com/twentyhq/twenty
synced 2026-04-21 13:37:22 +00:00
## Summary - **Model pricing overhaul**: All model constants updated with accurate pricing in dollars per 1M tokens, including cached input rates, cache creation rates, and tiered >200k context pricing - **New providers**: Added Google (Gemini 3.x), Mistral, and AWS Bedrock as inference providers. Bedrock serves Claude Opus 4.6 and Sonnet 4.6 via AWS, with proper credential handling following the existing S3/SES pattern - **InferenceProvider/ModelFamily split**: Refactored `ModelProvider` into two orthogonal enums — `InferenceProvider` (who serves the model: auth, SDK, metadata format) and `ModelFamily` (who created it: token counting semantics). This eliminates growing `||` chains for token normalization checks like `excludesCachedTokens` - **Billing improvements**: Reasoning tokens charged at output rate, cache token discounts applied accurately, real errors thrown to Sentry on billing failures ## Test plan - [x] All existing unit tests updated and passing (23 tests across 3 test files) - [x] Lint passes for both twenty-server and twenty-front - [ ] CI checks pass Made with [Cursor](https://cursor.com) --------- Co-authored-by: Cursor <cursoragent@cursor.com>
62 lines
1.8 KiB
TypeScript
62 lines
1.8 KiB
TypeScript
import { aiModelsState } from '@/client-config/states/aiModelsState';
|
|
import { type SelectOption } from 'twenty-ui/input';
|
|
|
|
import { DEFAULT_FAST_MODEL } from '@/ai/constants/DefaultFastModel';
|
|
import { DEFAULT_SMART_MODEL } from '@/ai/constants/DefaultSmartModel';
|
|
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
|
import { MODEL_FAMILY_CONFIG } from '~/pages/settings/ai/constants/SettingsAiModelProviders';
|
|
|
|
export const useAiModelOptions = (
|
|
includeDeprecated = false,
|
|
): SelectOption<string>[] => {
|
|
const aiModels = useRecoilValueV2(aiModelsState);
|
|
|
|
return aiModels
|
|
.filter((model) => includeDeprecated || !model.deprecated)
|
|
.map((model) => ({
|
|
value: model.modelId,
|
|
label:
|
|
model.modelId === DEFAULT_FAST_MODEL ||
|
|
model.modelId === DEFAULT_SMART_MODEL
|
|
? model.label
|
|
: `${model.label} (${getModelFamilyLabel(model.modelFamily) ?? model.inferenceProvider})`,
|
|
}))
|
|
.sort((a, b) => a.label.localeCompare(b.label));
|
|
};
|
|
|
|
const getModelFamilyLabel = (
|
|
modelFamily: string | null | undefined,
|
|
): string | undefined => {
|
|
if (!modelFamily) {
|
|
return undefined;
|
|
}
|
|
|
|
return MODEL_FAMILY_CONFIG[modelFamily]?.label || modelFamily;
|
|
};
|
|
|
|
export const useAiModelLabel = (
|
|
modelId: string | undefined,
|
|
includeProvider = true,
|
|
): string => {
|
|
const aiModels = useRecoilValueV2(aiModelsState);
|
|
|
|
if (!modelId) {
|
|
return '';
|
|
}
|
|
|
|
const model = aiModels.find((m) => m.modelId === modelId);
|
|
|
|
if (!model) {
|
|
return modelId;
|
|
}
|
|
|
|
if (
|
|
model.modelId === DEFAULT_FAST_MODEL ||
|
|
model.modelId === DEFAULT_SMART_MODEL ||
|
|
!includeProvider
|
|
) {
|
|
return model.label;
|
|
}
|
|
|
|
return `${model.label} (${getModelFamilyLabel(model.modelFamily) ?? model.inferenceProvider})`;
|
|
};
|