Skip to content

Classification example #164

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 6 commits into
base: main
Choose a base branch
from
Open
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
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -9,9 +9,9 @@ node_modules/

# Local development and debugging
.scratch/
.vscode
**/.vscode/*
#.vscode
#**/.vscode/*
Comment on lines +12 to +13
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want these removed - if you need an exception for the root .vscode/launch.json, I'd add that.

**/tsconfig.debug.json
!**/.vscode/launch.json
#!**/.vscode/launch.json
**/build.bat

257 changes: 257 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "sentiment-wsl",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need wsl-specific tasks here? People should just enter the directory from within WSL if that's what they want.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's makes it easier to debug from the root. You need a separate config for WSL if you are debugging in that environment using VSCODE.

Copy link
Member

@DanielRosenwasser DanielRosenwasser Jan 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, really? Even if you're using the WSL extension?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I tried this out locally - my experience is that externalTerminal didn't seem to work at all, regardless of whether I was running in WSL or not. Instead, only integratedTerminal worked across both.

So I think that matches what I recommended elsewhere - that all the WSL-oriented tasks should be the defaults, and there shouldn't be any sort of variant. If that's not matching your experience, we can jump on a call and run through it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, let’s sync on Monday. I want to check the configuration on windows.

"type": "node",
"request": "launch",
"program": "${workspaceFolder}/examples/sentiment/src/main.ts",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceFolder}/examples/sentiment",
"skipFiles": [
"<node_internals>/**"
],
"preLaunchTask": "build-sentiment",
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/examples/sentiment/dist/**/*.js"
],
"console": "integratedTerminal"
},
{
"name": "sentiment",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/examples/sentiment/src/main.ts",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceFolder}/examples/sentiment",
"skipFiles": [
"<node_internals>/**"
],
"preLaunchTask": "build-sentiment",
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/examples/sentiment/dist/**/*.js"
],
"console": "externalTerminal"
},
{
"name": "calendar-wsl",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/examples/calendar/src/main.ts",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceFolder}/examples/calendar",
"skipFiles": [
"<node_internals>/**"
],
"preLaunchTask": "build-calendar",
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/examples/calendar/dist/**/*.js"
],
"console": "integratedTerminal"
},
{
"name": "calendar",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/examples/calendar/src/main.ts",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceFolder}/examples/calendar",
"skipFiles": [
"<node_internals>/**"
],
"preLaunchTask": "build-calendar",
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/examples/calendar/dist/**/*.js"
],
"console": "externalTerminal"
},
{
"name": "math-wsl",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/examples/math/src/main.ts",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceFolder}/examples/math",
"skipFiles": [
"<node_internals>/**"
],
"preLaunchTask": "build-math",
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/examples/math/dist/**/*.js"
],
"console": "integratedTerminal"
},
{
"name": "math",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/examples/math/src/main.ts",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceFolder}/examples/math",
"skipFiles": [
"<node_internals>/**"
],
"preLaunchTask": "build-math",
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/examples/math/dist/**/*.js"
],
"console": "externalTerminal"
},
{
"name": "music-wsl",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/examples/music/src/main.ts",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceFolder}/examples/music",
"skipFiles": [
"<node_internals>/**"
],
"preLaunchTask": "build-music",
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/examples/music/dist/**/*.js"
],
"console": "integratedTerminal"
},
{
"name": "music",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/examples/music/src/main.ts",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceFolder}/examples/music",
"skipFiles": [
"<node_internals>/**"
],
"preLaunchTask": "build-music",
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/examples/music/dist/**/*.js"
],
"console": "externalTerminal"
},
{
"name": "restaurant-wsl",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/examples/restaurant/src/main.ts",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceFolder}/examples/restaurant",
"skipFiles": [
"<node_internals>/**"
],
"preLaunchTask": "build-restaurant",
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/examples/restaurant/dist/**/*.js"
],
"console": "integratedTerminal"
},
{
"name": "restaurant",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/examples/restaurant/src/main.ts",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceFolder}/examples/restaurant",
"skipFiles": [
"<node_internals>/**"
],
"preLaunchTask": "build-restaurant",
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/examples/restaurant/dist/**/*.js"
],
"console": "externalTerminal"
},
{
"name": "coffeeShop-wsl",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/examples/coffeeShop/src/main.ts",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceFolder}/examples/coffeeShop",
"skipFiles": [
"<node_internals>/**"
],
"preLaunchTask": "build-coffeeShop",
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/examples/coffeeShop/dist/**/*.js"
],
"console": "integratedTerminal"
},
{
"name": "coffeeShop",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/examples/coffeeShop/src/main.ts",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceFolder}/examples/coffeeShop",
"skipFiles": [
"<node_internals>/**"
],
"preLaunchTask": "build-coffeeShop",
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/examples/coffeeShop/dist/**/*.js"
],
"console": "externalTerminal"
},
{
"name": "classification-wsl",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/examples/classification/src/main.ts",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceFolder}/examples/classification",
"skipFiles": [
"<node_internals>/**"
],
"preLaunchTask": "build-classification",
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/examples/classification/dist/**/*.js"
],
"console": "integratedTerminal"
},
{
"name": "classification",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/examples/classification/src/main.ts",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceFolder}/examples/classification",
"skipFiles": [
"<node_internals>/**"
],
"preLaunchTask": "build-classification",
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/examples/classification/dist/**/*.js"
],
"console": "externalTerminal"
}
],
}
68 changes: 68 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build-sentiment",
"type": "npm",
"script": "build",
"path": "examples/sentiment",
"group": "build",
"problemMatcher": [],
"detail": "tsc -p src"
},
{
"label": "build-calendar",
"type": "npm",
"script": "build",
"path": "examples/calendar",
"group": "build",
"problemMatcher": [],
"detail": "tsc -p src"
},
{
"label": "build-math",
"type": "npm",
"script": "build",
"path": "examples/math",
"group": "build",
"problemMatcher": [],
"detail": "tsc -p src"
},
{
"label": "build-music",
"type": "npm",
"script": "build",
"path": "examples/music",
"group": "build",
"problemMatcher": [],
"detail": "tsc -p src"
},
{
"label": "build-restaurant",
"type": "npm",
"script": "build",
"path": "examples/restaurant",
"group": "build",
"problemMatcher": [],
"detail": "tsc -p src"
},
{
"label": "build-coffeeShop",
"type": "npm",
"script": "build",
"path": "examples/coffeeShop",
"group": "build",
"problemMatcher": [],
"detail": "tsc -p src"
},
{
"label": "build-classification",
"type": "npm",
"script": "build",
"path": "examples/classification",
"group": "build",
"problemMatcher": [],
"detail": "tsc -p src"
}
]
}
18 changes: 18 additions & 0 deletions examples/classification/.vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/dist/main.js",
"console": "externalTerminal"
}
]
}
22 changes: 22 additions & 0 deletions examples/classification/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "classification",
"version": "0.0.1",
"private": true,
"description": "",
"main": "dist/main.js",
"scripts": {
"build": "tsc -p src",
"postbuild": "copyfiles -u 1 src/**/*Schema.ts src/**/*.txt dist"
},
"author": "",
"license": "MIT",
"dependencies": {
"dotenv": "^16.3.1",
"typechat": "^0.0.10"
},
"devDependencies": {
"@types/node": "^20.3.1",
"copyfiles": "^2.4.1",
"typescript": "^5.1.3"
}
}
8 changes: 8 additions & 0 deletions examples/classification/src/classificationSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type Classification = {
name: string;
description: string;
};

export interface ClassificationResponse {
class: Classification;
}
4 changes: 4 additions & 0 deletions examples/classification/src/input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
I want to buy a Sherlock Holmes novel
I want to order a tall latte with extra foam
I want to buy Richard Feynman's book on physics
I want to buy Nurtec 75mg
43 changes: 43 additions & 0 deletions examples/classification/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import fs from "fs";
import path from "path";
import dotenv from "dotenv";
import { createLanguageModel, createJsonTranslator, processRequests } from "typechat";
import { Classification, ClassificationResponse } from "./classificationSchema";

const classes: Classification[] = [
{
"name": "CoffeeShop",
"description": "Order Coffee Drinks (Italian names included) and Baked Goods"
},
{
"name": "Mystery Bookshop",
"description": "A bookstore that specializes in mystery books"
},
{
"name": "Bookstore",
"description": "A bookstore that sells all kinds of books"
},
{
"name": "Drugstore",
"description": "A drugstore that sells health and beauty products"
}
];

// TODO: use local .env file.
dotenv.config({ path: path.join(__dirname, "../../../.env") });

const model = createLanguageModel(process.env);
const schema = fs.readFileSync(path.join(__dirname, "classificationSchema.ts"), "utf8");
const translator = createJsonTranslator<ClassificationResponse>(model, schema, "ClassificationResponse");

// Process requests interactively or from the input file specified on the command line
processRequests("🤗> ", process.argv[2], async (request) => {
const initClasses: string = JSON.stringify(classes, undefined);
const fullRequest: string = `Classify "${request}" using the following classification table:\n${initClasses}\n`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few things about the classification example feels off.

First, this seems far too similar to the sentiment example. The biggest difference is the custom prompt - but it very well may be the case that a custom prompt needs a custom translator.

Then there is the fact that we're effectively doing a look-up - but if you have that information, then why do you need things like a description anyhow?

If you have a fixed set of items, you could describe that directly in the schema, right? Or you would just use embeddings to fetch the most similar one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way I look at it is the sentiment example is a special case of the classifier example. Another way of doing this would be to use a custom translator but we do not need it as the translate method has a promptPreamble parameter.

Yes, I could also describe it in the schema as well but that idea is to keep the data (classes) separate from the schema. What do you think?

const response = await translator.translate(request, [{role: "assistant", content:`${fullRequest}`}]);
if (!response.success) {
console.log(response.message);
return;
}
console.log(`The classification is ${response.data.class.name}`);
});
16 changes: 16 additions & 0 deletions examples/classification/src/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "es2021",
"lib": ["es2021"],
"module": "node16",
"types": ["node"],
"outDir": "../dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"exactOptionalPropertyTypes": true,
"inlineSourceMap": true
}
}
23 changes: 20 additions & 3 deletions package-lock.json