Skip to content

feat: porting over python to typescript #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"semi": true,
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"bracketSpacing": true,
"arrowParens": "avoid"
}
Binary file added __pycache__/analytics.cpython-311.pyc
Binary file not shown.
Binary file added __pycache__/analytics.cpython-312.pyc
Binary file not shown.
Binary file added __pycache__/client.cpython-311.pyc
Binary file not shown.
Binary file added __pycache__/client.cpython-312.pyc
Binary file not shown.
Binary file added __pycache__/complexity.cpython-311.pyc
Binary file not shown.
Binary file added __pycache__/complexity.cpython-312.pyc
Binary file not shown.
Binary file added __pycache__/examples.cpython-311.pyc
Binary file not shown.
Binary file added __pycache__/examples.cpython-312.pyc
Binary file not shown.
Binary file added __pycache__/format.cpython-311.pyc
Binary file not shown.
Binary file added __pycache__/format.cpython-312.pyc
Binary file not shown.
Binary file added __pycache__/reasoning.cpython-311.pyc
Binary file not shown.
Binary file added __pycache__/reasoning.cpython-312.pyc
Binary file not shown.
Binary file added cod_analytics.db
Binary file not shown.
Binary file added cod_examples.db
Binary file not shown.
21 changes: 21 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
version: "3.8"
services:
app:
build: .
ports:
- "3000:3000"
environment:
- COD_DB_URL=postgresql://postgres:postgres@db:5432/cod_db
depends_on:
- db
db:
image: postgres:15-alpine
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=cod_db
volumes:
- postgres_data:/var/lib/postgresql/data

volumes:
postgres_data:
68 changes: 68 additions & 0 deletions eslint.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import eslint from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import prettierPlugin from 'eslint-plugin-prettier';
import prettier from 'eslint-config-prettier';
import globals from 'globals';

const config = [
{
// Global configuration
ignores: ['dist/**', 'node_modules/**', 'coverage/**'],
},
{
// TypeScript files configuration
files: ['**/*.ts'],
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 'latest' as const,
sourceType: 'module' as const,
},
globals: {
...globals.node,
...globals.es2021,
},
},
plugins: {
'@typescript-eslint': eslint,
prettier: prettierPlugin,
},
rules: {
// Prettier
'prettier/prettier': 'error',

// TypeScript
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],

// General
'no-console': ['warn', { allow: ['warn', 'error'] }],

// Include recommended rules
...eslint.configs.recommended.rules,
...prettier.rules,
},
},
{
// JavaScript files configuration (for config files)
files: ['**/*.js', '**/*.cjs', '**/*.mjs'],
languageOptions: {
ecmaVersion: 'latest' as const,
sourceType: 'module' as const,
globals: {
...globals.node,
...globals.es2021,
},
},
plugins: {
prettier: prettierPlugin,
},
rules: {
'prettier/prettier': 'error',
...prettier.rules,
},
},
] as const;

export default config;
8 changes: 8 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/__tests__/**/*.test.ts'],
moduleFileExtensions: ['ts', 'js'],
transform: { '^.+\.ts$': 'ts-jest' },
globals: { 'ts-jest': { tsconfig: 'tsconfig.json' } },
};
1 change: 1 addition & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
datasource db { provider = "postgresql" url = env("COD_DB_URL") } generator client { provider = "prisma-client-js" } model InferenceRecord { id Int @id @default(autoincrement()) timestamp DateTime @default(now()) problemId String problemText String domain String approach String wordLimit Int tokensUsed Int executionTimeMs Float reasoningSteps String answer String expectedAnswer String? isCorrect Int? metaData Json? } model Example { id Int @id @default(autoincrement()) problem String @db.Text reasoning String @db.Text answer String domain String approach String metaData Json? }
49 changes: 49 additions & 0 deletions src/typescript/__tests__/client.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { ChainOfDraftClient } from '../client';
import { AnalyticsService } from '../analytics';
import { ComplexityEstimator } from '../complexity';
describe('ChainOfDraftClient', () => {
let client: ChainOfDraftClient;
beforeEach(() => {
client = new ChainOfDraftClient();
});
test('should solve a simple math problem', async () => {
const result = await client.completions('test-model', 'What is 2+2?', { domain: 'math' });
expect(result.choices[0].text).toBeDefined();
});
test('should handle chat completions', async () => {
const result = await client.chat('test-model', [{ role: 'user', content: 'What is 2+2?' }], {
domain: 'math',
});
expect(result.choices[0].message.content).toBeDefined();
});
});
describe('ComplexityEstimator', () => {
let estimator: ComplexityEstimator;
beforeEach(() => {
estimator = new ComplexityEstimator();
});
test('should estimate problem complexity', async () => {
const complexity = await estimator.estimateComplexity('What is 2+2?', 'math');
expect(complexity).toBeGreaterThanOrEqual(3);
expect(complexity).toBeLessThanOrEqual(10);
});
});
describe('AnalyticsService', () => {
let analytics: AnalyticsService;
beforeEach(() => {
analytics = new AnalyticsService('postgresql://test:test@localhost:5432/test_db');
});
test('should record inference', async () => {
const id = await analytics.recordInference(
'What is 2+2?',
'math',
'CoD',
5,
100,
500,
'Step 1: Add numbers',
'4'
);
expect(id).toBeDefined();
});
});
157 changes: 157 additions & 0 deletions src/typescript/analytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { PrismaClient } from '@prisma/client';
// interface InferenceRecord {
// id: number;
// timestamp: Date;
// problemId: string;
// problemText: string;
// domain: string;
// approach: string;
// wordLimit: number;
// tokensUsed: number;
// executionTimeMs: number;
// reasoningSteps: string;
// answer: string;
// expectedAnswer?: string;
// isCorrect?: number;
// metaData?: any;
// }
interface PerformanceStats {
domain: string;
approach: string;
avgTokens: number;
avgTimeMs: number;
accuracy: number | null;
count: number;
}
interface TokenReductionStats {
domain: string;
codAvgTokens: number;
cotAvgTokens: number;
reductionPercentage: number;
}
interface AccuracyComparison {
domain: string;
codAccuracy: number | null;
cotAccuracy: number | null;
accuracyDifference: number | null;
}
export class AnalyticsService {
private prisma: PrismaClient;
constructor(dbUrl?: string) {
this.prisma = new PrismaClient({
datasources: {
db: { url: dbUrl || process.env.COD_DB_URL || 'postgresql://localhost:5432/cod_analytics' },
},
});
}
async recordInference(
problem: string,
domain: string,
approach: string,
wordLimit: number,
tokensUsed: number,
executionTime: number,
reasoning: string,
answer: string,
expectedAnswer?: string,
metadata?: any
): Promise<number> {
const problemId = Math.abs(
problem.split('').reduce((acc, char) => {
return (acc * 31 + char.charCodeAt(0)) >>> 0;
}, 0) %
10 ** 10
).toString();
const record = await this.prisma.inferenceRecord.create({
data: {
problemId,
problemText: problem,
domain,
approach,
wordLimit,
tokensUsed,
executionTimeMs: executionTime,
reasoningSteps: reasoning,
answer,
expectedAnswer,
isCorrect: expectedAnswer ? this.checkCorrectness(answer, expectedAnswer) : null,
metaData: metadata,
},
});
return record.id;
}
private checkCorrectness(answer: string, expectedAnswer: string): number | null {
if (!answer || !expectedAnswer) {
return null;
}
return answer.trim().toLowerCase() === expectedAnswer.trim().toLowerCase() ? 1 : 0;
}
async getPerformanceByDomain(domain?: string): Promise<PerformanceStats[]> {
const records = await this.prisma.inferenceRecord.groupBy({
by: ['domain', 'approach'],
_avg: { tokensUsed: true, executionTimeMs: true, isCorrect: true },
_count: { id: true },
where: domain ? { domain } : undefined,
});
return records.map(r => ({
domain: r.domain,
approach: r.approach,
avgTokens: r._avg.tokensUsed || 0,
avgTimeMs: r._avg.executionTimeMs || 0,
accuracy: r._avg.isCorrect,
count: r._count.id,
}));
}
async getTokenReductionStats(): Promise<TokenReductionStats[]> {
const domains = await this.prisma.inferenceRecord.findMany({
distinct: ['domain'],
select: { domain: true },
});
const results: TokenReductionStats[] = [];
for (const { domain } of domains) {
const [codAvg, cotAvg] = await Promise.all([
this.prisma.inferenceRecord.aggregate({
where: { domain, approach: 'CoD' },
_avg: { tokensUsed: true },
}),
this.prisma.inferenceRecord.aggregate({
where: { domain, approach: 'CoT' },
_avg: { tokensUsed: true },
}),
]);
const codAvgTokens = codAvg._avg.tokensUsed || 0;
const cotAvgTokens = cotAvg._avg.tokensUsed || 0;
const reductionPercentage = cotAvgTokens > 0 ? (1 - codAvgTokens / cotAvgTokens) * 100 : 0;
results.push({ domain, codAvgTokens, cotAvgTokens, reductionPercentage });
}
return results;
}
async getAccuracyComparison(): Promise<AccuracyComparison[]> {
const domains = await this.prisma.inferenceRecord.findMany({
distinct: ['domain'],
select: { domain: true },
});
const results: AccuracyComparison[] = [];
for (const { domain } of domains) {
const [codAccuracy, cotAccuracy] = await Promise.all([
this.prisma.inferenceRecord.aggregate({
where: { domain, approach: 'CoD', isCorrect: { not: null } },
_avg: { isCorrect: true },
}),
this.prisma.inferenceRecord.aggregate({
where: { domain, approach: 'CoT', isCorrect: { not: null } },
_avg: { isCorrect: true },
}),
]);
const codAcc = codAccuracy._avg.isCorrect;
const cotAcc = cotAccuracy._avg.isCorrect;
results.push({
domain,
codAccuracy: codAcc,
cotAccuracy: cotAcc,
accuracyDifference: codAcc !== null && cotAcc !== null ? codAcc - cotAcc : null,
});
}
return results;
}
}
Loading