Given:
async def foo() -> str:
    return 'bar'
What you get when calling foo is an Awaitable, which obviously you'd want to await. What you need to think about is the return value of your function. You can for example do this:
def bar() -> Awaitable[str]:
    return foo()  # foo as defined above
There, bar is a synchronous function but returns an Awaitable which results in a str.
async def bar() -> str:
    return await foo()
Above, bar itself is async and results in an Awaitable when called which results in a str, same as above. There's no real difference between these two usages. Differences appear here:
async def bar() -> Awaitable[str]:
    return foo()
In that example, calling bar results in an Awaitable which results in an Awaitable which results in a str; quite different. If you naïvely use the above, you'll get this kind of result:
>>> asyncio.run(bar())
<coroutine object foo at 0x108706290>
RuntimeWarning: coroutine 'foo' was never awaited
As a rule of thumb, every call to an async must be awaited somewhere once. If you have two async (async def foo and async def bar) but no await in bar, then the caller of bar must await twice, which would be odd.