Detect hallucinated API calls in LLM-generated Python code
Project description
ghostcall
Detect hallucinated API calls in LLM-generated Python code.
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.
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
- Parse the Python source with the standard
astmodule. - Build an alias map from imports (
import pandas as pd→pd → pandas). - For each dotted call chain (
pd.DataFrame.to_jsonl), resolve it through the alias map and walk it through the actually installed package viaimportlib+getattr. - 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 analysis —
df = pd.DataFrame(); df.fake_method()is not caught becausedfis 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 resolutionsrc/ghostcall/checker.py— introspection against installed packagessrc/ghostcall/suggest.py— fuzzy matchingsrc/ghostcall/output.py— terminal and JSON rendering
Run tests with:
pip install -e ".[dev]"
pytest
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ghostcall-0.1.1.tar.gz.
File metadata
- Download URL: ghostcall-0.1.1.tar.gz
- Upload date:
- Size: 111.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e5053782cbbe3c8de0a87949a6ff4bba5a2f73dcf6b44b02752e28b0bf71a51f
|
|
| MD5 |
b05ff45f3686ff7eef121f9ec1298ffe
|
|
| BLAKE2b-256 |
fd4f998b74cabade1b0d0c6b9015e016992ee918dce5c0c5a6ec481e809fc421
|
Provenance
The following attestation bundles were made for ghostcall-0.1.1.tar.gz:
Publisher:
publish.yml on linosorice/ghostcall
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ghostcall-0.1.1.tar.gz -
Subject digest:
e5053782cbbe3c8de0a87949a6ff4bba5a2f73dcf6b44b02752e28b0bf71a51f - Sigstore transparency entry: 1340429689
- Sigstore integration time:
-
Permalink:
linosorice/ghostcall@56b74fc266cde790ef3d4fc474c1388ee2960d5f -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/linosorice
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@56b74fc266cde790ef3d4fc474c1388ee2960d5f -
Trigger Event:
release
-
Statement type:
File details
Details for the file ghostcall-0.1.1-py3-none-any.whl.
File metadata
- Download URL: ghostcall-0.1.1-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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
49bfc0ca6a4dbe64e5d9b8527e03daf77f437fa18971c0580c8c49e8f7ea7332
|
|
| MD5 |
c6f0a07596928f20eae7cd48559feda3
|
|
| BLAKE2b-256 |
12615fd20407dbf8b75b9ddb08dc42f87249f65bd7ae7a856b830a098d631fdc
|
Provenance
The following attestation bundles were made for ghostcall-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on linosorice/ghostcall
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ghostcall-0.1.1-py3-none-any.whl -
Subject digest:
49bfc0ca6a4dbe64e5d9b8527e03daf77f437fa18971c0580c8c49e8f7ea7332 - Sigstore transparency entry: 1340429691
- Sigstore integration time:
-
Permalink:
linosorice/ghostcall@56b74fc266cde790ef3d4fc474c1388ee2960d5f -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/linosorice
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@56b74fc266cde790ef3d4fc474c1388ee2960d5f -
Trigger Event:
release
-
Statement type: