diff --git a/README.md b/README.md
index 5464ff1c1..8405eb74e 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# Jan - Personal AI
+# Jan - Own Your AI
-
+
@@ -21,7 +21,7 @@
> ⚠️ **Jan is currently in Development**: Expect breaking changes and bugs!
-Jan is a free, open-source alternative to OpenAI that runs on your personal computer.
+Jan is a free, open-source alternative to OpenAI's platform that runs on a local folder of open-format files.
**Jan runs on any hardware.** From PCs to multi-GPU clusters, Jan supports universal architectures:
diff --git a/core/src/events.ts b/core/src/events.ts
index e962dead4..fa0bef15c 100644
--- a/core/src/events.ts
+++ b/core/src/events.ts
@@ -12,44 +12,16 @@ export enum EventName {
OnDownloadError = "onDownloadError",
}
-export type MessageHistory = {
- role: string;
- content: string;
-};
-/**
- * The `NewMessageRequest` type defines the shape of a new message request object.
- */
-export type NewMessageRequest = {
- id?: string;
- conversationId?: string;
- user?: string;
- avatar?: string;
- message?: string;
- createdAt?: string;
- updatedAt?: string;
- history?: MessageHistory[];
-};
-
-/**
- * The `NewMessageRequest` type defines the shape of a new message request object.
- */
-export type NewMessageResponse = {
- id?: string;
- conversationId?: string;
- user?: string;
- avatar?: string;
- message?: string;
- createdAt?: string;
- updatedAt?: string;
-};
-
/**
* Adds an observer for an event.
*
* @param eventName The name of the event to observe.
* @param handler The handler function to call when the event is observed.
*/
-const on: (eventName: string, handler: Function) => void = (eventName, handler) => {
+const on: (eventName: string, handler: Function) => void = (
+ eventName,
+ handler
+) => {
window.corePlugin?.events?.on(eventName, handler);
};
@@ -59,7 +31,10 @@ const on: (eventName: string, handler: Function) => void = (eventName, handler)
* @param eventName The name of the event to stop observing.
* @param handler The handler function to call when the event is observed.
*/
-const off: (eventName: string, handler: Function) => void = (eventName, handler) => {
+const off: (eventName: string, handler: Function) => void = (
+ eventName,
+ handler
+) => {
window.corePlugin?.events?.off(eventName, handler);
};
diff --git a/core/src/index.ts b/core/src/index.ts
index 5c741c863..76827f6b3 100644
--- a/core/src/index.ts
+++ b/core/src/index.ts
@@ -34,4 +34,4 @@ export { fs } from "./fs";
* Plugin base module export.
* @module
*/
-export { JanPlugin, PluginType } from "./plugin";
+export * from "./plugin";
diff --git a/core/src/plugins/conversational.ts b/core/src/plugins/conversational.ts
index a76c41a51..ebeb77333 100644
--- a/core/src/plugins/conversational.ts
+++ b/core/src/plugins/conversational.ts
@@ -1,5 +1,5 @@
+import { Thread } from "../index";
import { JanPlugin } from "../plugin";
-import { Conversation } from "../types/index";
/**
* Abstract class for conversational plugins.
@@ -17,10 +17,10 @@ export abstract class ConversationalPlugin extends JanPlugin {
/**
* Saves a conversation.
* @abstract
- * @param {Conversation} conversation - The conversation to save.
+ * @param {Thread} conversation - The conversation to save.
* @returns {Promise} A promise that resolves when the conversation is saved.
*/
- abstract saveConversation(conversation: Conversation): Promise;
+ abstract saveConversation(conversation: Thread): Promise;
/**
* Deletes a conversation.
diff --git a/core/src/plugins/inference.ts b/core/src/plugins/inference.ts
index 8da7f5059..663d0b258 100644
--- a/core/src/plugins/inference.ts
+++ b/core/src/plugins/inference.ts
@@ -1,4 +1,4 @@
-import { NewMessageRequest } from "../events";
+import { MessageRequest } from "../index";
import { JanPlugin } from "../plugin";
/**
@@ -21,5 +21,5 @@ export abstract class InferencePlugin extends JanPlugin {
* @param data - The data for the inference request.
* @returns The result of the inference request.
*/
- abstract inferenceRequest(data: NewMessageRequest): Promise;
+ abstract inferenceRequest(data: MessageRequest): Promise;
}
diff --git a/core/src/types/index.ts b/core/src/types/index.ts
index 296bc1a7e..dd227081a 100644
--- a/core/src/types/index.ts
+++ b/core/src/types/index.ts
@@ -1,140 +1,183 @@
-export interface Conversation {
- id: string;
- modelId?: string;
- botId?: string;
- name: string;
- message?: string;
- summary?: string;
- createdAt?: string;
- updatedAt?: string;
- messages: Message[];
- lastMessage?: string;
-}
-
-export interface Message {
- id: string;
- message?: string;
- user?: string;
- createdAt?: string;
- updatedAt?: string;
-}
-
-export interface RawMessage {
- id?: string;
- conversationId?: string;
- user?: string;
- avatar?: string;
- message?: string;
- createdAt?: string;
- updatedAt?: string;
-}
-
-export interface Model {
- /**
- * Combination of owner and model name.
- * Being used as file name. MUST be unique.
- */
- id: string;
- name: string;
- quantMethod: string;
- bits: number;
- size: number;
- maxRamRequired: number;
- usecase: string;
- downloadLink: string;
- modelFile?: string;
- /**
- * For tracking download info
- */
- startDownloadAt?: number;
- finishDownloadAt?: number;
- productId: string;
- productName: string;
- shortDescription: string;
- longDescription: string;
- avatarUrl: string;
- author: string;
- version: string;
- modelUrl: string;
- createdAt: number;
- updatedAt?: number;
- status: string;
- releaseDate: number;
- tags: string[];
-}
-export interface ModelCatalog {
- id: string;
- name: string;
- shortDescription: string;
- avatarUrl: string;
- longDescription: string;
- author: string;
- version: string;
- modelUrl: string;
- createdAt: number;
- updatedAt?: number;
- status: string;
- releaseDate: number;
- tags: string[];
- availableVersions: ModelVersion[];
-}
/**
- * Model type which will be stored in the database
+ * Message Request and Response
+ * ============================
+ * */
+
+/**
+ * The role of the author of this message.
+ * @data_transfer_object
*/
-export type ModelVersion = {
- /**
- * Combination of owner and model name.
- * Being used as file name. Should be unique.
- */
- id: string;
- name: string;
- quantMethod: string;
- bits: number;
- size: number;
- maxRamRequired: number;
- usecase: string;
- downloadLink: string;
- productId: string;
- /**
- * For tracking download state
- */
- startDownloadAt?: number;
- finishDownloadAt?: number;
-};
-
-export interface ChatMessage {
- id: string;
- conversationId: string;
- messageType: MessageType;
- messageSenderType: MessageSenderType;
- senderUid: string;
- senderName: string;
- senderAvatarUrl: string;
- text: string | undefined;
- imageUrls?: string[] | undefined;
- createdAt: number;
- status: MessageStatus;
-}
-
-export enum MessageType {
- Text = "Text",
- Image = "Image",
- ImageWithText = "ImageWithText",
- Error = "Error",
-}
-
-export enum MessageSenderType {
- Ai = "assistant",
+export enum ChatCompletionRole {
+ System = "system",
+ Assistant = "assistant",
User = "user",
}
+/**
+ * The `MessageRequest` type defines the shape of a new message request object.
+ * @data_transfer_object
+ */
+export type ChatCompletionMessage = {
+ /** The contents of the message. **/
+ content?: string;
+ /** The role of the author of this message. **/
+ role: ChatCompletionRole;
+};
+
+/**
+ * The `MessageRequest` type defines the shape of a new message request object.
+ * @data_transfer_object
+ */
+export type MessageRequest = {
+ id?: string;
+ /** The thread id of the message request. **/
+ threadId?: string;
+ /** Messages for constructing a chat completion request **/
+ messages?: ChatCompletionMessage[];
+};
+
+/**
+ * Thread and Message
+ * ========================
+ * */
+
+/**
+ * The status of the message.
+ * @data_transfer_object
+ */
export enum MessageStatus {
+ /** Message is fully loaded. **/
Ready = "ready",
+ /** Message is not fully loaded. **/
Pending = "pending",
}
-
-export type ConversationState = {
- hasMore: boolean;
- waitingForResponse: boolean;
- error?: Error;
+/**
+ * The `ThreadMessage` type defines the shape of a thread's message object.
+ * @stored
+ */
+export type ThreadMessage = {
+ /** Unique identifier for the message, generated by default using the ULID method. **/
+ id?: string;
+ /** Thread id, default is a ulid. **/
+ threadId?: string;
+ /** The role of the author of this message. **/
+ role?: ChatCompletionRole;
+ /** The content of this message. **/
+ content?: string;
+ /** The status of this message. **/
+ status: MessageStatus;
+ /** The timestamp indicating when this message was created, represented in ISO 8601 format. **/
+ createdAt?: string;
+};
+
+/**
+ * The `Thread` type defines the shape of a thread object.
+ * @stored
+ */
+export interface Thread {
+ /** Unique identifier for the thread, generated by default using the ULID method. **/
+ id: string;
+ /** The summary of this thread. **/
+ summary?: string;
+ /** The messages of this thread. **/
+ messages: ThreadMessage[];
+ /** The timestamp indicating when this thread was created, represented in ISO 8601 format. **/
+ createdAt?: string;
+ /** The timestamp indicating when this thread was updated, represented in ISO 8601 format. **/
+ updatedAt?: string;
+
+ /**
+ * @deprecated This field is deprecated and should not be used.
+ * Read from model file instead.
+ */
+ modelId?: string;
+}
+
+/**
+ * Model type defines the shape of a model object.
+ * @stored
+ */
+export interface Model {
+ /** Combination of owner and model name.*/
+ id: string;
+ /** The name of the model.*/
+ name: string;
+ /** Quantization method name.*/
+ quantizationName: string;
+ /** The the number of bits represents a number.*/
+ bits: number;
+ /** The size of the model file in bytes.*/
+ size: number;
+ /** The maximum RAM required to run the model in bytes.*/
+ maxRamRequired: number;
+ /** The use case of the model.*/
+ usecase: string;
+ /** The download link of the model.*/
+ downloadLink: string;
+ /** The short description of the model.*/
+ shortDescription: string;
+ /** The long description of the model.*/
+ longDescription: string;
+ /** The avatar url of the model.*/
+ avatarUrl: string;
+ /** The author name of the model.*/
+ author: string;
+ /** The version of the model.*/
+ version: string;
+ /** The origin url of the model repo.*/
+ modelUrl: string;
+ /** The timestamp indicating when this model was released.*/
+ releaseDate: number;
+ /** The tags attached to the model description */
+ tags: string[];
+}
+
+/**
+ * Model type of the presentation object which will be presented to the user
+ * @data_transfer_object
+ */
+export interface ModelCatalog {
+ /** The unique id of the model.*/
+ id: string;
+ /** The name of the model.*/
+ name: string;
+ /** The avatar url of the model.*/
+ avatarUrl: string;
+ /** The short description of the model.*/
+ shortDescription: string;
+ /** The long description of the model.*/
+ longDescription: string;
+ /** The author name of the model.*/
+ author: string;
+ /** The version of the model.*/
+ version: string;
+ /** The origin url of the model repo.*/
+ modelUrl: string;
+ /** The timestamp indicating when this model was released.*/
+ releaseDate: number;
+ /** The tags attached to the model description **/
+ tags: string[];
+
+ /** The available versions of this model to download. */
+ availableVersions: ModelVersion[];
+}
+/**
+ * Model type which will be present a version of ModelCatalog
+ * @data_transfer_object
+ */
+export type ModelVersion = {
+ /** The name of this model version.*/
+ name: string;
+ /** The quantization method name.*/
+ quantizationName: string;
+ /** The the number of bits represents a number.*/
+ bits: number;
+ /** The size of the model file in bytes.*/
+ size: number;
+ /** The maximum RAM required to run the model in bytes.*/
+ maxRamRequired: number;
+ /** The use case of the model.*/
+ usecase: string;
+ /** The download link of the model.*/
+ downloadLink: string;
};
diff --git a/docs/docs/docs/specs/assistants.md b/docs/docs/docs/specs/assistants.md
index 1d326fd2e..eb93265fe 100644
--- a/docs/docs/docs/specs/assistants.md
+++ b/docs/docs/docs/specs/assistants.md
@@ -2,79 +2,188 @@
title: "Assistants"
---
-Assistants can use models and tools.
+:::warning
-- Jan's `Assistants` are even more powerful than OpenAI due to customizable code in `index.js`
+Draft Specification: functionality has not been implemented yet.
-> OpenAI Equivalent: https://platform.openai.com/docs/api-reference/assistants
+Feedback: [HackMD: Assistants Spec](https://hackmd.io/KKAznzZvS668R6Vmyf8fCg)
-## Assistant Object
+:::
-- `assistant.json`
-- Equivalent to: https://platform.openai.com/docs/api-reference/assistants/object
+
+## User Stories
+
+_Users can chat with an assistant_
+
+- [Wireframes - show asst object properties]
+- See [Threads Spec](https://hackmd.io/BM_8o_OCQ-iLCYhunn2Aug)
+
+_Users can use Jan - the default assistant_
+
+- [Wireframes here - show model picker]
+- See [Default Jan Object](#Default-Jan-Example)
+
+_Users can create an assistant from scratch_
+
+- [Wireframes here - show create asst flow]
+- Users can select any model for an assistant. See Model Spec
+
+_Users can create an assistant from an existing assistant_
+
+- [Wireframes showing asst edit mode]
+
+## Jan Assistant Object
+
+- A `Jan Assistant Object` is a "representation of an assistant"
+- Objects are defined by `assistant-uuid.json` files in `json` format
+- Objects are designed to be compatible with `OpenAI Assistant Objects` with additional properties needed to run on our infrastructure.
+- ALL object properties are optional, i.e. users should be able to use an assistant declared by an empty `json` file.
+
+| Property | Type | Description | Validation |
+| ------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------- |
+| `object` | enum: `model`, `assistant`, `thread`, `message` | The Jan Object type | Defaults to `assistant` |
+| `name` | string | A vanity name. | Defaults to filename |
+| `description` | string | A vanity description. | Max `n` chars. Defaults to `""` |
+| `models` | array | A list of Model Objects that the assistant can use. | Defaults to ALL models |
+| `metadata` | map | This can be useful for storing additional information about the object in a structured format. | Defaults to `{}` |
+| `tools` | array | TBA. | TBA |
+| `files` | array | TBA. | TBA |
+
+### Generic Example
```json
-{
- // Jan specific properties
- "avatar": "https://lala.png"
- "thread_location": "ROOT/threads" // Default to root (optional field)
- // TODO: add moar
+// janroot/assistants/example/example.json
+"name": "Homework Helper",
- // OpenAI compatible properties: https://platform.openai.com/docs/api-reference/assistants
- "id": "asst_abc123",
- "object": "assistant",
- "created_at": 1698984975,
- "name": "Math Tutor",
- "description": null,
- "model": reference model.json,
- "instructions": reference model.json,
- "tools": [
- {
- "type": "rag"
- }
- ],
- "file_ids": [],
- "metadata": {}
-}
+// Option 1 (default): all models in janroot/models are available via Model Picker
+"models": [],
+
+// Option 2: creator can configure custom parameters on existing models in `janroot/models` &&
+// Option 3: creator can package a custom model with the assistant
+"models": [{ ...modelObject1 }, { ...modelObject2 }],
```
-## Assistants API
+### Default Jan Example
-- _TODO_: What would modifying Assistant do? (doesn't mutate `index.js`?)
+- Every user install has a default "Jan Assistant" declared below.
+ > Q: can we omit most properties in `jan.json`? It's all defaults anyway.
+
+```json
+// janroot/assistants/jan/jan.json
+"description": "Use Jan to chat with all models",
+```
+
+## Filesystem
+
+- Everything needed to represent & run an assistant is packaged into an `Assistant folder`.
+- The folder is standalone and can be easily zipped, imported, and exported, e.g. to Github.
+- The folder always contains an `Assistant Object`, declared in an `assistant-uuid.json`.
+ - The folder and file must share the same name: `assistant-uuid`
+- In the future, the folder will contain all of the resources an assistant needs to run, e.g. custom model binaries, pdf files, custom code, etc.
```sh
-GET https://api.openai.com/v1/assistants # List
-POST https://api.openai.com/v1/assistants # C
-GET https://api.openai.com/v1/assistants/{assistant_id} # R
-POST https://api.openai.com/v1/assistants/{assistant_id} # U
-DELETE https://api.openai.com/v1/assistants/{assistant_id} # D
+janroot/
+ assistants/
+ jan/ # Assistant Folder
+ jan.json # Assistant Object
+ homework-helper/ # Assistant Folder
+ homework-helper.json # Assistant Object
```
-## Assistants Filesystem
+### Custom Code
+
+> Not in scope yet. Sharing as a preview only.
+
+- Assistants can call custom code in the future
+- Custom code extends beyond `function calling` to any features that can be implemented in `/src`
```sh
-/assistants
- /jan
- assistant.json # Assistant configs (see below)
-
- # For any custom code
- package.json # Import npm modules
- # e.g. Langchain, Llamaindex
- /src # Supporting files (needs better name)
- index.js # Entrypoint
- process.js # For electron IPC processes (needs better name)
-
- # `/threads` at root level
- # `/models` at root level
- /shakespeare
- assistant.json
- model.json # Creator chooses model and settings
- package.json
- /src
- index.js
- process.js
-
- /threads # Assistants remember conversations in the future
- /models # Users can upload custom models
- /finetuned-model
+example/ # Assistant Folder
+ example.json # Assistant Object
+ package.json
+ src/
+ index.ts
+ helpers.ts
```
+
+### Knowledge Files
+
+> Not in scope yet. Sharing as a preview only
+
+- Assistants can do `retrieval` in future
+
+```sh
+
+example/ # Assistant Folder
+ example.json # Assistant Object
+ files/
+```
+
+## Jan API
+
+### Assistant API Object
+
+#### `GET /v1/assistants/{assistant_id}`
+
+- The `Jan Assistant Object` maps into the `OpenAI Assistant Object`.
+- Properties marked with `*` are compatible with the [OpenAI `assistant` object](https://platform.openai.com/docs/api-reference/assistants)
+- Note: The `Jan Assistant Object` has additional properties when retrieved via its API endpoint.
+- https://platform.openai.com/docs/api-reference/assistants/getAssistant
+
+| Property | Type | Public Description | Jan Assistant Object (`a`) Property |
+| ---------------- | -------------- | ------------------------------------------------------------------------- | ----------------------------------- |
+| `id`\* | string | Assistant uuid, also the name of the Jan Assistant Object file: `id.json` | `json` filename |
+| `object`\* | string | Always "assistant" | `a.object` |
+| `created_at`\* | integer | Timestamp when assistant was created. | `a.json` creation time |
+| `name`\* | string or null | A display name | `a.name` or `id` |
+| `description`\* | string or null | A description | `a.description` |
+| `model`\* | string | Text | `a.models[0].name` |
+| `instructions`\* | string or null | Text | `a.models[0].parameters.prompt` |
+| `tools`\* | array | TBA | `a.tools` |
+| `file_ids`\* | array | TBA | `a.files` |
+| `metadata`\* | map | TBA | `a.metadata` |
+| `models` | array | TBA | `a.models` |
+
+### Create Assistant
+
+#### `POST /v1/assistants`
+
+- https://platform.openai.com/docs/api-reference/assistants/createAssistant
+
+### Retrieve Assistant
+
+#### `GET v1/assistants/{assistant_id}`
+
+- https://platform.openai.com/docs/api-reference/assistants/getAssistant
+
+### Modify Assistant
+
+#### `POST v1/assistants/{assistant_id}`
+
+- https://platform.openai.com/docs/api-reference/assistants/modifyAssistant
+
+### Delete Assistant
+
+#### `DELETE v1/assistants/{assistant_id}`
+
+- https://platform.openai.com/docs/api-reference/assistants/deleteAssistant
+
+### List Assistants
+
+#### `GET v1/assistants`
+
+- https://platform.openai.com/docs/api-reference/assistants/listAssistants
+
+### CRUD Assistant.Models
+
+- This is a Jan-only endpoint, since Jan supports the ModelPicker, i.e. an `assistant` can be created to run with many `models`.
+
+#### `POST /v1/assistants/{assistant_id}/models`
+
+#### `GET /v1/assistants/{assistant_id}/models`
+
+#### `GET /v1/assistants/{assistant_id}/models/{model_id}`
+
+#### `DELETE /v1/assistants/{assistant_id}/models`
+
+Note: There's no need to implement `Modify Assistant.Models`
diff --git a/docs/docs/docs/specs/chats.md b/docs/docs/docs/specs/chats.md
index 58047c4c8..fedd6a9c8 100644
--- a/docs/docs/docs/specs/chats.md
+++ b/docs/docs/docs/specs/chats.md
@@ -2,6 +2,12 @@
title: "Chats"
---
+:::warning
+
+Draft Specification: functionality has not been implemented yet.
+
+:::
+
Chats are essentially inference requests to a model
> OpenAI Equivalent: https://platform.openai.com/docs/api-reference/chat
diff --git a/docs/docs/docs/specs/files.md b/docs/docs/docs/specs/files.md
index 70c3e345f..4d62e33d5 100644
--- a/docs/docs/docs/specs/files.md
+++ b/docs/docs/docs/specs/files.md
@@ -2,6 +2,12 @@
title: "Files"
---
+:::warning
+
+Draft Specification: functionality has not been implemented yet.
+
+:::
+
Files can be used by `threads`, `assistants` and `fine-tuning`
> Equivalent to: https://platform.openai.com/docs/api-reference/files
diff --git a/docs/docs/docs/specs/messages.md b/docs/docs/docs/specs/messages.md
index 8bc79d1ae..ed5c5d95b 100644
--- a/docs/docs/docs/specs/messages.md
+++ b/docs/docs/docs/specs/messages.md
@@ -2,6 +2,14 @@
title: "Messages"
---
+:::warning
+
+Draft Specification: functionality has not been implemented yet.
+
+Feedback: [HackMD: Threads Spec](https://hackmd.io/BM_8o_OCQ-iLCYhunn2Aug)
+
+:::
+
Messages are within `threads` and capture additional metadata.
- Equivalent to: https://platform.openai.com/docs/api-reference/messages
diff --git a/docs/docs/docs/specs/models.md b/docs/docs/docs/specs/models.md
index 0ad4b158d..7dea62115 100644
--- a/docs/docs/docs/specs/models.md
+++ b/docs/docs/docs/specs/models.md
@@ -1,4 +1,16 @@
-# Model Specs
+---
+title: "Models"
+---
+
+:::warning
+
+Draft Specification: functionality has not been implemented yet.
+
+Feedback: [HackMD: Models Spec](https://hackmd.io/ulO3uB1AQCqLa5SAAMFOQw)
+
+:::
+
+Models are AI models like Llama and Mistral
> OpenAI Equivalent: https://platform.openai.com/docs/api-reference/models
@@ -18,7 +30,7 @@ _Users can configure model settings, like run parameters_
_Users can override run settings at runtime_
-- See [assistant]() and [thread]()
+- See Assistant Spec and Thread
## Jan Model Object
diff --git a/docs/docs/docs/specs/threads.md b/docs/docs/docs/specs/threads.md
index f36f923ed..e7d0fe978 100644
--- a/docs/docs/docs/specs/threads.md
+++ b/docs/docs/docs/specs/threads.md
@@ -2,52 +2,133 @@
title: "Threads"
---
-Threads contain `messages` history with assistants. Messages in a thread share context.
+:::warning
-- Note: For now, threads "lock the model" after a `message` is sent
- - When a new `thread` is created with Jan, users can choose the models
- - Users can still edit model parameters/system prompts
- - Note: future Assistants may customize this behavior
-- Note: Assistants will be able to specify default thread location in the future
- - Jan uses root-level threads, to allow for future multi-assistant threads
- - Assistant Y may store threads in its own folder, to allow for [long-term assistant memory](https://github.com/janhq/jan/issues/344)
+Draft Specification: functionality has not been implemented yet.
-> OpenAI Equivalent: https://platform.openai.com/docs/api-reference/threads
+Feedback: [HackMD: Threads Spec](https://hackmd.io/BM_8o_OCQ-iLCYhunn2Aug)
-## Thread Object
+:::
-- `thread.json`
-- Equivalent to: https://platform.openai.com/docs/api-reference/threads/object
+## User Stories
+
+_Users can chat with an assistant in a thread_
+
+- See [Messages Spec]
+
+_Users can change model in a new thread_
+
+- Wireframes here
+
+_Users can change model parameters in a thread_
+
+- Wireframes of
+
+_Users can delete all thread history_
+
+- Wireframes of settings page.
+
+## Jan Thread Object
+
+- A `Jan Thread Object` is a "representation of a conversation thread" between an `assistant` and the user
+- Objects are defined by `thread-uuid.json` files in `json` format
+- Objects are designed to be compatible with `OpenAI Thread Objects` with additional properties needed to run on our infrastructure.
+- Objects contain a `models` field, to track when the user overrides the assistant's default model parameters.
+
+| Property | Type | Description | Validation |
+| ---------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
+| `object` | enum: `model`, `assistant`, `thread`, `message` | The Jan Object type | Defaults to `thread` |
+| `models` | array | An array of Jan Model Objects. Threads can "override" an assistant's model run parameters. Thread-level model parameters are directly saved in the `thread.models` property! (see Models spec) | Defaults to `assistant.models` |
+| `messages` | array | An array of Jan Message Objects. (see Messages spec) | Defaults to `[]` |
+| `metadata` | map | Useful for storing additional information about the object in a structured format. | Defaults to `{}` |
+
+### Generic Example
```json
-{
- // Jan specific properties:
- "summary": "HCMC restaurant recommendations",
- "messages": {see below}
-
- // OpenAI compatible properties: https://platform.openai.com/docs/api-reference/threads)
- "id": "thread_abc123",
- "object": "thread",
- "created_at": 1698107661,
- "metadata": {}
-}
+// janroot/threads/jan_1700123404.json
+"messages": [
+ {...message0}, {...message1}
+],
+"metadata": {
+ "summary": "funny physics joke",
+},
```
-## Threads API
+## Filesystem
-- Equivalent to: https://platform.openai.com/docs/api-reference/threads
-
-```sh=
-POST https://localhost:1337/v1/threads/{thread_id} # Create thread
-GET https://localhost:1337/v1/threads/{thread_id} # Get thread
-DELETE https://localhost:1337/v1/models/{thread_id} # Delete thread
-```
-
-## Threads Filesystem
+- `Jan Thread Objects`' `json` files always has the naming schema: `assistant_uuid` + `unix_time_thread_created_at. See below.
+- Threads are all saved in the `janroot/threads` folder in a flat folder structure.
+- The folder is standalone and can be easily zipped, exported, and cleared.
```sh
-/assistants
- /homework-helper
- /threads # context is "permanently remembered" by assistant in future conversations
-/threads # context is only retained within a single thread
+janroot/
+ threads/
+ jan_1700123404.json
+ homework_helper_700120003.json
```
+
+## Jan API
+
+### Thread API Object
+
+#### `GET /v1/threads/{thread_id}`
+
+- The `Jan Thread Object` maps into the `OpenAI Thread Object`.
+- Properties marked with `*` are compatible with the [OpenAI `thread` object](https://platform.openai.com/docs/api-reference/threads)
+- Note: The `Jan Thread Object` has additional properties when retrieved via its API endpoint.
+- https://platform.openai.com/docs/api-reference/threads/getThread
+
+| Property | Type | Public Description | Jan Thread Object (`t`) Property |
+| -------------- | ------- | ------------------------------------------------------------------- | -------------------------------- |
+| `id`\* | string | Thread uuid, also the name of the Jan Thread Object file: `id.json` | `json` filename |
+| `object`\* | string | Always "thread" | `t.object` |
+| `created_at`\* | integer | | `json` file creation time |
+| `metadata`\* | map | | `t.metadata` |
+| `models` | array | | `t.models` |
+| `messages` | array | | `t.messages` |
+
+### Create Thread
+
+#### `POST /v1/threads`
+
+- https://platform.openai.com/docs/api-reference/threads/createThread
+
+### Retrieve Thread
+
+#### `GET v1/threads/{thread_id}`
+
+- https://platform.openai.com/docs/api-reference/threads/getThread
+
+### Modify Thread
+
+#### `POST v1/threads/{thread_id}`
+
+- https://platform.openai.com/docs/api-reference/threads/modifyThread
+
+### Delete Thread
+
+#### `DELETE v1/threads/{thread_id}`
+
+- https://platform.openai.com/docs/api-reference/threads/deleteThread
+
+### List Threads
+
+> This is a Jan-only endpoint, not supported by OAI yet.
+
+#### `GET v1/threads`
+
+### Get & Modify `Thread.Models`
+
+> This is a Jan-only endpoint, not supported by OAI yet.
+
+#### `GET v1/threads/{thread_id}/models`
+
+#### `POST v1/threads/{thread_id}/models/{model_id}`
+
+- Since users can change model parameters in an existing thread
+
+### List `Thread.Messages`
+
+> This is a Jan-only endpoint, not supported by OAI yet.
+
+#### `GET v1/threads/{thread_id}/messages`
diff --git a/docs/src/components/Elements/downloadLink.js b/docs/src/components/Elements/downloadLink.js
new file mode 100644
index 000000000..744961380
--- /dev/null
+++ b/docs/src/components/Elements/downloadLink.js
@@ -0,0 +1,127 @@
+import React, { useState, useEffect } from "react";
+import axios from "axios";
+
+const systemsTemplate = [
+ {
+ name: "Download for Mac (M1/M2)",
+ logo: require("@site/static/img/apple-logo-white.png").default,
+ fileFormat: "{appname}-mac-arm64-{tag}.dmg",
+ },
+ {
+ name: "Download for Mac (Intel)",
+ logo: require("@site/static/img/apple-logo-white.png").default,
+ fileFormat: "{appname}-mac-x64-{tag}.dmg",
+ },
+ {
+ name: "Download for Windows",
+ logo: require("@site/static/img/windows-logo-white.png").default,
+ fileFormat: "{appname}-win-x64-{tag}.exe",
+ },
+ {
+ name: "Download for Linux",
+ logo: require("@site/static/img/linux-logo-white.png").default,
+ fileFormat: "{appname}-linux-amd64-{tag}.deb",
+ },
+];
+
+function classNames(...classes) {
+ return classes.filter(Boolean).join(" ");
+}
+
+export default function DownloadLink() {
+ const [systems, setSystems] = useState(systemsTemplate);
+ const [defaultSystem, setDefaultSystem] = useState(systems[0]);
+
+ const getLatestReleaseInfo = async (repoOwner, repoName) => {
+ const url = `https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`;
+ try {
+ const response = await axios.get(url);
+ return response.data;
+ } catch (error) {
+ console.error(error);
+ return null;
+ }
+ };
+
+ const extractAppName = (fileName) => {
+ // Extract appname using a regex that matches the provided file formats
+ const regex = /^(.*?)-(?:mac|win|linux)-(?:arm64|x64|amd64)-.*$/;
+ const match = fileName.match(regex);
+ return match ? match[1] : null;
+ };
+
+ const changeDefaultSystem = async (systems) => {
+ const userAgent = navigator.userAgent;
+
+ const arc = await navigator?.userAgentData?.getHighEntropyValues([
+ "architecture",
+ ]);
+
+ if (userAgent.includes("Windows")) {
+ // windows user
+ setDefaultSystem(systems[2]);
+ } else if (userAgent.includes("Linux")) {
+ // linux user
+ setDefaultSystem(systems[3]);
+ } else if (
+ userAgent.includes("Mac OS") &&
+ arc &&
+ arc.architecture === "arm"
+ ) {
+ setDefaultSystem(systems[0]);
+ } else {
+ setDefaultSystem(systems[1]);
+ }
+ };
+
+ useEffect(() => {
+ const updateDownloadLinks = async () => {
+ try {
+ const releaseInfo = await getLatestReleaseInfo("janhq", "jan");
+
+ // Extract appname from the first asset name
+ const firstAssetName = releaseInfo.assets[0].name;
+ const appname = extractAppName(firstAssetName);
+
+ if (!appname) {
+ console.error(
+ "Failed to extract appname from file name:",
+ firstAssetName
+ );
+ changeDefaultSystem(systems);
+ return;
+ }
+
+ // Remove 'v' at the start of the tag_name
+ const tag = releaseInfo.tag_name.startsWith("v")
+ ? releaseInfo.tag_name.substring(1)
+ : releaseInfo.tag_name;
+
+ const updatedSystems = systems.map((system) => {
+ const downloadUrl = system.fileFormat
+ .replace("{appname}", appname)
+ .replace("{tag}", tag);
+ return {
+ ...system,
+ href: `https://github.com/janhq/jan/releases/download/${releaseInfo.tag_name}/${downloadUrl}`,
+ };
+ });
+
+ setSystems(updatedSystems);
+ changeDefaultSystem(updatedSystems);
+ } catch (error) {
+ console.error("Failed to update download links:", error);
+ }
+ };
+
+ updateDownloadLinks();
+ }, []);
+
+ return (
+
+ );
+}
diff --git a/docs/src/components/Elements/dropdown.js b/docs/src/components/Elements/dropdown.js
index 31c2bf05a..5709a89ac 100644
--- a/docs/src/components/Elements/dropdown.js
+++ b/docs/src/components/Elements/dropdown.js
@@ -76,6 +76,7 @@ export default function Dropdown() {
setDefaultSystem(systems[1]);
}
};
+
useEffect(() => {
const updateDownloadLinks = async () => {
try {
diff --git a/docs/src/pages/index.js b/docs/src/pages/index.js
index bfefa0343..3bb1d0cf7 100644
--- a/docs/src/pages/index.js
+++ b/docs/src/pages/index.js
@@ -5,41 +5,12 @@ import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import useBaseUrl from "@docusaurus/useBaseUrl";
import Layout from "@theme/Layout";
import AnnoncementBanner from "@site/src/components/Announcement";
-import {
- CloudArrowUpIcon,
- CursorArrowRaysIcon,
- ShieldCheckIcon,
- CpuChipIcon,
- ClipboardDocumentIcon,
- CubeTransparentIcon,
- ComputerDesktopIcon,
- FolderPlusIcon,
-} from "@heroicons/react/24/outline";
+
+import { AiOutlineGithub } from "react-icons/ai";
import ThemedImage from "@theme/ThemedImage";
-const features = [
- {
- name: "Personal AI that runs on your computer",
- desc: "Jan runs directly on your local machine, offering privacy, convenience and customizability.",
- icon: ComputerDesktopIcon,
- },
- {
- name: "Private and offline, your data never leaves your machine",
- desc: "Your conversations and data are with an AI that runs on your computer, where only you have access.",
- icon: ShieldCheckIcon,
- },
- {
- name: "No subscription fees, the AI runs on your computer",
- desc: "Say goodbye to monthly subscriptions or usage-based APIs. Jan runs 100% free on your own hardware.",
- icon: CubeTransparentIcon,
- },
- {
- name: "Extendable via App and Plugin framework",
- desc: "Jan has a versatile app and plugin framework, allowing you to customize it to your needs.",
- icon: FolderPlusIcon,
- },
-];
+import DownloadLink from "@site/src/components/Elements/downloadLink";
export default function Home() {
const { siteConfig } = useDocusaurusContext();
@@ -48,8 +19,7 @@ export default function Home() {
@@ -75,7 +45,6 @@ export default function Home() {
*/}
-
Own your AI
@@ -111,7 +80,6 @@ export default function Home() {
- {/* */}
-
-
+
AI that you control
- Jan runs Large Language Models locally on Windows, Mac and Linux.
- Available on Desktop and Cloud-Native.
+ Private. Local. Infinitely Customizable.
-
- {features.map((feat, i) => {
- return (
-
-
+
+
+
+
+
Personal AI that runs on your computer
+
+ Jan runs directly on your local machine, offering privacy,
+ convenience and customizability.
+
+
+
+
+
+
+
Extendable via App and Plugin framework
+
+ Jan has a versatile app and plugin framework, allowing you
+ to customize it to your needs.
+
+
+
+
+
+
+
+ Private and offline, your data never leaves your machine
+
+
+ Your conversations and data are with an AI that runs on your
+ computer, where only you have access.
+
+
+
+
+
+
+
No subscription fees, the AI runs on your computer
+
+ Say goodbye to monthly subscriptions or usage-based APIs.
+ Jan runs 100% free on your own hardware.
+
+
+
+
+
+
+
+
+
+
+
+ Your AI, forever.
+
+
+ Apps come and go, but your AI and data should last.{" "}
+
+
+
-
{feat.name}
-
{feat.desc}
+
+
+ Jan uses open, standard and non-proprietary files stored
+ locally on your device.
+
+
+
+
+
+ You have total control over your AI, which means you can
+ use Jan offline and switch to another app easily if you
+ want.
+