Skip to content

Fix Runner service defaults#474

Merged
k82cn merged 2 commits into
xflops:mainfrom
k82cn:codex/fix-runner-instance-session
May 27, 2026
Merged

Fix Runner service defaults#474
k82cn merged 2 commits into
xflops:mainfrom
k82cn:codex/fix-runner-instance-session

Conversation

@k82cn
Copy link
Copy Markdown
Contributor

@k82cn k82cn commented May 27, 2026

Summary

  • derive Runner service stateful from execution object type and remove it from the public Runner.service API
  • keep autoscale configurable for functions/classes, while object instances are always stateful and fixed
  • update SDK/e2e tests and Runner docs for the new defaults, including object warmup validation

Verification

  • git diff --check
  • cd sdk/python && uv run --extra dev pytest -q tests/test_runner.py::test_runner_context_defaults_by_execution_object tests/test_runner.py::test_runner_service_passes_public_options tests/test_runner.py::test_runner_service_rejects_unsupported_options tests/test_runner.py::test_runner_context_rejects_unsupported_options tests/test_runner.py::test_runner_service_does_not_expose_stateful tests/test_runner.py::test_runner_service_rejects_stateful_keyword tests/test_runner.py::TestRunnerContext::test_runner_context_stateful_with_function_raises tests/test_runner.py::TestRunnerContext::test_runner_context_stateful_with_class_raises
  • local podman flame-console: python3 -m pytest -vv --durations=0 tests/test_runner.py tests/test_flmexec.py with patched SDK copied into the console container

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the Runner API to automatically derive service statefulness and autoscaling behavior based on the type of the execution object, removing the explicit stateful parameter. Functions, builtins, and classes are now strictly stateless and default to autoscaling, while object instances are stateful, fixed-size, and restrict warmup values to 0 or 1. The feedback highlights a discrepancy in how function-like objects are identified between RunnerService and RunnerContext (which would break support for functools.partial), and recommends adding validation for non-negative warmup values along with corresponding unit tests.

Comment thread sdk/python/src/flamepy/runner/types.py Outdated
Comment on lines +99 to +101
is_function = inspect.isroutine(self.execution_object)
is_class = inspect.isclass(self.execution_object)
is_object = not is_function and not is_class
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.

high

There is a discrepancy between how RunnerService (in runner.py) and RunnerContext (in types.py) identify function-like execution objects.

In runner.py (line 223), an execution object is treated as a function if it is callable and not a class:

callable(self._execution_object) and not inspect.isclass(self._execution_object)

However, in types.py (line 99), RunnerContext uses inspect.isroutine:

is_function = inspect.isroutine(self.execution_object)

Because inspect.isroutine returns False for callable objects like functools.partial or custom functors, RunnerContext will incorrectly classify them as object instances (is_object = True). This forces them to be stateful and fixed-size (autoscale = False), whereas RunnerService will wrap them as functions.

To resolve this discrepancy and support common callable wrappers like functools.partial, RunnerContext should use the same callable check as RunnerService.

Suggested change
is_function = inspect.isroutine(self.execution_object)
is_class = inspect.isclass(self.execution_object)
is_object = not is_function and not is_class
is_function = callable(self.execution_object) and not inspect.isclass(self.execution_object)
is_class = inspect.isclass(self.execution_object)
is_object = not is_function and not is_class

Comment on lines 97 to 98
def __post_init__(self) -> None:
"""Compute min/max instances and validate configuration."""
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.

medium

Negative values for warmup are currently not validated and are silently treated as 0 (which defaults min_instances to 0 or 1). We should explicitly validate that warmup is a non-negative integer to prevent invalid configurations.

Suggested change
def __post_init__(self) -> None:
"""Compute min/max instances and validate configuration."""
def __post_init__(self) -> None:
"""Compute min/max instances and validate configuration."""
if self.warmup < 0:
raise ValueError("warmup must be a non-negative integer.")

Comment on lines +446 to +462
contexts = [
RunnerContext(sample_func),
RunnerContext(len),
RunnerContext(SampleService),
RunnerContext(SampleService, autoscale=False, warmup=2),
RunnerContext(SampleService()),
RunnerContext(SampleService(), warmup=1),
]

assert [(ctx.stateful, ctx.autoscale, ctx.warmup, ctx.min_instances, ctx.max_instances) for ctx in contexts] == [
(False, True, 0, 0, None),
(False, True, 0, 0, None),
(False, True, 0, 0, None),
(False, False, 2, 2, 2),
(True, False, 0, 1, 1),
(True, False, 1, 1, 1),
]
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.

medium

Add a test case for functools.partial to verify that callable wrappers are correctly identified as stateless, autoscaling function-like services.

Suggested change
contexts = [
RunnerContext(sample_func),
RunnerContext(len),
RunnerContext(SampleService),
RunnerContext(SampleService, autoscale=False, warmup=2),
RunnerContext(SampleService()),
RunnerContext(SampleService(), warmup=1),
]
assert [(ctx.stateful, ctx.autoscale, ctx.warmup, ctx.min_instances, ctx.max_instances) for ctx in contexts] == [
(False, True, 0, 0, None),
(False, True, 0, 0, None),
(False, True, 0, 0, None),
(False, False, 2, 2, 2),
(True, False, 0, 1, 1),
(True, False, 1, 1, 1),
]
import functools
contexts = [
RunnerContext(sample_func),
RunnerContext(functools.partial(sample_func, x=1)),
RunnerContext(len),
RunnerContext(SampleService),
RunnerContext(SampleService, autoscale=False, warmup=2),
RunnerContext(SampleService()),
RunnerContext(SampleService(), warmup=1),
]
assert [(ctx.stateful, ctx.autoscale, ctx.warmup, ctx.min_instances, ctx.max_instances) for ctx in contexts] == [
(False, True, 0, 0, None),
(False, True, 0, 0, None),
(False, True, 0, 0, None),
(False, True, 0, 0, None),
(False, False, 2, 2, 2),
(True, False, 0, 1, 1),
(True, False, 1, 1, 1),
]

Comment on lines +509 to +518
@pytest.mark.parametrize(
("execution_object", "kwargs", "message"),
[
(lambda: None, {"stateful": True}, "always stateless"),
(str, {"stateful": True}, "always stateless"),
(object(), {"stateful": False}, "always stateful"),
(object(), {"autoscale": True}, "always fixed"),
(object(), {"warmup": 2}, "only support warmup=0 or warmup=1"),
],
)
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.

medium

Add a test case to verify that negative warmup values are rejected with a ValueError.

@pytest.mark.parametrize(
    ("execution_object", "kwargs", "message"),
    [
        (lambda: None, {"stateful": True}, "always stateless"),
        (str, {"stateful": True}, "always stateless"),
        (object(), {"stateful": False}, "always stateful"),
        (object(), {"autoscale": True}, "always fixed"),
        (object(), {"warmup": 2}, "only support warmup=0 or warmup=1"),
        (lambda: None, {"warmup": -1}, "warmup must be a non-negative integer"),
    ],
)

@k82cn k82cn merged commit cbee278 into xflops:main May 27, 2026
6 checks passed
@k82cn k82cn deleted the codex/fix-runner-instance-session branch May 27, 2026 07:01
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