Showing preview only (567K chars total). Download the full file or copy to clipboard to get everything.
Repository: microsoft/TypeChat
Branch: main
Commit: 7ee5629065a0
Files: 246
Total size: 509.3 KB
Directory structure:
gitextract_lrpyygt1/
├── .devcontainer/
│ └── devcontainer.json
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ ├── ci.js.yml
│ ├── ci.python.yml
│ └── github-pages.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── SECURITY.md
├── SUPPORT.md
├── TypeChat.code-workspace
├── dotnet/
│ └── README.md
├── python/
│ ├── .gitignore
│ ├── LICENSE
│ ├── README.md
│ ├── examples/
│ │ ├── README.md
│ │ ├── calendar/
│ │ │ ├── README.md
│ │ │ ├── demo.py
│ │ │ ├── input.txt
│ │ │ └── schema.py
│ │ ├── coffeeShop/
│ │ │ ├── README.md
│ │ │ ├── demo.py
│ │ │ ├── input.txt
│ │ │ ├── input2.txt
│ │ │ └── schema.py
│ │ ├── healthData/
│ │ │ ├── README.md
│ │ │ ├── demo.py
│ │ │ ├── input.txt
│ │ │ ├── schema.py
│ │ │ └── translator.py
│ │ ├── math/
│ │ │ ├── README.md
│ │ │ ├── demo.py
│ │ │ ├── input.txt
│ │ │ ├── program.py
│ │ │ ├── schema.py
│ │ │ └── schemaV2.py
│ │ ├── multiSchema/
│ │ │ ├── README.md
│ │ │ ├── agents.py
│ │ │ ├── demo.py
│ │ │ ├── input.txt
│ │ │ └── router.py
│ │ ├── music/
│ │ │ ├── README.md
│ │ │ ├── client.py
│ │ │ ├── demo.py
│ │ │ ├── input.txt
│ │ │ ├── schema.py
│ │ │ └── spotipyWrapper.py
│ │ ├── restaurant/
│ │ │ ├── README.md
│ │ │ ├── demo.py
│ │ │ ├── input.txt
│ │ │ └── schema.py
│ │ └── sentiment/
│ │ ├── README.md
│ │ ├── demo.py
│ │ ├── input.txt
│ │ └── schema.py
│ ├── notebooks/
│ │ ├── calendar.ipynb
│ │ ├── coffeeShop.ipynb
│ │ ├── healthData.ipynb
│ │ ├── math.ipynb
│ │ ├── music.ipynb
│ │ ├── restaurant.ipynb
│ │ └── sentiment.ipynb
│ ├── package.json
│ ├── pyproject.toml
│ ├── pyrightconfig.json
│ ├── src/
│ │ └── typechat/
│ │ ├── __about__.py
│ │ ├── __init__.py
│ │ ├── _internal/
│ │ │ ├── __init__.py
│ │ │ ├── interactive.py
│ │ │ ├── model.py
│ │ │ ├── result.py
│ │ │ ├── translator.py
│ │ │ ├── ts_conversion/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── python_type_to_ts_nodes.py
│ │ │ │ ├── ts_node_to_string.py
│ │ │ │ └── ts_type_nodes.py
│ │ │ └── validator.py
│ │ └── py.typed
│ └── tests/
│ ├── __init__.py
│ ├── __py3.11_snapshots__/
│ │ ├── test_conflicting_names_1/
│ │ │ └── test_conflicting_names_1.schema.d.ts
│ │ └── test_hello_world/
│ │ └── test_generic_alias1.schema.d.ts
│ ├── __py3.12+_snapshots__/
│ │ ├── test_generic_alias_3/
│ │ │ └── test_generic_alias3.schema.d.ts
│ │ ├── test_generic_alias_4/
│ │ │ └── test_generic_alias4.schema.d.ts
│ │ └── test_type_alias_syntax/
│ │ └── test_type_alias_union1.schema.d.ts
│ ├── __py3.12_snapshots__/
│ │ ├── test_conflicting_names_1/
│ │ │ └── test_conflicting_names_1.schema.d.ts
│ │ └── test_hello_world/
│ │ └── test_generic_alias1.schema.d.ts
│ ├── __py3.13_snapshots__/
│ │ ├── test_conflicting_names_1/
│ │ │ └── test_conflicting_names_1.schema.d.ts
│ │ └── test_hello_world/
│ │ └── test_generic_alias1.schema.d.ts
│ ├── __py3.14_snapshots__/
│ │ ├── test_conflicting_names_1/
│ │ │ └── test_conflicting_names_1.schema.d.ts
│ │ └── test_hello_world/
│ │ └── test_generic_alias1.schema.d.ts
│ ├── __snapshots__/
│ │ ├── test_coffeeshop/
│ │ │ └── test_coffeeshop_schema.schema.d.ts
│ │ ├── test_dataclasses/
│ │ │ └── test_data_classes.schema.d.ts
│ │ ├── test_generic_alias_1/
│ │ │ └── test_generic_alias1.schema.d.ts
│ │ ├── test_generic_alias_2/
│ │ │ └── test_generic_alias2.schema.d.ts
│ │ ├── test_translator.ambr
│ │ ├── test_tuple_errors_1/
│ │ │ └── test_tuples_2.schema.d.ts
│ │ └── test_tuples_1/
│ │ └── test_tuples_1.schema.d.ts
│ ├── coffeeshop_deprecated.py
│ ├── test_coffeeshop.py
│ ├── test_conflicting_names_1.py
│ ├── test_dataclasses.py
│ ├── test_generic_alias_1.py
│ ├── test_generic_alias_2.py
│ ├── test_generic_alias_3.py
│ ├── test_generic_alias_4.py
│ ├── test_hello_world.py
│ ├── test_translator.py
│ ├── test_tuple_errors_1.py
│ ├── test_tuples_1.py
│ ├── test_type_alias_syntax.py
│ ├── test_validator.py
│ └── utilities.py
├── site/
│ ├── .eleventy.js
│ ├── .gitignore
│ ├── jsconfig.json
│ ├── package.json
│ └── src/
│ ├── _data/
│ │ ├── docsTOC.json
│ │ └── headernav.json
│ ├── _includes/
│ │ ├── base.njk
│ │ ├── blog.njk
│ │ ├── doc-page.njk
│ │ ├── docs.njk
│ │ ├── footer.njk
│ │ └── header-prologue.njk
│ ├── blog/
│ │ ├── announcing-typechat-0-1-0.md
│ │ ├── index.njk
│ │ └── introducing-typechat.md
│ ├── css/
│ │ ├── noscript-styles.css
│ │ └── styles.css
│ ├── docs/
│ │ ├── examples.md
│ │ ├── faq.md
│ │ ├── index.njk
│ │ ├── introduction.md
│ │ ├── python/
│ │ │ └── basic-usage.md
│ │ ├── techniques.md
│ │ └── typescript/
│ │ └── basic-usage.md
│ ├── index.njk
│ └── js/
│ └── interactivity.js
└── typescript/
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── examples/
│ ├── README.md
│ ├── calendar/
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── calendarActionsSchema.ts
│ │ ├── expectedOutput.txt
│ │ ├── input.txt
│ │ ├── main.ts
│ │ └── tsconfig.json
│ ├── coffeeShop/
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── coffeeShopSchema.ts
│ │ ├── input.txt
│ │ ├── input2.txt
│ │ ├── main.ts
│ │ └── tsconfig.json
│ ├── coffeeShop-zod/
│ │ ├── README.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── coffeeShopSchema.ts
│ │ ├── input.txt
│ │ ├── input2.txt
│ │ ├── main.ts
│ │ └── tsconfig.json
│ ├── crossword/
│ │ ├── README.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── crosswordSchema.ts
│ │ ├── input.txt
│ │ ├── main.ts
│ │ ├── translator.ts
│ │ └── tsconfig.json
│ ├── healthData/
│ │ ├── README.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── healthDataSchema.ts
│ │ ├── input.txt
│ │ ├── main.ts
│ │ ├── translator.ts
│ │ └── tsconfig.json
│ ├── math/
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── input.txt
│ │ ├── main.ts
│ │ ├── mathSchema.ts
│ │ └── tsconfig.json
│ ├── multiSchema/
│ │ ├── README.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── agent.ts
│ │ ├── classificationSchema.ts
│ │ ├── input.txt
│ │ ├── main.ts
│ │ ├── router.ts
│ │ └── tsconfig.json
│ ├── music/
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── migrations.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── authz.ts
│ │ ├── callback.html
│ │ ├── chatifyActionsSchema.ts
│ │ ├── dbInterface.ts
│ │ ├── endpoints.ts
│ │ ├── input.txt
│ │ ├── localParser.ts
│ │ ├── main.ts
│ │ ├── playback.ts
│ │ ├── service.ts
│ │ ├── trackCollections.ts
│ │ ├── trackFilter.ts
│ │ └── tsconfig.json
│ ├── restaurant/
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── foodOrderViewSchema.ts
│ │ ├── input.txt
│ │ ├── main.ts
│ │ └── tsconfig.json
│ ├── sentiment/
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── input.txt
│ │ ├── main.ts
│ │ ├── sentimentSchema.ts
│ │ └── tsconfig.json
│ └── sentiment-zod/
│ ├── README.md
│ ├── package.json
│ └── src/
│ ├── input.txt
│ ├── main.ts
│ ├── sentimentSchema.ts
│ └── tsconfig.json
├── package.json
└── src/
├── index.ts
├── interactive/
│ ├── index.ts
│ └── interactive.ts
├── model.ts
├── result.ts
├── ts/
│ ├── index.ts
│ ├── program.ts
│ └── validate.ts
├── tsconfig.json
├── typechat.ts
└── zod/
├── index.ts
└── validate.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .devcontainer/devcontainer.json
================================================
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/ubuntu
{
"name": "TypeChat Development (Python and TypeScript)",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/python:1-3.12",
// Features to add to the dev container. More info: https://containers.dev/features.
"features": {
"ghcr.io/devcontainers/features/node:1": {
"nodeGypDependencies": true,
"version": "lts",
"nvmVersion": "latest"
},
"ghcr.io/devcontainers-contrib/features/hatch:2": {
"version": "latest"
}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [7860, 7861, 7862, 7863, 7864, 7865, 7866, 7867, 7868, 7869, 7870],
// Configure tool-specific properties.
"customizations": {
"vscode": {
"extensions": [
"ms-python.black-formatter"
],
"settings": {
// Force the editor to pick up on the right environment and interpreter.
"python.defaultInterpreterPath": "/workspaces/TypeChat/.venv",
// Respect the paths of the interpreter.
"python.analysis.autoSearchPaths": false
}
}
},
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": {
"site - npm": "cd site; npm ci",
"typescript - npm": "cd typescript; npm ci",
"python - hatch": "cd python; hatch env create",
"python - npm": "cd python; npm ci"
}
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
================================================
FILE: .github/dependabot.yml
================================================
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for more information:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
# https://containers.dev/guide/dependabot
version: 2
updates:
- package-ecosystem: "devcontainers"
directory: "/"
schedule:
interval: weekly
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: weekly
================================================
FILE: .github/workflows/ci.js.yml
================================================
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
name: Node.js CI
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
permissions:
contents: read
# Ensure scripts are run with pipefail. See:
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#exit-codes-and-error-action-preference
defaults:
run:
shell: bash
working-directory: typescript
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x, 22.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v6
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
cache: "npm"
cache-dependency-path: "typescript/package-lock.json"
- run: npm ci
- run: npm run build-all
================================================
FILE: .github/workflows/ci.python.yml
================================================
name: Python CI
on:
push:
branches:
- main
pull_request:
branches:
- main
defaults:
run:
shell: bash
working-directory: ./python
jobs:
pyright:
permissions:
contents: read
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
python-version:
- '3.11'
- '3.12'
- '3.13'
- '3.14'
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install Hatch
run: |
python -m pip install --upgrade pip
pip install hatch "virtualenv<20.29"
- name: Set up Hatch Environment
run: |
hatch env create
HATCH_ENV=$(hatch env find)
echo $HATCH_ENV
echo "$HATCH_ENV/bin" >> $GITHUB_PATH
- name: Get Pyright Version
id: pyright-version
run: |
PYRIGHT_VERSION=$(jq -r '.devDependencies.pyright' < package.json)
echo $PYRIGHT_VERSION
echo "version=$PYRIGHT_VERSION" >> $GITHUB_OUTPUT
- name: Run pyright ${{ steps.pyright-version.outputs.version }}
uses: jakebailey/pyright-action@v3
with:
version: ${{ steps.pyright-version.outputs.version }}
python-version: ${{ matrix.python-version}}
annotate: ${{ matrix.python-version == '3.11' && matrix.os == 'ubuntu-latest' }} # Only let one build post comments.
working-directory: ./python
- name: Test with Pytest
run: |
pytest -vv
================================================
FILE: .github/workflows/github-pages.yml
================================================
name: Deploy to GitHub Pages
on:
push:
branches:
- main
permissions:
contents: read
id-token: write
pages: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: 18.x
cache: 'npm'
cache-dependency-path: "site/package-lock.json"
- name: Build site
run: |
cd site
echo "Building the site"
npm ci
npm run build
- name : Upload artifact
uses: actions/upload-pages-artifact@v4
with:
name: github-pages
path: site/_site
- name: Deploy to GitHub Pages from artifacts
uses: actions/deploy-pages@v4
================================================
FILE: .gitignore
================================================
build/
dist/
out/
node_modules/
.venv/
.env*
*.map
*.out.txt
*.bat
# Local development and debugging
.scratch/
**/.vscode/*
**/tsconfig.debug.json
!**/.vscode/launch.json
**/build.bat
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Microsoft Open Source Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
Resources:
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) Microsoft Corporation.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
================================================
FILE: README.md
================================================
# TypeChat
TypeChat is a library that makes it easy to build natural language interfaces using types.
Building natural language interfaces has traditionally been difficult. These apps often relied on complex decision trees to determine intent and collect the required inputs to take action. Large language models (LLMs) have made this easier by enabling us to take natural language input from a user and match to intent. This has introduced its own challenges including the need to constrain the model's reply for safety, structure responses from the model for further processing, and ensuring that the reply from the model is valid. Prompt engineering aims to solve these problems, but comes with a steep learning curve and increased fragility as the prompt increases in size.
TypeChat replaces _prompt engineering_ with _schema engineering_.
Simply define types that represent the intents supported in your natural language application. That could be as simple as an interface for categorizing sentiment or more complex examples like types for a shopping cart or music application. For example, to add additional intents to a schema, a developer can add additional types into a discriminated union. To make schemas hierarchical, a developer can use a "meta-schema" to choose one or more sub-schemas based on user input.
After defining your types, TypeChat takes care of the rest by:
1. Constructing a prompt to the LLM using types.
2. Validating the LLM response conforms to the schema. If the validation fails, repair the non-conforming output through further language model interaction.
3. Summarizing succinctly (without use of a LLM) the instance and confirm that it aligns with user intent.
Types are all you need!
# Getting Started
Install TypeChat for TypeScript/JavaScript:
```
npm install typechat
```
<!--
Or from PyPI:
...
Or NuGet
...
-->
You can also work with TypeChat from source for:
* [Python](./python/README.md)
* [TypeScript](./typescript/README.md)
* [C#/.NET](https://github.com/microsoft/TypeChat.net)
To see TypeChat in action, we recommend exploring the [TypeChat example projects](./typescript/examples). You can try them on your local machine or in a GitHub Codespace.
To learn more about TypeChat, visit the [documentation](https://microsoft.github.io/TypeChat) which includes more information on TypeChat and how to get started.
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## Trademarks
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft
trademarks or logos is subject to and must follow
[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
Any use of third-party trademarks or logos are subject to those third-party's policies.
================================================
FILE: SECURITY.md
================================================
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.8 BLOCK -->
## Security
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
## Reporting Security Issues
**Please do not report security vulnerabilities through public GitHub issues.**
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
* Full paths of source file(s) related to the manifestation of the issue
* The location of the affected source code (tag/branch/commit or direct URL)
* Any special configuration required to reproduce the issue
* Step-by-step instructions to reproduce the issue
* Proof-of-concept or exploit code (if possible)
* Impact of the issue, including how an attacker might exploit the issue
This information will help us triage your report more quickly.
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
## Preferred Languages
We prefer all communications to be in English.
## Policy
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
<!-- END MICROSOFT SECURITY.MD BLOCK -->
================================================
FILE: SUPPORT.md
================================================
# Support
## How to file issues and get help
This project uses GitHub Issues to track bugs and feature requests.
Please search the existing issues before filing new issues to avoid duplicates.
For new issues, file your bug or feature request as a new issue.
For help and questions about using this project, please either use the project's GitHub Discussions area or Stack Overflow.
## Microsoft Support Policy
Support for this project is limited to the resources listed above.
================================================
FILE: TypeChat.code-workspace
================================================
{
"folders": [
{
"name": "TypeChat Root",
"path": "./"
},
{
"name": "Python",
"path": "./python"
},
{
"name": "TypeScript",
"path": "./typescript"
},
],
"settings": {
}
}
================================================
FILE: dotnet/README.md
================================================
# TypeChat for .NET
TypeChat in .NET and C# is currently available on a separate [TypeChat.NET repository](https://github.com/microsoft/TypeChat.net).
================================================
FILE: python/.gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
node_modules
================================================
FILE: python/LICENSE
================================================
MIT License
Copyright (c) Microsoft Corporation.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
================================================
FILE: python/README.md
================================================
# TypeChat
TypeChat is a library that makes it easy to build natural language interfaces using types.
Building natural language interfaces has traditionally been difficult.
These apps often relied on complex decision trees to determine intent and collect the required inputs to take action.
Large language models (LLMs) have made this easier by enabling us to take natural language input from a user and match to intent.
This has introduced its own challenges, including the need to constrain the model's reply for safety,
structure responses from the model for further processing, and ensuring that the reply from the model is valid.
Prompt engineering aims to solve these problems, but comes with a steep learning curve and increased fragility as the prompt increases in size.
TypeChat replaces _prompt engineering_ with _schema engineering_.
Simply define types that represent the intents supported in your natural language application.
That could be as simple as an interface for categorizing sentiment or more complex examples like types for a shopping cart or music application.
For example, to add additional intents to a schema, a developer can add additional types into a discriminated union.
To make schemas hierarchical, a developer can use a "meta-schema" to choose one or more sub-schemas based on user input.
After defining your types, TypeChat takes care of the rest by:
1. Constructing a prompt to the LLM using types.
2. Validating the LLM response conforms to the schema. If the validation fails, repair the non-conforming output through further language model interaction.
3. Summarizing succinctly (without use of a LLM) the instance and confirm that it aligns with user intent.
Types are all you need!
## Getting Started
Install TypeChat:
```sh
pip install typechat
```
You can also develop TypeChat from source, which needs [Python >=3.11](https://www.python.org/downloads/),
[hatch](https://hatch.pypa.io/1.6/install/), and [Node.js >=20](https://nodejs.org/en/download):
```sh
git clone https://github.com/microsoft/TypeChat
cd TypeChat/python
hatch shell
npm ci
```
To see TypeChat in action, we recommend exploring the
[TypeChat example projects](https://github.com/microsoft/TypeChat/tree/main/python/examples).
You can try them on your local machine or in a GitHub Codespace.
To learn more about TypeChat, visit the
[documentation](https://microsoft.github.io/TypeChat/docs/python/basic-usage/)
which includes more information on TypeChat and how to get started.
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## Trademarks
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft
trademarks or logos is subject to and must follow
[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
Any use of third-party trademarks or logos are subject to those third-party's policies.
================================================
FILE: python/examples/README.md
================================================
To see TypeChat in action, check out the examples found in this directory.
Each example shows how TypeChat handles natural language input, and maps to validated JSON as output. Most example inputs run on both GPT 3.5 and GPT 4.
We are working to reproduce outputs with other models.
Generally, models trained on both code and natural language text have high accuracy.
We recommend reading each example in the following order.
| Name | Description |
| ---- | ----------- |
| [Sentiment](https://github.com/microsoft/TypeChat/tree/main/python/examples/sentiment) | A sentiment classifier which categorizes user input as negative, neutral, or positive. This is TypeChat's "hello world!" |
| [Coffee Shop](https://github.com/microsoft/TypeChat/tree/main/python/examples/coffeeShop) | An intelligent agent for a coffee shop. This sample translates user intent is translated to a list of coffee order items.
| [Calendar](https://github.com/microsoft/TypeChat/tree/main/python/examples/calendar) | An intelligent scheduler. This sample translates user intent into a sequence of actions to modify a calendar. |
| [HealthData](https://github.com/microsoft/TypeChat/tree/main/python/examples/healthData) | 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"). |
| [Restaurant](https://github.com/microsoft/TypeChat/tree/main/python/examples/restaurant) | An intelligent agent for taking orders at a restaurant. Similar to the coffee shop example, but uses a more complex schema to model more complex linguistic input. The prose files illustrate the line between simpler and more advanced language models in handling compound sentences, distractions, and corrections. This example also shows how we can use TypeScript to provide a user intent summary. |
| [Math](https://github.com/microsoft/TypeChat/tree/main/python/examples/math) | Translate calculations into simple programs given an API that can perform the 4 basic mathematical operators. This example highlights TypeChat's program generation capabilities. |
| [MultiSchema](https://github.com/microsoft/TypeChat/tree/main/python/examples/multiSchema) | This application demonstrates a simple way to write a **super-app** that automatically routes user requests to child apps. |
| [Music](https://github.com/microsoft/TypeChat/tree/main/python/examples/music) | An app for playing music, creating playlists, etc. on Spotify through natural language. Each user intent is translated into a series of actions in JSON which correspond to a simple dataflow program, where each step can consume data produced from previous step. |
## Step 1: Configure your development environment
### Option 1: Local Machine
You can experiment with these TypeChat examples on your local machine.
You will need [Python >=3.11](https://www.python.org/downloads/) and [hatch](https://hatch.pypa.io/1.6/install/).
```sh
git clone https://github.com/microsoft/TypeChat
cd TypeChat/python
hatch shell
python examples/sentiment/demo.py
```
Alternatively, you can just use `venv` and `pip`:
```sh
git clone https://github.com/microsoft/TypeChat
cd TypeChat/python
python -m venv ../.venv
# Activate the virtual environment
# Windows
../.venv/Scripts/Activate.ps1
# Unix/POSIX
source ../.venv/bin/activate
pip install .[examples]
python examples/sentiment/demo.py
```
### Option 2: GitHub Codespaces
GitHub Codespaces enables you to try TypeChat quickly in a development environment hosted in the cloud.
On the TypeChat repository page:
1. Click the green button labeled `<> Code`
2. Select the `Codespaces` tab.
3. Click the green `Create codespace` button.
<details>
<summary>If this is your first time creating a codespace, read this.</summary>
If this is your first time creating a codespace on this repository, GitHub will take a moment to create a dev container image for your session.
Once the image has been created, the browser will load Visual Studio Code in a developer environment automatically configured with the necessary prerequisites, TypeChat cloned, and packages installed.
Remember that you are running in the cloud, so all changes you make to the source tree must be committed and pushed before destroying the codespace. GitHub accounts are usually configured to automatically delete codespaces that have been inactive for 30 days.
For more information, see the [GitHub Codespaces Overview](https://docs.github.com/en/codespaces/overview)
</details>
## Step 2: Configure environment variables
Currently, the examples are running on OpenAI or Azure OpenAI endpoints.
To use an OpenAI endpoint, include the following environment variables:
| Variable | Value |
|----------|-------|
| `OPENAI_MODEL`| The OpenAI model name (e.g. `gpt-3.5-turbo` or `gpt-4`) |
| `OPENAI_API_KEY` | Your OpenAI API key |
| `OPENAI_ENDPOINT` | OpenAI API Endpoint - *optional*, defaults to `"https://api.openai.com/v1/chat/completions"` |
| `OPENAI_ORGANIZATION` | OpenAI Organization - *optional*, defaults to `""` |
To use an Azure OpenAI endpoint, include the following environment variables:
| Variable | Value |
|----------|-------|
| `AZURE_OPENAI_ENDPOINT` | The full URL of the Azure OpenAI REST API (e.g. `https://YOUR_RESOURCE_NAME.openai.azure.com/openai/deployments/YOUR_DEPLOYMENT_NAME/chat/completions?api-version=2023-05-15`) |
| `AZURE_OPENAI_API_KEY` | Your Azure OpenAI API key |
We recommend setting environment variables by creating a `.env` file in the root directory of the project that looks like the following:
```ini
# For OpenAI
OPENAI_MODEL=...
OPENAI_API_KEY=...
# For Azure OpenAI
AZURE_OPENAI_ENDPOINT=...
AZURE_OPENAI_API_KEY=...
```
## Step 3: Run the examples
Examples can be found in the `examples` directory.
To run an example interactively, type `python examples/<example_name>/demo.py` from the example's directory and enter requests when prompted. Type `quit` or `exit` to end the session. You can also open in VS Code the selected example's directory and press <kbd>F5</kbd> to launch it in debug mode.
Note that there are various sample "prose" files (e.g. `input.txt`) provided in each `src` directory that can give a sense of what you can run.
To run an example with one of these input files, run `python demo.py <input-file-path>`.
For example, in the `coffeeShop` directory, you can run:
```
python demo.py input.txt
```
================================================
FILE: python/examples/calendar/README.md
================================================
# Calendar
The Calendar example shows how you can capture user intent as a sequence of actions, such as adding event to a calendar or searching for an event as defined by the [`CalendarActions`](./schema.py) type.
# Try Calendar
To run the Calendar 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`](./input.txt).
For example, we could use natural language to describe an event coming up soon:
**Input**:
```
📅> I need to get my tires changed from 12:00 to 2:00 pm on Friday March 15, 2024
```
**Output**:
```json
{
"actions": [
{
"actionType": "add event",
"event": {
"day": "Friday March 15, 2024",
"timeRange": {
"startTime": "12:00 pm",
"endTime": "2:00 pm"
},
"description": "get my tires changed"
}
}
]
}
```
================================================
FILE: python/examples/calendar/demo.py
================================================
import asyncio
import json
import sys
from dotenv import dotenv_values
import schema as calendar
from typechat import Failure, TypeChatJsonTranslator, TypeChatValidator, create_language_model, process_requests
async def main():
env_vals = dotenv_values()
model = create_language_model(env_vals)
validator = TypeChatValidator(calendar.CalendarActions)
translator = TypeChatJsonTranslator(model, validator, calendar.CalendarActions)
async def request_handler(message: str):
result = await translator.translate(message)
if isinstance(result, Failure):
print(result.message)
else:
result = result.value
print(json.dumps(result, indent=2))
if any(item["actionType"] == "Unknown" for item in result["actions"]):
print("I did not understand the following")
for item in result["actions"]:
if item["actionType"] == "Unknown":
print(item["text"])
file_path = sys.argv[1] if len(sys.argv) == 2 else None
await process_requests("📅> ", file_path, request_handler)
if __name__ == "__main__":
asyncio.run(main())
================================================
FILE: python/examples/calendar/input.txt
================================================
I need to get my tires changed from 12:00 to 2:00 pm on Friday March 15, 2024
Search for any meetings with Gavin this week
Set up an event for friday named Jeffs pizza party at 6pm
Please add Jennifer to the scrum next Thursday
Will you please add an appointment with Jerri Skinner at 9 am? I need it to last 2 hours
Do I have any plan with Rosy this month?
I need to add a meeting with my boss on Monday at 10am. Also make sure to schedule and appointment with Sally, May, and Boris tomorrow at 3pm. Now just add to it Jesse and Abby and make it last ninety minutes
Add meeting with team today at 2
can you record lunch with Luis at 12pm on Friday and also add Isobel to the Wednesday ping pong game at 4pm
I said I'd meet with Jenny this afternoon at 2pm and after that I need to go to the dry cleaner and then the soccer game. Leave an hour for each of those starting at 3:30
================================================
FILE: python/examples/calendar/schema.py
================================================
from typing_extensions import Literal, NotRequired, TypedDict, Annotated, Doc
class UnknownAction(TypedDict):
"""
if the user types text that can not easily be understood as a calendar action, this action is used
"""
actionType: Literal["Unknown"]
text: Annotated[str, Doc("text typed by the user that the system did not understand")]
class EventTimeRange(TypedDict, total=False):
startTime: str
endTime: str
duration: str
class Event(TypedDict):
day: Annotated[str, Doc("date (example: March 22, 2024) or relative date (example: after EventReference)")]
timeRange: EventTimeRange
description: str
location: NotRequired[str]
participants: NotRequired[Annotated[list[str], Doc("a list of people or named groups like 'team'")]]
class EventReference(TypedDict, total=False):
"""
properties used by the requester in referring to an event
these properties are only specified if given directly by the requester
"""
day: Annotated[str, Doc("date (example: March 22, 2024) or relative date (example: after EventReference)")]
dayRange: Annotated[str, Doc("(examples: this month, this week, in the next two days)")]
timeRange: EventTimeRange
description: str
location: str
participants: list[str]
class FindEventsAction(TypedDict):
actionType: Literal["find events"]
eventReference: Annotated[EventReference, Doc("one or more event properties to use to search for matching events")]
class ChangeDescriptionAction(TypedDict):
actionType: Literal["change description"]
eventReference: NotRequired[Annotated[EventReference, Doc("event to be changed")]]
description: Annotated[str, Doc("new description for the event")]
class ChangeTimeRangeAction(TypedDict):
actionType: Literal["change time range"]
eventReference: NotRequired[Annotated[EventReference, Doc("event to be changed")]]
timeRange: Annotated[EventTimeRange, Doc("new time range for the event")]
class AddParticipantsAction(TypedDict):
actionType: Literal["add participants"]
eventReference: NotRequired[
Annotated[EventReference, Doc("event to be augmented; if not specified assume last event discussed")]
]
participants: NotRequired[Annotated[list[str], "new participants (one or more)"]]
class RemoveEventAction(TypedDict):
actionType: Literal["remove event"]
eventReference: EventReference
class AddEventAction(TypedDict):
actionType: Literal["add event"]
event: Event
Actions = (
AddEventAction
| RemoveEventAction
| AddParticipantsAction
| ChangeTimeRangeAction
| ChangeDescriptionAction
| FindEventsAction
| UnknownAction
)
class CalendarActions(TypedDict):
actions: list[Actions]
================================================
FILE: python/examples/coffeeShop/README.md
================================================
# Coffee Shop
The Coffee Shop example shows how to capture user intent as a set of "nouns".
In this case, the nouns are items in a coffee order, where valid items are defined starting from the [`Cart`](./schema.py) type.
This example also uses the [`UnknownText`](./schema.py) type as a way to capture user input that doesn't match to an existing type in [`Cart`](./schema.py).
# Try Coffee Shop
To run the Coffee Shop example, follow the instructions in the [examples README](../README.md#step-1-configure-your-development-environment).
# Usage
Example prompts can be found in [`src/input.txt`](./input.txt) and [`src/input2.txt`](./input2.txt).
For example, we could use natural language to describe our coffee shop order:
**Input**:
```
☕> we'd like a cappuccino with a pack of sugar
```
**Output**:
```json
{
"items": [
{
"type": "lineitem",
"product": {
"type": "LatteDrinks",
"name": "cappuccino",
"options": [
{
"type": "Sweeteners",
"name": "sugar",
"optionQuantity": "regular"
}
]
},
"quantity": 1
}
]
}
```
================================================
FILE: python/examples/coffeeShop/demo.py
================================================
import asyncio
import json
import sys
import schema as coffeeshop
from dotenv import dotenv_values
from typechat import Failure, TypeChatJsonTranslator, TypeChatValidator, create_language_model, process_requests
async def main():
env_vals = dotenv_values()
model = create_language_model(env_vals)
validator = TypeChatValidator(coffeeshop.Cart)
translator = TypeChatJsonTranslator(model, validator, coffeeshop.Cart)
async def request_handler(message: str):
result = await translator.translate(message)
if isinstance(result, Failure):
print(result.message)
else:
result = result.value
print(json.dumps(result, indent=2))
if any(item["type"] == "Unknown" for item in result["items"]):
print("I did not understand the following")
for item in result["items"]:
if item["type"] == "Unknown":
print(item["text"])
file_path = sys.argv[1] if len(sys.argv) == 2 else None
await process_requests("☕> ", file_path, request_handler)
if __name__ == "__main__":
asyncio.run(main())
================================================
FILE: python/examples/coffeeShop/input.txt
================================================
i'd like a latte that's it
i'll have a dark roast coffee thank you
get me a coffee please
could i please get two mochas that's all
we need twenty five flat whites and that'll do it
how about a tall cappuccino
i'd like a venti iced latte
i'd like a iced venti latte
i'd like a venti latte iced
i'd like a latte iced venti
we'll also have a short tall latte
i wanna latte macchiato with vanilla
how about a peppermint latte
may i also get a decaf soy vanilla syrup caramel latte with sugar and foam
i want a latte with peppermint syrup with peppermint syrup
i'd like a decaf half caf latte
can I get a skim soy latte
i'd like a light nutmeg espresso that's it
can i have an cappuccino no foam
can i have an espresso with no nutmeg
we want a light whipped no foam mocha with extra hazelnut and cinnamon
i'd like a latte cut in half
i'd like a strawberry latte
i want a five pump caramel flat white
i want a flat white with five pumps of caramel syrup
i want a two pump peppermint three squirt raspberry skinny vanilla latte with a pump of caramel and two sugars
i want a latte cappuccino espresso and an apple muffin
i'd like a tall decaf latte iced a grande cappuccino double espresso and a warmed poppyseed muffin sliced in half
we'd like a latte with soy and a coffee with soy
i want a latte latte macchiato and a chai latte
we'd like a cappuccino with two pumps of vanilla
make that cappuccino with three pumps of vanilla
we'd like a cappuccino with a pack of sugar
make that cappuccino with two packs of sugar
we'd like a cappuccino with a pack of sugar
make that with two packs of sugar
i'd like a flat white with two equal
add three equal to the flat white
i'd like a flat white with two equal
two tall lattes. the first one with no foam. the second one with whole milk.
two tall lattes. the first one with no foam. the second one with whole milk. actually make the first one a grande.
un petit cafe
en lille kaffe
a raspberry latte
a strawberry latte
roses are red
two lawnmowers, a grande latte and a tall tree
================================================
FILE: python/examples/coffeeShop/input2.txt
================================================
two tall lattes. the first one with no foam. the second one with whole milk.
two tall lattes. the first one with no foam. the second one with whole milk. actually make the first one a grande.
un petit cafe
en lille kaffe
a raspberry latte
a strawberry latte
roses are red
two lawnmowers, a grande latte and a tall tree
================================================
FILE: python/examples/coffeeShop/schema.py
================================================
from typing_extensions import Literal, NotRequired, TypedDict, Annotated, Doc
class UnknownText(TypedDict):
"""
Use this type for order items that match nothing else
"""
type: Literal["Unknown"]
text: Annotated[str, Doc("The text that wasn't understood")]
class Caffeine(TypedDict):
type: Literal["Caffeine"]
name: Literal["regular", "two thirds caf", "half caf", "one third caf", "decaf"]
class Milk(TypedDict):
type: Literal["Milk"]
name: Literal[
"whole milk", "two percent milk", "nonfat milk", "coconut milk", "soy milk", "almond milk", "oat milk"
]
class Creamer(TypedDict):
type: Literal["Creamer"]
name: Literal[
"whole milk creamer",
"two percent milk creamer",
"one percent milk creamer",
"nonfat milk creamer",
"coconut milk creamer",
"soy milk creamer",
"almond milk creamer",
"oat milk creamer",
"half and half",
"heavy cream",
]
class Topping(TypedDict):
type: Literal["Topping"]
name: Literal["cinnamon", "foam", "ice", "nutmeg", "whipped cream", "water"]
optionQuantity: NotRequired["OptionQuantity"]
class LattePreparation(TypedDict):
type: Literal["LattePreparation"]
name: Literal["for here cup", "lid", "with room", "to go", "dry", "wet"]
class Sweetener(TypedDict):
type: Literal["Sweetener"]
name: Literal["equal", "honey", "splenda", "sugar", "sugar in the raw", "sweet n low", "espresso shot"]
optionQuantity: NotRequired["OptionQuantity"]
CaffeineOptions = Caffeine | Milk | Creamer
LatteOptions = CaffeineOptions | Topping | LattePreparation | Sweetener
CoffeeTemperature = Literal["hot", "extra hot", "warm", "iced"]
CoffeeSize = Literal["short", "tall", "grande", "venti"]
EspressoSize = Literal["solo", "doppio", "triple", "quad"]
OptionQuantity = Literal["no", "light", "regular", "extra"] | int
class Syrup(TypedDict):
type: Literal["Syrup"]
name: Literal[
"almond syrup",
"buttered rum syrup",
"caramel syrup",
"cinnamon syrup",
"hazelnut syrup",
"orange syrup",
"peppermint syrup",
"raspberry syrup",
"toffee syrup",
"vanilla syrup",
]
optionQuantity: NotRequired[OptionQuantity]
class LatteDrink(TypedDict):
type: Literal["LatteDrink"]
name: Literal["cappuccino", "flat white", "latte", "latte macchiato", "mocha", "chai latte"]
temperature: NotRequired["CoffeeTemperature"]
size: NotRequired[Annotated[CoffeeSize, Doc("The default is 'grande'")]]
options: NotRequired[list[Creamer | Sweetener | Syrup | Topping | Caffeine | LattePreparation]]
class EspressoDrink(TypedDict):
type: Literal["EspressoDrink"]
name: Literal["espresso", "lungo", "ristretto", "macchiato"]
temperature: NotRequired["CoffeeTemperature"]
size: NotRequired[Annotated["EspressoSize", Doc("The default is 'doppio'")]]
options: NotRequired[list[Creamer | Sweetener | Syrup | Topping | Caffeine | LattePreparation]]
class CoffeeDrink(TypedDict):
type: Literal["CoffeeDrink"]
name: Literal["americano", "coffee"]
temperature: NotRequired[CoffeeTemperature]
size: NotRequired[Annotated[CoffeeSize, Doc("The default is 'grande'")]]
options: NotRequired[list[Creamer | Sweetener | Syrup | Topping | Caffeine | LattePreparation]]
class BakeryOption(TypedDict):
type: Literal["BakeryOption"]
name: Literal["butter", "strawberry jam", "cream cheese"]
optionQuantity: NotRequired["OptionQuantity"]
class BakeryPreparation(TypedDict):
type: Literal["BakeryPreparation"]
name: Literal["warmed", "cut in half"]
class BakeryProduct(TypedDict):
type: Literal["BakeryProduct"]
name: Literal["apple bran muffin", "blueberry muffin", "lemon poppyseed muffin", "bagel"]
options: list[BakeryOption | BakeryPreparation]
Product = BakeryProduct | LatteDrink | EspressoDrink | CoffeeDrink | UnknownText
class LineItem(TypedDict):
type: Literal["LineItem"]
product: Product
quantity: int
class Cart(TypedDict):
type: Literal["Cart"]
items: list[LineItem | UnknownText]
================================================
FILE: python/examples/healthData/README.md
================================================
# 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`](./input.txt).
For example, given the following input statement:
**Input**:
```console
🤧> I am taking klaritin for my allergies
```
**Output**:
================================================
FILE: python/examples/healthData/demo.py
================================================
import asyncio
import json
import sys
from dotenv import dotenv_values
import schema as health
from typechat import Failure, TypeChatValidator, create_language_model, process_requests
from translator import TranslatorWithHistory
health_instructions = """
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.
"""
async def main():
env_vals = dotenv_values()
model = create_language_model(env_vals)
validator = TypeChatValidator(health.HealthDataResponse)
translator = TranslatorWithHistory(
model, validator, health.HealthDataResponse, additional_agent_instructions=health_instructions
)
async def request_handler(message: str):
result = await translator.translate(message)
if isinstance(result, Failure):
print(result.message)
else:
result = result.value
print(json.dumps(result, indent=2))
agent_message = result.get("message", "None")
not_translated = result.get("notTranslated", None)
if agent_message:
print(f"\n📝: {agent_message}")
if not_translated:
print(f"\n🤔: I did not understand\n {not_translated}")
file_path = sys.argv[1] if len(sys.argv) == 2 else None
await process_requests("💉💊🤧> ", file_path, request_handler)
if __name__ == "__main__":
asyncio.run(main())
================================================
FILE: python/examples/healthData/input.txt
================================================
#
# 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
================================================
FILE: python/examples/healthData/schema.py
================================================
from typing_extensions import TypedDict, Annotated, NotRequired, Literal, Doc
class Quantity(TypedDict):
value: Annotated[float, Doc("Exact number")]
units: Annotated[str, Doc("UNITS include mg, kg, cm, pounds, liter, ml, tablet, pill, cup, per-day, per-week..ETC")]
class ApproxDatetime(TypedDict):
displayText: Annotated[str, Doc("Default: Unknown. Required")]
timestamp: NotRequired[Annotated[str, Doc("If precise timestamp can be set")]]
class ApproxQuantity(TypedDict):
displayText: Annotated[str, Doc("Default: Unknown. Required")]
quantity: NotRequired[Annotated[Quantity, Doc("Optional: only if precise quantities are available")]]
class OtherHealthData(TypedDict):
"""
Use for health data that match nothing else. E.g. immunization, blood prssure etc
"""
text: str
when: NotRequired[ApproxDatetime]
class Condition(TypedDict):
"""
Disease, Ailment, Injury, Sickness
"""
name: Annotated[str, Doc("Fix any spelling mistakes, especially phonetic spelling")]
startDate: Annotated[ApproxDatetime, Doc("When the condition started? Required")]
status: Annotated[
Literal["active", "recurrence", "relapse", "inactive", "remission", "resolved", "unknown"],
Doc("Always ask for current status of the condition"),
]
endDate: NotRequired[Annotated[ApproxDatetime, Doc("If the condition was no longer active")]]
class Medication(TypedDict):
"""
Meds, pills etc.
"""
name: Annotated[str, Doc("Fix any spelling mistakes, especially phonetic spelling")]
dose: Annotated[ApproxQuantity, Doc("E.g. 2 tablets, 1 cup. Required")]
frequency: Annotated[ApproxQuantity, Doc("E.g. twice a day. Required")]
strength: Annotated[ApproxQuantity, Doc("E.g. 50 mg. Required")]
class HealthData(TypedDict, total=False):
medication: list[Medication]
condition: list[Condition]
other: list[OtherHealthData]
class HealthDataResponse(TypedDict, total=False):
data: Annotated[HealthData, Doc("Return this if JSON has ALL required information. Else ask questions")]
message: Annotated[str, Doc("Use this to ask questions and give pertinent responses")]
notTranslated: Annotated[str, Doc("Use this parts of the user request not translateed, off topic, etc")]
================================================
FILE: python/examples/healthData/translator.py
================================================
import json
from typing_extensions import TypeVar, Any, override, TypedDict, Literal
from typechat import TypeChatValidator, TypeChatLanguageModel, TypeChatJsonTranslator, Result, Failure, PromptSection
from datetime import datetime
T = TypeVar("T", covariant=True)
class ChatMessage(TypedDict):
source: Literal["system", "user", "assistant"]
body: Any
class TranslatorWithHistory(TypeChatJsonTranslator[T]):
_chat_history: list[ChatMessage]
_max_prompt_length: int
_additional_agent_instructions: str
def __init__(
self, model: TypeChatLanguageModel, validator: TypeChatValidator[T], target_type: type[T], additional_agent_instructions: str
):
super().__init__(model=model, validator=validator, target_type=target_type)
self._chat_history = []
self._max_prompt_length = 2048
self._additional_agent_instructions = additional_agent_instructions
@override
async def translate(self, input: str, *, prompt_preamble: str | list[PromptSection] | None = None) -> Result[T]:
result = await super().translate(input=input, prompt_preamble=prompt_preamble)
if not isinstance(result, Failure):
self._chat_history.append(ChatMessage(source="assistant", body=result.value))
return result
@override
def _create_request_prompt(self, intent: str) -> str:
# TODO: drop history entries if we exceed the max_prompt_length
history_str = json.dumps(self._chat_history, indent=2, default=lambda o: None, allow_nan=False)
now = datetime.now()
prompt = F"""
user: You are a service that translates user requests into JSON objects of type "{self.type_name}" according to the following TypeScript definitions:
'''
{self.schema_str}
'''
user:
Use precise date and times RELATIVE TO CURRENT DATE: {now.strftime('%A, %m %d, %Y')} CURRENT TIME: {now.strftime("%H:%M:%S")}
Also turn ranges like next week and next month into precise dates
user:
{self._additional_agent_instructions}
system:
IMPORTANT CONTEXT for the user request:
{history_str}
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
================================================
FILE: python/examples/math/README.md
================================================
# Math
The Math example shows how to use TypeChat for program generation based on an API schema with the `evaluateJsonProgram` function. This example translates calculations into simple programs given an [`API`](./schema.py) type that can perform the four basic mathematical operations.
# Try Math
To run the Math 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`](./input.txt).
For example, we could use natural language to describe mathematical operations, and TypeChat will generate a program that can execute the math API defined in the schema.
**Input**:
```
🟰> multiply two by three, then multiply four by five, then sum the results
```
**Output**:
```
import { API } from "./schema";
function program(api: API) {
const step1 = api.mul(2, 3);
const step2 = api.mul(4, 5);
return api.add(step1, step2);
}
Running program:
mul(2, 3)
mul(4, 5)
add(6, 20)
Result: 26
```
================================================
FILE: python/examples/math/demo.py
================================================
import asyncio
from collections.abc import Sequence
import json
import sys
from typing import cast
from dotenv import dotenv_values
import schema as math
from typechat import Failure, create_language_model, process_requests
from program import TypeChatProgramTranslator, TypeChatProgramValidator, evaluate_json_program
async def main():
env_vals = dotenv_values()
model = create_language_model(env_vals)
validator = TypeChatProgramValidator()
translator = TypeChatProgramTranslator(model, validator, math.MathAPI)
async def request_handler(message: str):
result = await translator.translate(message)
if isinstance(result, Failure):
print(result.message)
else:
result = result.value
print(json.dumps(result, indent=2))
math_result = await evaluate_json_program(result, apply_operations)
print(f"Math Result: {math_result}")
file_path = sys.argv[1] if len(sys.argv) == 2 else None
await process_requests("🧮> ", file_path, request_handler)
async def apply_operations(func: str, args: Sequence[object]) -> int | float:
print(f"{func}({json.dumps(args)}) ")
for arg in args:
if not isinstance(arg, (int, float)):
raise ValueError("All arguments are expected to be numeric.")
args = cast(Sequence[int | float], args)
match func:
case "add":
return args[0] + args[1]
case "sub":
return args[0] - args[1]
case "mul":
return args[0] * args[1]
case "div":
return args[0] / args[1]
case "neg":
return -1 * args[0]
case "id":
return args[0]
case _:
raise ValueError(f'Unexpected function name {func}')
if __name__ == "__main__":
asyncio.run(main())
================================================
FILE: python/examples/math/input.txt
================================================
1 + 2
1 + 2 * 3
2 * 3 + 4 * 5
2 3 * 4 5 * +
multiply two by three, then multiply four by five, then sum the results
================================================
FILE: python/examples/math/program.py
================================================
from __future__ import annotations
import asyncio
from collections.abc import Sequence
from typing import Any, TypeAlias, TypedDict, cast
from typing_extensions import (
TypeVar,
Callable,
Awaitable,
Annotated,
NotRequired,
override,
Doc,
)
from typechat import (
Failure,
Success,
TypeChatLanguageModel,
TypeChatValidator,
TypeChatJsonTranslator,
python_type_to_typescript_schema,
)
T = TypeVar("T", covariant=True)
Expression: TypeAlias = "str | int | float | bool | None | dict[str, Expression] | list[Expression] | FunctionCall | ResultReference"
JsonProgram = TypedDict("JsonProgram", {"@steps": list["FunctionCall"]})
ResultReference = TypedDict(
"ResultReference", {"@ref": Annotated[int, Doc("Index of the previous expression in the 'steps' array")]}
)
FunctionCall = TypedDict(
"FunctionCall",
{
"@func": Annotated[str, Doc("Name of the function")],
"@args": NotRequired[Annotated[list[Expression], Doc("Arguments for the function, if any")]],
},
)
translation_result = python_type_to_typescript_schema(JsonProgram)
program_schema_text = translation_result.typescript_schema_str
JsonValue = str | int | float | bool | None | dict[str, "JsonValue"] | list["JsonValue"]
async def evaluate_json_program(
program: JsonProgram,
onCall: Callable[[str, Sequence[JsonValue]], Awaitable[JsonValue]]
) -> JsonValue:
results: list[JsonValue] = []
def evaluate_array(array: Sequence[JsonValue]) -> Awaitable[list[JsonValue]]:
return asyncio.gather(*[evaluate_expression(e) for e in array])
async def evaluate_expression(expr: JsonValue) -> JsonValue:
match expr:
case bool() | int() | float() | str() | None:
return expr
case { "@ref": int(index) } if not isinstance(index, bool):
if 0 <= index < len(results):
return results[index]
raise ValueError(f"Index {index} is out of range [0, {len(results)})")
case { "@ref": ref_value }:
raise ValueError(f"'ref' value must be an integer, but was ${ref_value}")
case { "@func": str(function_name) }:
args: list[JsonValue]
match expr:
case { "@args": None }:
args = []
case { "@args": list() }:
args = cast(list[JsonValue], expr["@args"]) # TODO
case { "@args": _ }:
raise ValueError("Given an invalid value for '@args'.")
case _:
args = []
return await onCall(function_name, await evaluate_array(args))
case list(array_expression_elements):
return await evaluate_array(array_expression_elements)
case _:
raise ValueError("This condition should never hit")
for step in program["@steps"]:
results.append(await evaluate_expression(cast(JsonValue, step)))
if len(results) > 0:
return results[-1]
else:
return None
class TypeChatProgramValidator(TypeChatValidator[JsonProgram]):
def __init__(self):
# TODO: This example should eventually be updated to use Python 3.12 type aliases
# Passing in `JsonProgram` for `py_type` would cause issues because
# Pydantic's `TypeAdapter` ends up trying to eagerly construct an
# anonymous recursive type. Even a NewType does not work here.
# For now, we just pass in `Any` in place of `JsonProgram`.
super().__init__(py_type=cast(type[JsonProgram], Any))
@override
def validate_object(self, obj: Any) -> Success[JsonProgram] | Failure:
if "@steps" in obj and isinstance(obj["@steps"], Sequence):
return Success(obj)
else:
return Failure("This is not a valid program. The program must have an array of @steps")
class TypeChatProgramTranslator(TypeChatJsonTranslator[JsonProgram]):
_api_declaration_str: str
def __init__(self, model: TypeChatLanguageModel, validator: TypeChatProgramValidator, api_type: type):
super().__init__(model=model, validator=validator, target_type=api_type, _raise_on_schema_errors = False)
# TODO: the conversion result here has errors!
conversion_result = python_type_to_typescript_schema(api_type)
self._api_declaration_str = conversion_result.typescript_schema_str
@override
def _create_request_prompt(self, intent: str) -> str:
prompt = F"""
You are a service that translates user requests into programs represented as JSON using the following TypeScript definitions:
```
{program_schema_text}
```
The programs can call functions from the API defined in the following TypeScript definitions:
```
{self._api_declaration_str}
```
The following is a user request:
'''
{intent}
'''
The following is the user request translated into a JSON program object with 2 spaces of indentation and no properties with the value undefined:
"""
return prompt
@override
def _create_repair_prompt(self, validation_error: str) -> str:
prompt = F"""
The JSON program object is invalid for the following reason:
'''
{validation_error}
'''
The following is a revised JSON program object:
"""
return prompt
================================================
FILE: python/examples/math/schema.py
================================================
from typing_extensions import TypedDict, Annotated, Callable, Doc
class MathAPI(TypedDict):
"""
This is API for a simple calculator
"""
add: Annotated[Callable[[float, float], float], Doc("Add two numbers")]
sub: Annotated[Callable[[float, float], float], Doc("Subtract two numbers")]
mul: Annotated[Callable[[float, float], float], Doc("Multiply two numbers")]
div: Annotated[Callable[[float, float], float], Doc("Divide two numbers")]
neg: Annotated[Callable[[float], float], Doc("Negate a number")]
id: Annotated[Callable[[float], float], Doc("Identity function")]
unknown: Annotated[Callable[[str], float], Doc("Unknown request")]
================================================
FILE: python/examples/math/schemaV2.py
================================================
from typing_extensions import Protocol, runtime_checkable
@runtime_checkable
class MathAPI(Protocol):
"""
This is API for a simple calculator
"""
def add(self, x: float, y: float) -> float:
"""
Add two numbers
"""
...
def sub(self, x: float, y: float) -> float:
"""
Subtract two numbers
"""
...
def mul(self, x: float, y: float) -> float:
"""
Multiply two numbers
"""
...
def div(self, x: float, y: float) -> float:
"""
Divide two numbers
"""
...
def neg(self, x: float) -> float:
"""
Negate a number
"""
...
def id(self, x: float, y: float) -> float:
"""
Identity function
"""
...
def unknown(self, text: str) -> float:
"""
unknown request
"""
...
================================================
FILE: python/examples/multiSchema/README.md
================================================
# MultiSchema
This application demonstrates a simple way to write a **super-app** that automatically routes user requests to child apps.
In this example, the child apps are existing TypeChat chat examples:
* CoffeeShop
* Restaurant
* Calendar
* Sentiment
* Math
* Plugins
* HealthData
## Target Models
Works with GPT-3.5 Turbo and GPT-4.
Sub-apps like HealthData and Plugins work best with GPT-4.
# Usage
Example prompts can be found in [`input.txt`](input.txt).
================================================
FILE: python/examples/multiSchema/agents.py
================================================
from collections.abc import Sequence
import os
import sys
from typing import cast
examples_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
if examples_path not in sys.path:
sys.path.append(examples_path)
import json
from typing_extensions import TypeVar, Generic
from typechat import Failure, TypeChatJsonTranslator, TypeChatValidator, TypeChatLanguageModel
import examples.math.schema as math_schema
from examples.math.program import (
TypeChatProgramTranslator,
TypeChatProgramValidator,
evaluate_json_program,
)
import examples.music.schema as music_schema
from examples.music.client import ClientContext, handle_call, get_client_context
T = TypeVar("T", covariant=True)
class JsonPrintAgent(Generic[T]):
_validator: TypeChatValidator[T]
_translator: TypeChatJsonTranslator[T]
def __init__(self, model: TypeChatLanguageModel, target_type: type[T]):
super().__init__()
self._validator = TypeChatValidator(target_type)
self._translator = TypeChatJsonTranslator(model, self._validator, target_type)
async def handle_request(self, line: str):
result = await self._translator.translate(line)
if isinstance(result, Failure):
print(result.message)
else:
result = result.value
print(json.dumps(result, indent=2))
class MathAgent:
_validator: TypeChatProgramValidator
_translator: TypeChatProgramTranslator
def __init__(self, model: TypeChatLanguageModel):
super().__init__()
self._validator = TypeChatProgramValidator()
self._translator = TypeChatProgramTranslator(model, self._validator, math_schema.MathAPI)
async def _handle_json_program_call(self, func: str, args: Sequence[object]) -> int | float:
print(f"{func}({json.dumps(args)}) ")
for arg in args:
if not isinstance(arg, (int, float)):
raise ValueError("All arguments are expected to be numeric.")
args = cast(Sequence[int | float], args)
match func:
case "add":
return args[0] + args[1]
case "sub":
return args[0] - args[1]
case "mul":
return args[0] * args[1]
case "div":
return args[0] / args[1]
case "neg":
return -1 * args[0]
case "id":
return args[0]
case _:
raise ValueError(f'Unexpected function name {func}')
async def handle_request(self, line: str):
result = await self._translator.translate(line)
if isinstance(result, Failure):
print(result.message)
else:
result = result.value
print(json.dumps(result, indent=2))
math_result = await evaluate_json_program(result, self._handle_json_program_call)
print(f"Math Result: {math_result}")
class MusicAgent:
_validator: TypeChatValidator[music_schema.PlayerActions]
_translator: TypeChatJsonTranslator[music_schema.PlayerActions]
_client_context: ClientContext | None
_authentication_vals: dict[str, str | None]
def __init__(self, model: TypeChatLanguageModel, authentication_vals: dict[str, str | None]):
super().__init__()
self._validator = TypeChatValidator(music_schema.PlayerActions)
self._translator = TypeChatJsonTranslator(model, self._validator, music_schema.PlayerActions)
self._authentication_vals = authentication_vals
self._client_context = None
async def authenticate(self):
self._client_context = await get_client_context(self._authentication_vals)
async def handle_request(self, line: str):
if not self._client_context:
await self.authenticate()
assert self._client_context
result = await self._translator.translate(line)
if isinstance(result, Failure):
print(result.message)
else:
result = result.value
print(json.dumps(result, indent=2))
try:
for action in result["actions"]:
await handle_call(action, self._client_context)
except Exception as error:
print("An exception occurred: ", error)
================================================
FILE: python/examples/multiSchema/demo.py
================================================
import os
import sys
examples_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
if examples_path not in sys.path:
sys.path.append(examples_path)
import asyncio
from dotenv import dotenv_values
from typechat import create_language_model, process_requests
from router import TextRequestRouter
from agents import MathAgent, JsonPrintAgent, MusicAgent
import examples.restaurant.schema as restaurant
import examples.calendar.schema as calendar
import examples.coffeeShop.schema as coffeeShop
import examples.sentiment.schema as sentiment
async def handle_unknown(_line: str):
print("The input did not match any registered agents")
async def main():
env_vals = dotenv_values()
model = create_language_model(env_vals)
router = TextRequestRouter(model=model)
# register agents
math_agent = MathAgent(model=model)
router.register_agent(
name="Math", description="Calculations using the four basic math operations", handler=math_agent.handle_request
)
music_agent = MusicAgent(model=model, authentication_vals=env_vals)
await music_agent.authenticate()
router.register_agent(
name="Music Player",
description="Actions related to music, podcasts, artists, and managing music libraries",
handler=music_agent.handle_request,
)
coffee_agent = JsonPrintAgent(model=model, target_type=coffeeShop.Cart)
router.register_agent(
name="CoffeeShop",
description="Order Coffee Drinks (Italian names included) and Baked Goods",
handler=coffee_agent.handle_request,
)
calendar_agent = JsonPrintAgent(model=model, target_type=calendar.CalendarActions)
router.register_agent(
name="Calendar",
description="Actions related to calendars, appointments, meetings, schedules",
handler=calendar_agent.handle_request,
)
restaurant_agent = JsonPrintAgent(model=model, target_type=restaurant.Order)
router.register_agent(
name="Restaurant", description="Order pizza, beer and salads", handler=restaurant_agent.handle_request
)
sentiment_agent = JsonPrintAgent(model=model, target_type=sentiment.Sentiment)
router.register_agent(
name="Sentiment",
description="Statements with sentiments, emotions, feelings, impressions about places, things, the surroundings",
handler=sentiment_agent.handle_request,
)
# register a handler for unknown results
router.register_agent(name="No Match", description="Handles all unrecognized requests", handler=handle_unknown)
async def request_handler(message: str):
await router.route_request(message)
file_path = sys.argv[1] if len(sys.argv) == 2 else None
await process_requests("🔀> ", file_path, request_handler)
if __name__ == "__main__":
asyncio.run(main())
================================================
FILE: python/examples/multiSchema/input.txt
================================================
I'd like two large, one with pepperoni and the other with extra sauce. The pepperoni gets basil and the extra sauce gets Canadian bacon. And add a whole salad.
I also want an espresso with extra foam and a muffin with jam
And book me a lunch with Claude Debussy next week at 12.30 at Le Petit Chien!
I bought 4 shoes for 12.50 each. How much did I spend?
Its cold!
Its cold and I want hot cafe to warm me up
The coffee is cold
The coffee is awful
(2*4)+(9*7)
================================================
FILE: python/examples/multiSchema/router.py
================================================
import json
from typing_extensions import Any, Callable, Awaitable, TypedDict, Annotated
from typechat import Failure, TypeChatValidator, TypeChatLanguageModel, TypeChatJsonTranslator
class AgentInfo(TypedDict):
name: str
description: str
handler: Callable[[str], Awaitable[Any]]
class TaskClassification(TypedDict):
task_kind: Annotated[str, "Describe the kind of task to perform."]
class TextRequestRouter:
_current_agents: dict[str, AgentInfo]
_validator: TypeChatValidator[TaskClassification]
_translator: TypeChatJsonTranslator[TaskClassification]
def __init__(self, model: TypeChatLanguageModel):
super().__init__()
self._validator = TypeChatValidator(TaskClassification)
self._translator = TypeChatJsonTranslator(model, self._validator, TaskClassification)
self._current_agents = {}
def register_agent(self, name: str, description: str, handler: Callable[[str], Awaitable[Any]]):
agent = AgentInfo(name=name, description=description, handler=handler)
self._current_agents[name] = agent
async def route_request(self, line: str):
classes_str = json.dumps(self._current_agents, indent=2, default=lambda o: None, allow_nan=False)
prompt_fragment = F"""
Classify ""{line}"" using the following classification table:
'''
{classes_str}
'''
"""
result = await self._translator.translate(prompt_fragment)
if isinstance(result, Failure):
print("Translation Failed ❌")
print(f"Context: {result.message}")
else:
result = result.value
print("Translation Succeeded! ✅\n")
print(f"The target class is {result['task_kind']}")
target = self._current_agents[result["task_kind"]]
await target.get("handler")(line)
================================================
FILE: python/examples/music/README.md
================================================
# Music
The Music example shows how to capture user intent as actions in JSON which corresponds to a simple dataflow program over the API provided in the intent schema. This example shows this pattern using natural language to control the Spotify API to play music, create playlists, and perform other actions from the API.
# Try Music
A Spotify Premium account is required to run this example.
To run the Music example, follow the instructions in the [examples README](../README.md#step-1-configure-your-development-environment).
This example also requires additional setup to use the Spotify API:
1. Go to https://developer.spotify.com/dashboard.
2. Log into Spotify with your user account if you are not already logged in.
3. Click the button in the upper right labeled "Create App".
4. Fill in the form, making sure the Redirect URI is http://localhost:PORT/callback, where PORT is a four-digit port number you choose for the authorization redirect.
5. Click the settings button and copy down the Client ID and Client Secret (the client secret requires you to click 'View client secret').
6. In your `.env` file, set `SPOTIFY_APP_CLI` to your Client ID and `SPOTIFY_APP_CLISEC` to your Client Secret. Also set `SPOTIFY_APP_PORT` to the PORT on your local machine that you chose in step 4.
# Usage
Example prompts can be found in [`input.txt`](./input.txt).
For example, use natural language to start playing a song with the Spotify player:
**Input**:
```
🎵> play shake it off by taylor swift
```
**Output**:
```
JSON View
{
"actions": [
{
"actionName": "play",
"parameters": {
"artist": "taylor swift",
"trackName": "shake it off",
"quantity": 0
}
}
]
}
Playing...
Shake It Off
```
================================================
FILE: python/examples/music/client.py
================================================
import os
import sys
current_path = os.path.abspath(os.path.dirname(__file__))
if current_path not in sys.path:
sys.path.append(current_path)
import math
from typing import Any, Optional
from pydantic.dataclasses import dataclass
import spotipy # type: ignore
from schema import PlayerAction
from spotipyWrapper import SimplifiedTrackInfo, SimplifiedPlaylistInfo, AsyncSpotipy
class Config:
arbitrary_types_allowed = True
@dataclass(config=Config)
class ClientContext:
service: AsyncSpotipy
userId: str
deviceId: Optional[str] = None
currentTrackList: Optional[list[SimplifiedTrackInfo]] = None
lastTrackStartIndex: Optional[int] = 0
lastTrackEndIndex: Optional[int] = -1
async def get_client_context(vals: dict[str, str | None]) -> ClientContext:
scopes = [
"user-read-private",
"playlist-read-collaborative",
"playlist-modify-private",
"playlist-read-private",
"playlist-modify-public",
"streaming",
"user-library-read",
"user-top-read",
"user-read-playback-state",
"user-modify-playback-state",
"user-read-recently-played",
"user-read-currently-playing",
"user-library-modify",
"ugc-image-upload",
]
scopes_str = " ".join(scopes)
auth_manager = spotipy.SpotifyOAuth(
client_id=vals.get("SPOTIFY_APP_CLI", None),
client_secret=vals.get("SPOTIFY_APP_CLISEC", None),
redirect_uri=f"http://localhost:{vals.get('SPOTIFY_APP_PORT', 80)}/callback",
scope=scopes_str,
)
spotify = spotipy.Spotify(auth_manager=auth_manager)
devices = spotify.devices()
device_id: str = ""
if devices:
device_list = devices.get("devices", [])
if device_list:
device_id = device_list[0].get("id", "")
user: dict[str, Any] = spotify.current_user() # type: ignore
result = ClientContext(deviceId=device_id, service=AsyncSpotipy(spotify), userId=user.get("id", None))
return result
async def play_album(album_uri: str, context: ClientContext):
await context.service.start_playback(context_uri=album_uri, device_id=context.deviceId)
async def play_tracks_with_query(query: str, quantity: int, context: ClientContext):
# To do: paginate until we get to the requested number of items
results = await context.service.search(q=query, type='track', limit=quantity, offset=0)
item_uris = [t["uri"] for t in results['tracks']['items']]
await context.service.start_playback(device_id=context.deviceId, uris=item_uris)
async def play_albums_with_query(query: str, quantity: int, context: ClientContext):
results = await context.service.search(q=query, type='album', limit=quantity, offset=0)
item_uris = [t["uri"] for t in results['albums']['items']]
await context.service.start_playback(device_id=context.deviceId, uris=item_uris)
async def play_artist_with_query(query: str, context: ClientContext):
results = await context.service.search(q=query, type='artist')
items = results['artists']['items']
if len(items) > 0:
artist = items[0]
await context.service.start_playback(context_uri=artist["uri"], device_id=context.deviceId)
def get_tracks_from_result_list(resultItems: list[Any]) -> list[SimplifiedTrackInfo]:
tracks = [
SimplifiedTrackInfo(
name=track["name"],
artistNames=[a["name"] for a in track["artists"]],
artistUris=[a["uri"] for a in track["artists"]],
albumName=track["album"]["name"],
uri=track["uri"],
)
for track in resultItems
]
return tracks
async def get_tracks_from_search(query: str, context: ClientContext) -> list[SimplifiedTrackInfo]:
results = await context.service.search(q=query, type='track', limit=50, offset=0)
tracks: list[SimplifiedTrackInfo] = []
while results:
tracks.extend(get_tracks_from_result_list(resultItems=results['tracks']['items']))
if results['next']:
results = await context.service.next(results)
else:
results = None
return tracks
async def get_tracks_with_genres(
tracks: list[SimplifiedTrackInfo], context: ClientContext
) -> list[SimplifiedTrackInfo]:
unique_artist_ids: list[str] = list(set([a for a in track.artistUris])) # type: ignore
genre_lookup: dict[str, list[str]] = {}
for artist_id in unique_artist_ids:
artist = await context.service.artist(artist_id)
genre_lookup[artist_id] = [g.casefold() for g in artist["genres"]]
for track in tracks:
track_genres:set[str] = set()
for artist_id in track.artistUris:
track_genres.update(set(genre_lookup[artist_id]))
track.genres = list(track_genres)
return tracks
def print_tracks(tracks: list[SimplifiedTrackInfo]):
for track in tracks:
print(f" {track.name}")
print(f" Artists: {', '.join(track.artistNames)}")
print(f" Album: {track.albumName}")
def update_track_list_and_print(tracks: list[SimplifiedTrackInfo], context: ClientContext):
print_tracks(tracks)
context.currentTrackList = tracks
async def get_current_users_playlists(context: ClientContext) -> list[SimplifiedPlaylistInfo]:
results = await context.service.current_user_playlists(limit=50)
playlists: list[SimplifiedPlaylistInfo] = []
while results:
playlists.extend(
[SimplifiedPlaylistInfo(name=curr_list["name"], id=curr_list["id"]) for curr_list in results['items']]
)
if results['next']:
results = await context.service.next(results)
else:
results = None
return playlists
async def print_status(context: ClientContext):
state = await context.service.current_playback()
if not state:
print("Nothing playing according to Spotify")
await list_available_devices(context)
async def list_available_devices(context: ClientContext):
devices = await context.service.devices()
for device in devices["devices"]:
if device["is_active"]:
print(F"Active Device {device['name']} of type {device['type']}")
else:
print(f"Device {device['name']} of type {device['type']} is available")
async def handle_call(action: PlayerAction, context: ClientContext):
match action["actionName"]:
case "play":
start_index = action["parameters"].get("trackNumber", None)
end_index = 0
if start_index is None:
track_range = action["parameters"].get("trackRange", None)
if track_range:
start_index = track_range[0]
end_index = track_range[1]
if start_index is not None:
if not end_index:
end_index = start_index + 1
if context.currentTrackList is None:
queue = await context.service.queue()
if queue["queue"]:
tracks = get_tracks_from_result_list(resultItems=queue["queue"])
context.currentTrackList = tracks
if context.currentTrackList:
item_uris = [a.uri for a in context.currentTrackList]
await context.service.start_playback(
device_id=context.deviceId, uris=item_uris, offset={"position": start_index}
)
else:
query = action["parameters"].get("query", None)
album = action["parameters"].get("album", None)
track = action["parameters"].get("trackName", None)
artist = action["parameters"].get("artist", None)
quantity = action["parameters"].get("quantity", 1)
if quantity < 9:
quantity = 1
if query:
actionType = action["parameters"].get("itemType", "album")
if actionType == "track":
await play_tracks_with_query(query, quantity, context)
else:
await play_albums_with_query(query, quantity, context)
elif track is not None:
query = 'track:' + track
await play_tracks_with_query(query, quantity, context)
elif album is not None:
query = 'album:' + album
await play_albums_with_query(query, quantity, context)
elif artist is not None:
query = 'artist:' + artist
await play_artist_with_query(query, context)
else:
# Resume playback on default device
await context.service.start_playback(device_id=context.deviceId)
case "status":
await print_status(context)
case "getQueue":
queue = await context.service.queue()
print("Current Queue: ")
for track in queue["queue"]:
print(f" {track['name']}")
print(f" Artists: {', '.join([a['name'] for a in track['artists']])}")
print(f" Album: {track['album']['name']}")
await print_status(context)
case "pause":
await context.service.pause_playback(device_id=context.deviceId)
await print_status(context)
case "next":
await context.service.next_track(device_id=context.deviceId)
await print_status(context)
case "previous":
await context.service.previous_track(device_id=context.deviceId)
await print_status(context)
case "shuffle":
await context.service.shuffle(device_id=context.deviceId, state=action["parameters"]["on"])
await print_status(context)
case "resume":
await context.service.start_playback(device_id=context.deviceId)
await print_status(context)
case "listDevices":
await list_available_devices(context)
case "selectDevice":
deviceKeyword = action["parameters"]["keyword"].lower()
devices = await context.service.devices()
devices = devices["devices"]
target_device = next(
(d for d in devices if d["name"].lower() == deviceKeyword or d["type"].lower() == deviceKeyword), None
)
if target_device:
await context.service.transfer_playback(device_id=target_device)
print(f"Selected device {target_device}")
case "setVolume":
new_volume = action["parameters"].get("newVolumeLevel", None)
new_volume = max(0, min(new_volume, 100))
print(f"Setting volume to {new_volume} ...")
await context.service.volume(device_id=context.deviceId, volume_percent=new_volume)
case "changeVolume":
playback_state = await context.service.current_playback()
if playback_state and playback_state["device"]:
volume = int(playback_state["device"]["volume_percent"])
volume_change = int(action["parameters"].get("volumeChangePercentage", 0))
new_volume = math.floor((1.0 + volume_change / 100) * volume)
new_volume = max(0, min(new_volume, 100))
print(f"Setting volume to {new_volume} ...")
await context.service.volume(device_id=context.deviceId, volume_percent=new_volume)
case "searchTracks":
query = "track:" + action["parameters"].get("query", None)
tracks = await get_tracks_from_search(query=query, context=context)
print("Search Results: ")
update_track_list_and_print(tracks, context)
case "listPlaylists":
playlists = await get_current_users_playlists(context)
for i, playlist in enumerate(playlists):
print("%4d %s" % (i + 1, playlist.name))
case "getPlaylist":
playlists = await get_current_users_playlists(context)
name = action["parameters"].get("name", None)
target_playlist = next((p for p in playlists if p.name.casefold() == name.casefold()), None)
if target_playlist:
results = await context.service.playlist_items(
playlist_id=target_playlist.id, additional_types=['track']
)
tracks = get_tracks_from_result_list(resultItems=results['items'])
print("PLaylist items: ")
update_track_list_and_print(tracks, context)
case "getAlbum":
name = action["parameters"].get("name", None)
if name:
results = await context.service.search(q='album:' + name, type='album', limit=50, offset=0)
if results:
target_album_info = results["albums"]["items"][0]
if target_album_info:
target_album = await context.service.album(target_album_info["uri"])
tracks = get_tracks_from_result_list(resultItems=target_album['tracks'])
print("Album items: ")
update_track_list_and_print(tracks, context)
case "getFavorites":
count = action["parameters"].get("count", 50)
results = await context.service.current_user_top_tracks(limit=count, offset=0)
if results:
tracks = get_tracks_from_result_list(resultItems=results['items'])
print("Favorite tracks: ")
update_track_list_and_print(tracks, context)
case "filterTracks":
trackCollection = context.currentTrackList
filter_type = action["parameters"].get("filterType", None)
filter_value = action["parameters"].get("filterValue", None)
if filter_type and filter_value and trackCollection:
matched_tracks: list[SimplifiedTrackInfo] = []
filter_value = filter_value.casefold()
match filter_type:
case "genre":
extended_collection = await get_tracks_with_genres(trackCollection, context)
matched_tracks = [t for t in extended_collection if filter_value in t.genres]
case "artist":
matched_tracks = [
t
for t in trackCollection
if any(filter_value in a for a in list(map(str.casefold, t.artistNames)))
]
case "name":
matched_tracks = [t for t in trackCollection if filter_value in t.name.casefold()]
if action["parameters"].get("negate", None):
tracks = [t for t in trackCollection if t not in matched_tracks]
else:
tracks = matched_tracks
print("Filtered tracks:")
update_track_list_and_print(tracks, context)
case "createPlaylist":
name = action["parameters"]["name"]
trackCollection = context.currentTrackList
if name and trackCollection:
uris = [t.uri for t in trackCollection]
playlist = await context.service.user_playlist_create(user=context.userId, name=name)
await context.service.playlist_add_items(playlist_id=playlist["id"], items=uris)
print(f"Playlist {name} created with tracks:")
print_tracks(trackCollection)
else:
print("no input tracks for createPlaylist")
case "deletePlaylist":
name = action["parameters"].get("name", None)
playlists = await get_current_users_playlists(context)
if name and playlists:
target_playlist = next((p for p in playlists if p.name.casefold() == name.casefold()), None)
if target_playlist:
await context.service.current_user_unfollow_playlist(playlist_id=target_playlist.id)
print(f"Playlist {name} deleted")
case "Unknown":
print(f"Text not understood in this context: {action.get('text', None)}")
================================================
FILE: python/examples/music/demo.py
================================================
import asyncio
import json
import sys
from dotenv import dotenv_values
import schema as music
from typechat import Failure, TypeChatJsonTranslator, TypeChatValidator, create_language_model, process_requests
from client import handle_call, get_client_context
async def main():
env_vals = dotenv_values()
model = create_language_model(env_vals)
validator = TypeChatValidator(music.PlayerActions)
translator = TypeChatJsonTranslator(model, validator, music.PlayerActions)
player_context = await get_client_context(env_vals)
async def request_handler(message: str):
result = await translator.translate(message)
if isinstance(result, Failure):
print(result.message)
else:
result = result.value
print(json.dumps(result, indent=2))
try:
for action in result["actions"]:
await handle_call(action, player_context)
except Exception as error:
print("An exception occurred: ", error)
if any(item["actionName"] == "Unknown" for item in result["actions"]):
print("I did not understand the following")
for item in result["actions"]:
if item["actionName"] == "Unknown":
print(item["text"])
file_path = sys.argv[1] if len(sys.argv) == 2 else None
await process_requests("🎵> ", file_path, request_handler)
if __name__ == "__main__":
asyncio.run(main())
================================================
FILE: python/examples/music/input.txt
================================================
play Taylor Swift Shake It Off
get my top 20 favorites and make a playlist named animalTracks of the tracks that have animals in their names
get my favorite 100 tracks from the last two months and show only the ones by Bach
make it loud
get my favorite 80 tracks from the last 8 months and create one playlist named class8 containing the classical tracks and another playlist containing the blues tracks
toggle shuffle on and skip to the next track
go back to the last song
play my playlist class8
play the fourth one
show me my queue
================================================
FILE: python/examples/music/schema.py
================================================
from typing_extensions import Literal, Required, NotRequired, TypedDict, Annotated, Doc
class unknownActionParameters(TypedDict):
text: Annotated[str, "text typed by the user that the system did not understand"]
class UnknownAction(TypedDict):
"""
Use this action for requests that weren't understood
"""
actionName: Literal["Unknown"]
text: unknownActionParameters
class EmptyParameters(TypedDict):
pass
class PlayParameters(TypedDict, total=False):
artist: Annotated[str, Doc("artist (performer, composer) to search for to play")]
album: Annotated[str, Doc("album to search for to play")]
trackName: Annotated[str, Doc("track to search for to play")]
query: Annotated[str, Doc("other description to search for to play")]
itemType: Annotated[Literal["track", "album"], Doc("this property is only used when the user specifies the item type")]
quantity: Required[Annotated[
int,
Doc("number of items to play, examples: three, a/an (=1), a few (=3), a couple of (=2), some (=5). Use -1 for all, 0 if unspecified."),
]]
trackNumber: Annotated[int, Doc("play the track at this index in the current track list")]
trackRange: Annotated[list[int], Doc("play this range of tracks example 1-3")]
class PlayAction(TypedDict):
"""
play a track, album, or artist; this action is chosen over search if both could apply
with no parameters, play means resume playback
"""
actionName: Literal["play"]
parameters: PlayParameters
class StatusAction(TypedDict):
"""
show now playing including track information, and playback status including playback device
"""
actionName: Literal["status"]
parameters: EmptyParameters
class PauseAction(TypedDict):
"""
pause playback
"""
actionName: Literal["pause"]
parameters: EmptyParameters
class ResumeAction(TypedDict):
"""
resume playback
"""
actionName: Literal["resume"]
parameters: EmptyParameters
class NextAction(TypedDict):
"""
next track
"""
actionName: Literal["next"]
parameters: EmptyParameters
class PreviousAction(TypedDict):
"""
previous track
"""
actionName: Literal["previous"]
parameters: EmptyParameters
class ShuffleActionParameters(TypedDict):
on: bool
class ShuffleAction(TypedDict):
"""
turn shuffle on or off
"""
actionName: Literal["shuffle"]
parameters: ShuffleActionParameters
class ListDevicesAction(TypedDict):
"""
list available playback devices
"""
actionName: Literal["listDevices"]
parameters: EmptyParameters
class SelectDeviceActionParameters(TypedDict):
keyword: Annotated[str, Doc("keyword to match against device name")]
class SelectDeviceAction(TypedDict):
"""
select playback device by keyword
"""
actionName: Literal["selectDevice"]
parameters: SelectDeviceActionParameters
class SelectVolumeActionParameters(TypedDict):
newVolumeLevel: Annotated[int, Doc("new volume level")]
class SetVolumeAction(TypedDict):
"""
set volume
"""
actionName: Literal["setVolume"]
parameters: SelectVolumeActionParameters
class ChangeVolumeActionParameters(TypedDict):
volumeChangePercentage: Annotated[int, "volume change percentage"]
class ChangeVolumeAction(TypedDict):
"""
change volume plus or minus a specified percentage
"""
actionName: Literal["changeVolume"]
parameters: ChangeVolumeActionParameters
class SearchTracksActionParameters(TypedDict):
query: Annotated[
str,
Doc(
"""
the part of the request specifying the the search keywords
examples: song name, album name, artist name
"""),
]
class SearchTracksAction(TypedDict):
"""
this action is only used when the user asks for a search as in 'search', 'find', 'look for'
query is a Spotify search expression such as 'Rock Lobster' or 'te kanawa queen of night'
set the current track list to the result of the search
"""
actionName: Literal["searchTracks"]
parameters: SearchTracksActionParameters
class ListPlaylistsAction(TypedDict):
"""
list all playlists
"""
actionName: Literal["listPlaylists"]
parameters: EmptyParameters
class GetPlaylistActionParameters(TypedDict):
name: Annotated[str, "name of playlist to get"]
class GetPlaylistAction(TypedDict):
"""
get playlist by name
"""
actionName: Literal["getPlaylist"]
parameters: GetPlaylistActionParameters
class GetAlbumActionParameters(TypedDict):
name: Annotated[str, "name of album to get"]
class GetAlbumAction(TypedDict):
"""
get album by name; if name is "", use the currently playing track
set the current track list the tracks in the album
"""
actionName: Literal["getAlbum"]
parameters: GetPlaylistActionParameters
class GetFavoritesActionParameters(TypedDict):
count: NotRequired[Annotated[int, "number of favorites to get"]]
class GetFavoritesAction(TypedDict):
"""
Set the current track list to the user's favorite tracks
"""
actionName: Literal["getFavorites"]
parameters: GetFavoritesActionParameters
class FilterTracksActionParameters(TypedDict):
filterType: Annotated[
Literal["genre", "artist", "name"],
Doc("filter type is one of 'genre', 'artist', 'name'; name does a fuzzy match on the track name"),
]
filterValue: Annotated[str, Doc("filter value is the value to match against")]
negate: NotRequired[Annotated[bool, Doc("if negate is true, keep the tracks that do not match the filter")]]
class FilterTracksAction(TypedDict):
"""
apply a filter to match tracks in the current track list
set the current track list to the tracks that match the filter
"""
actionName: Literal["filterTracks"]
parameters: FilterTracksActionParameters
class CreatePlaylistActionParameters(TypedDict):
name: Annotated[str, "name of playlist to create"]
class CreatePlaylistAction(TypedDict):
"""
create a new playlist from the current track list
"""
actionName: Literal["createPlaylist"]
parameters: CreatePlaylistActionParameters
class DeletePlaylistActionParameters(TypedDict):
name: Annotated[str, Doc("name of playlist to delete")]
class DeletePlaylistAction(TypedDict):
"""
delete a playlist
"""
actionName: Literal["deletePlaylist"]
parameters: DeletePlaylistActionParameters
class GetQueueAction(TypedDict):
"""
set the current track list to the queue of upcoming tracks
"""
actionName: Literal["getQueue"]
parameters: EmptyParameters
PlayerAction = (
PlayAction
| StatusAction
| PauseAction
| ResumeAction
| NextAction
| PreviousAction
| ShuffleAction
| ListDevicesAction
| SelectDeviceAction
| SetVolumeAction
| ChangeVolumeAction
| SearchTracksAction
| ListPlaylistsAction
| GetPlaylistAction
| GetAlbumAction
| GetFavoritesAction
| FilterTracksAction
| CreatePlaylistAction
| DeletePlaylistAction
| GetQueueAction
| UnknownAction
)
class PlayerActions(TypedDict):
actions: list[PlayerAction]
================================================
FILE: python/examples/music/spotipyWrapper.py
================================================
from typing_extensions import Any
from dataclasses import dataclass, field
import spotipy # type: ignore
# The spotipy library does not provide type hints or async methods. This file has some wrappers and stubs
# to give just-enough typing for the demo
# This class holds the Track info needed for our use
@dataclass
class SimplifiedTrackInfo:
name: str
uri: str
artistNames: list[str]
artistUris: list[str]
albumName: str
genres: list[str] = field(default_factory=list)
# This class holds the Playlist info needed for our use
@dataclass
class SimplifiedPlaylistInfo:
name: str
id: str
# This wrapper class allows the rest of the code to use type hints and async pattern
class AsyncSpotipy:
_service: spotipy.Spotify
def __init__(self, service: spotipy.Spotify):
super().__init__()
self._service = service
async def devices(self) -> dict[str, Any]:
return self._service.devices() # type: ignore
async def search(
self, q: str, limit: int = 10, offset: int = 0, type: str = "track", market: str | None = None
) -> dict[str, Any]:
return self._service.search(q=q, limit=limit, offset=offset, type=type, market=market) # type: ignore
async def next(self, result: dict[str, Any]) -> dict[str, Any]:
return self._service.next(result=result) # type: ignore
async def artist(self, artist_id: str) -> dict[str, Any]:
return self._service.artist(artist_id=artist_id) # type: ignore
async def album(self, album_id: str, market: str | None = None) -> dict[str, Any]:
return self._service.album(album_id=album_id, market=market) # type: ignore
async def queue(self) -> dict[str, Any]:
return self._service.queue() # type: ignore
async def current_playback(self, market: str | None = None, additional_types: str | None = None) -> dict[str, Any]:
return self._service.current_playback(market=market, additional_types=additional_types) # type: ignore
async def start_playback(
self,
device_id: str | None = None,
context_uri: str | None = None,
uris: list[str] | None = None,
offset: dict[str, int] | None = None,
position_ms: int | None = None,
) -> None:
return self._service.start_playback(device_id=device_id, context_uri=context_uri, uris=uris, offset=offset, position_ms=position_ms) # type: ignore
async def pause_playback(self, device_id: str | None = None) -> None:
return self._service.pause_playback(device_id=device_id) # type: ignore
async def next_track(self, device_id: str | None = None) -> None:
return self._service.next_track(device_id=device_id) # type: ignore
async def previous_track(self, device_id: str | None = None) -> None:
return self._service.previous_track(device_id=device_id) # type: ignore
async def volume(self, volume_percent: int, device_id: str | None = None) -> None:
return self._service.volume(volume_percent=volume_percent, device_id=device_id) # type: ignore
async def shuffle(self, state: bool, device_id: str | None = None) -> None:
return self._service.shuffle(state=state, device_id=device_id) # type: ignore
async def transfer_playback(self, device_id: str, force_play: bool = True) -> None:
return self._service.transfer_playback(device_id=device_id, force_play=force_play) # type: ignore
async def current_user_top_tracks(
self, limit: int = 20, offset: int = 0, time_range: str = "medium_term"
) -> dict[str, Any]:
return self._service.current_user_top_tracks(limit=limit, offset=offset, time_range=time_range) # type: ignore
async def current_user_playlists(self, limit: int = 50, offset: int = 0) -> dict[str, Any]:
return self._service.current_user_playlists(limit=limit, offset=offset) # type: ignore
async def user_playlist_create(
self, user: str, name: str, public: bool = True, collaborative: bool = False, description: str = ""
) -> dict[str, Any]:
return self._service.user_playlist_create(user=user, name=name, public=public, collaborative=collaborative, description=description) # type: ignore
async def playlist_items(
self,
playlist_id: str,
fields: str | None = None,
limit: int = 100,
offset: int = 0,
market: str | None = None,
additional_types: list[str] | None = None,
) -> dict[str, Any]:
return self._service.playlist_items(playlist_id=playlist_id, fields=fields, limit=limit, offset=offset, market=market, additional_types=additional_types) # type: ignore
async def playlist_add_items(self, playlist_id: str, items: list[str], position: int | None = None) -> None:
return self._service.playlist_add_items(playlist_id=playlist_id, items=items, position=position) # type: ignore
async def current_user_unfollow_playlist(self, playlist_id: str) -> None:
return self._service.current_user_unfollow_playlist(playlist_id=playlist_id) # type: ignore
================================================
FILE: python/examples/restaurant/README.md
================================================
# Restaurant
The Restaurant example shows how to capture user intent as a set of "nouns", but with more complex linguistic input.
This example can act as a "stress test" for language models, illustrating the line between simpler and more advanced language models in handling compound sentences, distractions, and corrections.
This example also shows how we can create a "user intent summary" to display to a user.
It uses a natural language experience for placing an order with the [`Order`](./schema.py) type.
# Try Restaurant
To run the Restaurant 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`](./input.txt).
For example, given the following order:
**Input**:
```
🍕> I want three pizzas, one with mushrooms and the other two with sausage. Make one sausage a small. And give me a whole Greek and a Pale Ale. And give me a Mack and Jacks.
```
**Output**:
*This is GPT-4-0613 output; GPT-3.5-turbo and most other models miss this one.*
```
1 large pizza with mushrooms
1 large pizza with sausage
1 small pizza with sausage
1 whole Greek salad
1 Pale Ale
1 Mack and Jacks
```
> **Note**
>
> Across different models, you may see that model responses may not correspond to the user intent.
> In the above example, some models may not be able to capture the fact that the order is still only for 3 pizzas,
> and that "make one sausage a small" is not a request for a new pizza.
>
> ```diff
> 1 large pizza with mushrooms
> - 1 large pizza with sausage
> + 2 large pizza with sausage
> 1 small pizza with sausage
> 1 whole Greek salad
> 1 Pale Ale
> 1 Mack and Jacks
> ```
>
> The output here from GPT 3.5-turbo incorrectly shows 1 mushroom pizza and 3 sausage pizzas.
Because all language models are probabilistic and therefore will sometimes output incorrect inferences, the TypeChat pattern includes asking the user for confirmation (or giving the user an easy way to undo actions). It is important to ask for confirmation without use of the language model so that incorrect inference is guaranteed not to be part of the intent summary generated.
In this example, the function `printOrder` in the file `main.ts` summarizes the food order (as seen in the above output) without use of a language model. The `printOrder` function can work with a strongly typed `Order object` because the TypeChat validation process has checked that the emitted JSON corresponds to the `Order` type:
```typescript
function printOrder(order: Order) {
```
Having a validated, typed data structure simplifies the task of generating a succinct summary suitable for user confirmation.
================================================
FILE: python/examples/restaurant/demo.py
================================================
import asyncio
import json
import sys
from dotenv import dotenv_values
import schema as restaurant
from typechat import Failure, TypeChatJsonTranslator, TypeChatValidator, create_language_model, process_requests
async def main():
env_vals = dotenv_values()
model = create_language_model(env_vals)
validator = TypeChatValidator(restaurant.Order)
translator = TypeChatJsonTranslator(model, validator, restaurant.Order)
async def request_handler(message: str):
result = await translator.translate(message)
if isinstance(result, Failure):
print(result.message)
else:
result = result.value
print(json.dumps(result, indent=2))
if any(item["itemType"] == "Unknown" for item in result["items"]):
print("I did not understand the following")
for item in result["items"]:
if item["itemType"] == "Unknown":
print(item["text"])
file_path = sys.argv[1] if len(sys.argv) == 2 else None
await process_requests("🍕> ", file_path, request_handler)
if __name__ == "__main__":
asyncio.run(main())
================================================
FILE: python/examples/restaurant/input.txt
================================================
I'd like two large, one with pepperoni and the other with extra sauce. The pepperoni gets basil and the extra sauce gets Canadian bacon. And add a whole salad. Make the Canadian bacon a medium. Make the salad a Greek with no red onions. And give me two Mack and Jacks and a Sierra Nevada. Oh, and add another salad with no red onions.
I'd like two large with olives and mushrooms. And the first one gets extra sauce. The second one gets basil. Both get arugula. And add a Pale Ale. Give me a two Greeks with no red onions, a half and a whole. And a large with sausage and mushrooms. Plus three Pale Ales and a Mack and Jacks.
I'll take two large with pepperoni. Put olives on one of them. Make the olive a small. And give me whole Greek plus a Pale Ale and an M&J.
I want three pizzas, one with mushrooms and the other two with sausage. Make one sausage a small. And give me a whole Greek and a Pale Ale. And give me a Mack and Jacks.
I would like to order one with basil and one with extra sauce. Throw in a salad and an ale.
I would love to have a pepperoni with extra sauce, basil and arugula. Lovely weather we're having. Throw in some pineapple. And give me a whole Greek and a Pale Ale. Boy, those Mariners are doggin it. And how about a Mack and Jacks.
I'll have two pepperoni, the first with extra sauce and the second with basil. Add pineapple to the first and add olives to the second.
I sure am hungry for a pizza with pepperoni and a salad with no croutons. And I'm thirsty for 3 Pale Ales
give me three regular salads and two Greeks and make the regular ones with no red onions
I'll take four large pepperoni pizzas. Put extra sauce on two of them. plus an M&J and a Pale Ale
I'll take a yeti, a pale ale and a large with olives and take the extra cheese off the yeti and add a Greek
I'll take a medium Pig with no arugula
I'll take a small Pig with no arugula and a Greek with croutons and no red onions
================================================
FILE: python/examples/restaurant/schema.py
================================================
from typing_extensions import Literal, Required, NotRequired, TypedDict, Annotated, Doc
class UnknownText(TypedDict):
"""
Use this type for order items that match nothing else
"""
itemType: Literal["Unknown"]
text: Annotated[str, "The text that wasn't understood"]
class Pizza(TypedDict, total=False):
itemType: Required[Literal["Pizza"]]
size: Annotated[Literal["small", "medium", "large", "extra large"], "default: large"]
addedToppings: Annotated[list[str], Doc("toppings requested (examples: pepperoni, arugula)")]
removedToppings: Annotated[list[str], Doc("toppings requested to be removed (examples: fresh garlic, anchovies)")]
quantity: Annotated[int, "default: 1"]
name: Annotated[
Literal["Hawaiian", "Yeti", "Pig In a Forest", "Cherry Bomb"],
Doc("used if the requester references a pizza by name"),
]
class Beer(TypedDict):
itemType: Literal["Beer"]
kind: Annotated[str, Doc("examples: Mack and Jacks, Sierra Nevada Pale Ale, Miller Lite")]
quantity: NotRequired[Annotated[int, "default: 1"]]
SaladSize = Literal["half", "whole"]
SaladStyle = Literal["Garden", "Greek"]
class Salad(TypedDict, total=False):
itemType: Required[Literal["Salad"]]
portion: Annotated[str, "default: half"]
style: Annotated[str, "default: Garden"]
addedIngredients: Annotated[list[str], Doc("ingredients requested (examples: parmesan, croutons)")]
removedIngredients: Annotated[list[str], Doc("ingredients requested to be removed (example: red onions)")]
quantity: Annotated[int, "default: 1"]
OrderItem = Pizza | Beer | Salad
class Order(TypedDict):
items: list[OrderItem | UnknownText]
================================================
FILE: python/examples/sentiment/README.md
================================================
# Sentiment
The Sentiment example shows how to match user intent to a set of nouns, in this case categorizing user sentiment of the input as negative, neutral, or positive with the [`SentimentResponse`](./schema.py) type.
# Try Sentiment
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`](./input.txt).
For example, given the following input statement:
**Input**:
```
😀> TypeChat is awesome!
```
**Output**:
```
The sentiment is positive
```
================================================
FILE: python/examples/sentiment/demo.py
================================================
import asyncio
import sys
from dotenv import dotenv_values
from typechat import (Failure, TypeChatJsonTranslator, TypeChatValidator,
create_language_model, process_requests)
import schema as sentiment
async def main():
env_vals = dotenv_values()
model = create_language_model(env_vals)
validator = TypeChatValidator(sentiment.Sentiment)
translator = TypeChatJsonTranslator(model, validator, sentiment.Sentiment)
async def request_handler(message: str):
result = await translator.translate(message)
if isinstance(result, Failure):
print(result.message)
else:
result = result.value
print(f"The sentiment is {result.sentiment}")
file_path = sys.argv[1] if len(sys.argv) == 2 else None
await process_requests("😀> ", file_path, request_handler)
if __name__ == "__main__":
asyncio.run(main())
================================================
FILE: python/examples/sentiment/input.txt
================================================
hello, world
TypeChat is awesome!
I'm having a good day
it's very rainy outside
================================================
FILE: python/examples/sentiment/schema.py
================================================
from dataclasses import dataclass
from typing_extensions import Literal, Annotated, Doc
@dataclass
class Sentiment:
"""
The following is a schema definition for determining the sentiment of a some user input.
"""
sentiment: Annotated[Literal["negative", "neutral", "positive"], Doc("The sentiment for the text")]
================================================
FILE: python/notebooks/calendar.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%pip install --upgrade setuptools\n",
"%pip install --upgrade gradio\n",
"%pip install ipywidgets\n",
"%pip install pandas\n",
"%pip install tabulate\n",
"%pip install python-dotenv"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%pip install -e ../"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import json\n",
"import setuptools\n",
"\n",
"import os\n",
"import sys\n",
"module_path = os.path.abspath(os.path.join('..'))\n",
"if module_path not in sys.path:\n",
" sys.path.append(module_path)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from dotenv import dotenv_values\n",
"from typechat import Failure, TypeChatJsonTranslator, TypeChatValidator, create_language_model\n",
"from examples.calendar import schema as calendar"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"model = create_language_model(dotenv_values())\n",
"validator = TypeChatValidator(calendar.CalendarActions)\n",
"translator = TypeChatJsonTranslator(model, validator, calendar.CalendarActions)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas\n",
"\n",
"async def get_translation(message, history):\n",
" result = await translator.translate(message)\n",
" if isinstance(result, Failure):\n",
" return f\"Translation Failed ❌ \\n Context: {result.message}\"\n",
" else:\n",
" result = result.value\n",
" df = pandas.DataFrame.from_dict(result[\"actions\"])\n",
" return f\"Translation Succeeded! ✅\\n Table View \\n ``` {df.fillna('').to_markdown(tablefmt='grid')} \\n ``` \\n\"\n",
"\n",
"def get_examples():\n",
" example_prompts = []\n",
" with open('../examples/calendar/input.txt') as prompts_file:\n",
" for line in prompts_file:\n",
" example_prompts.append(line)\n",
" return example_prompts"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import gradio as gr\n",
"\n",
"gr.ChatInterface(get_translation, title=\"📅 Calendar\", examples=get_examples()).launch()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.1"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
================================================
FILE: python/notebooks/coffeeShop.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%pip install --upgrade setuptools\n",
"%pip install --upgrade gradio"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import json\n",
"\n",
"\n",
"import sys\n",
"from dotenv import dotenv_values\n",
"\n",
"import os\n",
"import sys\n",
"module_path = os.path.abspath(os.path.join('..'))\n",
"if module_path not in sys.path:\n",
" sys.path.append(module_path)\n",
"\n",
"from examples.coffeeShop import schema as coffeeshop\n",
"from typechat import Failure, TypeChatJsonTranslator, TypeChatValidator, create_language_model"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"env_vals = dotenv_values()\n",
"model = create_language_model(env_vals)\n",
"validator = TypeChatValidator(coffeeshop.Cart)\n",
"translator = TypeChatJsonTranslator(model, validator, coffeeshop.Cart)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas\n",
"async def get_translation(message, history):\n",
" result = await translator.translate(message)\n",
" if isinstance(result, Failure):\n",
" return f\"Translation Failed ❌ \\n Context: {result.message}\"\n",
" else:\n",
" result = result.value\n",
" df = pandas.DataFrame.from_dict(result[\"items\"])\n",
" #return f\"Translation Succeeded! ✅\\n JSON View \\n ``` {json.dumps(result, indent=2)} \\n ``` \\n\"\n",
" return f\"Translation Succeeded! ✅\\n Coffee Shop Items \\n ``` {df.fillna('').to_markdown(tablefmt='grid')} \\n ``` \\n\"\n",
"\n",
"def get_examples():\n",
" example_prompts = []\n",
" with open('../examples/coffeeShop/input.txt') as prompts_file:\n",
" for line in prompts_file:\n",
" example_prompts.append(line)\n",
" return example_prompts\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import setuptools\n",
"import gradio as gr\n",
"\n",
"gr.ChatInterface(get_translation, title=\"☕ Coffee\", examples=get_examples()).launch()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.1"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
================================================
FILE: python/notebooks/healthData.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%pip install --upgrade setuptools\n",
"%pip install --upgrade gradio"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import json\n",
"import setuptools\n",
"\n",
"import os\n",
"import sys\n",
"module_path = os.path.abspath(os.path.join('..'))\n",
"if module_path not in sys.path:\n",
" sys.path.append(module_path)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from dotenv import dotenv_values\n",
"from typechat import Failure, TypeChatValidator, create_language_model\n",
"from examples.healthData import schema as health\n",
"from examples.healthData.translator import TranslatorWithHistory"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"health_instructions = \"\"\"\n",
"Help me enter my health data step by step.\n",
"Ask specific questions to gather required and optional fields I have not already providedStop asking if I don't know the answer\n",
"Automatically fix my spelling mistakes\n",
"My health data may be complex: always record and return ALL of it.\n",
"Always return a response:\n",
"- If you don't understand what I say, ask a question.\n",
"- At least respond with an OK message.\n",
"\n",
"\"\"\"\n",
"\n",
"env_vals = dotenv_values()\n",
"model = create_language_model(env_vals)\n",
"validator = TypeChatValidator(health.HealthDataResponse)\n",
"translator = TranslatorWithHistory(model, validator, health.HealthDataResponse, additional_agent_instructions=health_instructions)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas\n",
"\n",
"async def get_translation(message, history):\n",
" result = await translator.translate(message)\n",
" if isinstance(result, Failure):\n",
" return f\"Translation Failed ❌ \\n Context: {result.message}\"\n",
" else:\n",
" result = result.value\n",
" output = f\"Translation Succeeded! ✅\\n\"\n",
" \n",
" data = result.get(\"data\", None)\n",
" if data:\n",
" df = pandas.DataFrame.from_dict(data)\n",
" output += f\"HealthData \\n ``` {df.fillna('').to_markdown(tablefmt='grid')} \\n ``` \\n\"\n",
"\n",
" message = result.get(\"message\", None)\n",
" not_translated = result.get(\"notTranslated\", None)\n",
"\n",
" if message:\n",
" output += f\"\\n📝: {message}\"\n",
" \n",
" if not_translated:\n",
" output += f\"\\n🤔: I did not understand\\n {not_translated}\" \n",
" \n",
" return output\n",
"\n",
"\n",
"def get_examples():\n",
" example_prompts = []\n",
" with open('../examples/healthData/input.txt') as prompts_file:\n",
" for line in prompts_file:\n",
" example_prompts.append(line)\n",
" return example_prompts\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import gradio as gr\n",
"\n",
"gr.ChatInterface(get_translation, title=\"💉💊🤧 Health Data\", examples=get_examples()).launch(debug=False)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
================================================
FILE: python/notebooks/math.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%pip install --upgrade setuptools\n",
"%pip install --upgrade gradio"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import json\n",
"import setuptools\n",
"\n",
"import os\n",
"import sys\n",
"module_path = os.path.abspath(os.path.join('..'))\n",
"if module_path not in sys.path:\n",
" sys.path.append(module_path)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from dotenv import dotenv_values\n",
"from typechat import Failure, create_language_model\n",
"from examples.math.program import TypeChatProgramTranslator, TypeChatProgramValidator, evaluate_json_program\n",
"from examples.math import schema as math"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"env_vals = dotenv_values()\n",
"model = create_language_model(env_vals)\n",
"validator = TypeChatProgramValidator()\n",
"translator = TypeChatProgramTranslator(model, validator, math.MathAPI)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas\n",
"async def handleCall(func:str, args: list[int|float]) -> int|float:\n",
" print(f\"{func}({json.dumps(args)}) \")\n",
" match func:\n",
" case \"add\":\n",
" return args[0] + args[1]\n",
" case \"sub\":\n",
" return args[0] - args[1]\n",
" case \"mul\":\n",
" return args[0] * args[1]\n",
" case \"div\":\n",
" return args[0] / args[1]\n",
" case \"neg\":\n",
" return -1 * args[0]\n",
" case \"id\":\n",
" return args[0]\n",
" case _:\n",
" raise ValueError(f'Unexpected function name {func}')\n",
" \n",
"async def get_translation(message, history):\n",
" result = await translator.translate(message)\n",
" if isinstance(result, Failure):\n",
" return f\"Translation Failed ❌ \\n Context: {result.message}\"\n",
" else:\n",
" result = result.value\n",
" math_result = await evaluate_json_program(result, handleCall)\n",
" df = pandas.DataFrame.from_dict(result[\"@steps\"])\n",
" return f\"Translation Succeeded! ✅\\n Here is a table of operations needed to get the answer \\n ``` {df.fillna('').to_markdown(tablefmt='grid')} \\n ``` \\n Math Result: {math_result}\"\n",
"\n",
"\n",
"def get_examples():\n",
" example_prompts = []\n",
" with open('../examples/math/input.txt') as prompts_file:\n",
" for line in prompts_file:\n",
" example_prompts.append(line)\n",
" return example_prompts"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import gradio as gr\n",
"\n",
"gr.ChatInterface(get_translation, title=\"🧮 Math\", examples=get_examples()).launch()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
================================================
FILE: python/notebooks/music.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%pip install --upgrade setuptools\n",
"%pip install --upgrade gradio\n",
"%pip install ipywidgets\n",
"%pip install openai\n",
"%pip install pandas\n",
"%pip install tabulate\n",
"%pip install python-dotenv"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%pip install -e ../"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import json\n",
"import setuptools\n",
"\n",
"import os\n",
"import sys\n",
"module_path = os.path.abspath(os.path.join('..'))\n",
"if module_path not in sys.path:\n",
" sys.path.append(module_path)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from dotenv import dotenv_values\n",
"from typechat import Failure, TypeChatJsonTranslator, TypeChatValidator, create_language_model\n",
"from examples.music import schema as music\n",
"from examples.music.client import handle_call, get_client_context"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"env_vals = dotenv_values()\n",
"model = create_language_model(env_vals)\n",
"validator = TypeChatValidator(music.PlayerActions)\n",
"translator = TypeChatJsonTranslator(model, validator, music.PlayerActions)\n",
"player_context = await get_client_context(env_vals)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas\n",
"\n",
"async def get_translation(message, history):\n",
" result = await translator.translate(message)\n",
" if isinstance(result, Failure):\n",
" return f\"Translation Failed ❌ \\n Context: {result.message}\"\n",
" else:\n",
" result = result.value\n",
" df = pandas.DataFrame.from_dict(result[\"actions\"])\n",
" try:\n",
" for action in result[\"actions\"]:\n",
" await handle_call(action, player_context)\n",
" return f\"Translation Succeeded! ✅\\n Table View \\n ``` {df.fillna('').to_markdown(tablefmt='grid')} \\n ``` \\n\"\n",
" except Exception as error:\n",
" return f\"An exception occurred: {error}\"\n",
" \n",
"\n",
"def get_examples():\n",
" example_prompts = []\n",
" with open('../examples/music/input.txt') as prompts_file:\n",
" for line in prompts_file:\n",
" example_prompts.append(line)\n",
" return example_prompts"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import gradio as gr\n",
"\n",
"gr.ChatInterface(get_translation, title=\"🎵 Music\", examples=get_examples()).launch()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
================================================
FILE: python/notebooks/restaurant.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%pip install --upgrade setuptools\n",
"%pip install --upgrade gradio"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import json\n",
"import setuptools\n",
"\n",
"import os\n",
"import sys\n",
"module_path = os.path.abspath(os.path.join('..'))\n",
"if module_path not in sys.path:\n",
" sys.path.append(module_path)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from dotenv import dotenv_values\n",
"from typechat import Failure, TypeChatJsonTranslator, TypeChatValidator, create_language_model\n",
"from examples.restaurant import schema as restaurant"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"env_vals = dotenv_values()\n",
"model = create_language_model(env_vals)\n",
"validator = TypeChatValidator(restaurant.Order)\n",
"translator = TypeChatJsonTranslator(model, validator, restaurant.Order)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas\n",
"async def get_translation(message, history):\n",
" result = await translator.translate(message)\n",
" if isinstance(result, Failure):\n",
" return f\"Translation Failed ❌ \\n Context: {result.message}\"\n",
" else:\n",
" result = result.value\n",
" df = pandas.DataFrame.from_dict(result[\"items\"])\n",
" return f\"Translation Succeeded! ✅\\n Restaurant orders \\n ``` {df.fillna('').to_markdown(tablefmt='grid')} \\n ``` \\n\"\n",
"\n",
"\n",
"def get_examples():\n",
" example_prompts = []\n",
" with open('../examples/restaurant/input.txt') as prompts_file:\n",
" for line in prompts_file:\n",
" example_prompts.append(line)\n",
" return example_prompts\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import gradio as gr\n",
"\n",
"gr.ChatInterface(get_translation, title=\"🍕 Restaurant\", examples=get_examples()).launch()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
================================================
FILE: python/notebooks/sentiment.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%pip install --upgrade setuptools\n",
"%pip install --upgrade gradio"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import json\n",
"\n",
"\n",
"import sys\n",
"from dotenv import dotenv_values\n",
"\n",
"import os\n",
"import sys\n",
"module_path = os.path.abspath(os.path.join('..'))\n",
"if module_path not in sys.path:\n",
" sys.path.append(module_path)\n",
"\n",
"from examples.sentiment import schema as sentiment\n",
"from typechat import Failure, TypeChatJsonTranslator, TypeChatValidator, create_language_model"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"env_vals = dotenv_values()\n",
"model = create_language_model(env_vals)\n",
"validator = TypeChatValidator(sentiment.Sentiment)\n",
"translator = TypeChatJsonTranslator(model, validator, sentiment.Sentiment)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"async def get_translation(message, history):\n",
" result = await translator.translate(message)\n",
" if isinstance(result, Failure):\n",
" return f\"Translation Failed ❌ \\n Context: {result.message}\"\n",
" else:\n",
" result = result.value\n",
" return f\"Translation Succeeded! ✅\\n The sentiment is {result['sentiment']}\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import setuptools\n",
"import gradio as gr\n",
"\n",
"gr.ChatInterface(get_translation, title=\"😀 Sentiment\").launch()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
================================================
FILE: python/package.json
================================================
{
"name": "typechat-py",
"private": true,
"version": "0.0.4",
"description": "TypeChat is a library that makes it easy to build natural language interfaces using types.",
"scripts": {
"check": "pyright"
},
"author": "Microsoft",
"license": "MIT",
"devDependencies": {
"pyright": "1.1.358"
}
}
================================================
FILE: python/pyproject.toml
================================================
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "typechat"
dynamic = ["version"]
description = 'TypeChat is a library that makes it easy to build natural language interfaces using types.'
readme = "README.md"
requires-python = ">=3.11"
license = "MIT"
keywords = []
authors = [
{ name = "Microsoft Corporation" },
]
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
dependencies = [
"pydantic>=2.5.2",
"pydantic_core>=2.16.3",
"httpx>=0.27.0",
"typing_extensions>=4.10.0",
]
[project.optional-dependencies]
# Development-time dependencies.
dev = [
"coverage[toml]>=6.5",
"pytest>=8.0.2",
"syrupy>=5.0.0",
]
# Dependencies for examples.
examples = [
"python-dotenv>=1.0.0",
"spotipy",
]
[project.urls]
Documentation = "https://github.com/microsoft/TypeChat#readme"
Issues = "https://github.com/microsoft/TypeChat/issues"
Source = "https://github.com/microsoft/TypeChat"
[tool.hatch.version]
path = "src/typechat/__about__.py"
[tool.hatch.envs.default]
# While users can always look up the virtual environment
# to select the right interpreter for their editor, often editors can
# automatically pick up on a local `.venv` or at least hint towards using it.
# The only catch is that this tends to only kick in at the workspace root.
type = "virtual"
path = "../.venv"
# Include dependencies from optional-dependencies for
# development of the core package along with examples.
features = [
"dev",
"examples"
]
[tool.hatch.envs.default.scripts]
test = "pytest {args:tests}"
test-cov = "coverage run -m pytest {args:tests}"
cov-report = [
"- coverage combine",
"coverage report",
]
cov = [
"test-cov",
"cov-report",
]
[[tool.hatch.envs.all.matrix]]
python = ["3.11", "3.12"]
[tool.hatch.envs.lint]
detached = true
dependencies = [
"black>=23.1.0",
"mypy>=1.0.0",
"ruff>=0.0.243",
]
[tool.hatch.envs.lint.scripts]
typing = [
"npx pyright",
# mypy should not include tests, as it does not fully support
# PEP 695 (type aliases, type parameters, etc.)
# https://github.com/python/mypy/issues/1523895
"mypy --install-types --non-interactive {args:src/typechat}"
]
style = [
"ruff {args:.}",
"black --check --diff {args:.}",
]
fmt = [
"black {args:.}",
"ruff --fix {args:.}",
"style",
]
all = [
"style",
"typing",
]
[tool.mypy]
python_version = "3.11"
untyped_calls_exclude = ["spotipy"]
[tool.black]
target-version = ["py311"]
line-length = 120
skip-string-normalization = true
[tool.ruff]
target-version = "py311"
line-length = 120
select = [
"A",
"ARG",
"B",
"C",
"DTZ",
"E",
"EM",
"F",
"FBT",
"I",
"ICN",
"ISC",
"N",
"PLC",
"PLE",
"PLR",
"PLW",
"Q",
"RUF",
"S",
"T",
"TID",
"UP",
"W",
"YTT",
]
ignore = [
# # Allow non-abstract empty methods in abstract base classes
# "B027",
# # Allow boolean positional values in function calls, like `dict.get(... True)`
# "FBT003",
# # Ignore checks for possible passwords
# "S105", "S106", "S107",
# # Ignore complexity
# "C901", "PLR0911", "PLR0912", "PLR0913", "PLR0915",
]
unfixable = [
# # Don't touch unused imports
# "F401",
]
[tool.ruff.isort]
known-first-party = ["typechat"]
[tool.ruff.flake8-tidy-imports]
ban-relative-imports = "all"
[tool.ruff.per-file-ignores]
# Tests can use magic values, assertions, and relative imports
"tests/**/*" = ["PLR2004", "S101", "TID252"]
[tool.coverage.run]
source_pkgs = ["typechat", "tests"]
branch = true
parallel = true
omit = [
"src/typechat/__about__.py",
]
[tool.coverage.paths]
typechat = ["src/typechat", "*/typechat/src/typechat"]
tests = ["tests", "*/typechat/tests"]
[tool.coverage.report]
exclude_lines = [
"no cov",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
]
================================================
FILE: python/pyrightconfig.json
================================================
{
"typeCheckingMode": "strict",
"reportCallInDefaultInitializer": "error",
"reportImplicitOverride": "error",
"reportImplicitStringConcatenation": "error",
"reportImportCycles": "error",
"reportMissingSuperCall": "error",
"reportPropertyTypeMismatch": "error",
"reportShadowedImports": "error",
"reportUninitializedInstanceVariable": "error",
"reportUnnecessaryTypeIgnoreComment": "error",
"reportUnusedCallResult": "none",
"pythonVersion": "3.11",
"include": [
"**/*",
],
}
================================================
FILE: python/src/typechat/__about__.py
================================================
# SPDX-FileCopyrightText: Microsoft Corporation
#
# SPDX-License-Identifier: MIT
__version__ = "0.0.4"
================================================
FILE: python/src/typechat/__init__.py
================================================
# SPDX-FileCopyrightText: Microsoft Corporation
#
# SPDX-License-Identifier: MIT
from typechat._internal.model import PromptSection, TypeChatLanguageModel, create_language_model, create_openai_language_model, create_azure_openai_language_model
from typechat._internal.result import Failure, Result, Success
from typechat._internal.translator import TypeChatJsonTranslator
from typechat._internal.ts_conversion import python_type_to_typescript_schema
from typechat._internal.validator import TypeChatValidator
from typechat._internal.interactive import process_requests
__all__ = [
"TypeChatLanguageModel",
"TypeChatJsonTranslator",
"TypeChatValidator",
"Success",
"Failure",
"Result",
"python_type_to_typescript_schema",
"PromptSection",
"create_language_model",
"create_openai_language_model",
"create_azure_openai_language_model",
"process_requests",
]
================================================
FILE: python/src/typechat/_internal/__init__.py
================================================
================================================
FILE: python/src/typechat/_internal/interactive.py
================================================
from typing import Callable, Awaitable
async def process_requests(interactive_prompt: str, input_file_name: str | None, process_request: Callable[[str], Awaitable[None]]):
"""
A request processor for interactive input or input from a text file. If an input file name is specified,
the callback function is invoked for each line in file. Otherwise, the callback function is invoked for
each line of interactive input until the user types "quit" or "exit".
Args:
interactive_prompt: Prompt to present to user.
input_file_name: Input text file name, if any.
process_request: Async callback function that is invoked for each interactive input or each line in text file.
"""
if input_file_name is not None:
with open(input_file_name, "r") as file:
lines = filter(str.rstrip, file)
for line in lines:
if line.startswith("# "):
continue
print(interactive_prompt + line)
await process_request(line)
else:
try:
# Use readline to enable input editing and history
import readline # type: ignore
except ImportError:
pass
while True:
try:
line = input(interactive_prompt)
except EOFError:
print("\n")
break
if line.lower().strip() in ("quit", "exit"):
break
else:
await process_request(line)
================================================
FILE: python/src/typechat/_internal/model.py
================================================
import asyncio
from types import TracebackType
from typing_extensions import AsyncContextManager, Literal, Protocol, Self, TypedDict, cast, override
from typechat._internal.result import Failure, Result, Success
import httpx
class PromptSection(TypedDict):
"""
Represents a section of an LLM prompt with an associated role. TypeChat uses the "user" role for
prompts it generates and the "assistant" role for previous LLM responses (which will be part of
the prompt in repair attempts). TypeChat currently doesn't use the "system" role.
"""
role: Literal["system", "user", "assistant"]
content: str
class TypeChatLanguageModel(Protocol):
async def complete(self, prompt: str | list[PromptSection]) -> Result[str]:
"""
Represents a AI language model that can complete prompts.
TypeChat uses an implementation of this protocol to communicate
with an AI service that can translate natural language requests to JSON
instances according to a provided schema.
The `create_language_model` function can create an instance.
"""
...
_TRANSIENT_ERROR_CODES = [
429,
500,
502,
503,
504,
]
class HttpxLanguageModel(TypeChatLanguageModel, AsyncContextManager):
url: str
headers: dict[str, str]
default_params: dict[str, str]
# Specifies the maximum number of retry attempts.
max_retry_attempts: int = 3
# Specifies the delay before retrying in milliseconds.
retry_pause_seconds: float = 1.0
# Specifies how long a request should wait in seconds
# before timing out with a Failure.
timeout_seconds = 10
_async_client: httpx.AsyncClient
def __init__(self, url: str, headers: dict[str, str], default_params: dict[str, str]):
super().__init__()
self.url = url
self.headers = headers
self.default_params = default_params
self._async_client = httpx.AsyncClient()
@override
async def complete(self, prompt: str | list[PromptSection]) -> Success[str] | Failure:
headers = {
"Content-Type": "application/json",
**self.headers,
}
if isinstance(prompt, str):
prompt = [{"role": "user", "content": prompt}]
body = {
**self.default_params,
"messages": prompt,
"temperature": 0.0,
"n": 1,
}
retry_count = 0
while True:
try:
response = await self._async_client.post(
self.url,
headers=headers,
json=body,
timeout=self.timeout_seconds
)
if response.is_success:
json_result = cast(
dict[Literal["choices"], list[dict[Literal["message"], PromptSection]]],
response.json()
)
return Success(json_result["choices"][0]["message"]["content"] or "")
if response.status_code not in _TRANSIENT_ERROR_CODES or retry_count >= self.max_retry_attempts:
return Failure(f"REST API error {response.status_code}: {response.reason_phrase}")
except Exception as e:
if retry_count >= self.max_retry_attempts:
return Failure(str(e) or f"{repr(e)} raised from within internal TypeChat language model.")
await asyncio.sleep(self.retry_pause_seconds)
retry_count += 1
@override
async def __aenter__(self) -> Self:
return self
@override
async def __aexit__(self, __exc_type: type[BaseException] | None, __exc_value: BaseException | None, __traceback: TracebackType | None) -> bool | None:
await self._async_client.aclose()
def __del__(self):
try:
asyncio.get_running_loop().create_task(self._async_client.aclose())
except Exception:
pass
def create_language_model(vals: dict[str, str | None]) -> HttpxLanguageModel:
"""
Creates a language model encapsulation of an OpenAI or Azure OpenAI REST API endpoint
chosen by a dictionary of variables (typically just `os.environ`).
If an `OPENAI_API_KEY` environment variable exists, an OpenAI model is constructed.
The `OPENAI_ENDPOINT` and `OPENAI_MODEL` environment variables must also be defined or an error will be raised.
If an `AZURE_OPENAI_API_KEY` environment variable exists, an Azure OpenAI model is constructed.
The `AZURE_OPENAI_ENDPOINT` environment variable must also be defined or an exception will be thrown.
If none of these key variables are defined, an exception is thrown.
@returns An instance of `TypeChatLanguageModel`.
Args:
vals: A dictionary of variables. Typically just `os.environ`.
"""
def required_var(name: str) -> str:
val = vals.get(name, None)
if val is None:
raise ValueError(f"Missing environment variable {name}.")
return val
if "OPENAI_API_KEY" in vals:
api_key = required_var("OPENAI_API_KEY")
model = required_var("OPENAI_MODEL")
endpoint = vals.get("OPENAI_ENDPOINT", None) or "https://api.openai.com/v1/chat/completions"
org = vals.get("OPENAI_ORG", None) or ""
return create_openai_language_model(api_key, model, endpoint, org)
elif "AZURE_OPENAI_API_KEY" in vals:
api_key=required_var("AZURE_OPENAI_API_KEY")
endpoint=required_var("AZURE_OPENAI_ENDPOINT")
return create_azure_openai_language_model(api_key, endpoint)
else:
raise ValueError("Missing environment variables for OPENAI_API_KEY or AZURE_OPENAI_API_KEY.")
def create_openai_language_model(api_key: str, model: str, endpoint: str = "https://api.openai.com/v1/chat/completions", org: str = "") -> HttpxLanguageModel:
"""
Creates a language model encapsulation of an OpenAI REST API endpoint.
Args:
api_key: The OpenAI API key.
model: The OpenAI model name.
endpoint: The OpenAI REST API endpoint.
org: The OpenAI organization.
"""
headers = {
"Authorization": f"Bearer {api_key}",
"OpenAI-Organization": org,
}
default_params = {
"model": model,
}
return HttpxLanguageModel(url=endpoint, headers=headers, default_params=default_params)
def create_azure_openai_language_model(api_key: str, endpoint: str) -> HttpxLanguageModel:
"""
Creates a language model encapsulation of an Azure OpenAI REST API endpoint.
Args:
api_key: The Azure OpenAI API key.
endpoint: The Azure OpenAI REST API endpoint.
"""
headers = {
# Needed when using managed identity
"Authorization": f"Bearer {api_key}",
# Needed when using regular API key
"api-key": api_key,
}
return HttpxLanguageModel(url=endpoint, headers=headers, default_params={})
================================================
FILE: python/src/typechat/_internal/result.py
================================================
from dataclasses import dataclass
from typing_extensions import Generic, TypeAlias, TypeVar
T = TypeVar("T", covariant=True)
@dataclass
class Success(Generic[T]):
"An object representing a successful operation with a result of type `T`."
value: T
@dataclass
class Failure:
"An object representing an operation that failed for the reason given in `message`."
message: str
"""
An object representing a successful or failed operation of type `T`.
"""
Result: TypeAlias = Success[T] | Failure
================================================
FILE: python/src/typechat/_internal/translator.py
================================================
from typing_extensions import Generic, TypeVar
import pydantic_core
from typechat._internal.model import PromptSection, TypeChatLanguageModel
from typechat._internal.result import Failure, Result, Success
from typechat._internal.ts_conversion import python_type_to_typescript_schema
from typechat._internal.validator import TypeChatValidator
T = TypeVar("T", covariant=True)
class TypeChatJsonTranslator(Generic[T]):
"""
Represents an object that can translate natural language requests in JSON objects of the given type.
"""
model: TypeChatLanguageModel
validator: TypeChatValidator[T]
target_type: type[T]
type_name: str
schema_str: str
_max_repair_attempts = 1
def __init__(
self,
model: TypeChatLanguageModel,
validator: TypeChatValidator[T],
target_type: type[T],
*, # keyword-only parameters follow
_raise_on_schema_errors: bool = True,
):
"""
Args:
model: The associated `TypeChatLanguageModel`.
validator: The associated `TypeChatValidator[T]`.
target_type: A runtime type object describing `T` - the expected shape of JSON data.
"""
super().__init__()
self.model = model
self.validator = validator
self.target_type = target_type
conversion_result = python_type_to_typescript_schema(target_type)
if _raise_on_schema_errors and conversion_result.errors:
error_text = "".join(f"\n- {error}" for error in conversion_result.errors)
raise ValueError(f"Could not convert Python type to TypeScript schema: \n{error_text}")
self.type_name = conversion_result.typescript_type_reference
self.schema_str = conversion_result.typescript_schema_str
async def translate(self, input: str, *, prompt_preamble: str | list[PromptSection] | None = None) -> Result[T]:
"""
Translates a natural language request into an object of type `T`. If the JSON object returned by
the language model fails to validate, repair attempts will be made up until `_max_repair_attempts`.
The prompt for the subsequent attempts will include the diagnostics produced for the prior attempt.
This often helps produce a valid instance.
Args:
input: A natural language request.
prompt_preamble: An optional string or list of prompt sections to prepend to the generated prompt.\
If a string is given, it is converted to a single "user" role prompt section.
"""
messages: list[PromptSection] = []
if prompt_preamble:
if isinstance(prompt_preamble, str):
prompt_preamble = [{"role": "user", "content": prompt_preamble}]
messages.extend(prompt_preamble)
messages.append({"role": "user", "content": self._create_request_prompt(input)})
num_repairs_attempted = 0
while True:
completion_response = await self.model.complete(messages)
if isinstance(completion_response, Failure):
return completion_response
text_response = completion_response.value
first_curly = text_response.find("{")
last_curly = text_response.rfind("}") + 1
error_message: str
if 0 <= first_curly < last_curly:
trimmed_response = text_response[first_curly:last_curly]
try:
parsed_response = pydantic_core.from_json(trimmed_response, allow_inf_nan=False, cache_strings=False)
except ValueError as e:
error_message = f"Error: {e}\n\nAttempted to parse:\n\n{trimmed_response}"
else:
result = self.validator.validate_object(parsed_response)
if isinstance(result, Success):
return result
error_message = result.message
else:
error_message = f"Response did not contain any text resembling JSON.\nResponse was\n\n{text_response}"
if num_repairs_attempted >= self._max_repair_attempts:
return Failure(error_message)
num_repairs_attempted += 1
messages.append({"role": "assistant", "content": text_response})
messages.append({"role": "user", "content": self._create_repair_prompt(error_message)})
def _create_request_prompt(self, intent: str) -> str:
prompt = f"""
You are a service that translates user requests into JSON objects of type "{self.type_name}" according to the following TypeScript definitions:
```
{self.schema_str}
```
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
def _create_repair_prompt(self, validation_error: str) -> str:
prompt = f"""
The above JSON object is invalid for the following reason:
'''
{validation_error}
'''
The following is a revised JSON object:
"""
return prompt
================================================
FILE: python/src/typechat/_internal/ts_conversion/__init__.py
================================================
from dataclasses import dataclass
from typing_extensions import TypeAliasType
from typechat._internal.ts_conversion.python_type_to_ts_nodes import python_type_to_typescript_nodes
from typechat._internal.ts_conversion.ts_node_to_string import ts_declaration_to_str
__all__ = [
"python_type_to_typescript_schema",
"TypeScriptSchemaConversionResult",
]
@dataclass
class TypeScriptSchemaConversionResult:
typescript_schema_str: str
"""The TypeScript declarations generated from the Python declarations."""
typescript_type_reference: str
"""The TypeScript string representation of a given Python type."""
errors: list[str]
"""Any errors that occurred during conversion."""
def python_type_to_typescript_schema(py_type: type | TypeAliasType) -> TypeScriptSchemaConversionResult:
"""Converts a Python type to a TypeScript schema."""
node_conversion_result = python_type_to_typescript_nodes(py_type)
decl_strs = map(ts_declaration_to_str, node_conversion_result.type_declarations)
schema_str = "\n".join(decl_strs)
return TypeScriptSchemaConversionResult(
typescript_schema_str=schema_str,
typescript_type_reference=py_type.__name__,
errors=node_conversion_result.errors,
)
================================================
FILE: python/src/typechat/_internal/ts_conversion/python_type_to_ts_nodes.py
================================================
from __future__ import annotations
from collections import OrderedDict
import inspect
import sys
import typing
import typing_extensions
from dataclasses import MISSING, Field, dataclass
from types import NoneType, UnionType
from typing_extensions import (
Annotated,
Any,
ClassVar,
Doc,
Final,
Generic,
Literal,
LiteralString,
Never,
NoReturn,
NotRequired,
Protocol,
Required,
TypeAlias,
TypeAliasType,
TypeGuard,
TypeVar,
Union,
cast,
get_args,
get_origin,
get_original_bases,
get_type_hints,
is_typeddict,
)
from typechat._internal.ts_conversion.ts_type_nodes import (
AnyTypeReferenceNode,
ArrayTypeNode,
BooleanTypeReferenceNode,
IdentifierNode,
IndexSignatureDeclarationNode,
InterfaceDeclarationNode,
LiteralTypeNode,
NeverTypeReferenceNode,
NullTypeReferenceNode,
NumberTypeReferenceNode,
PropertyDeclarationNode,
StringTypeReferenceNode,
ThisTypeReferenceNode,
TopLevelDeclarationNode,
TupleTypeNode,
TypeAliasDeclarationNode,
TypeNode,
TypeParameterDeclarationNode,
TypeReferenceNode,
UnionTypeNode,
)
class GenericDeclarationish(Protocol):
__parameters__: list[TypeVar]
__type_params__: list[TypeVar] # NOTE: may not be present unless running in 3.12
class GenericAliasish(Protocol):
__origin__: object
__args__: tuple[object, ...]
__name__: str
class Annotatedish(Protocol):
# NOTE: `__origin__` here refers to `SomeType` in `Annnotated[SomeType, ...]`
__origin__: object
__metadata__: tuple[object, ...]
class Dataclassish(Protocol):
__dataclass_fields__: dict[str, Field[Any]]
# type[TypedDict]
# https://github.com/microsoft/pyright/pull/6505#issuecomment-1834431725
class TypeOfTypedDict(Protocol):
__total__: bool
if sys.version_info >= (3, 12) and typing.TypeAliasType is not typing_extensions.TypeAliasType:
# Sometimes typing_extensions aliases TypeAliasType,
# sometimes it's its own declaration.
def is_type_alias_type(py_type: object) -> TypeGuard[TypeAliasType]:
return isinstance(py_type, typing.TypeAliasType | typing_extensions.TypeAliasType)
else:
def is_type_alias_type(py_type: object) -> TypeGuard[TypeAliasType]:
return isinstance(py_type, typing_extensions.TypeAliasType)
def is_generic(py_type: object) -> TypeGuard[GenericAliasish]:
return hasattr(py_type, "__origin__") and hasattr(py_type, "__args__")
def is_dataclass(py_type: object) -> TypeGuard[Dataclassish]:
return hasattr(py_type, "__dataclass_fields__") and isinstance(cast(Any, py_type).__dataclass_fields__, dict)
TypeReferenceTarget: TypeAlias = type | TypeAliasType | TypeVar | GenericAliasish
def is_python_type_or_alias(origin: object) -> TypeGuard[type | TypeAliasType]:
return isinstance(origin, type) or is_type_alias_type(origin)
_KNOWN_GENERIC_SPECIAL_FORMS: frozenset[Any] = frozenset(
[
Required,
NotRequired,
ClassVar,
Final,
Annotated,
Generic,
]
)
_KNOWN_SPECIAL_BASES: frozenset[Any] = frozenset([
typing.TypedDict,
typing_extensions.TypedDict,
Protocol,
# In older versions of Python, `__orig_bases__` will not be defined on `TypedDict`s
# derived from the built-in `typing` module (but they will from `typing_extensions`!).
# So `get_original_bases` will fetch `__bases__` which will map `TypedDict` to a plain `dict`.
dict,
])
@dataclass
class TypeScriptNodeTranslationResult:
type_declarations: list[TopLevelDeclarationNode]
errors: list[str]
# TODO: https://github.com/microsoft/pyright/issues/6587
_SELF_TYPE = getattr(typing_extensions, "Self")
_LIST_TYPES: set[object] = {
list,
set,
frozenset,
# TODO: https://github.com/microsoft/pyright/issues/6582
# collections.abc.MutableSequence,
# collections.abc.Sequence,
# collections.abc.Set
}
# TODO: https://github.com/microsoft/pyright/issues/6582
# _DICT_TYPES: set[type] = {
# dict,
# collections.abc.MutableMapping,
# collections.abc.Mapping
# }
def python_type_to_typescript_nodes(root_py_type: object) -> TypeScriptNodeTranslationResult:
# TODO: handle conflicting names
declared_types: OrderedDict[object, TopLevelDeclarationNode | None] = OrderedDict()
undeclared_types: OrderedDict[object, object] = OrderedDict({root_py_type: root_py_type}) # just a set, really
used_names: dict[str, type | TypeAliasType] = {}
errors: list[str] = []
def skip_annotations(py_type: object) -> object:
origin = py_type
while (origin := get_origin(py_type)) and origin in _KNOWN_GENERIC_SPECIAL_FORMS:
type_arguments = get_args(py_type)
if not type_arguments:
errors.append(f"'{origin}' has been used without any type arguments.")
return Any
py_type = type_arguments[0]
continue
return py_type
def convert_to_type_reference_node(py_type: TypeReferenceTarget) -> TypeNode:
py_type_to_declare = py_type
if is_generic(py_type):
py_type_to_declare = get_origin(py_type)
if py_type_to_declare not in declared_types:
if is_python_type_or_alias(py_type_to_declare):
undeclared_types[py_type_to_declare] = py_type_to_declare
elif not isinstance(py_type, TypeVar):
errors.append(f"Invalid usage of '{py_type}' as a type annotation.")
return AnyTypeReferenceNode
if is_generic(py_type):
return generic_alias_to_type_reference(py_type)
return TypeReferenceNode(IdentifierNode(py_type.__name__))
def generic_alias_to_type_reference(py_type: GenericAliasish) -> TypeReferenceNode:
origin = get_origin(py_type)
assert origin is not None
name = origin.__name__
type_arguments = list(map(convert_to_type_node, get_args(py_type)))
return TypeReferenceNode(IdentifierNode(name), type_arguments)
def convert_literal_type_arg_to_type_node(py_type: object) -> TypeNode:
py_type = skip_annotations(py_type)
match py_type:
case str() | int() | float(): # no need to match bool, it's a subclass of int
return LiteralTypeNode(py_type)
case None:
return NullTypeReferenceNode
case _:
errors.append(f"'{py_type}' cannot be used as a literal type.")
return AnyTypeReferenceNode
def convert_to_type_node(py_type: object) -> TypeNode:
py_type = skip_annotations(py_type)
if py_type is str or py_type is LiteralString:
return StringTypeReferenceNode
if py_type is int or py_type is float:
return NumberTypeReferenceNode
if py_type is bool:
return BooleanTypeReferenceNode
if py_type is Any or py_type is object:
return AnyTypeReferenceNode
if py_type is None or py_type is NoneType:
return NullTypeReferenceNode
if py_type is Never or py_type is NoReturn:
return NeverTypeReferenceNode
if py_type is _SELF_TYPE:
return ThisTypeReferenceNode
# TODO: consider handling bare 'tuple' (and list, etc.)
# https://docs.python.org/3/library/typing.html#annotating-tuples
# Using plain tuple as an annotation is equivalent to using tuple[Any, ...]:
origin = get_origin(py_type)
if origin is not None:
if origin in _LIST_TYPES:
(type_arg,) = get_type_argument_nodes(py_type, 1, AnyTypeReferenceNode)
if isinstance(type_arg, UnionTypeNode):
return TypeReferenceNode(IdentifierNode("Array"), [type_arg])
return ArrayTypeNode(type_arg)
if origin is dict:
# TODO
# Currently, we naively assume all dicts are string-keyed
# unless they're annotated with `int` or `float` (note: not `int | float`).
key_type_arg, value_type_arg = get_type_argument_nodes(py_type, 2, AnyTypeReferenceNode)
if key_type_arg is not NumberTypeReferenceNode:
key_type_arg = StringTypeReferenceNode
return TypeReferenceNode(IdentifierNode("Record"), [key_type_arg, value_type_arg])
if origin is tuple:
# Note that when the type is `tuple[()]`,
# `type_args` will be an empty tuple.
# Which is nice, because we don't have to special-case anything!
type_args = get_args(py_type)
if Ellipsis in type_args:
if len(type_args) != 2:
errors.append(
f"The tuple type '{py_type}' is ill-formed. Tuples with an ellipsis can only take the form 'tuple[SomeType, ...]'."
)
return ArrayTypeNode(AnyTypeReferenceNode)
ellipsis_index = type_args.index(Ellipsis)
if ellipsis_index != 1:
errors.append(
f"The tuple type '{py_type}' is ill-formed because the ellipsis (...) cannot be the first element."
)
return ArrayTypeNode(AnyTypeReferenceNode)
return ArrayTypeNode(convert_to_type_node(type_args[0]))
return TupleTypeNode([convert_to_type_node(py_type_arg) for py_type_arg in type_args])
if origin is Union or origin is UnionType:
type_node = [convert_to_type_node(py_type_arg) for py_type_arg in get_args(py_type)]
assert len(type_node) > 1
return UnionTypeNode(type_node)
if origin is Literal:
type_node = [convert_literal_type_arg_to_type_node(py_type_arg) for py_type_arg in get_args(py_type)]
assert len(type_node) >= 1
return UnionTypeNode(type_node)
assert is_generic(py_type)
return convert_to_type_reference_node(py_type)
if is_python_type_or_alias(py_type):
return convert_to_type_reference_node(py_type)
if isinstance(py_type, TypeVar):
return convert_to_type_reference_node(py_type)
errors.append(f"'{py_type}' cannot be used as a type annotation.")
return AnyTypeReferenceNode
def declare_property(name: str, py_annotation: type | TypeAliasType, is_typeddict_attribute: bool, optionality_default: bool):
"""
Declare a property for a given type.
If 'optionality_default' is
"""
current_annotation: object = py_annotation
origin: object
optional: bool | None = None
comment: str | None = None
while origin := get_origin(current_annotation):
if origin is Annotated and comment is None:
current_annotation = cast(Annotatedish, current_annotation)
for metadata in current_annotation.__metadata__:
if isinstance(metadata, Doc):
comment = metadata.documentation
break
if isinstance(metadata, str):
comment = metadata
break
current_annotation = current_annotation.__origin__
elif origin is Required or origin is NotRequired:
if not is_typeddict_attribute:
errors.append(f"Optionality cannot be specified with {origin} outside of TypedDicts.")
if optional is None:
optional = origin is NotRequired
else:
errors.append(f"{origin} cannot be used within another optionality annotation.")
current_annotation = get_args(current_annotation)[0]
else:
break
if optional is None:
optional = optionality_default
type_annotation = convert_to_type_node(skip_annotations(current_annotation))
return PropertyDeclarationNode(name, optional, comment or "", type_annotation)
def reserve_name(val: type | TypeAliasType):
type_name = val.__name__
if type_name in used_names:
errors.append(f"Cannot create a schema using two types with the same name. {type_name} conflicts between {val} and {used_names[type_name]}")
else:
used_names[type_name] = val
def declare_type(py_type: object):
if (is_typeddict(py_type) or is_dataclass(py_type)) and isinstance(py_type, type):
comment = py_type.__doc__ or ""
if hasattr(py_type, "__type_params__") and cast(GenericDeclarationish, py_type).__type_params__:
type_params = [
TypeParameterDeclarationNode(type_param.__name__)
for type_param in cast(GenericDeclarationish, py_type).__type_params__
]
elif hasattr(py_type, "__parameters__") and cast(GenericDeclarationish, py_type).__parameters__:
type_params = [
TypeParameterDeclarationNode(type_param.__name__)
for type_param in cast(GenericDeclarationish, py_type).__parameters__
]
else:
type_params = None
annotated_members = get_type_hints(py_type, include_extras=True)
raw_but_filtered_bases: list[type] = [
base
for base in get_original_bases(py_type)
if not(base is object or base in _KNOWN_SPECIAL_BASES or get_origin(base) in _KNOWN_GENERIC_SPECIAL_FORMS)
]
base_attributes: OrderedDict[str, set[object]] = OrderedDict()
for base in raw_but_filtered_bases:
for prop, type_hint in get_type_hints(get_origin(base) or base, include_extras=True).items():
base_attributes.setdefault(prop, set()).add(type_hint)
bases = [convert_to_type_node(base) for base in raw_but_filtered_bases]
properties: list[PropertyDeclarationNode | IndexSignatureDeclarationNode] = []
if is_typeddict(py_type):
for attr_name, type_hint in annotated_members.items():
if attribute_identical_in_all_bases(attr_name, type_hint, base_attributes):
continue
assume_optional = cast(TypeOfTypedDict, py_type).__total__ is False
prop = declare_property(attr_name, type_hint, is_typeddict_attribute=True, optionality_default=assume_optional)
properties.append(prop)
else:
# When a dataclass is created with no explicit docstring, @dataclass will
# generate one for us; however, we don't want these in the default output.
cleaned_signature = str(inspect.signature(py_type)).replace(" -> None", "")
dataclass_doc = f"{py_type.__name__}{cleaned_signature}"
if comment == dataclass_doc:
comment = ""
for attr_name, field in cast(Dataclassish, py_type).__dataclass_fields__.items():
type_hint = annotated_members[attr_name]
optional = not(field.default is MISSING and field.default_factory is MISSING)
prop = declare_property(attr_name, type_hint, is_typeddict_attribute=False, optionality_default=optional)
properties.append(prop)
reserve_name(py_type)
return InterfaceDeclarationNode(py_type.__name__, type_params, comment, bases, properties)
if isinstance(py_type, type):
errors.append(f"{py_type.__name__} was not a TypedDict, dataclass, or type alias, and cannot be translated.")
reserve_name(py_type)
return InterfaceDeclarationNode(py_type.__name__, None, "", None, [])
if is_type_alias_type(py_type):
type_params = [TypeParameterDeclarationNode(type_param.__name__) for type_param in py_type.__type_params__]
reserve_name(py_type)
return TypeAliasDeclarationNode(
py_type.__name__,
type_params,
f"Comment for {py_type.__name__}.",
convert_to_type_node(py_type.__value__),
)
raise RuntimeError(f"Cannot declare type {py_type}.")
def attribute_identical_in_all_bases(attr_name: str, type_hint: object, base_attributes: dict[str, set[object]]) -> bool:
"""
We typically want to omit attributes with type hints that are
identical to those declared in all base types.
"""
return attr_name in base_attributes and len(base_attributes[attr_name]) == 1 and type_hint in base_attributes[attr_name]
def get_type_argument_nodes(py_type: object, count: int, default: TypeNode) -> list[TypeNode]:
py_type_args = get_args(py_type)
result: list[TypeNode] = []
if len(py_type_args) != count:
errors.append(f"Expected '{count}' type arguments for '{py_type}'.")
for i in range(count):
if i < len(py_type_args):
type_node = convert_to_type_node(py_type_args[i])
else:
type_node = default
result.append(type_node)
return result
while undeclared_types:
py_type = undeclared_types.popitem()[0]
declared_types[py_type] = None
declared_types[py_type] = declare_type(py_type)
type_declarations = cast(list[TopLevelDeclarationNode], list(declared_types.values()))
assert None not in type_declarations
return TypeScriptNodeTranslationResult(type_declarations, errors)
================================================
FILE: python/src/typechat/_internal/ts_conversion/ts_node_to_string.py
================================================
import json
from typing_extensions import assert_never
from typechat._internal.ts_conversion.ts_type_nodes import (
ArrayTypeNode,
IdentifierNode,
IndexSignatureDeclarationNode,
InterfaceDeclarationNode,
LiteralTypeNode,
NullTypeReferenceNode,
PropertyDeclarationNode,
TopLevelDeclarationNode,
TupleTypeNode,
TypeAliasDeclarationNode,
TypeNode,
TypeReferenceNode,
UnionTypeNode,
)
def comment_to_str(comment_text: str, indentation: str) -> str:
comment_text = comment_text.strip()
if not comment_text:
return ""
lines = [line.strip() for line in comment_text.splitlines()]
return "\n".join([f"{indentation}// {line}" for line in lines]) + "\n"
def ts_type_to_str(type_node: TypeNode) -> str:
match type_node:
case TypeReferenceNode(name, type_arguments):
assert isinstance(name, IdentifierNode)
if type_arguments is None:
return name.text
return f"{name.text}<{', '.join([ts_type_to_str(arg) for arg in type_arguments])}>"
case ArrayTypeNode(element_type):
assert type(element_type) is not UnionTypeNode
# if type(element_type) is UnionTypeNode:
# return f"Array<{ts_type_to_str(element_type)}>"
return f"{ts_type_to_str(element_type)}[]"
case TupleTypeNode(element_types):
return f"[{', '.join([ts_type_to_str(element_type) for element_type in element_types])}]"
case UnionTypeNode(types):
# Remove duplicates, but try to preserve order of types,
# and put null at the end if it's present.
str_set: set[str] = set()
type_strs: list[str] = []
nullable = False
for type_node in types:
if type_node is NullTypeReferenceNode:
nullable = True
continue
type_str = ts_type_to_str(type_node)
if type_str not in str_set:
str_set.add(type_str)
type_strs.append(type_str)
if nullable:
type_strs.append("null")
return " | ".join(type_strs)
case LiteralTypeNode(value):
return json.dumps(value)
# case _:
# raise NotImplementedError(f"Unhandled type {type(type_node)}")
assert_never(type_node)
def object_member_to_str(member: PropertyDeclarationNode | IndexSignatureDeclarationNode) -> str:
match member:
case PropertyDeclarationNode(name, is_optional, comment, annotation):
comment = comment_to_str(comment, " ")
if not name.isidentifier():
name = json.dumps(name)
return f"{comment} {name}{'?' if is_optional else ''}: {ts_type_to_str(annotation)};"
case IndexSignatureDeclarationNode(key_type, value_type):
return f"[key: {ts_type_to_str(key_type)}]: {ts_type_to_str(value_type)};"
# case _:
# raise NotImplementedError(f"Unhandled member type {type(member)}")
assert_never(member)
def ts_declaration_to_str(declaration: TopLevelDeclarationNode) -> str:
match declaration:
case InterfaceDeclarationNode(name, type_parameters, comment, base_types, members):
comment = comment_to_str(comment, "")
type_param_str = f"<{', '.join([param.name for param in type_parameters])}>" if type_parameters else ""
base_type_str = (
f" extends {', '.join([ts_type_to_str(base_type) for base_type in base_types])}" if base_types else ""
)
members_str = "\n".join([f"{object_member_to_str(member)}" for member in members]) + "\n" if members else ""
return f"{comment}interface {name}{type_param_str}{base_type_str} {{\n{members_str}}}\n"
case TypeAliasDeclarationNode(name, type_parameters, comment, target):
type_param_str = f"<{', '.join([param.name for param in type_parameters])}>" if type_parameters else ""
return f"type {name}{type_param_str} = {ts_type_to_str(target)}\n"
# case _:
# raise NotImplementedError(f"Unhandled declaration type {type(declaration)}")
assert_never(declaration)
================================================
FILE: python/src/typechat/_internal/ts_conversion/ts_type_nodes.py
================================================
from __future__ import annotations
from dataclasses import dataclass
from typing_extensions import TypeAlias
TypeNode: TypeAlias = "TypeReferenceNode | UnionTypeNode | LiteralTypeNode | ArrayTypeNode | TupleTypeNode"
@dataclass
class IdentifierNode:
text: str
@dataclass
class QualifiedNameNode:
left: QualifiedNameNode | IdentifierNode
right: IdentifierNode
@dataclass
class TypeReferenceNode:
name: QualifiedNameNode | IdentifierNode
type_arguments: list[TypeNode] | None = None
@dataclass
class UnionTypeNode:
types: list[TypeNode]
@dataclass
class LiteralTypeNode:
value: str | int | float | bool
@dataclass
class ArrayTypeNode:
element_type: TypeNode
@dataclass
class TupleTypeNode:
element_types: list[TypeNode]
@dataclass
class InterfaceDeclarationNode:
name: str
type_parameters: list[TypeParameterDeclarationNode] | None
comment: str
base_types: list[TypeNode] | None
members: list[PropertyDeclarationNode | IndexSignatureDeclarationNode]
@dataclass
class TypeParameterDeclarationNode:
name: str
constraint: TypeNode | None = None
@dataclass
class PropertyDeclarationNode:
name: str
is_optional: bool
comment: str
type: TypeNode
@dataclass
class IndexSignatureDeclarationNode:
key_type: TypeNode
value_type: TypeNode
@dataclass
class TypeAliasDeclarationNode:
name: str
type_parameters: list[TypeParameterDeclarationNode] | None
comment: str
type: TypeNode
TopLevelDeclarationNode: TypeAlias = "InterfaceDeclarationNode | TypeAliasDeclarationNode"
StringTypeReferenceNode = TypeReferenceNode(IdentifierNode("string"))
NumberTypeReferenceNode = TypeReferenceNode(IdentifierNode("number"))
BooleanTypeReferenceNode = TypeReferenceNode(IdentifierNode("boolean"))
AnyTypeReferenceNode = TypeReferenceNode(IdentifierNode("any"))
NullTypeReferenceNode = TypeReferenceNode(IdentifierNode("null"))
NeverTypeReferenceNode = TypeReferenceNode(IdentifierNode("never"))
ThisTypeReferenceNode = TypeReferenceNode(IdentifierNode("this"))
================================================
FILE: python/src/typechat/_internal/validator.py
================================================
import json
from typing_extensions import Generic, TypeVar
import pydantic
import pydantic_core
from typechat._internal.result import Failure, Result, Success
T = TypeVar("T", covariant=True)
class TypeChatValidator(Generic[T]):
"""
Validates an object against a given Python type.
"""
_adapted_type: pydantic.TypeAdapter[T]
def __init__(self, py_type: type[T]):
"""
Args:
py_type: The schema type to validate against.
"""
super().__init__()
self._adapted_type = pydantic.TypeAdapter(py_type)
def validate_object(self, obj: object) -> Result[T]:
"""
Validates the given Python object according to the associated schema type.
Returns a `Success[T]` object containing the object if validation was successful.
Otherwise, returns a `Failure` object with a `message` property describing the error.
"""
try:
# TODO: Switch to `validate_python` when validation modes are exposed.
# https://github.com/pydantic/pydantic-core/issues/712
# We'd prefer to keep `validate_object` as the core method and
# allow translators to concern themselves with the JSON instead.
# However, under Pydantic's `strict` mode, a `dict` isn't considered compatible
# with a dataclass. So for now, jump back to JSON and validate the string.
json_str = pydantic_core.to_json(obj)
typed_dict = self._adapted_type.validate_json(json_str, strict=True)
return Success(typed_dict)
except pydantic.ValidationError as validation_error:
return _handle_error(validation_error)
def _handle_error(validation_error: pydantic.ValidationError) -> Failure:
error_strings: list[str] = []
for error in validation_error.errors(include_url=False):
error_string = ""
loc_path = error["loc"]
if loc_path:
error_string += f"Validation path `{'.'.join(map(str, loc_path))}` "
else:
error_string += "Root validation "
input = error["input"]
error_string += f"failed for value `{json.dumps(input)}` because:\n {error['msg']}"
error_strings.append(error_string)
if len(error_strings) > 1:
failure_message = "Several possible issues may have occurred with the given data.\n\n"
else:
failure_message = ""
failure_message += "\n".join(error_strings)
return Failure(failure_message)
================================================
FILE: python/src/typechat/py.typed
================================================
================================================
FILE: python/tests/__init__.py
================================================
# SPDX-FileCopyrightText: Microsoft Corporation
#
# SPDX-License-Identifier: MIT
================================================
FILE: python/tests/__py3.11_snapshots__/test_conflicting_names_1/test_conflicting_names_1.schema.d.ts
================================================
// Entry point is: 'Derived'
interface Derived {
my_attr_1: string;
my_attr_2: number;
}
================================================
FILE: python/tests/__py3.11_snapshots__/test_hello_world/test_generic_alias1.schema.d.ts
================================================
// Entry point is: 'D_or_E'
type D_or_E = D | E
// This is the definition of the class E.
interface E extends C<string> {
tag: "E";
next: this | null;
}
// This is a generic class named C.
interface C<T> {
x?: T;
c: C<number | null>;
}
// This is the definition of the class D.
interface D extends C<string> {
tag?: "D";
// This comes from string metadata
// within an Annotated hint.
y: boolean | null;
z?: number[] | null;
other?: IndirectC;
non_class?: NonClass;
// This comes from later metadata.
multiple_metadata?: string;
}
interface NonClass {
a: number;
"my-dict": Record<string, number>;
}
type IndirectC = C<number>
================================================
FILE: python/tests/__py3.12+_snapshots__/test_generic_alias_3/test_generic_alias3.schema.d.ts
================================================
// Entry point is: 'FirstOrSecond'
type FirstOrSecond<T> = First<T> | Second<T>
interface Second<T> {
kind: "second";
second_attr: T;
}
interface First<T> {
kind: "first";
first_attr: T;
}
================================================
FILE: python/tests/__py3.12+_snapshots__/test_generic_alias_4/test_generic_alias4.schema.d.ts
================================================
// Entry point is: 'Nested'
interface Nested {
item: FirstOrSecond<string>;
}
type FirstOrSecond<T> = First<T> | Second<T>
interface Second<T> {
kind: "second";
second_attr: T;
}
interface First<T> {
kind: "first";
first_attr: T;
}
================================================
FILE: python/tests/__py3.12+_snapshots__/test_type_alias_syntax/test_type_alias_union1.schema.d.ts
================================================
// Entry point is: 'StrOrInt'
type StrOrInt = string | number
================================================
FILE: python/tests/__py3.12_s
gitextract_lrpyygt1/
├── .devcontainer/
│ └── devcontainer.json
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ ├── ci.js.yml
│ ├── ci.python.yml
│ └── github-pages.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── SECURITY.md
├── SUPPORT.md
├── TypeChat.code-workspace
├── dotnet/
│ └── README.md
├── python/
│ ├── .gitignore
│ ├── LICENSE
│ ├── README.md
│ ├── examples/
│ │ ├── README.md
│ │ ├── calendar/
│ │ │ ├── README.md
│ │ │ ├── demo.py
│ │ │ ├── input.txt
│ │ │ └── schema.py
│ │ ├── coffeeShop/
│ │ │ ├── README.md
│ │ │ ├── demo.py
│ │ │ ├── input.txt
│ │ │ ├── input2.txt
│ │ │ └── schema.py
│ │ ├── healthData/
│ │ │ ├── README.md
│ │ │ ├── demo.py
│ │ │ ├── input.txt
│ │ │ ├── schema.py
│ │ │ └── translator.py
│ │ ├── math/
│ │ │ ├── README.md
│ │ │ ├── demo.py
│ │ │ ├── input.txt
│ │ │ ├── program.py
│ │ │ ├── schema.py
│ │ │ └── schemaV2.py
│ │ ├── multiSchema/
│ │ │ ├── README.md
│ │ │ ├── agents.py
│ │ │ ├── demo.py
│ │ │ ├── input.txt
│ │ │ └── router.py
│ │ ├── music/
│ │ │ ├── README.md
│ │ │ ├── client.py
│ │ │ ├── demo.py
│ │ │ ├── input.txt
│ │ │ ├── schema.py
│ │ │ └── spotipyWrapper.py
│ │ ├── restaurant/
│ │ │ ├── README.md
│ │ │ ├── demo.py
│ │ │ ├── input.txt
│ │ │ └── schema.py
│ │ └── sentiment/
│ │ ├── README.md
│ │ ├── demo.py
│ │ ├── input.txt
│ │ └── schema.py
│ ├── notebooks/
│ │ ├── calendar.ipynb
│ │ ├── coffeeShop.ipynb
│ │ ├── healthData.ipynb
│ │ ├── math.ipynb
│ │ ├── music.ipynb
│ │ ├── restaurant.ipynb
│ │ └── sentiment.ipynb
│ ├── package.json
│ ├── pyproject.toml
│ ├── pyrightconfig.json
│ ├── src/
│ │ └── typechat/
│ │ ├── __about__.py
│ │ ├── __init__.py
│ │ ├── _internal/
│ │ │ ├── __init__.py
│ │ │ ├── interactive.py
│ │ │ ├── model.py
│ │ │ ├── result.py
│ │ │ ├── translator.py
│ │ │ ├── ts_conversion/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── python_type_to_ts_nodes.py
│ │ │ │ ├── ts_node_to_string.py
│ │ │ │ └── ts_type_nodes.py
│ │ │ └── validator.py
│ │ └── py.typed
│ └── tests/
│ ├── __init__.py
│ ├── __py3.11_snapshots__/
│ │ ├── test_conflicting_names_1/
│ │ │ └── test_conflicting_names_1.schema.d.ts
│ │ └── test_hello_world/
│ │ └── test_generic_alias1.schema.d.ts
│ ├── __py3.12+_snapshots__/
│ │ ├── test_generic_alias_3/
│ │ │ └── test_generic_alias3.schema.d.ts
│ │ ├── test_generic_alias_4/
│ │ │ └── test_generic_alias4.schema.d.ts
│ │ └── test_type_alias_syntax/
│ │ └── test_type_alias_union1.schema.d.ts
│ ├── __py3.12_snapshots__/
│ │ ├── test_conflicting_names_1/
│ │ │ └── test_conflicting_names_1.schema.d.ts
│ │ └── test_hello_world/
│ │ └── test_generic_alias1.schema.d.ts
│ ├── __py3.13_snapshots__/
│ │ ├── test_conflicting_names_1/
│ │ │ └── test_conflicting_names_1.schema.d.ts
│ │ └── test_hello_world/
│ │ └── test_generic_alias1.schema.d.ts
│ ├── __py3.14_snapshots__/
│ │ ├── test_conflicting_names_1/
│ │ │ └── test_conflicting_names_1.schema.d.ts
│ │ └── test_hello_world/
│ │ └── test_generic_alias1.schema.d.ts
│ ├── __snapshots__/
│ │ ├── test_coffeeshop/
│ │ │ └── test_coffeeshop_schema.schema.d.ts
│ │ ├── test_dataclasses/
│ │ │ └── test_data_classes.schema.d.ts
│ │ ├── test_generic_alias_1/
│ │ │ └── test_generic_alias1.schema.d.ts
│ │ ├── test_generic_alias_2/
│ │ │ └── test_generic_alias2.schema.d.ts
│ │ ├── test_translator.ambr
│ │ ├── test_tuple_errors_1/
│ │ │ └── test_tuples_2.schema.d.ts
│ │ └── test_tuples_1/
│ │ └── test_tuples_1.schema.d.ts
│ ├── coffeeshop_deprecated.py
│ ├── test_coffeeshop.py
│ ├── test_conflicting_names_1.py
│ ├── test_dataclasses.py
│ ├── test_generic_alias_1.py
│ ├── test_generic_alias_2.py
│ ├── test_generic_alias_3.py
│ ├── test_generic_alias_4.py
│ ├── test_hello_world.py
│ ├── test_translator.py
│ ├── test_tuple_errors_1.py
│ ├── test_tuples_1.py
│ ├── test_type_alias_syntax.py
│ ├── test_validator.py
│ └── utilities.py
├── site/
│ ├── .eleventy.js
│ ├── .gitignore
│ ├── jsconfig.json
│ ├── package.json
│ └── src/
│ ├── _data/
│ │ ├── docsTOC.json
│ │ └── headernav.json
│ ├── _includes/
│ │ ├── base.njk
│ │ ├── blog.njk
│ │ ├── doc-page.njk
│ │ ├── docs.njk
│ │ ├── footer.njk
│ │ └── header-prologue.njk
│ ├── blog/
│ │ ├── announcing-typechat-0-1-0.md
│ │ ├── index.njk
│ │ └── introducing-typechat.md
│ ├── css/
│ │ ├── noscript-styles.css
│ │ └── styles.css
│ ├── docs/
│ │ ├── examples.md
│ │ ├── faq.md
│ │ ├── index.njk
│ │ ├── introduction.md
│ │ ├── python/
│ │ │ └── basic-usage.md
│ │ ├── techniques.md
│ │ └── typescript/
│ │ └── basic-usage.md
│ ├── index.njk
│ └── js/
│ └── interactivity.js
└── typescript/
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── examples/
│ ├── README.md
│ ├── calendar/
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── calendarActionsSchema.ts
│ │ ├── expectedOutput.txt
│ │ ├── input.txt
│ │ ├── main.ts
│ │ └── tsconfig.json
│ ├── coffeeShop/
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── coffeeShopSchema.ts
│ │ ├── input.txt
│ │ ├── input2.txt
│ │ ├── main.ts
│ │ └── tsconfig.json
│ ├── coffeeShop-zod/
│ │ ├── README.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── coffeeShopSchema.ts
│ │ ├── input.txt
│ │ ├── input2.txt
│ │ ├── main.ts
│ │ └── tsconfig.json
│ ├── crossword/
│ │ ├── README.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── crosswordSchema.ts
│ │ ├── input.txt
│ │ ├── main.ts
│ │ ├── translator.ts
│ │ └── tsconfig.json
│ ├── healthData/
│ │ ├── README.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── healthDataSchema.ts
│ │ ├── input.txt
│ │ ├── main.ts
│ │ ├── translator.ts
│ │ └── tsconfig.json
│ ├── math/
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── input.txt
│ │ ├── main.ts
│ │ ├── mathSchema.ts
│ │ └── tsconfig.json
│ ├── multiSchema/
│ │ ├── README.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── agent.ts
│ │ ├── classificationSchema.ts
│ │ ├── input.txt
│ │ ├── main.ts
│ │ ├── router.ts
│ │ └── tsconfig.json
│ ├── music/
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── migrations.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── authz.ts
│ │ ├── callback.html
│ │ ├── chatifyActionsSchema.ts
│ │ ├── dbInterface.ts
│ │ ├── endpoints.ts
│ │ ├── input.txt
│ │ ├── localParser.ts
│ │ ├── main.ts
│ │ ├── playback.ts
│ │ ├── service.ts
│ │ ├── trackCollections.ts
│ │ ├── trackFilter.ts
│ │ └── tsconfig.json
│ ├── restaurant/
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── foodOrderViewSchema.ts
│ │ ├── input.txt
│ │ ├── main.ts
│ │ └── tsconfig.json
│ ├── sentiment/
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── package.json
│ │ └── src/
│ │ ├── input.txt
│ │ ├── main.ts
│ │ ├── sentimentSchema.ts
│ │ └── tsconfig.json
│ └── sentiment-zod/
│ ├── README.md
│ ├── package.json
│ └── src/
│ ├── input.txt
│ ├── main.ts
│ ├── sentimentSchema.ts
│ └── tsconfig.json
├── package.json
└── src/
├── index.ts
├── interactive/
│ ├── index.ts
│ └── interactive.ts
├── model.ts
├── result.ts
├── ts/
│ ├── index.ts
│ ├── program.ts
│ └── validate.ts
├── tsconfig.json
├── typechat.ts
└── zod/
├── index.ts
└── validate.ts
SYMBOL INDEX (577 symbols across 96 files)
FILE: python/examples/calendar/demo.py
function main (line 9) | async def main():
FILE: python/examples/calendar/schema.py
class UnknownAction (line 3) | class UnknownAction(TypedDict):
class EventTimeRange (line 12) | class EventTimeRange(TypedDict, total=False):
class Event (line 18) | class Event(TypedDict):
class EventReference (line 26) | class EventReference(TypedDict, total=False):
class FindEventsAction (line 40) | class FindEventsAction(TypedDict):
class ChangeDescriptionAction (line 45) | class ChangeDescriptionAction(TypedDict):
class ChangeTimeRangeAction (line 51) | class ChangeTimeRangeAction(TypedDict):
class AddParticipantsAction (line 57) | class AddParticipantsAction(TypedDict):
class RemoveEventAction (line 65) | class RemoveEventAction(TypedDict):
class AddEventAction (line 70) | class AddEventAction(TypedDict):
class CalendarActions (line 86) | class CalendarActions(TypedDict):
FILE: python/examples/coffeeShop/demo.py
function main (line 10) | async def main():
FILE: python/examples/coffeeShop/schema.py
class UnknownText (line 4) | class UnknownText(TypedDict):
class Caffeine (line 13) | class Caffeine(TypedDict):
class Milk (line 18) | class Milk(TypedDict):
class Creamer (line 25) | class Creamer(TypedDict):
class Topping (line 41) | class Topping(TypedDict):
class LattePreparation (line 47) | class LattePreparation(TypedDict):
class Sweetener (line 52) | class Sweetener(TypedDict):
class Syrup (line 71) | class Syrup(TypedDict):
class LatteDrink (line 88) | class LatteDrink(TypedDict):
class EspressoDrink (line 96) | class EspressoDrink(TypedDict):
class CoffeeDrink (line 104) | class CoffeeDrink(TypedDict):
class BakeryOption (line 112) | class BakeryOption(TypedDict):
class BakeryPreparation (line 118) | class BakeryPreparation(TypedDict):
class BakeryProduct (line 123) | class BakeryProduct(TypedDict):
class LineItem (line 132) | class LineItem(TypedDict):
class Cart (line 138) | class Cart(TypedDict):
FILE: python/examples/healthData/demo.py
function main (line 20) | async def main():
FILE: python/examples/healthData/schema.py
class Quantity (line 4) | class Quantity(TypedDict):
class ApproxDatetime (line 9) | class ApproxDatetime(TypedDict):
class ApproxQuantity (line 14) | class ApproxQuantity(TypedDict):
class OtherHealthData (line 19) | class OtherHealthData(TypedDict):
class Condition (line 28) | class Condition(TypedDict):
class Medication (line 42) | class Medication(TypedDict):
class HealthData (line 53) | class HealthData(TypedDict, total=False):
class HealthDataResponse (line 59) | class HealthDataResponse(TypedDict, total=False):
FILE: python/examples/healthData/translator.py
class ChatMessage (line 11) | class ChatMessage(TypedDict):
class TranslatorWithHistory (line 16) | class TranslatorWithHistory(TypeChatJsonTranslator[T]):
method __init__ (line 21) | def __init__(
method translate (line 30) | async def translate(self, input: str, *, prompt_preamble: str | list[P...
method _create_request_prompt (line 37) | def _create_request_prompt(self, intent: str) -> str:
FILE: python/examples/math/demo.py
function main (line 11) | async def main():
function apply_operations (line 31) | async def apply_operations(func: str, args: Sequence[object]) -> int | f...
FILE: python/examples/math/program.py
function evaluate_json_program (line 50) | async def evaluate_json_program(
class TypeChatProgramValidator (line 102) | class TypeChatProgramValidator(TypeChatValidator[JsonProgram]):
method __init__ (line 103) | def __init__(self):
method validate_object (line 112) | def validate_object(self, obj: Any) -> Success[JsonProgram] | Failure:
class TypeChatProgramTranslator (line 120) | class TypeChatProgramTranslator(TypeChatJsonTranslator[JsonProgram]):
method __init__ (line 123) | def __init__(self, model: TypeChatLanguageModel, validator: TypeChatPr...
method _create_request_prompt (line 130) | def _create_request_prompt(self, intent: str) -> str:
method _create_repair_prompt (line 150) | def _create_repair_prompt(self, validation_error: str) -> str:
FILE: python/examples/math/schema.py
class MathAPI (line 4) | class MathAPI(TypedDict):
FILE: python/examples/math/schemaV2.py
class MathAPI (line 5) | class MathAPI(Protocol):
method add (line 10) | def add(self, x: float, y: float) -> float:
method sub (line 16) | def sub(self, x: float, y: float) -> float:
method mul (line 22) | def mul(self, x: float, y: float) -> float:
method div (line 28) | def div(self, x: float, y: float) -> float:
method neg (line 34) | def neg(self, x: float) -> float:
method id (line 40) | def id(self, x: float, y: float) -> float:
method unknown (line 46) | def unknown(self, text: str) -> float:
FILE: python/examples/multiSchema/agents.py
class JsonPrintAgent (line 28) | class JsonPrintAgent(Generic[T]):
method __init__ (line 32) | def __init__(self, model: TypeChatLanguageModel, target_type: type[T]):
method handle_request (line 37) | async def handle_request(self, line: str):
class MathAgent (line 46) | class MathAgent:
method __init__ (line 50) | def __init__(self, model: TypeChatLanguageModel):
method _handle_json_program_call (line 55) | async def _handle_json_program_call(self, func: str, args: Sequence[ob...
method handle_request (line 80) | async def handle_request(self, line: str):
class MusicAgent (line 92) | class MusicAgent:
method __init__ (line 98) | def __init__(self, model: TypeChatLanguageModel, authentication_vals: ...
method authenticate (line 105) | async def authenticate(self):
method handle_request (line 108) | async def handle_request(self, line: str):
FILE: python/examples/multiSchema/demo.py
function handle_unknown (line 20) | async def handle_unknown(_line: str):
function main (line 23) | async def main():
FILE: python/examples/multiSchema/router.py
class AgentInfo (line 6) | class AgentInfo(TypedDict):
class TaskClassification (line 12) | class TaskClassification(TypedDict):
class TextRequestRouter (line 16) | class TextRequestRouter:
method __init__ (line 21) | def __init__(self, model: TypeChatLanguageModel):
method register_agent (line 27) | def register_agent(self, name: str, description: str, handler: Callabl...
method route_request (line 31) | async def route_request(self, line: str):
FILE: python/examples/music/client.py
class Config (line 18) | class Config:
class ClientContext (line 23) | class ClientContext:
function get_client_context (line 32) | async def get_client_context(vals: dict[str, str | None]) -> ClientContext:
function play_album (line 71) | async def play_album(album_uri: str, context: ClientContext):
function play_tracks_with_query (line 75) | async def play_tracks_with_query(query: str, quantity: int, context: Cli...
function play_albums_with_query (line 82) | async def play_albums_with_query(query: str, quantity: int, context: Cli...
function play_artist_with_query (line 88) | async def play_artist_with_query(query: str, context: ClientContext):
function get_tracks_from_result_list (line 96) | def get_tracks_from_result_list(resultItems: list[Any]) -> list[Simplifi...
function get_tracks_from_search (line 111) | async def get_tracks_from_search(query: str, context: ClientContext) -> ...
function get_tracks_with_genres (line 125) | async def get_tracks_with_genres(
function print_tracks (line 142) | def print_tracks(tracks: list[SimplifiedTrackInfo]):
function update_track_list_and_print (line 149) | def update_track_list_and_print(tracks: list[SimplifiedTrackInfo], conte...
function get_current_users_playlists (line 154) | async def get_current_users_playlists(context: ClientContext) -> list[Si...
function print_status (line 170) | async def print_status(context: ClientContext):
function list_available_devices (line 177) | async def list_available_devices(context: ClientContext):
function handle_call (line 186) | async def handle_call(action: PlayerAction, context: ClientContext):
FILE: python/examples/music/demo.py
function main (line 10) | async def main():
FILE: python/examples/music/schema.py
class unknownActionParameters (line 4) | class unknownActionParameters(TypedDict):
class UnknownAction (line 8) | class UnknownAction(TypedDict):
class EmptyParameters (line 17) | class EmptyParameters(TypedDict):
class PlayParameters (line 21) | class PlayParameters(TypedDict, total=False):
class PlayAction (line 35) | class PlayAction(TypedDict):
class StatusAction (line 45) | class StatusAction(TypedDict):
class PauseAction (line 54) | class PauseAction(TypedDict):
class ResumeAction (line 63) | class ResumeAction(TypedDict):
class NextAction (line 72) | class NextAction(TypedDict):
class PreviousAction (line 81) | class PreviousAction(TypedDict):
class ShuffleActionParameters (line 90) | class ShuffleActionParameters(TypedDict):
class ShuffleAction (line 94) | class ShuffleAction(TypedDict):
class ListDevicesAction (line 103) | class ListDevicesAction(TypedDict):
class SelectDeviceActionParameters (line 112) | class SelectDeviceActionParameters(TypedDict):
class SelectDeviceAction (line 116) | class SelectDeviceAction(TypedDict):
class SelectVolumeActionParameters (line 125) | class SelectVolumeActionParameters(TypedDict):
class SetVolumeAction (line 129) | class SetVolumeAction(TypedDict):
class ChangeVolumeActionParameters (line 138) | class ChangeVolumeActionParameters(TypedDict):
class ChangeVolumeAction (line 142) | class ChangeVolumeAction(TypedDict):
class SearchTracksActionParameters (line 151) | class SearchTracksActionParameters(TypedDict):
class SearchTracksAction (line 162) | class SearchTracksAction(TypedDict):
class ListPlaylistsAction (line 173) | class ListPlaylistsAction(TypedDict):
class GetPlaylistActionParameters (line 182) | class GetPlaylistActionParameters(TypedDict):
class GetPlaylistAction (line 186) | class GetPlaylistAction(TypedDict):
class GetAlbumActionParameters (line 195) | class GetAlbumActionParameters(TypedDict):
class GetAlbumAction (line 199) | class GetAlbumAction(TypedDict):
class GetFavoritesActionParameters (line 209) | class GetFavoritesActionParameters(TypedDict):
class GetFavoritesAction (line 213) | class GetFavoritesAction(TypedDict):
class FilterTracksActionParameters (line 222) | class FilterTracksActionParameters(TypedDict):
class FilterTracksAction (line 231) | class FilterTracksAction(TypedDict):
class CreatePlaylistActionParameters (line 241) | class CreatePlaylistActionParameters(TypedDict):
class CreatePlaylistAction (line 245) | class CreatePlaylistAction(TypedDict):
class DeletePlaylistActionParameters (line 254) | class DeletePlaylistActionParameters(TypedDict):
class DeletePlaylistAction (line 258) | class DeletePlaylistAction(TypedDict):
class GetQueueAction (line 267) | class GetQueueAction(TypedDict):
class PlayerActions (line 301) | class PlayerActions(TypedDict):
FILE: python/examples/music/spotipyWrapper.py
class SimplifiedTrackInfo (line 10) | class SimplifiedTrackInfo:
class SimplifiedPlaylistInfo (line 20) | class SimplifiedPlaylistInfo:
class AsyncSpotipy (line 25) | class AsyncSpotipy:
method __init__ (line 28) | def __init__(self, service: spotipy.Spotify):
method devices (line 32) | async def devices(self) -> dict[str, Any]:
method search (line 35) | async def search(
method next (line 40) | async def next(self, result: dict[str, Any]) -> dict[str, Any]:
method artist (line 43) | async def artist(self, artist_id: str) -> dict[str, Any]:
method album (line 46) | async def album(self, album_id: str, market: str | None = None) -> dic...
method queue (line 49) | async def queue(self) -> dict[str, Any]:
method current_playback (line 52) | async def current_playback(self, market: str | None = None, additional...
method start_playback (line 55) | async def start_playback(
method pause_playback (line 65) | async def pause_playback(self, device_id: str | None = None) -> None:
method next_track (line 68) | async def next_track(self, device_id: str | None = None) -> None:
method previous_track (line 71) | async def previous_track(self, device_id: str | None = None) -> None:
method volume (line 74) | async def volume(self, volume_percent: int, device_id: str | None = No...
method shuffle (line 77) | async def shuffle(self, state: bool, device_id: str | None = None) -> ...
method transfer_playback (line 80) | async def transfer_playback(self, device_id: str, force_play: bool = T...
method current_user_top_tracks (line 83) | async def current_user_top_tracks(
method current_user_playlists (line 88) | async def current_user_playlists(self, limit: int = 50, offset: int = ...
method user_playlist_create (line 91) | async def user_playlist_create(
method playlist_items (line 96) | async def playlist_items(
method playlist_add_items (line 107) | async def playlist_add_items(self, playlist_id: str, items: list[str],...
method current_user_unfollow_playlist (line 110) | async def current_user_unfollow_playlist(self, playlist_id: str) -> None:
FILE: python/examples/restaurant/demo.py
function main (line 8) | async def main():
FILE: python/examples/restaurant/schema.py
class UnknownText (line 4) | class UnknownText(TypedDict):
class Pizza (line 13) | class Pizza(TypedDict, total=False):
class Beer (line 25) | class Beer(TypedDict):
class Salad (line 36) | class Salad(TypedDict, total=False):
class Order (line 48) | class Order(TypedDict):
FILE: python/examples/sentiment/demo.py
function main (line 11) | async def main():
FILE: python/examples/sentiment/schema.py
class Sentiment (line 5) | class Sentiment:
FILE: python/src/typechat/_internal/interactive.py
function process_requests (line 3) | async def process_requests(interactive_prompt: str, input_file_name: str...
FILE: python/src/typechat/_internal/model.py
class PromptSection (line 9) | class PromptSection(TypedDict):
class TypeChatLanguageModel (line 18) | class TypeChatLanguageModel(Protocol):
method complete (line 19) | async def complete(self, prompt: str | list[PromptSection]) -> Result[...
class HttpxLanguageModel (line 38) | class HttpxLanguageModel(TypeChatLanguageModel, AsyncContextManager):
method __init__ (line 51) | def __init__(self, url: str, headers: dict[str, str], default_params: ...
method complete (line 59) | async def complete(self, prompt: str | list[PromptSection]) -> Success...
method __aenter__ (line 100) | async def __aenter__(self) -> Self:
method __aexit__ (line 104) | async def __aexit__(self, __exc_type: type[BaseException] | None, __ex...
method __del__ (line 107) | def __del__(self):
function create_language_model (line 113) | def create_language_model(vals: dict[str, str | None]) -> HttpxLanguageM...
function create_openai_language_model (line 151) | def create_openai_language_model(api_key: str, model: str, endpoint: str...
function create_azure_openai_language_model (line 170) | def create_azure_openai_language_model(api_key: str, endpoint: str) -> H...
FILE: python/src/typechat/_internal/result.py
class Success (line 7) | class Success(Generic[T]):
class Failure (line 13) | class Failure:
FILE: python/src/typechat/_internal/translator.py
class TypeChatJsonTranslator (line 12) | class TypeChatJsonTranslator(Generic[T]):
method __init__ (line 24) | def __init__(
method translate (line 52) | async def translate(self, input: str, *, prompt_preamble: str | list[P...
method _create_request_prompt (line 103) | def _create_request_prompt(self, intent: str) -> str:
method _create_repair_prompt (line 117) | def _create_repair_prompt(self, validation_error: str) -> str:
FILE: python/src/typechat/_internal/ts_conversion/__init__.py
class TypeScriptSchemaConversionResult (line 13) | class TypeScriptSchemaConversionResult:
function python_type_to_typescript_schema (line 23) | def python_type_to_typescript_schema(py_type: type | TypeAliasType) -> T...
FILE: python/src/typechat/_internal/ts_conversion/python_type_to_ts_nodes.py
class GenericDeclarationish (line 60) | class GenericDeclarationish(Protocol):
class GenericAliasish (line 64) | class GenericAliasish(Protocol):
class Annotatedish (line 70) | class Annotatedish(Protocol):
class Dataclassish (line 75) | class Dataclassish(Protocol):
class TypeOfTypedDict (line 80) | class TypeOfTypedDict(Protocol):
function is_type_alias_type (line 86) | def is_type_alias_type(py_type: object) -> TypeGuard[TypeAliasType]:
function is_type_alias_type (line 89) | def is_type_alias_type(py_type: object) -> TypeGuard[TypeAliasType]:
function is_generic (line 93) | def is_generic(py_type: object) -> TypeGuard[GenericAliasish]:
function is_dataclass (line 96) | def is_dataclass(py_type: object) -> TypeGuard[Dataclassish]:
function is_python_type_or_alias (line 101) | def is_python_type_or_alias(origin: object) -> TypeGuard[type | TypeAlia...
class TypeScriptNodeTranslationResult (line 129) | class TypeScriptNodeTranslationResult:
function python_type_to_typescript_nodes (line 155) | def python_type_to_typescript_nodes(root_py_type: object) -> TypeScriptN...
FILE: python/src/typechat/_internal/ts_conversion/ts_node_to_string.py
function comment_to_str (line 21) | def comment_to_str(comment_text: str, indentation: str) -> str:
function ts_type_to_str (line 30) | def ts_type_to_str(type_node: TypeNode) -> str:
function object_member_to_str (line 67) | def object_member_to_str(member: PropertyDeclarationNode | IndexSignatur...
function ts_declaration_to_str (line 81) | def ts_declaration_to_str(declaration: TopLevelDeclarationNode) -> str:
FILE: python/src/typechat/_internal/ts_conversion/ts_type_nodes.py
class IdentifierNode (line 9) | class IdentifierNode:
class QualifiedNameNode (line 13) | class QualifiedNameNode:
class TypeReferenceNode (line 18) | class TypeReferenceNode:
class UnionTypeNode (line 23) | class UnionTypeNode:
class LiteralTypeNode (line 27) | class LiteralTypeNode:
class ArrayTypeNode (line 31) | class ArrayTypeNode:
class TupleTypeNode (line 35) | class TupleTypeNode:
class InterfaceDeclarationNode (line 39) | class InterfaceDeclarationNode:
class TypeParameterDeclarationNode (line 47) | class TypeParameterDeclarationNode:
class PropertyDeclarationNode (line 52) | class PropertyDeclarationNode:
class IndexSignatureDeclarationNode (line 59) | class IndexSignatureDeclarationNode:
class TypeAliasDeclarationNode (line 64) | class TypeAliasDeclarationNode:
FILE: python/src/typechat/_internal/validator.py
class TypeChatValidator (line 11) | class TypeChatValidator(Generic[T]):
method __init__ (line 18) | def __init__(self, py_type: type[T]):
method validate_object (line 27) | def validate_object(self, obj: object) -> Result[T]:
function _handle_error (line 48) | def _handle_error(validation_error: pydantic.ValidationError) -> Failure:
FILE: python/tests/__py3.11_snapshots__/test_conflicting_names_1/test_conflicting_names_1.schema.d.ts
type Derived (line 3) | interface Derived {
FILE: python/tests/__py3.11_snapshots__/test_hello_world/test_generic_alias1.schema.d.ts
type D_or_E (line 3) | type D_or_E = D | E
type E (line 6) | interface E extends C<string> {
type C (line 12) | interface C<T> {
type D (line 18) | interface D extends C<string> {
type NonClass (line 30) | interface NonClass {
type IndirectC (line 35) | type IndirectC = C<number>
FILE: python/tests/__py3.12+_snapshots__/test_generic_alias_3/test_generic_alias3.schema.d.ts
type FirstOrSecond (line 3) | type FirstOrSecond<T> = First<T> | Second<T>
type Second (line 5) | interface Second<T> {
type First (line 10) | interface First<T> {
FILE: python/tests/__py3.12+_snapshots__/test_generic_alias_4/test_generic_alias4.schema.d.ts
type Nested (line 3) | interface Nested {
type FirstOrSecond (line 7) | type FirstOrSecond<T> = First<T> | Second<T>
type Second (line 9) | interface Second<T> {
type First (line 14) | interface First<T> {
FILE: python/tests/__py3.12+_snapshots__/test_type_alias_syntax/test_type_alias_union1.schema.d.ts
type StrOrInt (line 3) | type StrOrInt = string | number
FILE: python/tests/__py3.12_snapshots__/test_conflicting_names_1/test_conflicting_names_1.schema.d.ts
type Derived (line 6) | interface Derived extends C, C {
type C (line 9) | interface C {
type C (line 13) | interface C {
FILE: python/tests/__py3.12_snapshots__/test_hello_world/test_generic_alias1.schema.d.ts
type D_or_E (line 3) | type D_or_E = D | E
type E (line 6) | interface E extends C<string> {
type C (line 12) | interface C<T> {
type D (line 18) | interface D extends C<string> {
type NonClass (line 30) | interface NonClass {
type IndirectC (line 35) | type IndirectC = C<number>
FILE: python/tests/__py3.13_snapshots__/test_conflicting_names_1/test_conflicting_names_1.schema.d.ts
type Derived (line 6) | interface Derived extends C, C {
type C (line 9) | interface C {
type C (line 13) | interface C {
FILE: python/tests/__py3.13_snapshots__/test_hello_world/test_generic_alias1.schema.d.ts
type D_or_E (line 3) | type D_or_E = D | E
type E (line 6) | interface E extends C<string> {
type C (line 12) | interface C<T> {
type D (line 18) | interface D extends C<string> {
type NonClass (line 30) | interface NonClass {
type IndirectC (line 35) | type IndirectC = C<number>
FILE: python/tests/__py3.14_snapshots__/test_conflicting_names_1/test_conflicting_names_1.schema.d.ts
type Derived (line 6) | interface Derived extends C, C {
type C (line 9) | interface C {
type C (line 13) | interface C {
FILE: python/tests/__py3.14_snapshots__/test_hello_world/test_generic_alias1.schema.d.ts
type D_or_E (line 3) | type D_or_E = D | E
type E (line 6) | interface E extends C<string> {
type C (line 12) | interface C<T> {
type D (line 18) | interface D extends C<string> {
type NonClass (line 30) | interface NonClass {
type IndirectC (line 35) | type IndirectC = C<number>
FILE: python/tests/__snapshots__/test_coffeeshop/test_coffeeshop_schema.schema.d.ts
type Cart (line 3) | interface Cart {
type UnknownText (line 9) | interface UnknownText {
type LineItem (line 15) | interface LineItem {
type EspressoDrink (line 21) | interface EspressoDrink {
type LattePreparation (line 30) | interface LattePreparation {
type Caffeine (line 35) | interface Caffeine {
type Topping (line 40) | interface Topping {
type Syrup (line 46) | interface Syrup {
type Sweetener (line 52) | interface Sweetener {
type Creamer (line 58) | interface Creamer {
type CoffeeDrink (line 63) | interface CoffeeDrink {
type LatteDrink (line 72) | interface LatteDrink {
type BakeryProduct (line 81) | interface BakeryProduct {
type BakeryPreparation (line 87) | interface BakeryPreparation {
type BakeryOption (line 92) | interface BakeryOption {
FILE: python/tests/__snapshots__/test_dataclasses/test_data_classes.schema.d.ts
type Response (line 3) | interface Response {
type Options (line 16) | interface Options {
FILE: python/tests/__snapshots__/test_generic_alias_1/test_generic_alias1.schema.d.ts
type FirstOrSecond (line 3) | type FirstOrSecond<T> = First<T> | Second<T>
type Second (line 5) | interface Second<T> {
type First (line 10) | interface First<T> {
FILE: python/tests/__snapshots__/test_generic_alias_2/test_generic_alias2.schema.d.ts
type Nested (line 3) | interface Nested {
type FirstOrSecond (line 7) | type FirstOrSecond<T> = First<T> | Second<T>
type Second (line 9) | interface Second<T> {
type First (line 14) | interface First<T> {
FILE: python/tests/__snapshots__/test_tuple_errors_1/test_tuples_2.schema.d.ts
type TupleContainer (line 14) | interface TupleContainer {
FILE: python/tests/__snapshots__/test_tuples_1/test_tuples_1.schema.d.ts
type TupleContainer (line 3) | interface TupleContainer {
FILE: python/tests/coffeeshop_deprecated.py
class UnknownText (line 9) | class UnknownText(TypedDict):
class Caffeine (line 18) | class Caffeine(TypedDict):
class Milk (line 23) | class Milk(TypedDict):
class Creamer (line 30) | class Creamer(TypedDict):
class Topping (line 46) | class Topping(TypedDict):
class LattePreparation (line 52) | class LattePreparation(TypedDict):
class Sweetener (line 57) | class Sweetener(TypedDict):
class Syrup (line 76) | class Syrup(TypedDict):
class LatteDrink (line 93) | class LatteDrink(TypedDict):
class EspressoDrink (line 101) | class EspressoDrink(TypedDict):
class CoffeeDrink (line 109) | class CoffeeDrink(TypedDict):
class BakeryOption (line 117) | class BakeryOption(TypedDict):
class BakeryPreparation (line 123) | class BakeryPreparation(TypedDict):
class BakeryProduct (line 128) | class BakeryProduct(TypedDict):
class LineItem (line 137) | class LineItem(TypedDict):
class Cart (line 143) | class Cart(TypedDict):
FILE: python/tests/test_coffeeshop.py
class UnknownText (line 5) | class UnknownText(TypedDict):
class Caffeine (line 14) | class Caffeine(TypedDict):
class Milk (line 19) | class Milk(TypedDict):
class Creamer (line 26) | class Creamer(TypedDict):
class Topping (line 42) | class Topping(TypedDict):
class LattePreparation (line 48) | class LattePreparation(TypedDict):
class Sweetener (line 53) | class Sweetener(TypedDict):
class Syrup (line 72) | class Syrup(TypedDict):
class LatteDrink (line 89) | class LatteDrink(TypedDict):
class EspressoDrink (line 97) | class EspressoDrink(TypedDict):
class CoffeeDrink (line 105) | class CoffeeDrink(TypedDict):
class BakeryOption (line 113) | class BakeryOption(TypedDict):
class BakeryPreparation (line 119) | class BakeryPreparation(TypedDict):
class BakeryProduct (line 124) | class BakeryProduct(TypedDict):
class LineItem (line 133) | class LineItem(TypedDict):
class Cart (line 139) | class Cart(TypedDict):
function test_coffeeshop_schema (line 144) | def test_coffeeshop_schema(snapshot: Any):
FILE: python/tests/test_conflicting_names_1.py
function a (line 7) | def a():
function b (line 13) | def b():
class Derived (line 21) | class Derived(A, B): # type: ignore
function test_conflicting_names_1 (line 24) | def test_conflicting_names_1(snapshot: Any):
FILE: python/tests/test_dataclasses.py
class Options (line 8) | class Options:
class Response (line 15) | class Response:
method do_something (line 25) | def do_something(self):
function test_data_classes (line 29) | def test_data_classes(snapshot: Any):
FILE: python/tests/test_generic_alias_1.py
class First (line 8) | class First(Generic[T], TypedDict):
class Second (line 13) | class Second(Generic[T], TypedDict):
function test_generic_alias1 (line 21) | def test_generic_alias1(snapshot: Any):
FILE: python/tests/test_generic_alias_2.py
class First (line 8) | class First(Generic[T], TypedDict):
class Second (line 13) | class Second(Generic[T], TypedDict):
class Nested (line 21) | class Nested(TypedDict):
function test_generic_alias2 (line 25) | def test_generic_alias2(snapshot: Any):
FILE: python/tests/test_generic_alias_3.py
function test_generic_alias3 (line 19) | def test_generic_alias3(snapshot: Any):
FILE: python/tests/test_generic_alias_4.py
function test_generic_alias4 (line 22) | def test_generic_alias4(snapshot: Any):
FILE: python/tests/test_hello_world.py
class C (line 8) | class C(Generic[T], TypedDict):
class D (line 16) | class D(C[str], total=False):
class E (line 30) | class E(C[str]):
function test_generic_alias1 (line 39) | def test_generic_alias1(snapshot: Any):
FILE: python/tests/test_translator.py
class ConvoRecord (line 7) | class ConvoRecord(TypedDict):
class FixedModel (line 11) | class FixedModel(typechat.TypeChatLanguageModel):
method __init__ (line 16) | def __init__(self, responses: list[str]) -> None:
method complete (line 22) | async def complete(self, prompt: str | list[typechat.PromptSection]) -...
class ExampleABC (line 34) | class ExampleABC:
function test_translator_with_immediate_pass (line 41) | def test_translator_with_immediate_pass(snapshot: Any):
function test_translator_with_single_failure (line 50) | def test_translator_with_single_failure(snapshot: Any):
function test_translator_with_invalid_json (line 60) | def test_translator_with_invalid_json(snapshot: Any):
function test_translator_with_single_failure_and_str_preamble (line 70) | def test_translator_with_single_failure_and_str_preamble(snapshot: Any):
function test_translator_with_single_failure_and_list_preamble_1 (line 83) | def test_translator_with_single_failure_and_list_preamble_1(snapshot: Any):
FILE: python/tests/test_tuple_errors_1.py
class TupleContainer (line 9) | class TupleContainer:
function test_tuples_2 (line 22) | def test_tuples_2(snapshot: Any):
FILE: python/tests/test_tuples_1.py
class TupleContainer (line 9) | class TupleContainer:
function test_tuples_1 (line 26) | def test_tuples_1(snapshot: Any):
FILE: python/tests/test_type_alias_syntax.py
function test_type_alias_union1 (line 6) | def test_type_alias_union1(snapshot: Any):
FILE: python/tests/test_validator.py
class Example (line 6) | class Example:
function test_dict_valid_as_dataclass (line 13) | def test_dict_valid_as_dataclass():
FILE: python/tests/utilities.py
class TypeScriptSchemaSnapshotExtension (line 13) | class TypeScriptSchemaSnapshotExtension(SingleFileSnapshotExtension):
method serialize (line 18) | def serialize(self, data: TypeScriptSchemaConversionResult, *,
class PyVersionedTypeScriptSchemaSnapshotExtension (line 33) | class PyVersionedTypeScriptSchemaSnapshotExtension(TypeScriptSchemaSnaps...
method dirname (line 38) | def dirname(cls, *, test_location: PyTestLocation) -> str:
class PyVersioned3_12_PlusSnapshotExtension (line 45) | class PyVersioned3_12_PlusSnapshotExtension(PyVersionedTypeScriptSchemaS...
function check_snapshot_for_module_string_if_3_12_plus (line 48) | def check_snapshot_for_module_string_if_3_12_plus(snapshot: Any, input_t...
function snapshot_schema (line 59) | def snapshot_schema(snapshot: Any):
FILE: typescript/examples/calendar/src/calendarActionsSchema.ts
type CalendarActions (line 3) | type CalendarActions = {
type Action (line 7) | type Action =
type AddEventAction (line 16) | type AddEventAction = {
type RemoveEventAction (line 21) | type RemoveEventAction = {
type AddParticipantsAction (line 26) | type AddParticipantsAction = {
type ChangeTimeRangeAction (line 34) | type ChangeTimeRangeAction = {
type ChangeDescriptionAction (line 42) | type ChangeDescriptionAction = {
type FindEventsAction (line 50) | type FindEventsAction = {
type UnknownAction (line 57) | interface UnknownAction {
type EventTimeRange (line 63) | type EventTimeRange = {
type Event (line 69) | type Event = {
type EventReference (line 81) | type EventReference = {
FILE: typescript/examples/coffeeShop-zod/src/main.ts
function processOrder (line 18) | function processOrder(cart: z.TypeOf<typeof CoffeeShopSchema.Cart>) {
FILE: typescript/examples/coffeeShop/src/coffeeShopSchema.ts
type Cart (line 3) | interface Cart {
type UnknownText (line 8) | interface UnknownText {
type LineItem (line 13) | interface LineItem {
type Product (line 19) | type Product = BakeryProducts | LatteDrinks | EspressoDrinks | CoffeeDri...
type BakeryProducts (line 21) | interface BakeryProducts {
type BakeryOptions (line 27) | interface BakeryOptions {
type BakeryPreparations (line 33) | interface BakeryPreparations {
type LatteDrinks (line 38) | interface LatteDrinks {
type EspressoDrinks (line 46) | interface EspressoDrinks {
type CoffeeDrinks (line 54) | interface CoffeeDrinks {
type Syrups (line 62) | interface Syrups {
type Caffeines (line 69) | interface Caffeines {
type Milks (line 74) | interface Milks {
type Creamers (line 79) | interface Creamers {
type Toppings (line 86) | interface Toppings {
type LattePreparations (line 92) | interface LattePreparations {
type Sweeteners (line 97) | interface Sweeteners {
type CoffeeTemperature (line 103) | type CoffeeTemperature = "hot" | "extra hot" | "warm" | "iced";
type CoffeeSize (line 105) | type CoffeeSize = "short" | "tall" | "grande" | "venti";
type EspressoSize (line 107) | type EspressoSize = "solo" | "doppio" | "triple" | "quad";
type OptionQuantity (line 109) | type OptionQuantity = "no" | "light" | "regular" | "extra" | number;
FILE: typescript/examples/coffeeShop/src/main.ts
function processOrder (line 20) | function processOrder(cart: Cart) {
FILE: typescript/examples/crossword/src/crosswordSchema.ts
type GetClueText (line 3) | type GetClueText = {
type GetAnswerValue (line 13) | type GetAnswerValue = {
type UnknownAction (line 22) | type UnknownAction = {
type CrosswordActions (line 30) | type CrosswordActions =
FILE: typescript/examples/crossword/src/translator.ts
function createCrosswordActionTranslator (line 10) | function createCrosswordActionTranslator<T extends object>(
FILE: typescript/examples/healthData/src/healthDataSchema.ts
type HealthDataResponse (line 3) | interface HealthDataResponse {
type HealthData (line 13) | interface HealthData {
type Medication (line 20) | interface Medication {
type Condition (line 32) | interface Condition {
type OtherHealthData (line 44) | interface OtherHealthData {
type ApproxQuantity (line 49) | interface ApproxQuantity {
type ApproxDatetime (line 56) | interface ApproxDatetime {
type Quantity (line 63) | interface Quantity {
FILE: typescript/examples/healthData/src/translator.ts
type ChatMessage (line 4) | type ChatMessage = {
type TranslatorWithHistory (line 9) | interface TranslatorWithHistory<T extends object> {
function createHealthDataTranslator (line 17) | function createHealthDataTranslator<T extends object>(model: TypeChatLan...
FILE: typescript/examples/math/src/main.ts
function handleCall (line 32) | async function handleCall(func: string, args: any[]): Promise<unknown> {
FILE: typescript/examples/math/src/mathSchema.ts
type API (line 3) | type API = {
FILE: typescript/examples/multiSchema/src/agent.ts
type AgentInfo (line 5) | type AgentInfo = {
type AgentClassificationResponse (line 10) | interface AgentClassificationResponse {
type MessageHandler (line 14) | type MessageHandler<T extends object> = (message: string) => Promise<Res...
type Agent (line 16) | interface Agent<T extends object> extends AgentInfo {
type JsonPrintAgent (line 20) | interface JsonPrintAgent<T extends object> extends Agent<T> {
function createJsonPrintAgent (line 24) | function createJsonPrintAgent<T extends object>(
type MathAgent (line 57) | interface MathAgent<T extends object> extends Agent<T> {
function createJsonMathAgent (line 62) | function createJsonMathAgent<T extends object>
FILE: typescript/examples/multiSchema/src/classificationSchema.ts
type TaskClassification (line 2) | interface TaskClassification {
type TaskClassificationResponse (line 10) | interface TaskClassificationResponse {
FILE: typescript/examples/multiSchema/src/router.ts
type AgentRouter (line 6) | interface AgentRouter<T extends object> {
function createAgentRouter (line 15) | function createAgentRouter<T extends object>(model: TypeChatLanguageMode...
FILE: typescript/examples/music/src/authz.ts
type AuthzHandlerFn (line 10) | type AuthzHandlerFn = (token: string | undefined) => void;
type AuthzHandler (line 11) | type AuthzHandler = AuthzHandlerFn | undefined;
type AuthzServer (line 12) | type AuthzServer = Server | undefined;
class Authzor (line 33) | class Authzor {
method constructor (line 40) | constructor(
method authorize (line 88) | authorize(connect: boolean, handler: AuthzHandlerFn) {
method close (line 106) | close() {
FILE: typescript/examples/music/src/chatifyActionsSchema.ts
type Track (line 3) | type Track = { name: string };
type TrackList (line 4) | type TrackList = Track[];
type Playlist (line 5) | type Playlist = TrackList;
type API (line 7) | type API = {
FILE: typescript/examples/music/src/dbInterface.ts
type Row (line 5) | type Row = { [key:string] : unknown }
function executeQuery (line 7) | function executeQuery(query: string, params: any[] = []): Row[] | void {
function insertTracks (line 27) | function insertTracks(tracks: SpotifyApi.TrackObjectFull[]) {
function getArtists (line 36) | function getArtists() {
FILE: typescript/examples/music/src/endpoints.ts
function search (line 6) | async function search(
function getTop (line 33) | async function getTop(
function getTopK (line 62) | async function getTopK(service: SpotifyService, k = limitMax) {
function getArtist (line 88) | async function getArtist(service: SpotifyService, id: string) {
function getHistoryURL (line 112) | async function getHistoryURL(service: SpotifyService, url: string) {
function getRecent (line 136) | async function getRecent(
function getUserProfile (line 162) | async function getUserProfile(service: SpotifyService) {
function getPlaybackState (line 186) | async function getPlaybackState(service: SpotifyService) {
function transferPlayback (line 209) | async function transferPlayback(
function play (line 236) | async function play(
function getDevices (line 279) | async function getDevices(service: SpotifyService) {
function pause (line 303) | async function pause(service: SpotifyService, deviceId: string) {
function getQueue (line 327) | async function getQueue(service: SpotifyService) {
function previous (line 350) | async function previous(service: SpotifyService, deviceId: string) {
function shuffle (line 374) | async function shuffle(
function next (line 402) | async function next(service: SpotifyService, deviceId: string) {
function getPlaylists (line 426) | async function getPlaylists(service: SpotifyService) {
function getAlbumTracks (line 447) | async function getAlbumTracks(service: SpotifyService, albumId: string) {
function getPlaylistTracks (line 470) | async function getPlaylistTracks(
function deletePlaylist (line 496) | async function deletePlaylist(
function createPlaylist (line 522) | async function createPlaylist(
function setVolume (line 559) | async function setVolume(service: SpotifyService, amt = limitMax) {
function getUrlWithParams (line 585) | function getUrlWithParams(urlString: string, queryParams: Record<string,...
FILE: typescript/examples/music/src/localParser.ts
function parseOut (line 8) | async function parseOut(request: string, surl: string) {
function localParser (line 23) | function localParser(userPrompt: string) {
FILE: typescript/examples/music/src/main.ts
type IClientContext (line 65) | interface IClientContext {
function printTrackNames (line 74) | async function printTrackNames(
function printPlaylist (line 100) | async function printPlaylist(
function chalkPlan (line 127) | function chalkPlan(plan: Program) {
function getClientContext (line 147) | async function getClientContext(token: string) {
function handleCall (line 180) | async function handleCall(
function index (line 539) | async function index(context: IClientContext) {
function checkAck (line 554) | function checkAck(input: string, program: Program): Program | undefined {
function musicApp (line 567) | async function musicApp() {
FILE: typescript/examples/music/src/playback.ts
function msToElapsedMinSec (line 6) | function msToElapsedMinSec(ms: number) {
function chalkStatus (line 21) | function chalkStatus(status: SpotifyApi.CurrentPlaybackResponse) {
function printStatus (line 42) | async function printStatus(context: IClientContext) {
function selectDevice (line 70) | async function selectDevice(keyword: string, context: IClientContext) {
function listAvailableDevices (line 103) | async function listAvailableDevices(context: IClientContext) {
FILE: typescript/examples/music/src/service.ts
type ClientData (line 3) | type ClientData = {
type User (line 8) | type User = {
class SpotifyService (line 14) | class SpotifyService {
method constructor (line 21) | constructor(clientData: ClientData) {
method storeToken (line 28) | storeToken(token: string): string {
method retrieveToken (line 33) | retrieveToken(): string {
method isLoggedIn (line 40) | isLoggedIn(): boolean {
method retrieveUser (line 44) | retrieveUser(): User {
method storeUser (line 51) | storeUser(user: User) {
method init (line 55) | async init(): Promise<Object> {
FILE: typescript/examples/music/src/trackCollections.ts
type ITrackCollection (line 4) | interface ITrackCollection {
class TrackCollection (line 11) | class TrackCollection implements ITrackCollection {
method constructor (line 14) | constructor(public tracks: SpotifyApi.TrackObjectFull[], public trackC...
method getContext (line 17) | getContext() {
method getTracks (line 21) | async getTracks(
method getTrackCount (line 27) | getTrackCount(): number {
method getPlaylist (line 31) | getPlaylist(): SpotifyApi.PlaylistObjectSimplified | undefined {
class PlaylistTrackCollection (line 36) | class PlaylistTrackCollection extends TrackCollection {
method constructor (line 37) | constructor(public playlist: SpotifyApi.PlaylistObjectSimplified,
method getPlaylist (line 44) | getPlaylist() {
class AlbumTrackCollection (line 49) | class AlbumTrackCollection extends TrackCollection {
method constructor (line 50) | constructor(public album: SpotifyApi.AlbumObjectSimplified,
FILE: typescript/examples/music/src/trackFilter.ts
type FilterTokenType (line 5) | enum FilterTokenType {
function splitNonWhitespace (line 19) | function splitNonWhitespace(str: string): string[] {
type FilterToken (line 24) | interface FilterToken {
function tokenize (line 29) | function tokenize(filter: string) {
type FilterConstraintType (line 59) | enum FilterConstraintType {
type FilterCombinerType (line 66) | enum FilterCombinerType {
type FilterCombiner (line 71) | interface FilterCombiner {
type FilterConstraint (line 77) | interface FilterConstraint {
type FilterNode (line 83) | type FilterNode = FilterConstraint | FilterCombiner;
type IFilterResult (line 85) | interface IFilterResult {
function makeFilterCombiner (line 90) | function makeFilterCombiner(combinerType = FilterCombinerType.AND) {
function makeFilterConstraint (line 102) | function makeFilterConstraint(
function isValueBoundary (line 113) | function isValueBoundary(tokenType: FilterTokenType) {
function filterNodeToString (line 120) | function filterNodeToString(node: FilterNode, depth = 0): string {
function simplifyFilterNode (line 136) | function simplifyFilterNode(ast: FilterNode): FilterNode {
type FilterStackFrame (line 151) | interface FilterStackFrame {
function parseFilter (line 156) | function parseFilter(filter: string): IFilterResult {
function applyFilterExpr (line 249) | async function applyFilterExpr(
function uniqueTracks (line 398) | function uniqueTracks(tracks: SpotifyApi.TrackObjectFull[]) {
function llmFilter (line 406) | async function llmFilter(
FILE: typescript/examples/restaurant/src/foodOrderViewSchema.ts
type Order (line 2) | type Order = {
type OrderItem (line 6) | type OrderItem = Pizza | Beer | Salad;
type UnknownText (line 9) | interface UnknownText {
type Pizza (line 14) | type Pizza = {
type Beer (line 28) | type Beer = {
type Salad (line 40) | type Salad = {
FILE: typescript/examples/restaurant/src/main.ts
function removeCommonStrings (line 57) | function removeCommonStrings(a: string[], b: string[]) {
function printOrder (line 76) | function printOrder(order: Order) {
FILE: typescript/examples/sentiment/src/sentimentSchema.ts
type SentimentResponse (line 3) | interface SentimentResponse {
FILE: typescript/src/interactive/interactive.ts
function processRequests (line 12) | async function processRequests(interactivePrompt: string, inputFileName:...
FILE: typescript/src/model.ts
type PromptSection (line 8) | interface PromptSection {
type PromptContent (line 19) | type PromptContent =
type MultimodalPromptContent (line 27) | type MultimodalPromptContent =
type TextPromptContent (line 32) | type TextPromptContent = {
type ImagePromptContent (line 37) | type ImagePromptContent = {
type ImageUrl (line 42) | type ImageUrl = {
type TypeChatLanguageModel (line 62) | interface TypeChatLanguageModel {
function createLanguageModel (line 94) | function createLanguageModel(env: Record<string, string | undefined>): T...
function createOpenAILanguageModel (line 118) | function createOpenAILanguageModel(apiKey: string, model: string, endPoi...
function createAzureOpenAILanguageModel (line 134) | function createAzureOpenAILanguageModel(apiKey: string, endPoint: string...
function createFetchLanguageModel (line 147) | function createFetchLanguageModel(url: string, headers: object, defaultP...
function isTransientHttpError (line 193) | function isTransientHttpError(code: number): boolean {
function sleep (line 208) | function sleep(ms: number): Promise<void> {
function missingEnvironmentVariable (line 215) | function missingEnvironmentVariable(name: string): never {
FILE: typescript/src/result.ts
type Success (line 4) | type Success<T> = { success: true, data: T };
type Error (line 9) | type Error = { success: false, message: string };
type Result (line 14) | type Result<T> = Success<T> | Error;
function success (line 21) | function success<T>(data: T): Success<T> {
function error (line 30) | function error(message: string): Error {
function getData (line 40) | function getData<T>(result: Result<T>) {
FILE: typescript/src/ts/program.ts
type Program (line 37) | type Program = {
type FunctionCall (line 45) | type FunctionCall = {
type Expression (line 55) | type Expression = JsonValue | FunctionCall | ResultReference;
type JsonValue (line 61) | type JsonValue = string | number | boolean | null | { [x: string]: Expre...
type ResultReference (line 66) | type ResultReference = {
function createModuleTextFromProgram (line 86) | function createModuleTextFromProgram(jsonObject: object): Result<string> {
function evaluateJsonProgram (line 147) | async function evaluateJsonProgram(program: Program, onCall: (func: stri...
function createProgramTranslator (line 196) | function createProgramTranslator(model: TypeChatLanguageModel, schema: s...
FILE: typescript/src/ts/validate.ts
type TypeScriptJsonValidator (line 18) | interface TypeScriptJsonValidator<T extends object> extends TypeChatJson...
function createTypeScriptJsonValidator (line 35) | function createTypeScriptJsonValidator<T extends object = object>(schema...
FILE: typescript/src/typechat.ts
type TypeChatJsonTranslator (line 7) | interface TypeChatJsonTranslator<T extends object> {
type TypeChatJsonValidator (line 69) | interface TypeChatJsonValidator<T extends object> {
function createJsonTranslator (line 97) | function createJsonTranslator<T extends object>(model: TypeChatLanguageM...
function stripNulls (line 170) | function stripNulls(obj: any) {
FILE: typescript/src/zod/validate.ts
function createZodJsonValidator (line 14) | function createZodJsonValidator<T extends Record<string, z.ZodType>, K e...
function getTypeKind (line 32) | function getTypeKind(type: z.ZodType) {
function getTypeIdentity (line 36) | function getTypeIdentity(type: z.ZodType): object {
type TypePrecedence (line 48) | const enum TypePrecedence {
function getTypePrecedence (line 54) | function getTypePrecedence(type: z.ZodType): TypePrecedence {
function getZodSchemaAsTypeScript (line 75) | function getZodSchemaAsTypeScript(schema: Record<string, z.ZodType>): st...
Condensed preview — 246 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (565K chars).
[
{
"path": ".devcontainer/devcontainer.json",
"chars": 1907,
"preview": "// For format details, see https://aka.ms/devcontainer.json. For config options, see the\n// README at: https://github.co"
},
{
"path": ".github/dependabot.yml",
"chars": 567,
"preview": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where "
},
{
"path": ".github/workflows/ci.js.yml",
"chars": 1168,
"preview": "# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tes"
},
{
"path": ".github/workflows/ci.python.yml",
"chars": 1746,
"preview": "name: Python CI\n\non:\n push:\n branches:\n - main\n pull_request:\n branches:\n - main\n\ndefaults:\n run:\n "
},
{
"path": ".github/workflows/github-pages.yml",
"chars": 781,
"preview": "name: Deploy to GitHub Pages\non:\n push:\n branches:\n - main\n \npermissions:\n contents: read\n id-token: wri"
},
{
"path": ".gitignore",
"chars": 203,
"preview": "build/\r\ndist/\r\nout/\r\nnode_modules/\r\n.venv/\r\n.env*\r\n*.map\r\n*.out.txt\r\n*.bat\r\n\r\n# Local development and debugging\r\n.scratc"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 444,
"preview": "# Microsoft Open Source Code of Conduct\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://op"
},
{
"path": "LICENSE",
"chars": 1141,
"preview": " MIT License\n\n Copyright (c) Microsoft Corporation.\n\n Permission is hereby granted, free of charge, to any pers"
},
{
"path": "README.md",
"chars": 3841,
"preview": "# TypeChat\n\nTypeChat is a library that makes it easy to build natural language interfaces using types.\n\nBuilding natural"
},
{
"path": "SECURITY.md",
"chars": 2757,
"preview": "<!-- BEGIN MICROSOFT SECURITY.MD V0.0.8 BLOCK -->\n\n## Security\n\nMicrosoft takes the security of our software products an"
},
{
"path": "SUPPORT.md",
"chars": 499,
"preview": "# Support\r\n\r\n## How to file issues and get help \r\n\r\nThis project uses GitHub Issues to track bugs and feature requests."
},
{
"path": "TypeChat.code-workspace",
"chars": 204,
"preview": "{\n\t\"folders\": [\n\t\t{\n\t\t\t\"name\": \"TypeChat Root\",\n\t\t\t\"path\": \"./\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"Python\",\n\t\t\t\"path\": \"./python\"\n\t\t},"
},
{
"path": "dotnet/README.md",
"chars": 152,
"preview": "# TypeChat for .NET\n\nTypeChat in .NET and C# is currently available on a separate [TypeChat.NET repository](https://gith"
},
{
"path": "python/.gitignore",
"chars": 3092,
"preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
},
{
"path": "python/LICENSE",
"chars": 1141,
"preview": " MIT License\n\n Copyright (c) Microsoft Corporation.\n\n Permission is hereby granted, free of charge, to any pers"
},
{
"path": "python/README.md",
"chars": 3966,
"preview": "# TypeChat\n\nTypeChat is a library that makes it easy to build natural language interfaces using types.\n\nBuilding natural"
},
{
"path": "python/examples/README.md",
"chars": 6434,
"preview": "\nTo see TypeChat in action, check out the examples found in this directory.\n\nEach example shows how TypeChat handles nat"
},
{
"path": "python/examples/calendar/README.md",
"chars": 928,
"preview": "# Calendar\n\nThe Calendar example shows how you can capture user intent as a sequence of actions, such as adding event to"
},
{
"path": "python/examples/calendar/demo.py",
"chars": 1184,
"preview": "import asyncio\nimport json\n\nimport sys\nfrom dotenv import dotenv_values\nimport schema as calendar\nfrom typechat import F"
},
{
"path": "python/examples/calendar/input.txt",
"chars": 881,
"preview": "I need to get my tires changed from 12:00 to 2:00 pm on Friday March 15, 2024\nSearch for any meetings with Gavin this we"
},
{
"path": "python/examples/calendar/schema.py",
"chars": 2757,
"preview": "from typing_extensions import Literal, NotRequired, TypedDict, Annotated, Doc\n\nclass UnknownAction(TypedDict):\n \"\"\"\n "
},
{
"path": "python/examples/coffeeShop/README.md",
"chars": 1150,
"preview": "# Coffee Shop\n\nThe Coffee Shop example shows how to capture user intent as a set of \"nouns\".\nIn this case, the nouns are"
},
{
"path": "python/examples/coffeeShop/demo.py",
"chars": 1153,
"preview": "import asyncio\nimport json\nimport sys\n\nimport schema as coffeeshop\nfrom dotenv import dotenv_values\n\nfrom typechat impor"
},
{
"path": "python/examples/coffeeShop/input.txt",
"chars": 2017,
"preview": "i'd like a latte that's it\ni'll have a dark roast coffee thank you\nget me a coffee please\ncould i please get two mochas "
},
{
"path": "python/examples/coffeeShop/input2.txt",
"chars": 319,
"preview": "two tall lattes. the first one with no foam. the second one with whole milk.\ntwo tall lattes. the first one with no foam"
},
{
"path": "python/examples/coffeeShop/schema.py",
"chars": 4173,
"preview": "from typing_extensions import Literal, NotRequired, TypedDict, Annotated, Doc\n\n\nclass UnknownText(TypedDict):\n \"\"\"\n "
},
{
"path": "python/examples/healthData/README.md",
"chars": 863,
"preview": "# Health Data Agent\n\nThis example requires GPT-4.\n\nDemonstrates a ***strongly typed*** chat: a natural language interfac"
},
{
"path": "python/examples/healthData/demo.py",
"chars": 1712,
"preview": "import asyncio\nimport json\nimport sys\nfrom dotenv import dotenv_values\nimport schema as health\nfrom typechat import Fail"
},
{
"path": "python/examples/healthData/input.txt",
"chars": 1044,
"preview": "#\n# Conversations with a Health Data Agent\n# For each conversation: \n# You start with the first line \n# Then type the ne"
},
{
"path": "python/examples/healthData/schema.py",
"chars": 2293,
"preview": "from typing_extensions import TypedDict, Annotated, NotRequired, Literal, Doc\n\n\nclass Quantity(TypedDict):\n value: An"
},
{
"path": "python/examples/healthData/translator.py",
"chars": 2305,
"preview": "import json\nfrom typing_extensions import TypeVar, Any, override, TypedDict, Literal\n\nfrom typechat import TypeChatValid"
},
{
"path": "python/examples/math/README.md",
"chars": 1011,
"preview": "# Math\n\nThe Math example shows how to use TypeChat for program generation based on an API schema with the `evaluateJsonP"
},
{
"path": "python/examples/math/demo.py",
"chars": 1838,
"preview": "import asyncio\nfrom collections.abc import Sequence\nimport json\nimport sys\nfrom typing import cast\nfrom dotenv import do"
},
{
"path": "python/examples/math/input.txt",
"chars": 121,
"preview": "1 + 2\r\n1 + 2 * 3\r\n2 * 3 + 4 * 5\r\n2 3 * 4 5 * +\r\nmultiply two by three, then multiply four by five, then sum the results\r"
},
{
"path": "python/examples/math/program.py",
"chars": 5414,
"preview": "from __future__ import annotations\nimport asyncio\nfrom collections.abc import Sequence\nfrom typing import Any, TypeAlias"
},
{
"path": "python/examples/math/schema.py",
"chars": 678,
"preview": "from typing_extensions import TypedDict, Annotated, Callable, Doc\n\n\nclass MathAPI(TypedDict):\n \"\"\"\n This is API fo"
},
{
"path": "python/examples/math/schemaV2.py",
"chars": 922,
"preview": "from typing_extensions import Protocol, runtime_checkable\n\n\n@runtime_checkable\nclass MathAPI(Protocol):\n \"\"\"\n This"
},
{
"path": "python/examples/multiSchema/README.md",
"chars": 472,
"preview": "# MultiSchema\n\nThis application demonstrates a simple way to write a **super-app** that automatically routes user reques"
},
{
"path": "python/examples/multiSchema/agents.py",
"chars": 4446,
"preview": "from collections.abc import Sequence\r\nimport os\r\nimport sys\r\nfrom typing import cast\r\n\r\nexamples_path = os.path.abspath("
},
{
"path": "python/examples/multiSchema/demo.py",
"chars": 2925,
"preview": "import os\r\nimport sys\r\n\r\nexamples_path = os.path.abspath(os.path.join(os.path.dirname(__file__), \"..\", \"..\"))\r\nif exampl"
},
{
"path": "python/examples/multiSchema/input.txt",
"chars": 461,
"preview": "I'd like two large, one with pepperoni and the other with extra sauce. The pepperoni gets basil and the extra sauce get"
},
{
"path": "python/examples/multiSchema/router.py",
"chars": 1875,
"preview": "import json\r\nfrom typing_extensions import Any, Callable, Awaitable, TypedDict, Annotated\r\nfrom typechat import Failure,"
},
{
"path": "python/examples/music/README.md",
"chars": 1802,
"preview": "# Music\r\n\r\nThe Music example shows how to capture user intent as actions in JSON which corresponds to a simple dataflow "
},
{
"path": "python/examples/music/client.py",
"chars": 16386,
"preview": "import os\nimport sys\n\ncurrent_path = os.path.abspath(os.path.dirname(__file__))\nif current_path not in sys.path:\n sys"
},
{
"path": "python/examples/music/demo.py",
"chars": 1502,
"preview": "import asyncio\nimport json\n\nimport sys\nfrom dotenv import dotenv_values\nimport schema as music\nfrom typechat import Fail"
},
{
"path": "python/examples/music/input.txt",
"chars": 545,
"preview": "play Taylor Swift Shake It Off\r\nget my top 20 favorites and make a playlist named animalTracks of the tracks that have a"
},
{
"path": "python/examples/music/schema.py",
"chars": 7271,
"preview": "from typing_extensions import Literal, Required, NotRequired, TypedDict, Annotated, Doc\n\n\nclass unknownActionParameters("
},
{
"path": "python/examples/music/spotipyWrapper.py",
"chars": 5091,
"preview": "from typing_extensions import Any\nfrom dataclasses import dataclass, field\nimport spotipy # type: ignore\n\n# The spotipy "
},
{
"path": "python/examples/restaurant/README.md",
"chars": 2711,
"preview": "# Restaurant\n\nThe Restaurant example shows how to capture user intent as a set of \"nouns\", but with more complex linguis"
},
{
"path": "python/examples/restaurant/demo.py",
"chars": 1161,
"preview": "import asyncio\nimport json\nimport sys\nfrom dotenv import dotenv_values\nimport schema as restaurant\nfrom typechat import "
},
{
"path": "python/examples/restaurant/input.txt",
"chars": 1941,
"preview": "I'd like two large, one with pepperoni and the other with extra sauce. The pepperoni gets basil and the extra sauce get"
},
{
"path": "python/examples/restaurant/schema.py",
"chars": 1697,
"preview": "from typing_extensions import Literal, Required, NotRequired, TypedDict, Annotated, Doc\n\n\nclass UnknownText(TypedDict):\n"
},
{
"path": "python/examples/sentiment/README.md",
"chars": 590,
"preview": "# Sentiment\n\nThe Sentiment example shows how to match user intent to a set of nouns, in this case categorizing user sent"
},
{
"path": "python/examples/sentiment/demo.py",
"chars": 913,
"preview": "import asyncio\nimport sys\n\nfrom dotenv import dotenv_values\nfrom typechat import (Failure, TypeChatJsonTranslator, TypeC"
},
{
"path": "python/examples/sentiment/input.txt",
"chars": 79,
"preview": "hello, world\nTypeChat is awesome!\nI'm having a good day\nit's very rainy outside"
},
{
"path": "python/examples/sentiment/schema.py",
"chars": 331,
"preview": "from dataclasses import dataclass\nfrom typing_extensions import Literal, Annotated, Doc\n\n@dataclass\nclass Sentiment:\n "
},
{
"path": "python/notebooks/calendar.ipynb",
"chars": 3214,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"metadata\": {},\n \"outputs\": [],\n \"source\": "
},
{
"path": "python/notebooks/coffeeShop.ipynb",
"chars": 3005,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"metadata\": {},\n \"outputs\": [],\n \"source\": "
},
{
"path": "python/notebooks/healthData.ipynb",
"chars": 4180,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"metadata\": {},\n \"outputs\": [],\n \"source\": "
},
{
"path": "python/notebooks/math.ipynb",
"chars": 3870,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"metadata\": {},\n \"outputs\": [],\n \"source\": "
},
{
"path": "python/notebooks/music.ipynb",
"chars": 3661,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"metadata\": {},\n \"outputs\": [],\n \"source\": "
},
{
"path": "python/notebooks/restaurant.ipynb",
"chars": 2969,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"metadata\": {},\n \"outputs\": [],\n \"source\": "
},
{
"path": "python/notebooks/sentiment.ipynb",
"chars": 2456,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"metadata\": {},\n \"outputs\": [],\n \"source\": "
},
{
"path": "python/package.json",
"chars": 321,
"preview": "{\n \"name\": \"typechat-py\",\n \"private\": true,\n \"version\": \"0.0.4\",\n \"description\": \"TypeChat is a library that makes i"
},
{
"path": "python/pyproject.toml",
"chars": 4063,
"preview": "[build-system]\nrequires = [\"hatchling\"]\nbuild-backend = \"hatchling.build\"\n\n[project]\nname = \"typechat\"\ndynamic = [\"versi"
},
{
"path": "python/pyrightconfig.json",
"chars": 538,
"preview": "{\n \"typeCheckingMode\": \"strict\",\n \"reportCallInDefaultInitializer\": \"error\",\n \"reportImplicitOverride\": \"error\""
},
{
"path": "python/src/typechat/__about__.py",
"chars": 103,
"preview": "# SPDX-FileCopyrightText: Microsoft Corporation\n#\n# SPDX-License-Identifier: MIT\n__version__ = \"0.0.4\"\n"
},
{
"path": "python/src/typechat/__init__.py",
"chars": 905,
"preview": "# SPDX-FileCopyrightText: Microsoft Corporation\n#\n# SPDX-License-Identifier: MIT\n\nfrom typechat._internal.model import P"
},
{
"path": "python/src/typechat/_internal/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "python/src/typechat/_internal/interactive.py",
"chars": 1530,
"preview": "from typing import Callable, Awaitable\n\nasync def process_requests(interactive_prompt: str, input_file_name: str | None,"
},
{
"path": "python/src/typechat/_internal/model.py",
"chars": 6975,
"preview": "import asyncio\nfrom types import TracebackType\nfrom typing_extensions import AsyncContextManager, Literal, Protocol, Sel"
},
{
"path": "python/src/typechat/_internal/result.py",
"chars": 511,
"preview": "from dataclasses import dataclass\nfrom typing_extensions import Generic, TypeAlias, TypeVar\n\nT = TypeVar(\"T\", covariant="
},
{
"path": "python/src/typechat/_internal/translator.py",
"chars": 5149,
"preview": "from typing_extensions import Generic, TypeVar\n\nimport pydantic_core\n\nfrom typechat._internal.model import PromptSection"
},
{
"path": "python/src/typechat/_internal/ts_conversion/__init__.py",
"chars": 1255,
"preview": "from dataclasses import dataclass\nfrom typing_extensions import TypeAliasType\n\nfrom typechat._internal.ts_conversion.pyt"
},
{
"path": "python/src/typechat/_internal/ts_conversion/python_type_to_ts_nodes.py",
"chars": 17909,
"preview": "from __future__ import annotations\n\nfrom collections import OrderedDict\nimport inspect\nimport sys\nimport typing\nimport t"
},
{
"path": "python/src/typechat/_internal/ts_conversion/ts_node_to_string.py",
"chars": 4260,
"preview": "import json\nfrom typing_extensions import assert_never\n\nfrom typechat._internal.ts_conversion.ts_type_nodes import (\n "
},
{
"path": "python/src/typechat/_internal/ts_conversion/ts_type_nodes.py",
"chars": 2053,
"preview": "from __future__ import annotations\n\nfrom dataclasses import dataclass\nfrom typing_extensions import TypeAlias\n\nTypeNode:"
},
{
"path": "python/src/typechat/_internal/validator.py",
"chars": 2508,
"preview": "import json\nfrom typing_extensions import Generic, TypeVar\n\nimport pydantic\nimport pydantic_core\n\nfrom typechat._interna"
},
{
"path": "python/src/typechat/py.typed",
"chars": 0,
"preview": ""
},
{
"path": "python/tests/__init__.py",
"chars": 81,
"preview": "# SPDX-FileCopyrightText: Microsoft Corporation\n#\n# SPDX-License-Identifier: MIT\n"
},
{
"path": "python/tests/__py3.11_snapshots__/test_conflicting_names_1/test_conflicting_names_1.schema.d.ts",
"chars": 98,
"preview": "// Entry point is: 'Derived'\n\ninterface Derived {\n my_attr_1: string;\n my_attr_2: number;\n}\n"
},
{
"path": "python/tests/__py3.11_snapshots__/test_hello_world/test_generic_alias1.schema.d.ts",
"chars": 693,
"preview": "// Entry point is: 'D_or_E'\n\ntype D_or_E = D | E\n\n// This is the definition of the class E.\ninterface E extends C<string"
},
{
"path": "python/tests/__py3.12+_snapshots__/test_generic_alias_3/test_generic_alias3.schema.d.ts",
"chars": 208,
"preview": "// Entry point is: 'FirstOrSecond'\n\ntype FirstOrSecond<T> = First<T> | Second<T>\n\ninterface Second<T> {\n kind: \"secon"
},
{
"path": "python/tests/__py3.12+_snapshots__/test_generic_alias_4/test_generic_alias4.schema.d.ts",
"chars": 256,
"preview": "// Entry point is: 'Nested'\n\ninterface Nested {\n item: FirstOrSecond<string>;\n}\n\ntype FirstOrSecond<T> = First<T> | S"
},
{
"path": "python/tests/__py3.12+_snapshots__/test_type_alias_syntax/test_type_alias_union1.schema.d.ts",
"chars": 63,
"preview": "// Entry point is: 'StrOrInt'\n\ntype StrOrInt = string | number\n"
},
{
"path": "python/tests/__py3.12_snapshots__/test_conflicting_names_1/test_conflicting_names_1.schema.d.ts",
"chars": 355,
"preview": "// Entry point is: 'Derived'\n\n// ERRORS:\n// !!! Cannot create a schema using two types with the same name. C conflicts b"
},
{
"path": "python/tests/__py3.12_snapshots__/test_hello_world/test_generic_alias1.schema.d.ts",
"chars": 693,
"preview": "// Entry point is: 'D_or_E'\n\ntype D_or_E = D | E\n\n// This is the definition of the class E.\ninterface E extends C<string"
},
{
"path": "python/tests/__py3.13_snapshots__/test_conflicting_names_1/test_conflicting_names_1.schema.d.ts",
"chars": 355,
"preview": "// Entry point is: 'Derived'\n\n// ERRORS:\n// !!! Cannot create a schema using two types with the same name. C conflicts b"
},
{
"path": "python/tests/__py3.13_snapshots__/test_hello_world/test_generic_alias1.schema.d.ts",
"chars": 693,
"preview": "// Entry point is: 'D_or_E'\n\ntype D_or_E = D | E\n\n// This is the definition of the class E.\ninterface E extends C<string"
},
{
"path": "python/tests/__py3.14_snapshots__/test_conflicting_names_1/test_conflicting_names_1.schema.d.ts",
"chars": 355,
"preview": "// Entry point is: 'Derived'\n\n// ERRORS:\n// !!! Cannot create a schema using two types with the same name. C conflicts b"
},
{
"path": "python/tests/__py3.14_snapshots__/test_hello_world/test_generic_alias1.schema.d.ts",
"chars": 693,
"preview": "// Entry point is: 'D_or_E'\n\ntype D_or_E = D | E\n\n// This is the definition of the class E.\ninterface E extends C<string"
},
{
"path": "python/tests/__snapshots__/test_coffeeshop/test_coffeeshop_schema.schema.d.ts",
"chars": 3157,
"preview": "// Entry point is: 'Cart'\n\ninterface Cart {\n type: \"Cart\";\n items: Array<LineItem | UnknownText>;\n}\n\n// Represents"
},
{
"path": "python/tests/__snapshots__/test_dataclasses/test_data_classes.schema.d.ts",
"chars": 322,
"preview": "// Entry point is: 'Response'\n\ninterface Response {\n attr_1: string;\n // Hello!\n attr_2: number;\n attr_3: st"
},
{
"path": "python/tests/__snapshots__/test_generic_alias_1/test_generic_alias1.schema.d.ts",
"chars": 208,
"preview": "// Entry point is: 'FirstOrSecond'\n\ntype FirstOrSecond<T> = First<T> | Second<T>\n\ninterface Second<T> {\n kind: \"secon"
},
{
"path": "python/tests/__snapshots__/test_generic_alias_2/test_generic_alias2.schema.d.ts",
"chars": 256,
"preview": "// Entry point is: 'Nested'\n\ninterface Nested {\n item: FirstOrSecond<string>;\n}\n\ntype FirstOrSecond<T> = First<T> | S"
},
{
"path": "python/tests/__snapshots__/test_translator.ambr",
"chars": 10913,
"preview": "# serializer version: 1\n# name: test_translator_with_immediate_pass\n list([\n dict({\n 'kind': 'CLIENT REQUEST',\n"
},
{
"path": "python/tests/__snapshots__/test_tuple_errors_1/test_tuples_2.schema.d.ts",
"chars": 1219,
"preview": "// Entry point is: 'TupleContainer'\n\n// ERRORS:\n// !!! '()' cannot be used as a type annotation.\n// !!! '()' cannot be u"
},
{
"path": "python/tests/__snapshots__/test_tuples_1/test_tuples_1.schema.d.ts",
"chars": 417,
"preview": "// Entry point is: 'TupleContainer'\n\ninterface TupleContainer {\n empty_tuple: [];\n tuple_1: [number];\n tuple_2:"
},
{
"path": "python/tests/coffeeshop_deprecated.py",
"chars": 4599,
"preview": "from typing import List, Literal, NotRequired, TypeAlias, TypedDict, Union\n\nfrom typechat import python_type_to_typescri"
},
{
"path": "python/tests/test_coffeeshop.py",
"chars": 4457,
"preview": "from typing_extensions import Literal, NotRequired, TypedDict, Annotated, Doc, Any\nfrom typechat import python_type_to_t"
},
{
"path": "python/tests/test_conflicting_names_1.py",
"chars": 553,
"preview": "from typing import Any, TypedDict, cast\n\nfrom typechat import python_type_to_typescript_schema\nfrom .utilities import Py"
},
{
"path": "python/tests/test_dataclasses.py",
"chars": 824,
"preview": "from typing_extensions import Any\nfrom typing import Annotated\nfrom dataclasses import dataclass, field\nfrom typechat im"
},
{
"path": "python/tests/test_generic_alias_1.py",
"chars": 672,
"preview": "from typing_extensions import TypeAliasType, Any\nfrom typing import Literal, TypedDict, TypeVar, Generic\nfrom typechat i"
},
{
"path": "python/tests/test_generic_alias_2.py",
"chars": 721,
"preview": "from typing_extensions import TypeAliasType, Any\nfrom typing import Literal, TypedDict, Generic, TypeVar\nfrom typechat i"
},
{
"path": "python/tests/test_generic_alias_3.py",
"chars": 504,
"preview": "from typing import Any\nfrom .utilities import check_snapshot_for_module_string_if_3_12_plus\n\nmodule_str = \"\"\"\nfrom typin"
},
{
"path": "python/tests/test_generic_alias_4.py",
"chars": 552,
"preview": "from typing import Any\nfrom .utilities import check_snapshot_for_module_string_if_3_12_plus\n\nmodule_str = \"\"\"\nfrom typin"
},
{
"path": "python/tests/test_hello_world.py",
"chars": 1242,
"preview": "from typing import Annotated, Literal, NotRequired, Optional, Required, Self, TypedDict, TypeVar, Generic, Any\nfrom typi"
},
{
"path": "python/tests/test_translator.py",
"chars": 3098,
"preview": "\nimport asyncio\nfrom dataclasses import dataclass\nfrom typing_extensions import Any, Iterator, Literal, TypedDict, overr"
},
{
"path": "python/tests/test_tuple_errors_1.py",
"chars": 967,
"preview": "\nfrom dataclasses import dataclass\nfrom typing import Any\n\nfrom typechat import python_type_to_typescript_schema\nfrom .u"
},
{
"path": "python/tests/test_tuples_1.py",
"chars": 1000,
"preview": "\nfrom dataclasses import dataclass\nfrom typing import Any\n\nfrom typechat import python_type_to_typescript_schema\nfrom .u"
},
{
"path": "python/tests/test_type_alias_syntax.py",
"chars": 262,
"preview": "from typing import Any\nfrom .utilities import check_snapshot_for_module_string_if_3_12_plus\n\nmodule_str = \"type StrOrInt"
},
{
"path": "python/tests/test_validator.py",
"chars": 325,
"preview": "\nfrom dataclasses import dataclass\nimport typechat\n\n@dataclass\nclass Example:\n a: str\n b: int\n c: bool\n\nv = typ"
},
{
"path": "python/tests/utilities.py",
"chars": 2191,
"preview": "from pathlib import Path\nimport sys\nimport types\n\nfrom typing_extensions import Any, override\nimport pytest\n\nfrom syrupy"
},
{
"path": "site/.eleventy.js",
"chars": 1612,
"preview": "\nconst dateFormatter = new Intl.DateTimeFormat(\"en-US\", { year: \"numeric\", month: \"long\", day: \"numeric\" });\nconst listF"
},
{
"path": "site/.gitignore",
"chars": 5,
"preview": "_site"
},
{
"path": "site/jsconfig.json",
"chars": 252,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es2021\",\n \"module\": \"nodenext\",\n \"lib\": [\"esnext\", \"es2021.i"
},
{
"path": "site/package.json",
"chars": 568,
"preview": "{\n \"name\": \"typechat-site\",\n \"private\": true,\n \"version\": \"0.0.1\",\n \"description\": \"Website for TypeChat\",\n \"main\":"
},
{
"path": "site/src/_data/docsTOC.json",
"chars": 417,
"preview": "[\n {\n \"groupName\": \"Home\",\n \"pages\": [\n { \"title\": \"Introduction\", \"url\": \"/docs/introductio"
},
{
"path": "site/src/_data/headernav.json",
"chars": 310,
"preview": "[\n {\n \"title\": \"Home\",\n \"dest\": \"/\"\n },\n {\n \"title\": \"Docs\",\n \"dest\": \"/docs/\"\n "
},
{
"path": "site/src/_includes/base.njk",
"chars": 779,
"preview": "<!DOCTYPE html>\n<html lang=\"en\" class=\"h-100\">\n\n<head>\n <meta charset='utf-8'>\n <meta http-equiv='X-UA-Compatible'"
},
{
"path": "site/src/_includes/blog.njk",
"chars": 1551,
"preview": "---\nlayout: base\n---\n\n<main>\n<nav class=\"typechat-docs-smol-nav d-md-none mx-auto text-center\">\n <label class=\"mb-3\">"
},
{
"path": "site/src/_includes/doc-page.njk",
"chars": 58,
"preview": "---\nlayout: docs\n---\n<h1>{{title}}</h1>\n{{content | safe}}"
},
{
"path": "site/src/_includes/docs.njk",
"chars": 2009,
"preview": "---\nlayout: base\ntitle: Docs\n---\n<main>\n<nav class=\"typechat-docs-smol-nav d-md-none mx-auto text-center\">\n <label cl"
},
{
"path": "site/src/_includes/footer.njk",
"chars": 1132,
"preview": "<footer class=\"container mt-auto d-flex flex-wrap justify-content-between align-items-center py-3 border-top\">\n <ul c"
},
{
"path": "site/src/_includes/header-prologue.njk",
"chars": 1306,
"preview": "<a class=\"skip-to-main\" href=\"#site-content\" tabindex=\"0\">Skip to main content</a>\n<div class=\"d-flex w-100 h-100 p-3 mx"
},
{
"path": "site/src/blog/announcing-typechat-0-1-0.md",
"chars": 8607,
"preview": "---\ntitle: Announcing TypeChat 0.1.0\nlayout: blog\ntags: post\ndate: 2024-03-25\nauthors: [\"Daniel Rosenwasser\"]\n---\n\n# {{t"
},
{
"path": "site/src/blog/index.njk",
"chars": 1381,
"preview": "---\ntitle: Blog\n---\n\n<!DOCTYPE html>\n<html lang=\"en\" class=\"h-100\">\n\n<head>\n <meta charset='utf-8'>\n <meta http-eq"
},
{
"path": "site/src/blog/introducing-typechat.md",
"chars": 7391,
"preview": "---\ntitle: Introducing TypeChat\nlayout: blog\ntags: post\ndate: 2023-07-20\nauthors: [\"Anders Hejlsberg\", \"Steve Lucco\", \"D"
},
{
"path": "site/src/css/noscript-styles.css",
"chars": 115,
"preview": ".typechat-hero button {\n display: none !important;\n}\n\n.typechat-docs-smol-nav {\n display: none !important;\n}\n"
},
{
"path": "site/src/css/styles.css",
"chars": 2401,
"preview": ".skip-to-main {\n position: absolute;\n opacity: 0;\n z-index: -999999999;\n margin: 0 auto;\n padding: 2rem 0"
},
{
"path": "site/src/docs/examples.md",
"chars": 5661,
"preview": "---\nlayout: doc-page\ntitle: Examples\n---\n\nTo see TypeChat in action, check out the examples found in [`/typescript/examp"
},
{
"path": "site/src/docs/faq.md",
"chars": 3894,
"preview": "---\nlayout: doc-page\ntitle: Frequently Asked Questions (FAQ)\n---\n\n### What is TypeChat?\n\nTypeChat makes it easy to build"
},
{
"path": "site/src/docs/index.njk",
"chars": 836,
"preview": "---\ntitle: Docs\n---\n\n<!DOCTYPE html>\n<html lang=\"en\" class=\"h-100\">\n<head>\n <meta charset='utf-8'>\n <meta http-equ"
},
{
"path": "site/src/docs/introduction.md",
"chars": 7107,
"preview": "---\nlayout: doc-page\ntitle: Introduction\n---\n\nLarge language models (or LLMs) can give us surprisingly rich answers to r"
},
{
"path": "site/src/docs/python/basic-usage.md",
"chars": 9111,
"preview": "---\nlayout: doc-page\ntitle: Basic Python Usage\n---\n\nTypeChat is currently a small library, so we can get a solid underst"
},
{
"path": "site/src/docs/techniques.md",
"chars": 2435,
"preview": "---\nlayout: doc-page\ntitle: Techniques\n---\n\nThis document defines techniques for working with TypeChat.\n\n### Schema Engi"
},
{
"path": "site/src/docs/typescript/basic-usage.md",
"chars": 11815,
"preview": "---\nlayout: doc-page\ntitle: Basic TypeScript Usage\n---\n\nTypeChat is currently a small library, so we can get a solid und"
},
{
"path": "site/src/index.njk",
"chars": 1388,
"preview": "---\nlayout: base\n---\n\n<main class=\"container d-flex flex-nowrap col-xl-10 col-xxl-8 px-4 py-5 typechat-hero typechat-cap"
},
{
"path": "site/src/js/interactivity.js",
"chars": 1261,
"preview": "// @ts-check\n\n{\n /** @type {any} */\n let lastTimeout;\n\n /** @type {HTMLButtonElement | null} */\n const copyB"
},
{
"path": "typescript/.gitignore",
"chars": 231,
"preview": "build/\ndist/\nout/\nnode_modules/\n.env\n*.map\n*.out.txt\n*.bat\n\n# Copied at publish time from repo root\nSECURITY.md\n\n# Local"
},
{
"path": "typescript/.npmignore",
"chars": 73,
"preview": "src/\nexamples/\nnode_modules/\n*.map\n*.ts\n!*.d.ts\ntsconfig.json\n.gitignore\n"
},
{
"path": "typescript/LICENSE",
"chars": 1141,
"preview": " MIT License\n\n Copyright (c) Microsoft Corporation.\n\n Permission is hereby granted, free of charge, to any pers"
},
{
"path": "typescript/README.md",
"chars": 3791,
"preview": "# TypeChat\n\nTypeChat is a library that makes it easy to build natural language interfaces using types.\n\nBuilding natural"
},
{
"path": "typescript/examples/README.md",
"chars": 5751,
"preview": "\nTo see TypeChat in action, check out the examples found in this directory.\n\nEach example shows how TypeChat handles nat"
},
{
"path": "typescript/examples/calendar/.vscode/launch.json",
"chars": 557,
"preview": "{\n // Use IntelliSense to learn about possible attributes.\n // Hover to view descriptions of existing attributes.\n"
},
{
"path": "typescript/examples/calendar/README.md",
"chars": 955,
"preview": "# Calendar\n\nThe Calendar example shows how you can capture user intent as a sequence of actions, such as adding event to"
},
{
"path": "typescript/examples/calendar/package.json",
"chars": 518,
"preview": "{\n \"name\": \"calendar\",\n \"version\": \"0.0.1\",\n \"private\": true,\n \"description\": \"\",\n \"main\": \"dist/main.js\",\n \"scrip"
},
{
"path": "typescript/examples/calendar/src/calendarActionsSchema.ts",
"chars": 2543,
"preview": "// The following types define the structure of an object of type CalendarActions that represents a list of requested cal"
},
{
"path": "typescript/examples/calendar/src/expectedOutput.txt",
"chars": 5090,
"preview": "I need to get my tires changed from 12:00 to 2:00 pm on Friday March 15, 2024\nValid instance:\n{\n \"actions\": [\n {\n "
},
{
"path": "typescript/examples/calendar/src/input.txt",
"chars": 881,
"preview": "I need to get my tires changed from 12:00 to 2:00 pm on Friday March 15, 2024\nSearch for any meetings with Gavin this we"
},
{
"path": "typescript/examples/calendar/src/main.ts",
"chars": 1541,
"preview": "import assert from \"assert\";\nimport dotenv from \"dotenv\";\nimport findConfig from \"find-config\";\nimport fs from \"fs\";\nimp"
},
{
"path": "typescript/examples/calendar/src/tsconfig.json",
"chars": 373,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es2021\",\n \"lib\": [\"es2021\"],\n \"module\": \"node16\",\n \"types\": [\"node\"],\n "
},
{
"path": "typescript/examples/coffeeShop/.vscode/launch.json",
"chars": 557,
"preview": "{\n // Use IntelliSense to learn about possible attributes.\n // Hover to view descriptions of existing attributes.\n"
},
{
"path": "typescript/examples/coffeeShop/README.md",
"chars": 1200,
"preview": "# Coffee Shop\n\nThe Coffee Shop example shows how to capture user intent as a set of \"nouns\".\nIn this case, the nouns are"
},
{
"path": "typescript/examples/coffeeShop/package.json",
"chars": 520,
"preview": "{\n \"name\": \"coffeeshop\",\n \"version\": \"0.0.1\",\n \"private\": true,\n \"description\": \"\",\n \"main\": \"dist/main.js\",\n \"scr"
},
{
"path": "typescript/examples/coffeeShop/src/coffeeShopSchema.ts",
"chars": 3558,
"preview": "// The following is a schema definition for ordering lattes.\n\nexport interface Cart {\n items: (LineItem | UnknownText"
},
{
"path": "typescript/examples/coffeeShop/src/input.txt",
"chars": 2017,
"preview": "i'd like a latte that's it\ni'll have a dark roast coffee thank you\nget me a coffee please\ncould i please get two mochas "
},
{
"path": "typescript/examples/coffeeShop/src/input2.txt",
"chars": 319,
"preview": "two tall lattes. the first one with no foam. the second one with whole milk.\ntwo tall lattes. the first one with no foam"
},
{
"path": "typescript/examples/coffeeShop/src/main.ts",
"chars": 1544,
"preview": "import assert from \"assert\";\nimport dotenv from \"dotenv\";\nimport findConfig from \"find-config\";\nimport fs from \"fs\";\nimp"
},
{
"path": "typescript/examples/coffeeShop/src/tsconfig.json",
"chars": 373,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es2021\",\n \"lib\": [\"es2021\"],\n \"module\": \"node16\",\n \"types\": [\"node\"],\n "
},
{
"path": "typescript/examples/coffeeShop-zod/README.md",
"chars": 1200,
"preview": "# Coffee Shop\n\nThe Coffee Shop example shows how to capture user intent as a set of \"nouns\".\nIn this case, the nouns are"
},
{
"path": "typescript/examples/coffeeShop-zod/package.json",
"chars": 528,
"preview": "{\n \"name\": \"coffeeshop-zod\",\n \"version\": \"0.0.1\",\n \"private\": true,\n \"description\": \"\",\n \"main\": \"dist/main.js\",\n "
},
{
"path": "typescript/examples/coffeeShop-zod/src/coffeeShopSchema.ts",
"chars": 4801,
"preview": "import { z } from \"zod\";\n\nexport const OptionQuantity = z.union([z.literal('no'), z.literal('light'), z.literal('regular"
},
{
"path": "typescript/examples/coffeeShop-zod/src/input.txt",
"chars": 2017,
"preview": "i'd like a latte that's it\ni'll have a dark roast coffee thank you\nget me a coffee please\ncould i please get two mochas "
},
{
"path": "typescript/examples/coffeeShop-zod/src/input2.txt",
"chars": 319,
"preview": "two tall lattes. the first one with no foam. the second one with whole milk.\ntwo tall lattes. the first one with no foam"
},
{
"path": "typescript/examples/coffeeShop-zod/src/main.ts",
"chars": 1475,
"preview": "import assert from \"assert\";\nimport dotenv from \"dotenv\";\nimport findConfig from \"find-config\";\nimport { createJsonTrans"
},
{
"path": "typescript/examples/coffeeShop-zod/src/tsconfig.json",
"chars": 373,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es2021\",\n \"lib\": [\"es2021\"],\n \"module\": \"node16\",\n \"types\": [\"node\"],\n "
},
{
"path": "typescript/examples/crossword/README.md",
"chars": 803,
"preview": "# Crossword\n\nThe Crossword example shows how to include an image in a multimodal prompt and use the image to answer a us"
},
{
"path": "typescript/examples/crossword/package.json",
"chars": 533,
"preview": "{\n \"name\": \"crossword\",\n \"version\": \"0.0.1\",\n \"private\": true,\n \"description\": \"\",\n \"main\": \"dist/main.js\",\n \"scri"
},
{
"path": "typescript/examples/crossword/src/crosswordSchema.ts",
"chars": 795,
"preview": "// The following is a schema definition for determining the sentiment of a some user input.\n\nexport type GetClueText = {"
},
{
"path": "typescript/examples/crossword/src/input.txt",
"chars": 61,
"preview": "What is the clue for 1 down\nGive me a hint for solving 4 down"
},
{
"path": "typescript/examples/crossword/src/main.ts",
"chars": 1355,
"preview": "import assert from \"assert\";\nimport dotenv from \"dotenv\";\nimport findConfig from \"find-config\";\nimport fs from \"fs\";\nimp"
},
{
"path": "typescript/examples/crossword/src/translator.ts",
"chars": 2560,
"preview": "import {\n TypeChatLanguageModel,\n createJsonTranslator,\n TypeChatJsonTranslator,\n MultimodalPromptContent,\n PromptC"
},
{
"path": "typescript/examples/crossword/src/tsconfig.json",
"chars": 373,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es2021\",\n \"lib\": [\"es2021\"],\n \"module\": \"node16\",\n \"types\": [\"node\"],\n "
},
{
"path": "typescript/examples/healthData/README.md",
"chars": 871,
"preview": "# Health Data Agent\n\nThis example requires GPT-4.\n\nDemonstrates a ***strongly typed*** chat: a natural language interfac"
},
{
"path": "typescript/examples/healthData/package.json",
"chars": 520,
"preview": "{\n \"name\": \"health-data\",\n \"version\": \"0.0.1\",\n \"private\": true,\n \"description\": \"\",\n \"main\": \"dist/main.js\",\n \"sc"
},
{
"path": "typescript/examples/healthData/src/healthDataSchema.ts",
"chars": 1995,
"preview": "// The following is a schema definition for enetring health data.\n\nexport interface HealthDataResponse {\n // ONLY pre"
},
{
"path": "typescript/examples/healthData/src/input.txt",
"chars": 1044,
"preview": "#\n# Conversations with a Health Data Agent\n# For each conversation: \n# You start with the first line \n# Then type the ne"
},
{
"path": "typescript/examples/healthData/src/main.ts",
"chars": 2048,
"preview": "import assert from \"assert\";\nimport dotenv from \"dotenv\";\nimport findConfig from \"find-config\";\nimport fs from \"fs\";\nimp"
},
{
"path": "typescript/examples/healthData/src/translator.ts",
"chars": 2537,
"preview": "import {Result, TypeChatLanguageModel, createJsonTranslator, TypeChatJsonTranslator} from \"typechat\";\nimport { createTyp"
},
{
"path": "typescript/examples/healthData/src/tsconfig.json",
"chars": 373,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es2021\",\n \"lib\": [\"es2021\"],\n \"module\": \"node16\",\n \"types\": [\"node\"],\n "
},
{
"path": "typescript/examples/math/.vscode/launch.json",
"chars": 557,
"preview": "{\n // Use IntelliSense to learn about possible attributes.\n // Hover to view descriptions of existing attributes.\n"
},
{
"path": "typescript/examples/math/README.md",
"chars": 1027,
"preview": "# Math\n\nThe Math example shows how to use TypeChat for program generation based on an API schema with the `evaluateJsonP"
},
{
"path": "typescript/examples/math/package.json",
"chars": 514,
"preview": "{\n \"name\": \"math\",\n \"version\": \"0.0.1\",\n \"private\": true,\n \"description\": \"\",\n \"main\": \"dist/main.js\",\n \"scripts\":"
},
{
"path": "typescript/examples/math/src/input.txt",
"chars": 121,
"preview": "1 + 2\r\n1 + 2 * 3\r\n2 * 3 + 4 * 5\r\n2 3 * 4 5 * +\r\nmultiply two by three, then multiply four by five, then sum the results\r"
},
{
"path": "typescript/examples/math/src/main.ts",
"chars": 1822,
"preview": "import assert from \"assert\";\nimport dotenv from \"dotenv\";\nimport findConfig from \"find-config\";\nimport fs from \"fs\";\nimp"
},
{
"path": "typescript/examples/math/src/mathSchema.ts",
"chars": 513,
"preview": "// This is a schema for writing programs that evaluate expressions.\n\nexport type API = {\n // Add two numbers\n add("
},
{
"path": "typescript/examples/math/src/tsconfig.json",
"chars": 374,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es2021\",\n \"lib\": [\"es2021\"],\n \"module\": \"node16\",\n \"types\": [\"node\"],\n "
},
{
"path": "typescript/examples/multiSchema/README.md",
"chars": 480,
"preview": "# MultiSchema\n\nThis application demonstrates a simple way to write a **super-app** that automatically routes user reques"
},
{
"path": "typescript/examples/multiSchema/package.json",
"chars": 566,
"preview": "{\n \"name\": \"multi-schema\",\n \"version\": \"0.0.1\",\n \"private\": true,\n \"description\": \"\",\n \"main\": \"dist/main.js\",\n \"s"
},
{
"path": "typescript/examples/multiSchema/src/agent.ts",
"chars": 3697,
"preview": "// TypeScript file for TypeChat agents.\nimport { Result, TypeChatJsonTranslator, TypeChatLanguageModel, createJsonTransl"
},
{
"path": "typescript/examples/multiSchema/src/classificationSchema.ts",
"chars": 261,
"preview": "\nexport interface TaskClassification {\n name: string;\n description: string;\n}\n\n/**\n * Represents the response of a"
},
{
"path": "typescript/examples/multiSchema/src/input.txt",
"chars": 461,
"preview": "I'd like two large, one with pepperoni and the other with extra sauce. The pepperoni gets basil and the extra sauce get"
},
{
"path": "typescript/examples/multiSchema/src/main.ts",
"chars": 2491,
"preview": "import assert from \"assert\";\nimport dotenv from \"dotenv\";\nimport findConfig from \"find-config\";\nimport fs from \"fs\";\nimp"
},
{
"path": "typescript/examples/multiSchema/src/router.ts",
"chars": 3048,
"preview": "import { Result, TypeChatJsonTranslator, TypeChatLanguageModel, createJsonTranslator } from \"typechat\";\nimport { createT"
},
{
"path": "typescript/examples/multiSchema/src/tsconfig.json",
"chars": 429,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es2021\",\n \"lib\": [\"es2021\"],\n \"module\": \"node16\",\n \"types\": [\"node\"],\n "
},
{
"path": "typescript/examples/music/.vscode/launch.json",
"chars": 557,
"preview": "{\n // Use IntelliSense to learn about possible attributes.\n // Hover to view descriptions of existing attributes.\n"
},
{
"path": "typescript/examples/music/README.md",
"chars": 2110,
"preview": "# Music\r\n\r\nThe Music example shows how to capture user intent as actions in JSON which corresponds to a simple dataflow "
},
{
"path": "typescript/examples/music/migrations.md",
"chars": 747,
"preview": "# Local Music DB Migrations\n\nTracks table\n```[SQL]\nCREATE TABLE tracks (\n id INTEGER PRIMARY KEY,\n title TEXT NOT "
},
{
"path": "typescript/examples/music/package.json",
"chars": 766,
"preview": "{\n \"name\": \"music\",\n \"version\": \"0.0.1\",\n \"private\": true,\n \"description\": \"\",\n \"main\": \"dist/main.js\",\n \"scripts\""
}
]
// ... and 46 more files (download for full content)
About this extraction
This page contains the full source code of the microsoft/TypeChat GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 246 files (509.3 KB), approximately 127.9k tokens, and a symbol index with 577 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.