Function calling in code
Validate outputs and schemas
Make trust boundaries explicit: build a support assistant that asks a model when to call a read-only order-status function as a bounded, testable system whose behavior does not depend on trusting plausible model output.
Before you start
Why this matters
Before coding, write the single observable result this page must add to the previous page. Then name one failure that the implementation must reject. For Function calling in code, use the running project—not a toy explanation—as your test: a support assistant that asks a model when to call a read-only order-status function. Predict what the CLI, test, or HTTP client will observe when the page is complete.
1Learn the idea
Read
Make trust boundaries explicit
Validate environment variables at boot, user input before work, model-selected arguments before dispatch, and external output before use. A TypeScript annotation does not validate runtime JSON. Prefer strict schemas so unexpected keys reveal contract drift rather than flowing downstream.
Return useful validation errors internally while exposing a stable, safe error code to callers. Record the schema version with artifacts so queued or cached values can be migrated deliberately.
The most important invariant is that only an allowlisted function receives schema-validated arguments. Encode it as a parser or post-parse refinement, not as a comment. Reject empty collections, non-finite numbers, impossible enum values, excess lengths, unknown names, and mismatched identifiers according to this project’s actual contract. For this topic, deliberately feed a case involving unknown tool names, malformed arguments, repeated calls, and partial provider responses and verify that execution stops at the correct boundary.
Validation should preserve a machine-readable code such as invalid_input, invalid_output, or contract_drift, plus issue paths for operators. The public response should remain concise. If low confidence is valid, represent it explicitly and route it to review; do not convert uncertainty into invented certainty.
Read
Implementation
const parsed = OutputSchema.safeParse(raw);
if (!parsed.success) {
throw new ContractError("invalid_output", parsed.error.issues.map(i => ({
path: i.path.join("."), code: i.code
})));
}
return parsed.data;
The code is intentionally provider-neutral at the adapter boundary. AI_BASE_URL and model names are environment placeholders, not invented services. If the topic uses MCP, install the current official SDK and verify its exported paths against its release documentation. If a provider offers an official SDK, it can replace fetch inside execute without changing the domain contract. Always inspect the real provider’s documented request and response fields before connecting credentials.
Read
Debug the boundary
Debug from deterministic code outward. First print the parsed configuration with secret values replaced by [set]. Next run the parser against a local fixture. Then run the orchestration with a fake dependency. Only after those pass should you inspect the real boundary. Capture status, content type, request ID, elapsed time, and a redacted response shape. Do not “fix” malformed data with a permissive cast; save it as a failing fixture and decide whether the contract or adapter is wrong.
For this chapter, the fastest diagnostic is to assert that only an allowlisted function receives schema-validated arguments. When that assertion fails, stop before downstream work. Expected behavior is concrete: A request for ord_123 produces one validated lookup and a reply based only on its result. A different but plausible sentence is not enough if the structural and policy checks fail.
Read
Verification notes
- Unknown data is parsed at every trust boundary.
- Cross-field invariants are executable checks.
- Validation failures return stable safe codes.
- Run
npm run typecheckandnpm testwithout provider credentials. - Record expected terminal behavior beside the fixture so future changes remain reviewable.
A passing test is necessary but does not establish production quality. Review the fixture set for realistic distributions, stale assumptions, tenant boundaries, and expensive edge cases. When outputs are probabilistic, assert schemas, citations, chosen capabilities, stop reasons, and task rubric scores. Reserve exact string assertions for deterministic adapters and protocol framing.
Continue learning · glossary & guides
- Can you point to the executable check proving that only an allowlisted function receives schema-validated arguments?
- Does malformed or unauthorized data stop before external or privileged work?
- Is the observable success criterion exactly this clear: A request for ord_123 produces one validated lookup and a reply based only on its result.
- Glossary: tool · Glossary: structured output · Cheatsheet: production ops signals
- Previous · Next