Skip to content

Commit eedbf72

Browse files
committed
a
1 parent 279676d commit eedbf72

File tree

2 files changed

+112
-16
lines changed

2 files changed

+112
-16
lines changed

Ask.mjs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import fs from 'fs/promises';
2+
import os from 'os';
3+
import path from 'path';
4+
import { OpenAI } from "openai";
5+
import { Anthropic } from '@anthropic-ai/sdk';
6+
import { encode } from "gpt-tokenizer/esm/model/davinci-codex"; // tokenizer
7+
8+
// Map of model shortcodes to full model names
9+
export const MODELS = {
10+
g: 'gpt-4-turbo-2024-04-09',
11+
G: 'gpt-4-32k-0314',
12+
c: 'claude-3-haiku-20240307',
13+
s: 'claude-3-sonnet-20240229',
14+
C: 'claude-3-opus-20240229',
15+
};
16+
17+
// Utility function to read the OpenAI API token
18+
async function getOpenAIToken() {
19+
const tokenPath = path.join(os.homedir(), '.config', 'openai.token');
20+
try {
21+
return (await fs.readFile(tokenPath, 'utf8')).trim();
22+
} catch (err) {
23+
console.error('Error reading openai.token file:', err.message);
24+
process.exit(1);
25+
}
26+
}
27+
28+
// Utility function to read the Anthropic API token
29+
async function getAnthropicToken() {
30+
const tokenPath = path.join(os.homedir(), '.config', 'anthropic.token');
31+
try {
32+
return (await fs.readFile(tokenPath, 'utf8')).trim();
33+
} catch (err) {
34+
console.error('Error reading anthropic.token file:', err.message);
35+
process.exit(1);
36+
}
37+
}
38+
39+
// Factory function to create a stateful asker
40+
export function asker() {
41+
const messages = [];
42+
43+
// Asker function that maintains conversation state
44+
async function ask(userMessage, { model, temperature = 0.0, max_tokens = 4096 }) {
45+
model = MODELS[model] || model;
46+
const isGPT = model.startsWith('gpt');
47+
48+
const client = isGPT ?
49+
new OpenAI({ apiKey: await getOpenAIToken() }) :
50+
new Anthropic({ apiKey: await getAnthropicToken() });
51+
52+
messages.push({ role: 'user', content: userMessage });
53+
54+
const params = {
55+
model,
56+
temperature,
57+
max_tokens,
58+
stream: true,
59+
};
60+
61+
let result = "";
62+
63+
if (isGPT) {
64+
params.messages = messages;
65+
66+
const stream = await client.chat.completions.create(params);
67+
68+
for await (const chunk of stream) {
69+
const text = chunk.choices[0]?.delta?.content || "";
70+
process.stdout.write(text);
71+
result += text;
72+
}
73+
} else {
74+
const stream = client.messages.stream({
75+
...params,
76+
messages
77+
}).on('text', (text) => {
78+
process.stdout.write(text);
79+
result += text;
80+
});
81+
await stream.finalMessage();
82+
}
83+
84+
messages.push({ role: 'assistant', content: result });
85+
86+
return result;
87+
}
88+
89+
return ask;
90+
}
91+
92+
export function token_count(inputText) {
93+
// Encode the input string into tokens
94+
const tokens = encode(inputText);
95+
96+
// Get the number of tokens
97+
const numberOfTokens = tokens.length;
98+
99+
// Return the number of tokens
100+
return numberOfTokens;
101+
}
102+

holefill.mjs

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,38 @@
11
#!/usr/bin/env node
2-
import { asker, MODELS } from './Ask.mjs';
2+
import { asker, MODELS, token_count } from './Ask.mjs';
33
import process from "process";
44
import fs from 'fs/promises';
55
import path from 'path';
66

77
const system = `
88
You are a HOLE FILLER. You are provided with a file containing holes, formatted
9-
as '{{HOLE}}'. Your TASK is to answer with a string to replace this hole with.
9+
as '{{X}}'. Your TASK is to answer with a string to replace this hole with.
1010
11-
## EXAMPLE QUERY:
11+
# EXAMPLE QUERY:
1212
1313
function sum_evens(lim) {
1414
var sum = 0;
1515
for (var i = 0; i < lim; ++i) {
16-
{{LOOP}}
16+
{{X}}
1717
}
1818
return sum;
1919
}
2020
21-
TASK: Fill the {{LOOP}} hole.
21+
TASK: Fill the {{X}} hole.
2222
23-
## CORRECT ANSWER:
23+
# CORRECT ANSWER:
2424
2525
if (i % 2 === 0) {
2626
sum += i;
2727
}
2828
29-
## NOTICE THE CONTEXT-AWARE INDENTATION:
29+
# NOTICE THE CONTEXT-AWARE INDENTATION:
3030
3131
1. The first line is NOT indented, because there are already spaces before {{LOOP}}.
3232
3333
2. The other lines ARE indented, to match the indentation of the context.
3434
35-
# NOTICE NO EXTRA EXPLANATORY WORDS:
36-
37-
1. Do NOT add explanatory words like 'here is the code...' in the answer.
38-
39-
2. Unless demanded by context, do NOT add backticks around the answer.
40-
41-
3. Answer just with the correct substitution.
35+
# ANSWER ONLY WITH THE CORRECT SUBSTITUTION. NOTHING ELSE.
4236
`;
4337

4438
var file = process.argv[2];
@@ -75,8 +69,8 @@ while ((match = regex.exec(curr_code)) !== null) {
7569

7670
await fs.writeFile(curr, curr_code, 'utf-8');
7771

78-
var tokens = curr_code.length; // Use the length of the code as a rough estimate of tokens
79-
var holes = curr_code.match(/{{\w+}}/g) || [];
72+
var tokens = token_count(curr_code);
73+
var holes = curr_code.match(/{{\w+}}/g) || [];
8074

8175
var ask = asker();
8276

0 commit comments

Comments
 (0)