Skip to content

Commit

Permalink
refactor to move assistant control plane stuff into the Pinecone clas…
Browse files Browse the repository at this point in the history
…s, clean up assistant targeting and host resolution
  • Loading branch information
austin-denoble committed Jan 19, 2025
1 parent 435229a commit 2462a5f
Show file tree
Hide file tree
Showing 39 changed files with 738 additions and 254 deletions.
68 changes: 52 additions & 16 deletions src/assistant/assistantHostSingleton.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,60 @@
import type { PineconeConfiguration } from '../data';
import { normalizeUrl } from '../utils';
import { asstControlOperationsBuilder } from './control/asstControlOperationsBuilder';
import { describeAssistant } from './control';

export const AssistantHostSingleton = (function () {
const hostUrls = {}; // map of apiKey-assistantName to hostUrl

const getAssistantHostUrl = (region: string): string => {
const regionHosts = {
us: 'https://prod-1-data.ke.pinecone.io/assistant',
eu: 'https://prod-eu-data.ke.pinecone.io/assistant',
};
return regionHosts[region];
function ensureAssistantPath(url) {
if (!url.endsWith('/assistant')) {
// Append "/assistant" if it doesn't already end with it
url = url.endsWith('/') ? `${url}assistant` : `${url}/assistant`;
}
return url;
}

const _describeAssistant = async (
config: PineconeConfiguration,
assistantName: string
): Promise<string> => {
const assistantControlApi = asstControlOperationsBuilder(config);
const describeResponse = await describeAssistant(assistantControlApi)(
assistantName
);
const host = describeResponse.host;

if (!host) {
// if the host is empty for some reason, default based on region
let defaultHost = 'https://prod-1-data.ke.pinecone.io';
// If 'eu' is specified use that, otherwise default to 'us'
if (
config.assistantRegion &&
config.assistantRegion.toLowerCase() === 'eu'
) {
defaultHost = 'https://prod-eu-data.ke.pinecone.io';
}
return defaultHost;
} else {
return host;
}
};

const _key = (config: PineconeConfiguration, assistantName: string) =>
`${config.apiKey}-${assistantName}`;

// singleton object
return {
getHostUrl: (config: PineconeConfiguration, assistantName: string) => {
const singleton = {
getHostUrl: async (
config: PineconeConfiguration,
assistantName: string
) => {
const cacheKey = _key(config, assistantName);
if (!hostUrls[cacheKey]) {
const region = 'us'; // Default to region 'us' if not specified
const hostUrl = getAssistantHostUrl(region);
hostUrls[cacheKey] = normalizeUrl(hostUrl);
if (cacheKey in hostUrls) {
return hostUrls[cacheKey];
} else {
const hostUrl = await _describeAssistant(config, assistantName);
hostUrls[cacheKey] = normalizeUrl(ensureAssistantPath(hostUrl));
}
return hostUrls[cacheKey];
},
Expand All @@ -36,11 +68,13 @@ export const AssistantHostSingleton = (function () {
_set: (
config: PineconeConfiguration,
assistantName: string,
region: string
hostUrl: string
) => {
const hostUrl = getAssistantHostUrl(region);
const normalizedHostUrl = normalizeUrl(hostUrl);
if (!normalizedHostUrl) return;
const normalizedHostUrl = normalizeUrl(ensureAssistantPath(hostUrl));
// prevent adding an empty hostUrl to the cache
if (!normalizedHostUrl) {
return;
}

const cacheKey = _key(config, assistantName);
hostUrls[cacheKey] = normalizedHostUrl;
Expand All @@ -51,4 +85,6 @@ export const AssistantHostSingleton = (function () {
delete hostUrls[cacheKey];
},
};

return singleton;
})();
53 changes: 25 additions & 28 deletions src/assistant/control/AssistantCtrlPlane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,14 @@ import {
ManageAssistantsApi as ManageAssistantsApiCtrl,
} from '../../pinecone-generated-ts-fetch/assistant_control';
import { MetricsApi } from '../../pinecone-generated-ts-fetch/assistant_evaluation';
import { AssistantHostSingleton } from '../assistantHostSingleton';
// import { AssistantHostSingleton } from '../assistantHostSingleton';
import { PineconeConfiguration } from '../../data';
import {
createAssistantClosed,
createAssistantRequest,
} from './createAssistant';
import { deleteAssistantClosed } from './deleteAssistant';
import { getAssistantClosed } from './getAssistant';
import { listAssistantsClosed } from './listAssistants';
import { updateAssistant, updateAssistantClosed } from './updateAssistant';
import { Eval, evaluateClosed } from './evaluate';
import { createAssistant, CreateAssistantOptions } from './createAssistant';
import { deleteAssistant } from './deleteAssistant';
import { describeAssistant } from './describeAssistant';
import { listAssistants } from './listAssistants';
import { UpdateAssistantOptions, updateAssistant } from './updateAssistant';
import { AssistantEval, evaluate } from './evaluate';

/**
* The `AssistantCtrlPlane` class holds the control plane methods for interacting with
Expand Down Expand Up @@ -41,13 +38,13 @@ import { Eval, evaluateClosed } from './evaluate';
* ```
*/
export class AssistantCtrlPlane {
readonly _createAssistant?: ReturnType<typeof createAssistantClosed>;
readonly _deleteAssistant?: ReturnType<typeof deleteAssistantClosed>;
readonly _getAssistant?: ReturnType<typeof getAssistantClosed>;
readonly _listAssistants?: ReturnType<typeof listAssistantsClosed>;
readonly _updateAssistant?: ReturnType<typeof updateAssistantClosed>;
readonly _createAssistant?: ReturnType<typeof createAssistant>;
readonly _deleteAssistant?: ReturnType<typeof deleteAssistant>;
readonly _getAssistant?: ReturnType<typeof describeAssistant>;
readonly _listAssistants?: ReturnType<typeof listAssistants>;
readonly _updateAssistant?: ReturnType<typeof updateAssistant>;
readonly evalApi?: MetricsApi;
readonly _evaluate?: ReturnType<typeof evaluateClosed>;
readonly _evaluate?: ReturnType<typeof evaluate>;

private config: PineconeConfiguration;

Expand All @@ -67,14 +64,14 @@ export class AssistantCtrlPlane {
this.config = config;
if (params.evalApi) {
this.evalApi = params.evalApi;
this._evaluate = evaluateClosed(this.evalApi);
this._evaluate = evaluate(this.evalApi);
}
if (params.assistantApi) {
this._createAssistant = createAssistantClosed(params.assistantApi);
this._deleteAssistant = deleteAssistantClosed(params.assistantApi);
this._getAssistant = getAssistantClosed(params.assistantApi);
this._listAssistants = listAssistantsClosed(params.assistantApi);
this._updateAssistant = updateAssistantClosed(params.assistantApi);
this._createAssistant = createAssistant(params.assistantApi);
this._deleteAssistant = deleteAssistant(params.assistantApi);
this._getAssistant = describeAssistant(params.assistantApi);
this._listAssistants = listAssistants(params.assistantApi);
this._updateAssistant = updateAssistant(params.assistantApi);
}
}

Expand All @@ -97,14 +94,14 @@ export class AssistantCtrlPlane {
* // }
* ```
*
* @param options - A {@link createAssistantRequest} object containing the `name` of the Assistant to be created.
* @param options - A {@link CreateAssistantOptions} object containing the `name` of the Assistant to be created.
* Optionally, users can also specify instructions, metadata, and host region. Region must be one of "us" or "eu"
* and determines where the Assistant will be hosted.
* @throws Error if the Assistant API is not initialized.
* @throws Error if an invalid region is provided.
* @returns A Promise that resolves to an {@link Assistant} model.
*/
async createAssistant(options: createAssistantRequest) {
async createAssistant(options: CreateAssistantOptions) {
if (!this._createAssistant) {
throw new Error('Assistant API is not initialized');
}
Expand All @@ -123,7 +120,7 @@ export class AssistantCtrlPlane {
region =
options.region.toLowerCase() as CreateAssistantRequestRegionEnum;
}
AssistantHostSingleton._set(this.config, options.name, region);
//AssistantHostSingleton._set(this.config, options.name, region);
options.region = region;
}
return await this._createAssistant(options);
Expand Down Expand Up @@ -234,7 +231,7 @@ export class AssistantCtrlPlane {
* @throws Error if the Assistant API is not initialized.
* @returns A Promise that resolves to an {@link UpdateAssistant200Response} object.
*/
async updateAssistant(options: updateAssistant) {
async updateAssistant(options: UpdateAssistantOptions) {
if (!this._updateAssistant) {
throw new Error('Assistant API is not initialized');
}
Expand Down Expand Up @@ -262,12 +259,12 @@ export class AssistantCtrlPlane {
* // usage: { promptTokens: 1134, completionTokens: 21, totalTokens: 1155 }
* // }
* ```
* @param options - An {@link Eval} object containing the question, the answer, and a ground truth answer to
* @param options - An {@link AssistantEval} object containing the question, the answer, and a ground truth answer to
* evaluate.
* @throws Error if the Evaluation API is not initialized.
* @returns A Promise that resolves to an {@link AlignmentResponse} object.
*/
evaluate(options: Eval) {
evaluate(options: AssistantEval) {
if (!this._evaluate) {
throw new Error('Evaluation API is not initialized');
}
Expand Down
14 changes: 14 additions & 0 deletions src/assistant/control/__tests__/assistantHostSingleton.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
import { AssistantHostSingleton } from '../../assistantHostSingleton';

const mockDescribeAsst = jest.fn();
const mockAsstOperationsBuilder = jest.fn();

jest.mock('../../control', () => {
const realControl = jest.requireActual('../../control');
return {
...realControl,
describeAssistant: () => mockDescribeAsst,
assistantOperationsBuilder: (config) => mockAsstOperationsBuilder(config),
};
});

describe('AssistantHostSingleton', () => {
afterEach(() => {
AssistantHostSingleton._reset();
mockDescribeAsst.mockReset();
mockAsstOperationsBuilder.mockReset();
});

test('returns default host URL when no region is set', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from '../../utils';
import { middleware } from '../../utils/middleware';

export const assistantControlOperationsBuilder = (
export const asstControlOperationsBuilder = (
config: PineconeConfiguration
): ManageAssistantsControlApi => {
const { apiKey } = config;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@ import {
} from '../../pinecone-generated-ts-fetch/assistant_evaluation';
import { buildUserAgent, getFetch, queryParamsStringify } from '../../utils';
import { middleware } from '../../utils/middleware';
import { AssistantHostSingleton } from '../assistantHostSingleton';

export const assistantEvalOperationsBuilder = (
export const asstMetricsOperationsBuilder = (
config: PineconeConfiguration
): MetricsApi => {
const { apiKey } = config;
// All calls to eval will have 'eval' as assistantName
const hostUrl = AssistantHostSingleton.getHostUrl(config, 'eval');

let hostUrl = 'https://prod-1-data.ke.pinecone.io/assistant';
// If 'eu' is specified use that, otherwise default to 'us'
if (config.assistantRegion && config.assistantRegion.toLowerCase() === 'eu') {
hostUrl = 'https://prod-eu-data.ke.pinecone.io/assistant';
}

const headers = config.additionalHeaders || null;
const apiConfig: AssistantOperationsApiConfigurationParameters = {
basePath: hostUrl,
Expand Down
26 changes: 22 additions & 4 deletions src/assistant/control/createAssistant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import {
} from '../../pinecone-generated-ts-fetch/assistant_control';

/**
* The `createAssistantRequest` interface describes the name and optional configurations that can be
* The `CreateAssistantOptions` interface describes the name and optional configurations that can be
* passed when creating an Assistant.
*/
export interface createAssistantRequest {
export interface CreateAssistantOptions {
/**
* The name of the assistant. Resource name must be 1-45 characters long, start and end with an alphanumeric character, and consist only of lower case alphanumeric characters or '-'.
*/
Expand Down Expand Up @@ -49,8 +49,26 @@ export interface createAssistantRequest {
* @param api - The `ManageAssistantsControlApi` object that defines the methods for interacting with the Assistant API.
* @returns A Promise that resolves to an {@link Assistant} model.
*/
export const createAssistantClosed = (api: ManageAssistantsControlApi) => {
return async (options: createAssistantRequest): Promise<Assistant> => {
export const createAssistant = (api: ManageAssistantsControlApi) => {
return async (options: CreateAssistantOptions): Promise<Assistant> => {
if (options.region) {
let region: CreateAssistantRequestRegionEnum =
CreateAssistantRequestRegionEnum.Us;
if (
!Object.values(CreateAssistantRequestRegionEnum)
.toString()
.includes(options.region.toLowerCase())
) {
throw new Error(
'Invalid region specified. Must be one of "us" or "eu"'
);
} else {
region =
options.region.toLowerCase() as CreateAssistantRequestRegionEnum;
}
options.region = region;
}

return await api.createAssistant({
createAssistantRequest: {
name: options.name,
Expand Down
2 changes: 1 addition & 1 deletion src/assistant/control/deleteAssistant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { ManageAssistantsApi as ManageAssistantsControlApi } from '../../pinecon
*
* @param api - The `ManageAssistantsControlApi` object that defines the methods for interacting with the Assistant API.
*/
export const deleteAssistantClosed = (api: ManageAssistantsControlApi) => {
export const deleteAssistant = (api: ManageAssistantsControlApi) => {
return async (assistantName: string): Promise<void> => {
return await api.deleteAssistant({
assistantName: assistantName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
* @param api - The `ManageAssistantsControlApi` object that defines the methods for interacting with the Assistant API.
* @returns A Promise that resolves to an {@link Assistant} model.
*/
export const getAssistantClosed = (api: ManageAssistantsControlApi) => {
export const describeAssistant = (api: ManageAssistantsControlApi) => {
return async (assistantName: string): Promise<Assistant> => {
return await api.getAssistant({
assistantName: assistantName,
Expand Down
12 changes: 6 additions & 6 deletions src/assistant/control/evaluate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import {
} from '../../pinecone-generated-ts-fetch/assistant_evaluation';

/**
* The `Eval` interface defines the structure of the input object for the `evaluate` method.
* The `AssistantEval` interface defines the structure of the input object for the `evaluate` method.
*/
export interface Eval {
export interface AssistantEval {
/**
* The question to evaluate.
*/
Expand Down Expand Up @@ -39,12 +39,12 @@ export interface Eval {
* // usage: { promptTokens: 1134, completionTokens: 21, totalTokens: 1155 }
* // }
* ```
* @param options - An {@link Eval} object containing the question, the answer, and a ground truth answer to
* @param options - An {@link AssistantEval} object containing the question, the answer, and a ground truth answer to
* evaluate.
* @returns A Promise that resolves to an {@link AlignmentResponse} object.
*/
export const evaluateClosed = (api: MetricsApi) => {
return async (options: Eval) => {
export const evaluate = (metricsApi: MetricsApi) => {
return async (options: AssistantEval) => {
if (
options.question == '' ||
options.answer == '' ||
Expand All @@ -62,6 +62,6 @@ export const evaluateClosed = (api: MetricsApi) => {
groundTruthAnswer: options.groundTruth,
},
} as MetricsAlignmentRequest;
return api.metricsAlignment(request);
return metricsApi.metricsAlignment(request);
};
};
21 changes: 10 additions & 11 deletions src/assistant/control/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
export {
createAssistantClosed,
createAssistantRequest,
} from './createAssistant';
export { deleteAssistantClosed } from './deleteAssistant';
export { getAssistantClosed } from './getAssistant';
export { listAssistantsClosed } from './listAssistants';
export { updateAssistantClosed } from './updateAssistant';
export { AssistantCtrlPlane } from './AssistantCtrlPlane';
export { AssistantHostSingleton } from '../assistantHostSingleton';
export { assistantControlOperationsBuilder } from './assistantOperationsBuilderCtrl';
export { createAssistant, CreateAssistantOptions } from './createAssistant';
export { deleteAssistant } from './deleteAssistant';
export { describeAssistant } from './describeAssistant';
export { listAssistants } from './listAssistants';
export { updateAssistant, UpdateAssistantOptions } from './updateAssistant';
export { evaluate, AssistantEval } from './evaluate';
// export { AssistantCtrlPlane } from './AssistantCtrlPlane';
// export { AssistantHostSingleton } from '../assistantHostSingleton';
export { asstControlOperationsBuilder } from './asstControlOperationsBuilder';
export { asstMetricsOperationsBuilder } from './asstMetricsOperationsBuilder';
2 changes: 1 addition & 1 deletion src/assistant/control/listAssistants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
* @param api - The `ManageAssistantsControlApi` object that defines the methods for interacting with the Assistant API.
* @returns A Promise that resolves to an object containing an array of {@link Assistant} models.
*/
export const listAssistantsClosed = (api: ManageAssistantsControlApi) => {
export const listAssistants = (api: ManageAssistantsControlApi) => {
return async (): Promise<ListAssistants200Response> => {
return await api.listAssistants();
};
Expand Down
Loading

0 comments on commit 2462a5f

Please sign in to comment.