mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-09-16 14:57:15 +00:00
fix(runner): keep installation context within each stream resume
This commit is contained in:
@@ -48,12 +48,15 @@ class RunnerInvoker:
|
||||
context=context,
|
||||
)
|
||||
|
||||
while True:
|
||||
try:
|
||||
result_dict = await self._next_with_deadline(gen, descriptor, context)
|
||||
except StopAsyncIteration:
|
||||
break
|
||||
yield result_dict
|
||||
try:
|
||||
while True:
|
||||
try:
|
||||
result_dict = await self._next_with_deadline(gen, descriptor, context)
|
||||
except StopAsyncIteration:
|
||||
break
|
||||
yield result_dict
|
||||
finally:
|
||||
await self._close_generator(gen, descriptor)
|
||||
|
||||
except asyncio.TimeoutError as e:
|
||||
raise RunnerExecutionError(
|
||||
@@ -128,4 +131,4 @@ class RunnerInvoker:
|
||||
try:
|
||||
await gen.aclose()
|
||||
except Exception as e:
|
||||
self.ap.logger.warning(f'Failed to close timed-out runner {descriptor.id}: {e}')
|
||||
self.ap.logger.warning(f'Failed to close runner {descriptor.id}: {e}')
|
||||
|
||||
@@ -2237,12 +2237,12 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
|
||||
include_plugins=bound_plugins,
|
||||
)
|
||||
runtime_handler = self._runtime_handler()
|
||||
with runtime_handler.installation_scope(binding):
|
||||
gen = runtime_handler.execute_command(
|
||||
command_ctx.model_dump(serialize_as_any=True),
|
||||
include_plugins=bound_plugins,
|
||||
)
|
||||
async for ret in gen:
|
||||
gen = runtime_handler.execute_command(
|
||||
command_ctx.model_dump(serialize_as_any=True),
|
||||
include_plugins=bound_plugins,
|
||||
)
|
||||
async with contextlib.aclosing(self._installation_scoped_stream(runtime_handler, binding, gen)) as scoped:
|
||||
async for ret in scoped:
|
||||
yield command_context.CommandReturn.model_validate(ret)
|
||||
|
||||
# Runner methods
|
||||
@@ -2308,15 +2308,31 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
|
||||
require_enabled=True,
|
||||
)
|
||||
runtime_handler = self._runtime_handler()
|
||||
with runtime_handler.installation_scope(binding):
|
||||
async for ret in runtime_handler.run_runner(
|
||||
plugin_author,
|
||||
plugin_name,
|
||||
runner_name,
|
||||
context,
|
||||
):
|
||||
gen = runtime_handler.run_runner(plugin_author, plugin_name, runner_name, context)
|
||||
async with contextlib.aclosing(self._installation_scoped_stream(runtime_handler, binding, gen)) as scoped:
|
||||
async for ret in scoped:
|
||||
yield ret
|
||||
|
||||
@staticmethod
|
||||
async def _installation_scoped_stream(runtime_handler, binding, gen):
|
||||
"""Keep ContextVar tokens inside a single resume, never across yields.
|
||||
|
||||
Consumers may use a different Task for each anext (e.g. wait_for).
|
||||
Reset the installation before exposing a result to the consumer, and
|
||||
re-enter the same immutable binding for transport cleanup.
|
||||
"""
|
||||
try:
|
||||
while True:
|
||||
with runtime_handler.installation_scope(binding):
|
||||
try:
|
||||
result = await anext(gen)
|
||||
except StopAsyncIteration:
|
||||
return
|
||||
yield result
|
||||
finally:
|
||||
with runtime_handler.installation_scope(binding):
|
||||
await gen.aclose()
|
||||
|
||||
async def retrieve_knowledge(
|
||||
self,
|
||||
plugin_author: str,
|
||||
|
||||
@@ -2933,8 +2933,9 @@ class RuntimeConnectionHandler(handler.Handler):
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
async for ret in gen:
|
||||
yield ret
|
||||
async with contextlib.aclosing(gen):
|
||||
async for ret in gen:
|
||||
yield ret
|
||||
|
||||
def _get_runner_action_timeout(self, context: dict[str, Any]) -> float:
|
||||
"""Use the run deadline as the transport idle timeout when available."""
|
||||
@@ -3143,8 +3144,9 @@ class RuntimeConnectionHandler(handler.Handler):
|
||||
timeout=180,
|
||||
)
|
||||
|
||||
async for ret in gen:
|
||||
yield ret
|
||||
async with contextlib.aclosing(gen):
|
||||
async for ret in gen:
|
||||
yield ret
|
||||
|
||||
async def retrieve_knowledge(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user