mirror of
https://github.com/tcsenpai/PerplexiBot.git
synced 2025-06-06 19:25:22 +00:00
initial commit
This commit is contained in:
commit
1e45da611f
1
.env.example
Normal file
1
.env.example
Normal file
@ -0,0 +1 @@
|
|||||||
|
PPLX_API_KEY="pplx-yourkey"
|
178
.gitignore
vendored
Normal file
178
.gitignore
vendored
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
|
||||||
|
logs
|
||||||
|
_.log
|
||||||
|
npm-debug.log_
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Caches
|
||||||
|
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
|
||||||
|
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
|
||||||
|
pids
|
||||||
|
_.pid
|
||||||
|
_.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
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
|
||||||
|
.temp
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
|
# IntelliJ based IDEs
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Finder (MacOS) folder config
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
bun.lockdb
|
||||||
|
config.js
|
15
README.md
Normal file
15
README.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# tcsenpplx
|
||||||
|
|
||||||
|
To install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
To run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run index.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
This project was created using `bun init` in bun v1.0.30. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
|
4
context.json.example
Normal file
4
context.json.example
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[
|
||||||
|
{ "role": "user", "content": "*enters the room*" },
|
||||||
|
{ "role": "assistant", "content": "Hi! " }
|
||||||
|
]
|
74
index.ts
Normal file
74
index.ts
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import Perplexity from "./libs/Perplexity";
|
||||||
|
import "dotenv/config";
|
||||||
|
import required from "./libs/required";
|
||||||
|
import readin from "./libs/readin";
|
||||||
|
import fs from "fs"
|
||||||
|
import parseCommands from "./libs/parseCommands";
|
||||||
|
import config from "./config"
|
||||||
|
import type Message from "./libs/Perplexity"
|
||||||
|
import type { ListFormat } from "typescript";
|
||||||
|
|
||||||
|
required(process.env.PPLX_API_KEY);
|
||||||
|
const PPLX_API_KEY = process.env.PPLX_API_KEY as string;
|
||||||
|
let perplexity: Perplexity;
|
||||||
|
|
||||||
|
// NOTE Helper to load context from file
|
||||||
|
function inject_context() {
|
||||||
|
let loaded_context = fs.readFileSync("./context.json", {encoding: "utf-8"})
|
||||||
|
let list_context = JSON.parse(loaded_context)
|
||||||
|
for (let i=0; i<list_context.length; i++) {
|
||||||
|
perplexity.add_to_context(list_context[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE Helper to save context long term
|
||||||
|
function save_in_context(role: string, message: string) {
|
||||||
|
let loaded_context = fs.readFileSync("./context.json", {encoding: "utf-8"})
|
||||||
|
let list_context = JSON.parse(loaded_context) as Array<{"role": string, "content": string}>
|
||||||
|
let new_insertion = {
|
||||||
|
role: role,
|
||||||
|
content: message
|
||||||
|
}
|
||||||
|
list_context.push(new_insertion)
|
||||||
|
fs.writeFileSync("./context.json", JSON.stringify(list_context))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log("[TCSenPPLX] Working with: " + PPLX_API_KEY);
|
||||||
|
// Connecting to perplexity
|
||||||
|
perplexity = new Perplexity(
|
||||||
|
PPLX_API_KEY,
|
||||||
|
"https://api.perplexity.ai",
|
||||||
|
"sonar-medium-chat",
|
||||||
|
false,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
// Setting our ai personality
|
||||||
|
perplexity.add_to_context(config);
|
||||||
|
// If any, inject context
|
||||||
|
inject_context()
|
||||||
|
// Chatting
|
||||||
|
await chat();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function chat(loop: boolean = true, save: boolean = true) {
|
||||||
|
var proceed = true;
|
||||||
|
while (proceed) {
|
||||||
|
proceed = loop;
|
||||||
|
let question = await readin("You: ");
|
||||||
|
let parsed = parseCommands(question);
|
||||||
|
proceed = parsed.proceed
|
||||||
|
let logpart = parsed.logpart
|
||||||
|
let response = await perplexity.ask(question);
|
||||||
|
console.log("Assistant: " + response);
|
||||||
|
if (save) {
|
||||||
|
save_in_context("user", question)
|
||||||
|
if (logpart) {
|
||||||
|
response += +"\n[" + logpart + "]"
|
||||||
|
}
|
||||||
|
save_in_context("assistant", response as string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
106
libs/Perplexity.ts
Normal file
106
libs/Perplexity.ts
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
// Wrapper class for easy tasking with Perplexity (and OpenAI in general)
|
||||||
|
|
||||||
|
import OpenAI from "openai";
|
||||||
|
import type { RequestOptions } from "openai/core.mjs";
|
||||||
|
|
||||||
|
interface Message {
|
||||||
|
role: string;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Context {
|
||||||
|
messages: Message[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Perplexity {
|
||||||
|
API_KEY: string;
|
||||||
|
instance: OpenAI;
|
||||||
|
current_model: string;
|
||||||
|
verbose: boolean;
|
||||||
|
|
||||||
|
// Instance vars
|
||||||
|
roles = {
|
||||||
|
a: "system",
|
||||||
|
b: "user",
|
||||||
|
c: "assistant",
|
||||||
|
};
|
||||||
|
context: Context = { messages: [] };
|
||||||
|
options: RequestOptions = {}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
api_key: string,
|
||||||
|
base_url: string = "https://api.perplexity.ai",
|
||||||
|
base_model: string = "sonar-small-chat",
|
||||||
|
verbose: boolean = false,
|
||||||
|
options: RequestOptions = {}
|
||||||
|
) {
|
||||||
|
// Setting our API Key
|
||||||
|
this.API_KEY = api_key;
|
||||||
|
this.current_model = base_model;
|
||||||
|
// Setting verbosity
|
||||||
|
this.verbose = verbose;
|
||||||
|
// Setting options
|
||||||
|
this.options = options;
|
||||||
|
// Creating an openai agent
|
||||||
|
this.instance = new OpenAI({
|
||||||
|
baseURL: base_url,
|
||||||
|
apiKey: this.API_KEY, // This is the default and can be omitted
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// INFO Adding something to context
|
||||||
|
add_to_context(message: Message) {
|
||||||
|
// Validate roles
|
||||||
|
if (
|
||||||
|
message.role != this.roles.a &&
|
||||||
|
message.role != this.roles.b &&
|
||||||
|
message.role != this.roles.c
|
||||||
|
) {
|
||||||
|
console.log("[ERROR] Not added. Possible roles can be:");
|
||||||
|
console.log(this.roles);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.context.messages.push(message);
|
||||||
|
if(this.verbose) console.log("[OK] Added to context for: " + message.role);
|
||||||
|
}
|
||||||
|
|
||||||
|
// INFO Setting a default model
|
||||||
|
set_current_model(model: string) {
|
||||||
|
this.current_model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
// INFO Context viewer
|
||||||
|
show_context() {
|
||||||
|
console.log(this.context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// INFO Wrapper for asking
|
||||||
|
async ask(question: string) {
|
||||||
|
let insertion = {
|
||||||
|
role: "user",
|
||||||
|
content: question,
|
||||||
|
};
|
||||||
|
this.add_to_context(insertion);
|
||||||
|
let response = await this.complete(false, this.options);
|
||||||
|
this.add_to_context(response as Message);
|
||||||
|
return response.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
// INFO Wrapper around completion
|
||||||
|
async complete(override_model?: string | boolean, options: RequestOptions = {}) {
|
||||||
|
if (this.verbose) console.log("[WORKING] Thinking...");
|
||||||
|
let used_model = override_model ? override_model : this.current_model;
|
||||||
|
const chatCompletion = await this.instance.chat.completions.create(
|
||||||
|
{
|
||||||
|
messages: this.context.messages as [],
|
||||||
|
model: used_model as string,
|
||||||
|
stream: false,
|
||||||
|
},
|
||||||
|
options
|
||||||
|
);
|
||||||
|
let return_prompt = chatCompletion.choices[0].message;
|
||||||
|
return return_prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Support streams if needed
|
||||||
|
}
|
25
libs/parseCommands.ts
Normal file
25
libs/parseCommands.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
export interface ContextPart {
|
||||||
|
role: string
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function parseCommands(input: string): {proceed: boolean, logpart: string | null} {
|
||||||
|
let proceed = true
|
||||||
|
let logpart = null
|
||||||
|
switch (input) {
|
||||||
|
case "exit":
|
||||||
|
case "quit":
|
||||||
|
case "stop":
|
||||||
|
case "end":
|
||||||
|
case "bye":
|
||||||
|
proceed = false
|
||||||
|
logpart = "Last conversation ended at: " + String(Date.now())
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
proceed: proceed,
|
||||||
|
logpart: logpart
|
||||||
|
}
|
||||||
|
}
|
11
libs/readin.ts
Normal file
11
libs/readin.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import * as readline from "readline/promises";
|
||||||
|
|
||||||
|
export default async function readin(q: string = ""): Promise<string> {
|
||||||
|
const rl = readline.createInterface({
|
||||||
|
input: process.stdin,
|
||||||
|
output: process.stdout,
|
||||||
|
});
|
||||||
|
let answer = await rl.question(q);
|
||||||
|
rl.close();
|
||||||
|
return answer;
|
||||||
|
}
|
15
libs/required.ts
Normal file
15
libs/required.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export default function required(
|
||||||
|
condition: any,
|
||||||
|
message: string = "Oops! Something went wrong!",
|
||||||
|
fatal: boolean = true,
|
||||||
|
exitcode: number = -1
|
||||||
|
) {
|
||||||
|
if (condition) return
|
||||||
|
|
||||||
|
if (fatal) {
|
||||||
|
console.log("[FATAL] " + message);
|
||||||
|
process.exit(exitcode);
|
||||||
|
} else {
|
||||||
|
console.log("[ERROR] " + message);
|
||||||
|
}
|
||||||
|
}
|
15
package.json
Normal file
15
package.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "tcsenpplx",
|
||||||
|
"module": "index.ts",
|
||||||
|
"type": "module",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"dotenv": "^16.4.5",
|
||||||
|
"openai": "^4.29.2"
|
||||||
|
}
|
||||||
|
}
|
27
tsconfig.json
Normal file
27
tsconfig.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
// Enable latest features
|
||||||
|
"lib": ["ESNext"],
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"allowJs": true,
|
||||||
|
|
||||||
|
// Bundler mode
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
|
||||||
|
// Some stricter flags (disabled by default)
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": false
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user