This page documents the Python utility interface for turning an HTTP request description into a callable async tool using curl_tools.
The mental model is as follows:
- you start with a request description (often a
curl ...snippet copied from docs), - you compile it into a Python object (
HttpTool), - and later you run the request by calling that object with inputs:
await tool.call(...).
curl_tools is intentionally small. It does not implement retries, batching, rate limiting, backoff, or provider-specific SDK wrappers. It focuses on one job: represent an HTTP request in a deterministic way, execute it when you ask, and return a structured report of what happened.
This reference covers three things:
- how to compile a tool from a cURL snippet, an explicit schema, or GPT-assisted extraction,
- how runtime placeholders work when you execute the tool (env secrets and input variables),
- how to freeze a tool spec as JSON and reload it later without changing its meaning.
The typical usage pattern is:
-
Compile a tool via deterministic cURL parsing (
compiler.parse(...)), an explicit schema (compiler.request_schema(...)), or GPT-assisted extraction (compiler.gpt_parse(...)):compiler = CurlToolCompiler() tool = compiler.parse(curl_text) # or: compiler.request_schema(...), await compiler.gpt_parse(...)
-
Optionally snapshot the tool spec via
tool.to_dict()for deterministic persistence:spec_dict = tool.to_dict()
-
Execute the request by calling the tool with runtime inputs:
report = await tool.call({"response_id": "resp_..."})
-
Inspect the report to decide what to do next:
if report.ok: data = report.response_json else: raise RuntimeError(f"{report.status_code}: {report.response_text}")
-
If you persisted the spec, rehydrate it later and run it the same way:
tool2 = compiler.request_schema_from_dict(spec_dict)
The public symbols exported by this extension are:
CurlToolCompilerSecretResolverBasicAuthSpecparse_curl_command
| You want to… | Use | Why |
|---|---|---|
Turn a real docs/example curl ... snippet into a callable tool |
CurlToolCompiler.parse(...) |
Deterministic parsing of a practical subset of cURL |
| Build a request with no inference at all | CurlToolCompiler.request_schema(...) |
You control every field directly |
| Extract a first draft tool from prose docs or mixed docs + snippets | CurlToolCompiler.gpt_parse(...) |
Uses OpenAI structured outputs with token/cost guardrails |
| Persist a tool spec and restore it later | HttpTool.to_dict(...) + CurlToolCompiler.request_schema_from_dict(...) |
Gives deterministic reload after the one-time parse step |
| Parse a cURL snippet into a raw spec without creating a tool | parse_curl_command(...) |
Returns HttpRequestSpec directly |
- Deterministic cURL parsing: turn a practical subset of
curlsyntax intoHttpRequestSpec. - Explicit request construction: build the same runtime object without any parsing or inference.
- GPT-assisted extraction: generate a
CurlToolBlueprintfrom docs text, then compile it into a normalHttpTool. - Templated execution: resolve
{{env:...}}and{{var}}placeholders at call time. - Persistence and reload: freeze a tool spec as JSON, then rehydrate it later with the same request meaning.
curl_tools is shipped as an extension module hosted in the extension-utilities repository. In projects based on the summoner-sdk template, you add it to your SDK build list so it is included during composition.
Add the following entry to your SDK builder list (in the build.txt file):
https://github.com/Summoner-Network/extension-utilities.git:
curl_tools
This tells the SDK composition step to pull the curl_tools module from extension-utilities and merge it into the composed SDK.
Import the compiler from the SDK like any other public module:
from summoner.curl_tools import (
CurlToolCompiler,
SecretResolver,
BasicAuthSpec,
parse_curl_command,
)Note
In the extension-utilities repository, local tests may import this module directly as from tooling.curl_tools import ... (that is, without SDK composition). For normal SDK usage, always use the summoner.curl_tools import.
curl_tools compiles to one runtime object:
HttpTool: an async callable wrapper aroundHttpRequestSpec.
When you run await tool.call(inputs), the tool executes a well-defined sequence:
- it renders templates in the URL, headers, params, and body
- it resolves
{{env:NAME}}usingSecretResolver - it resolves
{{var}}using theinputsdict passed tocall(...) - it sends the HTTP request via
httpx.AsyncClient - it parses JSON when response content-type indicates JSON
- it can validate JSON responses against an optional Pydantic
output_model - it returns a
ToolCallReportdescribing what happened
Two placeholder syntaxes exist:
{{env:NAME}}: resolved viaSecretResolver.require(NAME){{var}}: resolved viainputs["var"]
Rendering happens in:
spec.url(string)spec.headersandspec.params(dict-like)spec.body(string/dict/list, recursively)
Fail-fast behavior:
- missing env var raises
KeyError("Missing required secret: ...") - missing runtime input raises
KeyError("Missing required input variable: ...")
This prevents partially-formed requests from silently executing.
def __init__(
self,
mapping: Optional[Mapping[str, str]] = None,
fallback: Optional[Callable[[str], Optional[str]]] = None,
auto_dotenv: bool = False,
dotenv_path: Optional[str] = None,
dotenv_override: bool = False,
) -> NoneCreates a secret resolver with a fixed resolution order:
- explicit
mappingpassed at construction time os.environ- optional
fallback(name)callable
If auto_dotenv=True, the constructor loads environment variables via dotenv.load_dotenv(...).
- Type:
Optional[Mapping[str, str]] - Meaning: Explicit secret mapping used as the highest-precedence source.
- Default:
None(treated as empty)
- Type:
Optional[Callable[[str], Optional[str]]] - Meaning: Optional callable used when a name is missing from both
mappingandos.environ. - Default:
None
- Type:
bool - Meaning: If
True, loads.envvariables during initialization. - Default:
False
- Type:
Optional[str] - Meaning: Optional path passed to
load_dotenv(...). - Default:
None
- Type:
bool - Meaning: Whether
.envvalues override existingos.environ. - Default:
False
This constructor returns a SecretResolver instance.
secrets = SecretResolver(auto_dotenv=True)
token = secrets.require("HUBSPOT_ACCESS_TOKEN")def get(self, name: str) -> Optional[str]Returns the resolved secret value, or None if no source provides it.
Resolution order:
mappingos.environfallback(name)if provided
- Type:
str - Meaning: Secret name to resolve.
Returns Optional[str].
token = secrets.get("HUBSPOT_ACCESS_TOKEN")
if token is None:
raise RuntimeError("Missing token")def require(self, name: str) -> strReturns the resolved secret value.
If the secret is unavailable, raises:
KeyError(f"Missing required secret: {name}")
- Type:
str - Meaning: Secret name to resolve.
Returns str.
token = secrets.require("HUBSPOT_ACCESS_TOKEN")def __init__(
self,
method: HttpMethod,
url: str,
headers: dict[str, str] = ...,
params: dict[str, str] = ...,
body: Optional[Any] = None,
body_mode: BodyMode = "json",
auth: Optional[BasicAuthSpec] = None,
timeout_s: Optional[float] = 30.0,
follow_redirects: bool = False,
verify_tls: bool = True,
description: Optional[str] = None,
input_model: Optional[Type[BaseModel]] = None,
output_model: Optional[Type[BaseModel]] = None,
) -> NoneCreates a deterministic HTTP request specification consumed by HttpTool.
The spec is intentionally close to the arguments accepted by httpx.AsyncClient.request(...), but keeps a stable structure that can be persisted and reloaded.
It describes:
- how to reach the endpoint (
method,url,params) - how to authenticate or identify the caller (
headers, optionalauth) - how to encode the request body (
body,body_mode) - how to control transport behavior (
timeout_s,follow_redirects,verify_tls) - optional metadata and validation (
description,input_model,output_model)
Body conventions:
-
body_mode="json"- if
bodyis adictorlist, it is sent as JSON - if
bodyis astr, it is sent as rawcontent
- if
-
body_mode="form"bodymay be adict, alist[tuple[str, str]], or a raw string like"a=1&b=2"- the runtime encodes the body as
application/x-www-form-urlencoded
-
body_mode="raw"bodyis sent as rawcontent- if
bodyis adictorlist, it is stringified usingjson.dumps(...)first
- Type:
HttpMethod - Meaning: HTTP method used for the request.
- Type:
str - Meaning: Endpoint URL. May include
{{env:...}}and{{var}}placeholders.
- Type:
dict[str, str] - Meaning: Request headers after templating.
- Default:
{}
- Type:
dict[str, str] - Meaning: Query parameters after templating.
- Default:
{}
- Type:
Optional[Any] - Meaning: Request body payload. Interpreted according to
body_mode. - Default:
None
- Type:
BodyMode - Meaning: Body encoding mode (
"json","form","raw"). - Default:
"json"
- Type:
Optional[BasicAuthSpec] - Meaning: Optional basic auth credentials.
- Default:
None
- Type:
Optional[float] - Meaning: Timeout in seconds passed to
httpx. - Default:
30.0
- Type:
bool - Meaning: Whether redirects are followed by
httpx. - Default:
False
- Type:
bool - Meaning: Whether TLS certificates are verified by
httpx. - Default:
True
- Type:
Optional[str] - Meaning: Optional human-readable description stored on the spec.
- Default:
None
- Type:
Optional[Type[BaseModel]] - Meaning: Optional Pydantic model used to validate inputs before templating.
- Default:
None
- Type:
Optional[Type[BaseModel]] - Meaning: Optional Pydantic model used to validate JSON responses when possible.
- Default:
None
This constructor returns an HttpRequestSpec instance.
def to_dict(self, *, include_models: bool = False) -> dict[str, Any]Returns a JSON-safe snapshot of the spec intended for persistence and later rehydration via CurlToolCompiler.request_schema_from_dict(...).
Serialization is best-effort:
- tuples become lists
- dataclasses become dict-like structures
- objects that are not JSON-friendly are stringified
input_modelandoutput_modelare omitted by default
If include_models=True, the snapshot includes best-effort import paths for input_model and output_model. Model rehydration is intentionally not automatic.
- Type:
bool - Meaning: Whether to include best-effort model import paths in the snapshot.
- Default:
False
Returns a JSON-safe dict[str, Any].
def to_request_schema_kwargs(self) -> dict[str, Any]Returns Python-native kwargs suitable for rebuilding a tool through:
tool2 = compiler.request_schema(**kwargs)This is a round-trip convenience when you do not want JSON serialization to coerce types. For example, tuple pairs in form bodies and Pydantic model types remain intact.
Important distinction:
to_request_schema_kwargs()returns Python-native valuesto_dict()returns JSON-safe values
Returns a dict[str, Any] intended to be passed directly into CurlToolCompiler.request_schema(...).
compiler = CurlToolCompiler()
tool = compiler.request_schema(
method="POST",
url="https://api.example.com/oauth/token",
body=[("grant_type", "client_credentials"), ("client_id", "{{env:CLIENT_ID}}")],
body_mode="form",
auth=BasicAuthSpec(username="{{env:USER}}", password="{{env:PASS}}"),
)
kwargs = tool.spec.to_request_schema_kwargs()
tool2 = compiler.request_schema(**kwargs)
report = await tool2.call({})def __init__(self, spec: HttpRequestSpec, secrets: Optional[SecretResolver] = None) -> NoneCreates an async callable tool around a fixed HttpRequestSpec.
- Stores the request spec on
self.spec. - Stores a
SecretResolveronself.secrets. If none is provided, a default resolver is created.
This constructor does not execute any request. Execution happens only when you call await tool.call(...).
- Type:
HttpRequestSpec - Meaning: Deterministic request description executed by this tool.
- Type:
Optional[SecretResolver] - Meaning: Resolver used to satisfy
{{env:NAME}}placeholders at runtime. - Default behavior: If not provided, a default
SecretResolver()is created.
This constructor returns an HttpTool instance.
compiler = CurlToolCompiler()
tool = compiler.parse(curl_text)async def call(self, inputs: Optional[dict[str, Any]] = None) -> ToolCallReportExecutes the request and returns a ToolCallReport.
Steps:
- If
spec.input_modelexists, validatesinputsvia Pydantic and replacesinputswith the validated dump. - Renders templates in URL, headers, params, and body.
- Applies Basic Auth if
spec.authis set, after template rendering. - Applies default
Content-Typebased onbody_modeif missing. - Sends the HTTP request via
httpx.AsyncClient. - Attempts JSON parsing when the response
content-typeincludesapplication/json. - If
spec.output_modelexists, validatesresponse_jsonand fills validation fields. - Returns a report with request summary, status, timing, and parsed response fields.
Request error behavior:
-
On network or client exceptions, returns a report with:
ok=Falsestatus_code=0response_text="Request error: ..."
- Type:
Optional[dict[str, Any]] - Meaning: Runtime values used to render
{{var}}placeholders, and optionally validated byspec.input_model. - Default:
None(treated as empty dict)
Returns a ToolCallReport instance.
tool = compiler.request_schema(
method="POST",
url="https://api.example.com/v1/items",
headers={"Authorization": "Bearer {{env:API_TOKEN}}"},
body={"name": "{{name}}"},
body_mode="json",
)
report = await tool.call({"name": "demo"})
assert report.ok
assert report.response_json is not Nonetool = compiler.request_schema(
method="POST",
url="https://api.example.com/oauth/token",
headers={"Content-Type": "application/x-www-form-urlencoded"},
body=[("grant_type", "client_credentials"), ("client_id", "{{env:CLIENT_ID}}")],
body_mode="form",
)
report = await tool.call({})
assert report.status_code in (200, 400)def to_dict(self, *, include_models: bool = False) -> dict[str, Any]Returns a JSON-safe snapshot of the underlying HttpRequestSpec.
This is a convenience wrapper around:
tool.spec.to_dict(include_models=include_models)The snapshot is intended for deterministic persistence and later rehydration using:
tool2 = compiler.request_schema_from_dict(spec_dict)Model handling:
input_modelandoutput_modelare omitted by default- if
include_models=True, the snapshot includes best-effort import paths forinput_modelandoutput_model - model rehydration is intentionally not automatic
- Type:
bool - Meaning: Whether to include best-effort model import paths.
- Default:
False
Returns a JSON-safe dict[str, Any].
spec_dict = tool.to_dict()
tool2 = compiler.request_schema_from_dict(spec_dict)
report = await tool2.call({"response_id": "resp_..."})def __init__(
self,
*,
secrets: Optional[SecretResolver] = None,
openai_api_key: Optional[str] = None,
auto_dotenv: bool = True,
dotenv_path: Optional[str] = None,
dotenv_override: bool = False,
max_chat_input_tokens: int = 600,
max_chat_output_tokens: int = 1200,
default_cost_limit: Optional[float] = None,
validate_model_name: bool = True,
) -> NoneCreates a compiler used to build HttpTool instances from either deterministic inputs or GPT-assisted extraction.
Constructor responsibilities:
- Optionally loads
.envviaload_dotenv(...)whenauto_dotenv=True. - Stores a
SecretResolveronself.secrets. If none is provided, a default resolver is created. - Configures budgeting parameters used by
gpt_parse(...). - If an OpenAI API key is available, bootstraps internal
HttpToolinstances for OpenAI/v1/responsesand/v1/modelsusing the same cURL parsing path exposed by this module. - If
validate_model_name=True,gpt_parse(...)may perform a best-effort lookup of available OpenAI model IDs through/v1/models. If model listing fails, it fails open and continues without blockinggpt_parse(...).
- Type:
Optional[SecretResolver] - Meaning: Secret resolver used for
{{env:...}}placeholders at runtime. - Default behavior: If not provided, a default resolver is created.
- Type:
Optional[str] - Meaning: Optional API key used for
gpt_parse(...). - Default behavior: If omitted, the constructor reads
OPENAI_API_KEYfrom the environment.
- Type:
bool - Meaning: Whether to load
.envvariables during initialization. - Default:
True
- Type:
Optional[str] - Meaning: Optional path passed to
load_dotenv(...). - Default:
None
- Type:
bool - Meaning: Whether
.envvalues override existingos.environ. - Default:
False
- Type:
int - Meaning: Prompt token ceiling enforced by
gpt_parse(...). - Default:
600
- Type:
int - Meaning: Output token budget used for API calls and worst-case cost estimation.
- Default:
1200
- Type:
Optional[float] - Meaning: Default USD cost ceiling used when
gpt_parse(..., cost_limit=None). - Default:
None
- Type:
bool - Meaning: Whether
gpt_parse(...)validatesmodel_nameagainst a best-effort model list. - Default:
True
This constructor returns a CurlToolCompiler instance.
compiler = CurlToolCompiler(secrets=SecretResolver(auto_dotenv=True))
tool = compiler.parse(curl_text)def set_budget(
self,
*,
max_chat_input_tokens: Optional[int] = None,
max_chat_output_tokens: Optional[int] = None,
default_cost_limit: Optional[float] = None,
) -> NoneUpdates the budgeting parameters used by CurlToolCompiler.gpt_parse(...).
This method does not affect deterministic compilation (parse(...), request_schema(...)) or runtime execution (HttpTool.call(...)). It only changes the ceilings enforced before making an OpenAI call inside gpt_parse(...).
Fields updated:
max_chat_input_tokens: prompt token ceiling (measured withcount_chat_tokens(...))max_chat_output_tokens: output token budget passed to the API and used for worst-case cost estimationdefault_cost_limit: default USD cost ceiling used whengpt_parse(..., cost_limit=None)
Any argument left as None leaves the existing value unchanged.
-
max_chat_input_tokens- Type:
Optional[int] - Meaning: Maximum allowed prompt tokens for the
gpt_parserequest. - Default:
None(do not change)
- Type:
-
max_chat_output_tokens- Type:
Optional[int] - Meaning: Output token budget used when calling OpenAI and when estimating worst-case cost.
- Default:
None(do not change)
- Type:
-
default_cost_limit- Type:
Optional[float] - Meaning: Default USD cost ceiling used by
gpt_parsewhencost_limitis not explicitly provided. - Default:
None(do not change)
- Type:
Returns None.
compiler = CurlToolCompiler()
# Tighten ceilings for docs-heavy parsing sessions.
compiler.set_budget(
max_chat_input_tokens=400,
max_chat_output_tokens=800,
default_cost_limit=0.01,
)
tool = await compiler.gpt_parse(docs_text, model_name="gpt-4o-mini")def parse(
self,
curl_text: str,
*,
description: Optional[str] = None,
input_model: Optional[Type[BaseModel]] = None,
output_model: Optional[Type[BaseModel]] = None,
) -> HttpToolParses a practical subset of curl into an HttpRequestSpec and returns an HttpTool.
This method is deterministic. It delegates curl parsing to parse_curl_command(...), then optionally attaches:
description(stored on the spec)input_model(validated againstinputsbefore sending the request)output_model(validated against JSON responses when possible)
- Type:
str - Meaning: cURL command text to parse. This may include
$ENV_VAR,${ENV_VAR}, headers, body flags, auth flags, and supported transport options.
- Type:
Optional[str] - Meaning: Optional human-readable description stored on the resulting request spec.
- Default:
None
- Type:
Optional[Type[BaseModel]] - Meaning: Optional Pydantic model used to validate
inputsbefore template rendering duringtool.call(...). - Default:
None
- Type:
Optional[Type[BaseModel]] - Meaning: Optional Pydantic model used to validate parsed JSON responses after the request runs.
- Default:
None
Returns an HttpTool.
compiler = CurlToolCompiler(secrets=SecretResolver(auto_dotenv=True))
curl_text = r"""
curl https://api.hubapi.com/crm/v3/objects/companies?limit={{limit}} \
-H "Authorization: Bearer $HUBSPOT_ACCESS_TOKEN"
""".strip()
tool = compiler.parse(curl_text, description="HubSpot: list companies")
report = await tool.call({"limit": 10})
assert report.okdef request_schema(
self,
*,
method: HttpMethod,
url: str,
headers: Optional[dict[str, str]] = None,
params: Optional[dict[str, str]] = None,
body: Optional[Any] = None,
body_mode: BodyMode = "json",
auth: Optional[BasicAuthSpec] = None,
timeout_s: Optional[float] = 30.0,
follow_redirects: bool = False,
verify_tls: bool = True,
description: Optional[str] = None,
input_model: Optional[Type[BaseModel]] = None,
output_model: Optional[Type[BaseModel]] = None,
) -> HttpToolConstructs an HttpRequestSpec with no inference and returns an HttpTool.
Use this when you want full control over:
- method selection
- body encoding (
json,form,raw) - TLS verification and redirect policy
- timeouts
- optional Pydantic input/output validation
The parameters mirror HttpRequestSpec.__init__(...):
method: HTTP method used for the request.url: endpoint URL, optionally containing{{env:...}}or{{var}}placeholders.headers: optional request headers.params: optional query parameters.body: optional request payload.body_mode: one of"json","form", or"raw".auth: optionalBasicAuthSpec.timeout_s: optional timeout passed tohttpx.follow_redirects: whether redirects are followed.verify_tls: whether TLS certificates are verified.description: optional human-readable description stored on the spec.input_model: optional Pydantic input validator.output_model: optional Pydantic output validator.
Returns an HttpTool.
tool = compiler.request_schema(
method="GET",
url="https://api.hubapi.com/crm/v3/objects/companies",
headers={"Authorization": "Bearer {{env:HUBSPOT_ACCESS_TOKEN}}"},
params={"limit": "{{limit}}"},
body=None,
body_mode="json",
description="HubSpot: list companies",
)
report = await tool.call({"limit": 10})
assert report.status_code == 200def request_schema_from_dict(self, d: Mapping[str, Any]) -> HttpToolDeterministically rehydrates a tool spec produced by HttpTool.to_dict().
Special fixup:
- if
body_mode == "form", it converts stored list pairs back into tuple pairs so form encoding works after JSON reload.
- Type:
Mapping[str, Any] - Meaning: JSON-safe tool snapshot, typically produced by
HttpTool.to_dict(...).
Returns an HttpTool.
tool = compiler.parse(curl_text)
spec_dict = tool.to_dict()
tool2 = compiler.request_schema_from_dict(spec_dict)
report2 = await tool2.call({"limit": 10})async def gpt_parse(
self,
docs: str,
*,
model_name: str = "gpt-4o-mini",
cost_limit: Optional[float] = None,
debug: bool = False,
) -> HttpToolExtracts a request description from documentation text using OpenAI structured outputs, then compiles it into an HttpTool.
- Requires
OPENAI_API_KEY(oropenai_api_keypassed at compiler init). If missing, raisesRuntimeError. - If
validate_model_name=True, optionally validatesmodel_nameby fetching available OpenAI models through/v1/models(fail-open if listing fails). - Redacts probable secrets in
docsbefore sending them to the model. - Enforces token and cost ceilings using
gpt_guardrails(count_chat_tokens,estimate_chat_request_cost). - Builds an internal OpenAI
/v1/responsesrequest using the samecurl->HttpToolpath used elsewhere in this module, and submits a strict JSON-schema request derived fromCurlToolBlueprint. - Extracts
output_textfrom the JSON response, validates it asCurlToolBlueprint, and raisesRuntimeErrorif the response is malformed, refused, incomplete, or schema-invalid. - Converts the resulting
CurlToolBlueprintinto anHttpRequestSpecand returns anHttpTool.
The returned tool is no different from tools built via parse(...) or request_schema(...).
- Type:
str - Meaning: Documentation text, examples, snippets, or mixed prose+cURL content used to infer the request blueprint.
- Type:
str - Meaning: OpenAI model id used for structured extraction.
- Default:
"gpt-4o-mini"
- Type:
Optional[float] - Meaning: Optional per-call USD ceiling. If
None,default_cost_limitfrom the compiler is used instead. - Default:
None
- Type:
bool - Meaning: When
True, prints prompt token and cost diagnostics, and prints actual usage when available. - Default:
False
Returns an HttpTool.
compiler = CurlToolCompiler(
secrets=SecretResolver(auto_dotenv=True),
max_chat_input_tokens=600,
max_chat_output_tokens=1200,
default_cost_limit=0.02,
)
tool = await compiler.gpt_parse(docs_text, model_name="gpt-4o-mini")
spec_dict = tool.to_dict()Note
A common workflow is to run gpt_parse once, persist tool.to_dict(), and then use request_schema_from_dict(...) in production paths so the runtime is deterministic and does not depend on GPT.
def parse_curl_command(curl_text: str) -> HttpRequestSpecParses a practical subset of curl into an HttpRequestSpec. It is deterministic.
Supported curl features:
-
URL: positional
https://...or--url -
Method:
-X/--request, or-G/--get(forces GET) -
Headers:
-H/--header "Key: Value" -
Body:
-d/--data/--data-raw/--data-binary/--data-ascii--data-urlencode(producesbody_mode="form"and list of pairs)
-
Basic auth:
-u user:pass -
Redirects:
-L/--location -
TLS verify disable:
-k/--insecure -
Timeout:
--connect-timeout,-m/--max-time
Environment variables:
$FOOand${FOO}are converted to{{env:FOO}}- escaped dollars are preserved (
\$FOOstays$FOO)
Body inference:
- JSON is selected if Content-Type is JSON or payload looks like JSON
- form is selected if Content-Type is urlencoded or payload parses as
k=v&... - raw is selected otherwise
Special case:
-G/--getplus-dtreats the-dpayload as query params (best-effort)
Common parsing errors:
- empty cURL text raises
ValueError("Empty curl command.") - missing URL raises
ValueError("curl: no URL found") - malformed
-u/--userauth or missing required flag arguments raiseValueError - unsupported HTTP methods raise
ValueError
- Type:
str - Meaning: cURL command text to parse into a deterministic
HttpRequestSpec.
Returns an HttpRequestSpec.
spec = parse_curl_command(
r'''
curl --request POST \
--url 'https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Messages.json' \
--user '$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN' \
--data-urlencode 'To=+15551234567' \
--data-urlencode 'From=+15557654321' \
--data-urlencode 'Body=Hello from Summoner'
'''.strip()
)
assert spec.method == "POST"
assert spec.body_mode == "form"
assert spec.auth is not None-
My tool output is not a dict
tool.call(...)returns aToolCallReport. Readreport.response_jsonorreport.response_textrather than assuming a dict return value. -
Why is
response_jsonmissing? JSON parsing is only attempted when the response content-type includesapplication/json. Otherwise the response is stored inresponse_text. -
Form tools fail after reload Reload via
request_schema_from_dict(...), not by manually feeding the dict intorequest_schema(...). The rehydrator converts list pairs back to tuple pairs. -
Template variables do not render Missing env vars raise
KeyErrorviaSecretResolver.require(...). Missing runtime variables raiseKeyErrorin_render_template_str(...). -
gpt_parsefails with token/cost ceiling errors Reduce the docs text, increasemax_chat_input_tokens, reducemax_chat_output_tokens, or raise the cost limit. -
gpt_parsesays the model name is invalid By default,CurlToolCompiler(validate_model_name=True)performs a best-effort preflight against OpenAI/v1/models. Use a valid model id or disable the preflight check withvalidate_model_name=Falseif you intentionally want to skip local validation.
« Previous: Summoner.visionary | Next: Summoner.gpt_guardrails »