Skip to content

Commit 203ba44

Browse files
authored
feat: support amazon bedrock deepseek-r1 model (#19)
1 parent 6875051 commit 203ba44

File tree

7 files changed

+62
-20
lines changed

7 files changed

+62
-20
lines changed

react-native/src/api/bedrock-api.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
} from '../chat/util/BedrockMessageConvertor.ts';
2828
import { invokeOpenAIWithCallBack } from './open-api.ts';
2929
import { invokeOllamaWithCallBack } from './ollama-api.ts';
30-
import { BedrockThinkingModels } from '../storage/Constants.ts';
30+
import { BedrockThinkingModels, DeepSeekModels } from '../storage/Constants.ts';
3131

3232
type CallbackFunction = (
3333
result: string,
@@ -45,7 +45,9 @@ export const invokeBedrockWithCallBack = async (
4545
controller: AbortController,
4646
callback: CallbackFunction
4747
) => {
48-
const isDeepSeek = getTextModel().modelId.includes('deepseek');
48+
const isDeepSeek = DeepSeekModels.some(
49+
model => model.modelId === getTextModel().modelId
50+
);
4951
const isOpenAI = getTextModel().modelId.includes('gpt');
5052
const isOllama = getTextModel().modelId.startsWith('ollama-');
5153
if (chatMode === ChatMode.Text && (isDeepSeek || isOpenAI || isOllama)) {

react-native/src/chat/component/CustomMessageComponent.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ import { isMac } from '../../App.tsx';
3131
import { CustomTokenizer } from './markdown/CustomTokenizer.ts';
3232
import { State, TapGestureHandler } from 'react-native-gesture-handler';
3333
import Markdown from './markdown/Markdown.tsx';
34+
import { DeepSeekModels } from '../../storage/Constants.ts';
35+
import { getTextModel } from '../../storage/StorageUtils.ts';
3436

3537
interface CustomMessageProps extends MessageProps<SwiftChatMessage> {
3638
chatStatus: ChatStatus;
@@ -78,7 +80,9 @@ const CustomMessageComponent: React.FC<CustomMessageProps> = ({
7880
currentMessage.user._id === 1
7981
? 'You'
8082
: currentMessage.user.name ?? 'Bedrock';
81-
const isDeepSeek = userName.includes('DeepSeek');
83+
const isDeepSeek = DeepSeekModels.some(
84+
model => model.modelId === getTextModel().modelId
85+
);
8286
const isOpenAI = userName.includes('GPT');
8387
const isOllama = userName.includes(':');
8488

react-native/src/chat/component/EmptyChatComponent.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { useNavigation } from '@react-navigation/native';
1111
import { RouteParamList } from '../../types/RouteTypes.ts';
1212
import { DrawerNavigationProp } from '@react-navigation/drawer';
1313
import { getTextModel } from '../../storage/StorageUtils.ts';
14+
import { DeepSeekModels } from '../../storage/Constants.ts';
1415

1516
const isAndroid = Platform.OS === 'android';
1617
type NavigationProp = DrawerNavigationProp<RouteParamList>;
@@ -23,7 +24,9 @@ export const EmptyChatComponent = ({
2324
chatMode,
2425
}: EmptyChatComponentProps): React.ReactElement => {
2526
const navigation = useNavigation<NavigationProp>();
26-
const isDeepSeek = getTextModel().modelId.includes('deepseek');
27+
const isDeepSeek = DeepSeekModels.some(
28+
model => model.modelId === getTextModel().modelId
29+
);
2730
const isOpenAI = getTextModel().modelId.includes('gpt');
2831
const isOllama = getTextModel().modelId.startsWith('ollama-');
2932
const modelIcon = isDeepSeek

react-native/src/chat/component/markdown/useMarkdown.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ const useMarkdown = (
113113
}
114114
}
115115
combinedText += newContent;
116-
if (lastToken.type === 'space') {
116+
if (lastToken.type === 'space' && lastTokenIndex > 0) {
117117
combinedText =
118118
cacheRef.current.cachedTokens[lastTokenIndex - 1].raw + combinedText;
119119
}

react-native/src/settings/ModelPrice.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Usage, UsagePrice } from '../types/Chat.ts';
1+
import { Model, Usage, UsagePrice } from '../types/Chat.ts';
22

33
export const getUsagePrice = (usage: Usage): UsagePrice => {
44
const usagePrice: UsagePrice = {
@@ -81,6 +81,10 @@ function getImagePrice(
8181

8282
export const ModelPrice: ModelPriceType = {
8383
textModelPrices: {
84+
'Bedrock DeepSeek-R1': {
85+
inputTokenPrice: 0.00135,
86+
outputTokenPrice: 0.0054,
87+
},
8488
'DeepSeek-V3': {
8589
inputTokenPrice: 0.00027,
8690
outputTokenPrice: 0.0011,
@@ -372,3 +376,14 @@ export function getTotalImagePrice(usage: Usage[]) {
372376
.toFixed(6)
373377
);
374378
}
379+
380+
export function addBedrockPrefixToDeepseekModels(models: Model[]): void {
381+
for (let i = 0; i < models.length; i++) {
382+
if (models[i].modelName.toLowerCase().includes('deepseek')) {
383+
models[i] = {
384+
...models[i],
385+
modelName: `Bedrock ${models[i].modelName}`,
386+
};
387+
}
388+
}
389+
}

react-native/src/settings/SettingsScreen.tsx

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,15 @@ import { DropdownItem, Model, UpgradeInfo } from '../types/Chat.ts';
5252
import packageJson from '../../package.json';
5353
import { isMac } from '../App.tsx';
5454
import CustomDropdown from './DropdownComponent.tsx';
55-
import { getTotalCost } from './ModelPrice.ts';
55+
import {
56+
addBedrockPrefixToDeepseekModels,
57+
getTotalCost,
58+
} from './ModelPrice.ts';
5659
import {
5760
BedrockThinkingModels,
58-
DeepSeekModels,
61+
DefaultTextModel,
5962
getAllRegions,
60-
getDefaultTextModels,
61-
GPTModels,
63+
getDefaultApiKeyModels,
6264
} from '../storage/Constants.ts';
6365
import CustomTextInput from './CustomTextInput.tsx';
6466
import { requestAllOllamaModels } from '../api/ollama-api.ts';
@@ -138,23 +140,30 @@ function SettingsScreen(): React.JSX.Element {
138140
return;
139141
}
140142
saveOllamaApiURL(ollamaApiUrl);
141-
if (ollamaApiUrl.length > 0) {
142-
fetchAndSetModelNames().then();
143-
}
143+
fetchAndSetModelNames().then();
144144
}, [ollamaApiUrl]);
145145

146146
useEffect(() => {
147+
if (deepSeekApiKey === getDeepSeekApiKey()) {
148+
return;
149+
}
147150
saveDeepSeekApiKey(deepSeekApiKey);
151+
fetchAndSetModelNames().then();
148152
}, [deepSeekApiKey]);
149153

150154
useEffect(() => {
155+
if (openAIApiKey === getOpenAIApiKey()) {
156+
return;
157+
}
151158
saveOpenAIApiKey(openAIApiKey);
159+
fetchAndSetModelNames().then();
152160
}, [openAIApiKey]);
153161

154162
const fetchAndSetModelNames = async () => {
155163
controllerRef.current = new AbortController();
156164
const ollamaModels = await requestAllOllamaModels();
157165
const response = await requestAllModels();
166+
addBedrockPrefixToDeepseekModels(response.textModel);
158167
if (response.imageModel.length > 0) {
159168
setImageModels(response.imageModel);
160169
const imageModel = getImageModel();
@@ -170,13 +179,16 @@ function SettingsScreen(): React.JSX.Element {
170179
}
171180
}
172181
if (response.textModel.length === 0) {
173-
response.textModel = [...getDefaultTextModels(), ...ollamaModels];
182+
response.textModel = [
183+
...DefaultTextModel,
184+
...ollamaModels,
185+
...getDefaultApiKeyModels(),
186+
];
174187
} else {
175188
response.textModel = [
176189
...response.textModel,
177190
...ollamaModels,
178-
...DeepSeekModels,
179-
...GPTModels,
191+
...getDefaultApiKeyModels(),
180192
];
181193
}
182194
setTextModels(response.textModel);

react-native/src/storage/Constants.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Model, SystemPrompt } from '../types/Chat.ts';
2+
import { getDeepSeekApiKey, getOpenAIApiKey } from './StorageUtils.ts';
23

34
const RegionList = [
45
'us-west-2',
@@ -28,13 +29,11 @@ export const DeepSeekModels = [
2829

2930
export const BedrockThinkingModels = ['Claude 3.7 Sonnet'];
3031

31-
const DefaultTextModel = [
32+
export const DefaultTextModel = [
3233
{
3334
modelName: 'Nova Pro',
3435
modelId: 'us.amazon.nova-pro-v1:0',
3536
},
36-
...DeepSeekModels,
37-
...GPTModels,
3837
];
3938

4039
const DefaultImageModel = {
@@ -78,7 +77,14 @@ export function getAllRegions() {
7877
}
7978

8079
export function getDefaultTextModels() {
81-
return DefaultTextModel as Model[];
80+
return [...DefaultTextModel, ...getDefaultApiKeyModels()] as Model[];
81+
}
82+
83+
export function getDefaultApiKeyModels() {
84+
return [
85+
...(getDeepSeekApiKey().length > 0 ? DeepSeekModels : []),
86+
...(getOpenAIApiKey().length > 0 ? GPTModels : []),
87+
] as Model[];
8288
}
8389

8490
export function getDefaultImageModels() {

0 commit comments

Comments
 (0)