Skip to content

Commit

Permalink
Merge branch 'main' into zodAndFetch
Browse files Browse the repository at this point in the history
# Conflicts:
#	package-lock.json
  • Loading branch information
ahejlsberg committed Jan 22, 2024
2 parents a5fc03b + b1df394 commit 0d218de
Show file tree
Hide file tree
Showing 64 changed files with 4,909 additions and 3 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ npm install typechat
You can also build TypeChat from source:

```
git clone https://github.com/microsoft/TypeChat
cd TypeChat
npm run build
```

Expand Down
30 changes: 30 additions & 0 deletions examples/healthData/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Health Data Agent

This example requires GPT-4.

Demonstrates a ***strongly typed*** chat: a natural language interface for entering health information. You work with a *health data agent* to interactively enter your medications or conditions.

The Health Data Agent shows how strongly typed **agents with history** could interact with a user to collect information needed for one or more data types ("form filling").

## Target models

For best and consistent results, use **gpt-4**.

## Try the Health Data Agent

To run the Sentiment example, follow the instructions in the [examples README](../README.md#step-1-configure-your-development-environment).

## Usage

Example prompts can be found in [`input.txt`](./src/input.txt).

For example, given the following input statement:

**Input**:

```console
🤧> I am taking klaritin for my allergies

```

**Output**:
22 changes: 22 additions & 0 deletions examples/healthData/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "health-data",
"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"
}
}
68 changes: 68 additions & 0 deletions examples/healthData/src/healthDataSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// The following is a schema definition for enetring health data.

export interface HealthDataResponse {
// ONLY present when ALL required information is known.
// Otherwise, use 'message' to keep asking questions.
data?: HealthData;
// Use this to ask questions and give pertinent responses
message?: string;
// Use this parts of the user request not translated, off topic, etc.
notTranslated?: string;
}

export interface HealthData {
medication?: Medication[];
condition?: Condition[];
other?: OtherHealthData[];
}

// Meds, pills etc.
export interface Medication {
// Fix any spelling mistakes, especially phonetic spelling
name: string;
// E.g. 2 tablets, 1 cup. Required
dose: ApproxQuantity;
// E.g. twice a day. Required
frequency: ApproxQuantity;
// E.g. 50 mg. Required
strength: ApproxQuantity;
}

// Disease, Ailment, Injury, Sickness
export interface Condition {
// Fix any spelling mistakes, especially phonetic spelling
name: string;
// When the condition started.
startDate: ApproxDatetime;
// Always ask for current status of the condition
status: "active" | "recurrence" | "relapse" | "inactive" | "remission" | "resolved" | "unknown";
// If the condition was no longer active
endDate?: ApproxDatetime;
}

// Use for health data that match nothing else. E.g. immunization, blood prssure etc
export interface OtherHealthData {
text: string;
when?: ApproxDatetime;
}

export interface ApproxQuantity {
// Default: "unknown"
displayText: string;
// Only specify if precise quantities are available
quantity?: Quantity;
}

export interface ApproxDatetime {
// Default: "unknown"
displayText: string;
// If precise timestamp can be set
timestamp?: string;
}

export interface Quantity {
// Exact number
value: number;
// Units like mg, kg, cm, pounds, liter, ml, tablet, pill, cup, per-day, per-week, etc.
units: string;
}
63 changes: 63 additions & 0 deletions examples/healthData/src/input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#
# Conversations with a Health Data Agent
# For each conversation:
# You start with the first line
# Then type the next line in response
#

# ================
# USE GPT4
# ================
# Conversation:
i want to record my shingles
August 2016
It lasted 3 months
I also broke my foot
I broke it in high school
2001
The foot took a year to be ok

# Conversation:
klaritin
2 tablets 3 times a day
300 mg
actually that is 1 tablet
@clear

# Conversation:
klaritin
1 pill, morning and before bedtime
Can't remember
Actually, that is 3 tablets
500 mg
@clear

#Conversation
I am taking binadryl now
As needed. Groceery store strength
That is all I have
I also got allergies. Pollen
@clear

# Conversation:
Robotussin
1 cup
Daily, as needed
Robotussin with Codeine
Put down strength as I don't know
@clear

# Conversation:
Hey
Melatonin
1 3mg tablet every night
@clear

# Conversation:
I got the flu
Started 2 weeks ago
Its gone now. Only lasted about a week
I took some sudafed though
I took 2 sudafed twice a day. Regular strength
@clear

51 changes: 51 additions & 0 deletions examples/healthData/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import fs from "fs";
import path from "path";
import dotenv from "dotenv";
import { createHealthDataTranslator } from "./translator";
import { createLanguageModel, processRequests } from "typechat";
import { HealthDataResponse } from "./healthDataSchema";

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

const healthInstructions = `
Help me enter my health data step by step.
Ask specific questions to gather required and optional fields
I have not already providedStop asking if I don't know the answer
Automatically fix my spelling mistakes
My health data may be complex: always record and return ALL of it.
Always return a response:
- If you don't understand what I say, ask a question.
- At least respond with an OK message.
`;

const model = createLanguageModel(process.env);
const schema = fs.readFileSync(path.join(__dirname, "healthDataSchema.ts"), "utf8");
const translator = createHealthDataTranslator<HealthDataResponse>(model, schema, "HealthDataResponse",
healthInstructions);

// Process requests interactively or from the input file specified on the command line
processRequests("🤧> ", process.argv[2], async (request) => {
const response = await translator.translate(request);
if (!response.success) {
console.log("Translation Failed ❌");
console.log(`Context: ${response.message}`);
}
else {
const healthData = response.data;
console.log("Translation Succeeded! ✅\n");
console.log("JSON View");
console.log(JSON.stringify(healthData, undefined, 2));

const message = healthData.message;
const notTranslated = healthData.notTranslated;

if (message) {
console.log(`\n📝: ${message}`);
}

if (notTranslated) {
console.log(`\n🤔: I did not understand\n ${notTranslated}`)
}
}
});
76 changes: 76 additions & 0 deletions examples/healthData/src/translator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import {Result, TypeChatLanguageModel, createJsonTranslator, TypeChatJsonTranslator} from "typechat";

type ChatMessage = {
source: "system" | "user" | "assistant";
body: object;
};

export interface TranslatorWithHistory<T extends object> {
_chatHistory: ChatMessage[];
_maxPromptLength: number;
_additionalAgentInstructions: string;
_translator: TypeChatJsonTranslator<T>;
translate(request: string): Promise<Result<T>>;
}

export function createHealthDataTranslator<T extends object>(model: TypeChatLanguageModel, schema: string, typename: string, additionalAgentInstructions: string): TranslatorWithHistory<T> {
const _chatHistory: ChatMessage[] = [];
const _maxPromptLength = 2048;
const _additionalAgentInstructions = additionalAgentInstructions;

const _translator = createJsonTranslator<T>(model, schema, typename);
_translator.createRequestPrompt = createRequestPrompt;

const customtranslator: TranslatorWithHistory<T> = {
_chatHistory,
_maxPromptLength,
_additionalAgentInstructions,
_translator,
translate,
};

return customtranslator;

async function translate(request: string): Promise<Result<T>> {
const response = await _translator.translate(request);
if (response.success) {
_chatHistory.push({ source: "assistant", body: response.data });
}
return response;

}

function createRequestPrompt(intent: string): string {
// TODO: drop history entries if we exceed the max_prompt_length
const historyStr = JSON.stringify(_chatHistory, undefined, 2);

const now = new Date();

const prompt = `
user: You are a service that translates user requests into JSON objects of type "${typename}" according to the following TypeScript definitions:
'''
${schema}
'''
user:
Use precise date and times RELATIVE TO CURRENT DATE: ${now.toLocaleDateString()} CURRENT TIME: ${now.toTimeString().split(' ')[0]}
Also turn ranges like next week and next month into precise dates
user:
${_additionalAgentInstructions}
system:
IMPORTANT CONTEXT for the user request:
${historyStr}
user:
The following is a user request:
'''
${intent}
'''
The following is the user request translated into a JSON object with 2 spaces of indentation and no properties with the value undefined:
"""
`;
return prompt;
}
}
16 changes: 16 additions & 0 deletions examples/healthData/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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 0d218de

Please sign in to comment.