Skip to content

feat: Replace async logic with waitSecs parameter for call-actor#825

Merged
MQ37 merged 26 commits into
feat/get-actor-run-wait-secfrom
feat/async-call-actor
May 18, 2026
Merged

feat: Replace async logic with waitSecs parameter for call-actor#825
MQ37 merged 26 commits into
feat/get-actor-run-wait-secfrom
feat/async-call-actor

Conversation

@jirispilka
Copy link
Copy Markdown
Collaborator

@jirispilka jirispilka commented May 11, 2026

I'm really sorry :(. This again a heavier PR than desired but I had to touch so many different things that the split would be really painful to test.

While testing, I was so happy with the experience that I think we should push direct Actor tools in this mode too.
This is a follow up issue: #852 (there is an issue with outputSchema, see the issue for details)

Summary

  • Replace async param with waitSecscall-actor no longer has an async: true/false flag. Callers control blocking via waitSecs (0 = fire-and-forget, >0 = wait up to N seconds, default 30). get-actor-run similarly accepts waitSecs for polling.

  • Reshape storages to plural alias-mapRunResponse.storages now mirrors the Apify client's ActorRunStorageIds structure: { datasets: { default: {...} }, keyValueStores: { default: {...} } } instead of the flat { dataset, keyValueStore } shape. The plural alias-map means named Actor storages (e.g. storages.datasets.results) can be added without introducing new field names, keeping the schema stable as the platform evolves.

  • Rename callActorOutputSchemadirectActorOutputSchema (and buildEnrichedCallActorOutputSchemabuildDirectActorOutputSchemaWithItems). This schema was previously declared on both call-actor and direct actor tools, but with v4 call-actor moved to getActorRunOutputSchema (the canonical RunResponse shape). The schema is now used only by dynamically-registered direct actor tools (e.g. apify--rag-web-browser) that wrap a single actor and return inline dataset items via buildActorResponseContent. Renamed for accuracy; stale fields (actorName, status, startedAt, input) that were never populated by the runtime were dropped.

Closes #824


jirispilka and others added 4 commits May 11, 2026 13:56
Apply the same content[]=[json, narrative] pattern to call-actor's
canonical-shape sites that landed on #823 for get-actor-run:

- src/tools/core/actor_run_response.ts:buildStartRunResponse
- src/tools/default/call_actor.ts (terminal-after-wait return)

structuredContent itself is unchanged. The narrative (summary +
nextStep) moves from content[0] to content[1]; content[0] now
carries JSON.stringify(structuredContent) per the spec
backwards-compat recommendation.

Closes the call-actor side of the conformance gap tracked by
#709/#744. Direct actor tools still pending (Phase 2 of #744).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jirispilka jirispilka force-pushed the feat/async-call-actor branch from 3a6a252 to 712b145 Compare May 11, 2026 12:06
Resolves conflicts after #823 (waitSecs on get-actor-run), #820 (extra
callOptions fields), and #842 (unused-export cleanup) landed on master:

- callOptionsSchema centralized in call_actor_common.ts; build,
  maxItems, maxTotalChargeUsd added (kept from #820). Both call-actor
  and call-actor-widget reuse the shared schema.
- waitForRunWithProgress: combine the parallel actorNamePromise (#823)
  with the caller-supplied actorName short-circuit (this PR), so
  call-actor skips the lookup it already knows the answer to.
- KV_KEYS_LIMIT export dropped (#842); v4 plural comment kept.
- previewOutput description flipped to "Deprecated: ignored" per v4
  spec (preview is agent-driven via get-dataset-items).
- Tests: kept memory-quota recovery + callOptions parsing tests from
  master alongside the public-search-helper test from this branch.
  Dropped obsolete isAsync arg (v4 removed the field).
- get-actor-run.response test: two leftover singular
  storages.dataset accesses migrated to storages.datasets.default.

This comment was marked as outdated.

@jirispilka jirispilka marked this pull request as ready for review May 15, 2026 22:34
@jirispilka jirispilka requested a review from MQ37 May 15, 2026 22:34
@jirispilka jirispilka self-assigned this May 16, 2026
@jirispilka jirispilka added the t-ai Issues owned by the AI team. label May 16, 2026
Copy link
Copy Markdown
Collaborator Author

jirispilka commented May 16, 2026

E2E test report (mcpc @ 3366da9)

All groups ✅:

  • A — Schema/validation: waitSecs int 0–45 enforced on both tools; async flag gone; boundary rejections return MCP -32602.
  • B — Non-task call-actor: default waits to SUCCEEDED; waitSecs=0 → READY + storage IDs; short waitSecs → RUNNING; abort → ABORTED template.
  • C — Non-task get-actor-run: snapshot / blocking / bad-runId all correct.
  • D — Task mode: lifecycle working → completed with actor-prefixed statusMessage; tasks-cancel aborts the platform run; waitSecs correctly ignored (caller passes 1, still reaches SUCCEEDED); soft-fails delivered with isError:true.
  • E — MCP spec: content[0] byte-equivalent to structuredContent; both tools' structuredContent Ajv-validates against their outputSchema; namespaced _meta (com.apify/ActorRun, io.modelcontextprotocol/related-task).
  • F — Storages: alias-map storages.{datasets,keyValueStores}.default.* everywhere; no flat keys.
  • G — Status templates: READY / RUNNING / SUCCEEDED-with-items / ABORTED render with attributed statusMessage. NEW SUCCEEDED + itemCount=0 now points at get-dataset-items (covered by updated unit test).
  • H — Repo health: type-check / lint / test:unit (688/688).

Session: https://claude.ai/code/session_01LhoaTxrwVZiT44LWeTNfpw

Copy link
Copy Markdown
Contributor

@MQ37 MQ37 left a comment

Choose a reason for hiding this comment

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

LGTM! 👍

`);
} else {
sections.push(dedent`
const WIDGET_ADDENDUM = dedent`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: never in my life I saw word "addendum" in a code, but let's keep it 😄

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

image :D

Follow-up from #825 Direct Actor tools now share the same contract as
`call-actor` and `get-actor-run`. Finally one uniform experience, this
is what I was looking forward to when writing #825 :)

Much smaller PR than #825 this time, just the migration and merging two
executors into one. It actually removes some code, which is good.

About the `itemsSchema` gotcha called out in #852 — LLMs may read
\`itemsSchema.properties\` as "these fields are in
\`structuredContent\`", which they are not. Rows live in the dataset and
you fetch them with \`get-dataset-items\`. The
\`itemsSchema.description\` says this explicitly, so it should be fine.

I tested it, it works but I did not tested thoroughly, there might be
some rough edges.

## Summary

- **Direct Actor tools now return the canonical \`RunResponse\` shape**
— same as \`call-actor\` and \`get-actor-run\`: \`runId\`, \`status\`,
\`storages\`, \`summary\`, \`nextStep\`. No more inlined items; the LLM
follows \`nextStep\` to \`get-dataset-items\`.

- **Same \`waitSecs\` contract as \`call-actor\`** — same MCP-only
opt-in: default 30 s, max 45 s. Task mode overrides \`waitSecs\` and
waits until terminal. If the Actor's own input schema happens to declare
\`waitSecs\`, ours wins so there's no collision.

- **Per-tool \`itemsSchema\` in the dataset metadata** — for direct
Actor tools the target Actor is known at \`tools/list\` time, so the
historical-inferred row schema lands both in the tool's \`outputSchema\`
and in the runtime response under
\`storages.datasets.default.itemsSchema\`. LLMs can plan a \`fields\`
projection before the first call.

- **One mode-agnostic executor** — separate default-mode and apps-mode
executors are gone, replaced by a single one. Direct Actor tools never
render widgets (\`widget: false\`), so the apps-mode executor was not
doing anything different anymore.

Closes #852. Phase 2 of #587.

---------

Co-authored-by: Jakub Kopecký <themq37@gmail.com>
@MQ37 MQ37 merged commit 889c41f into feat/get-actor-run-wait-sec May 18, 2026
9 checks passed
@MQ37 MQ37 deleted the feat/async-call-actor branch May 18, 2026 14:54
MQ37 added a commit that referenced this pull request May 18, 2026
`get-actor-run` returns the canonical run shape (status, storages,
stats, summary, nextStep) across all 8 Apify states. Text content
carries `summary \n nextStep`; structuredContent has the full shape — no
inlined items or record bodies.

Adds `waitSecs` (0–45, default 30) and emits `notifications/progress`
during the wait when `_meta.progressToken` is supplied.
`get-actor-run-widget` uses `waitSecs=0` so initial render is
non-blocking; widget self-polls.

`SUCCEEDED` with `itemCount=0` runs a one-item probe with one retry to
absorb Apify's ~5s pagination-counter lag — no false "no output" on a
run that did write items. KV shows up in `summary` only; never
recommended as `nextStep`, since it rarely carries real output (mostly
SDK state).

Follow-up PR #825 replaces `call-actor`'s `async` flag with the same
`waitSecs` and reshapes `storages` to a plural alias-map. Stacks on this
branch and lands together.

**Follow-up #847** — consolidate duplicated MCP server test fixtures
across three test files (surfaced during review); left out per "one
thing per change".

## Why this PR is bulky

Splitting the pieces below would leave master in a broken half-state:

- New shared module `src/tools/core/actor_run_response.ts` centralizes
the canonical shape, 8 status templates, wait+progress loop,
lag-fallback probe, and abort race. #825 (`call-actor` v4) reuses every
helper.
- Test coverage for the new contract — shape, 8 templates,
wait/progress, abort, lag-fallback.
- Widget UI in `src/web/src/pages/ActorRun/ActorRun.tsx` switched to a
`get-dataset-items` preview fetch with its own polling cadence — v4
dropped inlined items, so the UI is broken until the server stops
inlining.
- Spec, structured-output schema, tool-loader auto-inject, and widget
dev stub round out the v4 contract across modes.

Closes #822

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Jakub Kopecký <themq37@gmail.com>
RobertCrupa pushed a commit that referenced this pull request May 19, 2026
`get-actor-run` returns the canonical run shape (status, storages,
stats, summary, nextStep) across all 8 Apify states. Text content
carries `summary \n nextStep`; structuredContent has the full shape — no
inlined items or record bodies.

Adds `waitSecs` (0–45, default 30) and emits `notifications/progress`
during the wait when `_meta.progressToken` is supplied.
`get-actor-run-widget` uses `waitSecs=0` so initial render is
non-blocking; widget self-polls.

`SUCCEEDED` with `itemCount=0` runs a one-item probe with one retry to
absorb Apify's ~5s pagination-counter lag — no false "no output" on a
run that did write items. KV shows up in `summary` only; never
recommended as `nextStep`, since it rarely carries real output (mostly
SDK state).

Follow-up PR #825 replaces `call-actor`'s `async` flag with the same
`waitSecs` and reshapes `storages` to a plural alias-map. Stacks on this
branch and lands together.

**Follow-up #847** — consolidate duplicated MCP server test fixtures
across three test files (surfaced during review); left out per "one
thing per change".

## Why this PR is bulky

Splitting the pieces below would leave master in a broken half-state:

- New shared module `src/tools/core/actor_run_response.ts` centralizes
the canonical shape, 8 status templates, wait+progress loop,
lag-fallback probe, and abort race. #825 (`call-actor` v4) reuses every
helper.
- Test coverage for the new contract — shape, 8 templates,
wait/progress, abort, lag-fallback.
- Widget UI in `src/web/src/pages/ActorRun/ActorRun.tsx` switched to a
`get-dataset-items` preview fetch with its own polling cadence — v4
dropped inlined items, so the UI is broken until the server stops
inlining.
- Spec, structured-output schema, tool-loader auto-inject, and widget
dev stub round out the v4 contract across modes.

Closes #822

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Jakub Kopecký <themq37@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t-ai Issues owned by the AI team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants