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 expereince 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, and outside of the minimal needs we have that this was created for you should go use them.

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 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_py import WAILGenerator

# Create a validator with your WAIL schema
generator = WAILGenerator(r'''
    # Define your schema here
    object Response {
        name: String,
        age: Number,
        interests: Array<String>
    }

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

    # Define your main block which can handle variable assignments
    main {
        let template = Response(desc: "A really responsy response")

        prompt {
            We're showing off examples and example of of a prompt generated by WAIL looks like this:

            {{template}}
        }
    }
''')

# Get the prompt to send to the LLM
(prompt, warnings, errors) = generator.get_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 = """
<result>
{
    name: "Alice",
    "age": 25,
    "interests": [coding, 'AI', "music"]
}
</result>
"""

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

# 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 = """
    <result>
    {
        'name': 'Alice',
        'age': 25,
        'interests': [
            "coding",
            'AI',
            hiking,
        ]
    }
    </result>
    """

    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

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.8.1-cp312-cp312-musllinux_1_2_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.12musllinux: musl 1.2+ x86-64

gasp_py-0.8.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

gasp_py-0.8.1-cp312-cp312-macosx_11_0_arm64.whl (1.1 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

gasp_py-0.8.1-cp311-cp311-musllinux_1_2_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.11musllinux: musl 1.2+ x86-64

gasp_py-0.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

gasp_py-0.8.1-cp311-cp311-macosx_11_0_arm64.whl (1.1 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

gasp_py-0.8.1-cp310-cp310-musllinux_1_2_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.10musllinux: musl 1.2+ x86-64

gasp_py-0.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

gasp_py-0.8.1-cp310-cp310-macosx_10_12_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.10macOS 10.12+ x86-64

gasp_py-0.8.1-cp39-cp39-musllinux_1_2_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.9musllinux: musl 1.2+ x86-64

gasp_py-0.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ x86-64

gasp_py-0.8.1-cp39-cp39-macosx_10_12_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.9macOS 10.12+ x86-64

gasp_py-0.8.1-cp38-cp38-musllinux_1_2_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.8musllinux: musl 1.2+ x86-64

gasp_py-0.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ x86-64

gasp_py-0.8.1-cp38-cp38-macosx_10_12_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.8macOS 10.12+ x86-64

File details

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

File metadata

File hashes

Hashes for gasp_py-0.8.1-cp312-cp312-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 50d2697683e0d7726cca0ecf9386dfd94d4314368299285ca03aec2038883dbc
MD5 9ab113744062f76426ab7b3fee4b0e85
BLAKE2b-256 38dbfcc6983bc2a2a31d163af629951087aa9ad19f9c0687a5c7e499dbd1c7ad

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.8.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 b5c4b1d336eef3a7dfe3eb8598e95234b15a45b00ae3fdde7698cc731b407592
MD5 40cb58352377fb87ded9764c823c0e93
BLAKE2b-256 fb27d57a9943e12e666b51a6037ada2c4d0896212b7154ed0ebf913423916e2b

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.8.1-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 4691ebaf563b00750e7fd2791ae87353547f347a8e8957f8500e7ddd120f1aa6
MD5 cbc91cb93b33ceab8cd0198b6287ea56
BLAKE2b-256 64629cbb99f7c12e35df09b196794a3a3e1c801351dbf80a2bbb53d9cfc2df29

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.8.1-cp311-cp311-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 6ad8671817aa9b5966c5f6a71dba0d967110f2b86d9eb3b089fbf25ae898d384
MD5 86869aeb0f0b5a5ffac7c146e3b0df94
BLAKE2b-256 51368a56cd2023734a738d1b4f771e5f66a01deea04471de1028d1a2a673a309

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 4ab61552ff36f40b0ce56734b1ea69eb1c1d2fcfd42819fee2af01d5dd5344bd
MD5 e2b26b8f034faf665aba5512e337b999
BLAKE2b-256 bbcfb2eb4ce95105e9752aff54c4c162d75bfe7014be6ae0ad6ac39ffd1aa64d

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.8.1-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 ab304abd66d5267c2d3b1687e834b1544550220f11a0a91204789f037b5babf8
MD5 4e7f03d487709ae0824df7647133c407
BLAKE2b-256 701ee85f2f16ae69f7bfa9195c387178acb547698a8332be6a54d52bcd80ac56

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.8.1-cp310-cp310-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 572fc95e9fcb1ddb02fe2f15cd61bcf659df5ce33bb12509e1011b3431e35330
MD5 ba90f16d1b0ed4d92c2fbacafc39ed00
BLAKE2b-256 54d72e25b1947c5674fa483bc5fb5ce90da04bedd3536aac7256f2c66938970f

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 11cbe6372ff4a6e61e5de675a5802294b33161d5afc623ecbd7527b0a286f3a8
MD5 838629fb9e2e27b297f31d90de3d8950
BLAKE2b-256 fe801f1ad5148a2c7958ae5986748ca7c95e2cbe9f03757b061e43a1efc2c796

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.8.1-cp310-cp310-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 fd64c72d4968f4ce2e23811fb1c2fd58aaea45631138ad10b7aa40e725108e2c
MD5 a14d42cebc041f291f1b484c11f0d6f8
BLAKE2b-256 5844d9d325f59e67fb361aa9e44479b5d84910b286f16c075e8fc89a77fe5cd0

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.8.1-cp39-cp39-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 c2ef814de4f49591e22965c564605120f52b2925592f08c6eb22c5867eb9cb00
MD5 61eda8ef8ac8d21a6e579b0ff241538e
BLAKE2b-256 044300328247d5e6cb76a85974d92a0f0d8174f142879348f00aef6fa1681263

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 b11f52cb049c44a415b856ebf742b6948bb5004b7d23968ec25ab2606c671e43
MD5 01cda7673ee9ca1b16b50b3a29436209
BLAKE2b-256 fbfbc4fcb92f562b1623856ae0d198f78f5554e23ad4bab2b1d3f19d429b1ad9

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.8.1-cp39-cp39-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 3d5b6cb3a74b2194ed53bb1f171e9054e49c5076826433778c287bde23084153
MD5 f116b2d950932f2f4bc957a82dc5642e
BLAKE2b-256 751b40fcba43f053b9c0e4c9c973c0e44f2d3a36b5efc9488d8528e3c6898e01

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.8.1-cp38-cp38-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 c93a58f52f5761d173a879d194a9232ffc76ac11d3f1f7d0494fb1623b955b96
MD5 8c54b6efb61c43e81a29e75621345762
BLAKE2b-256 ca0c40c9052729087088fb1afdd081c4084e8a121517d0f81283af6c2ba96e50

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 7c598dccaad1070c4ec78c5f257248a699aa4c03fd0ca497532ba034d0f617b0
MD5 3d9399e863492dd8c2a0cf61a7da78ab
BLAKE2b-256 bac6aa366bbae85adbb4923e37b6341ecc24abdfe6136c3ff381c5735fcce9d9

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.8.1-cp38-cp38-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 c2ce44cfe797d92d7a882c0dcf517b1db2a7aa8975961bc30aa539813b13c6a0
MD5 68d0fd7a794679e0887e55aac17affed
BLAKE2b-256 80f07443e84653e49971a67aab90f68286beb1792fd7b15c479586ffc733ac27

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