Needle JSON Schema Support: Using Raw JSON Schemas Instead of Python Functions

Yes, Needle supports raw JSON schemas directly—pass a JSON-encoded string to the tools parameter and it bypasses Python function introspection entirely.

The cactus-compute/needle repository provides a flexible tool-calling framework designed for LLM agents. While Python functions with type annotations are the most common input format, the architecture explicitly allows you to supply pre-built JSON schemas instead. This enables integration with external API specifications, hand-written OpenAI-compatible definitions, or schemas generated from other sources.


How Needle Handles the Tools Parameter

The entry point for tool definition occurs in needle/__init__.py. The primary Needle class constructor accepts a tools argument that can be either:

  • A string containing valid JSON (raw schema mode)
  • A Python object (callable or collection of callables)

The constructor logic distinguishes between these cases:

tools_json = tools if isinstance(tools, str) else json.dumps(self._resolve(tools))

When tools is already a string, Needle uses it verbatim without transformation. Otherwise, it invokes _resolve() to convert Python callables into equivalent JSON schema definitions.


Submitting Raw JSON Schemas to Needle

Basic Usage with a JSON String

Define your tools as a JSON array following the standard tool-calling format, then pass the serialized result:

import json
from needle import Needle

raw_schema = [
    {
        "name": "search",
        "description": "Search the web for a query",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "The search query"
                }
            },
            "required": ["query"]
        }
    }
]

needle = Needle(tools=json.dumps(raw_schema))

The json.dumps() call produces the exact string format the constructor expects. Since the input is a string, _resolve() is skipped entirely.

Loading Schemas from External Files

For schemas maintained in separate files—common when versioning tool definitions or sharing specifications across services:

import json
from needle import Needle

with open("openapi_tools.json", "r", encoding="utf-8") as f:
    tools_json = f.read()

needle = Needle(tools=tools_json)

This pattern works with any JSON source: OpenAPI specifications, manually curated schemas, or exports from other framework.


Mixing Raw JSON with Python Functions

Needle's _resolve() method handles heterogeneous collections automatically. You can combine raw JSON schemas with Python callables in the same tools list:

def calculate_sum(a: int, b: int) -> int:
    """Add two integers together."""
    return a + b

mixed_tools = [
    {
        "name": "weather_lookup",
        "description": "Retrieve current weather for a location",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {"type": "string"}
            },
            "required": ["city"]
        }
    },
    calculate_sum  # Python callable with type hints

]

needle = Needle(tools=mixed_tools)

The framework iterates through the collection, applying JSON serialization only to non-string items. Raw schemas pass through unchanged.


Where Python Function Conversion Occurs

When you do provide Python callables, the transformation to JSON schema happens in needle/agent/tools.py. The _json_type() helper maps Python type hints to JSON Schema type definitions:

Python Type JSON Schema Equivalent
str {"type": "string"}
int / float {"type": "number"}
bool {"type": "boolean"}
list[T] {"type": "array", "items": ...}
dict / BaseModel {"type": "object", "properties": ...}

This introspection is bypassed entirely when you supply a pre-built JSON string, avoiding any runtime overhead from type analysis.


Key Source Files for JSON Schema Handling

File Purpose
needle/__init__.py Main Needle class; implements tools parameter parsing and _resolve() dispatch
needle/agent/tools.py Python-to-JSON schema conversion utilities; _json_type() and related helpers
needle/model/finetune.py Training data pipeline; demonstrates schema injection into JSONL formats
needle/playground/server.py HTTP server reference implementation; accepts raw tools payloads

Complete Working Example

Below is a runnable pattern showing end-to-end usage with a raw JSON schema and execution context:

import json
from needle import Needle

# Schema defining a hypothetical code execution tool

executor_schema = [
    {
        "name": "execute_python",
        "description": "Run Python code in a sandboxed environment",
        "parameters": {
            "type": "object",
            "properties": {
                "code": {
                    "type": "string",
                    "description": "Python code to execute"
                },
                "timeout_seconds": {
                    "type": "integer",
                    "description": "Maximum execution time",
                    "default": 30
                }
            },
            "required": ["code"]
        }
    }
]

# Initialize with raw JSON—no Python function defined

agent = Needle(tools=json.dumps(executor_schema))

# The LLM can now invoke execute_python based on this schema

result = agent.complete(
    messages=[{
        "role": "user",
        "content": "Calculate factorial of 5 using a Python loop"
    }]
)

Summary

  • Raw JSON schemas are first-class inputs—pass json.dumps(your_schema) to the tools parameter and Needle accepts it without modification
  • String detection happens at initialization—the isinstance(tools, str) check in needle/__init__.py routes execution around _resolve()
  • Mixed collections work transparently—combine raw JSON objects with Python callables in the same list
  • Conversion utilities remain available—when needed, needle/agent/tools.py provides _json_type() for Python-to-JSON transformation

Frequently Asked Questions

Does Needle validate the JSON schema I provide?

Needle performs minimal validation at initialization. The schema is passed directly to the underlying LLM interface, and validation errors typically surface during model execution or tool invocation rather than at construction time. For stricter validation, pre-validate your schemas against the JSON Schema specification before passing them to Needle.

Can I use OpenAI's function calling format directly?

Yes. Needle's JSON schema format aligns with OpenAI's function calling specification. Schemas formatted for tools or functions parameters in the OpenAI API should work without modification, making migration between services straightforward.

What happens if my schema references Python-specific types like datetime?

Raw JSON schemas must use standard JSON Schema types. If you need datetime handling, represent it as {"type": "string", "format": "date-time"} in your raw schema. When using Python callables, needle/agent/tools.py handles common type conversions automatically.

Is there a performance difference between raw JSON and Python functions?

Raw JSON schemas eliminate the introspection overhead of _resolve() and _json_type(). For applications invoking Needle frequently or with large tool collections, pre-serialized schemas reduce startup latency marginally. The runtime performance during LLM inference remains identical.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →