Skip to content

Commit 39cf6e7

Browse files
committed
chore(generative-ai): add url endpoints for compass web usage
1 parent 39d7222 commit 39cf6e7

File tree

6 files changed

+149
-56
lines changed

6 files changed

+149
-56
lines changed

packages/atlas-service/src/atlas-service.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,8 @@ export class AtlasService {
2929
) {
3030
this.config = getAtlasConfig(preferences);
3131
}
32-
adminApiEndpoint(path?: string, requestId?: string): string {
33-
const uri = `${this.config.atlasApiBaseUrl}${normalizePath(path)}`;
34-
const query = requestId
35-
? `?request_id=${encodeURIComponent(requestId)}`
36-
: '';
37-
return `${uri}${query}`;
32+
adminApiEndpoint(path?: string): string {
33+
return `${this.config.atlasApiBaseUrl}${normalizePath(path)}`;
3834
}
3935
cloudEndpoint(path?: string): string {
4036
return `${this.config.cloudBaseUrl}${normalizePath(path)}`;

packages/compass-generative-ai/src/atlas-ai-service.spec.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import Sinon from 'sinon';
22
import { expect } from 'chai';
3-
import { AtlasAiService } from './atlas-ai-service';
3+
import {
4+
type AIEndpoint,
5+
aiURLConfig,
6+
AtlasAiService,
7+
} from './atlas-ai-service';
48
import type { PreferencesAccess } from 'compass-preferences-model';
59
import { createSandboxFromDefaultPreferences } from 'compass-preferences-model';
610
import { createNoopLogger } from '@mongodb-js/compass-logging/provider';
@@ -20,13 +24,11 @@ const PREFERENCES_USER = {
2024
};
2125

2226
const BASE_URL = 'http://example.com';
27+
const urlConfig = aiURLConfig['admin-api'];
2328

2429
class MockAtlasService {
2530
getCurrentUser = () => Promise.resolve(ATLAS_USER);
26-
adminApiEndpoint = (url: string, requestId?: string) =>
27-
`${[BASE_URL, url].join('/')}${
28-
requestId ? `?request_id=${requestId}` : ''
29-
}`;
31+
adminApiEndpoint = (url: string) => `${[BASE_URL, url].join('/')}`;
3032
authenticatedFetch = (url: string, init: RequestInit) => {
3133
return fetch(url, init);
3234
};
@@ -54,11 +56,20 @@ describe('AtlasAiService', function () {
5456
preferences = await createSandboxFromDefaultPreferences();
5557
preferences['getPreferencesUser'] = () => PREFERENCES_USER;
5658

57-
atlasAiService = new AtlasAiService(
58-
new MockAtlasService() as any,
59+
const mockAtlasService = new MockAtlasService();
60+
atlasAiService = new AtlasAiService({
61+
atlasService: mockAtlasService as any,
62+
getUrlForEndpoint: (urlId: AIEndpoint) => {
63+
const urlPath: string =
64+
urlId === 'user-access'
65+
? urlConfig[urlId](PREFERENCES_USER.id)
66+
: urlConfig[urlId];
67+
68+
return mockAtlasService.adminApiEndpoint(urlPath);
69+
},
5970
preferences,
60-
createNoopLogger()
61-
);
71+
logger: createNoopLogger(),
72+
});
6273
});
6374

6475
afterEach(function () {

packages/compass-generative-ai/src/atlas-ai-service.ts

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ type GenerativeAiInput = {
2323
// want to ensure we're not uploading massive documents (some folks have documents > 1mb).
2424
const AI_MAX_REQUEST_SIZE = 5120000;
2525
const AI_MIN_SAMPLE_DOCUMENTS = 1;
26-
const USER_AI_URI = (userId: string) => `unauth/ai/api/v1/hello/${userId}`;
27-
const AGGREGATION_URI = 'ai/api/v1/mql-aggregation';
28-
const QUERY_URI = 'ai/api/v1/mql-query';
2926

3027
type AIAggregation = {
3128
content: {
@@ -192,14 +189,49 @@ export function validateAIAggregationResponse(
192189
}
193190
}
194191

192+
export const aiURLConfig = {
193+
// There are two different sets of endpoints we use for our requests.
194+
// Down the line we'd like to only use the admin api, however,
195+
// we cannot currently call that from the Atlas UI. Pending CLOUDP-251201
196+
'admin-api': {
197+
'user-access': (userId: string) => `unauth/ai/api/v1/hello/${userId}`,
198+
aggregation: 'ai/api/v1/mql-aggregation',
199+
query: 'ai/api/v1/mql-query',
200+
},
201+
cloud: {
202+
'user-access': (groupId: string, userId: string) =>
203+
`/ai/v1/groups/${groupId}/hello/${userId}`,
204+
aggregation: (groupId: string) =>
205+
`/ai/v1/groups/${groupId}/mql-aggregation`,
206+
query: (groupId: string) => `/ai/v1/groups/${groupId}/mql-query`,
207+
},
208+
} as const;
209+
export type AIEndpoint = 'user-access' | 'query' | 'aggregation';
210+
195211
export class AtlasAiService {
196212
private initPromise: Promise<void> | null = null;
197213

198-
constructor(
199-
private atlasService: AtlasService,
200-
private preferences: PreferencesAccess,
201-
private logger: Logger
202-
) {
214+
private atlasService: AtlasService;
215+
private getUrlForEndpoint: (urlId: AIEndpoint) => string;
216+
private preferences: PreferencesAccess;
217+
private logger: Logger;
218+
219+
constructor({
220+
atlasService,
221+
getUrlForEndpoint,
222+
preferences,
223+
logger,
224+
}: {
225+
atlasService: AtlasService;
226+
getUrlForEndpoint: (urlId: AIEndpoint) => string;
227+
preferences: PreferencesAccess;
228+
logger: Logger;
229+
}) {
230+
this.atlasService = atlasService;
231+
this.getUrlForEndpoint = getUrlForEndpoint;
232+
this.preferences = preferences;
233+
this.logger = logger;
234+
203235
this.initPromise = this.setupAIAccess();
204236
}
205237

@@ -215,8 +247,7 @@ export class AtlasAiService {
215247
}
216248

217249
private async getAIFeatureEnablement(): Promise<AIFeatureEnablement> {
218-
const userId = this.preferences.getPreferencesUser().id;
219-
const url = this.atlasService.adminApiEndpoint(USER_AI_URI(userId));
250+
const url = this.getUrlForEndpoint('user-access');
220251
const res = await this.atlasService.fetch(url, {
221252
headers: {
222253
Accept: 'application/json',
@@ -261,7 +292,7 @@ export class AtlasAiService {
261292
}
262293

263294
private getQueryOrAggregationFromUserInput = async <T>(
264-
uri: string,
295+
urlId: 'query' | 'aggregation',
265296
input: GenerativeAiInput,
266297
validationFn: (res: any) => asserts res is T
267298
): Promise<T> => {
@@ -271,14 +302,15 @@ export class AtlasAiService {
271302
const { signal, requestId, ...rest } = input;
272303
const msgBody = buildQueryOrAggregationMessageBody(rest);
273304

274-
const url = this.atlasService.adminApiEndpoint(uri, requestId);
305+
const url = new URL(this.getUrlForEndpoint(urlId));
306+
url.searchParams.append('request_id', encodeURIComponent(requestId));
275307

276308
this.logger.log.info(
277309
this.logger.mongoLogId(1_001_000_308),
278310
'AtlasAIService',
279311
'Running AI query generation request',
280312
{
281-
url,
313+
url: url.toString(),
282314
userInput: input.userInput,
283315
collectionName: input.collectionName,
284316
databaseName: input.databaseName,
@@ -287,7 +319,7 @@ export class AtlasAiService {
287319
}
288320
);
289321

290-
const res = await this.atlasService.authenticatedFetch(url, {
322+
const res = await this.atlasService.authenticatedFetch(url.toString(), {
291323
signal,
292324
method: 'POST',
293325
body: msgBody,
@@ -328,15 +360,15 @@ export class AtlasAiService {
328360

329361
async getAggregationFromUserInput(input: GenerativeAiInput) {
330362
return this.getQueryOrAggregationFromUserInput(
331-
AGGREGATION_URI,
363+
'aggregation',
332364
input,
333365
validateAIAggregationResponse
334366
);
335367
}
336368

337369
async getQueryFromUserInput(input: GenerativeAiInput) {
338370
return this.getQueryOrAggregationFromUserInput(
339-
QUERY_URI,
371+
'query',
340372
input,
341373
validateAIQueryResponse
342374
);
Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { createContext, useContext, useMemo } from 'react';
2-
import { AtlasAiService } from './atlas-ai-service';
2+
import { type AIEndpoint, AtlasAiService } from './atlas-ai-service';
33
import { preferencesLocator } from 'compass-preferences-model/provider';
44
import { useLogger } from '@mongodb-js/compass-logging/provider';
55
import { atlasServiceLocator } from '@mongodb-js/atlas-service/provider';
@@ -10,23 +10,48 @@ import {
1010

1111
const AtlasAiServiceContext = createContext<AtlasAiService | null>(null);
1212

13-
export const AtlasAiServiceProvider: React.FC = createServiceProvider(
14-
function AtlasAiServiceProvider({ children }) {
15-
const logger = useLogger('ATLAS-AI-SERVICE');
16-
const preferences = preferencesLocator();
17-
const atlasService = atlasServiceLocator();
18-
19-
const aiService = useMemo(() => {
20-
return new AtlasAiService(atlasService, preferences, logger);
21-
}, [preferences, logger, atlasService]);
22-
23-
return (
24-
<AtlasAiServiceContext.Provider value={aiService}>
25-
{children}
26-
</AtlasAiServiceContext.Provider>
27-
);
28-
}
29-
);
13+
export type URLConfig = {
14+
'user-access': (userId: string) => string;
15+
query: string;
16+
aggregation: string;
17+
};
18+
19+
export const AtlasAiServiceProvider: React.FC<{
20+
apiURLPreset: 'admin-api' | 'cloud';
21+
urlConfig: URLConfig;
22+
}> = createServiceProvider(function AtlasAiServiceProvider({
23+
apiURLPreset,
24+
children,
25+
urlConfig,
26+
}) {
27+
const logger = useLogger('ATLAS-AI-SERVICE');
28+
const preferences = preferencesLocator();
29+
const atlasService = atlasServiceLocator();
30+
31+
const aiService = useMemo(() => {
32+
const userId = preferences.getPreferencesUser().id;
33+
34+
return new AtlasAiService({
35+
getUrlForEndpoint: (urlId: AIEndpoint) => {
36+
const urlPath: string =
37+
urlId === 'user-access' ? urlConfig[urlId](userId) : urlConfig[urlId];
38+
39+
return apiURLPreset === 'admin-api'
40+
? atlasService.adminApiEndpoint(urlPath)
41+
: atlasService.cloudEndpoint(urlPath);
42+
},
43+
atlasService,
44+
preferences,
45+
logger,
46+
});
47+
}, [apiURLPreset, preferences, logger, atlasService, urlConfig]);
48+
49+
return (
50+
<AtlasAiServiceContext.Provider value={aiService}>
51+
{children}
52+
</AtlasAiServiceContext.Provider>
53+
);
54+
});
3055

3156
function useAtlasAiServiceContext(): AtlasAiService {
3257
const service = useContext(AtlasAiServiceContext);
@@ -40,4 +65,5 @@ export const atlasAiServiceLocator = createServiceLocator(
4065
useAtlasAiServiceContext,
4166
'atlasAiServiceLocator'
4267
);
43-
export { AtlasAiService } from './atlas-ai-service';
68+
export { AtlasAiService, aiURLConfig } from './atlas-ai-service';
69+
export type { AIEndpoint } from './atlas-ai-service';

packages/compass-web/src/entrypoint.tsx

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ import {
4444
import type { AllPreferences } from 'compass-preferences-model/provider';
4545
import FieldStorePlugin from '@mongodb-js/compass-field-store';
4646
import { AtlasServiceProvider } from '@mongodb-js/atlas-service/provider';
47-
import { AtlasAiServiceProvider } from '@mongodb-js/compass-generative-ai/provider';
47+
import {
48+
AtlasAiServiceProvider,
49+
aiURLConfig,
50+
} from '@mongodb-js/compass-generative-ai/provider';
4851
import { LoggerProvider } from '@mongodb-js/compass-logging/provider';
4952
import { TelemetryProvider } from '@mongodb-js/compass-telemetry/provider';
5053
import CompassConnections from '@mongodb-js/compass-connections';
@@ -59,11 +62,23 @@ import { useCompassWebLoggerAndTelemetry } from './logger-and-telemetry';
5962
import { type TelemetryServiceOptions } from '@mongodb-js/compass-telemetry';
6063
import { WorkspaceTab as WelcomeWorkspaceTab } from '@mongodb-js/compass-welcome';
6164

62-
const WithAtlasProviders: React.FC = ({ children }) => {
65+
const WithAtlasProviders: React.FC<{
66+
projectId: string;
67+
}> = ({ children, projectId }) => {
6368
return (
6469
<AtlasCloudAuthServiceProvider>
6570
<AtlasServiceProvider>
66-
<AtlasAiServiceProvider>{children}</AtlasAiServiceProvider>
71+
<AtlasAiServiceProvider
72+
apiURLPreset="cloud"
73+
urlConfig={{
74+
'user-access': (userId: string) =>
75+
aiURLConfig.cloud['user-access'](userId, projectId),
76+
query: aiURLConfig.cloud.query(projectId),
77+
aggregation: aiURLConfig.cloud.aggregation(projectId),
78+
}}
79+
>
80+
{children}
81+
</AtlasAiServiceProvider>
6782
</AtlasServiceProvider>
6883
</AtlasCloudAuthServiceProvider>
6984
);
@@ -315,7 +330,7 @@ const CompassWeb = ({
315330
<PreferencesProvider value={preferencesAccess.current}>
316331
<LoggerProvider value={logger}>
317332
<TelemetryProvider options={telemetryOptions.current}>
318-
<WithAtlasProviders>
333+
<WithAtlasProviders projectId={projectId}>
319334
<AtlasCloudConnectionStorageProvider
320335
orgId={orgId}
321336
projectId={projectId}

packages/compass/src/app/components/entrypoint.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import {
77
AtlasAuthServiceProvider,
88
AtlasServiceProvider,
99
} from '@mongodb-js/atlas-service/provider';
10-
import { AtlasAiServiceProvider } from '@mongodb-js/compass-generative-ai/provider';
10+
import {
11+
AtlasAiServiceProvider,
12+
aiURLConfig,
13+
} from '@mongodb-js/compass-generative-ai/provider';
1114
import {
1215
CompassFavoriteQueryStorage,
1316
CompassPipelineStorage,
@@ -61,7 +64,17 @@ export const WithAtlasProviders: React.FC = ({ children }) => {
6164
},
6265
}}
6366
>
64-
<AtlasAiServiceProvider>{children}</AtlasAiServiceProvider>
67+
<AtlasAiServiceProvider
68+
apiURLPreset="admin-api"
69+
urlConfig={{
70+
'user-access': (userId: string) =>
71+
aiURLConfig['admin-api']['user-access'](userId),
72+
query: aiURLConfig['admin-api'].query,
73+
aggregation: aiURLConfig['admin-api'].aggregation,
74+
}}
75+
>
76+
{children}
77+
</AtlasAiServiceProvider>
6578
</AtlasServiceProvider>
6679
</AtlasAuthServiceProvider>
6780
);

0 commit comments

Comments
 (0)