I added an AI helper to my JSON mock-API tool — the hybrid design, and a Workers AI gotcha

작성자

카테고리:

← 피드로
DEV Community · solca · 2026-07-23 개발(SW)

solca

I run TempTools — a small suite of free, no-signup web tools that expire and delete themselves. The one I use most is Temp API: paste JSON or CSV, get a live mock endpoint in seconds.

I just added an AI helper to it, and the design turned out more interesting than “call an LLM.” The rule I set for myself was: AI is never allowed to touch correctness. Here’s how that shook out — plus a Cloudflare Workers AI gotcha that broke two of the three features while the third worked fine.

What the helper does

Three buttons on the Temp API editor:

  • Fix & format — clean up messy/broken JSON and explain what changed
  • Generate schema — a JSON Schema (draft 2020-12) from your data
  • Generate sample — realistic mock data with the same shape

The design rule: keep AI away from your data

Here’s the thing I didn’t want: an AI silently rewriting my JSON while pretending to “format” it. If you paste {"id": 42} and the tool hands back {"id": 43}, that’s not a fix — that’s a bug you’ll chase for an hour.

So repair and formatting are 100% deterministic. No AI. I use jsonrepair:

export function formatOrRepair(input: string) {
  try {
    return { ok: true, formatted: JSON.stringify(JSON.parse(input), null, 2), repaired: false };
  } catch {
    /* not valid — try to repair */
  }
  try {
    const repaired = jsonrepair(input);
    return { ok: true, formatted: JSON.stringify(JSON.parse(repaired), null, 2), repaired: true };
  } catch {
    return { ok: false };
  }
}

Enter fullscreen mode Exit fullscreen mode

The AI is only used for things where being “approximately right” is fine and there’s no source of truth to corrupt:

  • explaining what the deterministic repair changed
  • generating a schema
  • generating brand-new sample data

That split matters for the copy too. It would be tempting to market this as “AI fixes your JSON!” — but that’s not true, and someone will call it out. The UI says the repair runs locally and reserves “AI” for the schema/sample/explanation. Honest and it dodges a whole class of complaints.

The AI calls (Cloudflare Workers AI)

The generation runs on Workers AI with an ai binding — no external API keys, it just runs on the edge:

export const AI_MODEL = "@cf/qwen/qwen2.5-coder-32b-instruct";

async function runText(ai, messages, maxTokens) {
  const out = await ai.run(AI_MODEL, { messages, max_tokens: maxTokens, temperature: 0.2 });
  return out.response.trim(); // ← this line is a trap. more below.
}

Enter fullscreen mode Exit fullscreen mode

generateSchema and generateSample are just runText with a system prompt that says “output ONLY raw JSON, no markdown fences,” and then I strip any stray fences/prose defensively before parsing.

The gotcha: response isn’t always a string

Here’s the bug that had me confused for a while. In production:

  • Fix & format worked perfectly (including its AI explanation)
  • Generate schema and Generate sample both failed with a generic 502

Same model. Same runText. Same binding. So why did one of three AI calls work and two fail?

I temporarily surfaced the real error in the response and got this:

((intermediate value).response ?? "").trim is not a function

Enter fullscreen mode Exit fullscreen mode

out.response wasn’t a string — so .trim() didn’t exist on it.

The pattern clicked once I saw which calls failed. The explanation prompt returns prose, so response is a string. The schema and sample prompts return JSON — and when the model’s output is JSON, Workers AI can hand you response as an already-parsed object, not a string. Calling .trim() on an object throws.

The fix is boring but worth knowing: don’t assume response is a string.

async function runText(ai, messages, maxTokens) {
  const out = await ai.run(AI_MODEL, { messages, max_tokens: maxTokens, temperature: 0.2 });
  const r = (out as { response?: unknown }).response;
  const text = typeof r === "string" ? r : r == null ? "" : JSON.stringify(r);
  return text.trim();
}

Enter fullscreen mode Exit fullscreen mode

If response is a string, use it. If it’s an object (parsed JSON), JSON.stringify it back — which is exactly what I want to hand to the schema/sample path anyway. null/undefined becomes an empty string instead of crashing.

Two debugging lessons I keep re-learning:

  1. “Some calls work, some don’t” is a gift. The difference between the working and broken calls is the bug. Here it was the output type (prose vs JSON), not the model or the binding.
  2. A generic catch → 502 hides the answer. One temporary line echoing the real error message turned a guessing game into a one-line fix. (Then I took it back out.)

Keeping it cheap and abuse-resistant

Because repair/formatting never calls the model, the common case (paste valid-ish JSON, format it) costs zero AI. The model only runs when you ask for an explanation, schema, or sample.

On top of that, AI calls are rate-limited per IP with a tiny rolling log table (same trick I use for uploads), and the input is size-capped before it ever reaches the model. It all stays comfortably inside the Cloudflare free tier.

Try it

It’s live at temptools.webcli.jp/tools/temp-api — paste some rough JSON and hit the AI buttons. No signup, and the endpoint you create expires on its own.

If you’re building on Workers AI, keep that response-type gotcha in your back pocket. And if you find a rough edge in Temp API, I’d genuinely love to hear it. 🛠️

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다