Skip to content

Commit

Permalink
feat(assistant): updated to the newest openai version (4.42.0)
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastianmusial committed May 7, 2024
1 parent 48697e4 commit aa8f1c0
Show file tree
Hide file tree
Showing 21 changed files with 1,344 additions and 1,338 deletions.
15 changes: 11 additions & 4 deletions apps/api/src/app/chat/chat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,21 @@ import 'dotenv/config';
export const assistantParams: AssistantCreateParams = {
name: '@boldare/openai-assistant',
instructions: `You are a chatbot assistant. Use the general knowledge to answer questions. Speak briefly and clearly.`,
tools: [{ type: 'code_interpreter' }, { type: 'retrieval' }],
model: 'gpt-4-1106-preview',
metadata: {},
tools: [{ type: 'code_interpreter' }, { type: 'file_search' }],
model: 'gpt-4-turbo',
temperature: 0.05,
};

export const assistantConfig: AssistantConfigParams = {
id: process.env['ASSISTANT_ID'] || '',
params: assistantParams,
filesDir: './apps/api/src/app/knowledge',
files: ['33-things-to-ask-your-digital-product-development-partner.md'],
toolResources: {
fileSearch: {
boldare: ['33-things-to-ask-your-digital-product-development-partner.md'],
},
codeInterpreter: {
fileNames: [],
},
},
};
2 changes: 1 addition & 1 deletion apps/api/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ async function bootstrap() {
const globalPrefix = 'api';
const config = new DocumentBuilder()
.setTitle('@boldare/openai-assistant')
.setVersion('1.0.2')
.setVersion('1.1.0')
.build();
const document = SwaggerModule.createDocument(app, config);

Expand Down
14 changes: 11 additions & 3 deletions apps/spa/src/app/modules/+chat/shared/chat.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ import { ThreadService } from './thread.service';
import { ChatFilesService } from './chat-files.service';
import { environment } from '../../../../environments/environment';
import { OpenAiFile, GetThreadResponseDto } from '@boldare/openai-assistant';
import { Message } from 'openai/resources/beta/threads/messages';
import { TextContentBlock } from 'openai/resources/beta/threads/messages/messages';
import {
Message,
TextContentBlock,
} from 'openai/resources/beta/threads/messages';

@Injectable({ providedIn: 'root' })
export class ChatService {
Expand Down Expand Up @@ -134,7 +136,13 @@ export class ChatService {
this.chatGatewayService.callStart({
content,
threadId: this.threadService.threadId$.value,
file_ids: files.map(file => file.id) || [],
attachments: files.map(
file =>
({
file_id: file.id,
tools: [{ type: 'code_interpreter' }],
}) || [],
),
});
}

Expand Down
4 changes: 2 additions & 2 deletions libs/openai-assistant/package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "@boldare/openai-assistant",
"description": "NestJS library for building chatbot solutions based on the OpenAI Assistant API",
"version": "1.0.3",
"version": "1.1.0",
"private": false,
"dependencies": {
"tslib": "^2.3.0",
"openai": "^4.26.1",
"openai": "^4.42.0",
"@nestjs/common": "^10.0.2",
"@nestjs/platform-express": "^10.0.2",
"dotenv": "^16.3.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import OpenAI from 'openai';
import { ConfigService } from '../config';
import { AiService } from '../ai';
import { AssistantFilesService } from './assistant-files.service';
import OpenAI from 'openai';
import { AssistantToolResources } from './assistant.model';

jest.mock('fs', () => ({
createReadStream: jest.fn().mockReturnValue('file'),
Expand All @@ -11,11 +12,28 @@ describe('AssistantFilesService', () => {
let aiService: AiService;
let configService: ConfigService;
let assistantFilesService: AssistantFilesService;
const create = jest.fn().mockResolvedValue({ id: 'id' });
const fileNames = ['file1', 'file2'];
const toolResources: AssistantToolResources = {
codeInterpreter: { fileNames },
fileSearch: { boldare: fileNames },
};

beforeEach(() => {
aiService = new AiService();
configService = new ConfigService();
assistantFilesService = new AssistantFilesService(configService, aiService);
aiService.provider = {
files: { create },
beta: {
vectorStores: {
create,
fileBatches: {
uploadAndPoll: jest.fn().mockResolvedValue({ id: 'id' }),
},
},
},
} as unknown as OpenAI;
});

afterEach(() => {
Expand All @@ -27,34 +45,28 @@ describe('AssistantFilesService', () => {
});

it('should create files', async () => {
const fileNames = ['file1', 'file2'];
const create = jest.fn().mockResolvedValue({ id: 'id' });
aiService.provider = { files: { create } } as unknown as OpenAI;
configService.get = jest.fn().mockReturnValue({ filesDir: 'dir' });

const result = await assistantFilesService.create(fileNames);
const result = await assistantFilesService.create(toolResources);

expect(result).toEqual(['id', 'id']);
expect(create).toHaveBeenCalledTimes(2);
expect(result).toEqual({
code_interpreter: { file_ids: ['id', 'id'] },
file_search: { vector_store_ids: ['id'] },
});
expect(create).toHaveBeenCalledWith({
file: 'file',
purpose: 'assistants',
});
});

it('should create files without file directory', async () => {
const fileNames = ['file1', 'file2'];
const create = jest.fn().mockResolvedValue({ id: 'id' });
aiService.provider = { files: { create } } as unknown as OpenAI;
configService.get = jest.fn().mockReturnValue({});

const result = await assistantFilesService.create(fileNames);
const result = await assistantFilesService.create(toolResources);

expect(result).toEqual(['id', 'id']);
expect(create).toHaveBeenCalledTimes(2);
expect(create).toHaveBeenCalledWith({
file: 'file',
purpose: 'assistants',
expect(result).toEqual({
code_interpreter: { file_ids: ['id', 'id'] },
file_search: { vector_store_ids: ['id'] },
});
});
});
96 changes: 85 additions & 11 deletions libs/openai-assistant/src/lib/assistant/assistant-files.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,103 @@ import { FileObject } from 'openai/resources';
import { createReadStream } from 'fs';
import { AiService } from '../ai';
import { ConfigService } from '../config';

import { AssistantCreateParams, VectorStore } from 'openai/resources/beta';
import ToolResources = AssistantCreateParams.ToolResources;
import { AssistantUpdateParams } from 'openai/src/resources/beta/assistants';
import {
AssistantCodeInterpreter,
AssistantFileSearch,
AssistantToolResources,
} from './assistant.model';
@Injectable()
export class AssistantFilesService {
constructor(
private readonly assistantConfig: ConfigService,
private readonly aiService: AiService,
) {}

async create(
fileNames: string[],
async getFiles(
attachments: string[] = [],
fileDir = this.assistantConfig.get().filesDir,
): Promise<string[]> {
): Promise<FileObject[]> {
const files: FileObject[] = [];

for (const name of fileNames) {
const file = await this.aiService.provider.files.create({
file: createReadStream(`${fileDir || ''}/${name}`),
purpose: 'assistants',
});
await Promise.all(
attachments.map(async item => {
const file = await this.aiService.provider.files.create({
file: createReadStream(`${fileDir || ''}/${item}`),
purpose: 'assistants',
});

files.push(file);
}),
);

files.push(file);
return files;
}

async getCodeInterpreterResources(
data: AssistantCodeInterpreter,
fileDir = this.assistantConfig.get().filesDir,
): Promise<ToolResources.CodeInterpreter> {
const files = await this.getFiles(data?.fileNames, fileDir);

return { file_ids: files.map(({ id }) => id) };
}

async getFileSearchResources(
data: AssistantFileSearch,
fileDir = this.assistantConfig.get().filesDir,
): Promise<ToolResources.FileSearch> {
if (!data) {
return { vector_store_ids: [] };
}

return files.map(({ id }) => id);
const vectorStores: VectorStore[] = [];

await Promise.all(
Object.entries(data).map(async ([name, values]) => {
if (!values.length) {
return;
}

const files = values.map(item =>
createReadStream(`${fileDir || ''}/${item}`),
);

const vectorStore =
await this.aiService.provider.beta.vectorStores.create({ name });

await this.aiService.provider.beta.vectorStores.fileBatches.uploadAndPoll(
vectorStore.id,
{ files },
);

vectorStores.push(vectorStore);
return vectorStore;
}),
);

return { vector_store_ids: vectorStores.map(({ id }) => id) };
}

async create(
toolResources: AssistantToolResources,
fileDir = this.assistantConfig.get().filesDir,
): Promise<AssistantUpdateParams.ToolResources> {
const code_interpreter = toolResources.codeInterpreter
? await this.getCodeInterpreterResources(
toolResources.codeInterpreter,
fileDir,
)
: undefined;
const file_search = toolResources.fileSearch
? await this.getFileSearchResources(toolResources.fileSearch, fileDir)
: undefined;

return {
...(code_interpreter ? { code_interpreter } : {}),
...(file_search ? { file_search } : {}),
};
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Injectable, Logger } from '@nestjs/common';
import { writeFile, readFile } from 'fs/promises';
import * as envfile from 'envfile';
import * as process from 'process';
import * as dotenv from 'dotenv';

@Injectable()
export class AssistantMemoryService {
Expand All @@ -20,6 +20,7 @@ export class AssistantMemoryService {
process.env['ASSISTANT_ID'] = id;

await writeFile(sourcePath, envfile.stringify(newVariables));
dotenv.config({ path: sourcePath });
} catch (error) {
this.logger.error(`Can't save variable: ${error}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AiService } from '../ai';
import { AgentService } from '../agent';
import { AssistantFilesService } from './assistant-files.service';
import { AssistantMemoryService } from './assistant-memory.service';
import { AssistantToolResources } from './assistant.model';

describe('AssistantController', () => {
let assistantController: AssistantController;
Expand Down Expand Up @@ -38,11 +39,14 @@ describe('AssistantController', () => {
jest
.spyOn(assistantService, 'updateFiles')
.mockReturnValue(Promise.resolve({} as Assistant));
const files = { files: [] };
const toolResources: AssistantToolResources = {
codeInterpreter: { fileNames: ['file1'] },
fileSearch: { boldare: ['file1'] },
};

await assistantController.updateAssistant(files);
await assistantController.updateAssistant({ toolResources });

expect(assistantService.updateFiles).toHaveBeenCalledWith(files.files);
expect(assistantService.updateFiles).toHaveBeenCalledWith(toolResources);
});

afterEach(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { Body, Controller, Post } from '@nestjs/common';
import { Assistant } from 'openai/resources/beta';
import { AssistantService } from './assistant.service';
import { AssistantFiles } from './assistant.model';
import { AssistantUpdate } from './assistant.model';

@Controller('assistant')
export class AssistantController {
constructor(public readonly assistantService: AssistantService) {}

@Post('')
async updateAssistant(@Body() { files }: AssistantFiles): Promise<Assistant> {
return this.assistantService.updateFiles(files);
async updateAssistant(
@Body() { toolResources }: AssistantUpdate,
): Promise<Assistant> {
return this.assistantService.updateFiles(toolResources);
}
}
4 changes: 2 additions & 2 deletions libs/openai-assistant/src/lib/assistant/assistant.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { AssistantConfigParams } from './assistant.model';
export const assistantParamsMock: AssistantCreateParams = {
name: '@boldare/tests',
instructions: `test instructions`,
tools: [{ type: 'retrieval' }],
tools: [{ type: 'file_search' }],
model: 'gpt-3.5-turbo',
metadata: {},
};
Expand All @@ -13,5 +13,5 @@ export const assistantConfigMock: AssistantConfigParams = {
id: 'test1234',
params: assistantParamsMock,
filesDir: './apps/api/src/app/knowledge',
files: [],
toolResources: null,
};
14 changes: 11 additions & 3 deletions libs/openai-assistant/src/lib/assistant/assistant.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,17 @@ export interface AssistantConfigParams {
params: AssistantCreateParams;
options?: RequestOptions;
filesDir?: string;
files?: string[];
toolResources?: AssistantToolResources | null;
}

export interface AssistantFiles {
files?: string[];
export interface AssistantToolResources {
fileSearch?: AssistantFileSearch;
codeInterpreter?: AssistantCodeInterpreter;
}

export interface AssistantUpdate {
toolResources: AssistantToolResources;
}

export type AssistantFileSearch = Record<string, string[]> | null;
export type AssistantCodeInterpreter = Record<'fileNames', string[]> | null;
Loading

0 comments on commit aa8f1c0

Please sign in to comment.