Skip to content

Commit a6e9cb9

Browse files
authored
feat: ai assistant support llm proxy server (#3188)
1 parent 2005927 commit a6e9cb9

8 files changed

Lines changed: 261 additions & 171 deletions

File tree

examples/demo-app/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
"@loaders.gl/csv": "^4.3.2",
3232
"@loaders.gl/json": "^4.3.2",
3333
"@loaders.gl/parquet": "^4.3.2",
34-
"@openassistant/core": "^0.5.13",
35-
"@openassistant/ui": "^0.5.13",
34+
"@openassistant/core": "^0.5.17",
35+
"@openassistant/ui": "^0.5.17",
3636
"@types/classnames": "^2.3.1",
3737
"@types/keymirror": "^0.1.1",
3838
"apache-arrow": ">=15.0.0",

examples/demo-app/yarn.lock

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5049,6 +5049,44 @@ __metadata:
50495049
languageName: node
50505050
linkType: hard
50515051

5052+
"@openassistant/core@npm:0.5.18, @openassistant/core@npm:^0.5.17":
5053+
version: 0.5.18
5054+
resolution: "@openassistant/core@npm:0.5.18"
5055+
dependencies:
5056+
"@ai-sdk/openai": "npm:^1.3.21"
5057+
"@ai-sdk/react": "npm:^1.2.11"
5058+
"@ai-sdk/ui-utils": "npm:^1.2.10"
5059+
"@langchain/core": "npm:^0.3.38"
5060+
ai: "npm:^4.3.19"
5061+
openai: "npm:^4.93.0"
5062+
openai-zod-functions: "npm:^0.1.2"
5063+
zod: "npm:^3.24.4"
5064+
zod-to-json-schema: "npm:^3.24.1"
5065+
peerDependencies:
5066+
"@ai-sdk/amazon-bedrock": ^1.1.6
5067+
"@ai-sdk/anthropic": ^1.1.14
5068+
"@ai-sdk/deepseek": ^0.1.8
5069+
"@ai-sdk/google": ^1.1.8
5070+
"@ai-sdk/xai": ^1.1.8
5071+
ollama-ai-provider-v2: ^0.0.6
5072+
react: ">=18.2"
5073+
peerDependenciesMeta:
5074+
"@ai-sdk/amazon-bedrock":
5075+
optional: true
5076+
"@ai-sdk/anthropic":
5077+
optional: true
5078+
"@ai-sdk/deepseek":
5079+
optional: true
5080+
"@ai-sdk/google":
5081+
optional: true
5082+
"@ai-sdk/xai":
5083+
optional: true
5084+
ollama-ai-provider-v2:
5085+
optional: true
5086+
checksum: 10c0/3e370d3f574ae3eaa0cdbf0658812f2d5b9bacb92ee8645fc18cd29c9d0b6e0dd5d66c2377eb2d0b26382d8b9fe42ed320ad43f8b2610c068fdf1a3907098aa6
5087+
languageName: node
5088+
linkType: hard
5089+
50525090
"@openassistant/duckdb@npm:^0.5.13":
50535091
version: 0.5.18
50545092
resolution: "@openassistant/duckdb@npm:0.5.18"
@@ -5189,6 +5227,31 @@ __metadata:
51895227
languageName: node
51905228
linkType: hard
51915229

5230+
"@openassistant/ui@npm:^0.5.17":
5231+
version: 0.5.18
5232+
resolution: "@openassistant/ui@npm:0.5.18"
5233+
dependencies:
5234+
"@ai-sdk/ui-utils": "npm:^1.2.11"
5235+
"@heroui/react": "npm:^2.7.8"
5236+
"@heroui/use-clipboard": "npm:^2.1.8"
5237+
"@iconify/react": "npm:^5.1.0"
5238+
"@openassistant/core": "npm:0.5.18"
5239+
"@openassistant/utils": "npm:0.5.18"
5240+
ai: "npm:^4.3.19"
5241+
framer-motion: "npm:^11.15.0"
5242+
html2canvas: "npm:^1.4.1"
5243+
next-themes: "npm:^0.4.4"
5244+
react-audio-voice-recorder: "npm:^2.2.0"
5245+
react-markdown: "npm:^10.0.0"
5246+
remark-gfm: "npm:^4.0.1"
5247+
tailwindcss: "npm:^3.4.17"
5248+
peerDependencies:
5249+
react: ">=18.2"
5250+
react-dom: ">=18.2"
5251+
checksum: 10c0/d0ddada031f21e24396d0f060b629495506207b6fc875e72fd389be5a2fb6a61fc7424a924eb0709453e35e830ae5b063bb420a4edab721d303a6e0b01794b7e
5252+
languageName: node
5253+
linkType: hard
5254+
51925255
"@openassistant/utils@npm:0.5.13":
51935256
version: 0.5.13
51945257
resolution: "@openassistant/utils@npm:0.5.13"
@@ -9735,6 +9798,26 @@ __metadata:
97359798
languageName: node
97369799
linkType: hard
97379800

9801+
"ai@npm:^4.3.19":
9802+
version: 4.3.19
9803+
resolution: "ai@npm:4.3.19"
9804+
dependencies:
9805+
"@ai-sdk/provider": "npm:1.1.3"
9806+
"@ai-sdk/provider-utils": "npm:2.2.8"
9807+
"@ai-sdk/react": "npm:1.2.12"
9808+
"@ai-sdk/ui-utils": "npm:1.2.11"
9809+
"@opentelemetry/api": "npm:1.9.0"
9810+
jsondiffpatch: "npm:0.6.0"
9811+
peerDependencies:
9812+
react: ^18 || ^19 || ^19.0.0-rc
9813+
zod: ^3.23.8
9814+
peerDependenciesMeta:
9815+
react:
9816+
optional: true
9817+
checksum: 10c0/738ac453b3e61b2f2282941fe8af946c42696fbdcffa5ac213823377bcddf475f26923cf2ca5656d5655e5c351e355e1af62dcb04a6df6139b67bac650b01af2
9818+
languageName: node
9819+
linkType: hard
9820+
97389821
"almost-equal@npm:^1.1.0":
97399822
version: 1.1.0
97409823
resolution: "almost-equal@npm:1.1.0"
@@ -16002,8 +16085,8 @@ __metadata:
1600216085
"@loaders.gl/csv": "npm:^4.3.2"
1600316086
"@loaders.gl/json": "npm:^4.3.2"
1600416087
"@loaders.gl/parquet": "npm:^4.3.2"
16005-
"@openassistant/core": "npm:^0.5.13"
16006-
"@openassistant/ui": "npm:^0.5.13"
16088+
"@openassistant/core": "npm:^0.5.17"
16089+
"@openassistant/ui": "npm:^0.5.17"
1600716090
"@types/classnames": "npm:^2.3.1"
1600816091
"@types/keymirror": "npm:^0.1.1"
1600916092
apache-arrow: "npm:>=15.0.0"

src/ai-assistant/package.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,15 @@
4040
"@kepler.gl/table": "3.2.0",
4141
"@kepler.gl/types": "3.2.0",
4242
"@kepler.gl/utils": "3.2.0",
43-
"@openassistant/core": "^0.5.13",
44-
"@openassistant/duckdb": "^0.5.13",
45-
"@openassistant/echarts": "^0.5.13",
46-
"@openassistant/geoda": "^0.5.13",
47-
"@openassistant/osm": "^0.5.13",
48-
"@openassistant/plots": "^0.5.13",
49-
"@openassistant/tables": "^0.5.13",
50-
"@openassistant/ui": "^0.5.13",
51-
"@openassistant/utils": "^0.5.13",
43+
"@openassistant/core": "^0.5.17",
44+
"@openassistant/duckdb": "^0.5.17",
45+
"@openassistant/echarts": "^0.5.17",
46+
"@openassistant/geoda": "^0.5.17",
47+
"@openassistant/osm": "^0.5.17",
48+
"@openassistant/plots": "^0.5.17",
49+
"@openassistant/tables": "^0.5.17",
50+
"@openassistant/ui": "^0.5.17",
51+
"@openassistant/utils": "^0.5.17",
5252
"color-interpolate": "^1.0.5",
5353
"global": "^4.3.0",
5454
"ollama-ai-provider-v2": "^0.0.5",

src/ai-assistant/src/components/ai-assistant-config.tsx

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
} from '@kepler.gl/components';
1717
import {State} from '../index';
1818
import ApiKey from '../icons/api-key';
19-
import {PROVIDER_MODELS} from '../config/models';
19+
import {PROVIDER_MODELS, PROVIDER_DEFAULT_BASE_URLS} from '../config/models';
2020
import {useLocalStorage} from 'usehooks-ts';
2121
import {GetAssistantModelByProvider} from '@openassistant/core';
2222
import {updateAiAssistantConfig} from '../actions';
@@ -152,7 +152,7 @@ export function AiAssistantConfig() {
152152
const [topP, setTopP] = useLocalStorage('ai-assistant-top-p', aiAssistantConfig.topP || 1.0);
153153
const [baseUrl, setBaseUrl] = useLocalStorage(
154154
'ai-assistant-base-url',
155-
aiAssistantConfig.baseUrl || 'http://localhost:11434/api'
155+
aiAssistantConfig.baseUrl || PROVIDER_DEFAULT_BASE_URLS[provider]
156156
);
157157
const [mapboxToken, setMapboxToken] = useLocalStorage(
158158
'ai-assistant-mapbox-token',
@@ -168,6 +168,7 @@ export function AiAssistantConfig() {
168168
if (typeof value === 'string') {
169169
setProvider(value);
170170
setModel(PROVIDER_MODELS[value][0]);
171+
setBaseUrl(PROVIDER_DEFAULT_BASE_URLS[value]);
171172
setConnectionError(false);
172173
setErrorMessage('');
173174
}
@@ -224,19 +225,32 @@ export function AiAssistantConfig() {
224225
setTimeout(() => reject(new Error('Connection timeout after 15 seconds')), 15000);
225226
});
226227

227-
const AssistantModel = GetAssistantModelByProvider({
228+
const AssistantModel = await GetAssistantModelByProvider({
228229
provider: provider
229230
});
230231

232+
if (!AssistantModel || !AssistantModel.configure || !AssistantModel.testConnection) {
233+
throw new Error('Failed to initialize AI model');
234+
}
235+
236+
// configure model
237+
AssistantModel.configure({
238+
model: model,
239+
baseURL: baseUrl || PROVIDER_DEFAULT_BASE_URLS[provider],
240+
apiKey: apiKey,
241+
temperature: Number(temperature),
242+
topP: Number(topP)
243+
});
244+
231245
const success = (await Promise.race([
232-
AssistantModel?.testConnection(apiKey, model),
246+
AssistantModel.testConnection(apiKey, model),
233247
timeoutPromise
234248
])) as boolean;
235249

236250
const errorMessage = !success
237251
? provider === 'ollama'
238-
? 'Connection failed: maybe invalid Ollama Base URL'
239-
: 'Connection failed: maybe invalid API Key'
252+
? 'Connection failed: maybe invalid Base URL'
253+
: 'Connection failed: maybe invalid API Key or Base URL'
240254
: '';
241255
setConnectionError(!success);
242256
setErrorMessage(errorMessage);
@@ -245,7 +259,7 @@ export function AiAssistantConfig() {
245259
provider: provider,
246260
model: model,
247261
apiKey: apiKey,
248-
baseUrl: baseUrl,
262+
baseUrl: baseUrl || PROVIDER_DEFAULT_BASE_URLS[provider],
249263
isReady: success,
250264
temperature: temperature,
251265
topP: topP,
@@ -322,11 +336,11 @@ export function AiAssistantConfig() {
322336
/>
323337
</OllamaModelInputWrapper>
324338
)}
325-
{provider !== 'ollama' ? (
339+
{provider !== 'ollama' && (
326340
<>
327341
<PanelLabelWrapper>
328342
<SectionTitle>
329-
<FormattedMessage id="aiAssistantManager.apiKey.placeholder" />
343+
<FormattedMessage id="aiAssistantManager.apiKey.title" />
330344
</SectionTitle>
331345
</PanelLabelWrapper>
332346
<div className="api-key-input">
@@ -341,26 +355,23 @@ export function AiAssistantConfig() {
341355
/>
342356
</div>
343357
</>
344-
) : (
345-
<>
346-
<PanelLabelWrapper>
347-
<SectionTitle>
348-
<FormattedMessage id="aiAssistantManager.baseUrl.placeholder" />
349-
</SectionTitle>
350-
</PanelLabelWrapper>
351-
<div className="api-key-input">
352-
<div className="api-key-input__icon">
353-
<ApiKey height="20px" />
354-
</div>
355-
<Input
356-
type="text"
357-
onChange={onBaseUrlChange}
358-
placeholder={intl.formatMessage({id: 'aiAssistantManager.baseUrl.placeholder'})}
359-
value={baseUrl}
360-
/>
361-
</div>
362-
</>
363358
)}
359+
<PanelLabelWrapper>
360+
<SectionTitle>
361+
<FormattedMessage id="aiAssistantManager.baseUrl.title" />
362+
</SectionTitle>
363+
</PanelLabelWrapper>
364+
<div className="api-key-input">
365+
<div className="api-key-input__icon">
366+
<ApiKey height="20px" />
367+
</div>
368+
<Input
369+
type="text"
370+
onChange={onBaseUrlChange}
371+
placeholder={PROVIDER_DEFAULT_BASE_URLS[provider]}
372+
value={baseUrl}
373+
/>
374+
</div>
364375
{connectionError && (
365376
<StyleErrorMessage className="error-message">{errorMessage}</StyleErrorMessage>
366377
)}
Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,45 @@
11
// SPDX-License-Identifier: MIT
22
// Copyright contributors to the kepler.gl project
33

4+
export const PROVIDER_DEFAULT_BASE_URLS = {
5+
"openai": "https://api.openai.com/v1",
6+
"anthropic": "https://api.anthropic.com/v1",
7+
"google": "https://generativelanguage.googleapis.com/v1beta",
8+
"deepseek": "https://api.deepseek.com/v1",
9+
"xai": "https://api.x.ai/v1",
10+
"ollama": "http://localhost:11434/api"
11+
};
12+
413
export const PROVIDER_MODELS = {
5-
"openai": [
6-
"gpt-4.1",
7-
"gpt-4o",
8-
"gpt-4o-mini",
9-
"gpt-4",
10-
"gpt-3.5-turbo",
11-
"o1",
12-
"o1-preview",
13-
"o1-mini"
14+
openai: [
15+
'gpt-4.1',
16+
'gpt-4o',
17+
'gpt-4o-mini',
18+
'gpt-4',
19+
'gpt-3.5-turbo',
20+
'o1',
21+
'o1-preview',
22+
'o1-mini'
1423
],
15-
"google": [
16-
"gemini-2.5-flash-preview-04-17",
17-
"gemini-2.5-pro-preview-05-06",
18-
"gemini-2.0-flash",
19-
"gemini-1.5-flash",
20-
"gemini-1.5-pro"
24+
google: [
25+
'gemini-2.5-flash',
26+
'gemini-2.5-pro',
27+
'gemini-2.0-flash',
28+
'gemini-1.5-flash',
29+
'gemini-1.5-pro'
2130
],
22-
"deepseek": ["deepseek-chat"],
23-
"anthropic": [
24-
"claude-3.7-sonnet",
25-
"claude-3.5-sonnet",
26-
"claude-3.5-haiku",
27-
"claude-3-opus",
28-
"claude-3-sonnet",
29-
"claude-3-haiku"
31+
deepseek: ['deepseek-chat'],
32+
anthropic: [
33+
'claude-opus-4-1',
34+
'claude-opus-4-0',
35+
'claude-sonnet-4-0',
36+
'claude-3.7-sonnet',
37+
'claude-3.5-sonnet',
38+
'claude-3.5-haiku',
39+
'claude-3-opus',
40+
'claude-3-sonnet',
41+
'claude-3-haiku'
3042
],
31-
"xai": ["grok-3", "grok-3-fast", "grok-3-mini", "grok-3-mini-fast"],
32-
"ollama": ["deepseek-r1", "gemma3", "llama4", "llama3.3", "mistral", "phi4", "qwen3", "qwen2.5"]
43+
xai: ['grok-3', 'grok-3-fast', 'grok-3-mini', 'grok-3-mini-fast'],
44+
ollama: ['qwen3:32b', 'gpt-oss']
3345
};

src/ai-assistant/src/localization.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ export const messages = {
2121
placeholder: 'Enter your API Key'
2222
},
2323
baseUrl: {
24-
title: 'Ollama Base URL',
25-
placeholder: 'Enter OllamaBase URL'
24+
title: 'Base URL',
25+
placeholder: 'Enter Base URL'
2626
},
2727
temperature: {
2828
title: 'Temperature'
@@ -49,8 +49,8 @@ export const messages = {
4949
placeholder: 'Syötä API-avain'
5050
},
5151
baseUrl: {
52-
title: 'Ollama Base URL',
53-
placeholder: 'Syötä Ollama Base URL'
52+
title: 'Base URL',
53+
placeholder: 'Syötä Base URL'
5454
},
5555
temperature: {
5656
title: 'Lämpötila'
@@ -161,8 +161,8 @@ export const messages = {
161161
placeholder: 'API キーを入力'
162162
},
163163
baseUrl: {
164-
title: 'Ollama Base URL',
165-
placeholder: 'OllamaBase URL を入力'
164+
title: 'Base URL',
165+
placeholder: 'Base URL を入力'
166166
},
167167
temperature: {
168168
title: '温度'

0 commit comments

Comments
 (0)