Skip to content

Commit 28ba577

Browse files
bug fix
1 parent 7222520 commit 28ba577

19 files changed

+75
-108
lines changed

.env.example

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
# Protect endpoints with an access token
1616
# CCAT_API_KEY=meow
17-
# CCAT_API_KEY_WS=meow2
1817

1918
# self reload during development
2019
# CCAT_DEBUG=true
@@ -39,11 +38,18 @@
3938
# Turn on memory collections' snapshots on embedder change with SAVE_MEMORY_SNAPSHOTS=true
4039
# CCAT_SAVE_MEMORY_SNAPSHOTS=false
4140

42-
# CONFIG_FILE
43-
# CCAT_METADATA_FILE="cat/data/metadata.json"
44-
4541
# Set container timezone
4642
# CCAT_TIMEZONE=Europe/Rome
4743

44+
# Set the expiration time for the history conversation in the Redis storage
45+
# CCAT_HISTORY_EXPIRATION=30
46+
4847
# Set the ability of the Cheshire Cat to store the RabbitHole uploaded files into a remote storage
49-
# CCAT_RABBIT_HOLE_STORAGE_ENABLED=false
48+
# CCAT_RABBIT_HOLE_STORAGE_ENABLED=false
49+
50+
# Enable CORS
51+
# CCAT_CORS_ENABLED=true
52+
53+
# Set the number of workers and the limit of requests for the FastAPI server
54+
# CCAT_WORKERS=1
55+
# CCAT_LIMIT_MAX_REQUESTS=1000

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ New features will be added in the future. Please contact us if you want to contr
8080
To make Cheshire Cat run on your machine, you just need [`docker`](https://docs.docker.com/get-docker/) installed:
8181

8282
```bash
83-
docker run --rm -it -p 1865:80 ghcr.io/matteocacciola/cheshirecat-core:2.0.1
83+
docker run --rm -it -p 1865:80 ghcr.io/matteocacciola/cheshirecat-core:2.0.2
8484
```
8585
- Chat with the Cheshire Cat on [localhost:1865/docs](http://localhost:1865/docs).
8686

@@ -103,7 +103,7 @@ Follow instructions on how to run it with [docker compose and volumes](https://c
103103
```python
104104
from cat.mad_hatter.decorators import hook
105105

106-
# hooks are an event system to get finegraned control over your assistant
106+
# hooks are an event system to get fine-grained control over your assistant
107107
@hook
108108
def agent_prompt_prefix(prefix, cat):
109109
prefix = """You are Marvin the socks seller, a poetic vendor of socks.

core/cat/auth/permissions.py

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
from cat.utils import BaseModelDict, Enum
66

7-
from fastapi import Depends
87

98
class AuthResource(Enum):
109
CRUD = "CRUD"
@@ -143,31 +142,4 @@ class AuthUserInfo(BaseModelDict):
143142
# - profile data
144143
# - custom attributes
145144
# - roles
146-
extra: BaseModelDict = Field(default_factory=dict)
147-
148-
149-
def check_permissions(resource: AuthResource, permission: AuthPermission):
150-
"""
151-
Helper function to inject stray into endpoints after checking for required permissions.
152-
153-
Parameters
154-
----------
155-
resource: AuthResource | str
156-
The resource that the user must have permission for.
157-
permission: AuthPermission | str
158-
The permission that the user must have for the resource.
159-
160-
Returns
161-
----------
162-
stray: StrayCat | None
163-
User session object if auth is successfull, None otherwise.
164-
"""
165-
166-
167-
# import here to avoid circular imports
168-
from cat.auth.connection import HTTPAuth
169-
return Depends(HTTPAuth(
170-
# explicit convert to Enum
171-
resource = AuthResource(resource),
172-
permission = AuthPermission(permission),
173-
))
145+
extra: Dict = Field(default_factory=dict)

core/cat/env.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ def get_supported_env_variables():
88
"CCAT_CORE_USE_SECURE_PROTOCOLS": "",
99
"CCAT_ADMIN_DEFAULT_PASSWORD": "AIBlackBirdWithCheshireCat",
1010
"CCAT_API_KEY": None,
11-
"CCAT_API_KEY_WS": None,
1211
"CCAT_DEBUG": "true",
1312
"CCAT_LOG_LEVEL": "INFO",
1413
"CCAT_CORS_ALLOWED_ORIGINS": None,

core/cat/factory/custom_auth_handler.py

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -249,35 +249,30 @@ def authorize_user_from_key(
249249
request_user_id: str | None = None,
250250
**kwargs,
251251
) -> AuthUserInfo | None:
252-
http_key = get_env("CCAT_API_KEY")
253-
ws_key = get_env("CCAT_API_KEY_WS")
254-
255-
if not http_key and not ws_key:
252+
if not (current_api_key := get_env("CCAT_API_KEY")):
253+
return None
254+
if api_key != current_api_key:
256255
return None
257256

258257
key_id = kwargs.get("key_id")
259-
user_info = extract_user_info_on_api_key(key_id, request_user_id)
260-
if not user_info:
258+
if not (user_info := extract_user_info_on_api_key(key_id, request_user_id)):
261259
return None
262260

263-
if protocol == "websocket" and api_key == ws_key:
264-
permissions: Dict[str, List[str]] = kwargs.get("websocket_permissions", user_info.permissions)
265-
return AuthUserInfo(
266-
id=user_info.user_id,
267-
name=user_info.username,
268-
permissions=permissions
269-
)
270-
271-
if protocol == "http" and api_key == http_key:
272-
permissions: Dict[str, List[str]] = kwargs.get("http_permissions", user_info.permissions)
273-
return AuthUserInfo(
274-
id=user_info.user_id,
275-
name=user_info.username,
276-
permissions=permissions
277-
)
261+
permissions: Dict[str, List[str]] | None = None
262+
if protocol == "websocket":
263+
permissions = kwargs.get("websocket_permissions", user_info.permissions)
264+
elif protocol == "http":
265+
permissions = kwargs.get("http_permissions", user_info.permissions)
278266

279267
# No match -> deny access
280-
return None
268+
if not permissions:
269+
return None
270+
271+
return AuthUserInfo(
272+
id=user_info.user_id,
273+
name=user_info.username,
274+
permissions=permissions
275+
)
281276

282277

283278
# Default Auth, always deny auth by default (only core auth decides).

core/cat/looking_glass/stray_cat.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ async def __call__(self, user_message: UserMessage) -> CatMessage:
365365
answer. This is formatted in a dictionary to be sent as a JSON via Websocket to the client.
366366
"""
367367

368-
### setup working memory
368+
### setup working memory for this convo turn
369369
# keeping track of model interactions
370370
self.working_memory.model_interactions = []
371371
# latest user message

core/cat/memory/utils.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,14 @@ def to_document_recall(m: Record | ScoredPoint) -> DocumentRecall:
4343
DocumentRecall: The converted DocumentRecall object
4444
"""
4545

46-
document = DocumentRecall(document=Document(**m.payload), vector=m.vector, id=m.id)
46+
document = DocumentRecall(
47+
document=Document(
48+
page_content=m.payload.get("page_content", "") if m.payload else "",
49+
metadata=m.payload.get("metadata", {}) if m.payload else {},
50+
),
51+
vector=m.vector,
52+
id=m.id,
53+
)
4754

4855
if isinstance(m, ScoredPoint):
4956
document.score = m.score

core/cat/memory/vector_memory_collection.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ def _tenant_field_condition(self) -> FieldCondition:
3939
def _build_condition(self, key: str, value: Any) -> List[FieldCondition]:
4040
out = []
4141

42-
if isinstance(value, Dict):
43-
out.extend(self._build_condition(f"{key}.{k}", v) for k, v in value.items())
44-
elif isinstance(value, List):
45-
out.extend(
46-
self._build_condition(f"{key}[]" if isinstance(v, Dict) else f"{key}", v) for v in value
47-
)
42+
if isinstance(value, dict):
43+
for k, v in value.items():
44+
out.extend(self._build_condition(f"{key}.{k}", v))
45+
elif isinstance(value, list):
46+
for v in value:
47+
out.extend(self._build_condition(f"{key}[]" if isinstance(v, dict) else f"{key}", v))
4848
else:
4949
out.append(FieldCondition(key=f"metadata.{key}", match=MatchValue(value=value)))
5050

core/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[project]
22
name = "Cheshire-Cat"
33
description = "Production ready AI assistant framework"
4-
version = "2.0.1"
4+
version = "2.0.2"
55
requires-python = ">=3.10"
66
license = { file = "LICENSE" }
77
authors = [

core/tests/conftest.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import asyncio
21
import pytest
32
import pytest_asyncio
43
import os
@@ -29,7 +28,6 @@
2928
from tests.utils import (
3029
agent_id,
3130
api_key,
32-
api_key_ws,
3331
jwt_secret,
3432
create_mock_plugin_zip,
3533
get_class_from_decorated_singleton,
@@ -180,17 +178,15 @@ async def client(cheshire_cat) -> Generator[TestClient, Any, None]:
180178
yield client
181179

182180

183-
# This fixture sets the CCAT_API_KEY and CCAT_API_KEY_WS environment variables,
184-
# making mandatory for clients to possess api keys or JWT
181+
# This fixture sets the CCAT_API_KEY environment variable,
182+
# making mandatory for clients to possess api key or JWT
185183
@pytest_asyncio.fixture(scope="function")
186184
async def secure_client(client):
187185
current_api_key = os.getenv("CCAT_API_KEY")
188-
current_api_ws = os.getenv("CCAT_API_KEY_WS")
189186
current_jwt_secret = os.getenv("CCAT_JWT_SECRET")
190187

191188
# set ENV variables
192189
os.environ["CCAT_API_KEY"] = api_key
193-
os.environ["CCAT_API_KEY_WS"] = api_key_ws
194190
os.environ["CCAT_JWT_SECRET"] = jwt_secret
195191

196192
yield client
@@ -200,10 +196,6 @@ async def secure_client(client):
200196
os.environ["CCAT_API_KEY"] = current_api_key
201197
else:
202198
del os.environ["CCAT_API_KEY"]
203-
if current_api_ws:
204-
os.environ["CCAT_API_KEY_WS"] = current_api_ws
205-
else:
206-
del os.environ["CCAT_API_KEY_WS"]
207199
if current_jwt_secret:
208200
os.environ["CCAT_JWT_SECRET"] = current_jwt_secret
209201
else:

core/tests/routes/admins/test_admins_permissions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
# test endpoints with different user permissions
66
# NOTE: we are using here the secure_client:
7-
# - CCAT_API_KEY and CCAT_API_KEY_WS are active
7+
# - CCAT_API_KEY is active
88
# - we will auth with JWT
99

1010

core/tests/routes/auth/test_auth_setting.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from cat.factory.auth_handler import AuthHandlerFactory
66

7-
from tests.utils import api_key, api_key_ws
7+
from tests.utils import api_key
88

99

1010
def test_get_all_auth_handler_settings(secure_client, secure_client_headers, agent_plugin_manager):
@@ -54,10 +54,7 @@ def test_get_auth_handler_settings(secure_client, secure_client_headers):
5454
def test_upsert_auth_handler_settings(secure_client, secure_client_headers):
5555
# set a different auth_handler from default one (same class different size)
5656
new_auth_handler = "AuthApiKeyConfig"
57-
auth_handler_config = {
58-
"api_key_http": api_key,
59-
"api_key_ws": api_key_ws,
60-
}
57+
auth_handler_config = {"api_key": api_key}
6158
response = secure_client.put(
6259
f"/auth_handler/settings/{new_auth_handler}", json=auth_handler_config, headers=secure_client_headers
6360
)

core/tests/routes/auth/test_env_api_key.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from cat.env import get_env
66

7-
from tests.conftest import api_key, api_key_ws
7+
from tests.conftest import api_key
88
from tests.utils import send_websocket_message
99

1010

@@ -42,7 +42,7 @@ def test_api_key_http(secure_client, header_name):
4242
wrong_headers = [
4343
{}, # no key
4444
{header_name: f"{key_prefix}wrong"}, # wrong key
45-
{header_name: f"{key_prefix}{api_key_ws}"}, # websocket key
45+
{header_name: f"{key_prefix}{api_key}"}, # websocket key
4646
]
4747

4848
# all the previous headers result in a 403
@@ -61,8 +61,8 @@ def test_api_key_http(secure_client, header_name):
6161

6262

6363
def test_api_key_ws(secure_client, secure_client_headers):
64-
# set CCAT_API_KEY_WS
65-
old_api_key = set_api_key("CCAT_API_KEY_WS", api_key_ws)
64+
# set CCAT_API_KEY
65+
old_api_key = set_api_key("CCAT_API_KEY", api_key)
6666

6767
mex = {"text": "Where do I go?"}
6868

@@ -76,9 +76,9 @@ def test_api_key_ws(secure_client, secure_client_headers):
7676
with pytest.raises(WebSocketDisconnect):
7777
send_websocket_message(mex, secure_client, query_params=params)
7878

79-
# allow access if CCAT_API_KEY_WS is right
80-
query_params = {"apikey": api_key_ws}
79+
# allow access if CCAT_API_KEY is right
80+
query_params = {"apikey": api_key}
8181
res = send_websocket_message(mex, secure_client, query_params=query_params)
8282
assert "You did not configure" in res["content"]
8383

84-
reset_api_key("CCAT_API_KEY_WS", old_api_key)
84+
reset_api_key("CCAT_API_KEY", old_api_key)

core/tests/routes/memory/test_memory_collection.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from tests.utils import send_websocket_message, get_collections_names_and_point_count, api_key_ws
1+
from tests.utils import send_websocket_message, get_collections_names_and_point_count, api_key
22

33

44
def test_memory_collections_created(secure_client, secure_client_headers):
@@ -29,7 +29,7 @@ def test_memory_collection_episodic_stores_messages(
2929

3030
# send message via websocket
3131
message = {"text": "Meow"}
32-
res = send_websocket_message(message, secure_client, {"apikey": api_key_ws})
32+
res = send_websocket_message(message, secure_client, {"apikey": api_key})
3333
assert isinstance(res["content"], str)
3434

3535
# episodic memory should now contain one point
@@ -50,7 +50,7 @@ def test_memory_collection_episodic_cleared(
5050
):
5151
# send message via websocket
5252
message = {"text": "Meow"}
53-
res = send_websocket_message(message, secure_client, {"apikey": api_key_ws})
53+
res = send_websocket_message(message, secure_client, {"apikey": api_key})
5454
assert isinstance(res["content"], str)
5555

5656
# episodic memory should now contain one point
@@ -85,7 +85,7 @@ def test_memory_collections_wipe(
8585
):
8686
# create episodic memory
8787
message = {"text": "Meow"}
88-
send_websocket_message(message, secure_client, {"apikey": api_key_ws})
88+
send_websocket_message(message, secure_client, {"apikey": api_key})
8989

9090
# create declarative memories
9191
file_name = "sample.txt"

core/tests/routes/memory/test_memory_points.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
get_declarative_memory_contents,
88
agent_id,
99
fake_timestamp,
10-
api_key_ws,
10+
api_key,
1111
)
1212

1313

@@ -61,7 +61,7 @@ def test_create_memory_point(secure_client, secure_client_headers, cheshire_cat,
6161

6262
def test_point_deleted(secure_client, secure_client_headers, mocked_default_llm_answer_prompt):
6363
# send websocket message
64-
send_websocket_message({"text": "Hello Mad Hatter"}, secure_client, {"apikey": api_key_ws})
64+
send_websocket_message({"text": "Hello Mad Hatter"}, secure_client, {"apikey": api_key})
6565

6666
user = crud_users.get_user_by_username(agent_id, "user")
6767

0 commit comments

Comments
 (0)