Skip to content

Commit b57c4f8

Browse files
AryanBVclaude
andcommitted
Phase 4: Elimination logic bug fix + intelligent test suite
Bug Fix: mustBeInChapter precedence - Fixed critical bug where codes in correct chapter were excluded - "instant coffee" now correctly returns Ch.21 codes (2101.xx) - Root cause: description exclusions applied even to correct-chapter codes - Solution: mustBeInChapter takes precedence over description patterns - Added safety fallback to prevent all-candidates-eliminated scenarios Intelligent Test Suite: - Added 51 comprehensive test cases - Categories: critical regression, real-world, adversarial, typo handling - Test runner with detailed analysis and recommendations - Results tracking in JSON format Diagnostic Scripts: - diagnose-instant-coffee.ts: Debug instant coffee classification - trace-classification.ts: Trace full classification flow - test-instant-coffee-fix.ts: Verify elimination fix Test Results After Fix: - "instant coffee" → 2101.11.20 (Ch.21) ✅ - "roasted coffee beans" → 0901.21 (Ch.09) ✅ - "brake pads for cars" → 8708.30.00 (Ch.87) ✅ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent bb45ff8 commit b57c4f8

12 files changed

Lines changed: 7214 additions & 8 deletions

CLAUDE.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
HS Code Classifier - AI-powered export documentation assistant that classifies products into Harmonized System (HS) codes using a hybrid approach combining keyword matching, decision trees, and LLM reasoning. Targets Indian SME exporters, reducing classification time from 30 minutes to under 2 minutes.
8+
9+
## Build & Development Commands
10+
11+
### Backend (Express.js + TypeScript + Prisma)
12+
```bash
13+
cd backend
14+
npm install # Install dependencies
15+
npm run dev # Start dev server with hot reload (port 3001)
16+
npm run build # Compile TypeScript to dist/
17+
npm start # Run production build
18+
19+
# Database (Prisma + PostgreSQL/Supabase)
20+
npm run prisma:generate # Generate Prisma client after schema changes
21+
npm run prisma:push # Push schema changes to DB (development)
22+
npm run prisma:migrate # Run migrations (production)
23+
npm run prisma:studio # Open Prisma Studio GUI
24+
npm run prisma:seed # Seed database with HS codes
25+
```
26+
27+
### Frontend (Next.js 14 + React 18)
28+
```bash
29+
cd frontend
30+
npm install # Install dependencies
31+
npm run dev # Start dev server (port 3000)
32+
npm run build # Production build
33+
npm run lint # ESLint
34+
npm run type-check # TypeScript validation (tsc --noEmit)
35+
```
36+
37+
## Architecture
38+
39+
### Monorepo Structure
40+
- `backend/` - Express.js REST API with Prisma ORM
41+
- `frontend/` - Next.js 14 App Router with Tailwind/Shadcn UI
42+
- Separate package.json files; no root package manager
43+
44+
### Classification Pipeline
45+
The system uses a hybrid AI approach through multiple classification routes:
46+
1. **Keyword-based** (`/api/classify`) - PostgreSQL FTS + fuzzy matching
47+
2. **LLM-first** (`/api/classify-llm`) - GPT-4o primary classification
48+
3. **Conversational** (`/api/classify-conversational`) - Multi-turn Q&A refinement
49+
4. **Vector search** (`/api/vector-search`) - Semantic similarity matching
50+
51+
### Backend Service Layer (`backend/src/services/`)
52+
Core classification orchestration:
53+
- `ultimate-classifier.service.ts` - Main classifier entry point
54+
- `llm-first-classifier.service.ts` - LLM-driven classification
55+
- `llm-conversational-classifier.service.ts` - Multi-turn conversations
56+
- `conversational-classifier.service.ts` - Question flow management
57+
58+
Supporting services:
59+
- `keyword-matcher.service.ts` - Fuzzy keyword matching
60+
- `hierarchy-analyzer.service.ts` - HS code hierarchy traversal
61+
- `candidate-filter.service.ts` - Filters classification candidates
62+
- `decision-tree.service.ts` - Rule-based classification
63+
- `dynamic-question.service.ts` - Generates clarifying questions
64+
- `exclusion-classifier.service.ts` - Handles "Other" codes classification
65+
- `chapter-notes.service.ts` - Legal classification rules
66+
67+
### Database Schema (Prisma)
68+
Key models in `backend/prisma/schema.prisma`:
69+
- **HsCode** - Master codes with keywords, synonyms, descriptions
70+
- **HsCodeHierarchy** - Parent/child relationships for code tree
71+
- **ClassificationConversation** / **ConversationTurn** - Multi-turn state
72+
- **ClassificationFeedback** - User feedback for accuracy improvement
73+
74+
### Frontend Structure
75+
- `src/app/` - Next.js App Router pages
76+
- `src/components/classify/` - Chat interface, confidence meters, reasoning panels
77+
- `src/lib/api-client.ts` - Axios client for backend communication
78+
79+
## Environment Variables
80+
81+
### Backend (`backend/.env`)
82+
```
83+
DATABASE_URL=postgresql://... # Supabase PostgreSQL connection
84+
OPENAI_API_KEY=sk-proj-... # OpenAI API key for GPT-4o
85+
PORT=3001
86+
NODE_ENV=development
87+
FRONTEND_URL=http://localhost:3000 # CORS origin
88+
```
89+
90+
### Frontend (`frontend/.env.local`)
91+
```
92+
NEXT_PUBLIC_API_URL=http://localhost:3001
93+
```
94+
95+
## TypeScript Configuration
96+
Both projects use strict mode with `strictNullChecks`, `noImplicitAny`, and `strictFunctionTypes` enabled. Path alias `@/*` maps to `./src/*`.
97+
98+
## Deployment
99+
Production deployment configured for Railway.app. Backend serves API, frontend as separate service.

backend/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"prisma:studio": "prisma studio",
1313
"prisma:push": "prisma db push",
1414
"prisma:seed": "ts-node prisma/seed.ts",
15-
"test": "echo \"Error: no test specified\" && exit 1"
15+
"test": "echo \"Error: no test specified\" && exit 1",
16+
"test:intelligent": "npx ts-node src/tests/intelligent-test-suite/test-runner.ts"
1617
},
1718
"keywords": [
1819
"hs-code",
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { PrismaClient } from '@prisma/client';
2+
3+
const prisma = new PrismaClient();
4+
5+
async function diagnose() {
6+
console.log('=== DIAGNOSTIC: Instant Coffee Classification ===\n');
7+
8+
// 1. Check if Ch.21 codes exist
9+
console.log('1. Checking Chapter 21 codes in database...');
10+
const ch21Codes: any[] = await prisma.$queryRaw`
11+
SELECT code, description FROM "HsCode" WHERE code LIKE '21%' LIMIT 20
12+
`;
13+
console.log(` Found ${ch21Codes.length} Ch.21 codes`);
14+
if (ch21Codes.length > 0) {
15+
console.log(' Sample codes:');
16+
ch21Codes.slice(0, 5).forEach(c => console.log(` ${c.code}: ${c.description?.substring(0, 50)}...`));
17+
}
18+
19+
// 2. Check specifically for 2101 codes
20+
console.log('\n2. Checking for 2101.xx (coffee extracts) codes...');
21+
const coffeeExtractCodes: any[] = await prisma.$queryRaw`
22+
SELECT code, description, keywords FROM "HsCode" WHERE code LIKE '2101%'
23+
`;
24+
console.log(` Found ${coffeeExtractCodes.length} codes under 2101`);
25+
coffeeExtractCodes.forEach(c => {
26+
console.log(` ${c.code}: ${c.description}`);
27+
console.log(` Keywords: ${JSON.stringify(c.keywords) || 'NONE'}`);
28+
});
29+
30+
// 3. Search for "instant" keyword anywhere
31+
console.log('\n3. Searching for codes with "instant" in description or keywords...');
32+
const instantCodes: any[] = await prisma.$queryRaw`
33+
SELECT code, description FROM "HsCode"
34+
WHERE description ILIKE '%instant%'
35+
OR 'instant' = ANY(keywords)
36+
`;
37+
console.log(` Found ${instantCodes.length} codes with "instant"`);
38+
instantCodes.forEach(c => console.log(` ${c.code}: ${c.description?.substring(0, 60)}`));
39+
40+
// 4. Check for "coffee" in Ch.21
41+
console.log('\n4. Searching for "coffee" in Chapter 21...');
42+
const ch21Coffee: any[] = await prisma.$queryRaw`
43+
SELECT code, description FROM "HsCode"
44+
WHERE code LIKE '21%'
45+
AND (description ILIKE '%coffee%' OR 'coffee' = ANY(keywords))
46+
`;
47+
console.log(` Found ${ch21Coffee.length} codes`);
48+
ch21Coffee.forEach(c => console.log(` ${c.code}: ${c.description}`));
49+
50+
// 5. Check for "coffee" anywhere in database
51+
console.log('\n5. Searching for ALL codes with "coffee"...');
52+
const allCoffee: any[] = await prisma.$queryRaw`
53+
SELECT code, description FROM "HsCode"
54+
WHERE description ILIKE '%coffee%' OR 'coffee' = ANY(keywords)
55+
LIMIT 20
56+
`;
57+
console.log(` Found ${allCoffee.length} codes with "coffee"`);
58+
allCoffee.slice(0, 15).forEach(c => console.log(` ${c.code}: ${c.description?.substring(0, 60)}`));
59+
60+
// 6. Total database count
61+
console.log('\n6. Database statistics...');
62+
const totalCodes: any[] = await prisma.$queryRaw`SELECT COUNT(*) as count FROM "HsCode"`;
63+
console.log(` Total HS codes in database: ${totalCodes[0].count}`);
64+
65+
const ch09Count: any[] = await prisma.$queryRaw`SELECT COUNT(*) as count FROM "HsCode" WHERE code LIKE '09%'`;
66+
console.log(` Chapter 09 codes: ${ch09Count[0].count}`);
67+
68+
const ch21Count: any[] = await prisma.$queryRaw`SELECT COUNT(*) as count FROM "HsCode" WHERE code LIKE '21%'`;
69+
console.log(` Chapter 21 codes: ${ch21Count[0].count}`);
70+
71+
await prisma.$disconnect();
72+
}
73+
74+
diagnose().catch(console.error);

0 commit comments

Comments
 (0)