Skip to main content

GASP (Gee Another Schema Parser) - A validator for WAIL (Widely Applicable Interface Language) schemas and JSON

Project description

GASP (Gee Another Schema Parser)

GASP is a high-performance Rust-based parser and validator for WAIL (Widely Applicable Interface Language) schemas and JSON responses. It's specifically designed to work with Large Language Models (LLMs) by providing robust error recovery for common LLM response quirks.

What is WAIL?

WAIL (Widely Applicable Interface Language) is a schema language designed for:

  1. Generating type-validated LLM prompts
  2. Validating JSON responses from LLMs
  3. Providing clear error messages for schema violations

Why

In our experience the ergonomics around tool calling kind of suck right now and in the lowest common denominator settings are down right painful.

If you're using OpenRouter (which is great) they choose not to support some platform specific features (understandable) like the "ANY" parameter from Anthropic so you wind up with super verbose output, the occasional no tool call, missing params even when specified in "required" and so we decided to implement this prompt creator and schema validator because what are tool calls other than type interfaces.

Honestly, BAML is a really sick tool and more feature complete than this, with like people actually paid to work on it. However, they require you to use their code gen'd inference clients for sending messages to the LLM. That let's them do some really powerful things like validation mid streaming, but you have to be all in on them.

GASP and WAIL let you separate out prompt creation, inference and prompt validation from one another so you can apply GASP to whatever client floats your boat with the trade off that we aren't intending to make this work for every streaming format under the sun (at least I'm not, feel free to contribute!) so it's only applicable to fully generated outputs.

I didn't need all that especially because I along with my friend and co-founder have written Asimov a framework for building Agents that includes all of our own inference machinery I'm not looking to give up.

Anyway both Asimov and now GASP/WAIL are built for supporting Bismuth a programming agent that can help businesses find and patch bugs on your Github PRs before you ever know about them.

Features

  • Robust Error Recovery: Handles common LLM response issues like trailing commas, unquoted identifiers, and malformed JSON
  • Type Coercion: Attempt to fix type mismatches like Number -> String, single items to arrays, object types if a unique set of fields can be matched to a schema.
  • Type Validation: Strong type checking for both schema definitions and JSON responses
  • High Performance: Written in Rust with Python bindings for optimal speed
  • Developer Friendly: Clear error messages (except for syntax errors see below) and intuitive schema syntax
  • LLM-Optimized: Specifically designed to work with the quirks and inconsistencies of LLM outputs

Anti-Features

  • Limited syntax error messages - Right now syntax errors will tell you where the parser failed but messages aren't more helpful than that so sometimes it's hard to figure out what is wrong in the parser syntax.

Caveats

  • Output parsing is assumed to be sequential - That is we assume output happens in the same order as the template variable binding so like if binding1 doesn't correspond to output1 things will be wacky.

Installation

pip install gasp-py

Usage

from gasp import WAILGenerator

# Create a validator with your WAIL schema
generator = WAILGenerator()

schema = r'''
object Response {
    name: String
    age: Number
    interests: String[]
}

template GenerateResponse(desc: String) -> Response {
    prompt: """
    Based on {{desc}} generate a response that returns 
    
    {{return_type}}      
    """
}

main {
    template_args {
        desc: String
    }

    let res = GenerateResponse(desc: $desc);

    prompt {
        {{res}}
    }
}
'''

generator.load_wail(schema)

# Get the prompt to send to the LLM
(prompt, warnings, errors) = generator.get_prompt(desc="A totally good response")

print(prompt)

# Use your favorite LLM client to send the prompt and get a response
# response = your_fav_client.generate(prompt) # or whatever your interface is
examples_res = """
<action>
{
    name: "Alice",
    "age": 25,
    "interests": [coding, 'AI', "music"]
}
</action>
"""

# Parse and validate JSON responses
generator.parse_llm_output(examples_res)

# Note the ability to handle malformed JSON

Error Recovery

GASP includes built-in error recovery for common LLM response issues:

  • Trailing commas in arrays and objects
  • Unquoted identifiers in object keys
  • Missing quotes around strings
  • Inconsistent whitespace and formatting

License

Apache License, Version 2.0 - see LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Complete Example

Here's a complete example showing how to use GASP in Python:

from gasp import WAILGenerator
import json

def create_person_prompt():
    """Define the WAIL schema for person generation."""
    return r'''
    # Define our Person type
    object Person {
        name: String
        age: Number
        interests: String[]
    }

    # Define a template for generating a person from a description
    template GetPersonFromDescription(description: String) -> Person {
        prompt: """
        Given this description of a person: "{{description}}"

        Create a Person object with their name, age, and interests.
        Return in this format: 
        {{return_type}}
        """
    }

    main {
        # This is a comment
        let person_prompt = GetPersonFromDescription(
            description: "Alice is a 25-year-old software engineer who loves coding, AI, and hiking."
        );

        # This is the prompt we'll send to the LLM
        prompt {
            This is an example of a prompt generated by WAIL:
            {{person_prompt}}
        }
    }
    '''

def main():
    # Initialize our validator with the schema
    generator = WAILGenerator()
    generator.load_wail(create_person_prompt())

    warnings, errors = generator.validate_wail()

    print("Validation Results:")
    print("\nWarnings:")
    print(warnings)
    print("\nErrors:")
    print(errors)

    # Get the generated prompt - this is what you'd send to your LLM
    (prompt, warnings, errors) = generator.get_prompt()
    print("Generated Prompt:")
    print(prompt)
    print("Warnings:")
    print(warnings)
    print("Errors:")
    print(errors)

    # In a real application, you would send this prompt to your LLM
    # Here we'll simulate an LLM response with some typical quirks
    llm_response = """
    <action>
    {
        'name': 'Alice',
        'age': 25,
        'interests': [
            "coding",
            'AI',
            hiking,
        ]
    }
    </action>
    """

    try:
        # Validate the LLM's response and get the parsed JSON as a Python dict
        result = generator.parse_llm_output(llm_response)
        print("✓ Response validation successful!")

        result = result["person_prompt"]
        

        # Work with the validated data
        print("\nParsed Person:")
        print(f"Name: {result['name']}")
        print(f"Age: {result['age']}")
        print(f"Interests: {', '.join(result['interests'])}")
        
        # # You can also convert it to standard JSON
        # print("\nAs standard JSON:")
        # print(json.dumps(result, indent=2))
        
    except Exception as e:
        print(f"❌ Validation error: {e}")

if __name__ == "__main__":
    main() 

This example demonstrates:

  1. Creating a WAIL schema with proper Python string formatting
  2. Defining object types and templates in WAIL
  3. Generating a type-aware prompt for your LLM
  4. Handling common LLM response quirks automatically
  5. Validating and parsing the response
  6. Working with the validated data in Python

When run, this script will output:

Validation Results:
Warnings:
[]
Errors:
[]
Generated Prompt:

This is an example of a prompt generated by WAIL:

Given this description of a person: "Alice is a 25-year-old software engineer who loves coding, AI, and hiking."

Create a Person object with their name, age, and interests.
Return in this format: 

{
  name: string
  age: number
  interests: String[]>
}


Warnings:
[]
Errors:
[]
✓ Response validation successful!

Parsed Person:
Name: Alice
Age: 25
Interests: coding, AI, hiking

Changelog

0.9.0 - Complete parser rewrite to an incremental non recursive parser. All original tests pass but parsing semantics may be slightly different. Considering this a breaking change because there are likely parser edge cases I can't account for.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

gasp_py-0.10.1-cp312-cp312-musllinux_1_2_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.12musllinux: musl 1.2+ x86-64

gasp_py-0.10.1-cp312-cp312-musllinux_1_2_aarch64.whl (1.0 MB view details)

Uploaded CPython 3.12musllinux: musl 1.2+ ARM64

gasp_py-0.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

gasp_py-0.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (980.4 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ ARM64

gasp_py-0.10.1-cp312-cp312-macosx_11_0_arm64.whl (921.5 kB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

gasp_py-0.10.1-cp311-cp311-musllinux_1_2_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.11musllinux: musl 1.2+ x86-64

gasp_py-0.10.1-cp311-cp311-musllinux_1_2_aarch64.whl (1.0 MB view details)

Uploaded CPython 3.11musllinux: musl 1.2+ ARM64

gasp_py-0.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

gasp_py-0.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (980.8 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ ARM64

gasp_py-0.10.1-cp311-cp311-macosx_11_0_arm64.whl (921.2 kB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

gasp_py-0.10.1-cp310-cp310-musllinux_1_2_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.10musllinux: musl 1.2+ x86-64

gasp_py-0.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

gasp_py-0.10.1-cp310-cp310-macosx_10_12_x86_64.whl (984.1 kB view details)

Uploaded CPython 3.10macOS 10.12+ x86-64

gasp_py-0.10.1-cp39-cp39-musllinux_1_2_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.9musllinux: musl 1.2+ x86-64

gasp_py-0.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ x86-64

gasp_py-0.10.1-cp39-cp39-macosx_10_12_x86_64.whl (983.5 kB view details)

Uploaded CPython 3.9macOS 10.12+ x86-64

gasp_py-0.10.1-cp38-cp38-musllinux_1_2_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.8musllinux: musl 1.2+ x86-64

gasp_py-0.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ x86-64

gasp_py-0.10.1-cp38-cp38-macosx_10_12_x86_64.whl (984.0 kB view details)

Uploaded CPython 3.8macOS 10.12+ x86-64

File details

Details for the file gasp_py-0.10.1-cp312-cp312-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for gasp_py-0.10.1-cp312-cp312-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 f9d7c71e20748a24e2e99f059548359f45491df56b55232d578c33fca2293731
MD5 64a156e081351aa437d7f80dccda0a3e
BLAKE2b-256 c56289cfa498c407aba2a49a566518748a148e80ead0a8a200df3ce8b874f105

See more details on using hashes here.

File details

Details for the file gasp_py-0.10.1-cp312-cp312-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for gasp_py-0.10.1-cp312-cp312-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 0e3807601c6e920ca11857bcc2b1023459720954a8bfe4e563fc452782ad8a66
MD5 7a6fb70310c1053f986e77d9a9cd6b2c
BLAKE2b-256 fb92e68b27740cfe2b0813c84b5ff21b18d7f865decbd54fa1decea8e968b335

See more details on using hashes here.

File details

Details for the file gasp_py-0.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for gasp_py-0.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 ea38d1d889b5cbfdcfc46cef63f6378c72cd928b5704d45cf9d866c10fd821b3
MD5 5a189a94eca425bae9fa9906f2e63a32
BLAKE2b-256 5d3cabdf73534cef97a02a403d360adbfa075f0c6f16668ab271f43584a715f4

See more details on using hashes here.

File details

Details for the file gasp_py-0.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for gasp_py-0.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 443c4948e137f716f9277ab2b8426c9a18805534f8a102509b4ff04c877124ec
MD5 74bd9418c6bc0d0f7cf0d2fbb769984a
BLAKE2b-256 9bc6f2794ef08fe03b07b7ff8392c47ff52e5c55d8effc7c84bae0d0a1bc2305

See more details on using hashes here.

File details

Details for the file gasp_py-0.10.1-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for gasp_py-0.10.1-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 6f7350cf4af432c7d32302b400d1b6159c6a2a679fb23fdca8207273875cdfeb
MD5 215052ba4d7e845b65e2fd0f283aa89e
BLAKE2b-256 3f95a462f295cd8387bf6fbe2f7ff52365ac77171022b1d299f43a7702e3c84d

See more details on using hashes here.

File details

Details for the file gasp_py-0.10.1-cp311-cp311-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for gasp_py-0.10.1-cp311-cp311-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 3bd2ffb7babe2062154a04dcb0e6d508044f2c8ce9c120d2dafd24e2e46de0f3
MD5 5c31a5422cb5f4e3730c01aaccb28db4
BLAKE2b-256 9697fefc63188cf2e59b3d22568aa7a743a2bd4f1288f22114e67ee3c5775d58

See more details on using hashes here.

File details

Details for the file gasp_py-0.10.1-cp311-cp311-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for gasp_py-0.10.1-cp311-cp311-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 2ab2cef61605917a590d77792826bdfa84fd15e9173c332c556a2cae8ab87563
MD5 ed0c3a28a5d21b13b53cb36263fca42b
BLAKE2b-256 10589d64e9ca99f4137562951440ef30815f38cc20a12e4afa4c4e6b4a3e3ea1

See more details on using hashes here.

File details

Details for the file gasp_py-0.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for gasp_py-0.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 7f44e02c6acbbcb08d4dfa1c4c8bf51baf6a3aec691e7fdd45526f6422d9433b
MD5 1d7b2369c855380ae20319238980a9ca
BLAKE2b-256 30783347d7e83bb214d30d3cb8b355677bbdafe8e655cad5925778bfdf8f1764

See more details on using hashes here.

File details

Details for the file gasp_py-0.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for gasp_py-0.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 79167ea07d07ad9f3fbdb2efc198c602a9324a6e75f5459570e6b3ce6486ee93
MD5 3216c09ca9df4896afd101dd8767925c
BLAKE2b-256 e6f035c6b1cfe2eabc0c1fc1670cfdd59e279609bfbf62544bea06e3b9938187

See more details on using hashes here.

File details

Details for the file gasp_py-0.10.1-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for gasp_py-0.10.1-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 2c0d1d2e8339a91f9f514568966532734342025c27fdfad83dfc13052a194d18
MD5 4b6ab1c6b5f18af2503dd27f1575602b
BLAKE2b-256 980e204b300896a52102850233e82d53823e5608c89bcd282c2576dc0127aaf5

See more details on using hashes here.

File details

Details for the file gasp_py-0.10.1-cp310-cp310-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for gasp_py-0.10.1-cp310-cp310-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 5bac740fdc33827f2418171e3cd04f3d3070b382638acbcfc85529e7ab91a46a
MD5 d9bebbc570e9261d330416f2076e0e2b
BLAKE2b-256 2040d899658055b8ae9d4de3c4930a8b36c34e60b57405f69483e0b177114404

See more details on using hashes here.

File details

Details for the file gasp_py-0.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for gasp_py-0.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 4e4c56ea10f1e2e0cee209169590b4ba59056d74a374fd61e735e34f1ff6a9e0
MD5 38015cc245b26bf0816870096a5dc335
BLAKE2b-256 6fd2b58840cd45043edf66971ea0c1e2c2ae9de8b84d366102803beb9ccdd03f

See more details on using hashes here.

File details

Details for the file gasp_py-0.10.1-cp310-cp310-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for gasp_py-0.10.1-cp310-cp310-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 30341a7c0c517d0c2a422dcfb09be223f22e56e5a6e8c2bb19ca548d917161b1
MD5 a4752da5ca10c3d495a64a4ddf59dd73
BLAKE2b-256 9046abc963ba6af11f6b990f4a5c93c7addfd60806cf3066563920c6f1c9f906

See more details on using hashes here.

File details

Details for the file gasp_py-0.10.1-cp39-cp39-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for gasp_py-0.10.1-cp39-cp39-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 dc09547798c36e90bf54cd5f8306849251c1757e83031ce12367501144eb1715
MD5 693860275f0713af088f1f26080f412a
BLAKE2b-256 f61066caec5d2650a2bc655d92ea0e02829027959583e887929b786a877904d5

See more details on using hashes here.

File details

Details for the file gasp_py-0.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for gasp_py-0.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 3801354e4e23453a2a5558e29b9629e9b75e708e7044bbf17d00c3d69a015247
MD5 9e2f2babeb499da056cbf65e6e8a2e53
BLAKE2b-256 c57c86130d364fb12e32ccf6d81a873fb5a9343214e3fda4ac15cd3bca071ab7

See more details on using hashes here.

File details

Details for the file gasp_py-0.10.1-cp39-cp39-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for gasp_py-0.10.1-cp39-cp39-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 4fd7c6cb7b77b5d5be6a4bcff76611a305a8835c668609adabb31a558c1b9944
MD5 b0aa00da2add8c92a3877692b56e5a1a
BLAKE2b-256 957edc358a22fd8a25e9ab3f49ba13bda7180e48789332cdb80b68907e24a994

See more details on using hashes here.

File details

Details for the file gasp_py-0.10.1-cp38-cp38-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for gasp_py-0.10.1-cp38-cp38-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 339edd700d9accefffef41bc4df103a1e081ac495ddf6fda769cf19dc29032a6
MD5 589a114ba81ac0843517de38e9eb6422
BLAKE2b-256 50c597a2eef8008be55e03a707bac6e5fd2984d846d0c6c2fe893916bb94d5a1

See more details on using hashes here.

File details

Details for the file gasp_py-0.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for gasp_py-0.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 2dd39e730c457bef89be9b956bc3b93be548e74cffa930176a6f1182dc3049d2
MD5 d68a0eeaa7ff823653b36ab8ab115f6b
BLAKE2b-256 53a1fbe2f5b6eafe856b863a8cf173662c06acd13a2d241dd3a6bf572fe74724

See more details on using hashes here.

File details

Details for the file gasp_py-0.10.1-cp38-cp38-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for gasp_py-0.10.1-cp38-cp38-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 e1a76574f1e0e29a79dc52aa299b470ea1536fd3baba3c48596c8b5ad89b6578
MD5 73105e1150ac7968c89ca515c4c34013
BLAKE2b-256 57d121fb83d1b4b988826c7565044778b9fed4f824ef6998479494e648c20012

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page