Skip to main content

Detect hallucinated API calls in LLM-generated Python code

Project description

ghostcall

Detect hallucinated API calls in LLM-generated Python code.

CI PyPI License: MIT

LLMs invent methods that don't exist. ghostcall parses Python code, looks at the packages you actually have installed, and tells you which calls are real and which are phantoms.

demo

Install

pip install ghostcall

Requires Python 3.9+.

Quickstart

# Check a file
ghostcall path/to/llm_output.py

# Pipe from stdin (your favorite use case for ChatGPT output)
pbpaste | ghostcall

# Extract Python from a markdown file (e.g., LLM chat exports)
ghostcall --md chat.md

Why this tool?

Every developer who has used Copilot or ChatGPT has pasted code with a method that doesn't exist and lost 20 minutes debugging. Linters check your code — nothing checks generated code against the packages you actually have installed.

ghostcall fills that gap. Pipe in code, get back a list of phantom calls and suggestions.

It's not mypy or pyright: those check types and need a full project context. ghostcall checks existence and works on any snippet, in isolation.

Examples

Catch hallucinated method names

$ echo 'import pandas as pd
pd.DataFrame.to_jsonl()' | ghostcall

   pd.DataFrame.to_jsonl() does not exist
    line 2    Did you mean to_json, to_sql?

✗ Found 1 hallucinated call

Catch hallucinated functions on real modules

$ echo 'import requests
requests.get_async("https://api.example.com")' | ghostcall

   requests.get_async() does not exist
    line 2 Found 1 hallucinated call

Check Python blocks inside markdown

If you save your ChatGPT conversation as .md, just point ghostcall at it:

$ ghostcall --md chatgpt_export.md

Only fenced ```python code blocks are checked. Other languages and prose are ignored.

Machine-readable output for CI

$ ghostcall --json output.py
{
  "source": "output.py",
  "summary": {
    "total_calls_checked": 5,
    "hallucinations_found": 1,
    "module_missing": 0,
    "dynamic_skipped": 0,
    "ok": 4
  },
  "findings": [
    {
      "type": "hallucinated",
      "line": 2,
      "col": 0,
      "call": "pd.DataFrame.to_jsonl",
      "resolved": "pandas.DataFrame.to_jsonl",
      "missing_attr": "to_jsonl",
      "parent": "pandas.DataFrame",
      "suggestions": ["to_json", "to_sql"]
    }
  ]
}

Exit codes: 0 clean, 1 hallucinations found, 2 syntax error in input.

How it works

  1. Parse the Python source with the standard ast module.
  2. Build an alias map from imports (import pandas as pdpd → pandas).
  3. For each dotted call chain (pd.DataFrame.to_jsonl), resolve it through the alias map and walk it through the actually installed package via importlib + getattr.
  4. If an attribute is missing, suggest close matches via difflib.

Because it checks against your real environment, the answers reflect the exact version of the package you have.

Limitations

What ghostcall does NOT do
  • No type checking — that's mypy / pyright. ghostcall only checks existence.
  • No data-flow analysisdf = pd.DataFrame(); df.fake_method() is not caught because df is a local variable. Direct chains from imports only.
  • No support for import * — wildcard imports are skipped with a warning.
  • No support for non-Python languages.
  • No auto-fix.
  • Modules with __getattr__ magic (e.g., some ORMs) are skipped to avoid false positives.

Contributing

Issues and PRs welcome. The codebase is small (~330 lines) and built on stdlib (ast, importlib, difflib). The four files that matter:

  • src/ghostcall/parser.py — AST visitor, import resolution
  • src/ghostcall/checker.py — introspection against installed packages
  • src/ghostcall/suggest.py — fuzzy matching
  • src/ghostcall/output.py — terminal and JSON rendering

Run tests with:

pip install -e ".[dev]"
pytest

License

MIT — see LICENSE.

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

ghostcall-0.1.0.tar.gz (113.2 kB view details)

Uploaded Source

Built Distribution

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

ghostcall-0.1.0-py3-none-any.whl (9.8 kB view details)

Uploaded Python 3

File details

Details for the file ghostcall-0.1.0.tar.gz.

File metadata

  • Download URL: ghostcall-0.1.0.tar.gz
  • Upload date:
  • Size: 113.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ghostcall-0.1.0.tar.gz
Algorithm Hash digest
SHA256 7220e996a3d602ca67735319d2682c4c0144d518b208783a94fab0a13ddf9c8e
MD5 d6e32932c724339ac5672453d7038e7d
BLAKE2b-256 dbcab0f42cfbcb54602bfaba0adfba6e748979e7ff03f2a16391fd492cf2a97b

See more details on using hashes here.

Provenance

The following attestation bundles were made for ghostcall-0.1.0.tar.gz:

Publisher: publish.yml on linosorice/ghostcall

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ghostcall-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: ghostcall-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 9.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ghostcall-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 70084d199db9c9750b23bf3a84999c5103fd4bdee2b7d6e51756d1ab5f12df5c
MD5 4e53c8a69abc98cdd8a59ae6de6f4a3e
BLAKE2b-256 3e6629f78a7a182ad263a28a24f4ee69fb0886798e60b7f25d12ff37d698bbf8

See more details on using hashes here.

Provenance

The following attestation bundles were made for ghostcall-0.1.0-py3-none-any.whl:

Publisher: publish.yml on linosorice/ghostcall

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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