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

  • Non existant syntax error messages - I will get around to this soon but right now this is just as likely to segfault or die in rust with a cryptic error message as it is to tell you what you're doing is wrong.

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()

# Parse and validate JSON responses
generator.parse_llm_output("""
{
    name: "Alice",
    "age": 25,
    "interests": [coding, 'AI', "music"]
}
""")

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

    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 Distribution

gasp_py-0.3.1.tar.gz (45.3 kB view details)

Uploaded Source

Built Distributions

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

gasp_py-0.3.1-cp312-cp312-macosx_11_0_arm64.whl (855.9 kB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

gasp_py-0.3.1-cp311-cp311-musllinux_1_2_x86_64.whl (1.2 MB view details)

Uploaded CPython 3.11musllinux: musl 1.2+ x86-64

gasp_py-0.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.2 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

gasp_py-0.3.1-cp311-cp311-macosx_11_0_arm64.whl (972.6 kB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

gasp_py-0.3.1-cp310-cp310-musllinux_1_2_x86_64.whl (1.2 MB view details)

Uploaded CPython 3.10musllinux: musl 1.2+ x86-64

gasp_py-0.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.2 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

gasp_py-0.3.1-cp310-cp310-macosx_10_12_x86_64.whl (1.0 MB view details)

Uploaded CPython 3.10macOS 10.12+ x86-64

File details

Details for the file gasp_py-0.3.1.tar.gz.

File metadata

  • Download URL: gasp_py-0.3.1.tar.gz
  • Upload date:
  • Size: 45.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: maturin/1.8.0

File hashes

Hashes for gasp_py-0.3.1.tar.gz
Algorithm Hash digest
SHA256 ca847a1fe2e814d5c060aac17a3b73a806ff394a4be7630e1ba543d3bb16f710
MD5 fa3e9540eef06d8f68015fb4f0816804
BLAKE2b-256 061b1b2d22e2ade1a9faba50ae198e38565ba91bf4ab9250e208ad173d002e30

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.3.1-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 0f38347403d7ecc5fb14c5025875a900a67d55e3ad737871bbf5c7428a12deea
MD5 75bcc723c694ffc03c6bc39af6e343d1
BLAKE2b-256 1e7534d9d5a31cb18fc5c16885ba0e557b57e17d48395474bfc119b6bd2ef68c

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.3.1-cp311-cp311-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 a01c17ffecd9b196420a431ac5672204d0a3573f912ae338eb6e44a6d9bf8ab2
MD5 8f5ed285a1d614f77a8053ee3320b27f
BLAKE2b-256 89288298fa1232e28d2c04fecc4f0bdc2fe4bc3c9a373f08ddd31d97a002da8b

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 4444954a0acaa267106493d8cab18d48799b5759399bceb79299f7ff13aeb543
MD5 7a0429945a0f8938b42c0bc241356738
BLAKE2b-256 c3966f80b52717d64fe9c7bf60d734c5869b45db5924ae58fdb3cd6a1821d1c2

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.3.1-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 6afd15a3bfeee2aae3ece11787d4648e507f2f6150f73cb3561cb55af6ec9afb
MD5 f82ad5606dc59b90cc929a8310db6e43
BLAKE2b-256 58377da992a59c43e78adb4885076eae3a19fed84107222dc44c35c809a15e0d

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.3.1-cp310-cp310-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 ad5422dac0d35d8d789e6bab1dfff40b799d6dc9942520a61d4f6b46c9fd749d
MD5 e79c61ea616b30a4c9fa87ce230ee595
BLAKE2b-256 cf97f6bc63ba3e3ac751c7ac04325bf3585e4e42196a64e221e31796ac1d3f4f

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 40d74551cfe745a59a58aef45991363f4708314985baa32c89563c34e5ed02fc
MD5 498c9fa64e5d9c3e46957477600c9010
BLAKE2b-256 55adbe9a0f4d49fd54e4c276ab0b5466b7b8fd0d8fd04549a578a7ed7bf214e3

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gasp_py-0.3.1-cp310-cp310-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 4f25cdd9779520cdf15d0ab2162fa3258a4eb94a67c2e1c2fbf6fabfbc48c82d
MD5 f27a36a26c00c08d0642f37bc433cc67
BLAKE2b-256 f3e48410b283917ad4d1925d66d192e99bf90c03c518e90faf4cb05914f0f907

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