Skip to content

Commit bd4c33d

Browse files
committed
Added chat function along with token counting
1 parent 3fa9f9b commit bd4c33d

File tree

8 files changed

+238
-1
lines changed

8 files changed

+238
-1
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { CommandPlugin } from "./CommandPlugin";
2+
3+
export let taskComplete = false;
4+
5+
const TaskCompleteCommandPlugins: CommandPlugin[] = [
6+
{
7+
command: "task_complete",
8+
name: "Task Complete (Shutdown)",
9+
arguments: {
10+
reason: "reason",
11+
},
12+
execute: async (args) => {
13+
taskComplete = true;
14+
console.debug("Task complete", args["reason"]);
15+
return "Thank you";
16+
},
17+
},
18+
];
19+
export default TaskCompleteCommandPlugins;

app/commandPlugins/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import AgentCommandPlugins from "./AgentCommandPlugins";
22
import MemoryCommandPlugins from "./MemoryCommandPlugins";
33
import FileOperationCommandPlugins from "./FileOperationCommandPlugins";
4+
import TaskCompleteCommandPlugins from "./TaskCompleteCommandPlugins";
45

56
export const CommandPlugins = [
67
...MemoryCommandPlugins,
78
...AgentCommandPlugins,
8-
...FileOperationCommandPlugins
9+
...FileOperationCommandPlugins,
10+
...TaskCompleteCommandPlugins
911
];
1012

1113
export async function executeCommand(

app/utils/asserts.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function assertNever(arg: never) {
2+
throw new Error("Type not handled");
3+
}

app/utils/chat.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { callLLMChatCompletion, LLMMessage } from "./llmUtils";
2+
import { Config } from "./config";
3+
import { countMessageTokens } from "./tokenCounter";
4+
5+
interface ChatWithAiArgs {
6+
prompt: string;
7+
userInput: string;
8+
fullMessageHistory: LLMMessage[];
9+
permanentMemory: string[];
10+
tokenLimit: number;
11+
debug?: boolean;
12+
}
13+
14+
export async function chatWithAI({
15+
prompt,
16+
userInput,
17+
fullMessageHistory,
18+
permanentMemory: permanentMemory,
19+
tokenLimit: tokenLimit,
20+
debug = false,
21+
}: ChatWithAiArgs): Promise<string> {
22+
while (true) {
23+
try {
24+
const model = Config.fast_llm_model;
25+
const sendTokenLimit = tokenLimit - 1000;
26+
27+
const currentContext: LLMMessage[] = [
28+
{ role: "system", content: prompt },
29+
{ role: "system", content: `Permanent memory: ${permanentMemory}` },
30+
];
31+
32+
let nextMessageToAddIndex = fullMessageHistory.length - 1;
33+
let currentTokensUsed = 0;
34+
const insertionIndex = currentContext.length;
35+
36+
currentTokensUsed = countMessageTokens(currentContext, model);
37+
currentTokensUsed += countMessageTokens(
38+
[{ role: "user", content: userInput }],
39+
model
40+
);
41+
42+
while (nextMessageToAddIndex >= 0) {
43+
const messageToAdd = fullMessageHistory[nextMessageToAddIndex];
44+
const tokensToAdd = countMessageTokens([messageToAdd], model);
45+
46+
if (currentTokensUsed + tokensToAdd > sendTokenLimit) {
47+
break;
48+
}
49+
50+
currentContext.splice(
51+
insertionIndex,
52+
0,
53+
fullMessageHistory[nextMessageToAddIndex]
54+
);
55+
currentTokensUsed += tokensToAdd;
56+
nextMessageToAddIndex -= 1;
57+
}
58+
59+
currentContext.push({ role: "user", content: userInput });
60+
const tokensRemaining = tokenLimit - currentTokensUsed;
61+
62+
if (debug) {
63+
console.log(`Token limit: ${tokenLimit}`);
64+
console.log(`Send Token Count: ${currentTokensUsed}`);
65+
console.log(`Tokens remaining for response: ${tokensRemaining}`);
66+
console.log("------------ CONTEXT SENT TO AI ---------------");
67+
for (const message of currentContext) {
68+
if (message.role === "system" && message.content === prompt) {
69+
continue;
70+
}
71+
console.log(
72+
`${message.role.charAt(0).toUpperCase() + message.role.slice(1)}: ${
73+
message.content
74+
}`
75+
);
76+
console.log();
77+
}
78+
console.log("----------- END OF CONTEXT ----------------");
79+
}
80+
81+
const assistantReply = await callLLMChatCompletion(
82+
currentContext,
83+
model,
84+
undefined /* temperature */,
85+
tokensRemaining
86+
);
87+
88+
fullMessageHistory.push({ role: "user", content: userInput });
89+
fullMessageHistory.push({
90+
role: "assistant",
91+
content: assistantReply,
92+
});
93+
94+
return assistantReply;
95+
} catch (error) {
96+
console.error("Error calling chat", error);
97+
throw error;
98+
}
99+
}
100+
}

app/utils/prompt.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { CommandPlugins } from "../commandPlugins";
2+
3+
export function generatePrompt() {
4+
const commandsStr = CommandPlugins.map((commandPlugin, index) => {
5+
const argsStr = Object.entries(commandPlugin)
6+
.map(([key, val]) => `"${key}": "<${val}>"`)
7+
.join(", ");
8+
return `${index + 1}. ${commandPlugin.name}: "${
9+
commandPlugin.command
10+
}", args: ${argsStr}`;
11+
}).join("\n");
12+
13+
return `
14+
CONSTRAINTS:
15+
16+
1. ~4000 word limit for memory. Your memory is short, so immidiately save important information to long term memory and code to files.
17+
2. No user assistance
18+
19+
COMMANDS:
20+
21+
${commandsStr}
22+
23+
RESOURCES:
24+
25+
1. Long Term memory management.
26+
2. GPT-3.5 powered Agents for delegation of simple tasks.
27+
3. File output.
28+
29+
PERFORMANCE EVALUATION:
30+
31+
1. Continuously review and analyze your actions to ensure you are performing to the best of your abilities.
32+
2. Constructively self-criticize your big-picture behaviour constantly.
33+
3. Reflect on past decisions and strategies to refine your approach.
34+
4. Every command has a cost, so be smart and efficent. Aim to complete tasks in the least number of steps.
35+
36+
You should only respond in JSON format as described below
37+
38+
RESPONSE FORMAT:
39+
{
40+
"command": {
41+
"name": "command name",
42+
"args":{
43+
"arg name": "value"
44+
}
45+
},
46+
"thoughts":
47+
{
48+
"text": "thought",
49+
"reasoning": "reasoning",
50+
"plan": "- short bulleted\n- list that conveys\n- long-term plan",
51+
"criticism": "constructive self-criticism",
52+
"speak": "thoughts summary to say to user"
53+
}
54+
}
55+
56+
Ensure the response can be parsed by Python json.loads`;
57+
}

app/utils/tokenCounter.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { encoding_for_model } from "@dqbd/tiktoken";
2+
import type { LLMMessage, LLMModel } from "./llmUtils";
3+
import type { TiktokenModel } from "@dqbd/tiktoken";
4+
import { assertNever } from "./asserts";
5+
6+
export function countMessageTokens(messages: LLMMessage[], model: LLMModel): number {
7+
const encoding = encoding_for_model(model as TiktokenModel);
8+
9+
let tokensPerMessage = 0;
10+
let tokensPerName = 0;
11+
12+
if (model === "gpt-3.5-turbo") {
13+
return countMessageTokens(messages, "gpt-3.5-turbo-0301");
14+
} else if (model === "gpt-4") {
15+
return countMessageTokens(messages, "gpt-4-0314");
16+
} else if (model === "gpt-3.5-turbo-0301") {
17+
tokensPerMessage = 4;
18+
tokensPerName = -1;
19+
} else if (model === "gpt-4-0314") {
20+
tokensPerMessage = 3;
21+
tokensPerName = 1;
22+
} else {
23+
assertNever(model);
24+
}
25+
26+
let numTokens = 0;
27+
for(const message of messages) {
28+
numTokens += tokensPerMessage;
29+
for(const [key, val] of Object.entries(message)) {
30+
numTokens += encoding.encode(val).length;
31+
if(key === "name") {
32+
numTokens += tokensPerName;
33+
}
34+
}
35+
}
36+
37+
numTokens += 3 // every reply is primed with <|start|>assistant<|message|>
38+
return numTokens;
39+
}
40+
41+
export function countStringTokens(str: string, model: LLMModel): number {
42+
const encoding = encoding_for_model(model as TiktokenModel);
43+
return encoding.encode(str).length;
44+
}

package-lock.json

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"/public/build"
3030
],
3131
"dependencies": {
32+
"@dqbd/tiktoken": "^1.0.2",
3233
"@prisma/client": "^4.11.0",
3334
"@remix-run/node": "^1.15.0",
3435
"@remix-run/react": "^1.15.0",

0 commit comments

Comments
 (0)