Install
openclaw skills install @clarezoe/ai-address-parserParses a free-text address blob (supplier email, Google Maps result, business card) into structured fields (street, postal code, city, state, country, contact, phone) via an LLM chat-completions call. Use when a form has separate address inputs and you want a paste-a-blob-to-autofill affordance instead of manual field-by-field entry.
openclaw skills install @clarezoe/ai-address-parserTurns messy pasted address text into structured form fields using an OpenAI-compatible chat-completions endpoint in JSON mode, applied non-destructively so it never clobbers fields the user already filled in.
Extract exactly the fields your form needs, no more. A typical set:
{
"address": "",
"postalCode": "",
"city": "",
"state": "",
"country": "",
"contact": "",
"phone": "",
"confidence": "high"
}
postalCode — never coerce it to a number. Many countries' postal codes have leading zeros or embedded spaces (e.g. Swedish 123 45), and a numeric type silently destroys both.confidence enum (high / medium / low) so the UI can flag results worth double-checking instead of presenting every parse as equally certain.The prompt is the actual product here — get these rules right:
Return ONLY this JSON object: followed by the exact empty-valued schema, then the raw input text.Call the OpenAI-compatible chat-completions endpoint (POST {base_url}/chat/completions) with:
model: the configured model namemessages: a single user message containing the prompt from Step 2response_format: { type: 'json_object' } — forces JSON-mode output where the provider supports ittemperature parameter. Some newer models reject the request outright (HTTP 400) if temperature is present at all, rather than ignoring it. Omit it entirely instead of setting a default.Some models wrap JSON-mode output in markdown code fences despite response_format. Parse with a fallback:
try JSON.parse(raw)
catch: extract the first `{...}` block via a greedy regex (`/\{[\s\S]*\}/`) and JSON.parse that instead
catch again: treat as unparseable
Validate the parsed result against the Step 1 schema (e.g. with a schema library). On validation failure, return a distinct error ("AI returned an unexpected format — try again") rather than surfacing a raw parse error. On the network/API call failing, return a separate error ("parsing failed — try again"). Log the raw content (truncated) server-side for debugging, but do not leak it to the client.
Re-apply the same length limits from Step 2 by slicing each returned string server-side before returning it to the client. Prompt instructions are not guarantees — treat them as best-effort and enforce the real constraint in code.
This is the rule that makes the feature safe to ship: only write a parsed field into the form if the form's current value for that field is empty, and skip any field the model returned as an empty string.
if (!form.city && result.city) form.city = result.city
Never overwrite a field the user already typed, even if the parse result disagrees with it — the user's own input is always authoritative. Surface the confidence field when it is low so the admin knows to manually verify before saving, but do not block the merge on it.
Before shipping, run the parser against:
Some record types carry paired fields for two languages instead of one canonical value — e.g. companyNameZh/companyNameEn, companyAddressZh/companyAddressEn. Extend the base technique like this:
contactNameZh / contactNameEn), not a single field plus a separate "language" flag.companyNameEn does not depend on whether companyNameZh was also filled.