Skip to content

Add support for input plane invocation#32

Merged
rculbertson merged 3 commits intomainfrom
ryan/input-plane-strategy
Jul 2, 2025
Merged

Add support for input plane invocation#32
rculbertson merged 3 commits intomainfrom
ryan/input-plane-strategy

Conversation

@rculbertson
Copy link
Contributor

@rculbertson rculbertson commented Jun 17, 2025

This is based on #31.

This PR adds a new InputPlaneInvocation class, which sends inputs to the input plane, rather than the control plane. This will be used only for function definitions which include the input_plane_region experimental option like this:

@app.function(experimental_options={"input_plane_region": "us-east"})

If not specified, the ControlPlaneInvocation will be used.

For now this includes only the typescript implementation - if this approach looks good, we will add the go implementation.

@rculbertson rculbertson force-pushed the ryan/control-plane-strategy branch from 291fad9 to de7c480 Compare June 17, 2025 01:40
@rculbertson rculbertson force-pushed the ryan/input-plane-strategy branch 4 times, most recently from 57703ea to 0ce79ee Compare June 17, 2025 19:37
@rculbertson rculbertson force-pushed the ryan/control-plane-strategy branch from cacbb31 to 23a98f7 Compare June 18, 2025 15:57
@rculbertson rculbertson force-pushed the ryan/input-plane-strategy branch from 0ce79ee to f65d93e Compare June 18, 2025 18:23
@rculbertson rculbertson changed the title Add input plane strategy Add support for input plane invocation Jun 18, 2025
@rculbertson rculbertson force-pushed the ryan/input-plane-strategy branch 2 times, most recently from a998493 to 48296fb Compare June 18, 2025 18:34
@rculbertson rculbertson force-pushed the ryan/control-plane-strategy branch from 75afd74 to 55e27f0 Compare June 18, 2025 18:36
@rculbertson rculbertson force-pushed the ryan/input-plane-strategy branch from 48296fb to 02763e7 Compare June 18, 2025 18:37
@rculbertson rculbertson requested a review from ekzhang June 18, 2025 18:37
@rculbertson rculbertson force-pushed the ryan/control-plane-strategy branch from 55e27f0 to eb9fcc6 Compare June 18, 2025 18:56
@rculbertson rculbertson force-pushed the ryan/input-plane-strategy branch from 02763e7 to 7bc2d75 Compare June 18, 2025 18:56
@rculbertson rculbertson force-pushed the ryan/control-plane-strategy branch 2 times, most recently from a999ab3 to 99f9f77 Compare June 18, 2025 19:43
@rculbertson rculbertson force-pushed the ryan/input-plane-strategy branch from 7bc2d75 to 14f9d4f Compare June 18, 2025 20:06
@rculbertson rculbertson force-pushed the ryan/control-plane-strategy branch from a38d9c2 to 90b362b Compare June 18, 2025 20:24
@rculbertson rculbertson force-pushed the ryan/input-plane-strategy branch from 14f9d4f to 56efc9b Compare June 19, 2025 20:35
Base automatically changed from ryan/control-plane-strategy to main June 20, 2025 15:06
@rculbertson rculbertson force-pushed the ryan/input-plane-strategy branch 4 times, most recently from 4d45866 to 23652f8 Compare June 20, 2025 15:44
@rculbertson
Copy link
Contributor Author

The PR this was based on has been merged to main. This PR has been rebased on main, and is ready for review.

import { ClientType, ModalClientDefinition } from "../proto/modal_proto/api";
import { type Profile, profile } from "./config";

let modalAuthToken: string | undefined;
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a global variable right now and will end up shared between multiple clients. Instead I think it needs to be declared in the authMiddleware() factory between line 19 function authMiddleware(...) and line 20 return async function* authMiddleware(...), so it doesn't end up becoming accidentally shared global state.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We actually intentionally share the token between the control plane client and the input plane client. Here's the flow:

  1. We call the control plane, and do not specify an auth token (because we don't have one yet)
  2. The control plane sees there is no auth token - so it generates one, and returns it to the client
  3. The client the specifies that auth token in every subsequent request to both the control plane and the input plane.
    I will add a comment explaining this.

Comment on lines 42 to 44
if (prevOnHeader) {
prevOnHeader(header);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

prevOnHeader?.(header);

Comment on lines 110 to 104
} catch (err) {
throw new Error(`FunctionGetOutputs failed: ${err}`);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we should catch this error this way since it erases the error details and status code from the structured Status response, and turns it into a string. Also the grpc error already has the method name in the stack, so this isn't necessary.

My thought process on errors is, in theory we would have every single possible turn into a human-readable format that is intelligible to users. In practice there are a lot of unanticipated, and the set of actual errors that users experience in practice is much smaller and more tractable to manage — so we let errors propagate by default, and we catch errors strategically based on which ones are actually expected to happen from library use (NotFoundError when you get the name of a function wrong, OutputExpired when you poll too late, etc.) and make them public API instead of an opaque Error type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds reasonable to me! Will remove the catch.


@app.function(min_containers=1, experimental_options={"input_plane_region": "us-west"})
def input_plane(s: str) -> str:
return "output: " + s
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: can you keep formatting with black?

Comment on lines 32 to 38
/**
* Signature of a function that fetches a single output. Used by `pollForOutputs` to fetch from either
* the control plane or the input plane, depending on the implementation.
*/
type GetOutput = (
timeoutMillis: number,
) => Promise<FunctionGetOutputsItem | undefined>;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think if we make a limited utility type like this that's used in one place, we should define it next to where it's used in pollFunctionOutput().

The documentation/naming on this method and type pollFunctionOutput() and GetOutput could be improved. It's not clear that what it's actually doing right now is, wrapping an output fetcher that's limited to the outputsTimeout constant (55 s) with a loop and continued polling so that it takes any timeout.

Also the naming is different between Go and JS of the interface, the identifiers called awaitOutput() / GetOutput / pollFunctionOutput() in JS correspond to getOutput() / getOutput / pollFunctionOutput() in Go, which is a bit confusing too because of the double-naming.

Comment on lines 67 to 75
// Attach x-modal-auth-token to all future requests.
authTokenArray := header.Get("x-modal-auth-token")
if len(authTokenArray) == 0 {
authTokenArray = trailer.Get("x-modal-auth-token")
}
if len(authTokenArray) > 0 {
authToken := authTokenArray[0]
ctx = metadata.AppendToOutgoingContext(ctx, "x-modal-auth-token", authToken)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Also isn't this middleware implementation … different from how x-modal-auth-token is being handled in modal-js? One of them will save auth tokens per client/globally, and one will save per function. I think it's really important that we try to make the client libraries behave the same way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good. I will update the go code to match the typescript.

@rculbertson rculbertson force-pushed the ryan/input-plane-strategy branch 3 times, most recently from c3bcd2d to 90673b6 Compare June 30, 2025 15:54
@rculbertson rculbertson requested review from ehdr and ekzhang June 30, 2025 20:52
@rculbertson rculbertson force-pushed the ryan/input-plane-strategy branch from f1f43a9 to 9fff513 Compare June 30, 2025 20:54
@rculbertson rculbertson force-pushed the ryan/input-plane-strategy branch 3 times, most recently from 1315218 to 3635731 Compare June 30, 2025 22:15
@rculbertson rculbertson force-pushed the ryan/input-plane-strategy branch from 3635731 to c8fe4fb Compare June 30, 2025 22:17
@rculbertson
Copy link
Contributor Author

I addressed the PR comments, so this is ready for another review!

Copy link
Contributor

@ehdr ehdr left a comment

Choose a reason for hiding this comment

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

This looks good to me!

});
return response.output;
} catch (err) {
throw new Error(`AttemptAwait failed: ${err}`);
Copy link
Contributor

Choose a reason for hiding this comment

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

This should maybe also not be caught, with similar reasoning as #32 (comment)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, yes! I pushed another commit removing that.

@rculbertson rculbertson force-pushed the ryan/input-plane-strategy branch from 79ff102 to 1b26949 Compare July 2, 2025 14:09
@rculbertson
Copy link
Contributor Author

Thank you @ekzhang and @ehdr for reviewing!

@rculbertson rculbertson merged commit 94d225f into main Jul 2, 2025
5 checks passed
@rculbertson rculbertson deleted the ryan/input-plane-strategy branch July 2, 2025 14:14
@ekzhang
Copy link
Contributor

ekzhang commented Jul 2, 2025

Yes, and sorry for any delays!

billyb2 pushed a commit to depot/libmodal that referenced this pull request Jul 16, 2025
* Add support for input plane

* Address PR review comments

* Remove catch
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.

3 participants