cheshire-cat-ai / core

Production ready AI agent framework
https://cheshirecat.ai
GNU General Public License v3.0
2.38k stars 327 forks source link

Add New POST Endpoint /memory/recall #976

Closed dave90 closed 3 days ago

dave90 commented 3 days ago

Description

This PR create a new POST endpoint /memory/recall that accept a json with this format:

{
    "text": "...",
    "k": 34,
    "metadata": {}
}

metadata is a simple flat dictionary to search in AND

Endpoint GET /memory/recall remain and log a warning that is going to be deprecated in the next major version.

Related to issue #899

Type of change

Checklist:

matteocacciola commented 3 days ago

A POST request to get info from the vector database?🤔

lucagobbi commented 3 days ago

A POST request to get info from the vector database?🤔

How would you filter by metadata using a GET request? POST seems to be a pretty common practice: Qdrant, Pinecone

pieroit commented 3 days ago

A POST request to get info from the vector database?🤔

How would you filter by metadata using a GET request? POST seems to be a pretty common practice: Qdrant, Pinecone

Can confirm, using POST for queries is ugly but pragmatically used everywhere

pieroit commented 3 days ago

Thanks @dave90 !! Didn't know there was a deprecated flag in fastAPI

matteocacciola commented 3 days ago

A POST request to get info from the vector database?🤔

How would you filter by metadata using a GET request? POST seems to be a pretty common practice: Qdrant, Pinecone

A parsed associative array (or a dictionary, whatever you want to call it) as part of the query string. A simple example in PHP, for instance, able to return that parsed string I mentioned. Then, python could be able to parse back in a dictionary, afaik.

<?php
$arr = [
    'nome' => 'Mario',
    'cognome' => 'Rossi',
    'hobby' => ['primo' => 'calcio', 'secondo' => 'lettura', 'terzo' => 'musica'],
];

$queryString = http_build_query($arr);

var_dump($queryString);

parse_str($queryString, $arrFromQueryString);

var_dump($arrFromQueryString);
matteocacciola commented 2 days ago

I had the time to try something. Here is how the code could have been changed

@router.get("/recall")
async def recall_memory_points_from_text(
    request: Request,
    text: str = Query(description="Find memories similar to this text."),
    k: int = Query(default=100, description="How many memories to return."),
    metadata: Dict[str, Any] = Depends(create_dict_parser(
        "metadata",
        description="Flat dictionary where each key-value pair represents a filter."
                    "The memory points returned will match the specified metadata criteria."
    )),
    stray: StrayCat = Depends(HTTPAuth(AuthResource.MEMORY, AuthPermission.READ)),
) -> Dict:

and here is the create_dict_parser

def create_dict_parser(param_name: str, description: str | None = None):
    def parser(
        param_value: str | None = Query(
            default=None,
            alias=param_name,
            description=description or f"{param_name} JSON filter."
        )
    ) -> Dict[str, Any]:
        if not param_value:
            return {}
        try:
            return json.loads(param_value.replace("'", "\""))
        except json.JSONDecodeError:
            return {}
    return parser

and no changes to the new test, except the get rather than the post request :-).