Skip to content

Commit

Permalink
Merge pull request #8 from cddigi/main
Browse files Browse the repository at this point in the history
Feature: Add support for embeddings
  • Loading branch information
abrenneke committed Apr 22, 2024
2 parents 99dc489 + 97dda01 commit df6e466
Show file tree
Hide file tree
Showing 3 changed files with 235 additions and 10 deletions.
46 changes: 36 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
The Rivet Ollama Plugin is a plugin for [Rivet](https://rivet.ironcladapp.com) to allow you to use [Ollama](https://ollama.ai/) to run and chat with LLMs locally and easily. It adds the following nodes:

- Ollama Chat
- Ollama Embedding
- Get Ollama Model
- List Ollama Models
- Pull Model to Ollama
Expand All @@ -30,6 +31,10 @@ The Rivet Ollama Plugin is a plugin for [Rivet](https://rivet.ironcladapp.com) t
- [Inputs](#inputs)
- [Outputs](#outputs)
- [Editor Settings](#editor-settings)
- [Ollama Embedding](#ollama-embedding)
- [Inputs](#inputs)
- [Outputs](#outputs)
- [Editor Settings](#editor-settings)
- [Ollama Generate](#ollama-generate)
- [Inputs](#inputs)
- [Outputs](#outputs)
Expand Down Expand Up @@ -113,30 +118,31 @@ await createProcessor(project, {
## Nodes

### Ollama Chat

The main node of the plugin. Functions similarly to the [Chat Node](https://rivet.ironcladapp.com/docs/node-reference/chat) built in to Rivet. Uses /api/chat route

#### Inputs

| Title | Data Type | Description | Default Value | Notes |
| ------------- | ---------------- | --------------------------------------------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| System Prompt | `string` | The system prompt to prepend to the messages list. | (none) | Optional. |
| Title | Data Type | Description | Default Value | Notes |
| ------------- | ---------------- | --------------------------------------------------- | ------------- | ---------------------------------------------------------------------------------------- |
| System Prompt | `string` | The system prompt to prepend to the messages list. | (none) | Optional. |
| Messages | 'chat-message[]' | The chat messages to use as the prompt for the LLM. | (none) | Chat messages are converted to the OpenAI message format using "role" and "content" keys |

#### Outputs

| Title | Data Type | Description | Notes |
| -------------------- | ---------------- | ---------------------------------------------------------- | ----------------------------------------------------------- |
| Output | `string` | The response text from the LLM. | |
| Messages Sent | `chat-message[]` | The messages that were sent to Ollama. | |
| All Messages | `chat-message[]` | All messages, including the reply from the LLM. | |
| Title | Data Type | Description | Notes |
| ------------- | ---------------- | ----------------------------------------------- | ----- |
| Output | `string` | The response text from the LLM. | |
| Messages Sent | `chat-message[]` | The messages that were sent to Ollama. | |
| All Messages | `chat-message[]` | All messages, including the reply from the LLM. | |

#### Editor Settings

| Setting | Description | Default Value | Use Input Toggle | Input Data Type |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | ---------------- | --------------- |
| Model | The name of the LLM model in to use in Ollama. | (Empty) | Yes | `string` |
| Prompt Format | The way to format chat messages for the prompt being sent to the ollama model. Raw means no formatting is applied. Llama 2 Instruct follows the [Llama 2 prompt format](https://gpus.llm-utils.org/llama-2-prompt-template/). | Llama 2 Instruct | No | N/A |
| JSON Mode | Activates JSON output mode | false | Yes | `boolean` |
| JSON Mode | Activates JSON output mode | false | Yes | `boolean` |
| Parameters Group | | | | |
| Mirostat | Enable Mirostat sampling for controlling perplexity. (default: 0, 0 = disabled, 1 = Mirostat, 2 = Mirostat 2.0) | (unset) | Yes | `number` |
| Mirostat Eta | Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1) | (unset) | Yes | `number` |
Expand All @@ -156,6 +162,26 @@ The main node of the plugin. Functions similarly to the [Chat Node](https://rive
| Top P | Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9) | (unset) | Yes | `number` |
| Additional Parameters | Additional parameters to pass to Ollama. Numbers will be parsed and sent as numbers, otherwise they will be sent as strings. [See all supported parameters in Ollama](https://github.com/jmorganca/ollama/blob/main/docs/modelfile.md) | (none) | Yes | `object` |

### Ollama Embedding

Embedding models are models that are trained specifically to generate vector embeddings: long arrays of numbers that represent semantic meaning for a given sequence of text. The resulting vector embedding arrays can then be stored in a database, which will compare them as a way to search for data that is similar in meaning.

#### Inputs

See Editor Settings for all possible inputs.

#### Outputs

| Title | Data Type | Description | Notes |
| --------- | --------- | ------------------------------------------------------------------------------ | ----- |
| Embedding | `vector` | Array of numbers that represent semantic meaning for a given sequence of text. | |

#### Editor Settings

| Setting | Description | Default Value | Use Input Toggle | Input Data Type |
| ---------- | ----------------------------- | ------------- | ----------------- | --------------- |
| Model Name | The name of the model to get. | (Empty) | Yes (default off) | `string` |
| Text | The text to embed. | (Empty) | Yes (default off) | `string` |

### Ollama Generate

Expand Down Expand Up @@ -195,7 +221,7 @@ Additional inputs available with toggles in the editor.
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | ---------------- | --------------- |
| Model | The name of the LLM model in to use in Ollama. | (Empty) | Yes | `string` |
| Prompt Format | The way to format chat messages for the prompt being sent to the ollama model. Raw means no formatting is applied. Llama 2 Instruct follows the [Llama 2 prompt format](https://gpus.llm-utils.org/llama-2-prompt-template/). | Llama 2 Instruct | No | N/A |
| JSON Mode | Activates JSON output mode | false | Yes | `boolean` |
| JSON Mode | Activates JSON output mode | false | Yes | `boolean` |
| Advanced Outputs | Add additional outputs with detailed information about the Ollama execution. | No | No | N/A |
| Parameters Group | | | | |
| Mirostat | Enable Mirostat sampling for controlling perplexity. (default: 0, 0 = disabled, 1 = Mirostat, 2 = Mirostat 2.0) | (unset) | Yes | `number` |
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { RivetPlugin, RivetPluginInitializer } from "@ironclad/rivet-core";
import { ollamaChat } from "./nodes/OllamaGenerateNode";
import { ollamaChat2 } from "./nodes/OllamaChatNode";
import { ollamaEmbed } from "./nodes/OllamaEmbeddingNode";
import { getOllamaModel } from "./nodes/GetOllamaModelNode";
import { listOllamaModels } from "./nodes/ListOllamaModelsNode";
import { pullModelToOllama } from "./nodes/PullModelToOllamaNode";
Expand Down Expand Up @@ -32,6 +33,7 @@ const plugin: RivetPluginInitializer = (rivet) => {
register: (register) => {
register(ollamaChat(rivet));
register(ollamaChat2(rivet));
register(ollamaEmbed(rivet));
register(getOllamaModel(rivet));
register(listOllamaModels(rivet));
register(pullModelToOllama(rivet));
Expand Down
197 changes: 197 additions & 0 deletions src/nodes/OllamaEmbeddingNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import type {
ChartNode,
ChatMessage,
ChatMessageMessagePart,
EditorDefinition,
NodeId,
NodeInputDefinition,
NodeOutputDefinition,
NodeUIData,
Outputs,
PluginNodeImpl,
PortId,
Rivet,
} from "@ironclad/rivet-core";
import { match } from "ts-pattern";

export type OllamaEmbeddingNodeData = {
model: string;
useModelInput?: boolean;
embedding: number[];
text: string;
useTextInput?: boolean;
};

export type OllamaEmbeddingNode = ChartNode<
"ollamaEmbed",
OllamaEmbeddingNodeData
>;

type OllamaEmbeddingResponse = {
embedding: number[];
};

export const ollamaEmbed = (rivet: typeof Rivet) => {
const impl: PluginNodeImpl<OllamaEmbeddingNode> = {
create(): OllamaEmbeddingNode {
const node: OllamaEmbeddingNode = {
id: rivet.newId<NodeId>(),
data: {
model: "",
useModelInput: false,
embedding: [],
text: "",
useTextInput: false,
},
title: "Ollama Embedding",
type: "ollamaEmbed",
visualData: {
x: 0,
y: 0,
width: 250,
},
};
return node;
},

getInputDefinitions(data): NodeInputDefinition[] {
const inputs: NodeInputDefinition[] = [];

if (data.useModelInput) {
inputs.push({
id: "model" as PortId,
dataType: "string",
title: "Model",
});
}

if (data.useTextInput) {
inputs.push({
id: "text" as PortId,
dataType: "string",
title: "Text",
});
}

return inputs;
},

getOutputDefinitions(data): NodeOutputDefinition[] {
let outputs: NodeOutputDefinition[] = [
{
id: "embedding" as PortId,
dataType: "vector",
title: "Embedding",
description: "The embedding output from Ollama.",
},
];

return outputs;
},

getEditors(): EditorDefinition<OllamaEmbeddingNode>[] {
return [
{
type: "string",
dataKey: "model",
useInputToggleDataKey: "useModelInput",
label: "Model",
},
{
type: "string",
dataKey: "text",
useInputToggleDataKey: "useTextInput",
label: "Text",
},
];
},

getBody(data) {
return rivet.dedent`
Model: ${data.useModelInput ? "(From Input)" : data.model || "Unset!"}
Text: ${data.useTextInput ? "(From Input)" : data.text || "Unset!"}
`;
},

getUIData(): NodeUIData {
return {
contextMenuTitle: "Ollama Embedding",
group: "Ollama",
infoBoxBody: "This is an Ollama Embedding node using /api/embeddings.",
infoBoxTitle: "Ollama Embedding Node",
};
},

async process(data, inputData, context) {
let outputs: Outputs = {};

const host = context.getPluginConfig("host") || "http://localhost:11434";

if (!host.trim()) {
throw new Error("No host set!");
}

const model = rivet.getInputOrData(data, inputData, "model", "string");
if (!model) {
throw new Error("No model set!");
}

const prompt = rivet.getInputOrData(data, inputData, "text", "string");
let apiResponse: Response;

type RequestBodyType = {
model: string;
prompt: string;
};

const requestBody: RequestBodyType = {
model,
prompt,
};

try {
apiResponse = await fetch(`${host}/api/embeddings`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(requestBody),
});
} catch (err) {
throw new Error(
`Error from Ollama {POST}: ${rivet.getError(err).message}`,
);
}

if (!apiResponse.ok) {
try {
const error = await apiResponse.json();
throw new Error(`Error from Ollama {JSON}: ${error.message}`);
} catch (err) {
throw new Error(`Error from Ollama {RAW}: ${apiResponse.statusText}`);
}
}

const reader = apiResponse.body?.getReader();

if (!reader) {
throw new Error("No response body!");
}

let streamingResponseText = "";
let llmResponseText = "";
const { value, done } = await reader.read();
const line = new TextDecoder().decode(value);
const response = JSON.parse(line) as OllamaEmbeddingResponse;

outputs["embedding" as PortId] = {
type: "vector",
value: response.embedding,
};

return outputs;
},
};

return rivet.pluginNodeDefinition(impl, "Ollama Embedding");
};

0 comments on commit df6e466

Please sign in to comment.