Compare commits

...
Author SHA1 Message Date
JT2M0L3Y 99db6d8163 Update: fix imports based on last pkg fix 2026-09-23 10:57:14 -07:00
JT2M0L3Y 3a770be5de Added: defined objects directory 2026-09-23 10:24:07 -07:00
JT2M0L3Y f8d8fe765f Update: utility method logs use method name 2026-09-23 10:24:05 -07:00
Kevin Dang 2c25f7f500 Upgrade Node to Krypton (#200)
* node 24 and vulnerabilities

* ready is clientReady

* better about section
2026-09-23 09:52:28 -07:00
Eric Curtin 0beaed85aa Document llmman and rename OLLAMA_IP/PORT to LLM_ENDPOINT/PORT (#198)
llmman (https://github.com/llmmanorg/llmman) serves the Ollama API on
port 17434 and works with the bot as-is, so document it in setup-local
and link it from the README. Since the backend is no longer Ollama-only,
rename the env vars to LLM_ENDPOINT / LLM_PORT as requested in review.
OLLAMA_IP / OLLAMA_PORT are kept as deprecated aliases in keys.ts and
docker-compose.yml so existing deployments keep working.
2026-09-10 17:42:42 -07:00
Kevin Dang 69637722db Separate Test File for Fork PRs (#199) 2026-09-05 07:31:59 -07:00
Jonathan Smoley 3d6d651e18 drop refs to coverage (#197) 2026-07-17 11:30:46 -07:00
Jonathan Smoley 7583d6d179 Fix: Code Coverage Badge Failed (#196)
* switch coverage to codecov

* reset node, increment ver

* remove auto-pass job var

* remove coverage 'env' note

* set badge with new action

* add fqvn for action

* fix step name

* make badge commit explicit
2026-07-16 17:44:11 -07:00
33 changed files with 1464 additions and 1991 deletions
+6 -4
View File
@@ -4,11 +4,13 @@ CLIENT_TOKEN = BOT_TOKEN
# Default model for new users
MODEL = DEFAULT_MODEL
# ip/port address of docker container, I use 172.18.0.3 for docker, 127.0.0.1 for local
OLLAMA_IP = IP_ADDRESS
OLLAMA_PORT = PORT
# ip/port address of the LLM server (ollama, llmman, ...), I use 172.18.0.3 for docker, 127.0.0.1 for local
# ollama listens on 11434, llmman (https://github.com/llmmanorg/llmman) on 17434
# OLLAMA_IP / OLLAMA_PORT are still accepted as deprecated aliases
LLM_ENDPOINT = IP_ADDRESS
LLM_PORT = PORT
# ip address for discord bot container, I use 172.18.0.2, use different IP than ollama_ip
# ip address for discord bot container, I use 172.18.0.2, use different IP than llm_endpoint
DISCORD_IP = IP_ADDRESS
# subnet address, ex. 172.18.0.0 as we use /16.
+13 -11
View File
@@ -1,5 +1,6 @@
name: Builds
run-name: Validate Node and Docker Builds
on:
push:
branches:
@@ -9,15 +10,16 @@ jobs:
Discord-Node-Build: # test if the node install and run
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Node Environment lts/jod
- name: Set up Node Environment
uses: actions/setup-node@v4
with:
node-version: lts/jod
cache: "npm"
node-version: lts/krypton
cache: npm
- name: Install Project Dependencies
run: |
@@ -31,8 +33,8 @@ jobs:
run: |
touch .env
echo CLIENT_TOKEN = ${{ secrets.BOT_TOKEN }} >> .env
echo OLLAMA_IP = ${{ secrets.OLLAMA_IP }} >> .env
echo OLLAMA_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
echo LLM_ENDPOINT = ${{ secrets.OLLAMA_IP }} >> .env
echo LLM_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
echo MODEL = ${{ secrets.MODEL }} >> .env
# set -e ensures if nohup fails, this section fails
@@ -43,23 +45,23 @@ jobs:
Discord-Ollama-Container-Build: # test docker build and run
runs-on: ubuntu-latest
timeout-minutes: 2
timeout-minutes: 3
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Node Environment lts/jod
- name: Set up Node Environment
uses: actions/setup-node@v4
with:
node-version: lts/jod
cache: "npm"
node-version: lts/krypton
cache: npm
- name: Create Environment Variables
run: |
touch .env
echo CLIENT_TOKEN = ${{ secrets.BOT_TOKEN }} >> .env
echo OLLAMA_IP = ${{ secrets.OLLAMA_IP }} >> .env
echo OLLAMA_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
echo LLM_ENDPOINT = ${{ secrets.OLLAMA_IP }} >> .env
echo LLM_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
echo MODEL = ${{ secrets.MODEL }} >> .env
- name: Setup Docker Network and Images
-50
View File
@@ -1,50 +0,0 @@
name: Coverage
run-name: Code Coverage
on:
push:
branches:
- master
jobs:
Discord-Node-Coverage:
runs-on: ubuntu-latest
environment: coverage
timeout-minutes: 2
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Node Environment lts/jod
uses: actions/setup-node@v4
with:
node-version: lts/jod
cache: "npm"
- name: Install Project Dependencies
run: |
npm install
- name: Create Environment Variables
run: |
touch .env
echo CLIENT_TOKEN = ${{ secrets.BOT_TOKEN }} >> .env
echo OLLAMA_IP = ${{ secrets.OLLAMA_IP }} >> .env
echo OLLAMA_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
echo MODEL = ${{ secrets.MODEL }} >> .env
- name: Collect Code Coverage
run: |
LINE_PCT=$(npm run coverage | tail -2 | head -1 | awk '{print $3}')
echo "COVERAGE=$LINE_PCT" >> $GITHUB_ENV
- name: Upload Code Coverage
uses: schneegans/dynamic-badges-action@v1.7.0
with:
auth: ${{ secrets.GIST_SECRET }}
gistID: ${{ vars.GIST_ID }}
filename: coverage.json
label: Coverage
message: ${{ env.COVERAGE }}
valColorRange: ${{ env.COVERAGE }}
maxColorRange: 100
minColorRange: 0
+5 -7
View File
@@ -1,9 +1,6 @@
name: Deploy
run-name: Deploy Application Latest
# on:
# push:
# tags:
# - 'v*'
on: workflow_dispatch
jobs:
@@ -11,6 +8,7 @@ jobs:
runs-on: self-hosted
environment: deploy
timeout-minutes: 5
steps:
- name: Checkout Repo
uses: actions/checkout@v4
@@ -20,8 +18,8 @@ jobs:
run: |
touch .env
echo CLIENT_TOKEN = ${{ secrets.CLIENT }} >> .env
echo OLLAMA_IP = ${{ secrets.OLLAMA_IP }} >> .env
echo OLLAMA_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
echo LLM_ENDPOINT = ${{ secrets.OLLAMA_IP }} >> .env
echo LLM_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
echo MODEL = ${{ secrets.MODEL }} >> .env
echo DISCORD_IP = ${{ secrets.DISCORD_IP }} >> .env
echo SUBNET_ADDRESS = ${{ secrets.SUBNET_ADDRESS }} >> .env
@@ -40,7 +38,7 @@ jobs:
git clone https://github.com/kevinthedang/discord-ollama.git ${{ secrets.PATH }}
cd ${{ secrets.PATH }}
- name: Install nvm and Node.js lts/jod
- name: Install nvm and Node.js
run: |
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
export NVM_DIR="$HOME/.nvm"
+47
View File
@@ -0,0 +1,47 @@
name: Tests (Fork PRs)
run-name: Unit Tests
on:
pull_request:
branches:
- master
paths:
- '*'
- 'package*.json'
- 'src/**'
- 'tests/**'
- '!docs/**'
- '!imgs/**'
- '!.github/**'
- '.github/workflows/**'
- '!.gitignore'
- '!LICENSE'
- '!README'
jobs:
Discord-Node-Test-Fork:
if: ${{ github.event.pull_request.head.repo.fork == true }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 'lts/jod'
cache: npm
- name: Install Project Dependencies
run: |
npm install
- name: Create Dummy Env
run: |
echo CLIENT_TOKEN=test-token >> .env
echo LLM_ENDPOINT=127.0.0.1 >> .env
echo LLM_PORT=11434 >> .env
echo MODEL=dummy-model >> .env
- name: Test Application
run: |
npm run tests
+7 -4
View File
@@ -1,5 +1,6 @@
name: Tests
run-name: Unit Tests
on:
pull_request:
branches:
@@ -19,17 +20,19 @@ on:
jobs:
Discord-Node-Test:
if: ${{ github.event.pull_request.head.repo.fork == false }}
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Node Environment lts/jod
- name: Set up Node Environment
uses: actions/setup-node@v4
with:
node-version: lts/jod
cache: "npm"
cache: npm
- name: Install Project Dependencies
run: |
@@ -39,8 +42,8 @@ jobs:
run: |
touch .env
echo CLIENT_TOKEN = ${{ secrets.BOT_TOKEN }} >> .env
echo OLLAMA_IP = ${{ secrets.OLLAMA_IP }} >> .env
echo OLLAMA_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
echo LLM_ENDPOINT = ${{ secrets.OLLAMA_IP }} >> .env
echo LLM_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
echo MODEL = ${{ secrets.MODEL }} >> .env
- name: Test Application
-10
View File
@@ -37,16 +37,6 @@ pids
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
+1 -1
View File
@@ -12,7 +12,7 @@
# Technical/Business Code Ownership
/src/ @kevinthedang @JT2M0L3Y
/tests/ @JT2M0L3Y
/.github/ @kevinthedang
/.github/ @kevinthedang @JT2M0L3Y
# Docker Ownership
Dockerfile @kevinthedang
+2 -2
View File
@@ -1,5 +1,5 @@
# use node LTS image for version 22
FROM node:jod-alpine
# use node LTS image for version 24
FROM node:krypton-alpine
# set working directory inside container
WORKDIR /app
+10 -31
View File
@@ -5,41 +5,18 @@
<p><a href="#"></a><a href="https://creativecommons.org/licenses/by/4.0/"><img alt="License" src="https://img.shields.io/badge/License-CC_BY_4.0-darkgreen.svg" /></a>
<a href="#"></a><a href="https://github.com/kevinthedang/discord-ollama/releases/latest"><img alt="Release" src="https://img.shields.io/github/v/release/kevinthedang/discord-ollama?logo=github" /></a>
<a href="#"></a><a href="https://github.com/kevinthedang/discord-ollama/actions/workflows/build.yml"><img alt="Builds" src="https://github.com/kevinthedang/discord-ollama/actions/workflows/build.yml/badge.svg" /></a>
<!-- <a href="#"></a><a href="https://github.com/kevinthedang/discord-ollama/actions/workflows/deploy.yml"><img alt="Deploy Status" src="https://github.com/kevinthedang/discord-ollama/actions/workflows/deploy.yml/badge.svg" /></a> -->
<a href="#"></a><a href="https://github.com/kevinthedang/discord-ollama/actions/workflows/test.yml"><img alt="Tests" src="https://github.com/kevinthedang/discord-ollama/actions/workflows/test.yml/badge.svg" /></a>
<a href="#"></a><a href="https://github.com/kevinthedang/discord-ollama/actions/workflows/coverage.yml"><img alt="Code Coverage" src="https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/kevinthedang/bc7b5dcfa16561ab02bb3df67a99b22d/raw/coverage.json"></a>
</div>
## About/Goals
Ollama is an AI model management tool that allows users to install and use custom large language models locally.
The project aims to:
* [x] Create a Discord bot that will utilize Ollama and chat to chat with users!
* [x] User and Server Preferences
* [x] Message Persistance
* [x] Containerization with Docker
* [x] Slash Commands Compatible
* [x] Summary Command
* [ ] Model Info Command
* [ ] List Models Command
* [x] Pull Model Command
* [x] Switch Model Command
* [x] Delete Model Command
* [x] Create Thread Command
* [x] Create Private Thread Command
* [x] Message Stream Command
* [x] Change Message History Size Command
* [x] Clear Channel History Command (User Only)
* [x] Administrator Role Compatible
* [x] Generated Token Length Handling for >2000
* [x] Token Length Handling of any message size
* [x] Multi-User Chat Generation - This was built in from Ollama `v0.2.1+`
* [ ] Ollama Tool Support Implementation
* [ ] Enhanced Channel Context Awareness
* [ ] Improved User Replied Triggers
## About
Discord Ollama is a Discord bot that lets users interact with locally hosted
Ollama language models directly from Discord.
Further, Ollama provides the functionality to utilize custom models or provide context for the top-layer of any model available through the Ollama model library.
* [Customize a model](https://github.com/ollama/ollama#customize-a-model)
* [Modelfile Docs](https://github.com/ollama/ollama/blob/main/docs/modelfile.md)
The bot supports model management, streamed responses, conversation history,
private threads, and configurable server controls. Because Ollama runs locally,
you retain control over the models and data used by the bot.
See the [setup guides](./docs/setup-local.md) to get started.
## Documentation
These are guides to the features and capabilities of this app.
@@ -50,6 +27,7 @@ These are guides to the features and capabilities of this app.
* Clone this repo using `git clone https://github.com/kevinthedang/discord-ollama.git` or just use [GitHub Desktop](https://desktop.github.com/) to clone the repo.
* You will need a `.env` file in the root of the project directory with the bot's token. There is a `.env.sample` is provided for you as a reference for what environment variables.
* For example, `CLIENT_TOKEN = [Bot Token]`
* The LLM server is configured with `LLM_ENDPOINT` / `LLM_PORT` (`OLLAMA_IP` / `OLLAMA_PORT` still work as deprecated aliases).
* Please refer to the docs for bot setup.
* [Creating a Discord App](./docs/setup-discord-app.md)
* [Local Machine Setup](./docs/setup-local.md)
@@ -63,6 +41,7 @@ These are guides to the features and capabilities of this app.
* This project requires the use of npm version `10.9.0` or above.
* [Ollama](https://ollama.com/)
* [Ollama Docker Image](https://hub.docker.com/r/ollama/ollama)
* [llmman](https://github.com/llmmanorg/llmman) also serves the Ollama API (on port `17434`) and can be used in its place, see [Local Machine Setup](./docs/setup-local.md#using-with-llmman-alternative-to-ollama).
* [Discord.js Docs](https://discord.js.org/docs/packages/discord.js/main)
* [Setting up Docker (Ubuntu 20.04)](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-20-04)
* [Setting up Nvidia Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html)
+5 -5
View File
@@ -7,11 +7,11 @@ services:
build: ./ # find docker file in designated path
container_name: discord
restart: always # rebuild container always
image: kevinthedang/discord-ollama:0.9.0
image: kevinthedang/discord-ollama:0.9.2
environment:
CLIENT_TOKEN: ${CLIENT_TOKEN}
OLLAMA_IP: ${OLLAMA_IP}
OLLAMA_PORT: ${OLLAMA_PORT}
LLM_ENDPOINT: ${LLM_ENDPOINT:-${OLLAMA_IP}} # OLLAMA_IP is a deprecated alias
LLM_PORT: ${LLM_PORT:-${OLLAMA_PORT}} # OLLAMA_PORT is a deprecated alias
MODEL: ${MODEL}
networks:
ollama-net:
@@ -26,14 +26,14 @@ services:
restart: always
networks:
ollama-net:
ipv4_address: ${OLLAMA_IP}
ipv4_address: ${LLM_ENDPOINT:-${OLLAMA_IP}}
runtime: nvidia # use Nvidia Container Toolkit for GPU support
devices:
- /dev/nvidia0
volumes:
- ollama:/root/.ollama
ports:
- ${OLLAMA_PORT}:${OLLAMA_PORT}
- ${LLM_PORT:-${OLLAMA_PORT}}:${LLM_PORT:-${OLLAMA_PORT}}
# create a network that supports giving addresses withing a specific subnet
networks:
+3 -3
View File
@@ -43,10 +43,10 @@ sudo systemctl restart docker
* [GitHub repository](https://github.com/NVIDIA/nvidia-container-toolkit?tab=readme-ov-file) for Nvidia Container Toolkit
## To Run (with Docker and Docker Compose)
* With the inclusion of subnets in the `docker-compose.yml`, you will need to set the `SUBNET_ADDRESS`, `OLLAMA_IP`, `OLLAMA_PORT`, and `DISCORD_IP`. Here are some default values if you don't care:
* With the inclusion of subnets in the `docker-compose.yml`, you will need to set the `SUBNET_ADDRESS`, `LLM_ENDPOINT`, `LLM_PORT`, and `DISCORD_IP`. Here are some default values if you don't care:
* `SUBNET_ADDRESS = 172.18.0.0`
* `OLLAMA_IP = 172.18.0.2`
* `OLLAMA_PORT = 11434`
* `LLM_ENDPOINT = 172.18.0.2`
* `LLM_PORT = 11434`
* `DISCORD_IP = 172.18.0.3`
* Don't understand any of this? watch a Networking video to understand subnetting.
* You also need all environment variables shown in [`.env.sample`](../.env.sample)
+10 -3
View File
@@ -14,11 +14,18 @@
> [!NOTE]
> You can now pull models directly from the Discord client using `/pull-model <model-name>` or `/switch-model <model-name>`. They must exist from your local model library or from the [Ollama Model Library](https://ollama.com/library)
## Using with llmman (alternative to Ollama)
* [llmman](https://github.com/llmmanorg/llmman) is a local model runner that serves the Ollama API on port `17434`. The bot talks to it exactly as it would to Ollama, only the port differs.
* Install it with `curl -fsSL https://raw.githubusercontent.com/llmmanorg/llmman/main/install.sh | sh` (Linux/macOS) or `irm https://raw.githubusercontent.com/llmmanorg/llmman/main/install.ps1 | iex` (Windows).
* Start the server with `llmman serve` and pull a model with `llmman pull [model name]`, for example `llmman pull gemma4` or `llmman pull hf.co/unsloth/Qwen3.5-0.8B-GGUF`.
* Point the bot at it by setting `LLM_ENDPOINT = 127.0.0.1` and `LLM_PORT = 17434` in your `.env`, then set `MODEL` to a model you pulled. `/pull-model`, `/switch-model` and `/delete-model` work the same way.
## To Run Locally (without Docker)
* Run `npm install` to install the npm packages.
* Ensure that your [.env](../.env.sample) file's `OLLAMA_IP` is `127.0.0.1` to work properly.
* You only need your `CLIENT_TOKEN`, `OLLAMA_IP`, `OLLAMA_PORT`.
* The ollama ip and port should just use it's defaults by nature. If not, utilize `OLLAMA_IP = 127.0.0.1` and `OLLAMA_PORT = 11434`.
* Ensure that your [.env](../.env.sample) file's `LLM_ENDPOINT` is `127.0.0.1` to work properly.
* You only need your `CLIENT_TOKEN`, `LLM_ENDPOINT`, `LLM_PORT`.
* The LLM server ip and port should just use it's defaults by nature. If not, utilize `LLM_ENDPOINT = 127.0.0.1` and `LLM_PORT = 11434`.
* The older `OLLAMA_IP` / `OLLAMA_PORT` names are still accepted as deprecated aliases.
* Now, you can run the bot by running `npm run client` which will build and run the decompiled typescript and run the setup for ollama.
* **IMPORTANT**: This must be ran in the wsl/Linux instance to work properly! Using Command Prompt/Powershell/Git Bash/etc. will not work on Windows (at least in my experience).
* Refer to the [resources](../README.md#resources) on what node version to use.
+1251 -1822
View File
File diff suppressed because it is too large Load Diff
+6 -5
View File
@@ -1,12 +1,11 @@
{
"name": "discord-ollama",
"version": "0.9.0",
"version": "0.9.2",
"description": "Ollama Integration into discord",
"main": "build/index.js",
"exports": "./build/index.js",
"scripts": {
"tests": "vitest run",
"coverage": "vitest run --coverage",
"watch": "tsx watch src",
"build": "tsc",
"prod": "node .",
@@ -32,15 +31,17 @@
},
"devDependencies": {
"@types/node": "^22.13.14",
"@vitest/coverage-v8": "^3.0.9",
"ts-node": "^10.9.2",
"tsx": "^4.19.3",
"typescript": "^5.8.2",
"vitest": "^3.0.4"
"vitest": "^5.0.1"
},
"type": "module",
"engines": {
"npm": ">=10.9.0",
"node": ">=22.12.0"
"node": ">=24.0.0"
},
"allowScripts": {
"esbuild@0.25.12": true
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
import { Client, GatewayIntentBits } from 'discord.js'
import { Ollama } from 'ollama'
import { Queue } from './queues/queue.js'
import { Queue } from './components/index.js'
import { UserMessage, registerEvents } from './utils/index.js'
import Events from './events/index.js'
import Keys from './keys.js'
+2 -2
View File
@@ -1,5 +1,5 @@
import { Client, CommandInteraction, Message, MessageFlags, SendableChannels } from "discord.js";
import { accessChannelContext, normalMessage, SlashCommand, summarizeContextHistory, UserCommand, UserMessage } from "../utils/index.js";
import { Client, CommandInteraction, MessageFlags } from "discord.js";
import { accessChannelContext, SlashCommand, summarizeContextHistory, UserCommand, UserMessage } from "../utils/index.js";
import { ollama } from "../client.js"
export const Summary: SlashCommand = {
+46
View File
@@ -0,0 +1,46 @@
/**
* @class Logger
* @description A class to handle logging messages
* @method log
*/
export class Logger {
private logPrefix: string = ''
private type: string = 'log'
private constructPrefix(component?: string, method?: string): string {
let prefix = this.type.toUpperCase()
if (component) {
prefix += ` [${component}`
if (method) prefix += `: ${method}`
prefix += ']'
}
return prefix
}
public bind(component?: string, method?: string): CallableFunction {
let tempPrefix = this.constructPrefix(component, method)
if (tempPrefix !== this.logPrefix) this.logPrefix = tempPrefix
switch (this.type) {
case 'warn':
return console.warn.bind(console, this.logPrefix)
case 'error':
return console.error.bind(console, this.logPrefix)
case 'log':
default:
return console.log.bind(console, this.logPrefix)
}
}
public log(type: string, message: unknown, component?: string, method?: string): void {
if (type && type !== this.type) this.type = type
let log = this.bind(component, method)
log(message)
}
}
+2
View File
@@ -0,0 +1,2 @@
export * from './queue.js'
export * from './binder.js'
+1 -1
View File
@@ -1,6 +1,6 @@
import { TextChannel } from 'discord.js'
import { event, Events, normalMessage, UserMessage, clean, addToChannelContext } from '../utils/index.js'
import {
event, Events, normalMessage, UserMessage, clean, addToChannelContext,
getChannelInfo, getServerConfig, getUserConfig, openChannelInfo,
openConfig, UserConfig, getAttachmentData, getTextFileAttachmentData
} from '../utils/index.js'
+2 -2
View File
@@ -2,8 +2,8 @@ import { getEnvVar } from './utils/index.js'
export const Keys = {
clientToken: getEnvVar('CLIENT_TOKEN'),
ipAddress: getEnvVar('OLLAMA_IP', '127.0.0.1'), // default ollama ip if none
portAddress: getEnvVar('OLLAMA_PORT', '11434'), // default ollama port if none
ipAddress: getEnvVar('LLM_ENDPOINT', process.env.OLLAMA_IP ?? '127.0.0.1'), // OLLAMA_IP is a deprecated alias
portAddress: getEnvVar('LLM_PORT', process.env.OLLAMA_PORT ?? '11434'), // OLLAMA_PORT is a deprecated alias
defaultModel: getEnvVar('MODEL', 'llama3.2')
} as const // readonly keys
+1 -1
View File
@@ -26,7 +26,7 @@ export function getEnvVar(name: string, fallback?: string): string {
This is probably an invalid token unless Discord updated their token policy. Please provide a valid token.`)
// validate IPv4 address found in environment variables
if ((name.endsWith("_IP") || name.endsWith("_ADDRESS")) && !ipValidate.test(value))
if ((name.endsWith("_IP") || name.endsWith("_ADDRESS") || name.endsWith("_ENDPOINT")) && !ipValidate.test(value))
throw new Error(`Environment variable ${name} does not follow IPv4 formatting.`)
// return env variable
+1 -1
View File
@@ -1,6 +1,6 @@
import type { ClientEvents, Awaitable, Client } from 'discord.js'
import { Ollama } from 'ollama'
import { Queue } from '../queues/queue.js'
import { Queue } from '../components/index.js'
// Export events through here to reduce amount of imports
export { Events } from 'discord.js'
+2 -2
View File
@@ -112,7 +112,7 @@ export async function accessChannelContext(filename: string, callback: (config:
* @param user the user's name
* @param messages their messages
*/
export async function openChannelInfo(filename: string, channel: TextChannel | ThreadChannel, user: string, messages: UserMessage[] = []): Promise<void> {
export async function openChannelInfo(this: any, filename: string, channel: TextChannel | ThreadChannel, user: string, messages: UserMessage[] = []): Promise<void> {
const fullFileName = `data/${filename}-${user}.json`
if (fs.existsSync(fullFileName)) {
fs.readFile(fullFileName, 'utf8', (error, data) => {
@@ -143,7 +143,7 @@ export async function openChannelInfo(filename: string, channel: TextChannel | T
// only creating it, no need to add anything
fs.writeFileSync(fullFileName, JSON.stringify(object, null, 2))
console.log(`[Util: openChannelInfo] Created '${fullFileName}' in working directory`)
console.log(`[Util: ${this.name}] Created '${fullFileName}' in working directory`)
}
}
+2 -2
View File
@@ -10,7 +10,7 @@ import path from 'path'
* @param value new value to assign
*/
// add type of change (server, user)
export function openConfig(filename: string, key: string, value: any) {
export function openConfig(this: any, filename: string, key: string, value: any) {
const fullFileName = `data/${filename}`
// check if the file exists, if not then make the config file
@@ -41,7 +41,7 @@ export function openConfig(filename: string, key: string, value: any) {
fs.mkdirSync(directory, { recursive: true })
fs.writeFileSync(`data/${filename}`, JSON.stringify(object, null, 2))
console.log(`[Util: openConfig] Created '${filename}' in working directory`)
console.log(`[Util: ${this.name}] Created '${filename}' in working directory`)
}
}
+1 -2
View File
@@ -1,6 +1,5 @@
import { ChatResponse } from "ollama"
import { ChatResponse, AbortableAsyncIterator } from "ollama"
import { ChatParams } from "../index.js"
import { AbortableAsyncIterator } from "ollama/src/utils.js"
/**
* Method to query the Ollama client for async generation
+8 -9
View File
@@ -1,8 +1,7 @@
import { Message, SendableChannels } from 'discord.js'
import { ChatResponse, Ollama } from 'ollama'
import { ChatResponse, Ollama, AbortableAsyncIterator } from 'ollama'
import { ChatParams, UserMessage, streamResponse, blockResponse } from './index.js'
import { Queue } from '../queues/queue.js'
import { AbortableAsyncIterator } from 'ollama/src/utils.js'
import { Queue } from '../components/index.js'
/**
* Method to send replies as normal text on discord like any other user
@@ -11,6 +10,7 @@ import { AbortableAsyncIterator } from 'ollama/src/utils.js'
* @param msgHist message history between user and model
*/
export async function normalMessage(
this: any,
message: Message,
ollama: Ollama,
model: string,
@@ -77,12 +77,11 @@ export async function normalMessage(
sentMessage.edit(result)
}
} catch (error: any) {
console.log(`[Util: messageNormal] Error creating message: ${error.message}`)
if (error.message.includes('fetch failed'))
error.message = 'Missing ollama service on machine'
else if (error.message.includes('try pulling it first'))
error.message = `You do not have the ${model} downloaded. Ask an admin to pull it using the \`pull-model\` command.`
sentMessage.edit(`**Response generation failed.**\n\nReason: ${error.message}`)
console.log(`[Util: ${this.name}] Error creating message: ${error.message}`)
if (error.message.includes('try pulling it first'))
sentMessage.edit(`**Response generation failed.**\n\nReason: You do not have the ${model} downloaded. Ask an admin to pull it using the \`pull-model\` command.`)
else
sentMessage.edit(`**Response generation failed.**\n\nReason: ${error.message}`)
}
})
+1 -1
View File
@@ -26,6 +26,6 @@ describe('Events Existence', () => {
// test specific events in the object
it('references specific events', () => {
const eventsString = events.map(e => e.key.toString()).join(', ')
expect(eventsString).toBe('ready, messageCreate, interactionCreate, threadDelete')
expect(eventsString).toBe('clientReady, messageCreate, interactionCreate, threadDelete')
})
})
+24
View File
@@ -47,4 +47,28 @@ describe('Environment Setup', () => {
it('throws an error if key is not found', () => {
expect(() => getEnvVar('NON_EXISTENT_KEY')).toThrowError()
})
// test that *_ENDPOINT keys are validated as IPv4 addresses like *_IP keys
it('validates *_ENDPOINT keys as IPv4', () => {
expect(getEnvVar('UNSET_TEST_ENDPOINT', '127.0.0.1')).toBe('127.0.0.1')
expect(() => getEnvVar('UNSET_TEST_ENDPOINT', 'not-an-ip')).toThrowError()
})
// test the deprecated alias lookup used in src/keys.ts
it('falls back to the deprecated OLLAMA_IP / OLLAMA_PORT aliases', () => {
const saved = { LLM_ENDPOINT: process.env.LLM_ENDPOINT, LLM_PORT: process.env.LLM_PORT }
delete process.env.LLM_ENDPOINT
delete process.env.LLM_PORT
process.env.OLLAMA_IP = '10.0.0.5'
process.env.OLLAMA_PORT = '17434'
try {
expect(getEnvVar('LLM_ENDPOINT', process.env.OLLAMA_IP ?? '127.0.0.1')).toBe('10.0.0.5')
expect(getEnvVar('LLM_PORT', process.env.OLLAMA_PORT ?? '11434')).toBe('17434')
} finally {
delete process.env.OLLAMA_IP
delete process.env.OLLAMA_PORT
if (saved.LLM_ENDPOINT !== undefined) process.env.LLM_ENDPOINT = saved.LLM_ENDPOINT
if (saved.LLM_PORT !== undefined) process.env.LLM_PORT = saved.LLM_PORT
}
})
})
+1 -1
View File
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest'
import { Queue } from '../src/queues/queue.js'
import { Queue } from '../src/components/index.js'
/**
* Queue test suite, tests the Queue class
+1 -2
View File
@@ -18,9 +18,8 @@
// Decompile .ts to .js into a folder named build
"outDir": "build",
"rootDir": "src",
"baseUrl": ".",
"paths": {
"*": ["node_modules/"]
"*": ["./node_modules/"]
}
},
// environment for env vars
+2 -6
View File
@@ -1,13 +1,9 @@
import { defineConfig, configDefaults } from 'vitest/config'
import { defineConfig } from 'vitest/config'
// config for vitest
export default defineConfig({
test: {
globals: true, // <-- reduces test file imports
reporters: ['verbose'], // <-- verbose output
coverage: {
exclude: [...configDefaults.exclude, 'build/*', 'tests/*'], // <-- exclude JS build
reporter: ['text-summary'] // <-- report in text-summary
}
reporters: ['verbose'] // <-- verbose output
}
})