The base Chain acall method is not truly async should I want to implement a AsyncpgEntityStore

### System Info

langchain 0.0.201
python 3.11
debian bookworm

### Who can help?

@agola11 

### Information

- [ ] The official example notebooks/scripts
- [X] My own modified scripts

### Related Components

- [ ] LLMs/Chat Models
- [ ] Embedding Models
- [ ] Prompts / Prompt Templates / Prompt Selectors
- [ ] Output Parsers
- [ ] Document Loaders
- [ ] Vector Stores / Retrievers
- [X] Memory
- [ ] Agents / Agent Executors
- [ ] Tools / Toolkits
- [X] Chains
- [ ] Callbacks/Tracing
- [ ] Async

### Reproduction

I managed to implement a `AsyncpgEntityStore` that mimics the `SqliteEntityStore` but uses postgres [asyncpg](https://magicstack.github.io/asyncpg/current/) driver and using it successfully this way:

```
        entity_store = await AsyncpgEntityStore.from_connection(conn=conn, schema_name="public",)
        memory = AsyncConversationEntityMemory(llm=llm, entity_store=entity_store)
        return ConversationChain(llm=llm,
                                 prompt=ENTITY_MEMORY_CONVERSATION_TEMPLATE,
                                 verbose=True,
                                 memory=memory)
```

However the `AsyncpgEntityStore`  implementation (see below) overrides `EntityStore` base methods and turns them all into async which is problematic in current implentation of the base Chain, here's why :

the reason is `acall`  is calling:
1. `self.prep_inputs`
1.1. which calls `external_context = self.memory.load_memory_variables(inputs)` 
1.1.1. which calls  `entity_summaries[entity] = self.entity_store.get(entity, "")`
2. `self.prep_outputs`
2.1. which calls `self.memory.save_context(inputs, outputs)`
2.1.1.  which calls `self.entity_store.set(entity, output.strip())`

So in my case where the `get` and `set` method of the `self.entity_store` are asynchronous I end up not awaiting them:

I'd need to do:
in 1.1.1. ` entity_summaries[entity] = await self.entity_store.get(entity, "")`
in 2.1.1 `await self.entity_store.set(entity, output.strip())` 

So this is why I ended up using a `AsyncConversationEntityMemory` which overrides `ConversationEntityMemory`'s methods `save_context` and `load_memory_variables` and turn them async.

Finally I need to go one step above, the `acall` needs to do ` inputs = await self.aprep_inputs(inputs)` instead of ` inputs = await self.prep_inputs(inputs)` and the same for outputs.

Please see the full compare https://github.com/hwchase17/langchain/compare/master...euri10:asyncpg_memory?expand=1 needed in langchain to make this implementation below work.

Would there be a smarter way to proceed ?

```
class AsyncpgEntityStore(BaseEntityStore):
    conn: BuildPgConnection | None = None
    schema_name: str = "public"
    table_name: str = "memory_store"

    class Config:
        arbitrary_types_allowed = True

    def __init__(self, conn: BuildPgConnection | None, *args: Any, **kwargs: Any):
        try:
            import buildpg
        except ImportError:
            raise ImportError(
                "Could not import buildpg python package. "
                "Please install it with `pip install buildpg`."
            )
        super().__init__(*args, **kwargs)
        self.conn = conn

    @classmethod
    async def from_connection(cls, conn: Any, *args, **kwargs):
        instance = cls(conn, *args, **kwargs)
        await cls._create_table_if_not_exists(instance)
        return instance

    async def delete(self, key: str) -> None:
        query = f"""
            DELETE FROM :table
            WHERE key = :k
        """
        await self.conn.execute_b(
            query, table=V(f"{self.schema_name}.{self.table_name}"), k=key
        )

    async def exists(self, key: str) -> bool:
        query = f"""
            SELECT 1
            FROM :table
            WHERE key = :k
            LIMIT 1
        """
        result = await self.conn.fetch_b(
            query, table=V(f"{self.schema_name}.{self.table_name}"), k=(key)
        )
        return result is not None

    async def clear(self) -> None:
        query = f"""
            DELETE FROM :table
        """
        await self.conn.execute_b(query)

    async def get(self, key: str, default: Optional[str] = None) -> Optional[str]:
        query = f"""
            SELECT value
            FROM :table
            WHERE key = :k
        """
        result = await self.conn.fetchval_b(
            query, table=V(f"{self.schema_name}.{self.table_name}"), k=key
        )
        if result is not None:
            return result
        return default

    async def set(self, key: str, value: Optional[str]) -> None:
        if not value:
            return await self.delete(key)
        query = """
            INSERT INTO :table (key, value)
            VALUES (:k, :v)
            on conflict (key) do update set value = excluded.value
        """
        await self.conn.execute_b(
            query, table=V(f"{self.schema_name}.{self.table_name}"), k=key, v=value
        )

    async def _create_table_if_not_exists(self) -> None:
        create_table_query = f"""CREATE TABLE IF NOT EXISTS :table (
            key TEXT PRIMARY KEY,
            value TEXT
        );"""
        await self.conn.execute_b(
            create_table_query, table=V(f"{self.schema_name}.{self.table_name}")
        )
```

### Expected behavior

to be able to implement async class more easily