Skip to content

Commit ce5efb2

Browse files
feat: examples of agents (function calling), knowledge base (#44)
* feat: demo page with navbar * refactor: renamed package name ai-assistant -> openai-assistant * feat: examples of agents (function calling) * refactor: changed cors configuration * refactor: websocket configuration * feat: added document to the knowledge base * chore: release version 1.0.0
1 parent 51efa7a commit ce5efb2

29 files changed

+551
-241
lines changed

.env.dist

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# OpenAI API Key
22
OPENAI_API_KEY=
3-
43
# Assistant ID - leave it empty if you don't have an assistant yet
54
ASSISTANT_ID=
5+
6+
# Agents:
7+
# -------------------------------------------------------------------
8+
# OpenWeather (Current Weather Data)
9+
OPENWEATHER_API_KEY=

.github/workflows/demo-deploy.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
name: Demo deploy
22

33
on:
4+
push:
5+
branches:
6+
- preview
47
release:
58
types: [published]
69

README.md

Lines changed: 13 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,19 @@ Introducing the NestJS library, designed to harness the power of OpenAI's Assist
1919

2020
#### AI Assistant library features
2121

22-
- **WebSockets**: The library provides a WebSocket server for real-time communication between the client and the assistant.
23-
- **REST API**: The library provides a REST API for communication with the assistant.
2422
- **Function calling**: The library provides a way to create functions, which allows you to extend the assistant's capabilities with custom logic.
25-
- **File support**: The library provides a way to add files to the assistant, which allows you to extend the assistant's knowledge base with custom data.
2623
- **TTS (Text-to-Speech)**: The library provides a way to convert text to speech, which allows you to create voice-based interactions with the assistant.
2724
- **STT (Speech-to-Text)**: The library provides a way to convert speech to text, which allows you to create voice-based interactions with the assistant.
25+
- **File support**: The library provides a way to add files to the assistant, which allows you to extend the assistant's knowledge base with custom data.
26+
- **WebSockets**: The library provides a WebSocket server for real-time communication between the client and the assistant.
27+
- **REST API**: The library provides a REST API for communication with the assistant.
2828

2929
#### Additional features in the repository
3030

3131
- **Embedded chatbot**: The library provides a way to embed the chatbot on various websites through JavaScript scripts.
3232
- **Chatbot client application**: The repository includes an example client application (SPA) with a chatbot.
3333

34-
## Getting started
34+
## 🏆 Getting started
3535

3636
In this section, you will learn how to integrate the AI Assistant library into your NestJS application. The following steps will guide you through the process of setting up the library and creating simple functionalities.
3737

@@ -55,10 +55,10 @@ npm i @boldare/openai-assistant --save
5555

5656
### Step 2: Env variables
5757

58-
Set up your environment variables, create environment variables in the `.env` file in the root directory of the project, and populate it with the necessary secrets. You will need to add the OpenAI API Key and the Assistant ID. The Assistant ID is optional, and you can leave it empty if you don't have an assistant yet.
58+
Set up your environment variables, create environment variables in the `.env` file in the root directory of the project, and populate it with the necessary secrets. The assistant ID is optional and serves as a unique identifier for your assistant. When the environment variable is not set, the assistant will be created automatically. You can use the assistant ID to connect to an existing assistant, which can be found in the OpenAI platform after creating an assistant.
5959

6060
Create a `.env` file in the root directory of your project and populate it with the necessary secrets:
61-
61+
6262
```bash
6363
touch .env
6464
```
@@ -73,54 +73,14 @@ OPENAI_API_KEY=
7373
ASSISTANT_ID=
7474
```
7575

76-
### Step 3: Configuration
77-
78-
Configure the settings for your assistant. For more information about assistant parameters, you can refer to the [OpenAI documentation](https://platform.openai.com/docs/assistants/how-it-works/creating-assistants). A sample configuration can be found in ([chat.config.ts](apps%2Fapi%2Fsrc%2Fapp%2Fchat%2Fchat.config.ts)).
79-
80-
```js
81-
// chat.config.ts file
82-
83-
// Default OpenAI configuration
84-
export const assistantParams: AssistantCreateParams = {
85-
name: 'Your assistant name',
86-
instructions: `You are a chatbot assistant. Speak briefly and clearly.`,
87-
tools: [
88-
{ type: 'code_interpreter' },
89-
{ type: 'retrieval' },
90-
// (...) function calling - functions are configured by extended services
91-
],
92-
model: 'gpt-4-1106-preview',
93-
metadata: {},
94-
};
95-
96-
// Additional configuration for our assistant
97-
export const assistantConfig: AssistantConfigParams = {
98-
id: process.env['ASSISTANT_ID'], // OpenAI API Key
99-
params: assistantParams, // AssistantCreateParams
100-
filesDir: './apps/api/src/app/knowledge', // Path to the directory with files (the final path is "fileDir" + "single file")
101-
files: ['file1.txt', 'file2.json'], // List of file names (or paths if you didn't fill in the above parameter)
102-
};
103-
```
104-
105-
Import the AI Assistant module with your configuration into the module file where you intend to use it:
76+
Please note that the `.env` file should not be committed to the repository. Add it to the `.gitignore` file to prevent it from being committed.
10677

107-
```js
108-
@Module({
109-
imports: [AssistantModule.forRoot(assistantConfig)],
110-
})
111-
export class ChatbotModule {}
112-
```
113-
114-
Automatically, the library will add WebSockets ([chat.gateway.ts](libs/openai-assistant/src/lib/chat/chat.gateway.ts)) and a [REST API](https://assistant.ai.boldare.dev/api/docs) for the assistant. The WebSocket server will be available at the `/` endpoint, and the [REST API](https://assistant.ai.boldare.dev/api/docs) will be available at the `/api` endpoint (depending on the API prefix).
78+
### Step 3: Configuration
11579

116-
#### Websockets events
80+
The library provides a way to configure the assistant with the `AssistantModule.forRoot` method. The method takes a configuration object as an argument. Create a new configuration file like in a [sample configuration file (chat.config.ts)](apps%2Fapi%2Fsrc%2Fapp%2Fchat%2Fchat.config.ts) and fill it with the necessary configuration.
11781

118-
Currently, the library provides the following WebSocket events:
82+
More details about the configuration with code examples can be found in the [wiki](https://github.com/boldare/openai-assistant/wiki/%F0%9F%A4%96-AI-Assistant#step-3-configuration).
11983

120-
| Event name | Description |
121-
| ------------------ | -------------------------------------------------------- |
122-
| `send_message` | The event is emitted when the user sends a message. |
123-
| `message_received` | The event is emitted when the assistant sends a message. |
12484

12585
### Step 4: Function calling
12686

@@ -130,117 +90,16 @@ Create a new service that extends the `AgentBase` class, fill the definition and
13090
- The `definition` property is an object that describes the function and its parameters.
13191

13292
For more information about function calling, you can refer to the [OpenAI documentation](https://platform.openai.com/docs/assistants/tools/defining-functions).
133-
Below is an example of a service that extends the `AgentBase` class:
134-
135-
```js
136-
@Injectable()
137-
export class GetNicknameAgent extends AgentBase {
138-
definition: AssistantCreateParams.AssistantToolsFunction = {
139-
type: 'function',
140-
function: {
141-
name: this.constructor.name,
142-
description: `Get the nickname of a city`,
143-
parameters: {
144-
type: 'object',
145-
properties: {
146-
location: {
147-
type: 'string',
148-
description: 'The city and state e.g. San Francisco, CA',
149-
},
150-
},
151-
required: ['location'],
152-
},
153-
},
154-
};
155-
156-
constructor(protected readonly agentService: AgentService) {
157-
super(agentService);
158-
}
159-
160-
async output(data: AgentData): Promise<string> {
161-
// TODO: Your logic here
162-
return 'Your string value';
163-
}
164-
}
165-
```
166-
167-
More examples can be found in the [agents](apps/api/src/app/chat/agents) directory.
168-
169-
Import the service into the module file where you intend to use it:
17093

171-
```js
172-
import { Module } from '@nestjs/common';
173-
import { AgentModule } from '@boldare/openai-assistant';
174-
import { GetNicknameAgent } from './get-nickname.agent';
175-
176-
@Module({
177-
imports: [AgentModule],
178-
providers: [GetNicknameAgent],
179-
})
180-
export class AgentsModule {}
181-
```
182-
183-
and remember to add the `AgentsModule` above the `AssistantModule` in your main module file (e.g. `chat.module.ts`):
184-
185-
```js
186-
@Module({
187-
imports: [AgentsModule, AssistantModule.forRoot(assistantConfig)],
188-
})
189-
export class ChatModule {}
190-
```
94+
The instructions for creating a function can be found in the [wiki](https://github.com/boldare/openai-assistant/wiki/%F0%9F%A4%96-AI-Assistant#step-4-function-calling), while examples can be found in the [agents](apps/api/src/app/chat/agents) directory.
19195

19296
---
19397

19498
# 👨‍💻 Repository
19599

196-
The repository includes a library with an AI assistant as well as other useful parts:
100+
The complete documentation on how to run the demo with all applications and libraries from the repository can be found in the [wiki](https://github.com/boldare/openai-assistant/wiki/%F0%9F%91%A8%E2%80%8D%F0%9F%92%BB-Repository).
197101

198-
| Name | Description | More |
199-
|-------------------------|---------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------|
200-
| `@boldare/openai-assistant` | A NestJS library based on the OpenAI Assistant for building efficient, scalable, and quick solutions for AI assistants/chatbots | [Documentation](https://github.com/boldare/openai-assistant/wiki/%F0%9F%A4%96-AI-Assistant) |
201-
| `@boldare/ai-embedded` | The code enables embedding the chatbot on various websites through JavaScript scripts. | [Documentation](https://github.com/boldare/openai-assistant/wiki/%F0%9F%96%87-Integrating-Chatbot-into-Your-Website) |
202-
| `api` | Example usage of the `@boldare/openai-assistant` library. | [Documentation](https://github.com/boldare/openai-assistant/wiki/%F0%9F%91%A8%E2%80%8D%F0%9F%92%BB-Repository) |
203-
| `spa` | Example client application (SPA) with a chatbot. | [Documenation](https://github.com/boldare/openai-assistant/wiki/%F0%9F%92%AC-Chatbot-%E2%80%90-Client-application) |
204-
205-
## Getting started
206-
207-
### Step 1: Install dependencies
208-
209-
```bash
210-
npm install
211-
```
212-
213-
### Step 2: Env variables
214-
215-
Set up your environment variables, copy the `.env.dist` file to `.env` file in the root directory of the project, and populate it with the necessary secrets.
216-
217-
```bash
218-
cp .env.dist .env
219-
```
220-
221-
### Step 3: Run applications
222-
223-
```bash
224-
# Start the app (api and spa)
225-
npm run start:dev
226-
227-
# Start the api
228-
npm run start:api
229-
230-
# Start the spa
231-
npm run start:spa
232-
```
233-
234-
Now you can open your browser and navigate to:
235-
236-
| URL | Description |
237-
| ------------------------------ | --------------------------------------- |
238-
| http://localhost:4200/ | Client application (Angular) |
239-
| http://localhost:3000/ | API application, WebSockets (socket.io) |
240-
| http://localhost:3000/api/ | API endpoints |
241-
| http://localhost:3000/api/docs | API documentation (swagger) |
242-
243-
### 🎉 Happy coding 🎉
102+
---
244103

245104
# License
246105

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Module } from '@nestjs/common';
2-
import { GetAnimalAgent } from './get-animal.agent';
3-
import { AgentModule } from '@boldare/openai-assistant';
2+
import { WeatherModule } from './weather/weather.module';
3+
import { PokemonModule } from './pokemon/pokemon.module';
4+
import { CurrencyModule } from './currency/currency.module';
45

56
@Module({
6-
imports: [AgentModule],
7-
providers: [GetAnimalAgent],
7+
imports: [WeatherModule, PokemonModule, CurrencyModule],
88
})
99
export class AgentsModule {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Module } from '@nestjs/common';
2+
import { GetCurrencyAgent } from './get-currency.agent';
3+
import { HttpModule } from '@nestjs/axios';
4+
import { CurrencyService } from './currency.service';
5+
import { AgentModule } from '@boldare/openai-assistant';
6+
7+
@Module({
8+
imports: [AgentModule, HttpModule],
9+
providers: [CurrencyService, GetCurrencyAgent],
10+
})
11+
export class CurrencyModule {}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { HttpException, Injectable, Logger } from '@nestjs/common';
2+
import { HttpService } from '@nestjs/axios';
3+
import { catchError, firstValueFrom } from 'rxjs';
4+
import { AxiosError } from 'axios';
5+
6+
@Injectable()
7+
export class CurrencyService {
8+
private readonly logger = new Logger(CurrencyService.name);
9+
10+
constructor(private httpService: HttpService) {}
11+
12+
async getExchangeRate(currency: string) {
13+
const params = { from: currency };
14+
const { data } = await firstValueFrom(
15+
this.httpService
16+
.get('https://api.frankfurter.app/latest', { params })
17+
.pipe(
18+
catchError((error: AxiosError) => {
19+
const message = error?.response?.data || {
20+
message: 'Unknown error',
21+
};
22+
this.logger.error(message);
23+
throw new HttpException(message, error?.response?.status || 500);
24+
}),
25+
),
26+
);
27+
28+
return data;
29+
}
30+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { Injectable } from '@nestjs/common';
2+
import { AssistantCreateParams } from 'openai/resources/beta';
3+
import { AgentBase, AgentData, AgentService } from '@boldare/openai-assistant';
4+
import { CurrencyService } from './currency.service';
5+
6+
@Injectable()
7+
export class GetCurrencyAgent extends AgentBase {
8+
override definition: AssistantCreateParams.AssistantToolsFunction = {
9+
type: 'function',
10+
function: {
11+
name: this.constructor.name,
12+
description: 'Get the current currency exchange rate.',
13+
parameters: {
14+
type: 'object',
15+
properties: {
16+
currency: {
17+
type: 'string',
18+
description: 'Currency code e.g. USD, EUR, GBP, etc.',
19+
},
20+
},
21+
required: ['currency'],
22+
},
23+
},
24+
};
25+
26+
constructor(
27+
override readonly agentService: AgentService,
28+
private readonly currencyService: CurrencyService,
29+
) {
30+
super(agentService);
31+
}
32+
33+
override async output(data: AgentData): Promise<string> {
34+
try {
35+
// Parse the parameters from the input data
36+
const params = JSON.parse(data.params);
37+
const currency = params?.currency;
38+
39+
// Check if the currency is provided
40+
if (!currency) {
41+
return 'No currency provided';
42+
}
43+
44+
// Get the current currency exchange rate
45+
const response = await this.currencyService.getExchangeRate(currency);
46+
47+
// Return the result
48+
return `The current exchange rate for ${currency} is: ${JSON.stringify(
49+
response,
50+
)}`;
51+
} catch (errors) {
52+
// Handle the errors
53+
return `Invalid data: ${JSON.stringify(errors)}`;
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)