Skip to content

Fix Agent Flow evidence schema fallback - #286

Closed
NomadDigita wants to merge 0 commit into
mainfrom
codex/repair-flow-provider-schema-20261021
Closed

NomadDigita wants to merge 0 commit into
mainfrom
codex/repair-flow-provider-schema-20261021

Conversation

@NomadDigita

Copy link
Copy Markdown
Member

Summary

  • Tell the Agent Flow analyst that each evidence point needs both text and exact citedUrls, matching the existing validator.
  • Retry one schema-invalid response within the same bounded provider deadline, allowing the configured fallback to run.
  • Add focused tests for the prompt contract and provider retry.

Context

Production logs showed Gemini returning complete JSON, but Agent Flow rejected the response shape. The prompt named evidence fields without the per-point text key. Qwen also timed out on the observed batch.

Verification

  • Vercel preview and test status will be checked after opening.

@vercel

vercel Bot commented Oct 21, 2026

Copy link
Copy Markdown

Deployment failed for project tidelight with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/tessera8?upgradeToPro=build-rate-limit

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-21T09:57:12.157088Z d22a27a PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@NomadDigita
NomadDigita force-pushed the codex/repair-flow-provider-schema-20261021 branch from 6f2f571 to d75c711 Compare October 21, 2026 09:50

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d22a27a304

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread app/api/research/agent-flow/route.ts Outdated
Comment on lines +150 to +152
return { ticker: snap.ticker, issuer: snap.issuer, summary: assessment.summary, stance: assessment.stance, confidence: assessment.confidence, confidenceCapped: assessment.confidenceCapped ?? false, citedUrls: assessment.citedUrls, supportingEvidence: assessment.supportingEvidence, counterEvidence: assessment.counterEvidence, risks: assessment.risks, nextCheck: assessment.nextCheck, tokenizedMarket: marketReview, markets: { spot: snap.spot, perp: snap.perp }, tradeGate: { tradeable: gate.tradeable, direction: gate.direction, reasons: gateReasons }, destinations, sources: snap.sources, researchRunId: run.id };
});
stage("Coordinator", "complete", "Review ready", assets.some(asset => asset.tradeable) ? "One or more scenarios may proceed to a separate trade review. Nothing was sent to Bitget or the paper ledger." : "Keep watching the evidence and market. No scenario passed every trade review gate.");
const result = { researchRunId: run.id, question, researchCoverage: { mode: "deep", lookbackDays: 30, queriesPerTicker: 8, collectedItems: snapshots.reduce((count, snap) => count + snap.sources.length, 0), publishers: [...new Set(snapshots.flatMap(snap => snap.sources.map(source => source.publisher)))].length, sourceDomains: [...new Set(snapshots.flatMap(snap => snap.sources.map(source => new URL(source.url).hostname)))].length, collectedAt: new Date().toISOString(), limitations: ["Headlines and snippets are discovery references, not full article reads.", "Public-feed coverage is bounded and cannot represent the entire internet."] }, assets, tradeable: assets.some(asset => asset.tradeable), trace, contextRunId, ...(workspaceContext ? { workspaceContext } : {}) };
const { error: saveError } = await supabase.from("research_runs").update({ status: "complete", completed_at: new Date().toISOString(), summary: result }).eq("id", run.id);
if (saveError) {
stage("Coordinator", "blocked", "Private trace could not be saved", "No trade handoff was offered because the research record was not committed.");
throw new Error(`research-save-failed:${saveError.code}`);
} else push("result", result);
} catch (cause) {
console.error("agent-flow-failed", cause instanceof Error ? cause.message : "unknown");
await supabase.from("research_runs").update({ status: "failed", completed_at: new Date().toISOString(), summary: { trace, error: "The market or research workflow did not complete." } }).eq("id", run.id);
push("error", { error: "The research workflow could not finish. No paper or live order was placed." });
const summary = { question, collectedAt: new Date().toISOString(), agentTrace: trace, assets };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve the established Agent Flow result contract

On every successful run, this payload renames required fields (trace to agentTrace, market to markets, and tradeable/gateReasons/handoff to tradeGate/destinations) and omits top-level researchRunId and tradeable. Consequently, parseIntelligenceResult rejects every result in intelligence-provider.tsx, while the direct Agent Flow desk can crash when it dereferences asset.market; return the schema expected by the existing consumers rather than introducing an uncoordinated replacement.

Useful? React with 👍 / 👎.

Comment thread app/api/research/agent-flow/route.ts Outdated
} catch (error) {
console.error("agent-flow-error", error);
stage("Coordinator", "blocked", "Research run failed", error instanceof Error ? error.message : "The research run could not complete.");
await supabase.from("research_runs").update({ status: "error", summary: { question, error: "The research run could not complete. Retry shortly.", agentTrace: trace } }).eq("id", run.id);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Store failed runs with an allowed status

When any source, persistence, or workflow exception reaches this handler, the update attempts to write status: "error", but the research_runs constraint in 20261004104708_initial_tidelight_research_schema.sql permits failed rather than error. Because the update error is ignored, affected records remain stuck in collecting or analyzing with no saved failure summary or completion timestamp.

Useful? React with 👍 / 👎.

Comment thread lib/ai-fallback.ts Outdated
function jsonObject(raw: string): Record<string, unknown> | null {
try {
const clean = raw.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "");
const clean = raw.trim().replace(/^\`\`\`(?:json)?\\s*/i, "").replace(/\\s*\`\`\`$/, "");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore whitespace matching around fenced JSON

If a provider returns valid JSON inside a conventional fenced block such as `````json\n{"ok":true}\n``` ``, these expressions now look for the literal characters \s instead of whitespace, so neither fence is removed and `JSON.parse` rejects the response. This unnecessarily triggers retries or provider fallback for otherwise valid model output.

Useful? React with 👍 / 👎.

Comment thread lib/ai-fallback.ts Outdated
failures.push(`${provider.label}-invalid-response`);
// A well-formed but schema-invalid response can improve on retry;
// retries remain bounded by the same provider and route deadlines.
if (attempt === 0 && timeoutMs > 9000) continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow schema-invalid responses to retry under the default budget

With both providers configured and the default 20-second budget, the primary provider receives roughly one third of the remaining budget (about 6.7 seconds), making timeoutMs > 9000 false even when its first response is immediately schema-invalid. The newly added retry behavior therefore does not run for default callers, and the accompanying schema-invalid primary response is retried test fails with one Qwen call instead of two.

Useful? React with 👍 / 👎.

Comment thread app/api/research/agent-flow/route.ts Outdated
};
try {
const tokenizedMarketQuestion = /\br(?:[A-Z]{1,6})(?:usdt)?\b/i.test(question) || /\b(tokenized|tokenised|reality market|r-token|rtoken)\b/i.test(question);
const tokenizedMarketQuestion = /\\br(?:[A-Z]{1,6})(?:usdt)?\\b/i.test(question) || /\\b(tokenized|tokenised|reality market|r-token|rtoken)\\b/i.test(question);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore word boundaries in tokenized-market detection

For normal questions containing rNVDA, tokenized, reality market, or r-token, both expressions now search for literal \b text rather than regex word boundaries, so tokenizedMarketQuestion remains false. Those requests consequently receive the generic AI scope, the source-only fallback omits the rToken-specific limitations, and tokenizedMarketReview is skipped even though the user explicitly asked about the tokenized instrument.

Useful? React with 👍 / 👎.

Comment thread app/api/research/agent-flow/route.ts Outdated
await supabase.from("research_runs").update({ status: "failed", completed_at: new Date().toISOString(), summary: { trace, error: "The market or research workflow did not complete." } }).eq("id", run.id);
push("error", { error: "The research workflow could not finish. No paper or live order was placed." });
const summary = { question, collectedAt: new Date().toISOString(), agentTrace: trace, assets };
const { error: saveError } = await supabase.from("research_runs").update({ status: "complete", summary }).eq("id", run.id);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Record the completion timestamp for successful runs

Successful runs are now marked complete without populating completed_at. This leaves completed records without lifecycle metadata and makes /api/research/replays substitute the later publication time for capturedAt, so a replay published hours or days afterward misstates when its market snapshot and research were captured.

Useful? React with 👍 / 👎.

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