dev → master: BLS macro tools + AI-readable network error classification#172
Merged
luokerenx4 merged 2 commits intomasterfrom May 9, 2026
Merged
dev → master: BLS macro tools + AI-readable network error classification#172luokerenx4 merged 2 commits intomasterfrom
luokerenx4 merged 2 commits intomasterfrom
Conversation
…k-through
Third macro source plugged into the economy tool namespace. AI agents
can now query BLS catalog + observations the same way they query FRED
or EIA, with provider name pinned at the tool boundary.
Tools added:
- economyBlsSearch — keyword filter against the curated BLS catalog
(BLS itself has no search API; the catalog is a
hand-maintained list in the typebb fetcher)
- economyBlsSeries — observations for one or more BLS series ids,
comma-separated for multi-series. Returns long
form (one row per date+series_id) — different
from FRED's pivot-by-date wide form, documented
in the tool description so the LLM doesn't
pattern-match incorrectly from FRED.
Wiring:
- EconomyClientLike: added getBlsSearch + getBlsSeries (SDK already
implements them at /survey/bls_*).
- tool/economy.ts: BLS_PROVIDER pin + 2 new tool factories.
- tool/economy.spec.ts: extended mock and added 9 schema /
passthrough / cross-client-isolation cases.
Bug fixed (5th in the macro-data string→number family):
- bls-series.ts: BLS returns "-" for unavailable observations
(e.g. UNRATE 2025-10 dropped during the 2025 lapse in
appropriations — footnote text in the wire response confirms).
parseFloat("-") returns NaN; the schema rejects NaN with
"Expected number, received nan" and the whole batch dies. Same
shape as the EIA fix: skip NaN rows instead of pushing them
through. Verified end-to-end against the live API; the 2025-10
row is now silently dropped rather than poisoning the result.
E2e: added a BLS describe block to market-data.e2e.spec.ts
(test-provider button + bls_search via /economy/survey/bls_search +
bls_series with explicit Number.isFinite check on every value to
catch any future NaN regression). 11 e2e cases now (5 FRED + 3 EIA
+ 3 BLS) all green against live APIs.
Verified live with the user's BLS key:
- BlsSearch "CPI": top 5 CPI sub-series (All Items, Core, Food at Home,
Gasoline, New Vehicles)
- UNRATE Jan 2025–Apr 2026: 4.3-4.5% range, stable; 2025-10 dropout
silently skipped
- CPI + Nonfarm Payrolls multi: CPI-U 2026-03 = 330.213, NFP 2026-04
= 158.7M total nonfarm payrolls
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lers
Tool errors propagate verbatim into the AI's next turn (Vercel AI SDK
forwards the raw thrown value into the tool-result message). When the
underlying fetch fails at the network layer — DNS / TLS / proxy /
routing — we were throwing the generic OpenBBError("Request failed
(TypeError: fetch failed): ..."), which looks indistinguishable to an
LLM from a transient flake. Models retry, hit the same wall again,
burn tokens, and never tell the user to check their network.
This change introduces a NetworkUnreachableError that classifies the
fetch-failure case and gives the LLM unambiguous signal:
NETWORK_UNREACHABLE: cannot reach <host> from this machine (<cause>).
This is a network-layer failure (DNS / routing / TLS / proxy), not a
provider error — the provider's API may well be operational, the
connection from this network cannot complete. Do NOT retry the same
call; ask the user to check their VPN / proxy routing for this
hostname, or fall back to a different data source.
Concrete impact on the BLS / EIA / FRED tools just landed: when a user
runs OpenAlice behind a Clash-style proxy with fake-ip routing, hosts
that aren't in the rule set TCP-connect to the local fake IP, then die
in TLS handshake — fetch surfaces this as TypeError("fetch failed")
with cause.code = ECONNRESET / SSL_ERROR_SYSCALL / ENOTFOUND. The AI
now sees the new classified error instead of "fetch failed", recognizes
this as user-fixable, and tells them so on the first failure rather
than after N silent retries.
Implementation:
- packages/opentypebb/src/core/provider/utils/errors.ts: new
NetworkUnreachableError extending OpenBBError. Carries the host as a
field for callers that want to render structured errors. Message
string is intentionally LLM-targeted prose, not just a code.
- packages/opentypebb/src/core/provider/utils/helpers.ts: amakeRequest
now branches on isFetchNetworkFailure (TypeError + "fetch failed"
message — the Node 18+ fetch wrapper's signature for network-layer
failures). Cause-code extraction (ENOTFOUND / ECONNREFUSED /
ETIMEDOUT / etc.) is included in the AI-visible message for
debugging signal. Timeouts (DOMException TimeoutError) and HTTP 4xx/
5xx (response received but provider-rejected) keep their existing
OpenBBError paths — both are recoverable in different ways than
network-unreachable.
- Tests: 6 new helpers.spec.ts cases covering TypeError("fetch failed"),
ENOTFOUND cause preservation, generic non-network errors, HTTP
non-2xx, and DOMException timeouts. The classifier is the security-
critical bit (false positive => AI gives up retryable work; false
negative => AI burns tokens), so the boundary cases are pinned.
- Re-exported NetworkUnreachableError from package index so OpenAlice-
side code (or any future consumer) can `instanceof` against it for
structured handling beyond just reading the message.
Verified end-to-end: simulated BLS host being unreachable through the
real tool stack (createEconomyTools → SDKEconomyClient → executor →
amakeRequest → fetch stubbed to throw TypeError("fetch failed")),
caught at the tool execute boundary as NetworkUnreachableError with
the AI-targeted message intact.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two follow-ups to the FRED + EIA macro-data work that landed in PR #171:
economy*AI tool namespace — third macro provider (after FRED + EIA), exposed aseconomyBlsSearch+economyBlsSeries. Fixes a 5th instance of the "BLS returns string sentinels for missing data, fetcher pushes NaN to schema" pattern that bit FRED / EIA earlier.NetworkUnreachableErrorwith explicit "do not retry" guidance, instead of the indistinguishable-from-flakeOpenBBError("Request failed (TypeError: fetch failed): ..."). Motivation: users behind Clash-style fake-ip proxies routinely hit this on US gov hosts (BLS in particular), and AI agents need to recognize "user network problem, escalate" vs "transient, retry".Per-session contributions
2026-05-09 — BLS macro tools + AI-readable network errors
feat(tool): expose BLS search + series as economy tools, fix NaN sneak-through(7fdcc71)economyBlsSearch(curated BLS catalog filter) andeconomyBlsSeries(timeseries fetch).EconomyClientLikeextended withgetBlsSearch/getBlsSeries;createEconomyToolssignature unchanged from PR dev → master: ephemeral UTAs + IBKR-as-truth scope correction + FRED tools #171.bls-series.tswas passingparseFloat('-')(BLS's sentinel for missing observations, e.g. UNRATE 2025-10 footnoted as "Data unavailable due to the 2025 lapse in appropriations") through to the schema as NaN, killing entire batches. Fix: skip NaN rows. Same shape as the EIA STEO + Petroleum Status fixes from PR dev → master: ephemeral UTAs + IBKR-as-truth scope correction + FRED tools #171.feat(opentypebb): classify network-unreachable errors for AI tool callers(39b014a)NetworkUnreachableError(extends OpenBBError) thrown byamakeRequestwhen fetch fails at the network layer (TypeError("fetch failed") with cause-code ENOTFOUND / ECONNREFUSED / ETIMEDOUT / TLS errors). Message text is intentionally LLM-targeted prose: containsNETWORK_UNREACHABLEtoken, the host, the underlying cause code, "Do NOT retry", and a user-side fix hint.Request failed (TypeError: fetch failed)and pattern-matches as transient flake → retries N times same-args. Post-fix the AI sees the classified error on first call and tells the user to check their proxy.OpenBBErrorpaths. End-to-end verified by stubbing fetch in a tool-execute call and observing the AI-visible message.amakeRequesthelper.Full commit log
Test plan
npx tsc --noEmitclean (root + ui workspaces)pnpm vitest run— 76 files / 1478 tests passing (was 1463 in PR dev → master: ephemeral UTAs + IBKR-as-truth scope correction + FRED tools #171; +9 BLS tool spec cases + 6 helpers spec cases)pnpm vitest run --config vitest.e2e.config.ts src/domain/market-data/__test__/e2e/— 11 cases now (5 FRED + 3 EIA + 3 BLS), all green against live APIs with the user's keystool.economyBlsSeries.execute→ caught at the boundary asNetworkUnreachableErrorwith the AI-visible message containingNETWORK_UNREACHABLE, the host, "Do NOT retry", and the user-fix hinteconomyBlsSearch/economyBlsSeriesfrom an Agent SDK session — should return real data🤖 Generated with Claude Code