Skip to content

Commit f996089

Browse files
refactor: centralize token tracking and clean up console output (#3)
* refactor: centralize token tracking and clean up console output - Remove manual token tracking in agent.ts - Track tokens through tokenTracker.trackUsage() - Clean up verbose console output - Add ESLint configuration - Fix TypeScript linting issues Co-Authored-By: Han Xiao <[email protected]> * refactor: simplify sleep function and use console.log consistently Co-Authored-By: Han Xiao <[email protected]> * refactor: remove color modifiers from console.log statements Co-Authored-By: Han Xiao <[email protected]> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Han Xiao <[email protected]>
1 parent 966ef5d commit f996089

10 files changed

+71
-87
lines changed

.eslintrc.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module.exports = {
2+
parser: '@typescript-eslint/parser',
3+
plugins: ['@typescript-eslint'],
4+
extends: [
5+
'eslint:recommended',
6+
'plugin:@typescript-eslint/recommended'
7+
],
8+
env: {
9+
node: true,
10+
es6: true
11+
},
12+
parserOptions: {
13+
ecmaVersion: 2020,
14+
sourceType: 'module'
15+
},
16+
rules: {
17+
'no-console': ['error', { allow: ['log', 'error'] }],
18+
'@typescript-eslint/no-explicit-any': 'off'
19+
}
20+
};

package.json

+6-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
"build": "tsc",
77
"dev": "npx ts-node src/agent.ts",
88
"search": "npx ts-node src/test-duck.ts",
9-
"rewrite": "npx ts-node src/tools/query-rewriter.ts"
9+
"rewrite": "npx ts-node src/tools/query-rewriter.ts",
10+
"lint": "eslint . --ext .ts",
11+
"lint:fix": "eslint . --ext .ts --fix"
1012
},
1113
"keywords": [],
1214
"author": "",
@@ -20,6 +22,9 @@
2022
},
2123
"devDependencies": {
2224
"@types/node": "^22.10.10",
25+
"@typescript-eslint/eslint-plugin": "^7.0.1",
26+
"@typescript-eslint/parser": "^7.0.1",
27+
"eslint": "^8.56.0",
2328
"ts-node": "^10.9.2",
2429
"typescript": "^5.7.3"
2530
}

src/agent.ts

+35-60
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,9 @@ import { GEMINI_API_KEY, JINA_API_KEY, MODEL_NAME } from "./config";
1111
import { tokenTracker } from "./utils/token-tracker";
1212

1313
async function sleep(ms: number) {
14-
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
15-
const startTime = Date.now();
16-
const endTime = startTime + ms;
17-
18-
// Clear current line and hide cursor
19-
process.stdout.write('\x1B[?25l');
20-
21-
while (Date.now() < endTime) {
22-
const remaining = Math.ceil((endTime - Date.now()) / 1000);
23-
const frameIndex = Math.floor(Date.now() / 100) % frames.length;
24-
25-
// Clear line and write new frame
26-
process.stdout.write(`\r${frames[frameIndex]} Cool down... ${remaining}s remaining`);
27-
28-
// Small delay for animation
29-
await new Promise(resolve => setTimeout(resolve, 50));
30-
}
31-
32-
// Clear line, show cursor and move to next line
33-
process.stdout.write('\r\x1B[K\x1B[?25h\n');
34-
35-
// Original sleep
36-
await new Promise(resolve => setTimeout(resolve, 0));
14+
const seconds = Math.ceil(ms / 1000);
15+
console.log(`Waiting ${seconds}s...`);
16+
return new Promise(resolve => setTimeout(resolve, ms));
3717
}
3818

3919
type ResponseSchema = {
@@ -96,7 +76,7 @@ type ResponseSchema = {
9676
};
9777

9878
function getSchema(allowReflect: boolean, allowRead: boolean): ResponseSchema {
99-
let actions = ["search", "answer"];
79+
const actions = ["search", "answer"];
10080
if (allowReflect) {
10181
actions.push("reflect");
10282
}
@@ -164,8 +144,7 @@ function getSchema(allowReflect: boolean, allowRead: boolean): ResponseSchema {
164144
}
165145

166146
function getPrompt(question: string, context?: any[], allQuestions?: string[], allowReflect: boolean = false, badContext?: any[], knowledge?: any[], allURLs?: Record<string, string>) {
167-
// console.log('Context:', context);
168-
// console.log('All URLs:', JSON.stringify(allURLs, null, 2));
147+
169148

170149
const knowledgeIntro = knowledge?.length ?
171150
`
@@ -212,7 +191,7 @@ ${allURLs ? `
212191
**visit**:
213192
- Visit any URLs from below to gather external knowledge, choose the most relevant URLs that might contain the answer
214193
215-
${Object.keys(allURLs).map((url, i) => `
194+
${Object.keys(allURLs).map(url => `
216195
+ "${url}": "${allURLs[url]}"`).join('')}
217196
218197
- When you have enough search result in the context and want to deep dive into specific URLs
@@ -268,8 +247,8 @@ Critical Requirements:
268247
- Maintain strict JSON syntax`.trim();
269248
}
270249

271-
let context: StepData[] = []; // successful steps in the current session
272-
let allContext: StepData[] = []; // all steps in the current session, including those leads to wrong results
250+
const context: StepData[] = []; // successful steps in the current session
251+
const allContext: StepData[] = []; // all steps in the current session, including those leads to wrong results
273252

274253
function updateContext(step: any) {
275254
context.push(step);
@@ -280,27 +259,25 @@ function removeAllLineBreaks(text: string) {
280259
return text.replace(/(\r\n|\n|\r)/gm, " ");
281260
}
282261

283-
async function getResponse(question: string, tokenBudget: number = 1000000) {
284-
let totalTokens = 0;
262+
async function getResponse(question: string) {
285263
let step = 0;
286264
let totalStep = 0;
287265
let badAttempts = 0;
288-
let gaps: string[] = [question]; // All questions to be answered including the orginal question
289-
let allQuestions = [question];
290-
let allKeywords = [];
291-
let allKnowledge = []; // knowledge are intermedidate questions that are answered
292-
let badContext = [];
266+
const gaps: string[] = [question]; // All questions to be answered including the orginal question
267+
const allQuestions = [question];
268+
const allKeywords = [];
269+
const allKnowledge = []; // knowledge are intermedidate questions that are answered
270+
const badContext = [];
293271
let diaryContext = [];
294-
let allURLs: Record<string, string> = {};
295-
while (totalTokens < tokenBudget) {
272+
const allURLs: Record<string, string> = {};
273+
const currentQuestion = gaps.length > 0 ? gaps.shift()! : question;
274+
while (gaps.length > 0 || currentQuestion === question) {
296275
// add 1s delay to avoid rate limiting
297276
await sleep(1000);
298277
step++;
299278
totalStep++;
300-
console.log('===STEPS===', totalStep)
301-
console.log('Gaps:', gaps)
279+
console.log(`Step ${totalStep}: Processing ${gaps.length} remaining questions`);
302280
const allowReflect = gaps.length <= 1;
303-
const currentQuestion = gaps.length > 0 ? gaps.shift()! : question;
304281
// update all urls with buildURLMap
305282
const allowRead = Object.keys(allURLs).length > 0;
306283
const prompt = getPrompt(
@@ -311,7 +288,7 @@ async function getResponse(question: string, tokenBudget: number = 1000000) {
311288
badContext,
312289
allKnowledge,
313290
allURLs);
314-
console.log('Prompt len:', prompt.length)
291+
315292

316293
const model = genAI.getGenerativeModel({
317294
model: MODEL_NAME,
@@ -325,12 +302,12 @@ async function getResponse(question: string, tokenBudget: number = 1000000) {
325302
const result = await model.generateContent(prompt);
326303
const response = await result.response;
327304
const usage = response.usageMetadata;
305+
tokenTracker.trackUsage('agent', usage?.totalTokenCount || 0);
306+
328307

329-
totalTokens += usage?.totalTokenCount || 0;
330-
console.log(`Tokens: ${totalTokens}/${tokenBudget}`);
331308

332309
const action = JSON.parse(response.text());
333-
console.log('Question-Action:', currentQuestion, action);
310+
334311

335312
if (action.action === 'answer') {
336313
updateContext({
@@ -339,8 +316,8 @@ async function getResponse(question: string, tokenBudget: number = 1000000) {
339316
...action,
340317
});
341318

342-
const { response: evaluation, tokens: evalTokens } = await evaluateAnswer(currentQuestion, action.answer);
343-
totalTokens += evalTokens;
319+
const { response: evaluation } = await evaluateAnswer(currentQuestion, action.answer);
320+
344321

345322
if (currentQuestion === question) {
346323
if (badAttempts >= 3) {
@@ -359,7 +336,7 @@ ${evaluation.reasoning}
359336
360337
Your journey ends here.
361338
`);
362-
console.info('\x1b[32m%s\x1b[0m', 'Final Answer:', action.answer);
339+
console.log('Final Answer:', action.answer);
363340
tokenTracker.printSummary();
364341
await storeContext(prompt, [allContext, allKeywords, allQuestions, allKnowledge], totalStep);
365342
return action;
@@ -381,7 +358,7 @@ ${evaluation.reasoning}
381358
382359
Your journey ends here. You have successfully answered the original question. Congratulations! 🎉
383360
`);
384-
console.info('\x1b[32m%s\x1b[0m', 'Final Answer:', action.answer);
361+
console.log('Final Answer:', action.answer);
385362
tokenTracker.printSummary();
386363
await storeContext(prompt, [allContext, allKeywords, allQuestions, allKnowledge], totalStep);
387364
return action;
@@ -413,8 +390,8 @@ The evaluator thinks your answer is bad because:
413390
${evaluation.reasoning}
414391
`);
415392
// store the bad context and reset the diary context
416-
const { response: errorAnalysis, tokens: analyzeTokens } = await analyzeSteps(diaryContext);
417-
totalTokens += analyzeTokens;
393+
const { response: errorAnalysis } = await analyzeSteps(diaryContext);
394+
418395
badContext.push(errorAnalysis);
419396
badAttempts++;
420397
diaryContext = [];
@@ -457,7 +434,6 @@ You will now figure out the answers to these sub-questions and see if they can h
457434
allQuestions.push(...newGapQuestions);
458435
gaps.push(question); // always keep the original question in the gaps
459436
} else {
460-
console.log('No new questions to ask');
461437
diaryContext.push(`
462438
At step ${step}, you took **reflect** and think about the knowledge gaps. You tried to break down the question "${currentQuestion}" into gap-questions like this: ${oldQuestions.join(', ')}
463439
But then you realized you have asked them before. You decided to to think out of the box or cut from a completely different angle.
@@ -471,19 +447,18 @@ But then you realized you have asked them before. You decided to to think out of
471447
}
472448
else if (action.action === 'search' && action.searchQuery) {
473449
// rewrite queries
474-
let { keywords: keywordsQueries, tokens: rewriteTokens } = await rewriteQuery(action.searchQuery);
475-
totalTokens += rewriteTokens;
450+
let { keywords: keywordsQueries } = await rewriteQuery(action.searchQuery);
451+
476452
const oldKeywords = keywordsQueries;
477453
// avoid exisitng searched queries
478454
if (allKeywords.length) {
479-
const { unique_queries: dedupedQueries, tokens: dedupTokens } = await dedupQueries(keywordsQueries, allKeywords);
480-
totalTokens += dedupTokens;
455+
const { unique_queries: dedupedQueries } = await dedupQueries(keywordsQueries, allKeywords);
481456
keywordsQueries = dedupedQueries;
482457
}
483458
if (keywordsQueries.length > 0) {
484459
const searchResults = [];
485460
for (const query of keywordsQueries) {
486-
console.log('Searching:', query);
461+
console.log(`Search query: ${query}`);
487462
const results = await search(query, {
488463
safeSearch: SafeSearchType.STRICT
489464
});
@@ -519,7 +494,7 @@ But then you realized you have already searched for these keywords before.
519494
You decided to think out of the box or cut from a completely different angle.
520495
`);
521496

522-
console.log('No new queries to search');
497+
523498
updateContext({
524499
step,
525500
...action,
@@ -551,7 +526,7 @@ You found some useful information on the web and add them to your knowledge for
551526
result: urlResults
552527
});
553528

554-
totalTokens += urlResults.reduce((sum, r) => sum + r.tokens, 0);
529+
555530
}
556531

557532
await storeContext(prompt, [allContext, allKeywords, allQuestions, allKnowledge], totalStep);
@@ -567,7 +542,7 @@ async function storeContext(prompt: string, memory: any[][], step: number) {
567542
await fs.writeFile('questions.json', JSON.stringify(questions, null, 2));
568543
await fs.writeFile('knowledge.json', JSON.stringify(knowledge, null, 2));
569544
} catch (error) {
570-
console.error('Failed to store context:', error);
545+
console.error('Context storage failed:', error);
571546
}
572547
}
573548

src/tools/dedup.ts

+2-7
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,7 @@ export async function dedupQueries(newQueries: string[], existingQueries: string
8383
const response = await result.response;
8484
const usage = response.usageMetadata;
8585
const json = JSON.parse(response.text()) as DedupResponse;
86-
console.debug('\x1b[36m%s\x1b[0m', 'Dedup intermediate result:', json);
87-
console.info('\x1b[32m%s\x1b[0m', 'Dedup final output:', json.unique_queries);
86+
console.log('Dedup:', json.unique_queries);
8887
const tokens = usage?.totalTokenCount || 0;
8988
tokenTracker.trackUsage('dedup', tokens);
9089
return { unique_queries: json.unique_queries, tokens };
@@ -99,12 +98,8 @@ async function main() {
9998
const newQueries = process.argv[2] ? JSON.parse(process.argv[2]) : [];
10099
const existingQueries = process.argv[3] ? JSON.parse(process.argv[3]) : [];
101100

102-
console.log('\nNew Queries (Set A):', newQueries);
103-
console.log('Existing Queries (Set B):', existingQueries);
104-
105101
try {
106-
const uniqueQueries = await dedupQueries(newQueries, existingQueries);
107-
console.log('Unique Queries:', uniqueQueries);
102+
await dedupQueries(newQueries, existingQueries);
108103
} catch (error) {
109104
console.error('Failed to deduplicate queries:', error);
110105
}

src/tools/error-analyzer.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,7 @@ export async function analyzeSteps(diaryContext: string[]): Promise<{ response:
124124
const response = await result.response;
125125
const usage = response.usageMetadata;
126126
const json = JSON.parse(response.text()) as EvaluationResponse;
127-
console.debug('\x1b[36m%s\x1b[0m', 'Error analysis intermediate result:', json);
128-
console.info('\x1b[32m%s\x1b[0m', 'Error analysis final output:', {
127+
console.log('Error analysis:', {
129128
is_valid: json.blame ? false : true,
130129
reason: json.blame || 'No issues found'
131130
});

src/tools/evaluator.ts

+2-7
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,7 @@ export async function evaluateAnswer(question: string, answer: string): Promise<
8181
const response = await result.response;
8282
const usage = response.usageMetadata;
8383
const json = JSON.parse(response.text()) as EvaluationResponse;
84-
console.debug('\x1b[36m%s\x1b[0m', 'Evaluation intermediate result:', json);
85-
console.info('\x1b[32m%s\x1b[0m', 'Evaluation final output:', {
84+
console.log('Evaluation:', {
8685
valid: json.is_valid_answer,
8786
reason: json.reasoning
8887
});
@@ -105,12 +104,8 @@ async function main() {
105104
process.exit(1);
106105
}
107106

108-
console.log('\nQuestion:', question);
109-
console.log('Answer:', answer);
110-
111107
try {
112-
const evaluation = await evaluateAnswer(question, answer);
113-
console.log('\nEvaluation Result:', evaluation);
108+
await evaluateAnswer(question, answer);
114109
} catch (error) {
115110
console.error('Failed to evaluate answer:', error);
116111
}

src/tools/query-rewriter.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,7 @@ export async function rewriteQuery(query: string): Promise<{ keywords: string[],
9191
const response = await result.response;
9292
const usage = response.usageMetadata;
9393
const json = JSON.parse(response.text()) as KeywordsResponse;
94-
console.debug('\x1b[36m%s\x1b[0m', 'Query rewriter intermediate result:', json);
95-
console.info('\x1b[32m%s\x1b[0m', 'Query rewriter final output:', json.keywords)
94+
console.log('Query rewriter:', json.keywords)
9695
const tokens = usage?.totalTokenCount || 0;
9796
tokenTracker.trackUsage('query-rewriter', tokens);
9897
return { keywords: json.keywords, tokens };
@@ -106,10 +105,8 @@ export async function rewriteQuery(query: string): Promise<{ keywords: string[],
106105
async function main() {
107106
const query = process.argv[2] || "";
108107

109-
console.log('\nOriginal Query:', query);
110108
try {
111-
const keywords = await rewriteQuery(query);
112-
console.log('Rewritten Keywords:', keywords);
109+
await rewriteQuery(query);
113110
} catch (error) {
114111
console.error('Failed to rewrite query:', error);
115112
}

src/tools/read.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ export function readUrl(url: string, token: string): Promise<{ response: ReadRes
3636
res.on('data', (chunk) => responseData += chunk);
3737
res.on('end', () => {
3838
const response = JSON.parse(responseData) as ReadResponse;
39-
console.debug('\x1b[36m%s\x1b[0m', 'Read intermediate result:', response);
40-
console.info('\x1b[32m%s\x1b[0m', 'Read final output:', {
39+
console.log('Read:', {
4140
title: response.data.title,
4241
url: response.data.url,
4342
tokens: response.data.usage.tokens

src/tools/search.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ export function search(query: string, token: string): Promise<{ response: Search
3333
res.on('end', () => {
3434
const response = JSON.parse(responseData) as SearchResponse;
3535
const totalTokens = response.data.reduce((sum, item) => sum + (item.usage?.tokens || 0), 0);
36-
console.debug('\x1b[36m%s\x1b[0m', 'Search intermediate result:', response);
37-
console.info('\x1b[32m%s\x1b[0m', 'Search final output:', response.data.map(item => ({
36+
console.log('Search:', response.data.map(item => ({
3837
title: item.title,
3938
url: item.url,
4039
tokens: item.usage.tokens

src/utils/token-tracker.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class TokenTracker extends EventEmitter {
2626

2727
printSummary() {
2828
const breakdown = this.getUsageBreakdown();
29-
console.info('\x1b[32m%s\x1b[0m', 'Token Usage Summary:', {
29+
console.log('Token Usage Summary:', {
3030
total: this.getTotalUsage(),
3131
breakdown
3232
});

0 commit comments

Comments
 (0)