Migrating

From a base_url swap to the SDK — and when not to bother.

When not to bother

If your traffic is plain request/response — a support bot, a classifier, a RAG endpoint with no multi-step structure — the base_url swap is most of the benefit and the SDK adds little:

const openai = new OpenAI({
  baseURL: 'https://rfa-labs.com/v1',
  apiKey: process.env.SHIMMY_API_KEY,
});

Caching, routing and the quality gate all work. What you are missing is structure the optimizer can only guess at, and if your calls have no structure worth declaring, there is nothing to gain.

When it is worth it

Any of these means the edge is currently getting your structure wrong:

  • Concurrent work. Parallel sub-calls are recorded as a chain, and as a phantom loop when they share a step shape.
  • Context compaction. The moment history is trimmed, one run silently becomes two.
  • A summarizing agent. It never builds a prefix, so runs never link.
  • More than one replica. The run tracker is process-local; a five-step run can fragment into five one-step runs.
  • Prompt tuning. Editing a system prompt resets that step’s converged model search to zero.

And regardless of structure: reporting outcomes removes a paid LLM judge from the loop.

The migration, one rung at a time

Nothing here is all-or-nothing. A call with no annotations behaves exactly as it did before the SDK existed.

1. Wrap the client

const shimmy = new Shimmy();
const openai = shimmy.wrap(new OpenAI());   // instead of setting baseURL

Same routing you had. You can now read optimizerMeta(res) for per-call cost accounting, which the raw swap discards.

2. Scope your runs

Wrap the outermost handler. One line, and run identity stops being inferred.

await shimmy.run('handle-ticket', async () => { /* existing code */ });

3. Name your steps

Wherever you make a call, say what it is. This is where prompt edits stop resetting your accumulated optimization.

await shimmy.step('classify', { kind: 'classification' }, () => /* … */);

4. Report outcomes

The rung that pays for itself. You almost certainly already have the check:

const parsed = await shimmy.verify(() => JSON.parse(text));

Environment variables

OPTIMIZER_API_KEY and OPTIMIZER_BASE_URL are still honored, so nothing breaks on upgrade. SHIMMY_* takes precedence when both are set.

What does not change

  • Your provider keys. Still yours, still per-request.
  • The wire field. Annotations ride under optimizer, unchanged.
  • model: "auto". Still how you let the optimizer route; a pinned model is still an instruction that disables routing. model: "discover" goes further: it searches each step for the cheapest model that holds quality, on platform keys, once discovery is enabled on the agent.
  • Your client. wrap() returns the object you passed. Every OpenAI feature the SDK has never heard of keeps working.

Rolling back

Delete the run/step scopes and the SDK stops declaring anything; the edge returns to inference. Remove wrap() and set baseURL yourself, and you are exactly where you started. There is no stored state to unwind — annotations describe calls, they do not configure your account.