Services¶
A service is a long-lived object that a workflow starts once and then uses
many times: a loaded model, a simulator, a connection pool. AsyncFlow runs it
as a service task (function_task(..., service=True)), and Flowgentic's
SERVICE_TASK flow type registers one through LangraphIntegration.
Start a service¶
Decorate the function that starts the service. This example's service is a
small simulated server, examples/langgraph-integration/service-task/utils/simple_server.py;
it opens no network port:
# SERVICE_TASK that starts a persistent background server
@agents_manager.execution_wrappers.asyncflow(
flow_type=AsyncFlowType.SERVICE_TASK
)
async def start_api_server(port: int) -> SimpleAsyncServer:
"""
Starts a background API server.
Returns the server instance that persists until cancelled.
"""
print(f"🚀 COLD START: Initializing new server on port {port}...")
server = SimpleAsyncServer(host="localhost", port=port)
await server.start()
print(f"✅ Server {server.instance_id} is now running")
return server
The decorated function is async: awaiting a call submits the service task and returns its future. Awaiting that future gives the service, and later requests go to the same running instance:
print("\n🔷 Step 1: Start first service (await returns the service handle)")
service_future_1 = await start_api_server(8080)
print(f" → Got service future handle: {type(service_future_1)}")
# Await the future to get the actual server instance
print("\n🔷 Step 2: Await the future to get server instance")
server_1 = await service_future_1
print(f" → Server instance: {server_1.instance_id}")
print(f" → Port: {server_1.port}")
# Make first request
print("\n🔷 Step 3: Make first request")
result_1 = await server_1.handle_request("/api/data", "GET")
Each awaited call starts a new service instance, with its own state:
print("\n🔷 Step 6: Start SECOND service (different port)")
service_future_2 = await start_api_server(8081)
server_2 = await service_future_2
Cancel the future to stop the service task, and release the service's own resources:
print("\n🔷 Step 10: Cancel first service")
service_future_1.cancel()
await server_1.shutdown()
print(f" ✓ Server 1 ({server_1.instance_id}) shut down")
print("\n🔷 Step 11: Cancel second service")
service_future_2.cancel()
await server_2.shutdown()
print(f" ✓ Server 2 ({server_2.instance_id}) shut down")
Run the example from the repository root:
It starts two servers, sends four requests to the first and two to the second, and reports one cold start per server:
======================================================================
✅ PROOF: Both services running independently
======================================================================
Server 1: 4 total requests (counter at 4)
Server 2: 2 total requests (counter at 2)
Both maintained separate state and uptime
tests/integration/test_feature_examples.py runs the example and checks these
counts.
In the agentic demo¶
The agentic demo loads its surrogate model as a service on the ai backend,
and its tools use that one instance for every request:
# 1. The same resident surrogate service as the deterministic demo --------
@flowgentic(flow_type=AsyncFlowType.SERVICE_TASK, backend="ai")
async def load_surrogate() -> SurrogateService:
return await SurrogateService.load()
service_future = await load_surrogate()
service = await service_future
The demo's summary records one load and five requests ("loads": 1,
"requests": 5). The runner cancels the service's future when the campaign
ends.
Service or task¶
FUNCTION_TASK |
SERVICE_TASK |
|
|---|---|---|
| AsyncFlow call | function_task |
function_task(..., service=True) |
| Awaiting a call returns | The task's result | The task's future; awaiting it gives the service |
| Each awaited call | Runs the function | Starts a new service instance |
| Stop it | It ends when the function returns | Cancel the future |
Each call runs once. Flowgentic does not restart a service that fails; see Retries and timeouts.
Without Flowgentic, the same service is @flow.function_task(service=True); see
LangGraph on AsyncFlow directly.