Skip to content

fix(prediction): align PredictionMarket tool with Predexon v2 schema#62

Merged
1bcMax merged 2 commits into
mainfrom
fix/prediction-market-predexon-v2-schema
May 21, 2026
Merged

fix(prediction): align PredictionMarket tool with Predexon v2 schema#62
1bcMax merged 2 commits into
mainfrom
fix/prediction-market-predexon-v2-schema

Conversation

@KillerQueen-Z
Copy link
Copy Markdown
Collaborator

Summary

The PredictionMarket tool was modeled on the public Polymarket Gamma / Kalshi API field conventions, but the data actually comes from Predexon's normalized v2 schema behind the BlockRun gateway (the gateway is a pure pass-through proxy). Hand-written types cast from unknown gave no compile-time signal, and the local test suite makes no API calls — so every field/param mismatch was invisible until a live call. This PR aligns all 10 actions to the real schema (verified against openapi-v2.json and live gateway responses on 2026-05-20).

Bugs fixed (9)

# Action Symptom Root cause
1 searchPolymarket 422 on every call status defaulted to active; Predexon enum is {open, closed}
2 searchPolymarket / searchKalshi all metrics n/a real fields are total_volume_usd/liquidity_usd/outcomes[].price (PM) and last_price (Kalshi), not volume/outcome_prices[]/yes_bid
3 crossPlatform blank titles venues are UPPERCASE nested objects POLYMARKET/KALSHI{title} (KALSHI nullable)
4 leaderboard "no data" + n/a rows under entries, stats under metrics.*, address is user; sort should be sort_by enum
5 smartActivity 400 every call requires ≥1 smart-wallet criterion; field is smart_wallet_count
6 smartMoney 400, then wrong shape requires criterion; response is a single positioning aggregate, not buyers/sellers arrays
7 smartMoney 404 when chained condition_id truncated to 14 chars in search/activity output, so the agent could only pass a partial id
8 searchAll / searchKalshi status synonyms rejected active normalization missing
9 agent loop spun to 50-call cap external-wall guard didn't treat 400/422 as retry-useless (and 422s aren't billed, so the cost guard stayed idle)

Why it mattered

Before: a simple "compare Fed-cut odds on Polymarket vs Kalshi" 422'd, returned n/a, or pushed the agent to bash-curl the public Gamma API (forbidden by the system prompt) — burning ~$0.30 and sometimes freezing the terminal under the render churn of a runaway loop.

After (live-verified through the real agent):

  • searchPolymarket → real volumes + implied odds, top-5 by volume
  • leaderboard → real wallets with P&L / win rate
  • smartActivity → real markets with smart-wallet count / volume / net-buyer %
  • smartMoney → full positioning aggregate (chained from a real condition_id)
  • Polymarket vs Kalshi → 1.8% vs ~5% spread with arbitrage read

Testing

  • npm test — 405 pass, 0 fail
  • tsc --noEmit clean
  • All 10 actions invoked against the live gateway; the 4 article cases run end-to-end through franklin start -p.

Notes

  • No version bump / CHANGELOG entry — left for a separate release commit.
  • The fix touches only response/param mapping; endpoint paths and pricing are unchanged.

The PredictionMarket tool was written against the public Polymarket
Gamma / Kalshi API field conventions, not Predexon's normalized v2
schema that actually sits behind the BlockRun gateway. Hand-written
types cast from `unknown` gave no compile-time signal, and the local
test suite makes no API calls, so every mismatch was invisible until a
live call. All field names below verified against openapi-v2.json and
live gateway responses (2026-05-20).

Fixes:
- searchPolymarket: status defaulted to `active`, but Predexon's
  StatusOption enum is {open, closed} → 422 on every call. Add
  normalizeMarketStatus() (active/live→open, archived/resolved→closed);
  default `open`. Same normalization applied to searchKalshi/searchAll.
- searchPolymarket/searchKalshi metrics rendered `n/a`: real fields are
  total_volume_usd / liquidity_usd / outcomes[].price (nested) for
  Polymarket and last_price for Kalshi — not volume/liquidity/
  outcome_prices[]/yes_bid/yes_ask. Parse the real shapes (legacy names
  kept as fallbacks).
- crossPlatform rendered blank titles: venues are UPPERCASE nested
  sub-objects (POLYMARKET/KALSHI{title,...}), KALSHI nullable. Resolve
  via pickString over the sub-object.
- leaderboard returned "no data": rows live under `entries` (added to
  unwrapList) with stats nested under `metrics.*` and the address in
  `user`; the flat reads returned undefined for every row. Also map
  the `sort` input to Predexon's real `sort_by` enum.
- smartActivity/smartMoney 400'd every call: both require at least one
  smart-wallet criterion — inject a default min_total_pnl. smartMoney
  also returns a single `positioning` aggregate, not buyers/sellers
  arrays — rewrite the formatter. smartActivity field is
  smart_wallet_count (singular).
- condition_id was truncated to 14 chars in searchPolymarket/
  smartActivity/searchAll output, so the agent could only chain a
  partial id into smartMoney → 404. Emit the full condition_id.

All 10 actions now verified end-to-end against the live gateway.
The external-wall failure guard breaks a turn after 5 consecutive
"retrying won't help" failures, but only matched 401/403/429/5xx. A
422 (failed param validation) was charged-free AND unmatched here, so a
PredictionMarket status=active 422 spun the agent to the 50-call
HARD_TOOL_CAP — neither the cost guard (idle, 422s aren't billed) nor
the wall guard fired. Add 400/422 to the pattern. 404 is intentionally
excluded — "not found" is a legitimate cue to retry with a different
query, not a dead wall.
@1bcMax 1bcMax merged commit 724b483 into main May 21, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant