Skip to content

Commit

Permalink
#360 plugin preferences (#361)
Browse files Browse the repository at this point in the history
* feature: #360 plugin preferences

* chore: update core-plugin README.md

* chore: create collections on start
  • Loading branch information
louis-menlo authored Oct 16, 2023
1 parent 6e5137f commit 539e4f4
Show file tree
Hide file tree
Showing 10 changed files with 360 additions and 116 deletions.
8 changes: 3 additions & 5 deletions electron/core/plugins/data-plugin/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { core, store, RegisterExtensionPoint, StoreService, DataService } from "@janhq/plugin-core";
import { core, store, RegisterExtensionPoint, StoreService, DataService, PluginService } from "@janhq/plugin-core";

// Provide an async method to manipulate the price provided by the extension point
const PluginName = "data-plugin";
const MODULE_PATH = "data-plugin/dist/cjs/module.js";

/**
Expand All @@ -12,7 +12,6 @@ const MODULE_PATH = "data-plugin/dist/cjs/module.js";
*
*/
function createCollection({ name, schema }: { name: string; schema?: { [key: string]: any } }): Promise<void> {
console.log("renderer: creating collection:", name, schema);
return core.invokePluginFunc(MODULE_PATH, "createCollection", name, schema);
}

Expand Down Expand Up @@ -137,8 +136,7 @@ function onStart() {

// Register all the above functions and objects with the relevant extension points
export function init({ register }: { register: RegisterExtensionPoint }) {
onStart();

register(PluginService.OnStart, PluginName, onStart);
register(StoreService.CreateCollection, createCollection.name, createCollection);
register(StoreService.DeleteCollection, deleteCollection.name, deleteCollection);
register(StoreService.InsertOne, insertOne.name, insertOne);
Expand Down
34 changes: 21 additions & 13 deletions electron/core/plugins/inference-plugin/index.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import { EventName, InferenceService, NewMessageRequest, core, events, store } from "@janhq/plugin-core";
import { EventName, InferenceService, NewMessageRequest, PluginService, core, events, store } from "@janhq/plugin-core";

const MODULE_PATH = "inference-plugin/dist/module.js";
const PluginName = "inference-plugin";
const MODULE_PATH = `${PluginName}/dist/module.js`;
const inferenceUrl = "http://localhost:3928/llama/chat_completion";

const initModel = async (product) => core.invokePluginFunc(MODULE_PATH, "initModel", product);

const inferenceUrl = () => "http://localhost:3928/llama/chat_completion";

const stopModel = () => {
core.invokePluginFunc(MODULE_PATH, "killSubprocess");
};

async function handleMessageRequest(data: NewMessageRequest) {
// TODO: Common collections should be able to access via core functions instead of store
const messageHistory = (await store.findMany("messages", { conversationId: data.conversationId })) ?? [];
const recentMessages = messageHistory.slice(-10).map((message) => {
return {
content: message.message,
role: message.user === "user" ? "user" : "assistant",
};
});
const messageHistory =
(await store.findMany("messages", { conversationId: data.conversationId }, [{ createdAt: "asc" }])) ?? [];
const recentMessages = messageHistory
.filter((e) => e.message !== "" && (e.user === "user" || e.user === "assistant"))
.slice(-10)
.map((message) => {
return {
content: message.message,
role: message.user === "user" ? "user" : "assistant",
};
});

const message = {
...data,
Expand All @@ -33,7 +37,7 @@ async function handleMessageRequest(data: NewMessageRequest) {
message._id = id;
events.emit(EventName.OnNewMessageResponse, message);

const response = await fetch(inferenceUrl(), {
const response = await fetch(inferenceUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Expand Down Expand Up @@ -80,9 +84,13 @@ async function handleMessageRequest(data: NewMessageRequest) {
const registerListener = () => {
events.on(EventName.OnNewMessageRequest, handleMessageRequest);
};

const onStart = async () => {
registerListener();
};
// Register all the above functions and objects with the relevant extension points
export function init({ register }) {
registerListener();
register(PluginService.OnStart, PluginName, onStart);
register(InferenceService.InitModel, initModel.name, initModel);
register(InferenceService.StopModel, stopModel.name, stopModel);
}
6 changes: 4 additions & 2 deletions electron/core/plugins/model-management-plugin/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { ModelManagementService, RegisterExtensionPoint, core, store } from "@janhq/plugin-core";
import { ModelManagementService, PluginService, RegisterExtensionPoint, core, store } from "@janhq/plugin-core";

const PluginName = "model-management-plugin";
const MODULE_PATH = "model-management-plugin/dist/module.js";

const getDownloadedModels = () => core.invokePluginFunc(MODULE_PATH, "getDownloadedModels");
Expand Down Expand Up @@ -81,7 +83,7 @@ function onStart() {

// Register all the above functions and objects with the relevant extension points
export function init({ register }: { register: RegisterExtensionPoint }) {
onStart();
register(PluginService.OnStart, PluginName, onStart);

register(ModelManagementService.GetDownloadedModels, getDownloadedModels.name, getDownloadedModels);
register(ModelManagementService.GetAvailableModels, getAvailableModels.name, getAvailableModels);
Expand Down
89 changes: 76 additions & 13 deletions electron/core/plugins/openai-plugin/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import { EventName, NewMessageRequest, events, store } from "@janhq/plugin-core";
import {
PluginService,
EventName,
NewMessageRequest,
events,
store,
preferences,
RegisterExtensionPoint,
} from "@janhq/plugin-core";
import { Configuration, OpenAIApi } from "azure-openai";

const PluginName = "openai-plugin";

const setRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
XMLHttpRequest.prototype.setRequestHeader = function newSetRequestHeader(key: string, val: string) {
if (key.toLocaleLowerCase() === "user-agent") {
Expand All @@ -9,22 +19,52 @@ XMLHttpRequest.prototype.setRequestHeader = function newSetRequestHeader(key: st
setRequestHeader.apply(this, [key, val]);
};

const openai = new OpenAIApi(
new Configuration({
azure: {
apiKey: "", //Your API key goes here
endpoint: "", //Your endpoint goes here. It is like: "https://endpointname.openai.azure.com/"
deploymentName: "", //Your deployment name goes here. It is like "chatgpt"
},
})
);
var openai: OpenAIApi | undefined = undefined;

const setup = async () => {
const apiKey: string = (await preferences.get(PluginName, "apiKey")) ?? "";
const endpoint: string = (await preferences.get(PluginName, "endpoint")) ?? "";
const deploymentName: string = (await preferences.get(PluginName, "deploymentName")) ?? "";
if (apiKey === "") {
return;
}
openai = new OpenAIApi(
new Configuration({
azure: {
apiKey, //Your API key goes here
endpoint, //Your endpoint goes here. It is like: "https://endpointname.openai.azure.com/"
deploymentName, //Your deployment name goes here. It is like "chatgpt"
},
})
);
};

async function onStart() {
setup();
registerListener();
}

async function handleMessageRequest(data: NewMessageRequest) {
if (!openai) {
const message = {
...data,
message: "Your API key is not set. Please set it in the plugin preferences.",
user: "GPT-3",
avatar: "https://static-assets.jan.ai/openai-icon.jpg",
createdAt: new Date().toISOString(),
_id: undefined,
};
const id = await store.insertOne("messages", message);
message._id = id;
events.emit(EventName.OnNewMessageResponse, message);
return;
}

const message = {
...data,
message: "",
user: "GPT-3",
avatar: "",
avatar: "https://static-assets.jan.ai/openai-icon.jpg",
createdAt: new Date().toISOString(),
_id: undefined,
};
Expand All @@ -44,7 +84,30 @@ async function handleMessageRequest(data: NewMessageRequest) {
const registerListener = () => {
events.on(EventName.OnNewMessageRequest, handleMessageRequest);
};

const onPreferencesUpdate = () => {
setup();
};
// Register all the above functions and objects with the relevant extension points
export function init({ register }) {
registerListener();
export function init({ register }: { register: RegisterExtensionPoint }) {
register(PluginService.OnStart, PluginName, onStart);
register(PluginService.OnPreferencesUpdate, PluginName, onPreferencesUpdate);

preferences.registerPreferences<string>(register, PluginName, "apiKey", "API Key", "Azure Project API Key", "");
preferences.registerPreferences<string>(
register,
PluginName,
"endpoint",
"API Endpoint",
"Azure Deployment Endpoint API",
""
);
preferences.registerPreferences<string>(
register,
PluginName,
"deploymentName",
"Deployment Name",
"The deployment name you chose when you deployed the model",
""
);
}
2 changes: 1 addition & 1 deletion electron/core/plugins/openai-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "azure-openai-plugin",
"version": "1.0.0",
"description": "Inference plugin for Azure OpenAI",
"icon": "https://raw.githubusercontent.com/tailwindlabs/heroicons/88e98b0c2b458553fbadccddc2d2f878edc0387b/src/20/solid/command-line.svg",
"icon": "https://static-assets.jan.ai/openai-icon.jpg",
"main": "dist/index.js",
"author": "Jan",
"license": "MIT",
Expand Down
80 changes: 64 additions & 16 deletions plugin-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,7 @@ export function init({ register }: { register: RegisterExtensionPoint }) {
}
```

### Access Core API

To access the Core API in your plugin, you can follow the code examples and explanations provided below.

##### Import Core API and Store Module

In your main entry code (e.g., `index.ts`), start by importing the necessary modules and functions from the `@janhq/plugin-core` library.

```js
// index.ts
import { store, core } from "@janhq/plugin-core";
```

#### Interact with Local Data Storage
### Interact with Local Data Storage

The Core API allows you to interact with local data storage. Here are a couple of examples of how you can use it:

Expand All @@ -56,6 +43,8 @@ The Core API allows you to interact with local data storage. Here are a couple o
You can use the store.insertOne function to insert data into a specific collection in the local data store.

```js
import { store } from "@janhq/plugin-core";

function insertData() {
store.insertOne("conversations", { name: "meow" });
// Insert a new document with { name: "meow" } into the "conversations" collection.
Expand All @@ -70,6 +59,8 @@ store.getOne(collectionName, key) retrieves a single document that matches the p
store.getMany(collectionName, selector, sort) retrieves multiple documents that match the provided selector in the specified collection.

```js
import { store } from "@janhq/plugin-core";

function getData() {
const selector = { name: "meow" };
const data = store.findMany("conversations", selector);
Expand Down Expand Up @@ -108,7 +99,7 @@ function deleteData() {
}
```

#### Events
### Events

You can subscribe to NewMessageRequest events by defining a function to handle the event and registering it with the events object:

Expand Down Expand Up @@ -145,6 +136,56 @@ function handleMessageRequest(data: NewMessageRequest) {
}
```

### Preferences

To register plugin preferences, you can use the preferences object from the @janhq/plugin-core package. Here's an example of how to register and retrieve plugin preferences:

```js
import { PluginService, preferences } from "@janhq/plugin-core";

const PluginName = "your-first-plugin";

export function init({ register }: { register: RegisterExtensionPoint }) {
// Register preference update handlers. E.g. update plugin instance with new configuration
register(PluginService.OnPreferencesUpdate, PluginName, onPreferencesUpdate);

// Register plugin preferences. E.g. Plugin need apiKey and endpoint to connect to your service
preferences.registerPreferences<string>(register, PluginName, "apiKey", "");
preferences.registerPreferences<string>(register, PluginName, "endpoint", "");
}
```

In this example, we're registering preference update handlers and plugin preferences using the preferences object. We're also defining a PluginName constant to use as the name of the plugin.

To retrieve the values of the registered preferences, we're using the get method of the preferences object and passing in the name of the plugin and the name of the preference.

```js
import { preferences } from "@janhq/plugin-core";

const PluginName = "your-first-plugin";

const setup = async () => {
// Retrieve apiKey
const apiKey: string = (await preferences.get(PluginName, "apiKey")) ?? "";

// Retrieve endpoint
const endpoint: string = (await preferences.get(PluginName, "endpoint")) ?? "";
}
```
### Access Core API
To access the Core API in your plugin, you can follow the code examples and explanations provided below.
##### Import Core API and Store Module
In your main entry code (e.g., `index.ts`), start by importing the necessary modules and functions from the `@janhq/plugin-core` library.
```js
// index.ts
import { core } from "@janhq/plugin-core";
```
#### Perform File Operations
The Core API also provides functions to perform file operations. Here are a couple of examples:
Expand All @@ -169,7 +210,7 @@ function deleteModel(filePath: string) {
}
```
### Execute plugin module in main process
#### Execute plugin module in main process
To execute a plugin module in the main process of your application, you can follow the steps outlined below.
Expand Down Expand Up @@ -294,4 +335,11 @@ The `SystemMonitoringService` enum includes methods for monitoring system resour
- `GetResourcesInfo`: Gets information about system resources.
- `GetCurrentLoad`: Gets the current system load.
## PluginService
The `PluginService` enum includes plugin cycle handlers:
- `OnStart`: Handler for starting. E.g. Create a collection.
- `OnPreferencesUpdate`: Handler for preferences update. E.g. Update instances with new configurations.
For more detailed information on each of these components, please refer to the source code.
Loading

0 comments on commit 539e4f4

Please sign in to comment.